geo3d 0.0.5 → 0.0.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8af038ca077d7a6bdfd933b9486cf4a63ab8a73f
4
- data.tar.gz: 75bb46be5985112547d52bcdfb06046c651ba2af
3
+ metadata.gz: d8f3862b698223418bf989a4cce3c0dc45c74aec
4
+ data.tar.gz: 8df829279ef53a7003215b5aeb18c3a4127af036
5
5
  SHA512:
6
- metadata.gz: 81c47c990a4c186edb1c07d632f1c287d3854686ab589e071f6a496733d08b8c04929989ee473c7a08d91b5451e6cc1583311604cf87e4fb859a998b7c31b216
7
- data.tar.gz: 2799ed1ff5754f41b74ce781105c68a5ba864661346d36967012be1ca5cdbeebf8411b726713ea0ed95e3d23b4aaf845788de786c892157a13f64b1007c8a67f
6
+ metadata.gz: ef96a2ee15de30cc29f298cac21258a45c764d8895837525b172280973f89bf905ef7ceb5af4240471b5894c890e57aed57856e2c8337aca7472a47411462e66
7
+ data.tar.gz: 79c5f1a2776f2647d139f7de64f4fcc148a242bfe0bf9e9c0db3ba0357cda31d522114da94adf2cfd20581080028b36577c67bc814296b36c0d8428a93552518
data/README.md CHANGED
@@ -179,23 +179,71 @@ Matrix Decomposition
179
179
 
180
180
  A mathematical construct to represent rotations in 3d space.
181
181
 
182
+ Quaternions support all the basic math operations.
183
+
184
+ Addition
185
+ ```
186
+ quat_a + quat_b
187
+ ```
188
+ Subtraction
189
+ ```
190
+ quat_a - quat_b
191
+ ```
182
192
  Quaternion Multiplication
183
193
  ```
184
194
  quat_a * quat_b
185
195
  ```
196
+ Scalar Multiplication
197
+ ```
198
+ quat * scalar
199
+ ```
200
+ Scalar Division
201
+ ```
202
+ quat / scalar
203
+ ```
186
204
  Getting axis and angle
187
205
  ```
188
206
  quat.axis
189
- quat.angle
207
+ quat.angle #returns angle in radians
208
+ quat.angle_degrees #returns angle in degrees
190
209
  ```
191
210
  Converting to a matrix
192
211
  ```
193
212
  quat.to_matrix
194
213
  ```
214
+
215
+ Additional quaternion operations
216
+ Magnitude
217
+ ```
218
+ quat.length
219
+ ```
220
+ Squared Magnitude
221
+ ```
222
+ quat.length_squared
223
+ ```
224
+ Normalize
225
+ ```
226
+ quat.normalize #returns a normalized version of the quaternion
227
+ quat.normalize! #normalizes the quaternion in place
228
+ ```
229
+ Inverse
230
+ ```
231
+ quat.inverse #returns inverse of quaternion
232
+ ```
233
+ Conjugate
234
+ ```
235
+ quat.conjugate
236
+ ```
237
+ Dot product
238
+ ```
239
+ quat.dot
240
+ ```
241
+
195
242
  Constructors
196
243
  ```
197
244
  Geo3d::Quaternion.from_axis rotation_axis, radians #returns a quaternion from an axis and angle
198
245
  Geo3d::Quaternion.from_matrix m #returns a quaternion from a rotation matrix
246
+ Geo3d::Quaternion.identity #returns the identity quaternion
199
247
 
200
248
  ```
201
249
 
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
8
+
data/geo3d.gemspec CHANGED
@@ -20,4 +20,5 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
23
24
  end
data/lib/geo3d.rb CHANGED
@@ -2,5 +2,6 @@ require 'geo3d/version'
2
2
  require 'vector'
3
3
  require 'matrix'
4
4
  require 'quaternion'
5
+ require 'plane'
5
6
 
6
7
 
data/lib/geo3d/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Geo3d
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
data/lib/matrix.rb CHANGED
@@ -54,6 +54,32 @@ module Geo3d
54
54
  send (%w{_11 _12 _13 _14 _21 _22 _23 _24 _31 _32 _33 _34 _41 _42 _43 _44}[4*x + y] + '=').to_sym, v
55
55
  end
56
56
 
57
+ def row i
58
+ if i >= 0 && i <= 3
59
+ Vector.new self[0, i], self[1, i], self[2, i], self[3, i]
60
+ end
61
+ end
62
+
63
+ def col i
64
+ if i >= 0 && i <= 3
65
+ Vector.new self[i, 0], self[i, 1], self [i, 2], self[i, 3]
66
+ end
67
+ end
68
+
69
+ def set_row i, v
70
+ self[0, i] = v.x
71
+ self[1, i] = v.y
72
+ self[2, i] = v.z
73
+ self[3, i] = v.w
74
+ end
75
+
76
+ def set_col i, v
77
+ self[i, 0] = v.x
78
+ self[i, 1] = v.y
79
+ self[i, 2] = v.z
80
+ self[i, 3] = v.w
81
+ end
82
+
57
83
  def == m
58
84
  a = to_a
59
85
  b = m.to_a
@@ -155,10 +181,10 @@ module Geo3d
155
181
  elsif Vector == v.class
156
182
  vec = v
157
183
  transformed_vector = Vector.new
