eymiha_math3 1.0.1 → 1.0.2
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/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
|