snow-math 1.2.4 → 1.3.0pre0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/snow-math/ptr.rb CHANGED
@@ -7,18 +7,74 @@ require 'fiddle'
7
7
 
8
8
  module Snow
9
9
 
10
- [:Vec3, :Vec4, :Quat, :Mat3, :Mat4,
11
- :Vec3Array, :Vec4Array, :QuatArray, :Mat3Array, :Mat4Array].each {
12
- |klass_sym|
13
- if const_defined?(klass_sym)
14
- const_get(klass_sym).class_exec {
10
+ #
11
+ # A module to provide to_ptr methods for all Snow math types. This is optional
12
+ # and only used if you require `snow-math/ptr` because it depends on Fiddle,
13
+ # so if you don't want Fiddle pulled into your environment, you don't want to
14
+ # require the file for this.
15
+ #
16
+ module FiddlePointerSupport
17
+ #
18
+ # Returns a Fiddle::Pointer to the memory address of the object that extends
19
+ # through the size of the object.
20
+ #
21
+ # This function is only provided if you've required `snow-math/ptr`.
22
+ #
23
+ # call-seq: to_ptr -> Fiddle::Pointer
24
+ #
25
+ def to_ptr
26
+ Fiddle::Pointer.new(self.address, self.size)
27
+ end
28
+ end
29
+
30
+ class Vec3
31
+ include ::Snow::FiddlePointerSupport
32
+ end
33
+
34
+ class Vec4
35
+ include ::Snow::FiddlePointerSupport
36
+ end
37
+
38
+ class Quat
39
+ include ::Snow::FiddlePointerSupport
40
+ end
15
41
 
16
- def to_ptr
17
- Fiddle::Pointer.new(self.address, self.size)
18
- end
42
+ class Mat3
43
+ include ::Snow::FiddlePointerSupport
44
+ end
45
+
46
+ class Mat4
47
+ include ::Snow::FiddlePointerSupport
48
+ end
49
+
50
+ if const_defined?(:Vec3Array)
51
+ class Vec3Array
52
+ include ::Snow::FiddlePointerSupport
53
+ end
54
+ end
55
+
56
+ if const_defined?(:Vec4Array)
57
+ class Vec4Array
58
+ include ::Snow::FiddlePointerSupport
59
+ end
60
+ end
61
+
62
+ if const_defined?(:QuatArray)
63
+ class QuatArray
64
+ include ::Snow::FiddlePointerSupport
65
+ end
66
+ end
67
+
68
+ if const_defined?(:Mat3Array)
69
+ class Mat3Array
70
+ include ::Snow::FiddlePointerSupport
71
+ end
72
+ end
19
73
 
20
- }
74
+ if const_defined?(:Mat4Array)
75
+ class Mat4Array
76
+ include ::Snow::FiddlePointerSupport
21
77
  end
22
- }
78
+ end
23
79
 
24
80
  end
@@ -7,6 +7,11 @@ require 'snow-math/bindings'
7
7
  module Snow ; end
8
8
 
9
9
  if Snow.const_defined?(:QuatArray)
10
+ #
11
+ # A contiguous array of Quats. Allocated as a single block of memory so that
12
+ # it can easily be passed back to C libraries (like OpenGL) and to aid with
13
+ # cache locality.
14
+ #
10
15
  class Snow::QuatArray
11
16
  class << self ; alias_method :[], :new ; end
12
17
 
@@ -15,65 +20,116 @@ if Snow.const_defined?(:QuatArray)
15
20
  end
16
21
  end
17
22
 
23
+ #
24
+ # A simple quaternion class for representation rotations.
25
+ #
18
26
  class Snow::Quat
19
27
 
20
28
  class << self ; alias_method :[], :new ; end
21
29
 
22
30
  alias_method :[], :fetch
23
31
  alias_method :[]=, :store
32
+ alias_method :dup, :copy
33
+ alias_method :clone, :copy
24
34
 
35
+
36
+ # Returns the X component of the quaternion.
37
+ #
38
+ # call-seq: x -> float
25
39
  def x
26
40
  self[0]
27
41
  end
28
42
 
43
+ # Sets the X component of the quaternion.
44
+ #
45
+ # call-seq: x = value -> value
29
46
  def x=(value)
30
47
  self[0] = value
31
48
  end
32
49
 
50
+ # Returns the Y component of the quaternion.
51
+ #
52
+ # call-seq: y -> float
33
53
  def y
