geometry 6.3 → 6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +10 -2
- data/README.markdown +1 -0
- data/geometry.gemspec +2 -4
- data/lib/geometry/bezier.rb +48 -0
- data/lib/geometry/edge.rb +15 -2
- data/lib/geometry/line.rb +73 -0
- data/lib/geometry/point.rb +38 -0
- data/lib/geometry/point_iso.rb +38 -0
- data/lib/geometry/point_one.rb +38 -0
- data/lib/geometry/point_zero.rb +38 -0
- data/lib/geometry/square.rb +16 -0
- data/test/geometry/bezier.rb +25 -0
- data/test/geometry/edge.rb +10 -0
- data/test/geometry/line.rb +63 -0
- data/test/geometry/point.rb +52 -0
- data/test/geometry/point_iso.rb +52 -0
- data/test/geometry/point_one.rb +52 -0
- data/test/geometry/point_zero.rb +50 -0
- data/test/geometry/square.rb +8 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2071223ccd3ab40dc84675595b6b204aa0d7df10
|
4
|
+
data.tar.gz: ed6b83fe034a5734ca9938c7ab54f4a4fcdda736
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d78e31275dd418edf77a9067157213b63690bb7139ccf8c8561b228bb51e7be9e8b72e3a9187f02beb8d77ddc95427f882454f49c4827196f832acadec131e3c
|
7
|
+
data.tar.gz: ba61dfc2b8d83090d59fe4d5348d3b184edeca188bf59c604d6888644a7392b943f8979ac4ea82392b1d8a026b53873128bab58c64702af149c3be68c1852057
|
data/.travis.yml
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
|
4
|
-
|
3
|
+
- 2.0.0
|
4
|
+
- 2.1.1
|
5
|
+
deploy:
|
6
|
+
provider: rubygems
|
7
|
+
api_key:
|
8
|
+
secure: aCCrqOZ4pR5ACbPHfsurZbY2KOT3Qs5DZ7cJEG6Haa3sJXoMQ2srCtE2hsH1VO8idmrkHvl3wCEO9zNLOdx2x40xlaLVQ+wixtFKW1u9nnxjw4zszyyaxDIc8qnFsRNh6j997wyT2Sqx1YuQ8I82DnTh2I28mnxS51O33KeB0is=
|
9
|
+
gem: geometry
|
10
|
+
on:
|
11
|
+
tags: true
|
12
|
+
repo: bfoz/geometry
|
data/README.markdown
CHANGED
@@ -26,6 +26,7 @@ Primitives
|
|
26
26
|
- Edge
|
27
27
|
- [Annulus](http://en.wikipedia.org/wiki/Annulus_(mathematics))
|
28
28
|
- [Arc](http://en.wikipedia.org/wiki/Arc_(geometry)), Circle
|
29
|
+
- [Bézier Curves](http://en.wikipedia.org/wiki/Bézier_curve)
|
29
30
|
- Rectangle, Square
|
30
31
|
- Path, [Polyline](http://en.wikipedia.org/wiki/Polyline), [Polygon](http://en.wikipedia.org/wiki/Polygon), [RegularPolygon](http://en.wikipedia.org/wiki/Regular_polygon)
|
31
32
|
- Transformation
|
data/geometry.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "geometry"
|
6
|
-
s.version = '6.
|
6
|
+
s.version = '6.4'
|
7
7
|
s.authors = ["Brandon Fosdick"]
|
8
8
|
s.email = ["bfoz@bfoz.net"]
|
9
9
|
s.homepage = "http://github.com/bfoz/geometry"
|
@@ -17,7 +17,5 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
|
21
|
-
# s.add_development_dependency "rspec"
|
22
|
-
# s.add_runtime_dependency "rest-client"
|
20
|
+
s.required_ruby_version = '>= 2.0'
|
23
21
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Geometry
|
2
|
+
=begin
|
3
|
+
Bézier curves are like lines, but curvier.
|
4
|
+
|
5
|
+
http://en.wikipedia.org/wiki/Bézier_curve
|
6
|
+
|
7
|
+
== Constructors
|
8
|
+
|
9
|
+
Bezier.new [0,0], [1,1], [2,2] # From control points
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
|
13
|
+
To get a point on the curve for a particular value of t, you can use the subscript operator
|
14
|
+
|
15
|
+
bezier[0.5] # => [1,1]
|
16
|
+
=end
|
17
|
+
class Bezier
|
18
|
+
# @!attribute degree
|
19
|
+
# @return [Number] The degree of the curve
|
20
|
+
def degree
|
21
|
+
points.length - 1
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!attribute points
|
25
|
+
# @return [Array<Point>] The control points for the Bézier curve
|
26
|
+
attr_reader :points
|
27
|
+
|
28
|
+
def initialize(*points)
|
29
|
+
@points = points.map {|v| Point[v]}
|
30
|
+
end
|
31
|
+
|
32
|
+
# http://en.wikipedia.org/wiki/Binomial_coefficient
|
33
|
+
# http://rosettacode.org/wiki/Evaluate_binomial_coefficients#Ruby
|
34
|
+
def binomial_coefficient(k)
|
35
|
+
(0...k).inject(1) {|m,i| (m * (degree - i)) / (i + 1) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param t [Float] the input parameter
|
39
|
+
def [](t)
|
40
|
+
return nil unless (0..1).include?(t)
|
41
|
+
result = Point.zero(points.first.size)
|
42
|
+
points.each_with_index do |v, i|
|
43
|
+
result += v * binomial_coefficient(i) * ((1 - t) ** (degree - i)) * (t ** i)
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/geometry/edge.rb
CHANGED
@@ -8,8 +8,8 @@ module Geometry
|
|
8
8
|
An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
|
9
9
|
|
10
10
|
== Usage
|
11
|
+
edge = Geometry::Edge.new([1,1], [2,2])
|
11
12
|
edge = Geometry::Edge([1,1], [2,2])
|
12
|
-
|
13
13
|
=end
|
14
14
|
|
15
15
|
class Edge
|
@@ -44,7 +44,7 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
|
|
44
44
|
|
45
45
|
# Return a new {Edge} with swapped endpoints
|
46
46
|
def reverse
|
47
|
-
|
47
|
+
self.class.new(@last, @first)
|
48
48
|
end
|
49
49
|
|
50
50
|
# In-place swap the endpoints
|
@@ -53,6 +53,11 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
|
|
53
53
|
self
|
54
54
|
end
|
55
55
|
|
56
|
+
# @return [Number] the length of the {Edge}
|
57
|
+
def length
|
58
|
+
@length ||= vector.magnitude
|
59
|
+
end
|
60
|
+
|
56
61
|
# Return the {Edge}'s length along the Y axis
|
57
62
|
def height
|
58
63
|
(@first.y - @last.y).abs
|
@@ -140,4 +145,12 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
|
|
140
145
|
[@first, @last]
|
141
146
|
end
|
142
147
|
end
|
148
|
+
|
149
|
+
# Convenience initializer for {Edge} that tries to coerce its arguments into
|
150
|
+
# something useful
|
151
|
+
# @param first [Point, Array] the starting point of the {Edge}
|
152
|
+
# @param last [Point, Array] the endpoint of the {Edge}
|
153
|
+
def Edge(first, last)
|
154
|
+
Edge.new(first, last)
|
155
|
+
end
|
143
156
|
end
|
data/lib/geometry/line.rb
CHANGED
@@ -87,13 +87,42 @@ Supports two-point, slope-intercept, and point-slope initializer forms
|
|
87
87
|
|
88
88
|
# @private
|
89
89
|
class PointSlopeLine < Line
|
90
|
+
# @!attribute point
|
91
|
+
# @return [Point] the stating point
|
92
|
+
attr_reader :point
|
93
|
+
|
90
94
|
# @return [Number] the slope of the {Line}
|
91
95
|
attr_reader :slope
|
92
96
|
|
97
|
+
# @param point [Point] a {Point} that lies on the {Line}
|
98
|
+
# @param slope [Number] the slope of the {Line}
|
93
99
|
def initialize(point, slope)
|
94
100
|
@point = Point[point]
|
95
101
|
@slope = slope
|
96
102
|
end
|
103
|
+
|
104
|
+
# Two {PointSlopeLine}s are equal if both have equal slope and origin
|
105
|
+
def ==(other)
|
106
|
+
case other
|
107
|
+
when SlopeInterceptLine
|
108
|
+
# Check that the slopes are equal and that the starting point will solve the slope-intercept equation
|
109
|
+
(slope == other.slope) && (point.y == other.slope * point.x + other.intercept)
|
110
|
+
when TwoPointLine
|
111
|
+
# Plug both of other's endpoints into the line equation and check that they solve it
|
112
|
+
first_diff = other.first - point
|
113
|
+
last_diff = other.last - point
|
114
|
+
(first_diff.y == slope*first_diff.x) && (last_diff.y == slope*last_diff.x)
|
115
|
+
else
|
116
|
+
self.eql? other
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Two {PointSlopeLine}s are equal if both have equal slopes and origins
|
121
|
+
# @note eql? does not check for equivalence between cluster subclases
|
122
|
+
def eql?(other)
|
123
|
+
(point == other.point) && (slope == other.slope)
|
124
|
+
end
|
125
|
+
|
97
126
|
def to_s
|
98
127
|
'Line(' + @slope.to_s + ',' + @point.to_s + ')'
|
99
128
|
end
|
@@ -104,11 +133,33 @@ Supports two-point, slope-intercept, and point-slope initializer forms
|
|
104
133
|
# @return [Number] the slope of the {Line}
|
105
134
|
attr_reader :slope
|
106
135
|
|
136
|
+
# @param slope [Number] the slope
|
137
|
+
# @param intercept [Number] the location of the y-axis intercept
|
107
138
|
def initialize(slope, intercept)
|
108
139
|
@slope = slope
|
109
140
|
@intercept = intercept
|
110
141
|
end
|
111
142
|
|
143
|
+
# Two {SlopeInterceptLine}s are equal if both have equal slope and intercept
|
144
|
+
def ==(other)
|
145
|
+
case other
|
146
|
+
when PointSlopeLine
|
147
|
+
# Check that the slopes are equal and that the starting point will solve the slope-intercept equation
|
148
|
+
(slope == other.slope) && (other.point.y == slope * other.point.x + intercept)
|
149
|
+
when TwoPointLine
|
150
|
+
# Check that both endpoints solve the line equation
|
151
|
+
((other.first.y == slope * other.first.x + intercept)) && (other.last.y == (slope * other.last.x + intercept))
|
152
|
+
else
|
153
|
+
self.eql? other
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Two {SlopeInterceptLine}s are equal if both have equal slopes and intercepts
|
158
|
+
# @note eql? does not check for equivalence between cluster subclases
|
159
|
+
def eql?(other)
|
160
|
+
(intercept == other.intercept) && (slope == other.slope)
|
161
|
+
end
|
162
|
+
|
112
163
|
def horizontal?
|
113
164
|
0 == @slope
|
114
165
|
end
|
@@ -142,6 +193,28 @@ Supports two-point, slope-intercept, and point-slope initializer forms
|
|
142
193
|
end
|
143
194
|
alias :to_s :inspect
|
144
195
|
|
196
|
+
# Two {TwoPointLine}s are equal if both have equal {Point}s in the same order
|
197
|
+
def ==(other)
|
198
|
+
case other
|
199
|
+
when PointSlopeLine
|
200
|
+
# Plug both endpoints into the line equation and check that they solve it
|
201
|
+
first_diff = first - other.point
|
202
|
+
last_diff = last - other.point
|
203
|
+
(first_diff.y == other.slope*first_diff.x) && (last_diff.y == other.slope*last_diff.x)
|
204
|
+
when SlopeInterceptLine
|
205
|
+
# Check that both endpoints solve the line equation
|
206
|
+
((first.y == other.slope * first.x + other.intercept)) && (last.y == (other.slope * last.x + other.intercept))
|
207
|
+
else
|
208
|
+
self.eql?(other) || ((first == other.last) && (last == other.first))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Two {TwoPointLine}s are equal if both have equal endpoints
|
213
|
+
# @note eql? does not check for equivalence between cluster subclases
|
214
|
+
def eql?(other)
|
215
|
+
(first == other.first) && (last == other.last)
|
216
|
+
end
|
217
|
+
|
145
218
|
# @group Accessors
|
146
219
|
# !@attribute [r[ slope
|
147
220
|
# @return [Number] the slope of the {Line}
|
data/lib/geometry/point.rb
CHANGED
@@ -119,6 +119,44 @@ geometry class (x, y, z).
|
|
119
119
|
'Point' + @elements.to_s
|
120
120
|
end
|
121
121
|
|
122
|
+
# @override max()
|
123
|
+
# @return [Number] The maximum value of the {Point}'s elements
|
124
|
+
# @override max(point)
|
125
|
+
# @return [Point] The element-wise maximum values of the receiver and the given {Point}
|
126
|
+
def max(*args)
|
127
|
+
if args.empty?
|
128
|
+
@elements.max
|
129
|
+
else
|
130
|
+
args = args.first if 1 == args.size
|
131
|
+
self.class[@elements.zip(args).map(&:max)]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# @override min()
|
136
|
+
# @return [Number] The minimum value of the {Point}'s elements
|
137
|
+
# @override min(point)
|
138
|
+
# @return [Point] The element-wise minimum values of the receiver and the given {Point}
|
139
|
+
def min(*args)
|
140
|
+
if args.empty?
|
141
|
+
@elements.min
|
142
|
+
else
|
143
|
+
args = args.first if 1 == args.size
|
144
|
+
self.class[@elements.zip(args).map(&:min)]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @override minmax()
|
149
|
+
# @return [Array<Number>] The minimum value of the {Point}'s elements
|
150
|
+
# @override min(point)
|
151
|
+
# @return [Array<Point>] The element-wise minimum values of the receiver and the given {Point}
|
152
|
+
def minmax(*args)
|
153
|
+
if args.empty?
|
154
|
+
@elements.minmax
|
155
|
+
else
|
156
|
+
[min(*args), max(*args)]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
122
160
|
# @group Accessors
|
123
161
|
# @param [Integer] i Index into the {Point}'s elements
|
124
162
|
# @return [Numeric] Element i (starting at 0)
|
data/lib/geometry/point_iso.rb
CHANGED
@@ -53,6 +53,44 @@ An object repesenting a N-dimensional {Point} with identical elements.
|
|
53
53
|
[]
|
54
54
|
end
|
55
55
|
|
56
|
+
# @override max()
|
57
|
+
# @return [Number] The maximum value of the {Point}'s elements
|
58
|
+
# @override max(point)
|
59
|
+
# @return [Point] The element-wise maximum values of the receiver and the given {Point}
|
60
|
+
def max(*args)
|
61
|
+
if args.empty?
|
62
|
+
@value
|
63
|
+
else
|
64
|
+
args = args.first if 1 == args.size
|
65
|
+
Point[Array.new(args.size, @value).zip(args).map(&:max)]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @override min()
|
70
|
+
# @return [Number] The minimum value of the {Point}'s elements
|
71
|
+
# @override min(point)
|
72
|
+
# @return [Point] The element-wise minimum values of the receiver and the given {Point}
|
73
|
+
def min(*args)
|
74
|
+
if args.empty?
|
75
|
+
@value
|
76
|
+
else
|
77
|
+
args = args.first if 1 == args.size
|
78
|
+
Point[Array.new(args.size, @value).zip(args).map(&:min)]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @override minmax()
|
83
|
+
# @return [Array<Number>] The minimum value of the {Point}'s elements
|
84
|
+
# @override min(point)
|
85
|
+
# @return [Array<Point>] The element-wise minimum values of the receiver and the given {Point}
|
86
|
+
def minmax(*args)
|
87
|
+
if args.empty?
|
88
|
+
[@value, @value]
|
89
|
+
else
|
90
|
+
[min(*args), max(*args)]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
56
94
|
# @group Accessors
|
57
95
|
# @param i [Integer] Index into the {Point}'s elements
|
58
96
|
# @return [Numeric] Element i (starting at 0)
|
data/lib/geometry/point_one.rb
CHANGED
@@ -67,6 +67,44 @@ everything else, regardless of size. It's similar to the
|
|
67
67
|
end
|
68
68
|
# @endgroup
|
69
69
|
|
70
|
+
# @override max()
|
71
|
+
# @return [Number] The maximum value of the {Point}'s elements
|
72
|
+
# @override max(point)
|
73
|
+
# @return [Point] The element-wise maximum values of the receiver and the given {Point}
|
74
|
+
def max(*args)
|
75
|
+
if args.empty?
|
76
|
+
1
|
77
|
+
else
|
78
|
+
args = args.first if 1 == args.size
|
79
|
+
Point[Array.new(args.size, 1).zip(args).map(&:max)]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @override min()
|
84
|
+
# @return [Number] The minimum value of the {Point}'s elements
|
85
|
+
# @override min(point)
|
86
|
+
# @return [Point] The element-wise minimum values of the receiver and the given {Point}
|
87
|
+
def min(*args)
|
88
|
+
if args.empty?
|
89
|
+
1
|
90
|
+
else
|
91
|
+
args = args.first if 1 == args.size
|
92
|
+
Point[Array.new(args.size, 1).zip(args).map(&:min)]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# @override minmax()
|
97
|
+
# @return [Array<Number>] The minimum value of the {Point}'s elements
|
98
|
+
# @override min(point)
|
99
|
+
# @return [Array<Point>] The element-wise minimum values of the receiver and the given {Point}
|
100
|
+
def minmax(*args)
|
101
|
+
if args.empty?
|
102
|
+
[1, 1]
|
103
|
+
else
|
104
|
+
[min(*args), max(*args)]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
70
108
|
# @group Arithmetic
|
71
109
|
|
72
110
|
# @group Unary operators
|
data/lib/geometry/point_zero.rb
CHANGED
@@ -67,6 +67,44 @@ everything else, regardless of size. You can think of it as an application of th
|
|
67
67
|
end
|
68
68
|
# @endgroup
|
69
69
|
|
70
|
+
# @override max()
|
71
|
+
# @return [Number] The maximum value of the {Point}'s elements
|
72
|
+
# @override max(point)
|
73
|
+
# @return [Point] The element-wise maximum values of the receiver and the given {Point}
|
74
|
+
def max(*args)
|
75
|
+
if args.empty?
|
76
|
+
0
|
77
|
+
else
|
78
|
+
args = args.first if 1 == args.size
|
79
|
+
Array.new(args.size, 0).zip(args).map(&:max)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @override min()
|
84
|
+
# @return [Number] The minimum value of the {Point}'s elements
|
85
|
+
# @override min(point)
|
86
|
+
# @return [Point] The element-wise minimum values of the receiver and the given {Point}
|
87
|
+
def min(*args)
|
88
|
+
if args.empty?
|
89
|
+
0
|
90
|
+
else
|
91
|
+
args = args.first if 1 == args.size
|
92
|
+
Array.new(args.size, 0).zip(args).map(&:min)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# @override minmax()
|
97
|
+
# @return [Array<Number>] The minimum value of the {Point}'s elements
|
98
|
+
# @override min(point)
|
99
|
+
# @return [Array<Point>] The element-wise minimum values of the receiver and the given {Point}
|
100
|
+
def minmax(*args)
|
101
|
+
if args.empty?
|
102
|
+
[0, 0]
|
103
|
+
else
|
104
|
+
[min(*args), max(*args)]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
70
108
|
# @group Arithmetic
|
71
109
|
|
72
110
|
# @group Unary operators
|
data/lib/geometry/square.rb
CHANGED
@@ -20,6 +20,11 @@ The {Square} class cluster is like the {Rectangle} class cluster, but not longer
|
|
20
20
|
# @return [Point] The {Square}'s origin
|
21
21
|
attr_reader :origin
|
22
22
|
|
23
|
+
# @!attribute points
|
24
|
+
# @return [Array<Point>] the corner {Point}s of the {Square} in counter-clockwise order
|
25
|
+
attr_reader :points
|
26
|
+
alias :vertices :points
|
27
|
+
|
23
28
|
# @overload new(:origin, :size)
|
24
29
|
# Creates a {Square} with the given origin and size
|
25
30
|
# @option [Point] :origin The lower-left corner
|
@@ -75,6 +80,12 @@ The {Square} class cluster is like the {Rectangle} class cluster, but not longer
|
|
75
80
|
true
|
76
81
|
end
|
77
82
|
|
83
|
+
# @!attribute [r] edges
|
84
|
+
# @return [Array<Edge>] An array of {Edge}s corresponding to the sides of the {Square}
|
85
|
+
def edges
|
86
|
+
(points + [points.first]).each_cons(2).map {|v1,v2| Edge.new v1, v2}
|
87
|
+
end
|
88
|
+
|
78
89
|
# @return [Point] The upper right corner of the bounding {Rectangle}
|
79
90
|
def max
|
80
91
|
@points.last
|
@@ -96,6 +107,11 @@ The {Square} class cluster is like the {Rectangle} class cluster, but not longer
|
|
96
107
|
@points.first
|
97
108
|
end
|
98
109
|
|
110
|
+
def points
|
111
|
+
p0, p1 = *@points
|
112
|
+
[p0, Point[p1.x, p0.y], p1, Point[p0.x, p1.y]]
|
113
|
+
end
|
114
|
+
|
99
115
|
def height
|
100
116
|
min, max = @points.minmax {|a,b| a.y <=> b.y}
|
101
117
|
max.y - min.y
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'geometry/bezier'
|
3
|
+
|
4
|
+
describe Geometry::Bezier do
|
5
|
+
subject { Geometry::Bezier.new [0,0], [1,1], [2,2], [3,3] }
|
6
|
+
|
7
|
+
it 'must have control points' do
|
8
|
+
subject.points.length.must_equal 4
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'must generate Pascals Triangle' do
|
12
|
+
subject.binomial_coefficient(0).must_equal 1
|
13
|
+
subject.binomial_coefficient(1).must_equal 3
|
14
|
+
subject.binomial_coefficient(2).must_equal 3
|
15
|
+
subject.binomial_coefficient(3).must_equal 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'must return nil when t is out of range' do
|
19
|
+
subject[2].must_equal nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'must subscript on the parameter' do
|
23
|
+
subject[0.5].must_equal Point[1.5, 1.5]
|
24
|
+
end
|
25
|
+
end
|
data/test/geometry/edge.rb
CHANGED
@@ -16,6 +16,12 @@ describe Geometry::Edge do
|
|
16
16
|
assert_equal(Geometry::Point[1,0], edge.last)
|
17
17
|
end
|
18
18
|
|
19
|
+
it 'must have a convenience initializer' do
|
20
|
+
edge = Edge([0,0], [1,0])
|
21
|
+
edge.must_be_kind_of Geometry::Edge
|
22
|
+
edge.must_equal Edge.new([0,0], [1,0])
|
23
|
+
end
|
24
|
+
|
19
25
|
it "must handle equality" do
|
20
26
|
edge1 = Edge.new([1,0], [0,1])
|
21
27
|
edge2 = Edge.new([1,0], [0,1])
|
@@ -24,6 +30,10 @@ describe Geometry::Edge do
|
|
24
30
|
edge1.wont_equal edge3
|
25
31
|
end
|
26
32
|
|
33
|
+
it 'must have a length' do
|
34
|
+
Edge.new([0,0], [1,0]).length.must_equal 1
|
35
|
+
end
|
36
|
+
|
27
37
|
it "must return the height of the edge" do
|
28
38
|
edge = Edge([0,0], [1,1])
|
29
39
|
assert_equal(1, edge.height)
|
data/test/geometry/line.rb
CHANGED
@@ -113,6 +113,27 @@ describe Geometry::PointSlopeLine do
|
|
113
113
|
it "must have a slope attribute" do
|
114
114
|
subject.slope.must_equal 3
|
115
115
|
end
|
116
|
+
|
117
|
+
it "must handle equality" do
|
118
|
+
line2 = Geometry::PointSlopeLine.new([1,2], 3)
|
119
|
+
line3 = Geometry::PointSlopeLine.new([1,1], 4)
|
120
|
+
subject.must_equal line2
|
121
|
+
subject.wont_equal line3
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'must handle equality with a SlopeInterceptLine' do
|
125
|
+
line2 = Geometry::SlopeInterceptLine.new(3, -1)
|
126
|
+
line3 = Geometry::SlopeInterceptLine.new(4, -1)
|
127
|
+
line2.must_equal subject
|
128
|
+
line3.wont_equal subject
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'must handle equality with a TwoPointLine' do
|
132
|
+
line2 = Geometry::TwoPointLine.new([1,2], [2,5])
|
133
|
+
line3 = Geometry::TwoPointLine.new([1,2], [2,4])
|
134
|
+
line2.must_equal subject
|
135
|
+
line3.wont_equal subject
|
136
|
+
end
|
116
137
|
end
|
117
138
|
|
118
139
|
describe Geometry::SlopeInterceptLine do
|
@@ -121,6 +142,27 @@ describe Geometry::SlopeInterceptLine do
|
|
121
142
|
it "must have a slope attribute" do
|
122
143
|
subject.slope.must_equal 3
|
123
144
|
end
|
145
|
+
|
146
|
+
it "must handle equality" do
|
147
|
+
line2 = Geometry::SlopeInterceptLine.new(3, 2)
|
148
|
+
line3 = Geometry::SlopeInterceptLine.new(4, 3)
|
149
|
+
subject.must_equal line2
|
150
|
+
subject.wont_equal line3
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'must handle equality with a PointSlopeLine' do
|
154
|
+
line2 = Geometry::PointSlopeLine.new([0,2], 3)
|
155
|
+
line3 = Geometry::PointSlopeLine.new([0,2], 2)
|
156
|
+
line2.must_equal subject
|
157
|
+
line3.wont_equal subject
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'must handle equality with a TwoPointLine' do
|
161
|
+
line2 = Geometry::TwoPointLine.new([0,2], [1,5])
|
162
|
+
line3 = Geometry::TwoPointLine.new([0,2], [1,4])
|
163
|
+
line2.must_equal subject
|
164
|
+
line3.wont_equal subject
|
165
|
+
end
|
124
166
|
end
|
125
167
|
|
126
168
|
describe Geometry::TwoPointLine do
|
@@ -129,4 +171,25 @@ describe Geometry::TwoPointLine do
|
|
129
171
|
it "must have a slope attribute" do
|
130
172
|
subject.slope.must_equal 1
|
131
173
|
end
|
174
|
+
|
175
|
+
it "must handle equality" do
|
176
|
+
line2 = Geometry::TwoPointLine.new([1,2], [3,4])
|
177
|
+
line3 = Geometry::TwoPointLine.new([1,1], [5,5])
|
178
|
+
subject.must_equal line2
|
179
|
+
subject.wont_equal line3
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'must handle equality with a PointSlopeLine' do
|
183
|
+
line2 = Geometry::PointSlopeLine.new([1,2], 1)
|
184
|
+
line3 = Geometry::PointSlopeLine.new([1,2], 2)
|
185
|
+
line2.must_equal subject
|
186
|
+
line3.wont_equal subject
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'must handle equality with a SlopeInterceptLine' do
|
190
|
+
line2 = Geometry::SlopeInterceptLine.new(1, 1)
|
191
|
+
line3 = Geometry::SlopeInterceptLine.new(1, -1)
|
192
|
+
line2.must_equal subject
|
193
|
+
line3.wont_equal subject
|
194
|
+
end
|
132
195
|
end
|
data/test/geometry/point.rb
CHANGED
@@ -114,6 +114,58 @@ describe Geometry::Point do
|
|
114
114
|
Point[1,2].dup.must_equal Point[1,2]
|
115
115
|
end
|
116
116
|
|
117
|
+
describe 'minmax' do
|
118
|
+
it 'must have a minimum' do
|
119
|
+
Point[1,2].min.must_equal 1
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'must minimum with another Point' do
|
123
|
+
Point[1,3].min(Point[4,2]).must_equal Point[1,2]
|
124
|
+
Point[1,3].min(Point[4,2]).must_be_kind_of Point
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'must minimum with an Array' do
|
128
|
+
Point[1,3].min([4,2]).must_equal Point[1,2]
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'must minimum with a multiple arguments' do
|
132
|
+
Point[1,3].min(4,2).must_equal Point[1,2]
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'must have a maximum' do
|
136
|
+
Point[1,2].max.must_equal 2
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'must maximum with another Point' do
|
140
|
+
Point[1,3].max(Point[4,2]).must_equal Point[4,3]
|
141
|
+
Point[1,3].max(Point[4,2]).must_be_kind_of Point
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'must maximum with an Array' do
|
145
|
+
Point[1,3].max([4,2]).must_equal Point[4,3]
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'must maximum with multiple arguments' do
|
149
|
+
Point[1,3].max(4,2).must_equal Point[4,3]
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'must have a minmax' do
|
153
|
+
Point[1,2].minmax.must_equal [1,2]
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'must minmax with another Point' do
|
157
|
+
Point[1,3].minmax(Point[4,2]).must_equal [Point[1,2], Point[4,3]]
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'must minmax with an Array' do
|
161
|
+
Point[1,3].minmax([4,2]).must_equal [Point[1,2], Point[4,3]]
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'must maximum with multiple arguments' do
|
165
|
+
Point[1,3].minmax(4,2).must_equal [Point[1,2], Point[4,3]]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
117
169
|
describe "arithmetic" do
|
118
170
|
let(:left) { Point[1,2] }
|
119
171
|
let(:right) { Point[3,4] }
|
data/test/geometry/point_iso.rb
CHANGED
@@ -5,6 +5,58 @@ describe Geometry::PointIso do
|
|
5
5
|
let(:value) { 5 }
|
6
6
|
subject { Geometry::PointIso.new(5) }
|
7
7
|
|
8
|
+
describe 'minmax' do
|
9
|
+
it 'must have a minimum' do
|
10
|
+
subject.min.must_equal 5
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'must minimum with another Point' do
|
14
|
+
subject.min(Point[4,7]).must_equal Point[4,5]
|
15
|
+
subject.min(Point[4,7]).must_be_kind_of Point
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'must minimum with an Array' do
|
19
|
+
subject.min([4,7]).must_equal Point[4,5]
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'must minimum with a multiple arguments' do
|
23
|
+
subject.min(4,7).must_equal Point[4,5]
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'must have a maximum' do
|
27
|
+
subject.max.must_equal 5
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'must maximum with another Point' do
|
31
|
+
subject.max(Point[7,2]).must_equal Point[7,5]
|
32
|
+
subject.max(Point[7,2]).must_be_kind_of Point
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'must maximum with an Array' do
|
36
|
+
subject.max([7,2]).must_equal Point[7,5]
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'must maximum with multiple arguments' do
|
40
|
+
subject.max(7,2).must_equal Point[7,5]
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'must have a minmax' do
|
44
|
+
subject.minmax.must_equal [5,5]
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'must minmax with another Point' do
|
48
|
+
subject.minmax(Point[7,2]).must_equal [Point[5,2], Point[7,5]]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'must minmax with an Array' do
|
52
|
+
subject.minmax([7,2]).must_equal [Point[5,2], Point[7,5]]
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'must maximum with multiple arguments' do
|
56
|
+
subject.minmax(7,2).must_equal [Point[5,2], Point[7,5]]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
8
60
|
describe 'arithmetic' do
|
9
61
|
let(:left) { Point[1,2] }
|
10
62
|
let(:right) { Point[3,4] }
|
data/test/geometry/point_one.rb
CHANGED
@@ -5,6 +5,58 @@ describe Geometry::PointOne do
|
|
5
5
|
subject { Geometry::PointOne.new }
|
6
6
|
let(:one) { Geometry::PointOne.new }
|
7
7
|
|
8
|
+
describe 'minmax' do
|
9
|
+
it 'must have a minimum' do
|
10
|
+
subject.min.must_equal 1
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'must minimum with another Point' do
|
14
|
+
subject.min(Point[0,7]).must_equal Point[0,1]
|
15
|
+
subject.min(Point[0,7]).must_be_kind_of Point
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'must minimum with an Array' do
|
19
|
+
subject.min([0,7]).must_equal Point[0,1]
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'must minimum with a multiple arguments' do
|
23
|
+
subject.min(0,7).must_equal Point[0,1]
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'must have a maximum' do
|
27
|
+
subject.max.must_equal 1
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'must maximum with another Point' do
|
31
|
+
subject.max(Point[7,0]).must_equal Point[7,1]
|
32
|
+
subject.max(Point[7,0]).must_be_kind_of Point
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'must maximum with an Array' do
|
36
|
+
subject.max([7,0]).must_equal Point[7,1]
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'must maximum with multiple arguments' do
|
40
|
+
subject.max(7,0).must_equal Point[7,1]
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'must have a minmax' do
|
44
|
+
subject.minmax.must_equal [1,1]
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'must minmax with another Point' do
|
48
|
+
subject.minmax(Point[7,0]).must_equal [Point[1,0], Point[7,1]]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'must minmax with an Array' do
|
52
|
+
subject.minmax([7,0]).must_equal [Point[1,0], Point[7,1]]
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'must maximum with multiple arguments' do
|
56
|
+
subject.minmax(7,0).must_equal [Point[1,0], Point[7,1]]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
8
60
|
describe 'arithmetic' do
|
9
61
|
let(:left) { Point[1,2] }
|
10
62
|
let(:right) { Point[3,4] }
|
data/test/geometry/point_zero.rb
CHANGED
@@ -5,6 +5,56 @@ describe Geometry::PointZero do
|
|
5
5
|
subject { Geometry::PointZero.new }
|
6
6
|
let(:zero) { Geometry::PointZero.new }
|
7
7
|
|
8
|
+
describe 'minmax' do
|
9
|
+
it 'must have a minimum' do
|
10
|
+
subject.min.must_equal 0
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'must minimum with another Point' do
|
14
|
+
subject.min(Point[-1,7]).must_equal Point[-1,0]
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'must minimum with an Array' do
|
18
|
+
subject.min([-1,7]).must_equal Point[-1,0]
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'must minimum with a multiple arguments' do
|
22
|
+
subject.min(-1,7).must_equal Point[-1,0]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'must have a maximum' do
|
26
|
+
subject.max.must_equal 0
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'must maximum with another Point' do
|
30
|
+
subject.max(Point[7,-1]).must_equal Point[7,0]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'must maximum with an Array' do
|
34
|
+
subject.max([7,-1]).must_equal Point[7,0]
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'must maximum with multiple arguments' do
|
38
|
+
subject.max(7,-1).must_equal Point[7,0]
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'must have a minmax' do
|
42
|
+
subject.minmax.must_equal [0, 0]
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'must minmax with another Point' do
|
46
|
+
subject.minmax(Point[7,-1]).must_equal [Point[0,-1], Point[7,0]]
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'must minmax with an Array' do
|
50
|
+
subject.minmax([7,-1]).must_equal [Point[0,-1], Point[7,0]]
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'must maximum with multiple arguments' do
|
54
|
+
subject.minmax(7,-1).must_equal [Point[0,-1], Point[7,0]]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
8
58
|
describe "arithmetic" do
|
9
59
|
let(:left) { Point[1,2] }
|
10
60
|
let(:right) { Point[3,4] }
|
data/test/geometry/square.rb
CHANGED
@@ -65,6 +65,10 @@ describe Geometry::Square do
|
|
65
65
|
subject.closed?.must_equal true
|
66
66
|
end
|
67
67
|
|
68
|
+
it 'must have edges' do
|
69
|
+
subject.edges.must_equal [Edge([2,3], [3,3]), Edge.new([3,3], [3,4]), Edge.new([3,4], [2,4]), Edge.new([2,4], [2,3])]
|
70
|
+
end
|
71
|
+
|
68
72
|
it "must have an origin accessor" do
|
69
73
|
subject.origin.must_equal Point[2,3]
|
70
74
|
end
|
@@ -80,6 +84,10 @@ describe Geometry::Square do
|
|
80
84
|
it "must have a min property that returns the lower left corner of the bounding rectangle" do
|
81
85
|
subject.min.must_equal Point[2, 3]
|
82
86
|
end
|
87
|
+
|
88
|
+
it 'must have points' do
|
89
|
+
subject.points.must_equal [Point[2,3], Point[3,3], Point[3,4], Point[2,4]]
|
90
|
+
end
|
83
91
|
end
|
84
92
|
end
|
85
93
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geometry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '6.
|
4
|
+
version: '6.4'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Fosdick
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Geometric primitives and algorithms for Ruby
|
14
14
|
email:
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- lib/geometry.rb
|
28
28
|
- lib/geometry/annulus.rb
|
29
29
|
- lib/geometry/arc.rb
|
30
|
+
- lib/geometry/bezier.rb
|
30
31
|
- lib/geometry/circle.rb
|
31
32
|
- lib/geometry/cluster_factory.rb
|
32
33
|
- lib/geometry/edge.rb
|
@@ -53,6 +54,7 @@ files:
|
|
53
54
|
- test/geometry.rb
|
54
55
|
- test/geometry/annulus.rb
|
55
56
|
- test/geometry/arc.rb
|
57
|
+
- test/geometry/bezier.rb
|
56
58
|
- test/geometry/circle.rb
|
57
59
|
- test/geometry/edge.rb
|
58
60
|
- test/geometry/line.rb
|
@@ -86,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
88
|
requirements:
|
87
89
|
- - '>='
|
88
90
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
91
|
+
version: '2.0'
|
90
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
93
|
requirements:
|
92
94
|
- - '>='
|
@@ -102,6 +104,7 @@ test_files:
|
|
102
104
|
- test/geometry.rb
|
103
105
|
- test/geometry/annulus.rb
|
104
106
|
- test/geometry/arc.rb
|
107
|
+
- test/geometry/bezier.rb
|
105
108
|
- test/geometry/circle.rb
|
106
109
|
- test/geometry/edge.rb
|
107
110
|
- test/geometry/line.rb
|