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.



12
13
14
# File 'lib/gosling/transformable.rb', line 12

def rotation
  @rotation
end

Class Method Details

.transform_point(mat, point, out = nil) ⇒ Object



340
341
342
343
344
345
346
347
348
# File 'lib/gosling/transformable.rb', line 340

def self.transform_point(mat, point, out = nil)
  @@transformable_point ||= Snow::Vec3.new
  @@transformable_point.set(point[0], point[1], 1)

  out ||= Snow::Vec3.new
  mat.multiply(@@transformable_point, out)
  out[2] = 0
  out
end

.untransform_point(mat, point, out = nil) ⇒ 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.



362
363
364
365
366
367
368
# File 'lib/gosling/transformable.rb', line 362

def self.untransform_point(mat, point, out = nil)
  inverse_mat = MatrixCache.instance.get
  raise "Unable to invert matrix: #{mat}!" unless mat.inverse(inverse_mat)
  transform_point(inverse_mat, point, out)
ensure
  MatrixCache.instance.recycle(inverse_mat) if inverse_mat
end

Instance Method Details

#centerObject

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



38
39
40
# File 'lib/gosling/transformable.rb', line 38

def center
  @center.dup.freeze
end

#center=(args = nil) ⇒ 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]

  • transform.set_center { |c| c.add(-sprite.pos, c) }



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/gosling/transformable.rb', line 124

def center=(args = nil)
  if block_given?
    yield(@center)
  else
    case args[0]
    when Array
      self.center = args[0][0], args[0][1]
    when Snow::Vec2, Snow::Vec3, Snow::Vec4
      @center[0] = args[0][0]
      @center[1] = args[0][1]
    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[0] = args[0]
      @center[1] = args[1]
    else
      raise ArgumentError.new("Cannot set center from #{args.inspect}: bad type!")
    end
  end
  @center_is_dirty = @is_dirty = true
end

#center_xObject

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



45
46
47
# File 'lib/gosling/transformable.rb', line 45

def center_x
  @center[0]
end

#center_x=(val) ⇒ Object

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



150
151
152
153
154
# File 'lib/gosling/transformable.rb', line 150

def center_x=(val)
  type_check(val, Numeric)
  @center[0] = val
  @center_is_dirty = @is_dirty = true
end

#center_yObject

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



52
53
54
# File 'lib/gosling/transformable.rb', line 52

def center_y
  @center[1]
end

#center_y=(val) ⇒ Object

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



159
160
161
162
163
# File 'lib/gosling/transformable.rb', line 159

def center_y=(val)
  type_check(val, Numeric)
  @center[1] = val
  @center_is_dirty = @is_dirty = true
end

#initializeObject

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



17
18
19
20
21
22
# File 'lib/gosling/transformable.rb', line 17

def initialize
  @center = Snow::Vec3[0, 0, 1]
  @scale = Snow::Vec2[1, 1]
  @translation = Snow::Vec3[0, 0, 1]
  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.



28
29
30
31
32
33
# File 'lib/gosling/transformable.rb', line 28

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

#scaleObject

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



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

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 = scalar

  • 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]

  • transform.set_scale { |s| s.multiply(0.5, s) }



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/gosling/transformable.rb', line 180

def scale=(*args)
  if block_given?
    yield(@scale)
  else
    if args.length >= 2
      case args[0]
      when Numeric
        args.each { |arg| type_check(arg, Numeric) }
        @scale[0] = args[0]
        @scale[1] = args[1]
      else
        raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
      end
    elsif args.length == 1
      case args[0]
      when Array
        self.set_scale(*(args[0]))
      when Snow::Vec2
        self.set_scale(args[0][0], args[0][1])
      when Numeric
        self.set_scale(args[0], args[0])
      else
        raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
      end
    else
      raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
    end
  end
  @scale_is_dirty = @is_dirty = true
end

#scale_xObject

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



66
67
68
# File 'lib/gosling/transformable.rb', line 66

def scale_x
  @scale[0]
end

#scale_x=(val) ⇒ Object

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



215
216
217
218
219
# File 'lib/gosling/transformable.rb', line 215

def scale_x=(val)
  type_check(val, Numeric)
  @scale[0] = val
  @scale_is_dirty = @is_dirty = true
end

#scale_yObject

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



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

def scale_y
  @scale[1]
end

#scale_y=(val) ⇒ Object

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



224
225
226
227
228
# File 'lib/gosling/transformable.rb', line 224

def scale_y=(val)
  type_check(val, Numeric)
  @scale[1] = 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.



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/gosling/transformable.rb', line 316

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(point, out = nil) ⇒ 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.



354
355
356
# File 'lib/gosling/transformable.rb', line 354

def transform_point(point, out = nil)
  Transformable.transform_point(to_matrix, point, out)
end

#translationObject Also known as: pos

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



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

def translation
  @translation.dup.freeze
end

#translation=(args = nil) ⇒ 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.

Optionally, this method can be passed a block and no arguments. A reference to this Transformable’s translation will be passed to the block as the first parameter, allowing direct manipulation using all of snow-math’s Vec3 methods. This is particularly useful when optimizing methods that MUST be as fast as possible, such as animation and game physics, since the result of your mathematics can be written directly to this Transformable’s translation without having to instantiate an interim Vector during every physics step.

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]

  • transform.set_translation { |t| t.add(velocity * elapsed, t) }



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/gosling/transformable.rb', line 267

def translation=(args = nil)
  if block_given?
    yield(@translation)
  else
    case args[0]
    when Array
      self.translation = args[0][0], args[0][1]
    when Snow::Vec2, Snow::Vec3, Snow::Vec4
      @translation[0] = args[0][0]
      @translation[1] = args[0][1]
    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[0] = args[0]
      @translation[1] = args[1]
    else
      raise ArgumentError.new("Cannot set translation from #{args.inspect}: bad type!")
    end
  end
  @translate_is_dirty = @is_dirty = true
end

#untransform_point(point, out = nil) ⇒ 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.



374
375
376
# File 'lib/gosling/transformable.rb', line 374

def untransform_point(point, out = nil)
  Transformable.untransform_point(to_matrix, point, out)
end

#xObject

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



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

def x
  @translation[0]
end

#x=(val) ⇒ Object

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



294
295
296
297
298
# File 'lib/gosling/transformable.rb', line 294

def x=(val)
  type_check(val, Numeric)
  @translation[0] = val
  @translate_is_dirty = @is_dirty = true
end

#yObject

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



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

def y
  @translation[1]
end

#y=(val) ⇒ Object

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



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

def y=(val)
  type_check(val, Numeric)
  @translation[1] = val
  @translate_is_dirty = @is_dirty = true
end