gosling 2.3.0 → 2.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|