158
- transformed_vector.x = _11 * vec.x + _21 * vec.y + _31 * vec.z + _41
159
- transformed_vector.y = _12 * vec.x + _22 * vec.y + _32 * vec.z + _42
160
- transformed_vector.z = _13 * vec.x + _23 * vec.y + _33 * vec.z + _43
161
- transformed_vector.w = _14 * vec.x + _24 * vec.y + _34 * vec.z + _44
184
+ transformed_vector.x = _11 * vec.x + _21 * vec.y + _31 * vec.z + _41 * vec.w
185
+ transformed_vector.y = _12 * vec.x + _22 * vec.y + _32 * vec.z + _42 * vec.w
186
+ transformed_vector.z = _13 * vec.x + _23 * vec.y + _33 * vec.z + _43 * vec.w
187
+ transformed_vector.w = _14 * vec.x + _24 * vec.y + _34 * vec.z + _44 * vec.w
162
188
  return transformed_vector
163
189
  else
164
190
  scalar = v
@@ -212,12 +238,7 @@ module Geo3d
212
238
  end
213
239
 
214
240
  def transform_coord vec
215
- norm =_14 * vec.x + _24 * vec.y + _34 * vec.z + _44
216
- transformed_vector = self * vec
217
- transformed_vector.x /= norm
218
- transformed_vector.y /= norm
219
- transformed_vector.z /= norm
220
- transformed_vector
241
+ self * Vector.new( vec.x, vec.y, vec.z, 1.0 )
221
242
  end
222
243
 
223
244
  def transform vec
@@ -348,9 +369,9 @@ module Geo3d
348
369
 
349
370
 
350
371
  # calculate matrix inverse
351
- det = 1.0/det
372
+ inverse_det = 1.0/det
352
373
  for j in 0..15
353
- dst[j] *= det
374
+ dst[j] *= inverse_det
354
375
  end
355
376
 
356
377
  inverted_matrix = self.class.new *dst
@@ -402,6 +423,10 @@ module Geo3d
402
423
  puts "_44: #{_44}\n"
403
424
  end
404
425
 
426
+ def to_s
427
+ (0..3).to_a.map{ |i| row(i).to_s}.join "\n"
428
+ end
429
+
405
430
  def self.perspective_fov_rh fovy, aspect, zn, zf
406
431
  fovy = fovy.to_f
407
432
  aspect = aspect.to_f
@@ -533,6 +558,7 @@ module Geo3d
533
558
  end
534
559
 
535
560
  def self.reflection reflection_plane
561
+ reflection_plane = Geo3d::Vector.new *reflection_plane.to_a
536
562
  reflection_matrix = self.new
537
563
 
538
564
  plane_magnitude = Vector.new(reflection_plane.x, reflection_plane.y, reflection_plane.z, 0).length
@@ -566,6 +592,7 @@ module Geo3d
566
592
  end
567
593
 
568
594
  def self.shadow light_position, plane
595
+ plane = Geo3d::Vector.new *plane.to_a
569
596
  norm = plane.x * plane.x + plane.y * plane.y + plane.z * plane.z
570
597
  normalized_plane = plane / norm
571
598
  dot = normalized_plane.dot(light_position)
data/lib/plane.rb ADDED
@@ -0,0 +1,95 @@
1
+ module Geo3d
2
+ class Plane
3
+ attr_accessor :a, :b, :c, :d
4
+ alias :x :a
5
+ alias :y :b
6
+ alias :z :c
7
+ alias :w :d
8
+
9
+ def initialize *args
10
+ @a = 0.0
11
+ @b = 0.0
12
+ @c = 0.0
13
+ @d = 0.0
14
+ @a = args[0].to_f if args.size > 0
15
+ @b = args[1].to_f if args.size > 1
16
+ @c = args[2].to_f if args.size > 2
17
+ @d = args[3].to_f if args.size > 3
18
+ end
19
+
20
+ def self.from_points pv1, pv2, pv3
21
+ edge1 = pv2 - pv1
22
+ edge2 = pv3 - pv1
23
+ from_point_and_normal pv1, edge1.cross(edge2).normalize
24
+ end
25
+
26
+ def self.from_point_and_normal point, normal
27
+ point.w = 0
28
+ self.new normal.x, normal.y, normal.z, -point.dot(normal)
29
+ end
30
+
31
+ def to_a
32
+ [a,b,c,d]
33
+ end
34
+
35
+ def == p
36
+ Geo3d::Utils.float_cmp(a, p.a) && Geo3d::Utils.float_cmp(b, p.b) && Geo3d::Utils.float_cmp(c, p.c) && Geo3d::Utils.float_cmp(d, p.d)
37
+ end
38
+
39
+ def != vec
40
+ !(self == vec)
41
+ end
42
+
43
+ def dot v
44
+ a * v.x + b * v.y + c * v.z + d * v.w
45
+ end
46
+
47
+ def normalize!
48
+ norm = Math.sqrt(a*a + b*b + c*c)
49
+ if norm.zero?
50
+ @a = 0
51
+ @b = 0
52
+ @c = 0
53
+ @d = 0
54
+ else
55
+ @a /= norm
56
+ @b /= norm
57
+ @c /= norm
58
+ @d /= norm
59
+ end
60
+ end
61
+
62
+ def normalize
63
+ p = self.class.new a, b, c, d
64
+ p.normalize!
65
+ p
66
+ end
67
+
68
+ def normal
69
+ Vector.new a, b, c
70
+ end
71
+
72
+ def line_intersection line_start, line_end
73
+ direction = line_end - line_start
74
+
75
+ normal_dot_direction = normal.dot direction
76
+
77
+ if (normal_dot_direction.zero?)
78
+ nil
79
+ else
80
+ temp = (d + normal.dot(line_start)) / normal_dot_direction
81
+ line_start - direction * temp
82
+ end
83
+ end
84
+
85
+ def transform matrix, use_inverse_transpose = true
86
+ matrix = matrix.inverse.transpose if use_inverse_transpose
87
+ p = self.class.new
88
+ p.a = dot matrix.row(0)
89
+ p.b = dot matrix.row(1)
90
+ p.c = dot matrix.row(2)
91
+ p.d = dot matrix.row(3)
92
+ p
93
+ end
94
+ end
95
+ end
data/lib/quaternion.rb CHANGED
@@ -46,9 +46,7 @@ module Geo3d
46
46
  end