34
54
  self[1]
35
55
  end
36
56
 
57
+ # Sets the Y component of the quaternion.
58
+ #
59
+ # call-seq: y = value -> value
37
60
  def y=(value)
38
61
  self[1] = value
39
62
  end
40
63
 
64
+ # Returns the Z component of the quaternion.
65
+ #
66
+ # call-seq: z -> float
41
67
  def z
42
68
  self[2]
43
69
  end
44
70
 
71
+ # Sets the Z component of the quaternion.
72
+ #
73
+ # call-seq: z = value -> value
45
74
  def z=(value)
46
75
  self[2] = value
47
76
  end
48
77
 
78
+ # Returns the W component of the quaternion.
79
+ #
80
+ # call-seq: w -> float
49
81
  def w
50
82
  self[3]
51
83
  end
52
84
 
85
+ # Sets the W component of the quaternion.
86
+ #
87
+ # call-seq: w = value -> value
53
88
  def w=(value)
54
89
  self[3] = value
55
90
  end
56
91
 
57
- def dup
58
- self.class.new(self)
59
- end
60
-
92
+ # Calls #normalize(self)
93
+ #
94
+ # call-seq: normalize! -> self
61
95
  def normalize!
62
96
  normalize self
63
97
  end
64
98
 
99
+ # Calls #inverse(self)
100
+ #
101
+ # call-seq: inverse! -> self
65
102
  def inverse!
66
103
  inverse self
67
104
  end
68
105
 
106
+ # Calls #negate(self)
107
+ #
108
+ # call-seq: negate! -> self
69
109
  def negate!
70
110
  negate self
71
111
  end
72
112
 
113
+ # Calls #multiply_quat(rhs, self)
114
+ #
115
+ # call-seq: multiply_quat!(rhs) -> self
73
116
  def multiply_quat!(rhs)
74
117
  multiply_quat rhs, self
75
118
  end
76
119
 
120
+ # Calls #multiply_vec3(rhs, rhs)
121
+ #
122
+ # call-seq: multiply_vec3!(rhs) -> rhs
123
+ def multiply_vec3!(rhs)
124
+ multiply_vec3 rhs, rhs
125
+ end
126
+
127
+ # Wrapper around #multiply_quat, #multiply_vec3, and #scale respectively.
128
+ #
129
+ # call-seq:
130
+ # multiply(quat, output = nil) -> output or new quat
131
+ # multiply(scalar, output = nil) -> output or new quat
132
+ # multiply(vec3, output = nil) -> output or new vec3
77
133
  def multiply(rhs, output = nil)
78
134
  case rhs
79
135
  when ::Snow::Quat then multiply_quat(rhs, output)
@@ -83,26 +139,56 @@ class Snow::Quat
83
139
  end
84
140
  end
85
141
 
142
+ # Calls #multiply(rhs, self) for scaling and Quat multiplication, otherwise
143
+ # #multiply(rhs, rhs) for Vec3 multiplication.
144
+ #
145
+ # call-seq:
146
+ # multiply!(quat) -> self
147
+ # multiply!(scalar) -> self
148
+ # multiply!(vec3) -> vec3
86
149
  def multiply!(rhs)
87
- multiply_quat! rhs
150
+ case rhs
151
+ when ::Snow::Vec3 then multiply(rhs, rhs)
152
+ else multiply(rhs, self)
153
+ end
88
154
  end
89
155
 
156
+ # Calls #add(rhs, self)
157
+ #
158
+ # call-seq: add!(rhs) -> self
90
159
  def add!(rhs)
91
160
  add rhs, self
92
161
  end
93
162
 
163
+ # Calls #subtract(rhs, self)
164
+ #
165
+ # call-seq: subtract!(rhs) -> self
94
166
  def subtract!(rhs)
95
167
  subtract rhs, self
96
168
  end
97
169
 
170
+ # Calls #scale(rhs, self)
171
+ #
172
+ # call-seq: scale!(rhs) -> self
98
173
  def scale!(rhs)
99
174
  scale rhs, self
100
175
  end
101
176
 
177
+ # Calls #divide(rhs, self)
178
+ #
179
+ # call-seq: divide!(rhs) -> self
102
180
  def divide!(rhs)
103
181
  divide rhs, self
104
182
  end
105
183
 
