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
-
#rotation ⇒ Object
Returns the value of attribute rotation.
Class Method Summary collapse
- .transform_point(mat, point, out = nil) ⇒ Object
-
.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.
Instance Method Summary collapse
-
#center ⇒ Object
Returns a duplicate of the center Vec3 (@center is read-only).
-
#center=(args = nil) ⇒ Object
(also: #set_center)
Sets this transform’s centerpoint.
-
#center_x ⇒ Object
Returns the x component of the centerpoint of this Transformable.
-
#center_x=(val) ⇒ Object
Sets the x component of the centerpoint of this Transformable.
-
#center_y ⇒ Object
Returns the y component of the centerpoint of this Transformable.
-
#center_y=(val) ⇒ Object
Sets the y component of the centerpoint of this Transformable.
-
#initialize ⇒ Object
Initializes this Transformable to have no transformations (identity matrix).
-
#reset ⇒ Object
Resets center and translation to [0, 0], scale to [1, 1], and rotation to 0, restoring this transformation to the identity matrix.
-
#scale ⇒ Object
Returns a duplicate of the scale Vec2 (@scale is read-only).
-
#scale=(*args) ⇒ Object
(also: #set_scale)
Sets this transform’s x/y scaling.
-
#scale_x ⇒ Object
Returns the x component of the scaling of this Transformable.
-
#scale_x=(val) ⇒ Object
Wrapper method.
-
#scale_y ⇒ Object
Returns the y component of the scaling of this Transformable.
-
#scale_y=(val) ⇒ Object
Wrapper method.
-
#to_matrix ⇒ Object
Returns a Snow::Mat3 which combines our current center, scale, rotation, and translation into a single transform matrix.
-
#transform_point(point, out = nil) ⇒ Object
Applies all of our transformations to the point, returning the resulting point as a new Vec3.
-
#translation ⇒ Object
(also: #pos)
Returns a duplicate of the translation Vec3 (@translation is read-only).
-
#translation=(args = nil) ⇒ Object
(also: #set_translation, #pos=)
Sets this transform’s x/y translation in radians.
-
#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.
-
#x ⇒ Object
Returns this Transformable’s x position in relative space.
-
#x=(val) ⇒ Object
Sets this Transformable’s x position in relative space.
-
#y ⇒ Object
Returns this Transformable’s y position in relative space.
-
#y=(val) ⇒ Object
Sets this Transformable’s y position in relative space.
Instance Attribute Details
#rotation ⇒ Object
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
#center ⇒ Object
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_x ⇒ Object
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_y ⇒ Object
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 |
#initialize ⇒ Object
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 |
#reset ⇒ Object
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 |
#scale ⇒ Object
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_x ⇒ Object
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_y ⇒ Object
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_matrix ⇒ Object
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 |
#translation ⇒ Object 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 |
#x ⇒ Object
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 |
#y ⇒ Object
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 |