47
47
 
48
48
  def self.from_axis rotation_axis, radians = 0
49
- normalized_rotation_axis = rotation_axis.normalize
50
- #const float radians = GeoConvertToRadians( degrees );
51
-
49
+ normalized_rotation_axis = rotation_axis.zero_w.normalize
52
50
  q = self.new
53
51
  q.x = Math.sin(radians / 2.0) * normalized_rotation_axis.x
54
52
  q.y = Math.sin(radians / 2.0) * normalized_rotation_axis.y
@@ -70,6 +68,8 @@ module Geo3d
70
68
  pout.y = (pm._31 - pm._13) / (2.0 * Math.sqrt(trace))
71
69
  pout.z = (pm._12- pm._21) / (2.0 * Math.sqrt(trace))
72
70
  pout.w = Math.sqrt(trace) / 2.0
71
+ puts "a and pout is #{pout.inspect}"
72
+
73
73
  return pout
74
74
  end
75
75
  maxi = 0
@@ -104,6 +104,7 @@ module Geo3d
104
104
  pout.z = 0.25 * s
105
105
  pout.w = (pm._12 - pm._21) / s
106
106
  end
107
+ puts "b"
107
108
  pout
108
109
  end
109
110
 
@@ -134,7 +135,7 @@ module Geo3d
134
135
  end
135
136
 
136
137
  def to_matrix
137
- v = Vector.new(x, y, z, w); ## Normalize();
138
+ v = normalize
138
139
  matrix = Matrix.identity
139
140
  matrix._11 = 1.0 - 2.0 * (v.y * v.y + v.z * v.z)
140
141
  matrix._12 = 2.0 * (v.x * v.y + v.z * v.w)
data/lib/vector.rb CHANGED
@@ -17,6 +17,14 @@ module Geo3d
17
17
  @w = args[3].to_f if args.size > 3
18
18
  end
19
19
 
20
+ def zero_w
21
+ self.class.new x, y, z, 0
22
+ end
23
+
24
+ def one_w
25
+ self.class.new x, y, z, 1
26
+ end
27
+
20
28
  def to_s
21
29
  to_a.compact.join ' '
22
30
  end
