geo3d 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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