triangular 0.0.2 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +30 -0
  3. data/.gitignore +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +31 -0
  6. data/Gemfile +3 -1
  7. data/Gemfile.lock +51 -12
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +98 -0
  10. data/examples/slice_example.rb +6 -4
  11. data/lib/triangular/facet.rb +32 -42
  12. data/lib/triangular/line.rb +42 -16
  13. data/lib/triangular/point.rb +15 -15
  14. data/lib/triangular/polyline.rb +13 -12
  15. data/lib/triangular/ray.rb +41 -0
  16. data/lib/triangular/solid.rb +34 -36
  17. data/lib/triangular/units.rb +18 -14
  18. data/lib/triangular/vector.rb +8 -1
  19. data/lib/triangular/version.rb +3 -1
  20. data/lib/triangular/vertex.rb +17 -16
  21. data/lib/triangular.rb +4 -1
  22. data/spec/benchmark/benchmark_spec.rb +23 -0
  23. data/spec/profile/profile_spec.rb +20 -0
  24. data/spec/spec_helper.rb +17 -3
  25. data/spec/triangular/facet_spec.rb +235 -0
  26. data/spec/triangular/line_spec.rb +285 -0
  27. data/spec/triangular/point_spec.rb +108 -0
  28. data/spec/triangular/polyline_spec.rb +22 -0
  29. data/spec/triangular/ray_spec.rb +63 -0
  30. data/spec/{solid_spec.rb → triangular/solid_spec.rb} +71 -70
  31. data/spec/triangular/triangular_spec.rb +24 -0
  32. data/spec/triangular/units_spec.rb +77 -0
  33. data/spec/triangular/vector_spec.rb +23 -0
  34. data/spec/triangular/vertex_spec.rb +46 -0
  35. data/triangular.gemspec +22 -18
  36. metadata +114 -65
  37. data/README.rdoc +0 -64
  38. data/Rakefile +0 -11
  39. data/spec/facet_spec.rb +0 -233
  40. data/spec/line_spec.rb +0 -108
  41. data/spec/point_spec.rb +0 -88
  42. data/spec/polyline_spec.rb +0 -20
  43. data/spec/triangular_spec.rb +0 -22
  44. data/spec/units_spec.rb +0 -75
  45. data/spec/vertex_spec.rb +0 -44
