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.
@@ -0,0 +1,173 @@
1
+ module Geom
2
+ # Vector defined by coordinates x, y, z
3
+ class Vector
4
+ attr_accessor :x, :y, :z
5
+
6
+ def initialize(*args)
7
+ if args.size == 2
8
+ @x = args[1].x.to_f - args[0].x.to_f
9
+ @y = args[1].y.to_f - args[0].y.to_f
10
+ @z = args[1].z.to_f - args[0].z.to_f
11
+ else
12
+ @x, @y, @z = args.flatten
13
+ end
14
+ end
15
+
16
+ def -(vector)
17
+ Vector.new(@x - vector.x, @y - vector.y, @z - vector.z)
18
+ end
19
+
20
+ def +(vector)
21
+ Vector.new(@x + vector.x, @y + vector.y, @z + vector.z)
22
+ end
23
+
24
+ def *(scale)
25
+ Vector.new(@x * scale, @y * scale, @z * scale)
26
+ end
27
+
28
+ def ==(vector)
29
+ (@x - vector.x).abs < TOLERANCE &&
30
+ (@y - vector.y).abs < TOLERANCE &&
31
+ (@z - vector.z).abs < TOLERANCE
32
+ end
33
+
34
+ alias_method :eql?, :==
35
+
36
+ def hash
37
+ (@x.to_int ^ @y.to_int ^ @z.to_int)
38
+ end
39
+
40
+ alias_method :scale, :*
41
+
42
+ def **(power)
43
+ Vector.new(@x ** power, @y ** power, @z ** power)
44
+ end
45
+
46
+ def /(scale)
47
+ self * (1.0/scale)
48
+ end
49
+
50
+ def dot(vector)
51
+ @x * vector.x + @y * vector.y + @z * vector.z
52
+ end
53
+
54
+ def cross(vector)
55
+ Vector.new(@y * vector.z - @z * vector.y,
56
+ @z * vector.x - @x * vector.z,
57
+ @x * vector.y - @y * vector.x)
58
+ end
59
+
60
+ def unitize
61
+ self / self.length
62
+ end
63
+
64
+ def reverse
65
+ Vector.new(-@x, -@y, -@z)
66
+ end
67
+
68
+ def length
69
+ Math.sqrt(self.dot(self))
70
+ end
71
+
72
+ def rotate(ref_vector, angle)
73
+ r = [[0,0,0],[0,0,0],[0,0,0]]
74
+ c = Math.cos(angle)
75
+ s = Math.sin(angle)
76
+
77
+ unit_ref_vector = ref_vector.unitize
78
+
79
+ ax = unit_ref_vector.x
80
+ ay = unit_ref_vector.y
81
+ az = unit_ref_vector.z
82
+
83
+ r[0][0] = (c + ((1 - c) * (ax * ax)))
84
+ r[0][1] = (((1 - c) * (ax * ay)) - (s * az))
85
+ r[0][2] = (((1 - c) * (ax * az)) + (s * ay))
86
+ r[1][0] = (((1 - c) * (ax * ay)) + (s * az))
87
+ r[1][1] = (c + ((1 - c) * (ay * ay)))
88
+ r[1][2] = (((1 - c) * (ay * az)) - (s * ax))
89
+ r[2][0] = (((1 - c) * (ax * az)) - (s * ay))
90
+ r[2][1] = (((1 - c) * (ay * az)) + (s * ax))
91
+ r[2][2] = (c + ((1 - c) * (az * az)))
92
+
93
+ vx = ((r[0][0] * @x) + ((r[0][1] * @y) + (r[0][2] * @z)))
94
+ vy = ((r[1][0] * @x) + ((r[1][1] * @y) + (r[1][2] * @z)))
95
+ vz = ((r[2][0] * @x) + ((r[2][1] * @y) + (r[2][2] * @z)))
96
+
97
+ Vector.new(vx, vy, vz)
98
+ end
99
+
100
+ def angle_between(vector)
101
+ # One of the vectors is a zero vector
102
+ return nil if ((vector.length == 0) || (self.length == 0))
103
+
104
+ dot = self.dot(vector)
105
+ val = dot / (self.length * vector.length)
106
+
107
+ if (val > 1.0)
108
+ # Would result in NaN
109
+ 0.0
110
+ else
111
+ Math.acos(val)
112
+ end
113
+ end
114
+
115
+
116
+ def transform(rectangular_coordinate_system)
117
+ m = Transformation.new(rectangular_coordinate_system).matrix
118
+ tx = m[0,0] * @x + m[0,1] * @y + m[0,2] * @z + m[0,3] * 1.0
119
+ ty = m[1,0] * @x + m[1,1] * @y + m[1,2] * @z + m[1,3] * 1.0
120
+ tz = m[2,0] * @x + m[2,1] * @y + m[2,2] * @z + m[2,3] * 1.0
121
+ Vector.new(tx, ty, tz)
122
+ end
123
+
124
+ # If dot product > 0, angle is acute and vectors are the same direction
125
+ # If dot product < 0, angle is obtuse and vectors are in opposite direction
126
+ # If dot product = 0, vectors are orthogonal, including if one is zero vector (taken as same direction)
127
+ def same_direction?(vector)
128
+ dotp = self.dot(vector);
129
+ dotp > 0
130
+ end
131
+
132
+ def parallel?(vector)
133
+ angle = self.angle_between(vector)
134
+ ((angle - Math::PI).abs < TOLERANCE) || (angle.abs < TOLERANCE)
135
+ end
136
+
137
+ def zero?
138
+ self.length <= 0.0
139
+ end
140
+
141
+ def unitity?
142
+ self.x == 1.0 && self.x == 1.0 && self.z == 1.0
143
+ end
144
+
145
+ def self.average(vectors)
146
+ num = vectors.size.to_f
147
+ Vector.sum(vectors).scale(1/num)
148
+ end
149
+
150
+ def self.sum(vectors)
151
+ tx, ty, tz = 0, 0, 0
152
+ vectors.each do |vector|
153
+ tx += vector.x
154
+ ty += vector.y
155
+ tz += vector.z
156
+ end
157
+ Vector.new(tx, ty, tz)
158
+ end
159
+
160
+ def to_point
161
+ Point.new(@x,@y,@z)
162
+ end
163
+
164
+ def to_s
165
+ "Vector(%.3f,%.3f,%.3f)" % [@x, @y, @z]
166
+ end
167
+
168
+ def to_a
169
+ [@x, @y, @z]
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,3 @@
1
+ module Geom
2
+ VERSION = "0.0.1"
3
+ end
data/lib/geom.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'geom/version'
2
+ require 'geom/tolerance'
3
+ require 'matrix'
4
+ require 'geom/point'
5
+ require 'geom/vector'
6
+ require 'geom/plane'
7
+ require 'geom/line'
8
+ require 'geom/transformation'
9
+ require 'geom/rectangular_coordinate_system'
10
+
11
+ module Geom
12
+
13
+ end
@@ -0,0 +1,125 @@
1
+ require_relative '../spec_helper.rb'
2
+ require 'geom/line'
3
+
4
+ module Geom
5
+ describe Line do
6
+ describe "Construction" do
7
+ before do
8
+ @valid_attributes = [1, 2, 3, 4, 5, 6]
9
+ @attributes = [:x0, :xa, :y0, :ya, :z0, :za]
10
+ end
11
+
12
+ it "should create a valid instance from an array of parameters" do
13
+ line = Line.new(@valid_attributes)
14
+ @attributes.each_with_index{|value, index| line.send(value).should == @valid_attributes[index] }
15
+ end
16
+
17
+ it "should create a valid instance from two points" do
18
+ line = Line.new(Point.new(1, 3, 5), Point.new(3, 7, 11))
19
+ @attributes.each_with_index{|value, index| line.send(value).should == @valid_attributes[index] }
20
+ end
21
+ end
22
+
23
+ describe "Equality" do
24
+ it "should determine equality with another line" do
25
+ line_1 = Line.new(Point.new(0, 0, 0), Point.new(2, 2, 2))
26
+ line_2 = Line.new(Point.new(0, 0, 0), Point.new(2, 2, 2))
27
+ line_1.should == line_2
28
+ end
29
+ end
30
+
31
+ describe "Return types" do
32
+ it "should return a summary string" do
33
+ Line.new(1,2,3,1,0,0).to_s.should == "Line(1.000,2.000,3.000,1.000,0.000,0.000)"
34
+ end
35
+
36
+ it "should return a hash code" do
37
+ Line.new(1,2.88,3,1,-45.111,101).hash.should == -73
38
+ end
39
+ end
40
+
41
+ describe "Intersection" do
42
+ it "should determine intersection with another line" do
43
+ line_1 = Line.new(Point.new(0, 0, 0), Point.new(10, 10, 0))
44
+ line_2 = Line.new(Point.new(8, 0, 0), Point.new(8, 100, 0))
45
+ line_1.intersection_with_line(line_2).should == Point.new(8, 8, 0)
46
+ end
47
+
48
+ it "should raise exception when lines are parallel" do
49
+ line_1 = Line.new(1, 1, 3, -1, 0, 2)
50
+ line_2 = Line.new(1, 2, 3, -2, 0, 4)
51
+ lambda {line_1.intersection_with_line(line_2)}.should raise_error(ArgumentError, "Lines are parallel")
52
+ end
53
+
54
+ it "should raise exception when lines are skew" do
55
+ line_1 = Line.new(1, 1, 3, -1, 0, 2)
56
+ line_2 = Line.new(1, 2, 3, -2.5, 0.3, 4)
57
+ lambda {line_1.intersection_with_line(line_2)}.should raise_error(ArgumentError, "Lines do not intersect")
58
+ end
59
+
60
+ it "should calculation intersection with a plane" do
61
+ plane = Plane.new(Point.new(0, 0, 0), Vector.new(0, 0, 1))
62
+ line = Line.new(Point.new(2, 2, 10), Point.new(2, 2, 20))
63
+ line.intersection_with_plane(plane).should == Point.new(2, 2, 0)
64
+ end
65
+ end
66
+
67
+ describe "Parameters" do
68
+ it "should calculate closest approach parameter" do
69
+ line_1 = Line.new(Point.new(0, 0, 0), Point.new(1, 1, 0))
70
+ line_2 = Line.new(Point.new(0.5, -0.5, 0), Point.new(0.5, -0.1, 0))
71
+ line_1.closest_approach_parameter(line_2).should be_within(0.001).of(0.5)
72
+ end
73
+
74
+ it "should calculate point at parameter" do
75
+ line = Line.new(1, 1, 3, -1, 0, 2)
76
+ line.point_at_parameter(5).should == Point.new(6, -2, 10)
77
+ end
78
+
79
+ describe "should calculate parameter" do
80
+ before do
81
+ @start_point = Point.new(-1, -1, -1)
82
+ @end_point = Point.new(1, 1, 1)
83
+ @mid_point = Point.new(0, 0, 0)
84
+ @line = Line.new(@start_point, @end_point)
85
+ end
86
+
87
+ it "at start point" do
88
+ @line.parameter_at_point(@start_point).should == 0
89
+ end
90
+
91
+ it "at end point" do
92
+ @line.parameter_at_point(@end_point).should == 1
93
+ end
94
+
95
+ it "at mid point" do
96
+ @line.parameter_at_point(@mid_point).should == 0.5
97
+ end
98
+
99
+ it "at point beyond end point" do
100
+ @line.parameter_at_point(Point.new(10,10,10)).should == 5.5
101
+ end
102
+
103
+ end
104
+
105
+ describe "should determine if a line is on a plane" do
106
+ before do
107
+ @plane = Plane.new(0, 0, 1, 0)
108
+ end
109
+ it "when a line is on a plane" do
110
+ line_1 = Line.new(Point.new(1, 1, 0), Point.new(2, -3, 0))
111
+ line_1.on_plane?(@plane).should be_true
112
+ end
113
+
114
+ it "when a line is parallel to a plane" do
115
+ line_2 = Line.new(Point.new(1, 1, 1), Point.new(2, -3, 1))
116
+ line_2.on_plane?(@plane).should be_false
117
+ end
118
+ it "when a line intersects plane" do
119
+ line_3 = Line.new(Point.new(1, 1, 0), Point.new(2, -3, 1))
120
+ line_3.on_plane?(@plane).should be_false
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,55 @@
1
+ require_relative '../spec_helper.rb'
2
+ require 'geom/plane'
3
+
4
+ module Geom
5
+ describe Plane do
6
+ before do
7
+ @valid_attributes = [-10, 10, 0, 4]
8
+ @tol = 0.001
9
+ end
10
+
11
+ describe "Construction" do
12
+ it "should create a valid instance from an array of coordinates" do
13
+ plane = Plane.new(@valid_attributes)
14
+ plane.a.should be_within(@tol).of(-0.707)
15
+ plane.b.should be_within(@tol).of(0.707)
16
+ plane.c.should be_within(@tol).of(0.0)
17
+ plane.d.should be_within(@tol).of(0.283)
18
+ end
19
+
20
+ it "should create a valid instance from three numbers" do
21
+ plane = Plane.new(Point.new(0, 3, 0), Vector.new(0, 3, 0))
22
+ plane.a.should == 0
23
+ plane.b.should == 1
24
+ plane.c.should == 0
25
+ plane.d.should == 3
26
+ end
27
+ end
28
+
29
+ describe "Equality" do
30
+ it "should determine if another plane is equal" do
31
+ plane_1 = Plane.new(Point.new(0, 3, 0), Vector.new(0, 3, 0))
32
+ plane_2 = Plane.new(Point.new(0, 3, 0), Vector.new(0, 3, 0))
33
+ plane_1.should == plane_2
34
+ end
35
+
36
+ it "should calculate the plane normal vector" do
37
+ test_plane = Plane.new(@valid_attributes)
38
+ normal = test_plane.normal
39
+ normal.x.should be_within(@tol).of(-0.707)
40
+ normal.y.should be_within(@tol).of(0.707)
41
+ normal.z.should be_within(@tol).of(0)
42
+ end
43
+ end
44
+
45
+ describe "Return types" do
46
+ it "should return a summary string" do
47
+ Plane.new(1,2,3,1).to_s.should == "Plane(0.267,0.535,0.802,0.267)"
48
+ end
49
+
50
+ it "should return a hash code" do
51
+ Plane.new(1,2.88,2,-45.111).hash.should == -12
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,213 @@
1
+ require_relative '../spec_helper.rb'
2
+ require 'geom/point'
3
+
4
+ module Geom
5
+ describe Point do
6
+ before do
7
+ @valid_attributes = [1.1, -2, 10]
8
+ end
9
+
10
+ describe "Construction" do
11
+ it "should create a valid instance from an array of coordinates" do
12
+ point = Point.new(@valid_attributes)
13
+ point.x.should == @valid_attributes[0]
14
+ point.y.should == @valid_attributes[1]
15
+ point.z.should == @valid_attributes[2]
16
+ end
17
+
18
+ it "should create a valid instance from three numbers" do
19
+ point = Point.new(@valid_attributes[0], @valid_attributes[1], @valid_attributes[2])
20
+ point.x.should == @valid_attributes[0]
21
+ point.y.should == @valid_attributes[1]
22
+ point.z.should == @valid_attributes[2]
23
+ end
24
+ end
25
+
26
+ describe "Arithmetic" do
27
+ before do
28
+ @first_point = Point.new(0,0,0)
29
+ @second_point = Point.new(1,2,3)
30
+ end
31
+
32
+ it "should allow addition with another point" do
33
+ result = @first_point + @second_point
34
+ result.should == Point.new(1,2,3)
35
+ end
36
+
37
+ it "should allow subtraction with another point" do
38
+ result = @first_point - @second_point
39
+ result.should == Point.new(-1,-2,-3)
40
+ end
41
+ end
42
+
43
+ describe "Return Types" do
44
+ it "should return as point" do
45
+ Point.new(@valid_attributes).to_vector.should == Vector.new(@valid_attributes)
46
+ end
47
+
48
+ it "should return as array" do
49
+ Point.new(@valid_attributes).to_a.should == @valid_attributes
50
+ end
51
+
52
+ it "should return a summary string" do
53
+ Point.new(@valid_attributes).to_s.should == "Point(1.100,-2.000,10.000)"
54
+ end
55
+
56
+ it "should return a hash code" do
57
+ Point.new(1,2.88,-45.111).hash.should == -48
58
+ end
59
+ end
60
+
61
+ describe "should calculate minimum distance" do
62
+ it "to another point" do
63
+ Point.new(0,0,0).distance_to_point(Point.new(1,3,2)).should == Math.sqrt(1*1 + 3*3 + 2*2)
64
+ end
65
+
66
+ it "to a plane when the point is remote from the plane" do
67
+ plane_1 = Plane.new(3, 2, 6, 6)
68
+ point_1 = Point.new(1, 1, 3)
69
+ point_1.distance_to_plane(plane_1).should be_within(0.001).of(-2.429)
70
+ end
71
+
72
+ it "to a plane when the point is on the plane" do
73
+ plane_2 = Plane.new(2, 3, 4, 20)
74
+ point_2 = Point.new(1, 2, 3)
75
+ point_2.distance_to_plane(plane_2).should be_within(0.001).of(0.0)
76
+ end
77
+ end
78
+
79
+ it "should calculated the average of an array of points" do
80
+ points = [Point.new(0,0,0), Point.new(1,1,1), Point.new(10,-10,2)]
81
+ average_point = Point.new(11/3.0, -9/3.0, 3/3.0)
82
+ Point.average(points).should == average_point
83
+ end
84
+
85
+ describe "should determine coincidence" do
86
+ before do
87
+ @p1 = Point.new(1,-1,0)
88
+ @p2 = Point.new(1,-1,0)
89
+ @p3 = Point.new(1.1,-1,0)
90
+ end
91
+
92
+ it "with another identical point" do
93
+ @p1.coincident?(@p2).should be_true
94
+ end
95
+
96
+ it "with a non-identical point" do
97
+ @p1.coincident?(@p3).should be_false
98
+ end
99
+
100
+ it "from an array of points and remove any coincident points" do
101
+ Point.remove_coincident([Point.new(1,-1,0), Point.new(1,-1,0), Point.new(1,-1,0), Point.new(1.1,-1,0)]).size.should == 2
102
+ end
103
+ end
104
+
105
+ describe "should determine if a point is between two points" do
106
+ before do
107
+ @p1 = Point.new(0,-1,0)
108
+ @p2 = Point.new(4,-1,0)
109
+ @p3 = Point.new(9,-1,0)
110
+ end
111
+ it "when the point is bounded" do
112
+ @p2.between?(@p1, @p3).should be_true
113
+ end
114
+
115
+ it "when the point is not bounded" do
116
+ @p1.between?(@p2, @p3).should be_false
117
+ end
118
+ end
119
+
120
+ describe "should determine if point is on plane" do
121
+ before do
122
+ @plane = Plane.new(0, 0, 1, 1)
123
+ @point_1 = Point.new(0, 0, 1)
124
+ @point_2 = Point.new(0, 0, 2)
125
+ @point_3 = Point.new(0, 0, 1.0000000001)
126
+ end
127
+
128
+ it "when the point is on the plane" do
129
+ @point_1.on_plane?(@plane).should be_true
130
+ end
131
+
132
+ it "when the point is not on the plane" do
133
+ @point_2.on_plane?(@plane).should be_false
134
+ end
135
+
136
+ it "when the point is close but not on the plane" do
137
+ @point_3.on_plane?(@plane).should be_false
138
+ end
139
+ end
140
+
141
+ describe "Projection" do
142
+ it "should be projected normal (dropped) onto plane" do
143
+ z_plane = Plane.new(0, 0, 1, 0)
144
+ oblique_plane = Plane.new(-1, 1, 0, 0)
145
+ oblique_offset_plane = Plane.new(0, 0, 1, 10)
146
+
147
+ point_1 = Point.new(1, 1, 1)
148
+ point_2 = Point.new(0, 1, 0)
149
+
150
+ point_1.project_onto_plane(z_plane).should == Point.new(1, 1, 0)
151
+ point_2.project_onto_plane(oblique_plane).should == Point.new(0.5, 0.5, 0)
152
+ point_1.project_onto_plane(oblique_offset_plane).should == Point.new(1, 1, 10)
153
+ end
154
+
155
+ it "should be projected along a vector onto plane" do
156
+ plane = Plane.new(Point.new(1,1,3), Point.new(0,0,3), Point.new(-1,1,3))
157
+ direction = Vector.new(1,1,4)
158
+ start_point = Point.new(5,5,0)
159
+ end_point = start_point.project_onto_plane_along_vector(plane, direction)
160
+
161
+ end_point.x.should be_within(0.001).of(5.75)
162
+ end_point.y.should be_within(0.001).of(5.75)
163
+ end_point.z.should be_within(0.001).of(3)
164
+ end
165
+
166
+ it "should be projected normal (dropped) onto line" do
167
+ line = Line.new(1, 1, 3, -1, 0, 2)
168
+ point_1 = Point.new(1, 1, 5)
169
+ point_1.project_onto_line(line).should == Point.new(3, 1, 4)
170
+
171
+ point_2 = Point.new(1, 3, 0)
172
+ point_2.project_onto_line(line).should == Point.new(1, 3, 0)
173
+ end
174
+
175
+ it "should be projected along a vector onto line" do
176
+ line = Line.new(Point.new(0, 0, 0), Point.new(2, 0, 0))
177
+ point = Point.new(0, 1, 0)
178
+ direction = Vector.new(1, -1, 0)
179
+ point.project_onto_line_along_vector(line, direction).should == Point.new(1, 0, 0)
180
+ point.should == Point.new(0, 1, 0)
181
+ end
182
+ end
183
+
184
+ describe "Translation" do
185
+ it "should translate along a vector" do
186
+ p = Point.new(1,0,0)
187
+ v = Vector.new(1,0,0)
188
+ p.translate(v).should == Point.new(2,0,0)
189
+ p.should == Point.new(1,0,0)
190
+ end
191
+
192
+ it "should translate along a vector a specified distance" do
193
+ p = Point.new(0,0,1)
194
+ v = Vector.new(0,1,0)
195
+ p.translate(v, 10).should == Point.new(0,10,1)
196
+ p.should == Point.new(0,0,1)
197
+ end
198
+ end
199
+
200
+ describe "Transformation:" do
201
+ it "should transform into a coordinate system" do
202
+ p1 = Point.new(2,2,0)
203
+ p2 = Point.new(5,5,0)
204
+ vx = Vector.new(1,0,0)
205
+ vy = Vector.new(0,1,0)
206
+ vz = Vector.new(0,0,1)
207
+ rcs = RectangularCoordinateSystem.new_from_xvector_and_xyplane(p2, vy, vz)
208
+ p1.transform(rcs).should == Point.new(-3,3,0)
209
+ p1.should == Point.new(2,2,0)
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../spec_helper.rb'
2
+ require 'geom/rectangular_coordinate_system'
3
+
4
+ module Geom
5
+ describe RectangularCoordinateSystem do
6
+
7
+ describe "Construction" do
8
+ before do
9
+ @origin = Point.new(0,0,0)
10
+ @ivector = Vector.new(1,0,0)
11
+ @jvector = Vector.new(0,1,0)
12
+ @kvector = Vector.new(0,0,1)
13
+ @rcs = RectangularCoordinateSystem.new
14
+ end
15
+
16
+ it "should construct a RCS using x-vector and xy-plane normal vector" do
17
+ RectangularCoordinateSystem.new_from_xvector_and_xyplane(@origin, @ivector, @kvector).should == @rcs
18
+ end
19
+
20
+ it "should construct a RCS using y-vector and yz-plane normal vector" do
21
+ RectangularCoordinateSystem.new_from_yvector_and_yzplane(@origin, @jvector, @ivector).should == @rcs
22
+ end
23
+
24
+ it "should construct a RCS using z-vector and zx-plane normal vector" do
25
+ RectangularCoordinateSystem.new_from_zvector_and_zxplane(@origin, @kvector, @jvector).should == @rcs
26
+ end
27
+ end
28
+
29
+ describe "Return types" do
30
+ it "should return a summary string" do
31
+ RectangularCoordinateSystem.new.to_s.should == "RCS[Point(0.000,0.000,0.000) X-Vector(1.000,0.000,0.000) Y-Vector(0.000,1.000,0.000) Z-Vector(0.000,0.000,1.000)]"
32
+ end
33
+ it "should print matrix formatted string" do
34
+ RectangularCoordinateSystem.new.to_s(true).should be
35
+ end
36
+
37
+ it "should calculate a 4x4 transformation matrix" do
38
+ RectangularCoordinateSystem.new.transformation_matrix.should == Matrix[
39
+ [1.0, 0.0, 0.0, 0.0],
40
+ [0.0, 1.0, 0.0, 0.0],
41
+ [0.0, 0.0, 1.0, 0.0],
42
+ [0.0, 0.0, 0.0, 1.0]
43
+ ]
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,69 @@
1
+ require_relative '../spec_helper.rb'
2
+ require 'geom/transformation'
3
+
4
+ module Geom
5
+ describe Transformation do
6
+ before do
7
+ @p1 = Point.new(0,0,0)
8
+ @p2 = Point.new(10,1,1)
9
+ @vx = Vector.new(1,0,0)
10
+ @vy = Vector.new(0,1,0)
11
+ @vz = Vector.new(0,0,1)
12
+ @rcs_1 = RectangularCoordinateSystem.new_from_xvector_and_xyplane(@p1, @vx, @vz)
13
+ @rcs_2 = RectangularCoordinateSystem.new_from_xvector_and_xyplane(@p2, @vx, @vz)
14
+ @rcs_3 = RectangularCoordinateSystem.new_from_xvector_and_xyplane(@p1, @vy, @vz)
15
+ end
16
+
17
+ describe "Construction" do
18
+ it "should create a valid instance from a coordinate systems" do
19
+ transform = Transformation.new(RectangularCoordinateSystem.new)
20
+ transform.identity?.should be_true
21
+ end
22
+ end
23
+
24
+ describe "Type" do
25
+ it "should determine if transformation involves translation" do
26
+ transform = Transformation.new(@rcs_2)
27
+ transform.translation?.should be_true
28
+ transform.rotation?.should be_false
29
+ transform.scaling?.should be_false
30
+ transform.type.should == 1
31
+ end
32
+
33
+ it "should determine if transformation involved rotation" do
34
+ transform = Transformation.new(@rcs_3)
35
+ transform.translation?.should be_false
36
+ transform.rotation?.should be_true
37
+ transform.scaling?.should be_false
38
+ transform.type.should == 2
39
+ end
40
+ end
41
+
42
+ describe "Return Types" do
43
+ it "should print matrix formatted string" do
44
+ Transformation.new(@rcs_1).to_s(true).should be
45
+ end
46
+
47
+ it "should create a description" do
48
+ Transformation.new(@rcs_2).type_description.should == "Type 1 Translation"
49
+ Transformation.new(@rcs_3).type_description.should == "Type 2 Rotation"
50
+ end
51
+
52
+ it "should calculate translation vector" do
53
+ Transformation.new(@rcs_2).translation_vector.should == Vector.new(10.0, 1.0, 1.0)
54
+ end
55
+ end
56
+
57
+ describe "Rotation Matrix" do
58
+ it "should extract sub-matrix" do
59
+ transform = Transformation.new(@rcs_2)
60
+ transform.rotation_submatrix.should == Matrix.diagonal(1,1,1)
61
+ end
62
+
63
+ it "should determine if diagonal when only non-zeros on the diagonal" do
64
+ transform = Transformation.new(@rcs_2)
65
+ transform.rotation_submatrix_diagonal?.should be_true
66
+ end
67
+ end
68
+ end
69
+ end