geometry 6.3 → 6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|