@@ -0,0 +1,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Triangular::Line do
6
+ describe '#intersects_z?' do
7
+ context 'for a line that intersects the target Z plane' do
8
+ context 'with a positive Z vector' do
9
+ before do
10
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 0.0), Triangular::Vertex.new(0.0, 0.0, 6.0))
11
+ end
12
+
13
+ it 'should return true' do
14
+ expect(@line.intersects_z?(3.0)).to be true
15
+ end
16
+ end
17
+
18
+ context 'with a negative Z vector' do
19
+ before do
20
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 6.0), Triangular::Vertex.new(0.0, 0.0, 0.0))
21
+ end
22
+
23
+ it 'should return true' do
24
+ expect(@line.intersects_z?(3.0)).to be true
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'for a line that does not intersect the target Z plane' do
30
+ before do
31
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 4.0), Triangular::Vertex.new(0.0, 0.0, 6.0))
32
+ end
33
+
34
+ it 'should return false' do
35
+ expect(@line.intersects_z?(3.0)).to be false
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#intersection_at_z' do
41
+ context 'for a line that intersects the target Z plane' do
42
+ context 'and spans both positive and negative space' do
43
+ context 'with a positive Z vector' do
44
+ before do
45
+ @line = Triangular::Line.new(Triangular::Vertex.new(-1.0, 1.0, 1.0), Triangular::Vertex.new(1.0, 1.0, -1.0))
46
+ end
47
+
48
+ it 'should return a Point representing the intersection' do
49
+ expect(@line.intersection_at_z(0).x).to eq(0.0)
50
+ expect(@line.intersection_at_z(0).y).to eq(1.0)
51
+ expect(@line.intersection_at_z(0).z).to eq(0.0)
52
+ end
53
+ end
54
+
55
+ context 'with a negative Z vector' do
56
+ before do
57
+ @line = Triangular::Line.new(Triangular::Vertex.new(1.0, 1.0, 1.0),
58
+ Triangular::Vertex.new(-1.0, -1.0, -1.0))
59
+ end
60
+
61
+ it 'should return a Point representing the intersection' do
62
+ expect(@line.intersection_at_z(0).x).to eq(0)
63
+ expect(@line.intersection_at_z(0).y).to eq(0)
64
+ expect(@line.intersection_at_z(0).z).to eq(0)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'for a line that lies on the target Z plane' do
71
+ before do
72
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 3.0), Triangular::Vertex.new(0.0, 6.0, 3.0))
73
+ end
74
+
75
+ it 'should raise an error' do
76
+ expect(@line.intersection_at_z(3.0)).to eq(nil)
77
+ end
78
+ end
79
+
80
+ context 'for a line that does not intersect the target Z plane' do
81
+ before do
82
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 4.0), Triangular::Vertex.new(0.0, 0.0, 6.0))
83
+ end
84
+
85
+ it 'should return nil' do
86
+ expect(@line.intersection_at_z(3.0)).to be_nil
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#intersects_x?' do
92
+ context 'for a line that intersects the target X plane' do
93
+ context 'with a positive X vector' do
94
+ before do
95
+ @line = Triangular::Line.new(Triangular::Vertex.new(2.0, 0.0, 0.0), Triangular::Vertex.new(3.2, 0.0, 6.0))
96
+ end
97
+
98
+ it 'should return true' do
99
+ expect(@line.intersects_x?(3.0)).to be true
100
+ end
101
+ end
102
+
103
+ context 'with a negative X vector' do
104
+ before do
105
+ @line = Triangular::Line.new(Triangular::Vertex.new(3.2, 0.0, 6.0), Triangular::Vertex.new(2.0, 0.0, 0.0))
106
+ end
107
+
108
+ it 'should return true' do
109
+ expect(@line.intersects_x?(3.0)).to be true
110
+ end
111
+ end
112
+ end
113
+
114
+ context 'for a line that does not intersect the target X plane' do
115
+ before do
116
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 4.0), Triangular::Vertex.new(0.0, 0.0, 6.0))
117
+ end
118
+
119
+ it 'should return false' do
120
+ expect(@line.intersects_x?(3.0)).to be false
121
+ end
122
+ end
123
+ end
124
+
125
+ describe '#intersection_at_x' do
126
+ context 'for a line that intersects the target X plane' do
127
+ context 'and spans both positive and negative space' do
128
+ context 'with a positive X vector' do
129
+ before do
130
+ @line = Triangular::Line.new(Triangular::Vertex.new(-1.0, 2.0, 1.0), Triangular::Vertex.new(1.0, 2.0, -1.0))
131
+ end
132
+
133
+ it 'should return a Point representing the intersection' do
134
+ expect(@line.intersection_at_x(0).x).to eq(0.0)
135
+ expect(@line.intersection_at_x(0).y).to eq(2.0)
136
+ expect(@line.intersection_at_x(0).z).to eq(0.0)
137
+ end
138
+ end
139
+
140
+ context 'with a negative X vector' do
141
+ before do
142
+ @line = Triangular::Line.new(Triangular::Vertex.new(1.0, 1.0, 1.0),
143
+ Triangular::Vertex.new(-1.0, -1.0, -1.0))
144
+ end
145
+
146
+ it 'should return a Point representing the intersection' do
147
+ expect(@line.intersection_at_x(0).x).to eq(0)
148
+ expect(@line.intersection_at_x(0).y).to eq(0)
149
+ expect(@line.intersection_at_x(0).z).to eq(0)
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ context 'for a line that lies on the target X plane' do
156
+ before do
157
+ @line = Triangular::Line.new(Triangular::Vertex.new(3.0, 0.0, 3.0), Triangular::Vertex.new(3.0, 6.0, 6.0))
158
+ end
159
+
160
+ it 'should raise an error' do
161
+ expect(@line.intersection_at_x(3.0)).to eq(nil)
162
+ end
163
+ end
164
+
165
+ context 'for a line that does not intersect the target X plane' do
166
+ before do
167
+ @line = Triangular::Line.new(Triangular::Vertex.new(4.0, 0.0, 4.0), Triangular::Vertex.new(6.0, 0.0, 6.0))
168
+ end
169
+
170
+ it 'should return nil' do
171
+ expect(@line.intersection_at_x(3.0)).to be_nil
172
+ end
173
+ end
174
+ end
175
+
176
+ describe '#intersects_y?' do
177
+ context 'for a line that intersects the target Y plane' do
178
+ context 'with a positive Y vector' do
179
+ before do
180
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 2.0, 0.0), Triangular::Vertex.new(0.0, 3.1, 6.0))
181
+ end
182
+
183
+ it 'should return true' do
184
+ expect(@line.intersects_y?(3.0)).to be true
185
+ end
186
+ end
187
+
188
+ context 'with a negative Y vector' do
189
+ before do
190
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 3.1, 6.0), Triangular::Vertex.new(0.0, 2.0, 0.0))
191
+ end
192
+
193
+ it 'should return true' do
194
+ expect(@line.intersects_y?(3.0)).to be true
195
+ end
196
+ end
197
+ end
198
+
199
+ context 'for a line that does not intersect the target Y plane' do
200
+ before do
201
+ @line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 4.0), Triangular::Vertex.new(0.0, 0.0, 6.0))
202
+ end
203
+
204
+ it 'should return false' do
205
+ expect(@line.intersects_y?(3.0)).to be false
206
+ end
207
+ end
208
+ end
209
+
210
+ describe '#intersection_at_y' do
211
+ context 'for a line that intersects the target Y plane' do
212
+ context 'and spans both positive and negative space' do
213
+ context 'with a positive Y vector' do
214
+ before do
215
+ @line = Triangular::Line.new(Triangular::Vertex.new(2.0, -1.0, 1.0), Triangular::Vertex.new(2.0, 1.0, -1.0))
216
+ end
217
+
218
+ it 'should return a Point representing the intersection' do
219
+ expect(@line.intersection_at_y(0).x).to eq(2.0)
220
+ expect(@line.intersection_at_y(0).y).to eq(0.0)
221
+ expect(@line.intersection_at_y(0).z).to eq(0.0)
222
+ end
223
+ end
224
+
225
+ context 'with a negative Y vector' do
226
+ before do
227
+ @line = Triangular::Line.new(Triangular::Vertex.new(1.0, 1.0, 1.0),
228
+ Triangular::Vertex.new(-1.0, -1.0, -1.0))
229
+ end
230
+
231
+ it 'should return a Point representing the intersection' do
232
+ expect(@line.intersection_at_y(0).x).to eq(0)
233
+ expect(@line.intersection_at_y(0).y).to eq(0)
234
+ expect(@line.intersection_at_y(0).z).to eq(0)
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ context 'for a line that lies on the target Y plane' do
241
+ before do
242
+ @line = Triangular::Line.new(Triangular::Vertex.new(3.0, 3.0, 3.0), Triangular::Vertex.new(3.0, 3.0, 6.0))
243
+ end
244
+
245
+ it 'should raise an error' do
246
+ expect(@line.intersection_at_y(3.0)).to eq(nil)
247
+ end
248
+ end
249
+
250
+ context 'for a line that does not intersect the target Y plane' do
251
+ before do
252
+ @line = Triangular::Line.new(Triangular::Vertex.new(4.0, 4.0, 4.0), Triangular::Vertex.new(6.0, 6.0, 6.0))
253
+ end
254
+
255
+ it 'should return nil' do
256
+ expect(@line.intersection_at_y(3.0)).to be_nil
257
+ end
258
+ end
259
+ end
260
+
261
+ describe '==' do
262
+ it 'should return true when the two lines are identical' do
263
+ expect(
264
+ Triangular::Line.new(Triangular::Vertex.new(-1.0, -1.0, -1.0), Triangular::Vertex.new(1.0, 1.0, 1.0)) ==
265
+ Triangular::Line.new(Triangular::Vertex.new(-1.0, -1.0, -1.0), Triangular::Vertex.new(1.0, 1.0, 1.0))
266
+ ).to be true
267
+ end
268
+
269
+ it 'should not return true when the lines are not identical' do
270
+ expect(
271
+ Triangular::Line.new(Triangular::Vertex.new(-1.0, -1.0, -1.1), Triangular::Vertex.new(1.0, 1.0, 1.0)) ==
272
+ Triangular::Line.new(Triangular::Vertex.new(-1.0, -1.0, -1.0), Triangular::Vertex.new(1.0, 1.0, 1.0))
273
+ ).to be false
274
+ end
275
+ end
276
+
277
+ describe '#to_svg_path' do
278
+ it 'should return a string containing an SVG path' do
279
+ line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 0.0), Triangular::Vertex.new(1.0, 1.0, 1.0))
280
+ expected_output = '<path d="M 0.0 0.0 L 1.0 1.0" fill="none" stroke="black" stroke-width="0.005" />'
281
+
282
+ expect(line.to_svg_path(:inches)).to eq(expected_output)
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Triangular::Point do
6
+ describe '.parse' do
7
+ context 'with a correctly formatted point' do
8
+ before do
9
+ @result = Triangular::Point.parse(' 16.5 0.0 -0.75 ')
10
+ end
11
+
12
+ it 'should return a point object' do
13
+ expect(@result).to be_a Triangular::Point
14
+ end
15
+
16
+ it 'should correctly set the X value' do
17
+ expect(@result.x).to eq(16.5)
18
+ end
19
+
20
+ it 'should correctly set the Y value' do
21
+ expect(@result.y).to eq(0)
22
+ end
23
+
24
+ it 'should correctly set the Z value' do
25
+ expect(@result.z).to eq(-0.75)
26
+ end
27
+ end
28
+
29
+ context 'with a point that contains exponential notation' do
30
+ before do
31
+ @result = Triangular::Point.parse(' -5.23431439438796e-32 0.0 5.23431439438796e32 ')
32
+ end
33
+
34
+ it 'should correctly set the X value' do
35
+ expect(@result.x).to eq(-5.23431439438796e-32)
36
+ end
37
+
38
+ it 'should correctly set the Y value' do
39
+ expect(@result.y).to eq(0.0)
40
+ end
41
+
42
+ it 'should correctly set the Z value' do
43
+ expect(@result.z).to eq(5.23431439438796e32)
44
+ end
45
+ end
46
+
47
+ context 'with a point with no fractional part' do
48
+ before do
49
+ @result = Triangular::Point.parse(' -5 0 5 ')
50
+ end
51
+
52
+ it 'should correctly set the X value' do
53
+ expect(@result.x).to eq(-5.0)
54
+ end
55
+
56
+ it 'should correctly set the Y value' do
57
+ expect(@result.y).to eq(0.0)
58
+ end
59
+
60
+ it 'should correctly set the Z value' do
61
+ expect(@result.z).to eq(5.0)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#to_s' do
67
+ it 'should return the XYZ components separated by spaces' do
68
+ point = Triangular::Point.new(1.0, 2.0, -3.1)
69
+ expect(point.to_s).to eq('1.0 2.0 -3.1')
70
+ end
71
+
72
+ it 'should convert integers into floats for output' do
73
+ point = Triangular::Point.new(1, 2, 3)
74
+ expect(point.to_s).to eq('1.0 2.0 3.0')
75
+ end
76
+ end
77
+
78
+ describe '#==' do
79
+ it 'should return true when the points have identical values' do
80
+ expect(Triangular::Point.new(1.0, 2.0, -3.1) == Triangular::Point.new(1.0, 2.0, -3.1)).to be true
81
+ end
82
+
83
+ it 'should return false when the points do not have identical values' do
84
+ expect(Triangular::Point.new(1.0, 2.0, -3.1) == Triangular::Point.new(1.0, 2.0, -3.2)).to be false
85
+ end
86
+ end
87
+
88
+ describe '#translate!' do
89
+ before do
90
+ @point = Triangular::Point.new(1.0, 1.0, 1.0)
91
+ end
92
+
93
+ it 'should add the supplied value to X' do
94
+ @point.translate!(16.5, 9.5, 0.75)
95
+ expect(@point.x).to eq(17.5)
96
+ end
97
+
98
+ it 'should add the supplied value to Y' do
99
+ @point.translate!(16.5, 9.5, 0.75)
100
+ expect(@point.y).to eq(10.5)
101
+ end
102
+
103
+ it 'should add the supplied value to Z' do
104
+ @point.translate!(16.5, 9.5, 0.75)
105
+ expect(@point.z).to eq(1.75)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Triangular::Polyline do
6
+ describe '#to_svg' do
7
+ it 'should output an SVG document as a string' do
8
+ line = Triangular::Line.new(Triangular::Vertex.new(0.0, 0.0, 0.0), Triangular::Vertex.new(1.0, 1.0, 1.0))
9
+ polyline = Triangular::Polyline.new([line])
10
+
11
+ expected_svg = "<?xml version=\"1.0\" standalone=\"no\"?>\n"
12
+ expected_svg += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
13
+ expected_svg += "<svg x=\"0\" y=\"0\" width=\"20in\" height=\"30in\" viewBox=\"0 0 20 30\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n"
14
+ expected_svg += " <g transform=\"translate(1in,2in)\">\n"
15
+ expected_svg += " #{line.to_svg_path(:inches)}\n"
16
+ expected_svg += " </g>\n"
17
+ expected_svg += '</svg>'
18
+
19
+ expect(polyline.to_svg(20, 30, :inches, 1, 2)).to eq(expected_svg)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Triangular::Ray do
6
+ describe '#intersection' do
7
+ before do
8
+ @ray = Triangular::Ray.new(1.0, 1.0)
9
+ end
10
+
11
+ context 'with facet that does not intersect the Ray' do
12
+ it 'should return nil' do
13
+ vertex1 = Triangular::Vertex.new(1.1, 1.1, 1.0)
14
+ vertex2 = Triangular::Vertex.new(2.0, 1.1, 1.0)
15
+ vertex3 = Triangular::Vertex.new(2.0, 2.0, 1.0)
16
+
17
+ facet = Triangular::Facet.new(nil, vertex1, vertex2, vertex3)
18
+
19
+ expect(@ray.intersection(facet)).to eq(nil)
20
+ end
21
+ end
22
+
23
+ context 'with flat facet that intersects the Ray' do
24
+ it 'should return Point that represents intersection' do
25
+ vertex1 = Triangular::Vertex.new(0.0, 0.0, 1.2)
26
+ vertex2 = Triangular::Vertex.new(2.0, 0.0, 1.2)
27
+ vertex3 = Triangular::Vertex.new(2.0, 2.0, 1.2)
28
+
29
+ facet = Triangular::Facet.new(nil, vertex1, vertex2, vertex3)
30
+
31
+ expect(@ray.intersection(facet)).to eq(Triangular::Point.new(1.0, 1.0, 1.2))
32
+ end
33
+ end
34
+
35
+ context 'with simple angled facet that intersects the Ray' do
36
+ it 'should return Point that represents intersection' do
37
+ vertex1 = Triangular::Vertex.new(0.0, 0.0, 0.0)
38
+ vertex2 = Triangular::Vertex.new(2.0, 0.0, 2.0)
39
+ vertex3 = Triangular::Vertex.new(2.0, 2.0, 2.0)
40
+
41
+ facet = Triangular::Facet.new(nil, vertex1, vertex2, vertex3)
42
+
43
+ expect(@ray.intersection(facet)).to eq(Triangular::Point.new(1.0, 1.0, 1.0))
44
+ end
45
+ end
46
+
47
+ context 'with compound angled facet that intersects the Ray' do
48
+ before do
49
+ @ray = Triangular::Ray.new(0.5, 0.5)
50
+ end
51
+
52
+ it 'should return Point that represents intersection' do
53
+ vertex1 = Triangular::Vertex.new(0.0, 0.0, 0.0)
54
+ vertex2 = Triangular::Vertex.new(2.0, 0.0, 1.0)
55
+ vertex3 = Triangular::Vertex.new(0.0, 2.0, 2.0)
56
+
57
+ facet = Triangular::Facet.new(nil, vertex1, vertex2, vertex3)
58
+
59
+ expect(@ray.intersection(facet)).to eq(Triangular::Point.new(0.5, 0.5, 0.75))
60
+ end
61
+ end
62
+ end
63
+ end