eymiha_math3 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/gem_package.rb +2 -2
- data/lib/eymiha/math3.rb +7 -0
- data/lib/eymiha/math3/envelope3.rb +100 -0
- data/lib/eymiha/math3/math3.rb +100 -0
- data/lib/eymiha/math3/point3.rb +283 -0
- data/lib/eymiha/math3/point3c.rb +127 -0
- data/lib/eymiha/math3/point3s.rb +132 -0
- data/lib/eymiha/math3/quaternion.rb +246 -0
- metadata +10 -2
data/gem_package.rb
CHANGED
data/lib/eymiha/math3.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'eymiha/math3'
|
2
|
+
require 'eymiha/util/envelope'
|
3
|
+
|
4
|
+
module Eymiha
|
5
|
+
|
6
|
+
# Envelope3 is the minimum 3D envelope that will completely contain a set of
|
7
|
+
# 3D points. 3D cartersian points or other 3D envelopes may be added to an
|
8
|
+
# instance, and it can return the number or points considered so far, the
|
9
|
+
# x, y and z envelopes, and the high and low Point3 boundaries.
|
10
|
+
class Envelope3 < BaseEnvelope
|
11
|
+
|
12
|
+
include ThreeDimensions
|
13
|
+
|
14
|
+
# 3D cartesian x coordinate envelope reader.
|
15
|
+
attr_reader :x
|
16
|
+
# 3D cartesian y coordinate envelope reader.
|
17
|
+
attr_reader :y
|
18
|
+
# 3D cartesian z coordinate envelope reader.
|
19
|
+
attr_reader :z
|
20
|
+
|
21
|
+
# Creates and returns an instance. If arguments are given, they are passed to
|
22
|
+
# the set method to initialize the new instance.
|
23
|
+
def initialize(x=nil,y=0,z=0)
|
24
|
+
super()
|
25
|
+
@x = Envelope.new
|
26
|
+
@y = Envelope.new
|
27
|
+
@z = Envelope.new
|
28
|
+
add(x,y,z) unless x == nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a string representation of the instance.
|
32
|
+
def to_s
|
33
|
+
values = (count > 0)? "\n high #{high}\n low #{low}" : ""
|
34
|
+
"Envelope3: count #{count}#{values}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Adds a value to the instance. When
|
38
|
+
# * x is an Envelope3, it is coalesced into the instance.
|
39
|
+
# * x responds like a Point3, its coordinates are added.
|
40
|
+
# * x us Numeric, the arguments are added.
|
41
|
+
# * otherwise, an EnvelopeException is raised.
|
42
|
+
# The modified instance is returned.
|
43
|
+
def add(x=0,y=0,z=0)
|
44
|
+
if x.kind_of? Envelope3
|
45
|
+
count = x.count
|
46
|
+
if count > 0
|
47
|
+
add x.high
|
48
|
+
add x.low
|
49
|
+
@count += (count-2)
|
50
|
+
end
|
51
|
+
self
|
52
|
+
elsif x.point3_like?
|
53
|
+
add x.x, x.y, x.z
|
54
|
+
elsif x.kind_of? Numeric
|
55
|
+
@x.add x
|
56
|
+
@y.add y
|
57
|
+
@z.add z
|
58
|
+
@count += 1
|
59
|
+
self
|
60
|
+
else
|
61
|
+
raise_no_compare x
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a Point3 whose coordinates are the high values of the x, y, and z
|
66
|
+
# envelopes.
|
67
|
+
# * if there are no boundaries, an EnvelopeException is raised.
|
68
|
+
def high
|
69
|
+
raise_no_envelope if @count == 0
|
70
|
+
(@high ||= Point3.new).set @x.high, @y.high, @z.high
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns a Point3 whose coordinates are the low values of the x, y, and z
|
74
|
+
# envelopes.
|
75
|
+
# * if there are no boundaries, an EnvelopeException is raised.
|
76
|
+
def low
|
77
|
+
raise_no_envelope if @count == 0
|
78
|
+
(@low ||= Point3.new).set @x.low, @y.low, @z.low
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns true if the instance completely contains the arguments:
|
82
|
+
# * x is an Envelope3, its high and low are contained.
|
83
|
+
# * x responds like a Point3, its coordinates are contained.
|
84
|
+
# * x is Numeric, the arguments are contained.
|
85
|
+
# * otherwise, false is returned.
|
86
|
+
def contains?(x=0,y=0,z=0)
|
87
|
+
if x.kind_of? Envelope3
|
88
|
+
(contains? x.high) && (contains? x.low)
|
89
|
+
elsif x.point3_like?
|
90
|
+
contains?(x.x,x.y,x.z)
|
91
|
+
elsif x.kind_of? Numeric
|
92
|
+
(@x.contains? x) && (@y.contains? y) && (@z.contains? z)
|
93
|
+
else
|
94
|
+
raise_no_compare x
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'eymiha'
|
2
|
+
require 'eymiha/math'
|
3
|
+
|
4
|
+
# These modifications to objects add 3D point duck classifying.
|
5
|
+
class Object
|
6
|
+
|
7
|
+
# Returns true if the instance has 3D cartesian coordinates, ie. responds
|
8
|
+
# to x, y and z.
|
9
|
+
def point3_like?
|
10
|
+
(respond_to? :x)&&(respond_to? :y)&&(respond_to? :z)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns true if the instance has 3D spherical coordinates, ie. responds
|
14
|
+
# to s_radius, theta and phi.
|
15
|
+
def point3s_like?
|
16
|
+
(respond_to? :s_radius)&&(respond_to? :theta)&&(respond_to? :phi)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns true if the instance has 3D cylindrical coordinates, ie responds
|
20
|
+
# to c_radius, theta and z.
|
21
|
+
def point3c_like?
|
22
|
+
(respond_to? :c_radius)&&(respond_to? :theta)&&(respond_to? :z)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
# The ThreeDimensions module adds shorthands for common 3D entities, mostly to
|
28
|
+
# improve code readability.
|
29
|
+
module Eymiha
|
30
|
+
module ThreeDimensions
|
31
|
+
|
32
|
+
# shorthand for 2*pi.
|
33
|
+
def two_pi
|
34
|
+
2.0*Math::PI
|
35
|
+
end
|
36
|
+
|
37
|
+
# shorthand for pi.
|
38
|
+
def pi
|
39
|
+
Math::PI
|
40
|
+
end
|
41
|
+
|
42
|
+
# shorthand for pi/2.
|
43
|
+
def half_pi
|
44
|
+
Math::PI/2.0
|
45
|
+
end
|
46
|
+
|
47
|
+
# shorthand for the square root of 2.
|
48
|
+
def sqrt2
|
49
|
+
Math.sqrt 2
|
50
|
+
end
|
51
|
+
|
52
|
+
# shorthand for the square root of 3.
|
53
|
+
def sqrt3
|
54
|
+
Math.sqrt 3
|
55
|
+
end
|
56
|
+
|
57
|
+
# the origin of 3D space.
|
58
|
+
def origin
|
59
|
+
Point3.origin
|
60
|
+
end
|
61
|
+
|
62
|
+
# shorthand for creating a 3D point in cartesian coordinates.
|
63
|
+
def point3(x=0, y=0, z=0)
|
64
|
+
Point3.new(x,y,z)
|
65
|
+
end
|
66
|
+
|
67
|
+
# shorthand for creating a 3D point in spherical coordinates.
|
68
|
+
def point3s(s_radius=0, theta=0, phi=0)
|
69
|
+
Point3s.new(s_radius,theta,phi)
|
70
|
+
end
|
71
|
+
|
72
|
+
# shorthand for creating a 3D point in cylindrical coordinates.
|
73
|
+
def point3c(c_radius=0, theta=0, z=0)
|
74
|
+
Point3c.new(c_radius,theta,z)
|
75
|
+
end
|
76
|
+
|
77
|
+
# shorthand for creating a quaternion.
|
78
|
+
def quaternion(axis=origin,real=0)
|
79
|
+
Quaternion.new(axis,real)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
class Numeric
|
87
|
+
|
88
|
+
include Eymiha::ThreeDimensions
|
89
|
+
|
90
|
+
# Returns the instance's value folded to lie between [0, 2*pi).
|
91
|
+
def rectify_theta
|
92
|
+
wrap_rectify two_pi
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the instance's value cut to lie between [0, pi].
|
96
|
+
def rectify_phi
|
97
|
+
cut_rectify pi
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'eymiha/math3'
|
2
|
+
|
3
|
+
module Eymiha
|
4
|
+
|
5
|
+
# Point3 represents a 3D point in cartesian coordinates:
|
6
|
+
# * x is the signed distance from the 3D origin along the x axis.
|
7
|
+
# * y is the signed distance from the 3D origin along the y axis.
|
8
|
+
# * z is the signed distance from the 3D origin along the z axis.
|
9
|
+
# The cylindrical z coordinate is equal to the cartesian z coordinate.
|
10
|
+
#
|
11
|
+
# Point3 instances may be converted to Point3s and Point3c instances, but
|
12
|
+
# information at the "boundaries" may be lost. Besides responding as a Point3,
|
13
|
+
# an instance will also respond like a Point3s and Point3c as it has a full
|
14
|
+
# complement of readers for the different coordinate systems.
|
15
|
+
class Point3
|
16
|
+
|
17
|
+
include ThreeDimensions
|
18
|
+
|
19
|
+
# x coordinate reader and writer.
|
20
|
+
attr_accessor :x
|
21
|
+
# y coordinate reader and writer.
|
22
|
+
attr_accessor :y
|
23
|
+
# z coordinate reader and writer.
|
24
|
+
attr_accessor :z
|
25
|
+
|
26
|
+
# Returns the origin of 3D space, where x, y, and z all have zero values.
|
27
|
+
def Point3.origin
|
28
|
+
@@origin3
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates and returns a Point3 instance. Sets the coordinates values using
|
32
|
+
# the set method.
|
33
|
+
def initialize(x=0, y=0, z=0)
|
34
|
+
set x, y, z
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns a string representation of the instance.
|
38
|
+
def to_s
|
39
|
+
"Point3: x #{x} y #{y} z #{z}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sets the coordinate values of the instance. When
|
43
|
+
# * x is Numeric, the arguments are interpretted as coordinates.
|
44
|
+
# * x responds like a Point3, its cartesian coordinates are assigned.
|
45
|
+
# * otherwise a TypeError is raised.
|
46
|
+
# The modified instance is returned.
|
47
|
+
def set(x=0, y=0, z=0)
|
48
|
+
if x.kind_of? Numeric
|
49
|
+
@x, @y, @z = 1.0*x, 1.0*y, 1.0*z
|
50
|
+
elsif x.point3_like?
|
51
|
+
set x.x, x.y, x.z
|
52
|
+
else
|
53
|
+
raise_no_conversion x
|
54
|
+
end
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns true if the coordinates of the instance are equal to the
|
59
|
+
# coordinates of the given point.
|
60
|
+
def ==(point3)
|
61
|
+
(@x == point3.x)&&(@y == point3.y)&&(@z == point3.z)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns true if the coordinates of the instance are approximately equal
|
65
|
+
# to the coordinates of the given point, each coordinate less than a distance
|
66
|
+
# epsilon from the target.
|
67
|
+
def approximately_equals?(point3,epsilon=Numeric.epsilon)
|
68
|
+
@x.approximately_equals?(point3.x,epsilon)&&
|
69
|
+
@y.approximately_equals?(point3.y,epsilon)&&
|
70
|
+
@z.approximately_equals?(point3.z,epsilon)
|
71
|
+
end
|
72
|
+
|
73
|
+
alias =~ approximately_equals?
|
74
|
+
|
75
|
+
# Returns a copy of point3 with the given cartesian coordinates:
|
76
|
+
# * x is Numeric, the arguments are copied as the coordinates.
|
77
|
+
# * x responds like a Point3, its coordinates are copied.
|
78
|
+
# * otherwise a TypeError is raised.
|
79
|
+
def point3(x=self.x,y=self.y,z=self.z)
|
80
|
+
Point3.new(x,y,z)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns a 3D point in spherical coordinates, filling point3s if given,
|
84
|
+
# and copied from point3.
|
85
|
+
def point3s(point3s=nil,point3=self)
|
86
|
+
(point3s ||= Point3s.new).set(point3.s_radius,point3.theta,point3.phi)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a 3D point in cylindrical coordinates, filling point3c if given,
|
90
|
+
# and copied from point3.
|
91
|
+
def point3c(point3c=nil,point3=self)
|
92
|
+
(point3c ||= Point3c.new).set(point3.c_radius,point3.theta,point3.z)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the spherical radius coordinate of the instance.
|
96
|
+
def s_radius
|
97
|
+
modulus
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the cylindrical radius coordinate of the instance.
|
101
|
+
def c_radius
|
102
|
+
Math.sqrt((@x*@x)+(@y*@y))
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the spherical/cylindrical theta coordinate of the instance.
|
106
|
+
def theta
|
107
|
+
(@x == 0)? ((@y > 0)? @@pio2 : -@@pio2) : Math.atan2(@y,@x)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns the spherical phi coordinate of the instance.
|
111
|
+
def phi
|
112
|
+
m = modulus
|
113
|
+
(m == 0)? 0 : Math.acos(z/m)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the 3D distance from the instance to point3.
|
117
|
+
def distance_to(point3)
|
118
|
+
Math.sqrt(((@x-point3.x)**2)+((@y-point3.y)**2)+((@z-point3.z)**2))
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns the 3D distance from the instance to the origin.
|
122
|
+
def modulus
|
123
|
+
distance_to(origin)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the dot product of the vectors represented by the instance and
|
127
|
+
# point3, with common endpoints at the origin.
|
128
|
+
def dot(point3)
|
129
|
+
(@x*point3.x)+(@y*point3.y)+(@z*point3.z)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns a new Point3 instance whose coordinates are the original
|
133
|
+
# instance's mirrored through the origin.
|
134
|
+
def mirror
|
135
|
+
point3.mirror!
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns the modified instance whose coordinates have been mirrored through
|
139
|
+
# the origin.
|
140
|
+
def mirror!
|
141
|
+
set(-@x, -@y, -@z)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a new Point3 instance whose coordinates are the original
|
145
|
+
# instance's with the given amounts added:
|
146
|
+
# * x is Numeric, the arguments are added to the coordinates.
|
147
|
+
# * x responds like a Point3, its cartesian coordinates are added.
|
148
|
+
# * otherwise a TypeError is raised.
|
149
|
+
def add(x=0,y=0,z=0)
|
150
|
+
point3.add!(x,y,z)
|
151
|
+
end
|
152
|
+
|
153
|
+
alias + add
|
154
|
+
|
155
|
+
# Returns the modified instance with the arguments added.
|
156
|
+
def add!(x=0,y=0,z=0)
|
157
|
+
if x.kind_of? Numeric
|
158
|
+
set(@x+x, @y+y, @z+z)
|
159
|
+
elsif x.point3_like?
|
160
|
+
add! x.x, x.y, x.z
|
161
|
+
else
|
162
|
+
raise_no_conversion x
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns a new Point3 instance whose coordinates are the original
|
167
|
+
# instance's with the given amounts subtracted:
|
168
|
+
# * x is Numeric, the arguments are subtracted from the coordinates.
|
169
|
+
# * x responds like a Point3, its cartesian coordinates are subtracted.
|
170
|
+
# * otherwise a TypeError is raised.
|
171
|
+
def subtract(x=0,y=0,z=0)
|
172
|
+
point3.subtract!(x,y,z)
|
173
|
+
end
|
174
|
+
|
175
|
+
alias - subtract
|
176
|
+
|
177
|
+
# Returns the modified instance with the arguments subtracted.
|
178
|
+
def subtract!(x=0,y=0,z=0)
|
179
|
+
if x.kind_of? Numeric
|
180
|
+
add!(-x, -y, -z)
|
181
|
+
elsif x.point3_like?
|
182
|
+
subtract! x.x, x.y, x.z
|
183
|
+
else
|
184
|
+
raise_no_conversion x
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Returns a new Point3 instance whose coordinates are the original
|
189
|
+
# instance's multiplied by the given amounts:
|
190
|
+
# * x is Numeric, the coordinates are multiplied by the arguments.
|
191
|
+
# * x responds like a Point3, the instance's coordinates are multiplied by x's coordinates.
|
192
|
+
# * otherwise a TypeError is raised.
|
193
|
+
def multiply(x=1,y=1,z=1)
|
194
|
+
point3.multiply!(x,y,z)
|
195
|
+
end
|
196
|
+
|
197
|
+
alias * multiply
|
198
|
+
|
199
|
+
# Returns the modified instance as multiplied by the arguments.
|
200
|
+
def multiply!(x=1,y=1,z=1)
|
201
|
+
if x.kind_of? Numeric
|
202
|
+
set(@x*x, @y*y, @z*z)
|
203
|
+
elsif x.point3_like?
|
204
|
+
multiply! x.x, x.y, x.z
|
205
|
+
else
|
206
|
+
raise_no_conversion x
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns a new Point3 instance whose coordinates are the original
|
211
|
+
# instance's multiplied by the scalar.
|
212
|
+
# * scalar is Numeric, the arguments are multiplied by the coordinates.
|
213
|
+
# * x responds like a Point3, the instance is multiplied by the scalar.
|
214
|
+
# * otherwise a TypeError is raised.
|
215
|
+
def scale(scalar=1)
|
216
|
+
point3.scale!(scalar)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Returns the modified instance as multiplied by the scalar.
|
220
|
+
def scale!(scalar=1)
|
221
|
+
if scalar.kind_of? Numeric
|
222
|
+
multiply! scalar, scalar, scalar
|
223
|
+
elsif scalar.point3_like?
|
224
|
+
multiply! scalar
|
225
|
+
else
|
226
|
+
raise_no_conversion scalar
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Returns a new Point3 instance representing the unit vector (with the same
|
231
|
+
# direction as the original instance, but whose length is 1.)
|
232
|
+
def unit(x=1,y=1,z=1)
|
233
|
+
point3.unit!
|
234
|
+
end
|
235
|
+
|
236
|
+
# Returns the modified instance as the unit vector.
|
237
|
+
def unit!(x=1,y=1,z=1)
|
238
|
+
scale!(1/modulus)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Returns a new Point3 instance that is the cross product of the given
|
242
|
+
# arguments treated as vectors with endpoints at the origin:
|
243
|
+
# * x is Numeric, the cross product of the instance with the arguments.
|
244
|
+
# * x responds like a Point3,
|
245
|
+
# * y is Numeric, the cross product of the instance with x's coordinates.
|
246
|
+
# * y responds like a Point3, the cross product of x with y.
|
247
|
+
# * otherwise a TypeError is raised.
|
248
|
+
def cross(x=1/sqrt3,y=1/sqrt3,z=1/sqrt3)
|
249
|
+
point3.cross!(x,y,z)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Returns the modified instance as the cross product.
|
253
|
+
def cross!(x=1/sqrt3,y=1/sqrt3,z=1/sqrt3)
|
254
|
+
if x.kind_of? Numeric
|
255
|
+
set((@y*z)-(@z*y), (@z*x)-(@x*z), (@x*y)-(@y*x))
|
256
|
+
elsif x.point3_like?
|
257
|
+
if y.kind_of? Numeric
|
258
|
+
cross! x.x, x.y, x.z
|
259
|
+
elsif y.point3_like?
|
260
|
+
set(x).cross!(y)
|
261
|
+
else
|
262
|
+
raise_no_conversion y
|
263
|
+
end
|
264
|
+
else
|
265
|
+
raise_no_conversion x
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Returns a new Point3 that is a distance d from the instance along the
|
270
|
+
# line to the Point3 e. If normalized is true, the d argument specifies
|
271
|
+
# the fraction of the distance from the instance (being 0) to e (being 1).
|
272
|
+
#If normalize is false, the d argument specifies an absolute distance.
|
273
|
+
def to_along (e, d, normalize=true)
|
274
|
+
scalar = normalize ? (distance_to e) : 1
|
275
|
+
point3(e).subtract!(self).unit!.scale!(d*scalar).add(self)
|
276
|
+
end
|
277
|
+
|
278
|
+
# The 3D origin
|
279
|
+
@@origin3 = Point3.new
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'eymiha/math3'
|
2
|
+
|
3
|
+
module Eymiha
|
4
|
+
|
5
|
+
# Point3c represents a 3D point in cylindrical coordinates:
|
6
|
+
# * c_radius is the distance from the cylindrical axis.
|
7
|
+
# * theta is the angular distance around the cylindrical axis.
|
8
|
+
# * z is the distance from the cylindrical base-plane.
|
9
|
+
# The cylindrical theta coordinate is equal to the spherical theta. The
|
10
|
+
# cylindrical z coordinate is equal to the cartesian z coordinate.
|
11
|
+
#
|
12
|
+
# From a cartesian reference, the cylindrical axis is the z axis, theta is
|
13
|
+
# measured using the right hand rule with the positive x axis representing 0,
|
14
|
+
# and the cylindrical plane is the plane z=0.
|
15
|
+
#
|
16
|
+
# Point3c instances may be converted to Point3 and Point3s instances, but
|
17
|
+
# information at the "boundaries" may be lost. Besides responding as a Point3c,
|
18
|
+
# an instance will also respond like a Point3 and Point3s as it has a full
|
19
|
+
# complement of readers for the different coordinate systems.
|
20
|
+
class Point3c
|
21
|
+
|
22
|
+
include ThreeDimensions
|
23
|
+
|
24
|
+
# cylindrical radius coordinate reader and writer.
|
25
|
+
attr_accessor :c_radius
|
26
|
+
# cylindrical z coordinate reader and writer.
|
27
|
+
attr_accessor :z
|
28
|
+
# cylindrical theta reader.
|
29
|
+
attr_reader :theta
|
30
|
+
|
31
|
+
# cylindrical theta coordinate writer, rectifying theta to a value in
|
32
|
+
# [0, 2*pi).
|
33
|
+
def theta=(theta)
|
34
|
+
@theta = theta.rectify_theta
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates and returns a Point3c instance. Sets the coordinates values using
|
38
|
+
# the set method.
|
39
|
+
def initialize(c_radius=0, theta=0, z=0)
|
40
|
+
set c_radius, theta, z
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a string representation of the instance.
|
44
|
+
def to_s
|
45
|
+
"Point3c: c_radius #{c_radius} theta #{theta} z #{z}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sets the coordinate values of the instance. When
|
49
|
+
# * c_radius is Numeric, the arguments are interpretted as coordinates.
|
50
|
+
# * c_radius responds like a Point3c, its cylindrical coordinates are assigned.
|
51
|
+
# * otherwise a TypeError is raised.
|
52
|
+
# The modified instance is returned.
|
53
|
+
def set(c_radius=0, theta=0, z=0)
|
54
|
+
if c_radius.kind_of? Numeric
|
55
|
+
@c_radius, @theta, @z = 1.0*c_radius, (1.0*theta).rectify_theta, 1.0*z
|
56
|
+
elsif c_radius.point3c_like?
|
57
|
+
set c_radius.c_radius, c_radius.theta, c_radius.z
|
58
|
+
else
|
59
|
+
raise_no_conversion c_radius
|
60
|
+
end
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns true if the coordinates of the instance are effectively equal to the
|
65
|
+
# coordinates of the given point.
|
66
|
+
def ==(point3c)
|
67
|
+
((point3c.c_radius == c_radius) && (point3c.z == z)) &&
|
68
|
+
(((c_radius == 0) && (z == 0)) || (point3c.theta == theta))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns true if the coordinates of the instance are approximately
|
72
|
+
# effectively equal to the coordinates of the given point, each coordinate
|
73
|
+
# less than a distance epsilon from the target.
|
74
|
+
def approximately_equals?(point3c,epsilon=Numeric.epsilon)
|
75
|
+
(@c_radius.approximately_equals?(point3c.c_radius,epsilon) &&
|
76
|
+
@z.approximately_equals?(point3c.z,epsilon)) &&
|
77
|
+
((@c_radius.approximately_equals?(0,epsilon) &&
|
78
|
+
@z.approximately_equals?(0,epsilon)) ||
|
79
|
+
@theta.approximately_equals?(point3c.theta,epsilon))
|
80
|
+
end
|
81
|
+
|
82
|
+
alias =~ approximately_equals?
|
83
|
+
|
84
|
+
# Returns a 3D point in cartesian coordinates, filling point3 if given,
|
85
|
+
# and copied from point3c.
|
86
|
+
def point3(point3=nil,point3c=self)
|
87
|
+
(point3 ||= Point3.new).set(point3c.x,point3c.y,point3c.z)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a 3D point in spherical coordinates, filling point3s if given,
|
91
|
+
# and copied from point3c.
|
92
|
+
def point3s(point3s=nil,point3c=self)
|
93
|
+
(point3s ||= Point3s.new).set(point3c.s_radius,point3c.theta,point3c.phi)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns a copy of point3c with the given cylindrical coordinates:
|
97
|
+
# * c_radius is Numeric, the arguments are copied as the coordinates.
|
98
|
+
# * c_radius responds like a Point3c, its coordinates are copied.
|
99
|
+
# * otherwise a TypeError is raised.
|
100
|
+
def point3c(c_radius=self.c_radius,theta=self.theta,z=self.z)
|
101
|
+
Point3c.new(c_radius,theta,z)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the cartesian x coordinate of the instance.
|
105
|
+
def x
|
106
|
+
c_radius*Math.cos(theta)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the cartesian y coordinate of the instance.
|
110
|
+
def y
|
111
|
+
c_radius*Math.sin(theta)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the spherical radius coordinate of the instance.
|
115
|
+
def s_radius
|
116
|
+
Math.sqrt((x**2)+(y**2)+(z**2))
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns the spherical phi coordinate of the instance.
|
120
|
+
def phi
|
121
|
+
m = s_radius
|
122
|
+
(m == 0)? 0 : Math.acos(z/m)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'eymiha/math3'
|
2
|
+
|
3
|
+
module Eymiha
|
4
|
+
|
5
|
+
# Point3s represents a 3D point in spherical coordinates:
|
6
|
+
# * s_radius is the distance from the origin.
|
7
|
+
# * theta is the angular distance around the cylindrical axis.
|
8
|
+
# * phi is angle that rotates the cylindrical axis to be directed at the point.
|
9
|
+
# The spherical theta coordinate is equal to the cylindrical theta.
|
10
|
+
#
|
11
|
+
# From a cartesian reference, the cylindrical axis is the z axis, theta is
|
12
|
+
# measured using the right hand rule with the positive x axis representing 0,
|
13
|
+
# and the phi is an angle measured from the positive z axis.
|
14
|
+
#
|
15
|
+
# Point3s instances may be converted to Point3 and Point3c instances, but
|
16
|
+
# information at the "boundaries" may be lost. Besides responding as a Point3s,
|
17
|
+
# an instance will also respond like a Point3 and Point3c as it has a full
|
18
|
+
# complement of readers for the different coordinate systems.
|
19
|
+
class Point3s
|
20
|
+
|
21
|
+
include ThreeDimensions
|
22
|
+
|
23
|
+
# spherical radius coordinate reader and writer.
|
24
|
+
attr_accessor :s_radius
|
25
|
+
# spherical theta coordinate reader.
|
26
|
+
attr_reader :theta
|
27
|
+
# spherical phi coordinate reader.
|
28
|
+
attr_reader :phi
|
29
|
+
|
30
|
+
# spherical coordinate theta writer, rectifying theta to a value in
|
31
|
+
# [0, 2*pi).
|
32
|
+
def theta=(theta)
|
33
|
+
@theta = theta.rectify_theta
|
34
|
+
end
|
35
|
+
|
36
|
+
# spherical coordinate theta writer, rectifying theta to a value in
|
37
|
+
# [0, pi].
|
38
|
+
def phi=(phi)
|
39
|
+
@phi = phi.rectify_phi
|
40
|
+
end
|
41
|
+
|
42
|
+
# Creates and returns a Point3s instance. Sets the coordinates values using
|
43
|
+
# the set method.
|
44
|
+
def initialize(s_radius=0, theta=0, phi=0)
|
45
|
+
set s_radius, theta, phi
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a string representation of the instance.
|
49
|
+
def to_s
|
50
|
+
"Point3s: s_radius #{s_radius} theta #{theta} phi #{phi}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns a string representation of the instance.
|
54
|
+
# Sets the coordinate values of the instance. When
|
55
|
+
# * s_radius is Numeric, the arguments are interpretted as coordinates.
|
56
|
+
# * s_radius responds like a Point3c, its spherical coordinates are assigned.
|
57
|
+
# * otherwise a TypeError is raised.
|
58
|
+
# The modified instance is returned.
|
59
|
+
def set(s_radius=0, theta=0, phi=0)
|
60
|
+
if s_radius.kind_of? Numeric
|
61
|
+
@s_radius, @theta, @phi =
|
62
|
+
1.0*s_radius, (1.0*theta).rectify_theta, (1.0*phi).rectify_phi
|
63
|
+
elsif s_radius.point3s_like?
|
64
|
+
set s_radius.s_radius, s_radius.theta, s_radius.phi
|
65
|
+
else
|
66
|
+
raise_no_conversion s_radius
|
67
|
+
end
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns true if the coordinates of the instance are effectively equal to the
|
72
|
+
# coordinates of the given point.
|
73
|
+
def ==(point3s)
|
74
|
+
(point3s.s_radius == s_radius) &&
|
75
|
+
((s_radius == 0) || ((point3s.theta == theta) && (point3s.phi == phi)))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns true if the coordinates of the instance are approximately
|
79
|
+
# effectively equal to the coordinates of the given point, each coordinate
|
80
|
+
# less than a distance epsilon from the target.
|
81
|
+
def approximately_equals?(point3s,epsilon=Numeric.epsilon)
|
82
|
+
(@s_radius.approximately_equals?(point3s.s_radius,epsilon)&&
|
83
|
+
((@s_radius.approximately_equals?(0,epsilon) ||
|
84
|
+
(@theta.approximately_equals?(point3s.theta,epsilon)&&
|
85
|
+
@phi.approximately_equals?(point3s.phi,epsilon)))))
|
86
|
+
end
|
87
|
+
|
88
|
+
alias =~ approximately_equals?
|
89
|
+
|
90
|
+
# Returns a 3D point in cartesian coordinates, filling point3 if given,
|
91
|
+
# and copied from point3s.
|
92
|
+
def point3(point3=nil, point3s=self)
|
93
|
+
(point3 ||= Point3.new).set(point3s.x,point3s.y,point3s.z)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns a copy of point3s with the given spherical coordinates:
|
97
|
+
# * s_radius is Numeric, the arguments are copied as the coordinates.
|
98
|
+
# * s_radius responds like a Point3c, its coordinates are copied.
|
99
|
+
# * otherwise a TypeError is raised.
|
100
|
+
def point3s(s_radius=self.s_radius,theta=self.theta,phi=self.phi)
|
101
|
+
Point3s.new(s_radius,theta,phi)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns a 3D point in cartesian coordinates, filling point3 if given,
|
105
|
+
# and copied from point3s.
|
106
|
+
def point3c(point3c=nil,point3s=self)
|
107
|
+
(point3c ||= Point3c.new).set(point3s.c_radius,point3s.theta,point3s.z)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns the cartesian x coordinate of the instance.
|
111
|
+
def x
|
112
|
+
s_radius*Math.cos(theta)*Math.sin(phi)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the cartesian y coordinate of the instance.
|
116
|
+
def y
|
117
|
+
s_radius*Math.sin(theta)*Math.sin(phi)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns the cartesian z coordinate of the instance.
|
121
|
+
def z
|
122
|
+
s_radius*Math.cos(phi);
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the cylindrical radius coordinate of the instance.
|
126
|
+
def c_radius
|
127
|
+
s_radius*Math.sin(phi)
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'eymiha/math3'
|
2
|
+
|
3
|
+
module Eymiha
|
4
|
+
|
5
|
+
# Quaternion instances represents Quaternions, complex numbers with one real
|
6
|
+
# part and three imaginary parts. While the math is a bit obscure, they can be
|
7
|
+
# used to manipulate 3D rotations without "gimbal lock", the problem of
|
8
|
+
# coincident viewing angle alignment problems at the boundaries, for example,
|
9
|
+
# where the difference between the x and y coordinates of the viewing vector
|
10
|
+
# are zero (ie. looking straight up or straight down.)
|
11
|
+
#
|
12
|
+
# Interestingly, Quaternions were first conceptualized by Hamilton in 1843,
|
13
|
+
# well before the widespread use of vector notation. While mostly put on the
|
14
|
+
# shelf, they still solve certain problems elegantly, and in some cases more
|
15
|
+
# quickly than matrix-based 3D calulations.
|
16
|
+
#
|
17
|
+
# The quaternion's real part is accessed through the real instance variable,
|
18
|
+
# the three complex parts as the quaternion's "axis" vector, a Point3.
|
19
|
+
class Quaternion
|
20
|
+
|
21
|
+
include ThreeDimensions
|
22
|
+
|
23
|
+
# complex axis vector part reader and writer
|
24
|
+
attr_accessor :axis
|
25
|
+
# real part reader and writer
|
26
|
+
attr_accessor :real
|
27
|
+
|
28
|
+
# Creates and returns a Quaternion instance. Sets the coordinates values
|
29
|
+
# using the set method.
|
30
|
+
def initialize(axis=origin,real=0)
|
31
|
+
set axis, real
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a string representation of the instance.
|
35
|
+
def to_s
|
36
|
+
"Quaternion: axis: x #{axis.x} y #{axis.y} z #{axis.z} real #{real}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sets the axis and real values of the instance. When
|
40
|
+
# * axis is a Quaternion, the arguments are interpretted as the parts of a quaternion.
|
41
|
+
# * axis reponds like a Point3, the axis and real are used to set the quaternion's values.
|
42
|
+
# * otherwise a TypeError is raised.
|
43
|
+
# The modified instance is returned.
|
44
|
+
def set(axis=origin,real=0)
|
45
|
+
if axis.kind_of? Quaternion
|
46
|
+
set axis.axis, axis.real
|
47
|
+
elsif axis.point3_like?
|
48
|
+
(@axis ||= point3).set(axis)
|
49
|
+
@real = real
|
50
|
+
else
|
51
|
+
raise_no_conversion(self.class.name,axis)
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns true if the parts of the instance are equal to the
|
57
|
+
# parts of the given quaternion.
|
58
|
+
def ==(quaternion)
|
59
|
+
(@axis == quaternion.axis)&&(@real == quaternion.real)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if the parts of the instance are approximately
|
63
|
+
# equal to the parts of the given quaternion, each part
|
64
|
+
# less than a distance epsilon from the target.
|
65
|
+
def approximately_equals?(quaternion,epsilon=Numeric.epsilon)
|
66
|
+
(@axis.approximately_equals?(quaternion.axis,epsilon)&&
|
67
|
+
@real.approximately_equals?(quaternion.real,epsilon))
|
68
|
+
end
|
69
|
+
|
70
|
+
alias =~ approximately_equals?
|
71
|
+
|
72
|
+
# Returns a copy of a Quaternion with the given parts:
|
73
|
+
# * axis is a Quaternion, its parts are copied.
|
74
|
+
# * axis responds like a Point3, the arguments are copied.
|
75
|
+
# * otherwise a TypeError is raised.
|
76
|
+
def quaternion(axis=self.axis,real=self.real)
|
77
|
+
Quaternion.new(axis,real)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns a new Quaternion-based encoding of a rotation.
|
81
|
+
def Quaternion.from_axis_angle(axis,angle)
|
82
|
+
half_angle = angle/2.0
|
83
|
+
Quaternion.new Point3.new(axis).scale(Math.sin(half_angle)),
|
84
|
+
Math.cos(half_angle)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns a new Quaternion encoded into a rotation.
|
88
|
+
def to_axis_angle
|
89
|
+
half_angle = Math.acos(@real)
|
90
|
+
sin_half_angle = Math.sin(half_angle)
|
91
|
+
axis = (sin_half_angle.abs < 0.00000001)?
|
92
|
+
Point3.new(1,0,0) : Point3.new(@axis).scale!(1.0/sin_half_angle)
|
93
|
+
Quaternion.new(axis,2.0*half_angle)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns a new Quaternion instance whose parts are the original
|
97
|
+
# instance's with the given amounts added:
|
98
|
+
# * axis is a Quaternion, its parts are added.
|
99
|
+
# * axis responds like a Point3, the arguments are added.
|
100
|
+
# * otherwise a TypeError is raised.
|
101
|
+
def add(axis=origin,real=0)
|
102
|
+
quaternion.add!(axis,real)
|
103
|
+
end
|
104
|
+
|
105
|
+
alias + add
|
106
|
+
|
107
|
+
# Returns the modified instance with the arguments added.
|
108
|
+
def add!(axis=origin,real=0)
|
109
|
+
if axis.kind_of? Quaternion
|
110
|
+
add!(axis.axis,axis.real)
|
111
|
+
elsif axis.point3_like?
|
112
|
+
set(@axis+axis,@real+real)
|
113
|
+
else
|
114
|
+
raise_no_conversion axis
|
115
|
+
end
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns a new Quaternion instance whose parts are the original
|
120
|
+
# instance's with the given amounts subtracted:
|
121
|
+
# * axis is a Quaternion, its parts are subtracted.
|
122
|
+
# * axis responds like a Point3, the arguments are subtracted.
|
123
|
+
# * otherwise a TypeError is raised.
|
124
|
+
def subtract(axis=origin,real=0)
|
125
|
+
quaternion.subtract!(axis,real)
|
126
|
+
end
|
127
|
+
|
128
|
+
alias - subtract
|
129
|
+
|
130
|
+
# Returns the modified instance with the arguments subtracted.
|
131
|
+
def subtract!(axis=origin,real=0)
|
132
|
+
if axis.kind_of? Quaternion
|
133
|
+
subtract!(axis.axis,axis.real)
|
134
|
+
elsif axis.point3_like?
|
135
|
+
set(@axis-axis,@real-real)
|
136
|
+
else
|
137
|
+
raise_no_conversion axis
|
138
|
+
end
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns a new Quaternion instance whose parts are the original
|
143
|
+
# instance's multiplied by the given amounts:
|
144
|
+
# * axis is a Quaternion, the instance is multiplied by the axis' parts.
|
145
|
+
# * axis responds like a Point3, the instance is multiplied by the arguments.
|
146
|
+
# * otherwise a TypeError is raised.
|
147
|
+
def multiply(axis=origin,real=1)
|
148
|
+
quaternion.multiply!(axis,real)
|
149
|
+
end
|
150
|
+
|
151
|
+
alias * multiply
|
152
|
+
|
153
|
+
# Returns the modified instance multiplied by the arguments.
|
154
|
+
def multiply!(axis=origin,real=1)
|
155
|
+
if axis.kind_of? Quaternion
|
156
|
+
multiply!(axis.axis,axis.real)
|
157
|
+
elsif axis.point3_like?
|
158
|
+
r = (@real*real)-@axis.dot(axis)
|
159
|
+
p3 = axis.scale r
|
160
|
+
qp3 = @axis.scale real
|
161
|
+
cross = @axis.cross axis
|
162
|
+
a = p3.add!(qp3).add!(cross)
|
163
|
+
set(a,r)
|
164
|
+
else
|
165
|
+
raise_no_conversion axis
|
166
|
+
end
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns a new Quaternion instance whose parts are the original
|
171
|
+
# instance's multiplied by the scalar:
|
172
|
+
# * scalar is a Quaternion, the instance is multiplied by the scalar's parts.
|
173
|
+
# * axis is a Numeric, the instance's parts are multiplied by the scalar.
|
174
|
+
# * otherwise a TypeError is raised.
|
175
|
+
def scale(scalar=1)
|
176
|
+
quaternion.scale!(scalar)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns the modified instance multiplied by the scalar.
|
180
|
+
def scale!(scalar=1)
|
181
|
+
if (scalar.kind_of? Quaternion)
|
182
|
+
multiply!(scalar)
|
183
|
+
elsif (scalar.kind_of? Numeric)
|
184
|
+
set(@axis.scale(scalar),@real*scalar)
|
185
|
+
else
|
186
|
+
raise_no_conversion scalar, "Numeric"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns a new Quaternion instance that is the conjugate of the original
|
191
|
+
# instance.
|
192
|
+
def conjugate
|
193
|
+
quaternion.conjugate!
|
194
|
+
end
|
195
|
+
|
196
|
+
# Returns the modified instance replaced with its conjugate.
|
197
|
+
def conjugate!
|
198
|
+
set(@axis.mirror,@real)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Returns the norm of the instance.
|
202
|
+
def norm
|
203
|
+
(@real * @real) + @axis.modulus
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns the absolute value of the instance.
|
207
|
+
def abs
|
208
|
+
Math.sqrt(norm)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Returns a new Quaternion instance that is the inverse of the original
|
212
|
+
# instance.
|
213
|
+
def inverse
|
214
|
+
quaternion.inverse!
|
215
|
+
end
|
216
|
+
|
217
|
+
# Returns the modified instance replaced with its inverse.
|
218
|
+
def inverse!
|
219
|
+
conjugate!
|
220
|
+
scale!(1.0/norm)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Returns a new Quaternion instance whose parts are the original
|
224
|
+
# instance's divided by the given amounts:
|
225
|
+
# * axis is a Quaternion, the instance is divided by the axis' parts.
|
226
|
+
# * axis responds like a Point3, the instance is divided by the arguments.
|
227
|
+
# * otherwise a TypeError is raised.
|
228
|
+
def divide(axis=origin,real=1)
|
229
|
+
quaternion.divide!(axis,origin)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Returns the modified instance divided by the arguments.
|
233
|
+
def divide!(axis=origin,real=1)
|
234
|
+
if axis.kind_of? Quaternion
|
235
|
+
divide!(axis.axis,axis.real)
|
236
|
+
elsif axis.point3_like?
|
237
|
+
multiply!(quaternion(axis,real).inverse!)
|
238
|
+
else
|
239
|
+
raise_no_conversion axis
|
240
|
+
end
|
241
|
+
self
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eymiha_math3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Anderson
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-10-20 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -44,6 +44,14 @@ files:
|
|
44
44
|
- gem_package.rb
|
45
45
|
- rakefile.rb
|
46
46
|
- lib/eymiha
|
47
|
+
- lib/eymiha/math3
|
48
|
+
- lib/eymiha/math3/envelope3.rb
|
49
|
+
- lib/eymiha/math3/math3.rb
|
50
|
+
- lib/eymiha/math3/point3.rb
|
51
|
+
- lib/eymiha/math3/point3c.rb
|
52
|
+
- lib/eymiha/math3/point3s.rb
|
53
|
+
- lib/eymiha/math3/quaternion.rb
|
54
|
+
- lib/eymiha/math3.rb
|
47
55
|
- lib/eymiha_math3.rb
|
48
56
|
- test/tc_envelope3.rb
|
49
57
|
- test/tc_point3.rb
|