Module: Gosling::Transformable

Included in:
Actor
Defined in:
lib/gosling/transformable.rb

Overview

A helper class for performing vector transforms in 2D space. Relies heavily on the Vec3 and Mat3 classes of the SnowMath gem to remain performant.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#rotationObject

Returns the value of attribute rotation.



54
55
56
# File 'lib/gosling/transformable.rb', line 54

def rotation
  @rotation
end

Class Method Details

.rational_cos(r) ⇒ Object

Wraps Math.cos to produce rationals instead of floats. Common values are returned quickly from a lookup table.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/gosling/transformable.rb', line 36

def self.rational_cos(r)
  type_check(r, Numeric)

  r = r % (2 * Math::PI)
  case r
  when 0.0
    1.to_r
  when Math::PI / 2
    0.to_r
  when Math::PI
    -1.to_r
  when Math::PI * 3 / 2
    0.to_r
  else
    Math.cos(r).to_r
  end
end

.rational_sin(r) ⇒ Object

Wraps Math.sin to produce rationals instead of floats. Common values are returned quickly from a lookup table.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/gosling/transformable.rb', line 15

def self.rational_sin(r)
  type_check(r, Numeric)

  r = r % (2 * Math::PI)
  case r
  when 0.0
    0.to_r
  when Math::PI / 2
    1.to_r
  when Math::PI
    0.to_r
  when Math::PI * 3 / 2
    -1.to_r
  else
    Math.sin(r).to_r
  end
end

.transform_point(mat, v) ⇒ Object

Transforms a Vec3 using the provided Mat3 transform and returns the result as a new Vec3. This is the opposite of Transformable.untransform_point.



347
348
349
350
351
352
353
# File 'lib/gosling/transformable.rb', line 347

def self.transform_point(mat, v)
  type_check(mat, Snow::Mat3)
  type_check(v, Snow::Vec3)
  result = mat * Snow::Vec3[v[0], v[1], 1.to_r]
  result[2] = 0.to_r
  result
end

.untransform_point(mat, v) ⇒ Object

Transforms a Vec3 using the inverse of the provided Mat3 transform and returns the result as a new Vec3. This is the opposite of Transformable.transform_point.



367
368
369
370
371
372
373
374
375
376
377
# File 'lib/gosling/transformable.rb', line 367

def self.untransform_point(mat, v)
  type_check(mat, Snow::Mat3)
  type_check(v, Snow::Vec3)
  inverse_mat = mat.inverse
  unless inverse_mat
    raise "Unable to invert matrix: #{mat}!"
  end
  result = mat.inverse * Snow::Vec3[v[0], v[1], 1.to_r]
  result[2] = 0.to_r
  result
end

Instance Method Details

#centerObject

Returns a duplicate of the center Vec3 (@center is read-only).



80
81
82
# File 'lib/gosling/transformable.rb', line 80

def center
  @center.dup.freeze
end

#center=(args) ⇒ Object Also known as: set_center

Sets this transform’s centerpoint. All other transforms are performed relative to this central point.

The default centerpoint is [0, 0], which is the same as no transform. For a square defined by the vertices [[0, 0], [10, 0], [10, 10], [0, 10]], this would translate to that square’s upper left corner. In this case, when scaled larger or smaller, only the square’s right and bottom edges would expand or contract, and when rotated it would spin around its upper left corner. For most applications, this is probably not what we want.

By setting the centerpoint to something other than the origin, we can change the scaling and rotation to something that makes more sense. For the square defined above, we could set center to be the actual center of the square: [5, 5]. By doing so, scaling the square would cause it to expand evenly on all sides, and rotating it would cause it to spin about its center like a four-cornered wheel.

You can set the centerpoint to be any point in local space, inside or even outside of the shape in question.

If passed more than two numeric arguments, only the first two are used.

Usage:

  • transform.center = x, y

  • transform.center = [x, y]

  • transform.center = Snow::Vec2[x, y]

  • transform.center = Snow::Vec3[x, y, z]

  • transform.center = Snow::Vec4[x, y, z, c]



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/gosling/transformable.rb', line 165

