gosling 2.3.0 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/gosling/rect.rb CHANGED
@@ -1,41 +1,41 @@
1
- require_relative 'polygon.rb'
2
-
3
- module Gosling
4
- ##
5
- # A Rect is a Polygon with exactly four vertices, defined by a width and height, with sides at right angles to one
6
- # another. The width and height can be modified at runtime; all vertices will be updated automatically.
7
- #
8
- class Rect < Polygon
9
- attr_reader :width, :height
10
-
11
- ##
12
- # Creates a new Rect with a width and height of 1.
13
- #
14
- def initialize(window)
15
- super(window)
16
- @width = 1
17
- @height = 1
18
- rebuild_vertices
19
- end
20
-
21
- def width=(val)
22
- raise ArgumentError.new("set_width() expects a positive, non-zero number") if val <= 0
23
- @width = val
24
- rebuild_vertices
25
- end
26
-
27
- def height=(val)
28
- raise ArgumentError.new("set_height() expects a positive, non-zero number") if val <= 0
29
- @height = val
30
- rebuild_vertices
31
- end
32
-
33
- private
34
-
35
- def rebuild_vertices
36
- set_vertices_rect(@width, @height)
37
- end
38
-
39
- private :set_vertices
40
- end
41
- end
1
+ require_relative 'polygon.rb'
2
+
3
+ module Gosling
4
+ ##
5
+ # A Rect is a Polygon with exactly four vertices, defined by a width and height, with sides at right angles to one
6
+ # another. The width and height can be modified at runtime; all vertices will be updated automatically.
7
+ #
8
+ class Rect < Polygon
9
+ attr_reader :width, :height
10
+
11
+ ##
12
+ # Creates a new Rect with a width and height of 1.
13
+ #
14
+ def initialize(window)
15
+ super(window)
16
+ @width = 1
17
+ @height = 1
18
+ rebuild_vertices
19
+ end
20
+
21
+ def width=(val)
22
+ raise ArgumentError.new("set_width() expects a positive, non-zero number") if val <= 0
23
+ @width = val
24
+ rebuild_vertices
25
+ end
26
+
27
+ def height=(val)
28
+ raise ArgumentError.new("set_height() expects a positive, non-zero number") if val <= 0
29
+ @height = val
30
+ rebuild_vertices
31
+ end
32
+
33
+ private
34
+
35
+ def rebuild_vertices
36
+ set_vertices_rect(@width, @height)
37
+ end
38
+
39
+ private :set_vertices
40
+ end
41
+ end
@@ -1,50 +1,50 @@
1
- require_relative 'rect.rb'
2
-
3
- module Gosling
4
- ##
5
- # This type of Actor accepts a Gosu::Image to be rendered in place of the standard flat-colored shape. It behaves very
6
- # much like a Rect, except its width and height are automatically set to the width and height of the image given to it
7
- # and cannot be modified otherwise. The image can be changed at runtime. Changing this actor's color or alpha
8
- # applies tinting and transparency to the image rendered.
9
- #
10
- class Sprite < Rect
11
- def initialize(window)
12
- super(window)
13
- @image = nil
14
- @color = Gosu::Color.rgba(255, 255, 255, 255)
15
- end
16
-
17
- ##
18
- # Returns the image currently assigned to this Sprite.
19
- #
20
- def get_image
21
- @image
22
- end
23
-
24
- ##
25
- # Assigns the image to this Sprite, setting its width and height to match the image's.
26
- #
27
- def set_image(image)
28
- raise ArgumentError.new("Expected Image, but received #{image.inspect}!") unless image.is_a?(Gosu::Image)
29
- @image = image
30
- self.width = @image.width
31
- self.height = @image.height
32
- end
33
-
34
- private
35
-
36
- def render(matrix)
37
- # TODO: write transformed vertices to a reserved list of vertices retained in memory each time
38
- global_vertices = @vertices.map { |v| Transformable.transform_point(matrix, v, Snow::Vec3.new) }
39
- @image.draw_as_quad(
40
- global_vertices[0][0].to_f, global_vertices[0][1].to_f, @color,
41
- global_vertices[1][0].to_f, global_vertices[1][1].to_f, @color,
42
- global_vertices[2][0].to_f, global_vertices[2][1].to_f, @color,
43
- global_vertices[3][0].to_f, global_vertices[3][1].to_f, @color,
44
- 0
45
- )
46
- end
47
-
48
- private :'width=', :'height='
49
- end
50
- end
1
+ require_relative 'rect.rb'
2
+
3
+ module Gosling
4
+ ##
5
+ # This type of Actor accepts a Gosu::Image to be rendered in place of the standard flat-colored shape. It behaves very
6
+ # much like a Rect, except its width and height are automatically set to the width and height of the image given to it
7
+ # and cannot be modified otherwise. The image can be changed at runtime. Changing this actor's color or alpha
8
+ # applies tinting and transparency to the image rendered.
9
+ #
10
+ class Sprite < Rect
11
+ def initialize(window)
12
+ super(window)
13
+ @image = nil
14
+ @color = Gosu::Color.rgba(255, 255, 255, 255)
15
+ end
16
+
17
+ ##
18
+ # Returns the image currently assigned to this Sprite.
19
+ #
20
+ def get_image
21
+ @image
22
+ end
23
+
24
+ ##
25
+ # Assigns the image to this Sprite, setting its width and height to match the image's.
26
+ #
27
+ def set_image(image)
28
+ raise ArgumentError.new("Expected Image, but received #{image.inspect}!") unless image.is_a?(Gosu::Image)
29
+ @image = image
30
+ self.width = @image.width
31
+ self.height = @image.height
32
+ end
33
+
34
+ private
35
+
36
+ def render(matrix)
37
+ # TODO: write transformed vertices to a reserved list of vertices retained in memory each time
38
+ global_vertices = @vertices.map { |v| Transformable.transform_point(matrix, v, Snow::Vec3.new) }
39
+ @image.draw_as_quad(
40
+ global_vertices[0][0].to_f, global_vertices[0][1].to_f, @color,
41
+ global_vertices[1][0].to_f, global_vertices[1][1].to_f, @color,
42
+ global_vertices[2][0].to_f, global_vertices[2][1].to_f, @color,
43
+ global_vertices[3][0].to_f, global_vertices[3][1].to_f, @color,
44
+ 0
45
+ )
46
+ end
47
+
48
+ private :'width=', :'height='
49
+ end
50
+ end
@@ -1,413 +1,413 @@
1
- require 'snow-math'
2
-
3
- require_relative 'patches.rb'
4
- require_relative 'utils.rb'
5
-
6
- module Gosling
7
- ##
8
- # A helper class for performing vector transforms in 2D space. Relies heavily on the Vec3 and Mat3 classes of the
9
- # SnowMath gem to remain performant.
10
- #
11
- module Transformable
12
- attr_reader :rotation
13
-
14
- ##
15
- # Initializes this Transformable to have no transformations (identity matrix).
16
- #
17
- def initialize
18
- @center = Snow::Vec3[0, 0, 1]
19
- @scale = Snow::Vec2[1, 1]
20
- @translation = Snow::Vec3[0, 0, 1]
21
- reset
22
- end
23
-
24
- ##
25
- # Resets center and translation to [0, 0], scale to [1, 1], and rotation to 0, restoring this transformation to the identity
26
- # matrix.
27
- #
28
- def reset
29
- self.center = 0, 0
30
- self.scale = 1, 1
31
- self.rotation = 0
32
- self.translation = 0, 0
33
- end
34
-
35
- ##
36
- # Returns a duplicate of the center Vec3 (@center is read-only).
37
- #
38
- def center
39
- @center.dup.freeze
40
- end
41
-
42
- ##
43
- # Returns the x component of the centerpoint of this Transformable. See Transformable#center.
44
- #
45
- def center_x
46
- @center[0]
47
- end
48
-
49
- ##
50
- # Returns the y component of the centerpoint of this Transformable. See Transformable#center.
51
- #
52
- def center_y
53
- @center[1]
54
- end
55
-
56
- ##
57
- # Returns a duplicate of the scale Vec2 (@scale is read-only).
58
- #
59
- def scale
60
- @scale.dup.freeze
61
- end
62
-
63
- ##
64
- # Returns the x component of the scaling of this Transformable. See Transformable#scale.
65
- #
66
- def scale_x
67
- @scale[0]
68
- end
69
-
70
- ##
71
- # Returns the y component of the scaling of this Transformable. See Transformable#scale.
72
- #
73
- def scale_y
74
- @scale[1]
75
- end
76
-
77
- ##
78
- # Returns a duplicate of the translation Vec3 (@translation is read-only).
79
- #
80
- def translation
81
- @translation.dup.freeze
82
- end
83
- alias :pos :translation
84
-
85
- ##
86
- # Returns this Transformable's x position in relative space. See Transformable#translation.
87
- #
88
- def x
89
- @translation[0]
90
- end
91
-
92
- ##
93
- # Returns this Transformable's y position in relative space. See Transformable#translation.
94
- #
95
- def y
96
- @translation[1]
97
- end
98
-
99
- ##
100
- # Sets this transform's centerpoint. All other transforms are performed relative to this central point.
101
- #
102
- # The default centerpoint is [0, 0], which is the same as no transform. For a square defined by the vertices
103
- # [[0, 0], [10, 0], [10, 10], [0, 10]], this would translate to that square's upper left corner. In this case, when scaled
104
- # larger or smaller, only the square's right and bottom edges would expand or contract, and when rotated it
105
- # would spin around its upper left corner. For most applications, this is probably not what we want.
106
- #
107
- # By setting the centerpoint to something other than the origin, we can change the scaling and rotation to
108
- # something that makes more sense. For the square defined above, we could set center to be the actual center of
109
- # the square: [5, 5]. By doing so, scaling the square would cause it to expand evenly on all sides, and rotating it
110
- # would cause it to spin about its center like a four-cornered wheel.
111
- #
112
- # You can set the centerpoint to be any point in local space, inside or even outside of the shape in question.
113
- #
114
- # If passed more than two numeric arguments, only the first two are used.
115
- #
116
- # Usage:
117
- # - transform.center = x, y
118
- # - transform.center = [x, y]
119
- # - transform.center = Snow::Vec2[x, y]
120
- # - transform.center = Snow::Vec3[x, y, z]
121
- # - transform.center = Snow::Vec4[x, y, z, c]
122
- # - transform.set_center { |c| c.add(-sprite.pos, c) }
123
- #
124
- def center=(args = nil)
125
- if block_given?
126
- yield(@center)
127
- else
128
- case args[0]
129
- when Array
130
- self.center = args[0][0], args[0][1]
131
- when Snow::Vec2, Snow::Vec3, Snow::Vec4
132
- @center[0] = args[0][0]
133
- @center[1] = args[0][1]
134
- when Numeric
135
- raise ArgumentError.new("Cannot set center from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
136
- args.each { |arg| type_check(arg, Numeric) }
137
- @center[0] = args[0]
138
- @center[1] = args[1]
139
- else
140
- raise ArgumentError.new("Cannot set center from #{args.inspect}: bad type!")
141
- end
142
- end
143
- @center_is_dirty = @is_dirty = true
144
- end
145
- alias :set_center :center=
146
-
147
- ##
148
- # Sets the x component of the centerpoint of this Transformable. See Transformable#center.
149
- #
150
- def center_x=(val)
151
- type_check(val, Numeric)
152
- @center[0] = val
153
- @center_is_dirty = @is_dirty = true
154
- end
155
-
156
- ##
157
- # Sets the y component of the centerpoint of this Transformable. See Transformable#center.
158
- #
159
- def center_y=(val)
160
- type_check(val, Numeric)
161
- @center[1] = val
162
- @center_is_dirty = @is_dirty = true
163
- end
164
-
165
- ##
166
- # Sets this transform's x/y scaling. A scale value of [1, 1] results in no scaling. Larger values make a shape bigger,
167
- # while smaller values will make it smaller. Great for shrinking/growing animations, or to zoom the camera in/out.
168
- #
169
- # If passed more than two numeric arguments, only the first two are used.
170
- #
171
- # Usage:
172
- # - transform.scale = scalar
173
- # - transform.scale = x, y
174
- # - transform.scale = [x, y]
175
- # - transform.scale = Snow::Vec2[x, y]
176
- # - transform.scale = Snow::Vec3[x, y, z]
177
- # - transform.scale = Snow::Vec4[x, y, z, c]
178
- # - transform.set_scale { |s| s.multiply(0.5, s) }
179
- #
180
- def scale=(*args)
181
- if block_given?
182
- yield(@scale)
183
- else
184
- if args.length >= 2
185
- case args[0]
186
- when Numeric
187
- args.each { |arg| type_check(arg, Numeric) }
188
- @scale[0] = args[0]
189
- @scale[1] = args[1]
190
- else
191
- raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
192
- end
193
- elsif args.length == 1
194
- case args[0]
195
- when Array
196
- self.set_scale(*(args[0]))
197
- when Snow::Vec2
198
- self.set_scale(args[0][0], args[0][1])
199
- when Numeric
200
- self.set_scale(args[0], args[0])
201
- else
202
- raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
203
- end
204
- else
205
- raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
206
- end
207
- end
208
- @scale_is_dirty = @is_dirty = true
209
- end
210
- alias :set_scale :scale=
211
-
212
- ##
213
- # Wrapper method. Sets the x component of the scaling of this Actor. See Transformable#scale.
214
- #
215
- def scale_x=(val)
216
- type_check(val, Numeric)
217
- @scale[0] = val
218
- @scale_is_dirty = @is_dirty = true
219
- end
220
-
221
- ##
222
- # Wrapper method. Sets the y component of the scaling of this Actor. See Transformable#scale.
223
- #
224
- def scale_y=(val)
225
- type_check(val, Numeric)
226
- @scale[1] = val
227
- @scale_is_dirty = @is_dirty = true
228
- end
229
-
230
- ##
231
- # Sets this transform's rotation in radians. A value of 0 results in no rotation. Great for spinning animations, or
232
- # rotating the player's camera view.
233
- #
234
- # Usage:
235
- # - transform.rotation = radians
236
- #
237
- def rotation=(radians)
238
- type_check(radians, Numeric)
239
- if radians.is_a?(Float)
240
- raise ArgumentError.new("Expected a finite number, but received #{radians.inspect}!") unless radians.finite?
241
- end
242
- @rotation = radians
243
- @rotate_is_dirty = @is_dirty = true
244
- end
245
- alias :set_rotation :rotation=
246
-
247
- ##
248
- # Sets this transform's x/y translation in radians. A value of [0, 0] results in no translation. Great for moving
249
- # actors across the screen or scrolling the camera.
250
- #
251
- # If passed more than two numeric arguments, only the first two are used.
252
- #
253
- # Optionally, this method can be passed a block and no arguments. A reference to this Transformable's translation
254
- # will be passed to the block as the first parameter, allowing direct manipulation using all of snow-math's Vec3
255
- # methods. This is particularly useful when optimizing methods that MUST be as fast as possible, such as animation
256
- # and game physics, since the result of your mathematics can be written directly to this Transformable's translation
257
- # without having to instantiate an interim Vector during every physics step.
258
- #
259
- # Usage:
260
- # - transform.translation = x, y
261
- # - transform.translation = [x, y]
262
- # - transform.translation = Snow::Vec2[x, y]
263
- # - transform.translation = Snow::Vec3[x, y, z]
264
- # - transform.translation = Snow::Vec4[x, y, z, c]
265
- # - transform.set_translation { |t| t.add(velocity * elapsed, t) }
266
- #
267
- def translation=(args = nil)
268
- if block_given?
269
- yield(@translation)
270
- else
271
- case args[0]
272
- when Array
273
- self.translation = args[0][0], args[0][1]
274
- when Snow::Vec2, Snow::Vec3, Snow::Vec4
275
- @translation[0] = args[0][0]
276
- @translation[1] = args[0][1]
277
- when Numeric
278
- raise ArgumentError.new("Cannot set translation from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
279
- args.each { |arg| type_check(arg, Numeric) }
280
- @translation[0] = args[0]
281
- @translation[1] = args[1]
282
- else
283
- raise ArgumentError.new("Cannot set translation from #{args.inspect}: bad type!")
284
- end
285
- end
286
- @translate_is_dirty = @is_dirty = true
287
- end
288
- alias :set_translation :translation=
289
- alias :pos= :translation=
290
-
291
- ##
292
- # Sets this Transformable's x position in relative space. See Transformable#translation.
293
- #
294
- def x=(val)
295
- type_check(val, Numeric)
296
- @translation[0] = val
297
- @translate_is_dirty = @is_dirty = true
298
- end
299
-
300
- ##
301
- # Sets this Transformable's y position in relative space. See Transformable#translation.
302
- #
303
- def y=(val)
304
- type_check(val, Numeric)
305
- @translation[1] = val
306
- @translate_is_dirty = @is_dirty = true
307
- end
308
-
309
- ##
310
- # Returns a Snow::Mat3 which combines our current center, scale, rotation, and translation into a single transform
311
- # matrix. When a point in space is multiplied by this transform, the centering, scaling, rotation, and translation
312
- # will all be applied to that point.
313
- #
314
- # This Snow::Mat3 is cached and will only be recalculated as needed.
315
- #
316
- def to_matrix
317
- return @matrix unless @is_dirty || @matrix.nil?
318
-
319
- update_center_matrix
320
- update_scale_matrix
321
- update_rotate_matrix
322
- update_translate_matrix
323
-
324
- @matrix = Snow::Mat3.new unless @matrix
325
-
326
- @matrix.set(@center_mat)
327
- @matrix.multiply!(@scale_mat)
328
- @matrix.multiply!(@rotate_mat)
329
- @matrix.multiply!(@translate_mat)
330
-
331
- @is_dirty = false
332
- @matrix
333
- end
334
-
335
- ##
336
- # Transforms a Vec3 using the provided Mat3 transform and returns the result as a new Vec3. This is the
337
- # opposite of Transformable.untransform_point.
338
- #
339
- @@transformable_point
340
- def self.transform_point(mat, point, out = nil)
341
- @@transformable_point ||= Snow::Vec3.new
342
- @@transformable_point.set(point[0], point[1], 1)
343
-
344
- out ||= Snow::Vec3.new
345
- mat.multiply(@@transformable_point, out)
346
- out[2] = 0
347
- out
348
- end
349
-
350
- ##
351
- # Applies all of our transformations to the point, returning the resulting point as a new Vec3. This is the opposite
352
- # of Transformable#untransform_point.
353
- #
354
- def transform_point(point, out = nil)
355
- Transformable.transform_point(to_matrix, point, out)
356
- end
357
-
358
- ##
359
- # Transforms a Vec3 using the inverse of the provided Mat3 transform and returns the result as a new Vec3. This
360
- # is the opposite of Transformable.transform_point.
361
- #
362
- def self.untransform_point(mat, point, out = nil)
363
- inverse_mat = MatrixCache.instance.get
364
- raise "Unable to invert matrix: #{mat}!" unless mat.inverse(inverse_mat)
365
- transform_point(inverse_mat, point, out)
366
- ensure
367
- MatrixCache.instance.recycle(inverse_mat) if inverse_mat
368
- end
369
-
370
- ##
371
- # Applies the inverse of all of our transformations to the point, returning the resulting point as a new Vec3. This
372
- # is the opposite of Transformable#transform_point.
373
- #
374
- def untransform_point(point, out = nil)
375
- Transformable.untransform_point(to_matrix, point, out)
376
- end
377
-
378
- private
379
-
380
- def update_center_matrix
381
- return unless @center_is_dirty || @center_mat.nil?
382
- @center_mat ||= Snow::Mat3.new
383
- @center_mat[2] = -@center[0]
384
- @center_mat[5] = -@center[1]
385
- @center_is_dirty = false
386
- end
387
-
388
- def update_scale_matrix
389
- return unless @scale_is_dirty || @scale_mat.nil?
390
- @scale_mat ||= Snow::Mat3.new
391
- @scale_mat[0] = @scale[0]
392
- @scale_mat[4] = @scale[1]
393
- @scale_is_dirty = false
394
- end
395
-
396
- def update_rotate_matrix
397
- return unless @rotate_is_dirty || @rotate_mat.nil?
398
- @rotate_mat ||= Snow::Mat3.new
399
- @rotate_mat[4] = @rotate_mat[0] = Math.cos(@rotation)
400
- @rotate_mat[1] = Math.sin(@rotation)
401
- @rotate_mat[3] = -@rotate_mat[1]
402
- @rotate_is_dirty = false
403
- end
404
-
405
- def update_translate_matrix
406
- return unless @translate_is_dirty || @translate_mat.nil?
407
- @translate_mat ||= Snow::Mat3.new
408
- @translate_mat[2] = @translation[0]
409
- @translate_mat[5] = @translation[1]
410
- @translate_is_dirty = false
411
- end
412
- end
413
- end
1
+ require 'snow-math'
2
+
3
+ require_relative 'patches.rb'
4
+ require_relative 'utils.rb'
5
+
6
+ module Gosling
7
+ ##
8
+ # A helper class for performing vector transforms in 2D space. Relies heavily on the Vec3 and Mat3 classes of the
9
+ # SnowMath gem to remain performant.
10
+ #
11
+ module Transformable
12
+ attr_reader :rotation
13
+
14
+ ##
15
+ # Initializes this Transformable to have no transformations (identity matrix).
16
+ #
17
+ def initialize
18
+ @center = Snow::Vec3[0, 0, 1]
19
+ @scale = Snow::Vec2[1, 1]
20
+ @translation = Snow::Vec3[0, 0, 1]
21
+ reset
22
+ end
23
+
24
+ ##
25
+ # Resets center and translation to [0, 0], scale to [1, 1], and rotation to 0, restoring this transformation to the identity
26
+ # matrix.
27
+ #
28
+ def reset
29
+ self.center = 0, 0
30
+ self.scale = 1, 1
31
+ self.rotation = 0
32
+ self.translation = 0, 0
33
+ end
34
+
35
+ ##
36
+ # Returns a duplicate of the center Vec3 (@center is read-only).
37
+ #
38
+ def center
39
+ @center.dup.freeze
40
+ end
41
+
42
+ ##
43
+ # Returns the x component of the centerpoint of this Transformable. See Transformable#center.
44
+ #
45
+ def center_x
46
+ @center[0]
47
+ end
48
+
49
+ ##
50
+ # Returns the y component of the centerpoint of this Transformable. See Transformable#center.
51
+ #
52
+ def center_y
53
+ @center[1]
54
+ end
55
+
56
+ ##
57
+ # Returns a duplicate of the scale Vec2 (@scale is read-only).
58
+ #
59
+ def scale
60
+ @scale.dup.freeze
61
+ end
62
+
63
+ ##
64
+ # Returns the x component of the scaling of this Transformable. See Transformable#scale.
65
+ #
66
+ def scale_x
67
+ @scale[0]
68
+ end
69
+
70
+ ##
71
+ # Returns the y component of the scaling of this Transformable. See Transformable#scale.
72
+ #
73
+ def scale_y
74
+ @scale[1]
75
+ end
76
+
77
+ ##
78
+ # Returns a duplicate of the translation Vec3 (@translation is read-only).
79
+ #
80
+ def translation
81
+ @translation.dup.freeze
82
+ end
83
+ alias :pos :translation
84
+
85
+ ##
86
+ # Returns this Transformable's x position in relative space. See Transformable#translation.
87
+ #
88
+ def x
89
+ @translation[0]
90
+ end
91
+
92
+ ##
93
+ # Returns this Transformable's y position in relative space. See Transformable#translation.
94
+ #
95
+ def y
96
+ @translation[1]
97
+ end
98
+
99
+ ##
100
+ # Sets this transform's centerpoint. All other transforms are performed relative to this central point.
101
+ #
102
+ # The default centerpoint is [0, 0], which is the same as no transform. For a square defined by the vertices
103
+ # [[0, 0], [10, 0], [10, 10], [0, 10]], this would translate to that square's upper left corner. In this case, when scaled
104
+ # larger or smaller, only the square's right and bottom edges would expand or contract, and when rotated it
105
+ # would spin around its upper left corner. For most applications, this is probably not what we want.
106
+ #
107
+ # By setting the centerpoint to something other than the origin, we can change the scaling and rotation to
108
+ # something that makes more sense. For the square defined above, we could set center to be the actual center of
109
+ # the square: [5, 5]. By doing so, scaling the square would cause it to expand evenly on all sides, and rotating it
110
+ # would cause it to spin about its center like a four-cornered wheel.
111
+ #
112
+ # You can set the centerpoint to be any point in local space, inside or even outside of the shape in question.
113
+ #
114
+ # If passed more than two numeric arguments, only the first two are used.
115
+ #
116
+ # Usage:
117
+ # - transform.center = x, y
118
+ # - transform.center = [x, y]
119
+ # - transform.center = Snow::Vec2[x, y]
120
+ # - transform.center = Snow::Vec3[x, y, z]
121
+ # - transform.center = Snow::Vec4[x, y, z, c]
122
+ # - transform.set_center { |c| c.add(-sprite.pos, c) }
123
+ #
124
+ def center=(args = nil)
125
+ if block_given?
126
+ yield(@center)
127
+ else
128
+ case args[0]
129
+ when Array
130
+ self.center = args[0][0], args[0][1]
131
+ when Snow::Vec2, Snow::Vec3, Snow::Vec4
132
+ @center[0] = args[0][0]
133
+ @center[1] = args[0][1]
134
+ when Numeric
135
+ raise ArgumentError.new("Cannot set center from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
136
+ args.each { |arg| type_check(arg, Numeric) }
137
+ @center[0] = args[0]
138
+ @center[1] = args[1]
139
+ else
140
+ raise ArgumentError.new("Cannot set center from #{args.inspect}: bad type!")
141
+ end
142
+ end
143
+ @center_is_dirty = @is_dirty = true
144
+ end
145
+ alias :set_center :center=
146
+
147
+ ##
148
+ # Sets the x component of the centerpoint of this Transformable. See Transformable#center.
149
+ #
150
+ def center_x=(val)
151
+ type_check(val, Numeric)
152
+ @center[0] = val
153
+ @center_is_dirty = @is_dirty = true
154
+ end
155
+
156
+ ##
157
+ # Sets the y component of the centerpoint of this Transformable. See Transformable#center.
158
+ #
159
+ def center_y=(val)
160
+ type_check(val, Numeric)
161
+ @center[1] = val
162
+ @center_is_dirty = @is_dirty = true
163
+ end
164
+
165
+ ##
166
+ # Sets this transform's x/y scaling. A scale value of [1, 1] results in no scaling. Larger values make a shape bigger,
167
+ # while smaller values will make it smaller. Great for shrinking/growing animations, or to zoom the camera in/out.
168
+ #
169
+ # If passed more than two numeric arguments, only the first two are used.
170
+ #
171
+ # Usage:
172
+ # - transform.scale = scalar
173
+ # - transform.scale = x, y
174
+ # - transform.scale = [x, y]
175
+ # - transform.scale = Snow::Vec2[x, y]
176
+ # - transform.scale = Snow::Vec3[x, y, z]
177
+ # - transform.scale = Snow::Vec4[x, y, z, c]
178
+ # - transform.set_scale { |s| s.multiply(0.5, s) }
179
+ #
180
+ def scale=(*args)
181
+ if block_given?
182
+ yield(@scale)
183
+ else
184
+ if args.length >= 2
185
+ case args[0]
186
+ when Numeric
187
+ args.each { |arg| type_check(arg, Numeric) }
188
+ @scale[0] = args[0]
189
+ @scale[1] = args[1]
190
+ else
191
+ raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
192
+ end
193
+ elsif args.length == 1
194
+ case args[0]
195
+ when Array
196
+ self.set_scale(*(args[0]))
197
+ when Snow::Vec2
198
+ self.set_scale(args[0][0], args[0][1])
199
+ when Numeric
200
+ self.set_scale(args[0], args[0])
201
+ else
202
+ raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
203
+ end
204
+ else
205
+ raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
206
+ end
207
+ end
208
+ @scale_is_dirty = @is_dirty = true
209
+ end
210
+ alias :set_scale :scale=
211
+
212
+ ##
213
+ # Wrapper method. Sets the x component of the scaling of this Actor. See Transformable#scale.
214
+ #
215
+ def scale_x=(val)
216
+ type_check(val, Numeric)
217
+ @scale[0] = val
218
+ @scale_is_dirty = @is_dirty = true
219
+ end
220
+
221
+ ##
222
+ # Wrapper method. Sets the y component of the scaling of this Actor. See Transformable#scale.
223
+ #
224
+ def scale_y=(val)
225
+ type_check(val, Numeric)
226
+ @scale[1] = val
227
+ @scale_is_dirty = @is_dirty = true
228
+ end
229
+
230
+ ##
231
+ # Sets this transform's rotation in radians. A value of 0 results in no rotation. Great for spinning animations, or
232
+ # rotating the player's camera view.
233
+ #
234
+ # Usage:
235
+ # - transform.rotation = radians
236
+ #
237
+ def rotation=(radians)
238
+ type_check(radians, Numeric)
239
+ if radians.is_a?(Float)
240
+ raise ArgumentError.new("Expected a finite number, but received #{radians.inspect}!") unless radians.finite?
241
+ end
242
+ @rotation = radians
243
+ @rotate_is_dirty = @is_dirty = true
244
+ end
245
+ alias :set_rotation :rotation=
246
+
247
+ ##
248
+ # Sets this transform's x/y translation in radians. A value of [0, 0] results in no translation. Great for moving
249
+ # actors across the screen or scrolling the camera.
250
+ #
251
+ # If passed more than two numeric arguments, only the first two are used.
252
+ #
253
+ # Optionally, this method can be passed a block and no arguments. A reference to this Transformable's translation
254
+ # will be passed to the block as the first parameter, allowing direct manipulation using all of snow-math's Vec3
255
+ # methods. This is particularly useful when optimizing methods that MUST be as fast as possible, such as animation
256
+ # and game physics, since the result of your mathematics can be written directly to this Transformable's translation
257
+ # without having to instantiate an interim Vector during every physics step.
258
+ #
259
+ # Usage:
260
+ # - transform.translation = x, y
261
+ # - transform.translation = [x, y]
262
+ # - transform.translation = Snow::Vec2[x, y]
263
+ # - transform.translation = Snow::Vec3[x, y, z]
264
+ # - transform.translation = Snow::Vec4[x, y, z, c]
265
+ # - transform.set_translation { |t| t.add(velocity * elapsed, t) }
266
+ #
267
+ def translation=(args = nil)
268
+ if block_given?
269
+ yield(@translation)
270
+ else
271
+ case args[0]
272
+ when Array
273
+ self.translation = args[0][0], args[0][1]
274
+ when Snow::Vec2, Snow::Vec3, Snow::Vec4
275
+ @translation[0] = args[0][0]
276
+ @translation[1] = args[0][1]
277
+ when Numeric
278
+ raise ArgumentError.new("Cannot set translation from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
279
+ args.each { |arg| type_check(arg, Numeric) }
280
+ @translation[0] = args[0]
281
+ @translation[1] = args[1]
282
+ else
283
+ raise ArgumentError.new("Cannot set translation from #{args.inspect}: bad type!")
284
+ end
285
+ end
286
+ @translate_is_dirty = @is_dirty = true
287
+ end
288
+ alias :set_translation :translation=
289
+ alias :pos= :translation=
290
+
291
+ ##
292
+ # Sets this Transformable's x position in relative space. See Transformable#translation.
293
+ #
294
+ def x=(val)
295
+ type_check(val, Numeric)
296
+ @translation[0] = val
297
+ @translate_is_dirty = @is_dirty = true
298
+ end
299
+
300
+ ##
301
+ # Sets this Transformable's y position in relative space. See Transformable#translation.
302
+ #
303
+ def y=(val)
304
+ type_check(val, Numeric)
305
+ @translation[1] = val
306
+ @translate_is_dirty = @is_dirty = true
307
+ end
308
+
309
+ ##
310
+ # Returns a Snow::Mat3 which combines our current center, scale, rotation, and translation into a single transform
311
+ # matrix. When a point in space is multiplied by this transform, the centering, scaling, rotation, and translation
312
+ # will all be applied to that point.
313
+ #
314
+ # This Snow::Mat3 is cached and will only be recalculated as needed.
315
+ #
316
+ def to_matrix
317
+ return @matrix unless @is_dirty || @matrix.nil?
318
+
319
+ update_center_matrix
320
+ update_scale_matrix
321
+ update_rotate_matrix
322
+ update_translate_matrix
323
+
324
+ @matrix = Snow::Mat3.new unless @matrix
325
+
326
+ @matrix.set(@center_mat)
327
+ @matrix.multiply!(@scale_mat)
328
+ @matrix.multiply!(@rotate_mat)
329
+ @matrix.multiply!(@translate_mat)
330
+
331
+ @is_dirty = false
332
+ @matrix
333
+ end
334
+
335
+ ##
336
+ # Transforms a Vec3 using the provided Mat3 transform and returns the result as a new Vec3. This is the
337
+ # opposite of Transformable.untransform_point.
338
+ #
339
+ @@transformable_point
340
+ def self.transform_point(mat, point, out = nil)
341
+ @@transformable_point ||= Snow::Vec3.new
342
+ @@transformable_point.set(point[0], point[1], 1)
343
+
344
+ out ||= Snow::Vec3.new
345
+ mat.multiply(@@transformable_point, out)
346
+ out[2] = 0
347
+ out
348
+ end
349
+
350
+ ##
351
+ # Applies all of our transformations to the point, returning the resulting point as a new Vec3. This is the opposite
352
+ # of Transformable#untransform_point.
353
+ #
354
+ def transform_point(point, out = nil)
355
+ Transformable.transform_point(to_matrix, point, out)
356
+ end
357
+
358
+ ##
359
+ # Transforms a Vec3 using the inverse of the provided Mat3 transform and returns the result as a new Vec3. This
360
+ # is the opposite of Transformable.transform_point.
361
+ #
362
+ def self.untransform_point(mat, point, out = nil)
363
+ inverse_mat = MatrixCache.instance.get
364
+ raise "Unable to invert matrix: #{mat}!" unless mat.inverse(inverse_mat)
365
+ transform_point(inverse_mat, point, out)
366
+ ensure
367
+ MatrixCache.instance.recycle(inverse_mat) if inverse_mat
368
+ end
369
+
370
+ ##
371
+ # Applies the inverse of all of our transformations to the point, returning the resulting point as a new Vec3. This
372
+ # is the opposite of Transformable#transform_point.
373
+ #
374
+ def untransform_point(point, out = nil)
375
+ Transformable.untransform_point(to_matrix, point, out)
376
+ end
377
+
378
+ private
379
+
380
+ def update_center_matrix
381
+ return unless @center_is_dirty || @center_mat.nil?
382
+ @center_mat ||= Snow::Mat3.new
383
+ @center_mat[2] = -@center[0]
384
+ @center_mat[5] = -@center[1]
385
+ @center_is_dirty = false
386
+ end
387
+
388
+ def update_scale_matrix
389
+ return unless @scale_is_dirty || @scale_mat.nil?
390
+ @scale_mat ||= Snow::Mat3.new
391
+ @scale_mat[0] = @scale[0]
392
+ @scale_mat[4] = @scale[1]
393
+ @scale_is_dirty = false
394
+ end
395
+
396
+ def update_rotate_matrix
397
+ return unless @rotate_is_dirty || @rotate_mat.nil?
398
+ @rotate_mat ||= Snow::Mat3.new
399
+ @rotate_mat[4] = @rotate_mat[0] = Math.cos(@rotation)
400
+ @rotate_mat[1] = Math.sin(@rotation)
401
+ @rotate_mat[3] = -@rotate_mat[1]
402
+ @rotate_is_dirty = false
403
+ end
404
+
405
+ def update_translate_matrix
406
+ return unless @translate_is_dirty || @translate_mat.nil?
407
+ @translate_mat ||= Snow::Mat3.new
408
+ @translate_mat[2] = @translation[0]
409
+ @translate_mat[5] = @translation[1]
410
+ @translate_is_dirty = false
411
+ end
412
+ end
413
+ end