triangular 0.0.2 → 0.1.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.
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