gosling 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/gosling.rb +4 -4
- data/lib/gosling/actor.rb +328 -328
- data/lib/gosling/circle.rb +65 -65
- data/lib/gosling/collision.rb +499 -499
- data/lib/gosling/image_library.rb +24 -24
- data/lib/gosling/inheritance_error.rb +4 -4
- data/lib/gosling/matrix_cache.rb +23 -21
- data/lib/gosling/object_cache.rb +48 -48
- data/lib/gosling/patches.rb +42 -42
- data/lib/gosling/polygon.rb +122 -122
- data/lib/gosling/rect.rb +41 -41
- data/lib/gosling/sprite.rb +50 -50
- data/lib/gosling/transformable.rb +413 -413
- data/lib/gosling/utils.rb +20 -20
- data/lib/gosling/vector_cache.rb +21 -21
- data/lib/gosling/version.rb +7 -7
- data/spec/actor_spec.rb +627 -627
- data/spec/circle_spec.rb +46 -46
- data/spec/collision_spec.rb +1755 -1755
- data/spec/image_library_spec.rb +19 -19
- data/spec/matrix_cache_spec.rb +25 -25
- data/spec/object_cache_spec.rb +22 -22
- data/spec/polygon_spec.rb +284 -284
- data/spec/rect_spec.rb +86 -86
- data/spec/spec_helper.rb +3 -3
- data/spec/sprite_spec.rb +45 -45
- data/spec/transformable_spec.rb +479 -479
- data/spec/vector_cache_spec.rb +80 -80
- metadata +3 -4
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
|
data/lib/gosling/sprite.rb
CHANGED
@@ -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
|