def center=(args)
  case args[0]
  when Array
    self.center = args[0][0], args[0][1]
  when Snow::Vec2, Snow::Vec3, Snow::Vec4
    @center.x = args[0].x
    @center.y = args[0].y
  when Numeric
    raise ArgumentError.new("Cannot set center from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
    args.each { |arg| type_check(arg, Numeric) }
    @center.x = args[0]
    @center.y = args[1]
  else
    raise ArgumentError.new("Cannot set center from #{args.inspect}: bad type!")
  end
  @center_is_dirty = @is_dirty = true
end

#center_xObject

Returns the x component of the centerpoint of this Transformable. See Transformable#center.



87
88
89
# File 'lib/gosling/transformable.rb', line 87

def center_x
  @center.x
end

#center_x=(val) ⇒ Object

Sets the x component of the centerpoint of this Transformable. See Transformable#center.



187
188
189
190
191
# File 'lib/gosling/transformable.rb', line 187

def center_x=(val)
  type_check(val, Numeric)
  @center.x = val
  @center_is_dirty = @is_dirty = true
end

#center_yObject

Returns the y component of the centerpoint of this Transformable. See Transformable#center.



94
95
96
# File 'lib/gosling/transformable.rb', line 94

def center_y
  @center.y
end

#center_y=(val) ⇒ Object

Sets the y component of the centerpoint of this Transformable. See Transformable#center.



196
197
198
199
200
# File 'lib/gosling/transformable.rb', line 196

def center_y=(val)
  type_check(val, Numeric)
  @center.y = val
  @center_is_dirty = @is_dirty = true
end

#initializeObject

Initializes this Transformable to have no transformations (identity matrix).



59
60
61
62
63
64
# File 'lib/gosling/transformable.rb', line 59

def initialize
  @center = Snow::Vec3[0.to_r, 0.to_r, 1.to_r]
  @scale = Snow::Vec2[1.to_r, 1.to_r]
  @translation = Snow::Vec3[0.to_r, 0.to_r, 1.to_r]
  reset
end

#resetObject

Resets center and translation to [0, 0], scale to [1, 1], and rotation to 0, restoring this transformation to the identity matrix.



70
71
72
73
74
75
# File 'lib/gosling/transformable.rb', line 70

def reset
  self.center = 0.to_r, 0.to_r
  self.scale = 1.to_r, 1.to_r
  self.rotation = 0.to_r
  self.translation = 0.to_r, 0.to_r
end

#scaleObject

Returns a duplicate of the scale Vec2 (@scale is read-only).



101
102
103
# File 'lib/gosling/transformable.rb', line 101

def scale
  @scale.dup.freeze
end

#scale=(args) ⇒ Object Also known as: set_scale

Sets this transform’s x/y scaling. A scale value of [1, 1] results in no scaling. Larger values make a shape bigger, while smaller values will make it smaller. Great for shrinking/growing animations, or to zoom the camera in/out.

If passed more than two numeric arguments, only the first two are used.

Usage:

  • transform.scale = x, y

  • transform.scale = [x, y]

  • transform.scale = Snow::Vec2[x, y]

  • transform.scale = Snow::Vec3[x, y, z]

  • transform.scale = Snow::Vec4[x, y, z, c]



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/gosling/transformable.rb', line 215

def scale=(args)
  case args[0]
  when Array
    self.scale = args[0][0], args[0][1]
  when Snow::Vec2, Snow::Vec3, Snow::Vec4
    @scale.x = args[0].x
    @scale.y = args[0].y
  when Numeric
    raise ArgumentError.new("Cannot set scale from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
    args.each { |arg| type_check(arg, Numeric) }
    @scale.x = args[0]
    @scale.y = args[1]
  else
    raise ArgumentError.new("Cannot set scale from #{args.inspect}: bad type!")
  end
  @scale_is_dirty = @is_dirty = true
end

#scale_xObject

Returns the x component of the scaling of this Transformable. See Transformable#scale.



108
109
110
# File 'lib/gosling/transformable.rb', line 108

def scale_x
  @scale.x
end

#scale_x=(val) ⇒ Object

Wrapper method. Sets the x component of the scaling of this Actor. See Transformable#scale.



237
238
239
240
241
# File 'lib/gosling/transformable.rb', line 237

def scale_x=(val)
  type_check(val, Numeric)
  @scale.x = val
  @scale_is_dirty = @is_dirty = true
end

#scale_yObject

Returns the y component of the scaling of this Transformable. See Transformable#scale.



115
116
117
# File 'lib/gosling/transformable.rb', line 115

def scale_y
  @scale.y
end

#scale_y=(val) ⇒ Object

Wrapper method. Sets the y component of the scaling of this Actor. See Transformable#scale.



246
247
248
249
250
# File 'lib/gosling/transformable.rb', line 246

def scale_y=(val)
  type_check(val, Numeric)
  @scale.y = val
  @scale_is_dirty = @is_dirty = true
end

#to_matrixObject

Returns a Snow::Mat3 which combines our current center, scale, rotation, and translation into a single transform matrix. When a point in space is multiplied by this transform, the centering, scaling, rotation, and translation will all be applied to that point.

This Snow::Mat3 is cached and will only be recalculated as needed.



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/gosling/transformable.rb', line 324

def to_matrix
  return @matrix unless @is_dirty || @matrix.nil?

  update_center_matrix
  update_scale_matrix
  update_rotate_matrix
  update_translate_matrix

  @matrix = Snow::Mat3.new unless @matrix

  @matrix.set(@center_mat)
  @matrix.multiply!(@scale_mat)
  @matrix.multiply!(@rotate_mat)
  @matrix.multiply!(@translate_mat)

  @is_dirty = false
  @matrix
end

#transform_point(v) ⇒ Object

Applies all of our transformations to the point, returning the resulting point as a new Vec3. This is the opposite of Transformable#untransform_point.



359
360
361
# File 'lib/gosling/transformable.rb', line 359

def transform_point(v)
  Transformable.transform_point(to_matrix, v)
end

#translationObject Also known as: pos

Returns a duplicate of the translation Vec3 (@translation is read-only).



122
123
124
# File 'lib/gosling/transformable.rb', line 122

def translation
  @translation.dup.freeze
end

#translation=(args) ⇒ Object Also known as: set_translation, pos=

Sets this transform’s x/y translation in radians. A value of [0, 0] results in no translation. Great for moving actors across the screen or scrolling the camera.

If passed more than two numeric arguments, only the first two are used.

Usage:

  • transform.translation = x, y

  • transform.translation = [x, y]

  • transform.translation = Snow::Vec2[x, y]

  • transform.translation = Snow::Vec3[x, y, z]

  • transform.translation = Snow::Vec4[x, y, z, c]



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/gosling/transformable.rb', line 279

def translation=(args)
  case args[0]
  when Array
    self.translation = args[0][0], args[0][1]
  when Snow::Vec2, Snow::Vec3, Snow::Vec4
    @translation.x = args[0].x
    @translation.y = args[0].y
  when Numeric
    raise ArgumentError.new("Cannot set translation from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
    args.each { |arg| type_check(arg, Numeric) }
    @translation.x = args[0]
    @translation.y = args[1]
  else
    raise ArgumentError.new("Cannot set translation from #{args.inspect}: bad type!")
  end
  @translate_is_dirty = @is_dirty = true
end

#untransform_point(v) ⇒ Object

Applies the inverse of all of our transformations to the point, returning the resulting point as a new Vec3. This is the opposite of Transformable#transform_point.



383
384
385
# File 'lib/gosling/transformable.rb', line 383

def untransform_point(v)
  Transformable.untransform_point(to_matrix, v)
end

#xObject

Returns this Transformable’s x position in relative space. See Transformable#translation.



130
131
132
# File 'lib/gosling/transformable.rb', line 130

def x
  @translation.x
end

#x=(val) ⇒ Object

Sets this Transformable’s x position in relative space. See Transformable#translation.



302
303
304
305
306
# File 'lib/gosling/transformable.rb', line 302

def x=(val)
  type_check(val, Numeric)
  @translation.x = val
  @translate_is_dirty = @is_dirty = true
end

#yObject

Returns this Transformable’s y position in relative space. See Transformable#translation.



137
138
139
# File 'lib/gosling/transformable.rb', line 137

def y
  @translation.y
end

#y=(val) ⇒ Object

Sets this Transformable’s y position in relative space. See Transformable#translation.



311
312
313
314
315
# File 'lib/gosling/transformable.rb', line 311

def y=(val)
  type_check(val, Numeric)
  @translation.y = val
  @translate_is_dirty = @is_dirty = true
end