gmath3D 0.2.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/triangle.rb CHANGED
@@ -1,281 +1,281 @@
1
- require 'gmath3D'
2
-
3
- module GMath3D
4
- #
5
- # Triangle represents a three edged finite plane on 3D space.
6
- #
7
- class Triangle < Geom
8
- public
9
- attr_accessor :vertices
10
-
11
- include BoxAvailable
12
-
13
- # [Input]
14
- # _vertex1_, _vertex2_, _vertex3_ should be Vector3.
15
- # [Output]
16
- # return new instance of Triangle.
17
- def initialize(vertex1 = Vector3.new(), vertex2 = Vector3.new(1,0,0), vertex3 = Vector3.new(0,1,0))
18
- Util3D.check_arg_type(::Vector3, vertex1)
19
- Util3D.check_arg_type(::Vector3, vertex2)
20
- Util3D.check_arg_type(::Vector3, vertex3)
21
- super()
22
- @vertices = Array.new([vertex1, vertex2, vertex3])
23
- end
24
-
25
- def initialize_copy( original_obj )
26
- @vertices = Array.new(original_obj.vertices.size)
27
- for i in 0..@vertices.size-1
28
- @vertices[i] = original_obj.vertices[i].dup
29
- end
30
- end
31
-
32
- # [Input]
33
- # _rhs_ is Line.
34
- # [Output]
35
- # return true if rhs equals myself.
36
- def ==(rhs)
37
- return false if rhs == nil
38
- return false if( !rhs.kind_of?(Triangle) )
39
- return false if(@vertices.size != rhs.vertices.size)
40
- for i in 0..@vertices.size-1
41
- return false if(@vertices[i] != rhs.vertices[i])
42
- end
43
- return true
44
- end
45
-
46
- def to_s
47
- "Triangle[#{@vertices[0].to_element_s}, #{@vertices[1].to_element_s}, #{@vertices[2].to_element_s}]"
48
- end
49
-
50
- # [Input]
51
- # _parameter_ should be three element Array of Numeric.
52
- # [Output]
53
- # return point on triangle at parameter position as Vector3.
54
- def point( parameter )
55
- Util3D.check_arg_type(::Array, parameter )
56
- # TODO Argument check
57
- return self.vertices[0]*parameter[0] + self.vertices[1]*parameter[1] + self.vertices[2]*parameter[2]
58
- end
59
-
60
- # [Output]
61
- # return edges as three element Array of Vector3.
62
- def edges
63
- return_edges = Array.new(3)
64
- return_edges[0] = FiniteLine.new(self.vertices[0], self.vertices[1])
65
- return_edges[1] = FiniteLine.new(self.vertices[1], self.vertices[2])
66
- return_edges[2] = FiniteLine.new(self.vertices[2], self.vertices[0])
67
- return return_edges
68
- end
69
-
70
- # [Output]
71
- # return area as Numeric.
72
- def area
73
- vec1 = vertices[1] - vertices[0]
74
- vec2 = vertices[2] - vertices[0]
75
- outer_product = vec1.cross(vec2)
76
- return outer_product.length / 2.0
77
- end
78
-
79
- # [Output]
80
- # return center point as Vector3.
81
- def center
82
- return vertices.avg
83
- end
84
-
85
- # [Output]
86
- # return normal vector as Vector3.
87
- def normal
88
- vec1 = self.vertices[1] - self.vertices[0]
89
- vec2 = self.vertices[2] - self.vertices[0]
90
- return (vec1.cross(vec2).normalize)
91
- end
92
-
93
- # [Input]
94
- # _vertex_index_ should be 0..2.
95
- # [Output]
96
- # return angle as Numeric(radian).
97
- def angle( vertex_index )
98
- return nil if(vertex_index < 0 || vertex_index > 2)
99
- vert1 = self.vertices[vertex_index]
100
- vert2 = self.vertices[(vertex_index+1)%3]
101
- vert3 = self.vertices[(vertex_index+2)%3]
102
- vec1 = vert2 - vert1
103
- vec2 = vert3 - vert1
104
- vec1.angle(vec2)
105
- end
106
-
107
- # [Output]
108
- # return normal vector reversed triangle
109
- def reverse
110
- return Triangle.new(@vertices[0], @vertices[2], @vertices[1])
111
- end
112
-
113
- # [Input]
114
- # _check_point_ should be Vector3.
115
- # [Output]
116
- # return barycentric_coordinate on check_point as three element Array of Numeric.
117
- def barycentric_coordinate( check_point )
118
- Util3D.check_arg_type(::Vector3, check_point)
119
-
120
- v0 = @vertices[0]
121
- v1 = @vertices[1]
122
- v2 = @vertices[2]
123
-
124
- d1 = v1 - v0
125
- d2 = v2 - v1
126
- n = d1.cross(d2);
127
- if((n.x).abs >= (n.y).abs && (n.x).abs >= (n.z).abs)
128
- uu1 = v0.y - v2.y;
129
- uu2 = v1.y - v2.y;
130
- uu3 = check_point.y - v0.y;
131
- uu4 = check_point.y - v2.y;
132
- vv1 = v0.z - v2.z;
133
- vv2 = v1.z - v2.z;
134
- vv3 = check_point.z - v0.z;
135
- vv4 = check_point.z - v2.z;
136
- elsif((n.y).abs >= (n.z).abs)
137
- uu1 = v0.z - v2.z;
138
- uu2 = v1.z - v2.z;
139
- uu3 = check_point.z - v0.z;
140
- uu4 = check_point.z - v2.z;
141
- vv1 = v0.x - v2.x;
142
- vv2 = v1.x - v2.x;
143
- vv3 = check_point.x - v0.x;
144
- vv4 = check_point.x - v2.x;
145
- else
146
- uu1 = v0.x - v2.x;
147
- uu2 = v1.x - v2.x;
148
- uu3 = check_point.x - v0.x;
149
- uu4 = check_point.x - v2.x;
150
- vv1 = v0.y - v2.y;
151
- vv2 = v1.y - v2.y;
152
- vv3 = check_point.y - v0.y;
153
- vv4 = check_point.y - v2.y;
154
- end
155
-
156
- denom = vv1 * uu2 - vv2* uu1
157
- if(denom == 0.0)
158
- return nil
159
- end
160
- b = Array.new(3)
161
- oneOverDenom = 1.0 / denom ;
162
- b[0] = (vv4*uu2 - vv2*uu4) * oneOverDenom;
163
- b[1] = (vv1*uu3 - vv3*uu1) * oneOverDenom;
164
- b[2] = 1.0 - b[0] - b[1];
165
- return b;
166
- end
167
-
168
- # [Input]
169
- # _target_ shold be Vector3 or Line or Plane.
170
- # [Output]
171
- # [In case _target_ is Vector3]
172
- # return "distance, point on triangle" as [Numeric, Vector3].
173
- # [In case _target_ is Line]
174
- # return "distance, point on tirangle, point on line, parameter on line" as [Numeric, Vector3, Vector3, Numeric].
175
- # [In case _target_ is Plane]
176
- # return "distance, intersect_line(or closet edge), point_on_triangle, point_on_plane" as [Numeric, Vector3, Vector3, Vector3].
177
- def distance(target)
178
- # with Point
179
- if(target.kind_of?(Vector3))
180
- return distance_to_point(target)
181
- elsif(target.kind_of?(Line))
182
- #with Line
183
- return distance_to_line(target)
184
- elsif(target.kind_of?(Plane))
185
- #with Plane
186
- return distance_to_plane(target)
187
- end
188
- Util3D.raise_argurment_error(target)
189
- end
190
-
191
- # [Input]
192
- # _check_point_ shold be Vector3.
193
- # [Output]
194
- # return true if triangle contains _check_point_.
195
- def contains?( check_point )
196
- Util3D.check_arg_type(Vector3, check_point )
197
- plane = Plane.new( vertices[0], self.normal)
198
- distance, projected_point = plane.distance(check_point)
199
- return false if( distance > self.tolerance )
200
- g_coord = self.barycentric_coordinate(check_point)
201
- g_coord.each do |item|
202
- return false if( item < 0 or 1 < item)
203
- end
204
- return true
205
- end
206
-
207
- private
208
- def distance_to_point(target_point)
209
- plane = Plane.new( vertices[0], self.normal)
210
- distance, projected_point = plane.distance(target_point)
211
- if( self.contains?(projected_point))
212
- return distance, projected_point
213
- end
214
- #check distance to FiniteLines
215
- finite_lines = self.edges
216
- return FiniteLine.ary_distanc_to_point(finite_lines, target_point)
217
- end
218
-
219
- def distance_to_line(target_line)
220
- plane = Plane.new( vertices[0], self.normal )
221
- distance, point_on_plane, parameter_on_line = plane.distance( target_line )
222
- if( point_on_plane == nil)
223
- # parallel case
224
- # check distance to FiniteLines
225
- finite_lines = self.edges
226
- distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target =
227
- FiniteLine.ary_distance_to_line(finite_lines, target_line)
228
- return distance, nil, nil, nil
229
- end
230
- if( self.contains?(point_on_plane) )
231
- return distance, point_on_plane, point_on_plane, parameter_on_line
232
- end
233
- # check distance to FiniteLines
234
- finite_lines = self.edges
235
- distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target =
236
- FiniteLine.ary_distance_to_line(finite_lines, target_line)
237
- return distance, point_on_edge, point_on_target, param_on_target
238
- end
239
-
240
- def distance_to_plane(target_plane)
241
- triangle_plane = Plane.new( vertices[0], self.normal )
242
- distance, intersect_line_each_plane = triangle_plane.distance( target_plane )
243
- if( intersect_line_each_plane == nil )
244
- return distance, nil, nil, nil
245
- end
246
-
247
- # check distance from intersection and each edge.
248
- distance_zero_count = 0
249
- distance_info = Array.new(0)
250
- prallel_edge_ary = Array.new(0)
251
- self.edges.each do |edge|
252
- distance, point_on_edge, point_on_line = edge.distance( intersect_line_each_plane)
253
- if point_on_edge != nil && point_on_line != nil
254
- distance_info.push([distance, point_on_edge, point_on_line])
255
- if distance <= self.tolerance
256
- distance_zero_count += 1
257
- end
258
- else
259
- prallel_edge_ary.push( edge )
260
- end
261
- end
262
- distance_info.sort!{|a,b| a[0] <=> b[0]}
263
- # distance, intersect_line(or closet edge), point_on_triangle, point_on_plan
264
- if (distance_zero_count == 2)
265
- point1 = distance_info[0][1]
266
- point2 = distance_info[1][1]
267
- if point1.distance(point2) > self.tolerance
268
- return 0.0, FiniteLine.new(point1, point2), nil, nil
269
- end
270
- return 0.0, nil, point1, point1
271
- elsif (distance_zero_count == 0)
272
- distance, closest_point_on_plane = target_plane.distance(distance_info[0][1])
273
- if(distance_info[0][1] != distance_info[1][1])
274
- return distance, FiniteLine.new(distance_info[0][1], distance_info[1][1]), nil, nil
275
- end
276
- return distance, nil, distance_info[0][1], closest_point_on_plane
277
- end
278
- return 0.0, nil, nil, nil
279
- end
280
- end
281
- end
1
+ require 'gmath3D'
2
+
3
+ module GMath3D
4
+ #
5
+ # Triangle represents a three edged finite plane on 3D space.
6
+ #
7
+ class Triangle < Geom
8
+ public
9
+ attr_accessor :vertices
10
+
11
+ include BoxAvailable
12
+
13
+ # [Input]
14
+ # _vertex1_, _vertex2_, _vertex3_ should be Vector3.
15
+ # [Output]
16
+ # return new instance of Triangle.
17
+ def initialize(vertex1 = Vector3.new(), vertex2 = Vector3.new(1,0,0), vertex3 = Vector3.new(0,1,0))
18
+ Util3D.check_arg_type(::Vector3, vertex1)
19
+ Util3D.check_arg_type(::Vector3, vertex2)
20
+ Util3D.check_arg_type(::Vector3, vertex3)
21
+ super()
22
+ @vertices = Array.new([vertex1, vertex2, vertex3])
23
+ end
24
+
25
+ def initialize_copy( original_obj )
26
+ @vertices = Array.new(original_obj.vertices.size)
27
+ for i in 0..@vertices.size-1
28
+ @vertices[i] = original_obj.vertices[i].dup
29
+ end
30
+ end
31
+
32
+ # [Input]
33
+ # _rhs_ is Line.
34
+ # [Output]
35
+ # return true if rhs equals myself.
36
+ def ==(rhs)
37
+ return false if rhs == nil
38
+ return false if( !rhs.kind_of?(Triangle) )
39
+ return false if(@vertices.size != rhs.vertices.size)
40
+ for i in 0..@vertices.size-1
41
+ return false if(@vertices[i] != rhs.vertices[i])
42
+ end
43
+ return true
44
+ end
45
+
46
+ def to_s
47
+ "Triangle[#{@vertices[0].to_element_s}, #{@vertices[1].to_element_s}, #{@vertices[2].to_element_s}]"
48
+ end
49
+
50
+ # [Input]
51
+ # _parameter_ should be three element Array of Numeric.
52
+ # [Output]
53
+ # return point on triangle at parameter position as Vector3.
54
+ def point( parameter )
55
+ Util3D.check_arg_type(::Array, parameter )
56
+ # TODO Argument check
57
+ return self.vertices[0]*parameter[0] + self.vertices[1]*parameter[1] + self.vertices[2]*parameter[2]
58
+ end
59
+
60
+ # [Output]
61
+ # return edges as three element Array of Vector3.
62
+ def edges
63
+ return_edges = Array.new(3)
64
+ return_edges[0] = FiniteLine.new(self.vertices[0], self.vertices[1])
65
+ return_edges[1] = FiniteLine.new(self.vertices[1], self.vertices[2])
66
+ return_edges[2] = FiniteLine.new(self.vertices[2], self.vertices[0])
67
+ return return_edges
68
+ end
69
+
70
+ # [Output]
71
+ # return area as Numeric.
72
+ def area
73
+ vec1 = vertices[1] - vertices[0]
74
+ vec2 = vertices[2] - vertices[0]
75
+ outer_product = vec1.cross(vec2)
76
+ return outer_product.length / 2.0
77
+ end
78
+
79
+ # [Output]
80
+ # return center point as Vector3.
81
+ def center
82
+ return vertices.avg
83
+ end
84
+
85
+ # [Output]
86
+ # return normal vector as Vector3.
87
+ def normal
88
+ vec1 = self.vertices[1] - self.vertices[0]
89
+ vec2 = self.vertices[2] - self.vertices[0]
90
+ return (vec1.cross(vec2).normalize)
91
+ end
92
+
93
+ # [Input]
94
+ # _vertex_index_ should be 0..2.
95
+ # [Output]
96
+ # return angle as Numeric(radian).
97
+ def angle( vertex_index )
98
+ return nil if(vertex_index < 0 || vertex_index > 2)
99
+ vert1 = self.vertices[vertex_index]
100
+ vert2 = self.vertices[(vertex_index+1)%3]
101
+ vert3 = self.vertices[(vertex_index+2)%3]
102
+ vec1 = vert2 - vert1
103
+ vec2 = vert3 - vert1
104
+ vec1.angle(vec2)
105
+ end
106
+
107
+ # [Output]
108
+ # return normal vector reversed triangle
109
+ def reverse
110
+ return Triangle.new(@vertices[0], @vertices[2], @vertices[1])
111
+ end
112
+
113
+ # [Input]
114
+ # _check_point_ should be Vector3.
115
+ # [Output]
116
+ # return barycentric_coordinate on check_point as three element Array of Numeric.
117
+ def barycentric_coordinate( check_point )
118
+ Util3D.check_arg_type(::Vector3, check_point)
119
+
120
+ v0 = @vertices[0]
121
+ v1 = @vertices[1]
122
+ v2 = @vertices[2]
123
+
124
+ d1 = v1 - v0
125
+ d2 = v2 - v1
126
+ n = d1.cross(d2);
127
+ if((n.x).abs >= (n.y).abs && (n.x).abs >= (n.z).abs)
128
+ uu1 = v0.y - v2.y;
129
+ uu2 = v1.y - v2.y;
130
+ uu3 = check_point.y - v0.y;
131
+ uu4 = check_point.y - v2.y;
132
+ vv1 = v0.z - v2.z;
133
+ vv2 = v1.z - v2.z;
134
+ vv3 = check_point.z - v0.z;
135
+ vv4 = check_point.z - v2.z;
136
+ elsif((n.y).abs >= (n.z).abs)
137
+ uu1 = v0.z - v2.z;
138
+ uu2 = v1.z - v2.z;
139
+ uu3 = check_point.z - v0.z;
140
+ uu4 = check_point.z - v2.z;
141
+ vv1 = v0.x - v2.x;
142
+ vv2 = v1.x - v2.x;
143
+ vv3 = check_point.x - v0.x;
144
+ vv4 = check_point.x - v2.x;
145
+ else
146
+ uu1 = v0.x - v2.x;
147
+ uu2 = v1.x - v2.x;
148
+ uu3 = check_point.x - v0.x;
149
+ uu4 = check_point.x - v2.x;
150
+ vv1 = v0.y - v2.y;
151
+ vv2 = v1.y - v2.y;
152
+ vv3 = check_point.y - v0.y;
153
+ vv4 = check_point.y - v2.y;
154
+ end
155
+
156
+ denom = vv1 * uu2 - vv2* uu1
157
+ if(denom == 0.0)
158
+ return nil
159
+ end
160
+ b = Array.new(3)
161
+ oneOverDenom = 1.0 / denom ;
162
+ b[0] = (vv4*uu2 - vv2*uu4) * oneOverDenom;
163
+ b[1] = (vv1*uu3 - vv3*uu1) * oneOverDenom;
164
+ b[2] = 1.0 - b[0] - b[1];
165
+ return b;
166
+ end
167
+
168
+ # [Input]
169
+ # _target_ shold be Vector3 or Line or Plane.
170
+ # [Output]
171
+ # [In case _target_ is Vector3]
172
+ # return "distance, point on triangle" as [Numeric, Vector3].
173
+ # [In case _target_ is Line]
174
+ # return "distance, point on tirangle, point on line, parameter on line" as [Numeric, Vector3, Vector3, Numeric].
175
+ # [In case _target_ is Plane]
176
+ # return "distance, intersect_line(or closet edge), point_on_triangle, point_on_plane" as [Numeric, Vector3, Vector3, Vector3].
177
+ def distance(target)
178
+ # with Point
179
+ if(target.kind_of?(Vector3))
180
+ return distance_to_point(target)
181
+ elsif(target.kind_of?(Line))
182
+ #with Line
183
+ return distance_to_line(target)
184
+ elsif(target.kind_of?(Plane))
185
+ #with Plane
186
+ return distance_to_plane(target)
187
+ end
188
+ Util3D.raise_argurment_error(target)
189
+ end
190
+
191
+ # [Input]
192
+ # _check_point_ shold be Vector3.
193
+ # [Output]
194
+ # return true if triangle contains _check_point_.
195
+ def contains?( check_point )
196
+ Util3D.check_arg_type(Vector3, check_point )
197
+ plane = Plane.new( vertices[0], self.normal)
198
+ distance, projected_point = plane.distance(check_point)
199
+ return false if( distance > self.tolerance )
200
+ g_coord = self.barycentric_coordinate(check_point)
201
+ g_coord.each do |item|
202
+ return false if( item < 0 or 1 < item)
203
+ end
204
+ return true
205
+ end
206
+
207
+ private
208
+ def distance_to_point(target_point)
209
+ plane = Plane.new( vertices[0], self.normal)
210
+ distance, projected_point = plane.distance(target_point)
211
+ if( self.contains?(projected_point))
212
+ return distance, projected_point
213
+ end
214
+ #check distance to FiniteLines
215
+ finite_lines = self.edges
216
+ return FiniteLine.ary_distanc_to_point(finite_lines, target_point)
217
+ end
218
+
219
+ def distance_to_line(target_line)
220
+ plane = Plane.new( vertices[0], self.normal )
221
+ distance, point_on_plane, parameter_on_line = plane.distance( target_line )
222
+ if( point_on_plane == nil)
223
+ # parallel case
224
+ # check distance to FiniteLines
225
+ finite_lines = self.edges
226
+ distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target =
227
+ FiniteLine.ary_distance_to_line(finite_lines, target_line)
228
+ return distance, nil, nil, nil
229
+ end
230
+ if( self.contains?(point_on_plane) )
231
+ return distance, point_on_plane, point_on_plane, parameter_on_line
232
+ end
233
+ # check distance to FiniteLines
234
+ finite_lines = self.edges
235
+ distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target =
236
+ FiniteLine.ary_distance_to_line(finite_lines, target_line)
237
+ return distance, point_on_edge, point_on_target, param_on_target
238
+ end
239
+
240
+ def distance_to_plane(target_plane)
241
+ triangle_plane = Plane.new( vertices[0], self.normal )
242
+ distance, intersect_line_each_plane = triangle_plane.distance( target_plane )
243
+ if( intersect_line_each_plane == nil )
244
+ return distance, nil, nil, nil
245
+ end
246
+
247
+ # check distance from intersection and each edge.
248
+ distance_zero_count = 0
249
+ distance_info = Array.new(0)
250
+ prallel_edge_ary = Array.new(0)
251
+ self.edges.each do |edge|
252
+ distance, point_on_edge, point_on_line = edge.distance( intersect_line_each_plane)
253
+ if point_on_edge != nil && point_on_line != nil
254
+ distance_info.push([distance, point_on_edge, point_on_line])
255
+ if distance <= self.tolerance
256
+ distance_zero_count += 1
257
+ end
258
+ else
259
+ prallel_edge_ary.push( edge )
260
+ end
261
+ end
262
+ distance_info.sort!{|a,b| a[0] <=> b[0]}
263
+ # distance, intersect_line(or closet edge), point_on_triangle, point_on_plan
264
+ if (distance_zero_count == 2)
265
+ point1 = distance_info[0][1]
266
+ point2 = distance_info[1][1]
267
+ if point1.distance(point2) > self.tolerance
268
+ return 0.0, FiniteLine.new(point1, point2), nil, nil
269
+ end
270
+ return 0.0, nil, point1, point1
271
+ elsif (distance_zero_count == 0)
272
+ distance, closest_point_on_plane = target_plane.distance(distance_info[0][1])
273
+ if(distance_info[0][1] != distance_info[1][1])
274
+ return distance, FiniteLine.new(distance_info[0][1], distance_info[1][1]), nil, nil
275
+ end
276
+ return distance, nil, distance_info[0][1], closest_point_on_plane
277
+ end
278
+ return 0.0, nil, nil, nil
279
+ end
280
+ end
281
+ end