geometry 6 → 6.1
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 +7 -0
- data/README.markdown +6 -1
- data/Rakefile +0 -1
- data/geometry.gemspec +1 -1
- data/lib/geometry/line.rb +14 -3
- data/lib/geometry/point.rb +13 -7
- data/lib/geometry/point_zero.rb +5 -4
- data/lib/geometry/polygon.rb +33 -27
- data/lib/geometry/polyline.rb +129 -23
- data/lib/geometry/rectangle.rb +35 -0
- data/lib/geometry/regular_polygon.rb +35 -3
- data/lib/geometry/rotation.rb +115 -3
- data/lib/geometry/size_zero.rb +2 -1
- data/lib/geometry/transformation.rb +72 -19
- data/lib/geometry/transformation/composition.rb +39 -0
- data/test/geometry/edge.rb +14 -7
- data/test/geometry/line.rb +24 -0
- data/test/geometry/point.rb +55 -14
- data/test/geometry/polygon.rb +28 -6
- data/test/geometry/polyline.rb +176 -0
- data/test/geometry/rectangle.rb +18 -1
- data/test/geometry/regular_polygon.rb +38 -2
- data/test/geometry/rotation.rb +60 -0
- data/test/geometry/transformation.rb +76 -10
- data/test/geometry/transformation/composition.rb +49 -0
- metadata +10 -15
data/test/geometry/edge.rb
CHANGED
@@ -7,6 +7,7 @@ end
|
|
7
7
|
|
8
8
|
describe Geometry::Edge do
|
9
9
|
Edge = Geometry::Edge
|
10
|
+
subject { Geometry::Edge.new [0,0], [1,1] }
|
10
11
|
|
11
12
|
it "must create an Edge object" do
|
12
13
|
edge = Edge.new([0,0], [1,0])
|
@@ -14,13 +15,7 @@ describe Geometry::Edge do
|
|
14
15
|
assert_equal(Geometry::Point[0,0], edge.first)
|
15
16
|
assert_equal(Geometry::Point[1,0], edge.last)
|
16
17
|
end
|
17
|
-
|
18
|
-
edge = Edge.new([0,0], [1,0])
|
19
|
-
assert_kind_of(Edge, edge)
|
20
|
-
edge.reverse!
|
21
|
-
assert_equal(Geometry::Point[1,0], edge.first)
|
22
|
-
assert_equal(Geometry::Point[0,0], edge.last)
|
23
|
-
end
|
18
|
+
|
24
19
|
it "must handle equality" do
|
25
20
|
edge1 = Edge.new([1,0], [0,1])
|
26
21
|
edge2 = Edge.new([1,0], [0,1])
|
@@ -58,6 +53,18 @@ describe Geometry::Edge do
|
|
58
53
|
Edge.new([0,0], [2,0]).parallel?(Edge.new([1,-1], [1,1])).must_equal false
|
59
54
|
end
|
60
55
|
|
56
|
+
it "must clone and reverse" do
|
57
|
+
reversed = subject.reverse
|
58
|
+
reversed.to_a.must_equal subject.to_a.reverse
|
59
|
+
reversed.wont_be_same_as subject
|
60
|
+
end
|
61
|
+
|
62
|
+
it "must reverse itself" do
|
63
|
+
original = subject.to_a
|
64
|
+
subject.reverse!
|
65
|
+
subject.to_a.must_equal original.reverse
|
66
|
+
end
|
67
|
+
|
61
68
|
describe "spaceship" do
|
62
69
|
it "ascending with a Point" do
|
63
70
|
edge = Edge.new [0,0], [1,1]
|
data/test/geometry/line.rb
CHANGED
@@ -106,3 +106,27 @@ describe Geometry::Line do
|
|
106
106
|
assert_equal('Line(Point[0, 0], Point[10, 10])', line.to_s)
|
107
107
|
end
|
108
108
|
end
|
109
|
+
|
110
|
+
describe Geometry::PointSlopeLine do
|
111
|
+
subject { Geometry::PointSlopeLine.new [1,2], 3 }
|
112
|
+
|
113
|
+
it "must have a slope attribute" do
|
114
|
+
subject.slope.must_equal 3
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe Geometry::SlopeInterceptLine do
|
119
|
+
subject { Geometry::SlopeInterceptLine.new 3, 2 }
|
120
|
+
|
121
|
+
it "must have a slope attribute" do
|
122
|
+
subject.slope.must_equal 3
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe Geometry::TwoPointLine do
|
127
|
+
subject { Geometry::TwoPointLine.new [1,2], [3,4] }
|
128
|
+
|
129
|
+
it "must have a slope attribute" do
|
130
|
+
subject.slope.must_equal 1
|
131
|
+
end
|
132
|
+
end
|
data/test/geometry/point.rb
CHANGED
@@ -2,6 +2,18 @@ require 'minitest/autorun'
|
|
2
2
|
require 'geometry/point'
|
3
3
|
|
4
4
|
describe Geometry::Point do
|
5
|
+
PointZero = Geometry::PointZero
|
6
|
+
|
7
|
+
describe "class methods" do
|
8
|
+
it "must generate a PointZero" do
|
9
|
+
Point.zero.must_be_instance_of(PointZero)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "must generate a Point full of zeros" do
|
13
|
+
Point.zero(3).must_equal Point[0,0,0]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
5
17
|
describe "constructor" do
|
6
18
|
it "must return the Point when constructed from a Point" do
|
7
19
|
original_point = Point[3,4]
|
@@ -19,12 +31,6 @@ describe Geometry::Point do
|
|
19
31
|
end
|
20
32
|
end
|
21
33
|
|
22
|
-
it "create a Point object using list syntax" do
|
23
|
-
point = Geometry::Point[2,1]
|
24
|
-
assert_equal(2, point.size)
|
25
|
-
assert_equal(2, point.x)
|
26
|
-
assert_equal(1, point.y)
|
27
|
-
end
|
28
34
|
it "create a Point object from an array" do
|
29
35
|
point = Geometry::Point[[3,4]]
|
30
36
|
assert_equal(2, point.size)
|
@@ -44,12 +50,6 @@ describe Geometry::Point do
|
|
44
50
|
assert_equal(4, point.y)
|
45
51
|
end
|
46
52
|
|
47
|
-
it "create a Point object from a Vector using list syntax" do
|
48
|
-
point = Geometry::Point[Vector[3,4]]
|
49
|
-
assert_equal(2, point.size)
|
50
|
-
assert_equal(3, point.x)
|
51
|
-
assert_equal(4, point.y)
|
52
|
-
end
|
53
53
|
it "create a Point object from a Point using list syntax" do
|
54
54
|
point = Geometry::Point[Geometry::Point[13,14]]
|
55
55
|
assert_equal(2, point.size)
|
@@ -85,6 +85,16 @@ describe Geometry::Point do
|
|
85
85
|
Point[1,2][2].must_equal nil
|
86
86
|
end
|
87
87
|
|
88
|
+
it "must clone" do
|
89
|
+
Point[1,2].clone.must_be_instance_of(Point)
|
90
|
+
Point[1,2].clone.must_equal Point[1,2]
|
91
|
+
end
|
92
|
+
|
93
|
+
it "must duplicate" do
|
94
|
+
Point[1,2].dup.must_be_instance_of(Point)
|
95
|
+
Point[1,2].dup.must_equal Point[1,2]
|
96
|
+
end
|
97
|
+
|
88
98
|
describe "arithmetic" do
|
89
99
|
let(:left) { Point[1,2] }
|
90
100
|
let(:right) { Point[3,4] }
|
@@ -122,6 +132,13 @@ describe Geometry::Point do
|
|
122
132
|
(Vector[5,6] + right).must_equal Vector[8,10]
|
123
133
|
end
|
124
134
|
|
135
|
+
it "must return self when adding a PointZero" do
|
136
|
+
(left + Point.zero).must_equal left
|
137
|
+
end
|
138
|
+
|
139
|
+
it "must return self when adding a NilClass" do
|
140
|
+
(left + nil).must_equal left
|
141
|
+
end
|
125
142
|
end
|
126
143
|
|
127
144
|
describe "when subtracting" do
|
@@ -141,16 +158,40 @@ describe Geometry::Point do
|
|
141
158
|
it "must raise an exception when subtracting mismatched sizes" do
|
142
159
|
lambda { left - [1,2,3,4] }.must_raise Geometry::DimensionMismatch
|
143
160
|
end
|
161
|
+
|
162
|
+
it "must return self when subtracting a PointZero" do
|
163
|
+
(left - Point.zero).must_equal left
|
164
|
+
end
|
165
|
+
|
166
|
+
it "must return self when subtracting a NilClass" do
|
167
|
+
(left - nil).must_equal left
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "when multiplying" do
|
172
|
+
it "must return a Point when multiplied by a Matrix" do
|
173
|
+
(Matrix[[1,2],[3,4]]*Point[5,6]).must_equal Point[17, 39]
|
174
|
+
end
|
144
175
|
end
|
145
176
|
end
|
146
177
|
|
147
178
|
describe "coercion" do
|
179
|
+
subject { Point[1,2] }
|
180
|
+
|
148
181
|
it "must coerce Arrays into Points" do
|
149
|
-
|
182
|
+
subject.coerce([3,4]).must_equal [Point[3,4], subject]
|
150
183
|
end
|
151
184
|
|
152
185
|
it "must coerce Vectors into Points" do
|
153
|
-
|
186
|
+
subject.coerce(Vector[3,4]).must_equal [Point[3,4], subject]
|
187
|
+
end
|
188
|
+
|
189
|
+
it "must coerce a Numeric into a Point" do
|
190
|
+
subject.coerce(42).must_equal [Point[42,42], subject]
|
191
|
+
end
|
192
|
+
|
193
|
+
it "must reject anything that can't be coerced" do
|
194
|
+
-> { subject.coerce(NilClass) }.must_raise TypeError
|
154
195
|
end
|
155
196
|
end
|
156
197
|
|
data/test/geometry/polygon.rb
CHANGED
@@ -6,6 +6,8 @@ describe Geometry::Polygon do
|
|
6
6
|
|
7
7
|
let(:cw_unit_square) { Polygon.new [0,0], [0,1], [1,1], [1,0] }
|
8
8
|
let(:unit_square) { Polygon.new [0,0], [1,0], [1,1], [0,1] }
|
9
|
+
let(:simple_concave) { Polygon.new [0,0], [4,0], [4,2], [3,2], [3,1], [1,1], [1,2], [0,2] }
|
10
|
+
subject { unit_square }
|
9
11
|
|
10
12
|
it "must create a Polygon object with no arguments" do
|
11
13
|
polygon = Geometry::Polygon.new
|
@@ -46,8 +48,7 @@ describe Geometry::Polygon do
|
|
46
48
|
end
|
47
49
|
|
48
50
|
it "must create closed polygons" do
|
49
|
-
|
50
|
-
assert_equal(polygon.edges.first.first, polygon.edges.last.last)
|
51
|
+
subject.closed?.must_equal true
|
51
52
|
end
|
52
53
|
|
53
54
|
it "must handle already closed polygons" do
|
@@ -58,13 +59,20 @@ describe Geometry::Polygon do
|
|
58
59
|
assert_equal(polygon.edges.first.first, polygon.edges.last.last)
|
59
60
|
end
|
60
61
|
|
62
|
+
it "must return itself on close" do
|
63
|
+
closed = subject.close
|
64
|
+
closed.closed?.must_equal true
|
65
|
+
closed.must_equal subject
|
66
|
+
closed.must_be_same_as subject
|
67
|
+
end
|
68
|
+
|
61
69
|
describe "orientation" do
|
62
70
|
it "must return true for clockwise" do
|
63
71
|
Polygon.new([0,0], [0,1], [1,1], [1,0]).clockwise?.must_equal true
|
64
72
|
Polygon.new([1,1], [1,3], [3,3], [3,1]).clockwise?.must_equal true
|
65
73
|
end
|
66
74
|
|
67
|
-
it "must return
|
75
|
+
it "must return false for counterclockwise" do
|
68
76
|
Polygon.new([0,0], [1,0], [1,1], [0,1]).clockwise?.must_equal false
|
69
77
|
Polygon.new([1,1], [3,1], [3,3], [1,3]).clockwise?.must_equal false
|
70
78
|
end
|
@@ -102,6 +110,12 @@ describe Geometry::Polygon do
|
|
102
110
|
convex_hull.vertices.must_equal [[0,0], [0,1], [2,1], [2,0], [1,-1]].map {|a| Point[*a]}
|
103
111
|
end
|
104
112
|
|
113
|
+
it "must generate spokes" do
|
114
|
+
unit_square.spokes.must_equal [Vector[-1,-1], Vector[1,-1], Vector[1,1], Vector[-1,1]]
|
115
|
+
cw_unit_square.spokes.must_equal [Vector[-1,-1], Vector[-1,1], Vector[1,1], Vector[1,-1]]
|
116
|
+
simple_concave.spokes.must_equal [Vector[-1,-1], Vector[1,-1], Vector[1,1], Vector[-1,1], Vector[-1,1], Vector[1,1], Vector[1,1], Vector[-1,1]]
|
117
|
+
end
|
118
|
+
|
105
119
|
describe "spaceship" do
|
106
120
|
it "with a Point" do
|
107
121
|
(unit_square <=> Point[2,0]).must_equal -1
|
@@ -140,10 +154,9 @@ describe Geometry::Polygon do
|
|
140
154
|
end
|
141
155
|
|
142
156
|
it "must outset a concave polygon with multiply-intersecting edges" do
|
143
|
-
skip
|
144
157
|
concave_polygon = Polygon.new [0,0], [5,0], [5,2], [4,2], [4,1], [3,1], [3,2], [2,2], [2,1], [1,1], [1,2], [0,2]
|
145
|
-
outset_polygon = concave_polygon.outset(
|
146
|
-
outset_polygon.must_equal Polygon.new [-
|
158
|
+
outset_polygon = concave_polygon.outset(1)
|
159
|
+
outset_polygon.must_equal Polygon.new [-1,-1], [6,-1], [6,3], [-1,3]
|
147
160
|
end
|
148
161
|
|
149
162
|
it "must outset a concave polygon where the first outset edge intersects with the last outset edge" do
|
@@ -151,6 +164,15 @@ describe Geometry::Polygon do
|
|
151
164
|
polygon.edges.count.must_equal 8
|
152
165
|
polygon.outset(1).must_equal Polygon.new [3, 0], [3, 3], [-2, 3], [-2, -2], [3, -2]
|
153
166
|
end
|
167
|
+
|
168
|
+
# Naturally, this test is very sensitive to the input coordinate values. This is a painfully contrived example that
|
169
|
+
# checks for sensitivity to edges that are very close to horizontal, but not quite.
|
170
|
+
# When the test fails, the first point of the offset polygon is at [0,-1]
|
171
|
+
it "must not be sensitive to floating point rounding errors" do
|
172
|
+
polygon = Polygon.new [0, 0], [0, -2], [10, -2], [10, 10], [-100, 10], [-100, -22], [-69, -22], [-69, 3.552713678800501e-15], [0,0]
|
173
|
+
outset = polygon.outset(1)
|
174
|
+
outset.edges.first.first.must_equal Geometry::Point[-1,-1]
|
175
|
+
end
|
154
176
|
end
|
155
177
|
|
156
178
|
describe "set operations" do
|
data/test/geometry/polyline.rb
CHANGED
@@ -4,7 +4,9 @@ require 'geometry/polyline'
|
|
4
4
|
describe Geometry::Polyline do
|
5
5
|
Polyline = Geometry::Polyline
|
6
6
|
|
7
|
+
let(:closed_unit_square) { Polyline.new [0,0], [1,0], [1,1], [0,1], [0,0] }
|
7
8
|
let(:unit_square) { Polyline.new [0,0], [1,0], [1,1], [0,1] }
|
9
|
+
let(:reverse_unit_square) { Polyline.new [0,1], [1,1], [1,0], [0,0] }
|
8
10
|
|
9
11
|
it "must create a Polyline object with no arguments" do
|
10
12
|
polyline = Geometry::Polyline.new
|
@@ -20,6 +22,154 @@ describe Geometry::Polyline do
|
|
20
22
|
polyline.vertices.count.must_equal 4
|
21
23
|
end
|
22
24
|
|
25
|
+
describe "when the Polyline is closed" do
|
26
|
+
let(:closed_concave_polyline) { Polyline.new [-2,0], [0,0], [0,-2], [2,-2], [2,2], [-2,2], [-2,0] }
|
27
|
+
subject { closed_concave_polyline }
|
28
|
+
|
29
|
+
it "must be closed" do
|
30
|
+
closed_unit_square.closed?.must_equal true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must clone and close" do
|
34
|
+
closed = subject.close
|
35
|
+
closed.closed?.must_equal true
|
36
|
+
closed.must_equal subject
|
37
|
+
closed.wont_be_same_as subject
|
38
|
+
end
|
39
|
+
|
40
|
+
it "must be able to close itself" do
|
41
|
+
subject.close!
|
42
|
+
subject.closed?.must_equal true
|
43
|
+
subject.must_equal subject
|
44
|
+
end
|
45
|
+
|
46
|
+
it "must clone and reverse" do
|
47
|
+
vertices = subject.vertices
|
48
|
+
vertices.push vertices.shift
|
49
|
+
reversed = subject.reverse
|
50
|
+
reversed.vertices.must_equal vertices.reverse
|
51
|
+
reversed.wont_be_same_as subject
|
52
|
+
reversed.closed?.must_equal true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "must reverse itself" do
|
56
|
+
original = subject.vertices.dup
|
57
|
+
subject.reverse!
|
58
|
+
subject.vertices.to_a.must_equal original.reverse
|
59
|
+
subject.closed?.must_equal true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "must generate bisectors" do
|
63
|
+
closed_unit_square.bisectors.must_equal [Vector[1, 1], Vector[-1, 1], Vector[-1, -1], Vector[1, -1]]
|
64
|
+
end
|
65
|
+
|
66
|
+
it "must generate bisectors with an inside corner" do
|
67
|
+
closed_concave_polyline.bisectors.must_equal [Vector[1,1], Vector[-1,-1], Vector[1,1], Vector[-1,1], Vector[-1,-1], Vector[1,-1]]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "must generate left bisectors" do
|
71
|
+
closed_unit_square.left_bisectors.must_equal [Vector[1, 1], Vector[-1, 1], Vector[-1, -1], Vector[1, -1]]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "must generate left bisectors with an inside corner" do
|
75
|
+
closed_concave_polyline.left_bisectors.must_equal [Vector[1,1], Vector[1,1], Vector[1,1], Vector[-1,1], Vector[-1,-1], Vector[1,-1]]
|
76
|
+
end
|
77
|
+
|
78
|
+
it "must generate right bisectors" do
|
79
|
+
closed_unit_square.right_bisectors.must_equal [Vector[-1,-1], Vector[1,-1], Vector[1,1], Vector[-1,1]]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "must generate right bisectors with an inside corner" do
|
83
|
+
closed_concave_polyline.right_bisectors.must_equal [Vector[-1,-1], Vector[-1,-1], Vector[-1,-1], Vector[1,-1], Vector[1,1], Vector[-1,1]]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "must generate spokes" do
|
87
|
+
closed_unit_square.spokes.must_equal [Vector[-1,-1], Vector[1,-1], Vector[1,1], Vector[-1,1]]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "must rightset a closed concave polyline where the first outset edge intersects with the last outset edge" do
|
91
|
+
skip
|
92
|
+
polyline = Polyline.new [0,0], [0,1], [2,1], [2,2], [-1,2], [-1,-1], [2,-1], [2,0], [0,0]
|
93
|
+
polyline.offset(-1).must_equal Polyline.new [1, 0], [3, 0], [3, 3], [-2, 3], [-2, -2], [3, -2]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "when the Polyline is open" do
|
98
|
+
let(:concave_polyline) { Polyline.new [-2,0], [0,0], [0,-2], [2,-2], [2,2], [-2,2] }
|
99
|
+
subject { concave_polyline }
|
100
|
+
|
101
|
+
it "must not be closed" do
|
102
|
+
unit_square.closed?.must_equal false
|
103
|
+
end
|
104
|
+
|
105
|
+
it "must clone and close" do
|
106
|
+
closed = subject.close
|
107
|
+
closed.closed?.must_equal true
|
108
|
+
closed.must_equal subject
|
109
|
+
closed.wont_be_same_as subject
|
110
|
+
end
|
111
|
+
|
112
|
+
it "must be able to close it" do
|
113
|
+
closed = subject.close!
|
114
|
+
closed.closed?.must_equal true
|
115
|
+
closed.must_equal subject
|
116
|
+
closed.must_be_same_as subject
|
117
|
+
end
|
118
|
+
|
119
|
+
it "must clone and reverse" do
|
120
|
+
reversed = subject.reverse
|
121
|
+
reversed.vertices.must_equal subject.vertices.reverse
|
122
|
+
reversed.wont_be_same_as subject
|
123
|
+
reversed.closed?.wont_equal true
|
124
|
+
end
|
125
|
+
|
126
|
+
it "must reverse itself" do
|
127
|
+
original = subject.vertices.dup
|
128
|
+
subject.reverse!
|
129
|
+
subject.vertices.to_a.must_equal original.reverse
|
130
|
+
subject.closed?.wont_equal true
|
131
|
+
end
|
132
|
+
|
133
|
+
it "must generate bisectors" do
|
134
|
+
unit_square.bisectors.must_equal [Vector[0, 1], Vector[-1, 1], Vector[-1, -1], Vector[0, -1]]
|
135
|
+
end
|
136
|
+
|
137
|
+
it "must generate bisectors with an inside corner" do
|
138
|
+
concave_polyline.bisectors.must_equal [Vector[0,1], Vector[-1,-1], Vector[1,1], Vector[-1,1], Vector[-1,-1], Vector[0,-1]]
|
139
|
+
end
|
140
|
+
|
141
|
+
it "must generate left bisectors" do
|
142
|
+
unit_square.left_bisectors.must_equal [Vector[0, 1], Vector[-1, 1], Vector[-1, -1], Vector[0, -1]]
|
143
|
+
reverse_unit_square.left_bisectors.must_equal [Vector[0, 1], Vector[1, 1], Vector[1, -1], Vector[0, -1]]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "must generate right bisectors" do
|
147
|
+
unit_square.right_bisectors.must_equal [Vector[0,-1], Vector[1,-1], Vector[1,1], Vector[0,1]]
|
148
|
+
end
|
149
|
+
|
150
|
+
it "must generate right bisectors with an inside corner" do
|
151
|
+
concave_polyline.right_bisectors.must_equal [Vector[0,-1], Vector[-1,-1], Vector[-1,-1], Vector[1,-1], Vector[1,1], Vector[0,1]]
|
152
|
+
end
|
153
|
+
|
154
|
+
it "must generate left bisectors with an inside corner" do
|
155
|
+
concave_polyline.left_bisectors.must_equal [Vector[0,1], Vector[1,1], Vector[1,1], Vector[-1,1], Vector[-1,-1], Vector[0,-1]]
|
156
|
+
end
|
157
|
+
|
158
|
+
it "must generate spokes" do
|
159
|
+
unit_square.spokes.must_equal [Vector[0,-1], Vector[1,-1], Vector[1,1], Vector[0,1]]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "when checking for closedness" do
|
164
|
+
it "must be closed when it is closed" do
|
165
|
+
closed_unit_square.closed?.must_equal true
|
166
|
+
end
|
167
|
+
|
168
|
+
it "must not be closed when it is not closed" do
|
169
|
+
unit_square.closed?.must_equal false
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
23
173
|
describe "when creating a Polyline from an array of Points" do
|
24
174
|
it "must ignore repeated Points" do
|
25
175
|
polyline = Geometry::Polyline.new([0,0], [1,0], [1,1], [1,1], [0,1])
|
@@ -42,6 +192,12 @@ describe Geometry::Polyline do
|
|
42
192
|
(unit_square.eql? unit_square).must_equal true
|
43
193
|
end
|
44
194
|
|
195
|
+
it "must rightset a closed concave polyline where the first outset edge intersects with the last outset edge" do
|
196
|
+
skip
|
197
|
+
polyline = Polyline.new [0,0], [0,1], [2,1], [2,2], [-1,2], [-1,-1], [2,-1], [2,0], [0,0]
|
198
|
+
polyline.offset(-1).must_equal Polyline.new [1, 0], [3, 0], [3, 3], [-2, 3], [-2, -2], [3, -2]
|
199
|
+
end
|
200
|
+
|
45
201
|
describe "when offsetting" do
|
46
202
|
describe "with a positive offset" do
|
47
203
|
it "must leftset a unit square" do
|
@@ -85,6 +241,26 @@ describe Geometry::Polyline do
|
|
85
241
|
offset_polyline = concave_polyline.offset(-2)
|
86
242
|
offset_polyline.must_equal Polyline.new [0,-2], [7,-2], [7,4], [0,4]
|
87
243
|
end
|
244
|
+
|
245
|
+
it "must rightset a closed concave polyline with multiply-intersecting edges" do
|
246
|
+
concave_polyline = Polyline.new [0,0], [5,0], [5,2], [4,2], [4,1], [3,1], [3,2], [2,2], [2,1], [1,1], [1,2], [0,2], [0,0]
|
247
|
+
offset_polyline = concave_polyline.offset(-2)
|
248
|
+
offset_polyline.must_equal Polyline.new [-2,-2], [7,-2], [7,4], [-2,4]
|
249
|
+
end
|
250
|
+
|
251
|
+
it "must rightset a concave polyline where the first outset edge intersects with the last outset edge" do
|
252
|
+
polyline = Polyline.new [0,0], [0,1], [2,1], [2,2], [-1,2], [-1,-1], [2,-1], [2,0]
|
253
|
+
polyline.offset(-1).must_equal Polyline.new [1, 0], [3, 0], [3, 3], [-2, 3], [-2, -2], [3, -2]
|
254
|
+
end
|
255
|
+
|
256
|
+
# Naturally, this test is very sensitive to the input coordinate values. This is a painfully contrived example that
|
257
|
+
# checks for sensitivity to edges that are very close to horizontal, but not quite.
|
258
|
+
# When the test fails, the first point of the offset polyline is at [0,-1]
|
259
|
+
it "must not be sensitive to floating point rounding errors" do
|
260
|
+
polyline = Polyline.new [0, 0], [0, -2], [10, -2], [10, 10], [-100, 10], [-100, -22], [-69, -22], [-69, 3.552713678800501e-15], [0,0]
|
261
|
+
outset = polyline.offset(-1)
|
262
|
+
outset.edges.first.first.must_equal Geometry::Point[-1,-1]
|
263
|
+
end
|
88
264
|
end
|
89
265
|
end
|
90
266
|
end
|