184
+ # Calls #slerp(destination, alpha, self)
185
+ #
186
+ # call-seq: slerp!(destination, alpha) -> self
187
+ def slerp!(destination, alpha)
188
+ slerp(destination, alpha, self)
189
+ end
190
+
191
+
106
192
  alias_method :-, :subtract
107
193
  alias_method :+, :add
108
194
  alias_method :**, :dot_product
@@ -6,57 +6,94 @@ require 'snow-math'
6
6
 
7
7
  module Snow ; end
8
8
 
9
+ module Snow
10
+
11
+ #
12
+ # Provides swizzle function support for Vec3, Vec4, and Quat. At runtime, if
13
+ # a message matching `/^[xyzw]{3,4}$/`, sans `w` in Vec3's case, is sent to
14
+ # any of those types, a new method will be generated to return a swizzled
15
+ # object of that type or one that contains at least as many elements as
16
+ # requested.
17
+ #
18
+ # For Vec3, valid swizzle components are `x`, `y`, and `z`. If a swizzle
19
+ # function with three of those components is called, a new Vec3 is returned.
20
+ # If a swizzle function with four components is called, a Vec4 is returned.
21
+ # This is the same for Vec4, although it also provides a `w` component.
22
+ #
23
+ # For Quat types, behavior is similar to Vec4, though a 4-component swizzle
24
+ # function returns a Quat instead of a Vec4.
25
+ #
26
+ # # Good
27
+ # Vec3[1, 2, 3].xxx # => Vec3[1, 1, 1]
28
+ # Vec3[1, 2, 3].xxzz # => Vec4[1, 1, 3, 3]
29
+ # Vec4[1, 2, 3, 4].xzwy # => Vec4[1, 3, 4, 2]
30
+ # Quat[1, 2, 3, 4].wzyx # => Quat[4, 3, 2, 1]
31
+ #
32
+ # # Bad
33
+ # Vec3[1, 2, 3].www # => Invalid, no W component for Vec3
34
+ # Vec3[1, 2, 3].xx # => Invalid, no 2-component vector type
35
+ # Vec3[1, 2, 3].xxxxx # => Invalid, no 5-component vector type
36
+ #
37
+ module SwizzleSupport
38
+
39
+ alias_method :__under_method_missing__, :method_missing
40
+
41
+ #
42
+ # Generates a swizzle function according to a class's @@SWIZZLE_CHARS and
43
+ # @@SWIZZLE_MAPPING class variables.
44
+ #
45
+ # Overrides old method_missing implementation. The old implementation is
46
+ # aliased as \_\_under_method_missing__ and called when no swizzle function
47
+ # can be generated for a symbol.
48
+ #
49
+ def method_missing(sym, *args)
50
+ chars = sym.to_s
51
+ if chars =~ self.class.class_variable_get(:@@SWIZZLE_CHARS)
52
+ mapping = self.class.class_variable_get(:@@SWIZZLE_MAPPING)
53
+ arg_indices = chars.each_char.map {
54
+ |char|
55
+ index = mapping[char]
56
+ if index.nil?
57
+ raise ArgumentError, "No index mapping for swizzle character #{char} found"
58
+ end
59
+ index
60
+ }
61
+ swizzle_klass = mapping[chars.length]
62
+
63
+ if swizzle_klass.nil?
64
+ raise ArgumentError, "No swizzle class defined for #{chars.length} components"
65
+ end
66
+
67
+ self.class.class_exec(arg_indices, swizzle_klass) {
68
+ |indices, klass|
69
+ define_method(sym) {
70
+ klass.new(indices.map { |index| self.fetch(index) })
71
+ }
72
+ }
73
+ return self.send(sym)
74
+ end
75
+
76
+ __under_method_missing__ sym, *args
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
9
83
  class Snow::Vec3
10
84
  @@SWIZZLE_CHARS = /^[xyz]{3,4}$/
11
85
  @@SWIZZLE_MAPPING = { 3 => self, 4 => ::Snow::Vec4, 'x' => 0, 'y' => 1, 'z' => 2 }
86
+ include ::Snow::SwizzleSupport
12
87
  end
13
88
 
14
89
  class Snow::Vec4
15
90
  @@SWIZZLE_CHARS = /^[xyzw]{3,4}$/
16
91
  @@SWIZZLE_MAPPING = { 3 => ::Snow::Vec3, 4 => self, 'x' => 0, 'y' => 1, 'z' => 2, 'w' => 3 }
