gmath3D 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
-