geometry 6.3 → 6.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d541e7a304b060e2bc7bd392a4530fbfca3040b
4
- data.tar.gz: 9f4bfc263c8c735ed3d28a6f32f676e0fe3606c4
3
+ metadata.gz: 2071223ccd3ab40dc84675595b6b204aa0d7df10
4
+ data.tar.gz: ed6b83fe034a5734ca9938c7ab54f4a4fcdda736
5
5
  SHA512:
6
- metadata.gz: e9e952e9d9c66085aa7fc2852605467b8456ab30ac05d6106f5593b183d6ff865ec812d47d63f32f01b280838b51d93ec58811542b1adfc5b6632b9c57cfc7fc
7
- data.tar.gz: 53f91b55768b360e9f07026228b150d7f250dc866098eba904b01ad0ee4d2cf9eb693399ec95a44b1a5ab6640ec8898ce68690e9971e5500456ad06db6a84b7d
6
+ metadata.gz: d78e31275dd418edf77a9067157213b63690bb7139ccf8c8561b228bb51e7be9e8b72e3a9187f02beb8d77ddc95427f882454f49c4827196f832acadec131e3c
7
+ data.tar.gz: ba61dfc2b8d83090d59fe4d5348d3b184edeca188bf59c604d6888644a7392b943f8979ac4ea82392b1d8a026b53873128bab58c64702af149c3be68c1852057
@@ -1,4 +1,12 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.0.0
4
- - 2.1.1
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
@@ -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
@@ -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.3'
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
- # specify any dependencies here; for example:
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
@@ -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
- Edge.new(@last, @first)
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
@@ -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}
@@ -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)
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -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] }
@@ -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] }
@@ -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] }
@@ -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] }
@@ -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.3'
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-08-17 00:00:00.000000000 Z
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