92
+ include ::Snow::SwizzleSupport
17
93
  end
18
94
 
19
95
  class Snow::Quat
20
96
  @@SWIZZLE_CHARS = /^[xyzw]{3,4}$/
21
97
  @@SWIZZLE_MAPPING = { 3 => ::Snow::Vec3, 4 => self, 'x' => 0, 'y' => 1, 'z' => 2, 'w' => 3 }
22
- end
23
-
24
- module Snow
25
-
26
- [:Vec3, :Vec4, :Quat].each {
27
- |klass_sym|
28
-
29
- const_get(klass_sym).class_exec {
30
-
31
- def method_missing(sym, *args)
32
- chars = sym.to_s
33
- if chars =~ self.class.class_variable_get(:@@SWIZZLE_CHARS)
34
- mapping = self.class.class_variable_get(:@@SWIZZLE_MAPPING)
35
- arg_indices = chars.each_char.map {
36
- |char|
37
- index = mapping[char]
38
- if index.nil?
39
- raise ArgumentError, "No index mapping for swizzle character #{char} found"
40
- end
41
- index
42
- }
43
- swizzle_klass = mapping[chars.length]
44
-
45
- if swizzle_klass.nil?
46
- raise ArgumentError, "No swizzle class defined for #{chars.length} components"
47
- end
48
-
49
- self.class.class_exec(arg_indices, swizzle_klass) {
50
- |indices, klass|
51
- define_method(sym) { klass.new(indices.map { |index| self.fetch(index) }) }
52
- }
53
- return self.send(sym)
54
- end
55
-
56
- super sym, *args
57
- end
58
-
59
- }
60
- }
61
-
98
+ include ::Snow::SwizzleSupport
62
99
  end
@@ -6,43 +6,186 @@ require 'snow-math'
6
6
 
7
7
  module Snow
8
8
 
