gmath3D 0.2.0 → 0.2.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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
data/gmath3D.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{gmath3D}
8
- s.version = "0.2.0"
8
+ s.version = "0.2.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Toshiyasu Shimizu"]
12
- s.date = %q{2011-10-09}
12
+ s.date = %q{2011-10-19}
13
13
  s.description = %q{This library defines 3D geometric elements(point, line, plane etc..). It can get two(or more) elements relation, like distance between two elements.}
14
14
  s.email = %q{toshi0328@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -30,7 +30,9 @@ Gem::Specification.new do |s|
30
30
  "lib/geom.rb",
31
31
  "lib/gmath3D.rb",
32
32
  "lib/line.rb",
33
+ "lib/matrix_util.rb",
33
34
  "lib/plane.rb",
35
+ "lib/quat.rb",
34
36
  "lib/rectangle.rb",
35
37
  "lib/triangle.rb",
36
38
  "lib/util.rb",
@@ -39,9 +41,10 @@ Gem::Specification.new do |s|
39
41
  "test/test_box.rb",
40
42
  "test/test_finite_line.rb",
41
43
  "test/test_geom.rb",
42
- "test/test_gmath3D.rb",
43
44
  "test/test_line.rb",
45
+ "test/test_matrix_util.rb",
44
46
  "test/test_plane.rb",
47
+ "test/test_quat.rb",
45
48
  "test/test_rectangle.rb",
46
49
  "test/test_triangle.rb",
47
50
  "test/test_util.rb",
data/lib/finite_line.rb CHANGED
@@ -10,21 +10,33 @@ public
10
10
  attr_accessor :end_point
11
11
 
12
12
  # [Input]
13
- # _start_point_arg_ and _end_point_arg_ should be Vector3.
13
+ # _start_point_ and _end_point_ should be Vector3.
14
14
  # [Output]
15
15
  # return new instance as FiniteLine
16
- def initialize(start_point_arg = Vector3.new(0.0,0.0,0.0), end_point_arg = Vector3.new(1.0,0.0,0.0))
17
- Util.check_arg_type(Vector3, start_point_arg)
18
- Util.check_arg_type(Vector3, end_point_arg)
16
+ def initialize(start_point = Vector3.new(0.0,0.0,0.0), end_point = Vector3.new(1.0,0.0,0.0))
17
+ Util.check_arg_type(Vector3, start_point)
18
+ Util.check_arg_type(Vector3, end_point)
19
19
  super()
20
- @start_point = start_point_arg
21
- @end_point = end_point_arg
20
+ @start_point = start_point
21
+ @end_point = end_point
22
22
  end
23
23
 
24
24
  def to_s
25
25
  "FiniteLine[from#{start_point.to_element_s}, to#{end_point.to_element_s}]"
26
26
  end
27
27
 
28
+ # [Input]
29
+ # _rhs_ should be FiniteLine.
30
+ # [Output]
31
+ # return true if rhs equals myself.
32
+ def ==(rhs)
33
+ return false if rhs == nil
34
+ Util.check_arg_type(FiniteLine, rhs)
35
+ return false if( self.start_point != rhs.start_point)
36
+ return false if( self.end_point != rhs.end_point)
37
+ return true
38
+ end
39
+
28
40
  # [Output]
29
41
  # return direction as vector from start_point to end_point as Vector3
30
42
  def direction
@@ -87,6 +99,30 @@ public
87
99
  closest_point = points_ary[distance_ary.index(distance)]
88
100
  return distance, closest_point
89
101
  end
102
+
103
+ def self.ary_distance_to_line(finite_lines, target_line)
104
+ Util.check_arg_type(::Array, finite_lines)
105
+ Util.check_arg_type(Line, target_line)
106
+ distance_ary = Array.new(0)
107
+ point_on_target_ary = Array.new(0)
108
+ point_on_finite_line_ary = Array.new(0)
109
+ param_on_target_ary = Array.new(0)
110
+ param_on_finite_line_ary = Array.new(0)
111
+ finite_lines.each do | item |
112
+ distance, point_on_myself, point_on_target, parameter_on_myself, parameter_on_tatget = item.distance(target_line)
113
+ distance_ary.push(distance)
114
+ point_on_target_ary.push(point_on_target)
115
+ point_on_finite_line_ary.push(point_on_myself)
116
+ param_on_target_ary.push(parameter_on_tatget)
117
+ param_on_finite_line_ary.push(parameter_on_myself)
118
+ end
119
+ distance = distance_ary.min
120
+ point_on_finiteline = point_on_finite_line_ary[distance_ary.index(distance)]
121
+ point_on_target = point_on_target_ary[distance_ary.index(distance)]
122
+ param_on_finiteline = param_on_finite_line_ary[distance_ary.index(distance)]
123
+ param_on_target = param_on_target_ary[distance_ary.index(distance)]
124
+ return distance, point_on_finiteline, point_on_target, param_on_finiteline, param_on_target
125
+ end
90
126
  private
91
127
  def distance_to_point(target)
92
128
  # get distance using infinite line
data/lib/gmath3D.rb CHANGED
@@ -3,6 +3,9 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
3
  require 'util'
4
4
  require 'geom'
5
5
  require 'vector3'
6
+ require 'quat'
7
+ require 'matrix_util'
8
+
6
9
  require 'line'
7
10
  require 'finite_line'
8
11
  require 'plane'
data/lib/line.rb CHANGED
@@ -9,6 +9,10 @@ module GMath3D
9
9
  attr_accessor :base_point
10
10
  attr_accessor :direction
11
11
 
12
+ def to_s
13
+ "Line[point#{@base_point.to_element_s}, vector#{@direction.to_element_s}"
14
+ end
15
+
12
16
  # [Input]
13
17
  # _point_ and _direction_ should be Vector3.
14
18
  # [Output]
@@ -21,10 +25,6 @@ module GMath3D
21
25
  @direction = direction
22
26
  end
23
27
 
24
- def to_s
25
- "Line[point#{@base_point.to_element_s}, vector#{@direction.to_element_s}"
26
- end
27
-
28
28
  # [Input]
29
29
  # _parameter_ should be Numeric.
30
30
  # [Output]
@@ -0,0 +1,55 @@
1
+ require 'gmath3D'
2
+ require 'matrix'
3
+
4
+
5
+ class Matrix
6
+ def self.from_axis(axis, angle)
7
+ Util.check_arg_type(Vector3, axis)
8
+ Util.check_arg_type(Numeric, angle)
9
+
10
+ return Matrix[
11
+ [axis.x*axis.x*(1 - Math.cos(angle)) + Math.cos(angle),
12
+ axis.x*axis.y*(1 - Math.cos(angle)) + axis.z*Math.sin(angle),
13
+ axis.x*axis.z*(1 - Math.cos(angle)) - axis.y*Math.sin(angle)],
14
+ [axis.x*axis.y*(1 - Math.cos(angle)) - axis.z*Math.sin(angle),
15
+ axis.y*axis.y*(1 - Math.cos(angle)) + Math.cos(angle),
16
+ axis.y*axis.z*(1 - Math.cos(angle)) + axis.x*Math.sin(angle)],
17
+ [axis.x*axis.z*(1 - Math.cos(angle)) + axis.y*Math.sin(angle),
18
+ axis.y*axis.z*(1 - Math.cos(angle)) - axis.x*Math.sin(angle),
19
+ axis.z*axis.z*(1 - Math.cos(angle)) + Math.cos(angle)]]
20
+ end
21
+
22
+ def self.from_quat(quat)
23
+ Util.check_arg_type(Quat, quat)
24
+ qw = quat.w
25
+ qx = quat.x
26
+ qy = quat.y
27
+ qz = quat.z
28
+
29
+ x2 = 2.0 * qx * qx;
30
+ y2 = 2.0 * qy * qy;
31
+ z2 = 2.0 * qz * qz;
32
+ xy = 2.0 * qx * qy;
33
+ yz = 2.0 * qy * qz;
34
+ zx = 2.0 * qz * qx;
35
+ wx = 2.0 * qw * qx;
36
+ wy = 2.0 * qw * qy;
37
+ wz = 2.0 * qw * qz;
38
+
39
+ return Matrix[
40
+ [ 1.0 - y2 - z2, xy + wz, zx - wy],
41
+ [ xy - wz, 1.0 - z2 - x2, yz + wx],
42
+ [ zx + wy, yz - wx, 1.0 - x2 - y2]]
43
+ end
44
+
45
+ alias_method :multi_inner, :* # hold original multiply processing
46
+ def multi_new(rhs)
47
+ if(rhs.kind_of?(Vector3))
48
+ ans = self.multi_inner(rhs.to_column_vector)
49
+ return Vector3.new(ans[0,0], ans[1,0], ans[2,0])
50
+ end
51
+ multi_inner(rhs)
52
+ end
53
+ alias_method :*, :multi_new # overwrite new multiply processing
54
+ end
55
+
data/lib/quat.rb ADDED
@@ -0,0 +1,303 @@
1
+ require 'gmath3D'
2
+
3
+ module GMath3D
4
+ #
5
+ # Quat represents quaternion.
6
+ #
7
+ class Quat
8
+ public
9
+ attr_accessor :x
10
+ attr_accessor :y
11
+ attr_accessor :z
12
+ attr_accessor :w
13
+
14
+ # [Input]
15
+ # _x_, _y_, _z_, _w_should be Numeric.
16
+ # [Output]
17
+ # return new instance of Quat.
18
+ def initialize(x=0.0,y=0.0,z=0.0,w=0.0)
19
+ Util.check_arg_type(Numeric, x)
20
+ Util.check_arg_type(Numeric, y)
21
+ Util.check_arg_type(Numeric, z)
22
+ Util.check_arg_type(Numeric, w)
23
+ super()
24
+ @x = x
25
+ @y = y
26
+ @z = z
27
+ @w = w
28
+ end
29
+
30
+ # [Input]
31
+ # _axsi_ should be Vector3 and _angle_ should be Numeric.
32
+ # [Output]
33
+ # return new instance of Quat.
34
+ def self.from_axis(axis, angle)
35
+ Util.check_arg_type(Vector3, axis)
36
+ Util.check_arg_type(Numeric, angle)
37
+ s = Math.sin(0.5*angle)
38
+ x = s * axis.x
39
+ y = s * axis.y
40
+ z = s * axis.z
41
+ w = Math.cos(0.5*angle)
42
+ return Quat.new(x,y,z,w)
43
+ end
44
+
45
+ # [Input]
46
+ # _matrix_ should be Matrix which row and column size are 3.
47
+ # [Output]
48
+ # return new instance of Quat.
49
+ def self.from_matrix(mat)
50
+ fourWSquaredMinus1 = mat[0,0] + mat[1,1] + mat[2,2]
51
+ fourXSquaredMinus1 = mat[0,0] - mat[1,1] - mat[2,2]
52
+ fourYSquaredMinus1 = mat[1,1] - mat[0,0] - mat[2,2]
53
+ fourZSquaredMinus1 = mat[2,2] - mat[0,0] - mat[1,1]
54
+
55
+ biggestIndex = 0
56
+ fourBiggestSquaredMinus1 = fourWSquaredMinus1
57
+ if(fourXSquaredMinus1 > fourBiggestSquaredMinus1)
58
+ fourBiggestSquaredMinus1 = fourXSquaredMinus1
59
+ biggestIndex = 1
60
+ end
61
+ if(fourYSquaredMinus1 > fourBiggestSquaredMinus1)
62
+ fourBiggestSquaredMinus1 = fourYSquaredMinus1
63
+ biggestIndex = 2
64
+ end
65
+ if(fourZSquaredMinus1 > fourBiggestSquaredMinus1)
66
+ fourBiggestSquaredMinus1 = fourZSquaredMinus1
67
+ biggestIndex = 3
68
+ end
69
+
70
+ biggestVal = Math.sqrt(fourBiggestSquaredMinus1 + 1.0) * 0.5
71
+ multi = 0.25 / biggestVal
72
+
73
+ case biggestIndex
74
+ when 0
75
+ w = biggestVal
76
+ x = (mat[1,2] - mat[2,1]) *multi
77
+ y = (mat[2,0] - mat[0,2]) *multi
78
+ z = (mat[0,1] - mat[1,0]) *multi
79
+ when 1
80
+ x = biggestVal;
81
+ w = (mat[1,2] - mat[2,1]) *multi
82
+ y = (mat[0,1] + mat[1,0]) *multi
83
+ z = (mat[2,0] + mat[0,2]) *multi
84
+ when 2
85
+ y = biggestVal;
86
+ w = (mat[2,0] - mat[0,2]) *multi
87
+ x = (mat[0,1] + mat[1,0]) *multi
88
+ z = (mat[1,2] + mat[2,1]) *multi
89
+ when 3
90
+ z = biggestVal;
91
+ w = (mat[0,1] - mat[1,0]) *multi
92
+ x = (mat[2,0] + mat[0,2]) *multi
93
+ y = (mat[1,2] + mat[2,1]) *multi
94
+ end
95
+ return Quat.new(x,y,z,w)
96
+ end
97
+
98
+ def to_element_s
99
+ "[#{@x}, #{@y}, #{@z}, #{@w}]"
100
+ end
101
+
102
+ def to_s
103
+ "Quat" + to_element_s
104
+ end
105
+
106
+ # [Input]
107
+ # _rhs_ should be Quat.
108
+ # [Output]
109
+ # return true if rhs equals myself.
110
+ def ==(rhs)
111
+ return false if( !rhs.kind_of?(Quat) )
112
+ return false if(self.x != rhs.x)
113
+ return false if(self.y != rhs.y)
114
+ return false if(self.z != rhs.z)
115
+ return false if(self.w != rhs.w)
116
+ true
117
+ end
118
+
119
+ # [Output]
120
+ # return conjugated Quat.
121
+ def conjugate
122
+ return Quat.new( -self.x, -self.y, -self.z, self.w)
123
+ end
124
+
125
+ # [Output]
126
+ # return normalized result as Quat.
127
+ def normalize()
128
+ mag = Math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
129
+ return Quat.new(self.x/mag, self.y/mag, self.z/mag, self.w/mag)
130
+ end
131
+
132
+ # [Input]
133
+ # _rhs_ should be Quat.
134
+ # [Output]
135
+ # return added result as Quat.
136
+ def +(rhs)
137
+ Util.check_arg_type(Quat, rhs)
138
+ t1 = Vector3.new(self.x, self.y, self.z)
139
+ t2 = Vector3.new(rhs.x, rhs.y, rhs.z)
140
+ dot = t1.dot(t2)
141
+ t3 = t2.cross(t1)
142
+
143
+ t1 *= rhs.w
144
+ t2 *= self.w
145
+
146
+ tf = t1 + t2 + t3
147
+ rtn_w = self.w * rhs.w - dot
148
+
149
+ return Quat.new(tf.x, tf.y, tf.z, rtn_w).normalize
150
+ end
151
+
152
+ # [Input]
153
+ # _rsh_ should be Quat.
154
+ # [Output]
155
+ # return (outer products) multiplyed result as Quat.
156
+ def *(rhs)
157
+ Util.check_arg_type(Quat, rhs)
158
+
159
+ pw = self.w; px = self.x; py = self.y; pz = self.z;
160
+ qw = rhs.w ; qx = rhs.x ; qy = rhs.y ; qz = rhs.z;
161
+
162
+ w = pw * qw - px * qx - py * qy - pz * qz
163
+ x = pw * qx + px * qw + py * qz - pz * qy
164
+ y = pw * qy - px * qz + py * qw + pz * qx
165
+ z = pw * qz + px * qy - py * qx + pz * qw
166
+ return Quat.new( x,y,z,w )
167
+ end
168
+
169
+ =begin
170
+ # [Input]
171
+ # _rhs_ should be Vector3.
172
+ # [Output]
173
+ # return subtracted result as Vector3.
174
+ def -(rhs)
175
+ subtract(rhs)
176
+ end
177
+
178
+
179
+ # [Input]
180
+ # _rhs_ should be Numeric.
181
+ # [Output]
182
+ # return divided result as Vector3.
183
+ def /(rhs)
184
+ divide(rhs)
185
+ end
186
+
187
+ # [Input]
188
+ # _rhs_ should be Vector3.
189
+ # [Output]
190
+ # return inner product as Numeric
191
+ def dot(rhs)
192
+ Util.check_arg_type(Vector3, rhs)
193
+ self.x*rhs.x + self.y*rhs.y + self.z*rhs.z
194
+ end
195
+
196
+ # [Input]
197
+ # _rhs_ should be Vector3.
198
+ # [Output]
199
+ # return cross product as Vector3.
200
+ def cross(rhs)
201
+ Util.check_arg_type(Vector3, rhs)
202
+ Vector3.new(
203
+ self.y*rhs.z - self.z*rhs.y,
204
+ self.z*rhs.x - self.x*rhs.z,
205
+ self.x*rhs.y - self.y*rhs.x)
206
+ end
207
+
208
+ # [Output]
209
+ # return vector length as Numeric
210
+ def length
211
+ Math::sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
212
+ end
213
+
214
+ # [Input]
215
+ # _rhs_ should be Vector3.
216
+ # [Output]
217
+ # return distance between two points as Numeric.
218
+ def distance(rhs)
219
+ Util.check_arg_type(Vector3, rhs)
220
+ Math::sqrt((self.x - rhs.x)*(self.x - rhs.x) + (self.y - rhs.y)*(self.y - rhs.y) + (self.z - rhs.z)*(self.z - rhs.z))
221
+ end
222
+
223
+ # [Input]
224
+ # _rhs_ should be Vector3.
225
+ # [Output]
226
+ # return two vectors angle as Numeric (rad).
227
+ def angle(rhs)
228
+ Util.check_arg_type(Vector3, rhs)
229
+ vec1Length = self.length ;
230
+ vec2Length = rhs.length ;
231
+ return 0.0 if(vec1Length*vec2Length < self.tolerance )
232
+ v = self.dot(rhs)/(vec1Length*vec2Length)
233
+ Math::acos( v )
234
+ end
235
+
236
+ # [Output]
237
+ # return normalized vector as Vector3
238
+ def normalize()
239
+ self / self.length.to_f
240
+ end
241
+
242
+ # [Input]
243
+ # _rhs_ should be Vector3
244
+ # [Output]
245
+ # return true if myself and rhs is parallel as boolean
246
+ def parallel?(rhs)
247
+ Util.check_arg_type(Vector3, rhs)
248
+ return false if(self.length < self.tolerance or rhs.length < rhs.tolerance)
249
+ return false if(self.cross(rhs).length > self.tolerance)
250
+ return true
251
+ end
252
+
253
+ # [Input]
254
+ # _rhs_ should be Vector3.
255
+ # [Output]
256
+ # return true if myself and rhs have same direction as boolean.
257
+ def same_direction?(rhs)
258
+ Util.check_arg_type(Vector3, rhs)
259
+ return false if(!parallel?(rhs))
260
+ return false if(self.dot(rhs) < self.tolerance)
261
+ return true
262
+ end
263
+
264
+ # This function projects self vector to rhs vector.
265
+ # [Input]
266
+ # _rhs_ should be Vector3.
267
+ # [Output]
268
+ # return projected result as Vector3.
269
+ def project_to(rhs)
270
+ Util.check_arg_type(Vector3, rhs)
271
+ return Vector3.new, 0.0 if( rhs.length < rhs.tolerance )
272
+ parameter = self.dot( rhs ) / ( rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z ).to_f
273
+ return rhs*parameter, parameter
274
+ end
275
+
276
+ # [Output]
277
+ # return column vector as Matrix
278
+ def to_column_vector
279
+ return Matrix.column_vector([x,y,z])
280
+ end
281
+
282
+ private
283
+
284
+ def add(rhs)
285
+ Util.check_arg_type(Vector3, rhs)
286
+ Vector3.new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
287
+ end
288
+ def subtract(rhs)
289
+ Util.check_arg_type(Vector3, rhs)
290
+ Vector3.new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
291
+ end
292
+ def multiply(rhs)
293
+ Util.check_arg_type(::Numeric, rhs)
294
+ Vector3.new(self.x * rhs, self.y * rhs, self.z * rhs)
295
+ end
296
+ def divide(rhs)
297
+ Util.check_arg_type(::Numeric, rhs)
298
+ Vector3.new(self.x.to_f / rhs, self.y / rhs.to_f, self.z / rhs.to_f)
299
+ end
300
+ =end
301
+ end
302
+ end
303
+
data/lib/triangle.rb CHANGED
@@ -123,16 +123,24 @@ public
123
123
  end
124
124
 
125
125
  # [Input]
126
- # _target_ shold be Vector3.
126
+ # _target_ shold be Vector3 or Line or Plane.
127
127
  # [Output]
128
- # return "distance, point on triangle" as [Numeric, Vector3].
128
+ # [In case _target_ is Vector3]
129
+ # return "distance, point on triangle" as [Numeric, Vector3].
130
+ # [In case _target_ is Line]
131
+ # return "distance, point on tirangle, point on line, parameter on line" as [Numeric, Vector3, Vector3, Numeric].
132
+ # [In case _target_ is Plane]
133
+ # return "distance, intersect_line(or closet edge), point_on_triangle, point_on_plane" as [Numeric, Vector3, Vector3, Vector3].
129
134
  def distance(target)
130
135
  # with Point
131
136
  if(target.kind_of?(Vector3))
132
137
  return distance_to_point(target)
133
138
  elsif(target.kind_of?(Line))
134
- #with Line
135
- # return distance_to_line(target)
139
+ #with Line
140
+ return distance_to_line(target)
141
+ elsif(target.kind_of?(Plane))
142
+ #with Plane
143
+ return distance_to_plane(target)
136
144
  end
137
145
  Util.raise_argurment_error(target)
138
146
  end
@@ -164,6 +172,67 @@ private
164
172
  finite_lines = self.edges
165
173
  return FiniteLine.ary_distanc_to_point(finite_lines, target_point)
166
174
  end
175
+
176
+ def distance_to_line(target_line)
177
+ plane = Plane.new( vertices[0], self.normal )
178
+ distance, point_on_plane, parameter_on_line = plane.distance( target_line )
179
+ if( point_on_plane == nil)
180
+ # parallel case
181
+ # check distance to FiniteLines
182
+ finite_lines = self.edges
183
+ distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target =
184
+ FiniteLine.ary_distance_to_line(finite_lines, target_line)
185
+ return distance, nil, nil, nil
186
+ end
187
+ if( self.contains?(point_on_plane) )
188
+ return distance, point_on_plane, point_on_plane, parameter_on_line
189
+ end
190
+ # check distance to FiniteLines
191
+ finite_lines = self.edges
192
+ distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target =
193
+ FiniteLine.ary_distance_to_line(finite_lines, target_line)
194
+ return distance, point_on_edge, point_on_target, param_on_target
195
+ end
196
+
197
+ def distance_to_plane(target_plane)
198
+ triangle_plane = Plane.new( vertices[0], self.normal )
199
+ distance, intersect_line_each_plane = triangle_plane.distance( target_plane )
200
+ if( intersect_line_each_plane == nil )
201
+ return distance, nil, nil, nil
202
+ end
203
+
204
+ # check distance from intersection and each edge.
205
+ distance_zero_count = 0
206
+ distance_info = Array.new(0)
207
+ prallel_edge_ary = Array.new(0)
208
+ self.edges.each do |edge|
209
+ distance, point_on_edge, point_on_line = edge.distance( intersect_line_each_plane)
210
+ if point_on_edge != nil && point_on_line != nil
211
+ distance_info.push([distance, point_on_edge, point_on_line])
212
+ if distance <= self.tolerance
213
+ distance_zero_count += 1
214
+ end
215
+ else
216
+ prallel_edge_ary.push( edge )
217
+ end
218
+ end
219
+ distance_info.sort!{|a,b| a[0] <=> b[0]}
220
+ # distance, intersect_line(or closet edge), point_on_triangle, point_on_plan
221
+ if (distance_zero_count == 2)
222
+ point1 = distance_info[0][1]
223
+ point2 = distance_info[1][1]
224
+ if point1.distance(point2) > self.tolerance
225
+ return 0.0, FiniteLine.new(point1, point2), nil, nil
226
+ end
227
+ return 0.0, nil, point1, point1
228
+ elsif (distance_zero_count == 0)
229
+ distance, closest_point_on_plane = target_plane.distance(distance_info[0][1])
230
+ if(distance_info[0][1] != distance_info[1][1])
231
+ return distance, FiniteLine.new(distance_info[0][1], distance_info[1][1]), nil, nil
232
+ end
233
+ return distance, nil, distance_info[0][1], closest_point_on_plane
234
+ end
235
+ return 0.0, nil, nil, nil
236
+ end
167
237
  end
168
238
  end
169
-
@@ -24,6 +24,15 @@ class FiniteLineTestCase < MiniTest::Unit::TestCase
24
24
  assert_equal("FiniteLine[from[1, 0, 2], to[1, -3.5, 2]]", line.to_s)
25
25
  end
26
26
 
27
+ def test_equals
28
+ assert_equal(FiniteLine.new(Vector3.new(1,2,3), Vector3.new(2,3,4)),
29
+ FiniteLine.new(Vector3.new(1.0,2.0,3.0), Vector3.new(2.0,3.0,4.0)))
30
+
31
+ assert(FiniteLine.new(Vector3.new(1,2,3), Vector3.new(2,3,4)) == FiniteLine.new(Vector3.new(1.0,2.0,3.0), Vector3.new(2.0,3.0,4.0)))
32
+ assert(FiniteLine.new(Vector3.new(1,2,3), Vector3.new(2,3,3)) != FiniteLine.new(Vector3.new(1.0,2.0,3.0), Vector3.new(2.0,3.0,4.0)))
33
+ assert(FiniteLine.new(Vector3.new(1,2,3), Vector3.new(2,3,4)) != FiniteLine.new(Vector3.new(2,3,4), Vector3.new(1,2,3)))
34
+ end
35
+
27
36
  def test_direction
28
37
  start_point_tmp = Vector3.new(1.0, 0.0, 2.0)
29
38
  end_point_tmp = Vector3.new(1.0, -3.5, 1.0)
@@ -0,0 +1,84 @@
1
+ # -*- coding: cp932 -*-
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'helper'
4
+
5
+ include GMath3D
6
+
7
+ MiniTest::Unit.autorun
8
+
9
+ class MatrixTestCase < MiniTest::Unit::TestCase
10
+ def setup
11
+ @mat_default = Matrix.zero(3)
12
+ @mat = Matrix[[1,2,3],[4,5,6],[7,8,9]]
13
+ end
14
+
15
+ def test_initalize
16
+ @mat_default.each do |cell|
17
+ assert_equal(0, cell)
18
+ end
19
+
20
+ i = 1
21
+ @mat.each do |cell|
22
+ assert_equal(i, cell)
23
+ i+=1
24
+ end
25
+ end
26
+
27
+ def test_to_s
28
+ assert_equal("Matrix[[0, 0, 0], [0, 0, 0], [0, 0, 0]]", @mat_default.to_s)
29
+ assert_equal("Matrix[[1, 2, 3], [4, 5, 6], [7, 8, 9]]", @mat.to_s)
30
+ end
31
+
32
+ def test_multiply
33
+ mat = Matrix[[3,2,1],[-4,2,1],[3,4,1]]
34
+ vec = Matrix.column_vector([2,3,4])
35
+ ans = mat*vec
36
+
37
+ assert_equal(ans[0,0], 16)
38
+ assert_equal(ans[1,0], 2 )
39
+ assert_equal(ans[2,0], 22)
40
+
41
+ vec2 = Vector3.new(2,3,4)
42
+ ans2 = mat*(vec2)
43
+ assert_equal(Vector3.new(16,2,22), ans2)
44
+ end
45
+
46
+ def test_from_axis
47
+ local_point = Vector3.new(1,0,1)
48
+ angle = 45*Math::PI/180
49
+ local_cod_x = Vector3.new(Math.cos(angle), Math.sin(angle),0)
50
+ x_vec = Vector3.new(1,0,0)
51
+ diff_angle = x_vec.angle(local_cod_x)
52
+ axis = x_vec.cross(local_cod_x).normalize()
53
+
54
+ rotate_matrix = Matrix.from_axis(axis, diff_angle)
55
+ rotated_point = rotate_matrix.inv * local_point
56
+ assert_equal(Vector3.new(Math.sqrt(2)/2, Math.sqrt(2)/2, 1), rotated_point)
57
+
58
+ local_point = Vector3.new(1,0,0)
59
+ local_cod_x = Vector3.new(1,1,1).normalize()
60
+ diff_angle = x_vec.angle(local_cod_x)
61
+ axis = x_vec.cross(local_cod_x).normalize()
62
+
63
+ assert_equal(Math::PI/2, local_cod_x.angle(axis))
64
+ rotate_matrix = Matrix.from_axis(axis, diff_angle)
65
+ rotated_point = rotate_matrix.inv()*local_point
66
+
67
+ assert_in_delta(rotated_point.x, rotated_point.y, rotated_point.tolerance)
68
+ assert_in_delta(rotated_point.x, rotated_point.z, rotated_point.tolerance)
69
+ end
70
+
71
+ def test_from_quat
72
+ axis = Vector3.new(1,2,4).normalize()
73
+ angle = 46*Math::PI/180
74
+ quat = Quat.from_axis(axis, angle)
75
+ mat_expected = Matrix.from_axis(axis, angle)
76
+ mat_actual = Matrix.from_quat(quat)
77
+
78
+ [0,1,2].each do |i|
79
+ [0,1,2].each do |j|
80
+ assert_in_delta( mat_expected[i,j], mat_actual[i,j], 1e-8)
81
+ end
82
+ end
83
+ end
84
+ end
data/test/test_quat.rb ADDED
@@ -0,0 +1,135 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ require 'helper'
3
+
4
+ include GMath3D
5
+
6
+ MiniTest::Unit.autorun
7
+
8
+ class QuatTestCase < MiniTest::Unit::TestCase
9
+ def setup
10
+ @quat_default = Quat.new()
11
+ @quat1 = Quat.new(2.0, 3.0, 4.0, 1.0)
12
+ @quat2 = Quat.new(6.0, 7.0, 8.0, 5.0)
13
+ end
14
+
15
+ def test_initalize
16
+ assert_equal(0, @quat_default.x)
17
+ assert_equal(0, @quat_default.y)
18
+ assert_equal(0, @quat_default.z)
19
+ assert_equal(0, @quat_default.w)
20
+
21
+ assert_equal(2.0, @quat1.x)
22
+ assert_equal(3.0, @quat1.y)
23
+ assert_equal(4.0, @quat1.z)
24
+ assert_equal(1.0, @quat1.w)
25
+
26
+ assert_raises ArgumentError do
27
+ invalidResult = Quat.new( "hoge" )
28
+ end
29
+ end
30
+
31
+ def test_from_axis
32
+ axis = Vector3.new(1.0, 0.0, 0.0)
33
+ rad = 30.0*Math::PI / 180
34
+ rot_quat = Quat.from_axis(axis, rad)
35
+ assert_equal( Math.cos(rad/2.0), rot_quat.w)
36
+ assert_equal( Math.sin(rad/2.0), rot_quat.x)
37
+ assert_equal( 0.0, rot_quat.y)
38
+ assert_equal( 0.0, rot_quat.z)
39
+ end
40
+
41
+ def test_from_matrix
42
+ axis = Vector3.new(1,2,4).normalize()
43
+ angle = 46*Math::PI/180
44
+ mat = Matrix.from_axis(axis, angle)
45
+ quat_expected = Quat.from_axis(axis, angle)
46
+ quat_actual = Quat.from_matrix(mat)
47
+ assert_equal(quat_expected, quat_actual)
48
+ end
49
+
50
+ def test_to_s
51
+ assert_equal("Quat[2.0, 3.0, 4.0, 1.0]", @quat1.to_s)
52
+ end
53
+
54
+ def test_assign_value
55
+ assert_equal(2.0, @quat1.x)
56
+ assert_equal(3.0, @quat1.y)
57
+ assert_equal(4.0, @quat1.z)
58
+ assert_equal(1.0, @quat1.w)
59
+
60
+ @quat1.x = 2.0
61
+ @quat1.y *= 2.0
62
+ @quat1.z -= 3.0
63
+ @quat1.w /= 2.0
64
+
65
+ assert_equal(2.0, @quat1.x)
66
+ assert_equal(6.0, @quat1.y)
67
+ assert_equal(1.0, @quat1.z)
68
+ assert_equal(0.5, @quat1.w)
69
+ end
70
+
71
+ def test_equals
72
+ assert(!(@quat_default == @quat1))
73
+ assert(@quat_default != @quat1)
74
+
75
+ assert(@quat1 == @quat1)
76
+
77
+ quat = Quat.new(2,3,4,1)
78
+ assert(@quat1 == quat)
79
+
80
+ #invlid value comparison
81
+ assert(@quat1 != "string")
82
+ assert(@quat1 != -4)
83
+ end
84
+
85
+ def test_conjugate
86
+ quat1_conjugate = @quat1.conjugate()
87
+ quat2_conjugate = @quat2.conjugate()
88
+
89
+ assert_equal(-2.0, quat1_conjugate.x)
90
+ assert_equal(-3.0, quat1_conjugate.y)
91
+ assert_equal(-4.0, quat1_conjugate.z)
92
+ assert_equal( 1.0, quat1_conjugate.w)
93
+ assert_equal(-6.0, quat2_conjugate.x)
94
+ assert_equal(-7.0, quat2_conjugate.y)
95
+ assert_equal(-8.0, quat2_conjugate.z)
96
+ assert_equal( 5.0, quat2_conjugate.w)
97
+ end
98
+
99
+ def test_add
100
+ # implemented, but how to validate?
101
+ end
102
+
103
+ def test_add_invalid_value
104
+ assert_raises ArgumentError do
105
+ invalidResult = @quat1 + 5
106
+ end
107
+ assert_raises ArgumentError do
108
+ invalidResult = @quat1 + nil
109
+ end
110
+ end
111
+
112
+ def test_multiply
113
+ multiply = @quat1 * @quat2
114
+ assert_equal(-60, multiply.w)
115
+ assert_equal( 12, multiply.x)
116
+ assert_equal( 30, multiply.y)
117
+ assert_equal( 24, multiply.z)
118
+
119
+ @quat1 *= @quat2
120
+ assert_equal(-60, @quat1.w)
121
+ assert_equal( 12, @quat1.x)
122
+ assert_equal( 30, @quat1.y)
123
+ assert_equal( 24, @quat1.z)
124
+ end
125
+
126
+ def test_multiply_invalid_value
127
+ assert_raises ArgumentError do
128
+ invalidResult = @quat1 * 3
129
+ end
130
+ assert_raises ArgumentError do
131
+ invalidResult = @quat2 * nil
132
+ end
133
+ end
134
+
135
+ end
@@ -147,4 +147,110 @@ class TriangleTestCase < MiniTest::Unit::TestCase
147
147
  assert_in_delta(0, distance, @triangle_default.tolerance)
148
148
  assert_equal(@triangle_default.center, point_on_triangle)
149
149
  end
150
+
151
+ def test_distance_to_line
152
+ # on inside
153
+ line = Line.new(Vector3.new(0,3,-1), Vector3.new(0,0,4))
154
+ distance, point_on_triangle, point_on_line, param_on_line = @triangle.distance(line)
155
+ assert_equal(0, distance)
156
+ assert_equal(Vector3.new(0,3,1), point_on_triangle)
157
+ assert_equal(Vector3.new(0,3,1), point_on_line)
158
+ assert_in_delta(0.5, param_on_line, line.tolerance)
159
+
160
+ # on edge
161
+ line = Line.new(Vector3.new(1,3,-1), Vector3.new(0,0,4))
162
+ distance, point_on_triangle, point_on_line, param_on_line = @triangle.distance(line)
163
+ assert_equal(0, distance)
164
+ assert_equal(Vector3.new(1,3,2), point_on_triangle)
165
+ assert_equal(Vector3.new(1,3,2), point_on_line)
166
+ assert_in_delta(0.75, param_on_line, line.tolerance)
167
+
168
+ # on vertex
169
+ line = Line.new(Vector3.new(-1,3,0), Vector3.new(1,1,2))
170
+ distance, point_on_triangle, point_on_line, param_on_line = @triangle.distance(line)
171
+ assert_equal(0, distance)
172
+ assert_equal(Vector3.new(-1,3,0), point_on_triangle)
173
+ assert_equal(Vector3.new(-1,3,0), point_on_line)
174
+ assert_in_delta(0.0, param_on_line, line.tolerance)
175
+
176
+ # closest point is out of triangle1
177
+ line = Line.new(Vector3.new(2,3,0), Vector3.new(0,0,4))
178
+ distance, point_on_triangle, point_on_line, param_on_line = @triangle.distance(line)
179
+ assert_equal(1, distance)
180
+ assert_equal(Vector3.new(1,3,2), point_on_triangle)
181
+ assert_equal(Vector3.new(2,3,2), point_on_line)
182
+ assert_in_delta(0.5, param_on_line, line.tolerance)
183
+
184
+ # closest point is out of triangle2
185
+ line = Line.new(Vector3.new(-2,3,-1), Vector3.new(0,0,1))
186
+ distance, point_on_triangle, point_on_line, param_on_line = @triangle.distance(line)
187
+ assert_equal(1, distance)
188
+ assert_equal(Vector3.new(-1,3,0), point_on_triangle)
189
+ assert_equal(Vector3.new(-2,3,0), point_on_line)
190
+ assert_in_delta(1, param_on_line, line.tolerance)
191
+
192
+ # parallel case
193
+ line = Line.new(Vector3.new(1,0,4), Vector3.new(0,6,0))
194
+ distance, point_on_triangle, point_on_line, param_on_line = @triangle.distance(line)
195
+ assert_equal(2, distance)
196
+ assert_equal(nil, point_on_triangle)
197
+ assert_equal(nil, point_on_line)
198
+ assert_equal(nil, param_on_line)
199
+ end
200
+
201
+ def test_distance_to_plane
202
+ # intersect case
203
+ plane = Plane.new(Vector3.new(0,0,1), Vector3.new(1,0,0))
204
+ distance, intersect_line, point_on_triangle, point_on_plane = @triangle.distance(plane)
205
+ assert_equal( 0, distance )
206
+ point1 = Vector3.new(0,2.5,1)
207
+ point2 = Vector3.new(0,3.5,1)
208
+ assert( FiniteLine.new(point1, point2) == intersect_line || FiniteLine.new(point2, point1) == intersect_line)
209
+ assert_equal( nil, point_on_triangle )
210
+ assert_equal( nil, point_on_plane )
211
+
212
+ # contains edge
213
+ plane = Plane.new(Vector3.new(1,0,1), Vector3.new(-1,0,0))
214
+ distance, intersect_line, point_on_triangle, point_on_plane = @triangle.distance(plane)
215
+ assert_equal( 0, distance )
216
+ point1 = Vector3.new(1,2,2)
217
+ point2 = Vector3.new(1,4,2)
218
+ assert( FiniteLine.new(point1, point2) == intersect_line || FiniteLine.new(point2, point1) == intersect_line)
219
+ assert_equal( nil, point_on_triangle )
220
+ assert_equal( nil, point_on_plane )
221
+
222
+ # contains vertex
223
+ plane = Plane.new(Vector3.new(-1,0,1), Vector3.new(1,0,0))
224
+ distance, intersect_line, point_on_triangle, point_on_plane = @triangle.distance(plane)
225
+ assert_equal( 0, distance )
226
+ assert_equal( nil, intersect_line)
227
+ assert_equal( Vector3.new(-1,3,0), point_on_triangle )
228
+ assert_equal( Vector3.new(-1,3,0), point_on_plane )
229
+
230
+ # closeing point is outside of triangle
231
+ plane = Plane.new(Vector3.new(-2,0,0), Vector3.new(-1,0,0))
232
+ distance, intersect_line, point_on_triangle, point_on_plane = @triangle.distance(plane)
233
+ assert_equal( 1, distance )
234
+ assert_equal( nil, intersect_line)
235
+ assert_equal( Vector3.new(-1,3,0), point_on_triangle )
236
+ assert_equal( Vector3.new(-2,3,0), point_on_plane )
237
+
238
+ # parallel to plane
239
+ plane = Plane.new(Vector3.new(0,0,4), Vector3.new(0,0,1))
240
+ distance, intersect_line, point_on_triangle, point_on_plane = @triangle_default.distance(plane)
241
+ assert_equal( 4, distance )
242
+ assert_equal( nil, intersect_line)
243
+ assert_equal( nil, point_on_triangle )
244
+ assert_equal( nil, point_on_plane )
245
+
246
+ # parallel to edge
247
+ plane = Plane.new(Vector3.new(3.5,0,3), Vector3.new(-1,0,0))
248
+ distance, closest_line, point_on_triangle, point_on_plane = @triangle.distance(plane)
249
+ assert_equal( 2.5, distance )
250
+ point1 = Vector3.new(1,2,2)
251
+ point2 = Vector3.new(1,4,2)
252
+ assert( FiniteLine.new(point1, point2) == closest_line || FiniteLine.new(point2, point1) == closest_line)
253
+ assert_equal( nil, point_on_triangle )
254
+ assert_equal( nil, point_on_plane )
255
+ end
150
256
  end
data/test/test_vector3.rb CHANGED
@@ -72,6 +72,8 @@ class Vector3TestCase < MiniTest::Unit::TestCase
72
72
  vector = Vector3.new(1.0 - floatingError2, 2.0 + floatingError2, 3.0)
73
73
  assert(@vector != vector)
74
74
 
75
+ assert_equal(Vector3.new(1,2,3), Vector3.new(1.0,2.0,3.0))
76
+
75
77
  #invlid value comparison
76
78
  assert(@vector != "string")
77
79
  assert(@vector != -4)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gmath3D
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-09 00:00:00.000000000 +09:00
12
+ date: 2011-10-19 00:00:00.000000000 +09:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
17
- requirement: &2176871500 !ruby/object:Gem::Requirement
17
+ requirement: &2156099140 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 1.0.0
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *2176871500
25
+ version_requirements: *2156099140
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: jeweler
28
- requirement: &2176871020 !ruby/object:Gem::Requirement
28
+ requirement: &2156097640 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: 1.6.4
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *2176871020
36
+ version_requirements: *2156097640
37
37
  description: This library defines 3D geometric elements(point, line, plane etc..).
38
38
  It can get two(or more) elements relation, like distance between two elements.
39
39
  email: toshi0328@gmail.com
@@ -56,7 +56,9 @@ files:
56
56
  - lib/geom.rb
57
57
  - lib/gmath3D.rb
58
58
  - lib/line.rb
59
+ - lib/matrix_util.rb
59
60
  - lib/plane.rb
61
+ - lib/quat.rb
60
62
  - lib/rectangle.rb
61
63
  - lib/triangle.rb
62
64
  - lib/util.rb
@@ -65,9 +67,10 @@ files:
65
67
  - test/test_box.rb
66
68
  - test/test_finite_line.rb
67
69
  - test/test_geom.rb
68
- - test/test_gmath3D.rb
69
70
  - test/test_line.rb
71
+ - test/test_matrix_util.rb
70
72
  - test/test_plane.rb
73
+ - test/test_quat.rb
71
74
  - test/test_rectangle.rb
72
75
  - test/test_triangle.rb
73
76
  - test/test_util.rb
@@ -88,7 +91,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
91
  version: '0'
89
92
  segments:
90
93
  - 0
91
- hash: 3189821684836837166
94
+ hash: 3561879194129992333
92
95
  required_rubygems_version: !ruby/object:Gem::Requirement
93
96
  none: false
94
97
  requirements:
data/test/test_gmath3D.rb DELETED
@@ -1,15 +0,0 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
-
3
- require 'helper'
4
- require 'test_geom'
5
- require 'test_util'
6
- require 'test_vector3'
7
- require 'test_line'
8
- require 'test_finite_line'
9
- require 'test_plane'
10
- require 'test_rectangle'
11
- require 'test_triangle'
12
- require 'test_box'
13
-
14
-
15
-