@@ -0,0 +1,227 @@
1
+ require 'spec_helper'
2
+
3
+ describe Geo3d::Matrix do
4
+ def random_matrix
5
+ r = Geo3d::Matrix.new
6
+ for i in 0..3
7
+ for j in 0..3
8
+ r[i, j] = rand 10000
9
+ end
10
+ end
11
+ r
12
+ end
13
+
14
+ def random_vector
15
+ v = Geo3d::Vector.new
16
+ v.x = rand 10000
17
+ v.y = rand 10000
18
+ v.z = rand 10000
19
+ v.w = rand 10000
20
+ v
21
+ end
22
+
23
+
24
+ it "should default all values to zero" do
25
+ Geo3d::Matrix.new.to_a.select(&:zero?).size.should == 16
26
+ end
27
+
28
+ it "should be able to extract translation component" do
29
+
30
+ end
31
+
32
+ it "should be able to extract scaling component" do
33
+
34
+ end
35
+
36
+ it "should be able to extract rotation component" do
37
+
38
+ end
39
+
40
+ it "should be invertible" do
41
+ 100.times do
42
+ r = random_matrix
43
+ (r * r.inverse).identity?.should == true
44
+ end
45
+ end
46
+
47
+ it "should return the determinant" do
48
+ [{:matrix => [0.321046, 0.000000, 0.000000, 0.000000, 0.000000, 0.642093, 0.000000, 0.000000, 0.000000, 0.000000, -1.000095, -1.000000, 0.000000, 0.000000, -2.000190, 0.000000], :expected => -0.412322},
49
+ {:matrix => [1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, -2.000000, 0.000000, 1.000000], :expected => -1},
50
+ {:matrix => [-0.392804, -0.878379, -0.272314, 0.000000, 0.000000, 0.296115, -0.955152, 0.000000, 0.919622, -0.375187, -0.116315, 0.000000, -2.366064, 1.411711, 2.531564, 1.000000], :expected => 1}].each do |data|
51
+ data[:matrix].size.should == 16
52
+ Geo3d::Utils.float_cmp( Geo3d::Matrix.new(*data[:matrix]).determinant, data[:expected] ).should == true
53
+ end
54
+ end
55
+
56
+ it "should be transposable" do
57
+
58
+ end
59
+
60
+ it "should have an identity constructor" do
61
+ identity = Geo3d::Matrix.identity
62
+ identity.identity?.should == true
63
+ end
64
+
65
+ it "should have a right handed perspective projection constructor" do
66
+ [{:fovy => 2, :aspect => 2, :zn => 2, :zf => 21000, :expected => [0.321046, 0.000000, 0.000000, 0.000000, 0.000000, 0.642093, 0.000000, 0.000000, 0.000000, 0.000000, -1.000095, -1.000000, 0.000000, 0.000000, -2.000190, 0.000000]}].each do |data|
67
+ matrix = Geo3d::Matrix.perspective_fov_rh data[:fovy], data[:aspect], data[:zn], data[:zf]
68
+ data[:expected].size.should == 16
69
+ expected = Geo3d::Matrix.new *data[:expected]
70
+ matrix.should == expected
71
+ end
72
+ end
73
+
74
+
75
+ it "should have a left handed perspective projection constructor" do
76
+ [{:fovy => 2, :aspect => 2, :zn => 2, :zf => 21000, :expected => [0.321046, 0.000000, 0.000000, 0.000000, 0.000000, 0.642093, 0.000000, 0.000000, 0.000000, 0.000000, 1.000095, 1.000000, 0.000000, 0.000000, -2.000190, 0.000000]}].each do |data|
77
+ matrix = Geo3d::Matrix.perspective_fov_lh data[:fovy], data[:aspect], data[:zn], data[:zf]
78
+ data[:expected].size.should == 16
79
+ expected = Geo3d::Matrix.new *data[:expected]
80
+ matrix.should == expected
81
+ end
82
+ end
83
+
84
+
85
+ it "should have a right handed orthographic projection constructor" do
86
+ [{:l => -100, :r => 100, :b => -200, :t => 200, :zn => 1, :zf => 2000, :expected => [0.010000, 0.000000, 0.000000, 0.000000, 0.000000, 0.005000, 0.000000, 0.000000, 0.000000, 0.000000, -0.000500, 0.000000, -0.000000, -0.000000, -0.000500, 1.000000]}].each do |data|
87
+ matrix = Geo3d::Matrix.ortho_off_center_rh data[:l], data[:r], data[:b], data[:t], data[:zn], data[:zf]
88
+ data[:expected].size.should == 16
89
+ expected = Geo3d::Matrix.new *data[:expected]
90
+ matrix.should == expected
91
+ end
92
+ end
93
+
94
+
95
+ it "should have a left handed orthographic projection constructor" do
96
+ [{:l => -100, :r => 100, :b => -200, :t => 200, :zn => 1, :zf => 2000, :expected => [0.010000, 0.000000, 0.000000, 0.000000, 0.000000, 0.005000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000500, 0.000000, -0.000000, -0.000000, -0.000500, 1.000000]}].each do |data|
97
+ matrix = Geo3d::Matrix.ortho_off_center_lh data[:l], data[:r], data[:b], data[:t], data[:zn], data[:zf]
98
+ data[:expected].size.should == 16
99
+ expected = Geo3d::Matrix.new *data[:expected]
100
+ matrix.should == expected
101
+ end
102
+ end
103
+
104
+
105
+ it "should have a right handed view constructor" do
106
+ [{:eye => [1, 2, 3], :focus => [200, 700, 88], :up => [0, 1, 0], :expected => [-0.392804, -0.878379, -0.272314, 0.000000, 0.000000, 0.296115, -0.955152, 0.000000, 0.919622, -0.375187, -0.116315, 0.000000, -2.366064, 1.411711, 2.531564, 1.000000]}].each do |data|
107
+ matrix = Geo3d::Matrix.look_at_rh Geo3d::Vector.new(*data[:eye]), Geo3d::Vector.new(*data[:focus]), Geo3d::Vector.new(*data[:up])
108
+ data[:expected].size.should == 16
109
+ expected = Geo3d::Matrix.new *data[:expected]
110
+ matrix.should == expected
111
+ end
112
+ end
113
+
114
+
115
+ it "should have a left handed view constructor" do
116
+ [{:eye => [1, 2, 3], :focus => [200, 700, 88], :up => [0, 1, 0], :expected => [0.392804, -0.878379, 0.272314, 0.000000, 0.000000, 0.296115, 0.955152, 0.000000, -0.919622, -0.375187, 0.116315, 0.000000, 2.366064, 1.411711, -2.531564, 1.000000]}].each do |data|
117
+ matrix = Geo3d::Matrix.look_at_lh Geo3d::Vector.new(*data[:eye]), Geo3d::Vector.new(*data[:focus]), Geo3d::Vector.new(*data[:up])
118
+ data[:expected].size.should == 16
119
+ expected = Geo3d::Matrix.new *data[:expected]
120
+ matrix.should == expected
121
+ end
122
+ end
123
+
124
+ it "should have a translation constructor" do
125
+ 10.times do
126
+ random_translation = random_vector
127
+ matrix = Geo3d::Matrix.translation random_translation.x, random_translation.y, random_translation.z
128
+ 10.times do
129
+ random_vec = random_vector.one_w
130
+ Geo3d::Utils.float_cmp((matrix * random_vec).x, random_vec.x + random_translation.x).should == true
131
+ Geo3d::Utils.float_cmp((matrix * random_vec).y, random_vec.y + random_translation.y).should == true
132
+ Geo3d::Utils.float_cmp((matrix * random_vec).z, random_vec.z + random_translation.z).should == true
133
+ Geo3d::Utils.float_cmp((matrix * random_vec).w, 1).should == true
134
+ end
135
+ end
136
+ end
137
+
138
+ it "should have a scaling constructor" do
139
+ 10.times do
140
+ random_scaling = random_vector
141
+ matrix = Geo3d::Matrix.scaling random_scaling.x, random_scaling.y, random_scaling.z
142
+ 10.times do
143
+ random_vec = random_vector.one_w
144
+ Geo3d::Utils.float_cmp((matrix * random_vec).x, random_vec.x * random_scaling.x).should == true
145
+ Geo3d::Utils.float_cmp((matrix * random_vec).y, random_vec.y * random_scaling.y).should == true
146
+ Geo3d::Utils.float_cmp((matrix * random_vec).z, random_vec.z * random_scaling.z).should == true
147
+ Geo3d::Utils.float_cmp((matrix * random_vec).w, 1).should == true
148
+ end
149
+ end
150
+ end
151
+
152
+
153
+ it "should have an x-axis rotation constructor" do
154
+ [{:angle => 1, :expected => [1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.540302, 0.841471, 0.000000, 0.000000, -0.841471, 0.540302, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]},
155
+ {:angle => 3.2, :expected => [1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -0.998295, -0.058374, 0.000000, 0.000000, 0.058374, -0.998295, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]}].each do |data|
156
+ matrix = Geo3d::Matrix.rotation_x data[:angle]
157
+ data[:expected].size.should == 16
158
+ expected = Geo3d::Matrix.new *data[:expected]
159
+ matrix.should == expected
160
+ end
161
+ end
162
+
163
+ it "should have an y-axis rotation constructor" do
164
+ [{:angle => 1, :expected => [0.540302, 0.000000, -0.841471, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.841471, 0.000000, 0.540302, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]},
165
+ {:angle => 3.2, :expected => [-0.998295, 0.000000, 0.058374, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, -0.058374, 0.000000, -0.998295, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]}].each do |data|
166
+ matrix = Geo3d::Matrix.rotation_y data[:angle]
167
+ data[:expected].size.should == 16
168
+ expected = Geo3d::Matrix.new *data[:expected]
169
+ matrix.should == expected
170
+ end
171
+ end
172
+
173
+ it "should have a z-axis rotation constructor" do
174
+ [{:angle => 1, :expected => [0.540302, 0.841471, 0.000000, 0.000000, -0.841471, 0.540302, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]},
175
+ {:angle => 3.2, :expected => [-0.998295, -0.058374, 0.000000, 0.000000, 0.058374, -0.998295, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]}].each do |data|
176
+ matrix = Geo3d::Matrix.rotation_z data[:angle]
177
+ data[:expected].size.should == 16
178
+ expected = Geo3d::Matrix.new *data[:expected]
179
+ matrix.should == expected
180
+ end
181
+ end
182
+
183
+ it "should have an arbitrary axis rotation constructor" do
184
+ [{:axis => [1, 1, 0], :angle => 88.7, :expected => [0.870782, 0.129218, -0.474385, 0.000000, 0.129218, 0.870782, 0.474385, 0.000000, 0.474385, -0.474385, 0.741564, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]}].each do |data|
185
+ matrix = Geo3d::Matrix.rotation Geo3d::Vector.new(*data[:axis]), data[:angle]
186
+ data[:expected].size.should == 16
187
+ expected = Geo3d::Matrix.new *data[:expected]
188
+ matrix.should == expected
189
+ end
190
+ end
191
+
192
+ it "should have a reflection constructor" do
193
+ [{:plane => [0, 1, 0, 0], :expected => [1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]},
194
+ {:plane => [0, 1, 0, 1], :expected => [1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, -2.000000, 0.000000, 1.000000]}].each do |data|
195
+ matrix = Geo3d::Matrix.reflection Geo3d::Plane.new(*data[:plane])
196
+ data[:expected].size.should == 16
197
+ expected = Geo3d::Matrix.new *data[:expected]
198
+ matrix.should == expected
199
+ end
200
+ end
201
+
202
+ it "should have a shadow constructor" do
203
+ [{:plane => [0, 1, 0, 1], :light_pos => [0, 700, 0, 1], :expected => [701.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, -1.000000, 0.000000, 0.000000, 701.000000, 0.000000, 0.000000, -700.000000, 0.000000, 700.000000]}].each do |data|
204
+ matrix = Geo3d::Matrix.shadow Geo3d::Vector.new(*data[:light_pos]), Geo3d::Plane.new(*data[:plane])
205
+ data[:expected].size.should == 16
206
+ expected = Geo3d::Matrix.new *data[:expected]
207
+ matrix.should == expected
208
+ end
209
+ end
210
+
211
+ it "multiplying a matrix by the identity matrix should result in the same matrix" do
212
+ identity = Geo3d::Matrix.identity
213
+ 10.times do
214
+ r = random_matrix
215
+ (r * identity).should == r
216
+ (identity * r).should == r
217
+ end
218
+ end
219
+
220
+ it "should transform vectors" do
221
+
222
+ end
223
+
224
+ it "should multiply with other matrices" do
225
+
226
+ end
227
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Geo3d::Plane do
4
+ it "should default all values to zero" do
5
+ p = Geo3d::Plane.new
6
+ p.a.zero?.should == true
7
+ p.b.zero?.should == true
8
+ p.c.zero?.should == true
9
+ p.d.zero?.should == true
10
+ end
11
+
12
+ it "should construct from a point and a normal" do
13
+ [{:point => [0, 0, 0], :normal => [0, 1, 0], :expected => [0, 1, 0, 0]},
14
+ {:point => [1, 2, 3], :normal => [1, 1, 1], :expected => [1, 1, 1, -6]},
15
+ {:point => [221, 772, 33], :normal => [2, 0, 1], :expected => [2, 0, 1, -475]},
16
+ {:point => [999, 888, 777], :normal => [-1, 3, 0], :expected => [-1, 3, 0, -1665]}].each do |data|
17
+ plane = Geo3d::Plane.from_point_and_normal Geo3d::Vector.new(*data[:point]), Geo3d::Vector.new(*data[:normal])
18
+ expected = Geo3d::Plane.new *data[:expected]
19
+ plane.should == expected
20
+ end
21
+ end
22
+
23
+ it "should construct from points" do
24
+ [{:a => [1, 1, 1], :b => [2, 2, 2], :c => [3, 3, 3], :expected => [0, 0, 0, 0]},
25
+ {:a => [-1.5, 1.5, 3.0, 0], :b => [1.5, 1.5, 3.0, 0], :c => [-1.5, -1.5, 3.0, 0], :expected => [0, 0, -1, 3]},
26
+ {:a => [10, 1, 33], :b => [1, 11, 3], :c => [-1, -1, 3], :expected => [-0.930808, 0.155135, 0.330954, -1.768534]}].each do |data|
27
+ plane = Geo3d::Plane.from_points Geo3d::Vector.new(*data[:a]), Geo3d::Vector.new(*data[:b]), Geo3d::Vector.new(*data[:c])
28
+ expected = Geo3d::Plane.new *data[:expected]
29
+ plane.should == expected
30
+ end
31
+ end
32
+
33
+ it "should support dot products with vectors" do
34
+ [{:plane => [1, 1, 1, 1], :vector => [2, 2, 2, 2], :expected => 8},
35
+ {:plane => [0, 1, 0, -99], :vector => [0, -42, 2, 52], :expected => -5190},
36
+ {:plane => [91, -2731, 1, 123], :vector => [2, 7, -9, 2], :expected => -18698},
37
+ ].each do |data|
38
+ plane = Geo3d::Plane.new *data[:plane]
39
+ vector = Geo3d::Vector.new *data[:vector]
40
+ Geo3d::Utils.float_cmp(plane.dot(vector), data[:expected]).should == true
41
+ end
42
+ end
43
+
44
+ it "should be normalizable" do
45
+ [{:plane => [0, 0, -1, 3], :expected => [0, 0, -1, 3]},
46
+ {:plane => [11, 11, -7, 3], :expected => [0.644831, 0.644831, -0.410347, 0.175863]},
47
+ {:plane => [-56, 23, 923, 9], :expected => [-0.060542, 0.024865, 0.997856, 0.009730]}].each do |data|
48
+ plane = Geo3d::Plane.new(*data[:plane]).normalize
49
+ expected = Geo3d::Plane.new *data[:expected]
50
+ plane.should == expected
51
+ end
52
+ end
53
+
54
+ it "should be able to detect line intersections" do
55
+ [{:plane => [0,0,-1,3], :line_start => [1,1,1,0], :line_end => [2,2,2,0], :expected => [3,3,3,0]},
56
+ {:plane => [0,0,-1,3], :line_start => [1,1,1,1], :line_end => [2,2,2,1], :expected => [3,3,3,1]},
57
+ {:plane => [0,1,0,-30], :line_start => [1,0,0,0], :line_end => [2,0,0,0], :expected => nil},
58
+ {:plane => [0,1,0,-30], :line_start => [1,1,0,0], :line_end => [2,0,0,0], :expected => [-28,30,0,0]},
59
+ {:plane => [0,1,0,-30], :line_start => [1,1,0,1], :line_end => [2,0,0,1], :expected => [-28,30,0,1]}].each do |data|
60
+ plane = Geo3d::Plane.new *data[:plane]
61
+ line_start = Geo3d::Vector.new *data[:line_start]
62
+ line_end = Geo3d::Vector.new *data[:line_end]
63
+ if data[:expected]
64
+ expected = Geo3d::Vector.new *data[:expected]
65
+ else
66
+ expected = nil
67
+ end
68
+ plane.line_intersection(line_start, line_end).should == expected
69
+ end
70
+ end
71
+
72
+ it "should be transformable" do
73
+ test_transform = ->(matrix, plane,expected) do
74
+ plane.transform(matrix).should == expected
75
+ end
76
+ test_transform.call Geo3d::Matrix.translation(0,5,0), Geo3d::Plane.new(0,1,0,0), Geo3d::Plane.new(0,1,0,-5)
77
+ test_transform.call Geo3d::Matrix.translation(0,5,0), Geo3d::Plane.new(1,1,1,1), Geo3d::Plane.new(1,1,1,-4)
78
+ test_transform.call Geo3d::Matrix.rotation_x(1), Geo3d::Plane.new(1,1,1,1), Geo3d::Plane.new(1.000000, -0.301169, 1.381773, 1.000000)
79
+ end
80
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe Geo3d::Quaternion do
4
+ it "should default all values to zero" do
5
+ q = Geo3d::Quaternion.new
6
+ q.x.zero?.should == true
7
+ q.y.zero?.should == true
8
+ q.z.zero?.should == true
9
+ q.w.zero?.should == true
10
+ end
11
+
12
+ it "should be constructable from an axis and angle" do
13
+ [{:axis => [0, 1, 0], :angle => 1, :expected => [0.000000, 0.479426, 0.000000, 0.877583]},
14
+ {:axis => [0, -1, 0], :angle => 1, :expected => [0.000000, -0.479426, 0.000000, 0.877583]},
15
+ {:axis => [0, 1, 0], :angle => -1, :expected => [0.000000, -0.479426, 0.000000, 0.877583]},
16
+ {:axis => [0, 1, 0], :angle => -6, :expected => [-0.000000, -0.141120, -0.000000, -0.989992]},
17
+ {:axis => [-213, 133, 22, -232], :angle => -3432, :expected => [0.538065, -0.335975, -0.055575, 0.771050]}].each do |data|
18
+ Geo3d::Quaternion.from_axis(Geo3d::Vector.new(*data[:axis]), data[:angle]).should == Geo3d::Quaternion.new(*data[:expected])
19
+ end
20
+ end
21
+
22
+ it "should be constructable from a rotation matrix" do
23
+ Geo3d::Quaternion.from_matrix(Geo3d::Matrix.rotation_x 1).should == Geo3d::Quaternion.new(0.479426, 0.000000, -0.000000, 0.877583)
24
+ Geo3d::Quaternion.from_matrix(Geo3d::Matrix.rotation_y 1).should == Geo3d::Quaternion.new(-0.000000, 0.479426, -0.000000, 0.877583)
25
+ Geo3d::Quaternion.from_matrix(Geo3d::Matrix.rotation_z 1).should == Geo3d::Quaternion.new(-0.000000, 0.000000, 0.479426, 0.877583)
26
+ Geo3d::Quaternion.from_matrix(Geo3d::Matrix.rotation_x 3.2).should == Geo3d::Quaternion.new(0.999574, 0.000000, 0.000000, -0.029200)
27
+ end
28
+
29
+ it "should be able to construct as the identity quaternion" do
30
+ q = Geo3d::Quaternion.identity
31
+ q.should == Geo3d::Quaternion.new(0, 0, 0, 1)
32
+ q.identity?.should == true
33
+ end
34
+
35
+ it "should be able to convert to a rotation matrix" do
36
+
37
+ end
38
+
39
+ it "should return axis of rotation" do
40
+ for i in 0..10000
41
+ angle = 0.1 * i + 0.1
42
+ # puts "angle is #{angle}"
43
+ #Geo3d::Quaternion.from_matrix(Geo3d::Matrix.rotation_x angle).axis.should == Geo3d::Vector.new(1, 0, 0)
44
+ #Geo3d::Quaternion.from_matrix(Geo3d::Matrix.rotation_y angle).axis.should == Geo3d::Vector.new(0, 1, 0)
45
+ #Geo3d::Quaternion.from_matrix(Geo3d::Matrix.rotation_z angle).axis.should == Geo3d::Vector.new(0, 0, 1)
46
+ end
47
+ end
48
+
49
+ it "should return rotation amount as angle" do
50
+
51
+ end
52
+
53
+ it "should return conjugate" do
54
+ [{:quaternion => [1, 1, 1, 1], :expected => [-1, -1, -1, 1]}].each do |data|
55
+ quaternion = Geo3d::Quaternion.new *data[:quaternion]
56
+ expected = Geo3d::Quaternion.new *data[:expected]
57
+ quaternion.conjugate.should == expected
58
+ end
59
+ end
60
+
61
+ it "should return inverse" do
62
+ [{:quaternion => [1, 1, 1, 1], :expected => [-0.250000, -0.250000, -0.250000, 0.250000]},
63
+ {:quaternion => [-1, -1, -1, -1], :expected => [0.250000, 0.250000, 0.250000, -0.250000]}].each do |data|
64
+ quaternion = Geo3d::Quaternion.new *data[:quaternion]
65
+ expected = Geo3d::Quaternion.new *data[:expected]
66
+ quaternion.inverse.should == expected
67
+ end
68
+ end
69
+
70
+ it "should support dot products with other quaternions" do
71
+ [{:a => [1, 1, 1, 1], :b => [2, 2, 2, 2], :expected => 8},
72
+ {:a => [0, 1, 0, -99], :b => [0, -42, 2, 52], :expected => -5190},
73
+ {:a => [91, -2731, 1, 123], :b => [2, 7, -9, 2], :expected => -18698},
74
+ ].each do |data|
75
+ a = Geo3d::Quaternion.new *data[:a]
76
+ b = Geo3d::Quaternion.new *data[:b]
77
+ Geo3d::Utils.float_cmp(a.dot(b), data[:expected]).should == true
78
+ end
79
+ end
80
+
81
+ it "should return length" do
82
+ [{:quaternion => [0, 0, -1, 3], :expected => 3.162278},
83
+ {:quaternion => [11, 11, -7, 3], :expected => 17.320509},
84
+ {:quaternion => [-56, 23, 923, 9], :expected => 925.027039}].each do |data|
85
+ quaternion = Geo3d::Quaternion.new *data[:quaternion]
86
+ expected = data[:expected]
87
+ Geo3d::Utils.float_cmp(quaternion.length, expected).should == true
88
+ end
89
+ end
90
+
91
+ it "should return length squared" do
92
+ [{:quaternion => [0, 0, -1, 3], :expected => 10},
93
+ {:quaternion => [11, 11, -7, 3], :expected => 300},
94
+ {:quaternion => [-56, 23, 923, 9], :expected => 855675}].each do |data|
95
+ quaternion = Geo3d::Quaternion.new *data[:quaternion]
96
+ expected = data[:expected]
97
+ Geo3d::Utils.float_cmp(quaternion.length_squared, expected).should == true
98
+ end
99
+ end
100
+
101
+ it "should be normalizable" do
102
+ [{:quaternion => [0, 0, -1, 3], :expected => [0, 0, -0.316228, 0.948683]},
103
+ {:quaternion => [11, 11, -7, 3], :expected => [0.635085, 0.635085, -0.404145, 0.173205]},
104
+ {:quaternion => [-56, 23, 923, 9], :expected => [-0.060539, 0.024864, 0.997809, 0.009729]}].each do |data|
105
+ quaternion = Geo3d::Quaternion.new(*data[:quaternion]).normalize
106
+ expected = Geo3d::Quaternion.new *data[:expected]
107
+ quaternion.should == expected
108
+ end
109
+ end
110
+
111
+ #todo: add tests for quaternion interpolation
112
+
113
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Geo3d::Vector do
4
+ it "should default all values to zero" do
5
+ v = Geo3d::Vector.new
6
+ v.x.zero?.should == true
7
+ v.y.zero?.should == true
8
+ v.z.zero?.should == true
9
+ v.w.zero?.should == true
10
+ end
11
+
12
+ it "should support dot products with other vectors" do
13
+ [{:a => [1, 1, 1, 1], :b => [2, 2, 2, 2], :expected => 8},
14
+ {:a => [0, 1, 0, -99], :b => [0, -42, 2, 52], :expected => -5190},
15
+ {:a => [91, -2731, 1, 123], :b => [2, 7, -9, 2], :expected => -18698},
16
+ ].each do |data|
17
+ a = Geo3d::Vector.new *data[:a]
18
+ b = Geo3d::Vector.new *data[:b]
19
+ Geo3d::Utils.float_cmp(a.dot(b), data[:expected]).should == true
20
+ end
21
+ end
22
+
23
+ it "should support cross products with other vectors" do
24
+ [{:a => [1,0,0,0], :b => [0,1,0,0], :expected => [0,0,1,0] },
25
+ {:a => [1,0,0,1], :b => [0,1,0,1], :expected => [0,0,1,0] },
26
+ {:a => [1,1,1,1], :b => [1,1,1,1], :expected => [0,0,0,0] },
27
+ {:a => [2,99,6,0], :b => [-11,-91,77,0], :expected => [8169.000000, -220.000000, 907.000000, 0.000000] }].each do |data|
28
+ a = Geo3d::Vector.new *data[:a]
29
+ b = Geo3d::Vector.new *data[:b]
30
+ expected = Geo3d::Vector.new *data[:expected]
31
+ a.cross(b).should == expected
32
+ b.cross(a).should == -expected
33
+ end
34
+ end
35
+
36
+ it "should return length" do
37
+ [{:vector => [0, 0, -1, 3], :expected => 3.162278},
38
+ {:vector => [11, 11, -7, 3], :expected => 17.320509},
39
+ {:vector => [-56, 23, 923, 9], :expected => 925.027039}].each do |data|
40
+ vector = Geo3d::Vector.new *data[:vector]
41
+ expected = data[:expected]
42
+ Geo3d::Utils.float_cmp(vector.length, expected).should == true
43
+ end
44
+ end
45
+
46
+ it "should return length squared" do
47
+ [{:vector => [0, 0, -1, 3], :expected => 10},
48
+ {:vector => [11, 11, -7, 3], :expected => 300},
49
+ {:vector => [-56, 23, 923, 9], :expected => 855675}].each do |data|
50
+ vector = Geo3d::Vector.new *data[:vector]
51
+ expected = data[:expected]
52
+ Geo3d::Utils.float_cmp(vector.length_squared, expected).should == true
53
+ end
54
+ end
55
+
56
+ it "should be normalizable" do
57
+ [{:vector => [0, 0, -1, 3], :expected => [0, 0, -0.316228, 0.948683]},
58
+ {:vector => [11, 11, -7, 3], :expected => [0.635085, 0.635085, -0.404145, 0.173205]},
59
+ {:vector => [-56, 23, 923, 9], :expected => [-0.060539, 0.024864, 0.997809, 0.009729]}].each do |data|
60
+ vector = Geo3d::Vector.new(*data[:vector]).normalize
61
+ expected = Geo3d::Vector.new *data[:expected]
62
+ vector.should == expected
63
+ end
64
+ end
65
+
66
+ it "should be able to linearly interpolate" do
67
+ [{:a => [0,0,0,0], :b => [1,1,1,1], :interpolate_fraction => 0.5, :expected => [0.5, 0.5, 0.5, 0.5]},
68
+ {:a => [23,-3,425,-332], :b => [-22,-45443,886,122], :interpolate_fraction => 0.21234433, :expected => [13.444505, -9651.926758, 522.890747, -235.595673]}].each do |data|
69
+ a = Geo3d::Vector.new *data[:a]
70
+ b = Geo3d::Vector.new *data[:b]
71
+ expected = Geo3d::Vector.new *data[:expected]
72
+ s = data[:interpolate_fraction]
73
+ a.lerp( b, s).should == expected
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1 @@
1
+ require 'geo3d'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geo3d
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Misha Conway
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-13 00:00:00.000000000 Z
11
+ date: 2014-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  description: Library for common 3d graphics vector and matrix operations
42
56
  email:
43
57
  - MishaAConway@gmail.com
@@ -54,9 +68,15 @@ files:
54
68
  - lib/geo3d.rb
55
69
  - lib/geo3d/version.rb
56
70
  - lib/matrix.rb
71
+ - lib/plane.rb
57
72
  - lib/quaternion.rb
58
73
  - lib/utils.rb
59
74
  - lib/vector.rb
75
+ - spec/lib/matrix_spec.rb
76
+ - spec/lib/plane_spec.rb
77
+ - spec/lib/quaternion_spec.rb
78
+ - spec/lib/vector_spec.rb
79
+ - spec/spec_helper.rb
60
80
  homepage: https://github.com/MishaConway/geo3d
61
81
  licenses:
62
82
  - MIT
@@ -81,4 +101,9 @@ rubygems_version: 2.1.11
81
101
  signing_key:
82
102
  specification_version: 4
83
103
  summary: Library for common 3d graphics vector and matrix operations
84
- test_files: []
104
+ test_files:
105
+ - spec/lib/matrix_spec.rb
106
+ - spec/lib/plane_spec.rb
107
+ - spec/lib/quaternion_spec.rb
108
+ - spec/lib/vector_spec.rb
109
+ - spec/spec_helper.rb