9
- [:Vec3, :Vec4, :Quat, :Mat3, :Mat4,
10
- :Vec3Array, :Vec4Array, :QuatArray, :Mat3Array, :Mat4Array].each {
11
- |klass_sym|
12
- if const_defined?(klass_sym)
13
- const_get(klass_sym).class_exec {
14
-
15
- include ::Enumerable
16
-
17
- def to_a
18
- (0 ... self.length).each.map { |index| fetch(index) }
19
- end
20
-
21
- def each(&block)
22
- return to_enum(:each) unless block_given?
23
- (0 ... self.length).each {
24
- |index|
25
- yield(fetch(index))
26
- }
27
- self
28
- end
29
-
30
- def map!(&block)
31
- return to_enum(:map!) unless block_given?
32
- (0 ... self.length).each {
33
- |index|
34
- store(index, yield(fetch(index)))
35
- }
36
- self
37
- end
38
-
39
- def map(&block)
40
- return to_enum(:map) unless block_given?
41
- self.dup.map!(&block)
42
- end
9
+ #
10
+ # Provides basic support for converting Snow math objects to Ruby arrays. In
11
+ # addition, it also provides rudimentary support for each, map, and map! for
12
+ # all math types.
13
+ #
14
+ # For example:
15
+ #
16
+ # # Arrays of cells by column
17
+ # Mat4[*(1 .. 16)].group_by {
18
+ # |cell|
19
+ # (cell.floor - 1) % 4
20
+ # }
21
+ #
22
+ # # Arrays of cells by row
23
+ # Mat4[*(1 .. 16)].group_by {
24
+ # |cell|
25
+ # ((cell - 1) / 4).floor
26
+ # } # => { 0 => [1, 2, 3, 4],
27
+ # # 1 => [5, 6, 7, 8],
28
+ # # 2 => [9, 10, 11, 12],
29
+ # # 3 => [13, 14, 15, 16] }
30
+ #
31
+ # Note that these examples are only showing that you can use these types like
32
+ # most others that include the Enumerable module. The above examples are not
33
+ # sane ways to get columns or rows out of a Mat4.
34
+ #
35
+ module ArraySupport
43
36
 
37
+ include ::Enumerable
38
+
39
+ #
40
+ # Returns an array composed of the elements of self.
41
+ #
42
+ # call-seq: to_a -> new_ary
43
+ #
44
+ def to_a
45
+ (0 ... self.length).each.map { |index| fetch(index) }
46
+ end
47
+
48
+ #
49
+ # Iterates over all elements of the object and yields them to a block.
50
+ # In the second form, returns an Enumerator.
51
+ #
52
+ # call-seq:
53
+ # each { |elem| block } -> self
54
+ # each -> Enumerator
55
+ #
56
+ def each(&block)
57
+ return to_enum(:each) unless block_given?
58
+ (0 ... self.length).each {
59
+ |index|
60
+ yield(fetch(index))
44
61
  }
62
+ self
63
+ end
64
+
65
+ #
66
+ # In the first form, iterates over all elements of the object, yields them
67
+ # to the block given, and overwrites the element's value with the value
68
+ # returned by the block.
69
+ #
70
+ # In the second form, returns an Enumerator.
71
+ #
72
+ # The return value of the block must be the same kind of object as was
73
+ # yielded to the block. So, if yielded a Vec3, the block must return a Vec3.
74
+ # If yielded a Numeric, it must return a Numeric.
75
+ #
76
+ # call-seq:
77
+ # map! { |elem| block } -> self
78
+ # map! -> Enumerator
79
+ #
80
+ def map!(&block)
81
+ return to_enum(:map!) unless block_given?
82
+ (0 ... self.length).each {
83
+ |index|
84
+ store(index, yield(fetch(index)))
85
+ }
86
+ self
87
+ end
88
+
89
+ #
90
+ # In the first form, duplicates self and then calls map! on the duplicated
91
+ # object, passing the block to map!.
92
+ #
93
+ # In the second form, returns an Enumerator.
94
+ #
95
+ # The return value of the block must be the same kind of object as was
96
+ # yielded to the block. So, if yielded a Vec3, the block must return a Vec3.
97
+ # If yielded a Numeric, it must return a Numeric.
98
+ #
99
+ # call-seq:
100
+ # map { |elem| block } -> new object
101
+ # map -> Enumerator
102
+ #
103
+ def map(&block)
104
+ return to_enum(:map) unless block_given?
105
+ self.dup.map!(&block)
106
+ end
107
+
108
+ end
109
+
110
+ class Vec3 ; include ::Snow::ArraySupport ; end
111
+ class Vec4 ; include ::Snow::ArraySupport ; end
112
+ class Quat ; include ::Snow::ArraySupport ; end
113
+ class Mat3 ; include ::Snow::ArraySupport ; end
114
+ class Mat4 ; include ::Snow::ArraySupport ; end
115
+
116
+ if const_defined?(:Vec3Array)
117
+ class Vec3Array
118
+ include ::Snow::ArraySupport
119
+
120
+ #
121
+ # Duplicates the Vec3Array and returns it.
122
+ #
123
+ # call-seq: dup -> new vec3_array
124
+ #
125
+ def dup
126
+ self.class.new(self)
127
+ end
128
+ end
129
+ end
130
+
131
+ if const_defined?(:Vec4Array)
132
+ class Vec4Array
133
+ include ::Snow::ArraySupport
134
+
135
+ #
136
+ # Duplicates the Vec4Array and returns it.
137
+ #
138
+ # call-seq: dup -> new vec4_array
139
+ #
140
+ def dup
141
+ self.class.new(self)
142
+ end
143
+ end
144
+ end
145
+
146
+ if const_defined?(:QuatArray)
147
+ class QuatArray
148
+ include ::Snow::ArraySupport
149
+
150
+ #
151
+ # Duplicates the QuatArray and returns it.
152
+ #
153
+ # call-seq: dup -> new quat_array
154
+ #
155
+ def dup
156
+ self.class.new(self)
157
+ end
158
+ end
159
+ end
160
+
161
+ if const_defined?(:Mat3Array)
162
+ class Mat3Array
163
+ include ::Snow::ArraySupport
164
+
165
+ #
166
+ # Duplicates the Mat3Array and returns it.
167
+ #
168
+ # call-seq: dup -> new mat3_array
169
+ #
170
+ def dup
171
+ self.class.new(self)
172
+ end
173
+ end
174
+ end
175
+
176
+ if const_defined?(:Mat4Array)
177
+ class Mat4Array
178
+ include ::Snow::ArraySupport
179
+
180
+ #
181
+ # Duplicates the Mat4Array and returns it.
182
+ #
183
+ # call-seq: dup -> new mat4_array
184
+ #
185
+ def dup
186
+ self.class.new(self)
187
+ end
45
188
  end
46
- }
189
+ end
47
190
 
48
191
  end