geom 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/Gemfile +8 -0
- data/Rakefile +2 -0
- data/geom.gemspec +17 -0
- data/lib/geom/line.rb +118 -0
- data/lib/geom/plane.rb +71 -0
- data/lib/geom/point.rb +148 -0
- data/lib/geom/rectangular_coordinate_system.rb +77 -0
- data/lib/geom/tolerance.rb +3 -0
- data/lib/geom/transformation.rb +93 -0
- data/lib/geom/vector.rb +173 -0
- data/lib/geom/version.rb +3 -0
- data/lib/geom.rb +13 -0
- data/spec/geom/line_spec.rb +125 -0
- data/spec/geom/plane_spec.rb +55 -0
- data/spec/geom/point_spec.rb +213 -0
- data/spec/geom/rectangular_coordinate_system_spec.rb +48 -0
- data/spec/geom/transformation_spec.rb +69 -0
- data/spec/geom/vector_spec.rb +193 -0
- data/spec/spec_helper.rb +7 -0
- metadata +82 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/geom.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/geom/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Adrian Smith"]
|
6
|
+
gem.email = ["adrian.smith@ennova.com.au"]
|
7
|
+
gem.description = "A 3D geometry library that includes Point, Vector, Line, Plane, Coordinate System and Transformation objects"
|
8
|
+
gem.summary = "A 3D geometry library"
|
9
|
+
gem.homepage = "https://github.com/AdrianSmith/geom"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "geom"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Geom::VERSION
|
17
|
+
end
|
data/lib/geom/line.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module Geom
|
2
|
+
# Line defined by parametric equations X = X0 + XAt, Y = Y0 + YAt, ,Z = Z0 + ZAt
|
3
|
+
class Line
|
4
|
+
attr_accessor :x0, :xa, :y0, :ya, :z0, :za
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
|
8
|
+
case args.size
|
9
|
+
when 2 # Start point and End point
|
10
|
+
start_point = args[0]
|
11
|
+
end_point = args[1]
|
12
|
+
|
13
|
+
line_direction = Vector.new(start_point, end_point)
|
14
|
+
|
15
|
+
@x0 = start_point.x
|
16
|
+
@y0 = start_point.y
|
17
|
+
@z0 = start_point.z
|
18
|
+
@xa = line_direction.x
|
19
|
+
@ya = line_direction.y
|
20
|
+
@za = line_direction.z
|
21
|
+
|
22
|
+
else # Coefficients of line equation
|
23
|
+
@x0, @xa, @y0, @ya, @z0, @za = args.flatten
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(line)
|
28
|
+
(@x0 - line.x0).abs < TOLERANCE &&
|
29
|
+
(@y0 - line.y0).abs < TOLERANCE &&
|
30
|
+
(@z0 - line.z0).abs < TOLERANCE &&
|
31
|
+
(@xa - line.xa).abs < TOLERANCE &&
|
32
|
+
(@ya - line.ya).abs < TOLERANCE &&
|
33
|
+
(@za - line.za).abs < TOLERANCE
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method :eql?, :==
|
37
|
+
|
38
|
+
def hash
|
39
|
+
(@x0.to_int ^ @y0.to_int ^ @z0.to_int ^ @xa.to_int ^ @ya.to_int ^ @za.to_int)
|
40
|
+
end
|
41
|
+
|
42
|
+
def direction
|
43
|
+
Vector.new(point_at_parameter(0), point_at_parameter(1))
|
44
|
+
end
|
45
|
+
|
46
|
+
def point_at_parameter(t)
|
47
|
+
Point.new((@x0 + @xa * t), (@y0 + @ya * t), (@z0 + @za * t))
|
48
|
+
end
|
49
|
+
|
50
|
+
def parameter_at_point(point)
|
51
|
+
if (@xa != 0)
|
52
|
+
((point.x - @x0) / @xa)
|
53
|
+
elsif (@ya != 0)
|
54
|
+
((point.y - @y0) / @ya)
|
55
|
+
else
|
56
|
+
((point.z - @z0) / @za)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def closest_approach_parameter(line)
|
61
|
+
if self.direction.parallel?(line.direction)
|
62
|
+
raise ArgumentError.new("Lines are parallel")
|
63
|
+
else
|
64
|
+
s1 = Vector.new(@x0, @y0, @z0)
|
65
|
+
v1 = Vector.new(@xa, @ya, @za)
|
66
|
+
s2 = Vector.new(line.x0, line.y0, line.z0)
|
67
|
+
v2 = Vector.new(line.xa, line.ya, line.za)
|
68
|
+
|
69
|
+
i = 1 / ((v1.dot(v2) * v1.dot(v2)) - (v1.length * v1.length) * (v2.length * v2.length))
|
70
|
+
j11 = -(v2.length * v2.length)
|
71
|
+
j12 = v1.dot(v2)
|
72
|
+
|
73
|
+
vk = Vector.new(s1, s2)
|
74
|
+
|
75
|
+
k1 = vk.dot(v1)
|
76
|
+
k2 = vk.dot(v2)
|
77
|
+
|
78
|
+
i * (j11 * k1 + j12 * k2)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def intersection_with_line(line)
|
83
|
+
t1 = self.closest_approach_parameter(line)
|
84
|
+
t2 = line.closest_approach_parameter(self)
|
85
|
+
|
86
|
+
p1 = self.point_at_parameter(t1)
|
87
|
+
p2 = line.point_at_parameter(t2)
|
88
|
+
|
89
|
+
dist = p1.distance_to_point(p2)
|
90
|
+
|
91
|
+
if (dist < TOLERANCE)
|
92
|
+
p1
|
93
|
+
else
|
94
|
+
raise ArgumentError.new("Lines do not intersect")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def intersection_with_plane(plane)
|
99
|
+
var = [0,0]
|
100
|
+
var[0] = @x0 * plane.a + @y0 * plane.b + @z0 * plane.c
|
101
|
+
var[1] = @xa * plane.a + @ya * plane.b + @za * plane.c
|
102
|
+
|
103
|
+
raise ArgumentError.new("Line is parallel to the plane") if var[1] == 0
|
104
|
+
|
105
|
+
t = (plane.d - var[0]) / var[1]
|
106
|
+
Point.new((@x0 + @xa * t), (@y0 + @ya * t), (@z0 + @za * t))
|
107
|
+
end
|
108
|
+
|
109
|
+
def on_plane?(plane)
|
110
|
+
self.point_at_parameter(0).on_plane?(plane) && self.point_at_parameter(1).on_plane?(plane)
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
"Line(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)" % [@x0, @xa, @y0, @ya, @z0, @za]
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
data/lib/geom/plane.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Geom
|
2
|
+
# Plane defined by the equation:Ax + By + Cz = D
|
3
|
+
class Plane
|
4
|
+
attr_accessor :a, :b, :c, :d
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
|
8
|
+
case args.size
|
9
|
+
when 2 # Point and normal vector
|
10
|
+
point = args[0]
|
11
|
+
normal = args[1]
|
12
|
+
|
13
|
+
@a = normal.x
|
14
|
+
@b = normal.y
|
15
|
+
@c = normal.z
|
16
|
+
@d = ((normal.x * point.x) + ((normal.y * point.y) + (normal.z * point.z)))
|
17
|
+
|
18
|
+
when 3 # Points on the plane
|
19
|
+
point_1 = args[0]
|
20
|
+
point_2 = args[1]
|
21
|
+
point_3 = args[2]
|
22
|
+
|
23
|
+
vector_12 = Vector.new(point_1, point_2)
|
24
|
+
vector_13 = Vector.new(point_1, point_3)
|
25
|
+
plane_normal = vector_12.cross(vector_13)
|
26
|
+
|
27
|
+
@a = plane_normal.x
|
28
|
+
@b = plane_normal.y
|
29
|
+
@c = plane_normal.z
|
30
|
+
@d = ((point_1.x * @a) + ((point_1.y * @b) + (point_1.z * @c)))
|
31
|
+
|
32
|
+
else # Coefficients of plane equation
|
33
|
+
@a, @b, @c, @d = args.flatten
|
34
|
+
end
|
35
|
+
self.normalize
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(plane)
|
39
|
+
(@a - plane.a).abs < TOLERANCE &&
|
40
|
+
(@b - plane.b).abs < TOLERANCE &&
|
41
|
+
(@c - plane.c).abs < TOLERANCE &&
|
42
|
+
(@d - plane.d).abs < TOLERANCE
|
43
|
+
end
|
44
|
+
alias_method :eql?, :==
|
45
|
+
|
46
|
+
def hash
|
47
|
+
(@a.to_int ^ @b.to_int ^ @c.to_int ^ @d.to_int)
|
48
|
+
end
|
49
|
+
|
50
|
+
def normal
|
51
|
+
Vector.new(@a, @b, @c).unitize
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
"Plane(%.3f,%.3f,%.3f,%.3f)" % [@a, @b, @c, @d]
|
56
|
+
end
|
57
|
+
|
58
|
+
def normalize
|
59
|
+
if (@a == 0 && @b == 0 && @c == 0)
|
60
|
+
raise ArgumentError, "Plane definition error, points may be coincident or collinear"
|
61
|
+
else
|
62
|
+
norm_factor = 1.0 / Math.sqrt((@a * @a + @b * @b + @c * @c))
|
63
|
+
@a *= norm_factor
|
64
|
+
@b *= norm_factor
|
65
|
+
@c *= norm_factor
|
66
|
+
@d *= norm_factor
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/geom/point.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
module Geom
|
2
|
+
# Point defined by coordinates x, y, z
|
3
|
+
class Point
|
4
|
+
attr_accessor :x, :y, :z
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@x, @y, @z = args.flatten
|
8
|
+
end
|
9
|
+
|
10
|
+
def -(point)
|
11
|
+
Point.new(@x - point.x, @y - point.y, @z - point.z)
|
12
|
+
end
|
13
|
+
|
14
|
+
def +(point)
|
15
|
+
Point.new(@x + point.x, @y + point.y, @z + point.z)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(point)
|
19
|
+
(@x - point.x).abs < TOLERANCE &&
|
20
|
+
(@y - point.y).abs < TOLERANCE &&
|
21
|
+
(@z - point.z).abs < TOLERANCE
|
22
|
+
end
|
23
|
+
alias_method :eql?, :==
|
24
|
+
alias_method :coincident?, :==
|
25
|
+
|
26
|
+
def hash
|
27
|
+
(@x.to_int ^ @y.to_int ^ @z.to_int)
|
28
|
+
end
|
29
|
+
|
30
|
+
def distance_to_point(point)
|
31
|
+
x_dist = @x - point.x
|
32
|
+
y_dist = @y - point.y
|
33
|
+
z_dist = @z - point.z
|
34
|
+
Math.sqrt(x_dist * x_dist + y_dist * y_dist + z_dist * z_dist)
|
35
|
+
end
|
36
|
+
|
37
|
+
def distance_to_plane(plane)
|
38
|
+
n = Vector.new(plane.a, plane.b, plane.c)
|
39
|
+
l = n.length
|
40
|
+
d = (plane.d / (l * l))
|
41
|
+
pnt = Point.new((plane.a * d), (plane.b * d), (plane.c * d))
|
42
|
+
vec = Vector.new(self, pnt)
|
43
|
+
vec.dot(n) / l
|
44
|
+
end
|
45
|
+
|
46
|
+
def translate(direction, distance=1)
|
47
|
+
transation_vector = direction.unitize.scale distance
|
48
|
+
Point.new(@x + transation_vector.x, @y + transation_vector.y, @z + transation_vector.z)
|
49
|
+
end
|
50
|
+
|
51
|
+
def transform(rectangular_coordinate_system)
|
52
|
+
m = Transformation.new(rectangular_coordinate_system).matrix
|
53
|
+
tx = m[0,0] * @x + m[0,1] * @y + m[0,2] * @z + m[0,3] * 1.0
|
54
|
+
ty = m[1,0] * @x + m[1,1] * @y + m[1,2] * @z + m[1,3] * 1.0
|
55
|
+
tz = m[2,0] * @x + m[2,1] * @y + m[2,2] * @z + m[2,3] * 1.0
|
56
|
+
Point.new(tx, ty, tz)
|
57
|
+
end
|
58
|
+
|
59
|
+
def project_onto_line(line)
|
60
|
+
l = line.xa * line.xa + line.ya * line.ya + line.za * line.za
|
61
|
+
m = 2 * (line.x0 - self.x) * line.xa + 2 * (line.y0 - self.y) * line.ya + 2 * (line.z0 - self.z) * line.za
|
62
|
+
t = -m / (2 * l)
|
63
|
+
Point.new(line.x0 + line.xa * t, line.y0 + line.ya * t, line.z0 + line.za * t)
|
64
|
+
end
|
65
|
+
|
66
|
+
def project_onto_line_along_vector(line, vector)
|
67
|
+
ref_line = Line.new(@x, vector.x, @y, vector.y, @z, vector.z)
|
68
|
+
line.intersection_with_line(ref_line)
|
69
|
+
end
|
70
|
+
|
71
|
+
def project_onto_plane(plane)
|
72
|
+
n = plane.normal
|
73
|
+
q = self.to_vector
|
74
|
+
|
75
|
+
r = (q.dot(n) - plane.d) / n.length
|
76
|
+
|
77
|
+
result = q - n.unitize.scale(r)
|
78
|
+
result.to_point
|
79
|
+
end
|
80
|
+
|
81
|
+
def project_onto_plane_along_vector(plane, vector)
|
82
|
+
|
83
|
+
l = Math.sqrt(plane.a * plane.a + plane.b * plane.b + plane.c * plane.c)
|
84
|
+
d = (plane.d / (l * l))
|
85
|
+
q = Point.new((plane.a * d), (plane.b * d), (plane.c * d))
|
86
|
+
v = Vector.new(q, self)
|
87
|
+
|
88
|
+
u = plane.normal
|
89
|
+
w = vector.scale((v.dot(u) / vector.dot(u)))
|
90
|
+
r = q.to_vector + (v - w)
|
91
|
+
|
92
|
+
r.to_point
|
93
|
+
end
|
94
|
+
|
95
|
+
def between?(first_point, second_point, include_ends=true)
|
96
|
+
line = Line.new(first_point, second_point)
|
97
|
+
projected_point = self.project_onto_line(line)
|
98
|
+
|
99
|
+
distance_12 = first_point.distance_to_point(second_point)
|
100
|
+
distance_T1 = first_point.distance_to_point(projected_point)
|
101
|
+
distance_T2 = second_point.distance_to_point(projected_point)
|
102
|
+
|
103
|
+
if include_ends
|
104
|
+
(distance_T1 < distance_12) && (distance_T2 < distance_12)
|
105
|
+
else
|
106
|
+
(distance_T1 <= distance_12) && (distance_T2 <= distance_12)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def on_plane?(plane)
|
111
|
+
projected_point = self.project_onto_plane(plane)
|
112
|
+
projected_point.distance_to_point(self).abs <= TOLERANCE
|
113
|
+
end
|
114
|
+
|
115
|
+
def on_line?(line)
|
116
|
+
projected_point = self.project_onto_line(line)
|
117
|
+
self.Distance(projected_point) <= Tolerance
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.remove_coincident(points)
|
121
|
+
points.uniq
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.average(points)
|
125
|
+
tx, ty, tz = 0, 0, 0
|
126
|
+
num = points.size.to_f
|
127
|
+
points.each do |point|
|
128
|
+
tx += point.x
|
129
|
+
ty += point.y
|
130
|
+
tz += point.z
|
131
|
+
end
|
132
|
+
Point.new(tx/num, ty/num, tz/num)
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_vector
|
136
|
+
Vector.new(@x,@y,@z)
|
137
|
+
end
|
138
|
+
|
139
|
+
def to_s
|
140
|
+
"Point(%.3f,%.3f,%.3f)" % [@x, @y, @z]
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_a
|
144
|
+
[@x, @y, @z]
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Geom
|
2
|
+
class RectangularCoordinateSystem
|
3
|
+
attr_accessor :origin, :x_vector, :y_vector, :z_vector
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@origin = Point.new(0.0, 0.0, 0.0)
|
7
|
+
@x_vector = Vector.new(1.0, 0.0, 0.0)
|
8
|
+
@y_vector = Vector.new(0.0, 1.0, 0.0)
|
9
|
+
@z_vector = Vector.new(0.0, 0.0, 1.0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.new_from_xvector_and_xyplane(origin, axis_vector, plane_vector)
|
13
|
+
rcs = self.new
|
14
|
+
rcs.origin = origin
|
15
|
+
rcs.x_vector = axis_vector.unitize
|
16
|
+
rcs.z_vector = plane_vector.unitize
|
17
|
+
rcs.y_vector = rcs.z_vector.cross(rcs.x_vector).unitize
|
18
|
+
rcs.z_vector = rcs.x_vector.cross(rcs.y_vector).unitize
|
19
|
+
rcs
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.new_from_yvector_and_yzplane(origin, axis_vector, plane_vector)
|
23
|
+
rcs = self.new
|
24
|
+
rcs.origin = origin
|
25
|
+
rcs.y_vector = axis_vector.unitize
|
26
|
+
rcs.x_vector = plane_vector.unitize
|
27
|
+
rcs.z_vector = rcs.x_vector.cross(rcs.y_vector).unitize
|
28
|
+
rcs.x_vector = rcs.y_vector.cross(rcs.z_vector).unitize
|
29
|
+
rcs
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.new_from_zvector_and_zxplane(origin, axis_vector, plane_vector)
|
33
|
+
rcs = self.new
|
34
|
+
rcs.origin = origin
|
35
|
+
rcs.z_vector = axis_vector.unitize
|
36
|
+
rcs.y_vector = plane_vector.unitize
|
37
|
+
rcs.x_vector = rcs.y_vector.cross(rcs.z_vector).unitize
|
38
|
+
rcs.y_vector = rcs.z_vector.cross(rcs.x_vector).unitize
|
39
|
+
rcs
|
40
|
+
end
|
41
|
+
|
42
|
+
def transformation_matrix
|
43
|
+
Matrix[
|
44
|
+
[@x_vector.x, @y_vector.x, @z_vector.x, @origin.x],
|
45
|
+
[@x_vector.y, @y_vector.y, @z_vector.y, @origin.y],
|
46
|
+
[@x_vector.z, @y_vector.z, @z_vector.z, @origin.z],
|
47
|
+
[0.0, 0.0, 0.0, 1.0]
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s(verbose=false)
|
52
|
+
unless verbose
|
53
|
+
"RCS[#{@origin.to_s} X-#{@x_vector.to_s} Y-#{@y_vector.to_s} Z-#{@z_vector.to_s}]"
|
54
|
+
else
|
55
|
+
str = "Rectangular Coordinate System\n"
|
56
|
+
str += "Origin: #{@origin.to_s}\n"
|
57
|
+
str += "X-Vector: #{@x_vector.to_s}\n"
|
58
|
+
str += "Y-Vector: #{@y_vector.to_s}\n"
|
59
|
+
str += "Z-Vector: #{@z_vector.to_s}"
|
60
|
+
str
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def == rcs
|
65
|
+
rcs.x_vector == @x_vector &&
|
66
|
+
rcs.y_vector == @y_vector &&
|
67
|
+
rcs.z_vector == @z_vector &&
|
68
|
+
rcs.origin == @origin
|
69
|
+
end
|
70
|
+
|
71
|
+
alias_method :eql?, :==
|
72
|
+
|
73
|
+
def hash
|
74
|
+
(@x_vector.hash ^ @y_vector.hash ^ @z_vector.hash ^ @origin.hash)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Geom
|
2
|
+
# 3D transformation object that is composed of a 4x4 matrix representing the
|
3
|
+
# rotation and translation components
|
4
|
+
# rx1 ry1 rz1 tx
|
5
|
+
# rx2 ry2 rz2 ty
|
6
|
+
# rx3 ry3 rz3 tz
|
7
|
+
# 0 0 0 1
|
8
|
+
# Transformations are applied by using the * operator
|
9
|
+
# Available transformations include translation and rotation
|
10
|
+
class Transformation
|
11
|
+
attr_accessor :type, :matrix, :translation_vector
|
12
|
+
|
13
|
+
TRANSLATION = 1
|
14
|
+
ROTATION = 2
|
15
|
+
SCALING = 4
|
16
|
+
|
17
|
+
def initialize(coordinate_system)
|
18
|
+
@matrix = coordinate_system.transformation_matrix.inverse * 1.0 # ensure initialization as float not integer
|
19
|
+
@translation_vector = Vector.new(Point.new(0.0, 0.0, 0.0), coordinate_system.origin)
|
20
|
+
@type = 0 # initialize as integer not boolean
|
21
|
+
|
22
|
+
@type |= ROTATION if self.rotation_submatrix_orthogonal? && !rotation_submatrix_identity?
|
23
|
+
@type |= TRANSLATION unless self.translation_vector.zero?
|
24
|
+
@type |= SCALING unless self.scale_vector.unitity?
|
25
|
+
|
26
|
+
raise ArgumentError, "Transformation is non-linear" unless self.rotation_submatrix_orthogonal? or self.rotation_submatrix_diagonal
|
27
|
+
end
|
28
|
+
|
29
|
+
def type_description
|
30
|
+
names = ["Type #{@type}"]
|
31
|
+
names << 'Translation' if self.translation?
|
32
|
+
names << 'Rotation' if self.rotation?
|
33
|
+
names << 'Scaling' if self.scaling?
|
34
|
+
names.join(' ')
|
35
|
+
end
|
36
|
+
|
37
|
+
def rotation_submatrix
|
38
|
+
@matrix.minor(0..2,0..2)
|
39
|
+
end
|
40
|
+
|
41
|
+
def rotation_submatrix_orthogonal?
|
42
|
+
self.rotation_submatrix.transpose == self.rotation_submatrix.inverse
|
43
|
+
end
|
44
|
+
|
45
|
+
def rotation_submatrix_diagonal?
|
46
|
+
@matrix[1,0] == 0.0 && @matrix[2,0] == 0.0 && @matrix[2,1] == 0.0 &&
|
47
|
+
@matrix[0,1] == 0.0 && @matrix[0,2] == 0.0 && @matrix[1,2] == 0.0 &&
|
48
|
+
@matrix[0,0].abs > TOLERANCE && @matrix[1,1].abs > TOLERANCE && @matrix[2,2].abs > TOLERANCE
|
49
|
+
end
|
50
|
+
|
51
|
+
def identity?
|
52
|
+
@matrix == Matrix.identity(4) * 1.0
|
53
|
+
end
|
54
|
+
|
55
|
+
def rotation_submatrix_identity?
|
56
|
+
self.rotation_submatrix == Matrix.identity(3) * 1.0
|
57
|
+
end
|
58
|
+
|
59
|
+
def scaling?
|
60
|
+
SCALING & self.type > 0.0
|
61
|
+
end
|
62
|
+
|
63
|
+
def translation?
|
64
|
+
TRANSLATION & self.type > 0.0
|
65
|
+
end
|
66
|
+
|
67
|
+
def rotation?
|
68
|
+
ROTATION & self.type > 0.0
|
69
|
+
end
|
70
|
+
|
71
|
+
def scale_vector
|
72
|
+
Vector.new(
|
73
|
+
Math.sqrt(@matrix[0,0]*@matrix[0,0] + @matrix[0,1]*@matrix[0,1] + @matrix[0,2]*@matrix[0,2]),
|
74
|
+
Math.sqrt(@matrix[1,0]*@matrix[1,0] + @matrix[1,1]*@matrix[1,1] + @matrix[1,2]*@matrix[1,2]),
|
75
|
+
Math.sqrt(@matrix[2,0]*@matrix[2,0] + @matrix[2,1]*@matrix[2,1] + @matrix[2,2]*@matrix[2,2])
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s(verbose=false)
|
80
|
+
unless verbose
|
81
|
+
"Transformation #{@matrix.to_s}"
|
82
|
+
else
|
83
|
+
str = "Transformation\n"
|
84
|
+
str += " Vx | Vy | Vz | T\n"
|
85
|
+
str += sprintf("%9.2e | %9.2e | %9.2e | %9.2e\n", @matrix[0,0], @matrix[0,1], @matrix[0,2], @matrix[0,3])
|
86
|
+
str += sprintf("%9.2e | %9.2e | %9.2e | %9.2e\n", @matrix[1,0], @matrix[1,1], @matrix[1,2], @matrix[1,3])
|
87
|
+
str += sprintf("%9.2e | %9.2e | %9.2e | %9.2e\n", @matrix[2,0], @matrix[2,1], @matrix[2,2], @matrix[2,3])
|
88
|
+
str += sprintf("%9.2e | %9.2e | %9.2e | %9.2e", @matrix[3,0], @matrix[3,1], @matrix[3,2], @matrix[3,3])
|
89
|
+
str
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|