gosling 2.0.1 → 2.1.0
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 +4 -4
- data/lib/gosling/collision.rb +56 -1
- data/lib/gosling/polygon.rb +22 -4
- data/lib/gosling/transformable.rb +19 -59
- data/lib/gosling/version.rb +1 -1
- data/spec/collision_spec.rb +604 -331
- data/spec/polygon_spec.rb +57 -12
- data/spec/transformable_spec.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6522b7771bdf2e2b85a0bf3cbf7f440497d21a91
|
4
|
+
data.tar.gz: e0d6aa8c63c4361aeb7dffb1f25c6310bb050730
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1646556b4e6765ceda7ede9e7c7447cd71eda9af4eb510bd7413e9ba8e9116c4c4177e9b8096c3f2fc81584c0bcb310ab6caf00691d951f1b549b5ffb98eeb8
|
7
|
+
data.tar.gz: 719da8786b573ae344338586f2c53ebc0bc41987d392fc0383b218fc4408e94f5f411ffc3a506bf78a9958eae033ae34db14385db3c4702f380e6e312250d753
|
data/lib/gosling/collision.rb
CHANGED
@@ -15,6 +15,8 @@ module Gosling
|
|
15
15
|
class Collision
|
16
16
|
include Singleton
|
17
17
|
|
18
|
+
COLLISION_TOLERANCE = 0.000001
|
19
|
+
|
18
20
|
##
|
19
21
|
# Tests two Actors or child classes to see whether they overlap. Actors, having no shape, never overlap. Child
|
20
22
|
# classes use appropriate algorithms based on their shape.
|
@@ -42,6 +44,50 @@ module Gosling
|
|
42
44
|
return true
|
43
45
|
end
|
44
46
|
|
47
|
+
##
|
48
|
+
# Tests two Actors or child classes to see whether they overlap. This is similar to #test, but returns additional
|
49
|
+
# information.
|
50
|
+
#
|
51
|
+
# Arguments:
|
52
|
+
# - shapeA: an Actor
|
53
|
+
# - shapeB: another Actor
|
54
|
+
#
|
55
|
+
# Returns a hash with the following key/value pairs:
|
56
|
+
# - colliding: true if the Actors overlap; false otherwise
|
57
|
+
# - overlap: if colliding, the smallest overlapping distance; nil otherwise
|
58
|
+
# - penetration: if colliding, a vector representing how far shape B must move to be separated from (or merely
|
59
|
+
# touching) shape A; nil otherwise
|
60
|
+
#
|
61
|
+
def self.get_collision_info(shapeA, shapeB)
|
62
|
+
info = { colliding: false, overlap: nil, penetration: nil }
|
63
|
+
|
64
|
+
return info if shapeA.instance_of?(Actor) || shapeB.instance_of?(Actor)
|
65
|
+
|
66
|
+
return info if shapeA === shapeB
|
67
|
+
|
68
|
+
separation_axes = get_separation_axes(shapeA, shapeB)
|
69
|
+
|
70
|
+
smallest_overlap = nil
|
71
|
+
smallest_axis = nil
|
72
|
+
separation_axes.each do |axis|
|
73
|
+
projectionA = project_onto_axis(shapeA, axis)
|
74
|
+
projectionB = project_onto_axis(shapeB, axis)
|
75
|
+
overlap = get_overlap(projectionA, projectionB)
|
76
|
+
return info unless overlap && overlap > COLLISION_TOLERANCE
|
77
|
+
if smallest_overlap.nil? || smallest_overlap > overlap
|
78
|
+
smallest_overlap = overlap
|
79
|
+
flip = (projectionA[0] + projectionA[1]) * 0.5 > (projectionB[0] + projectionB[1]) * 0.5
|
80
|
+
smallest_axis = flip ? -axis : axis
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
info[:colliding] = true
|
85
|
+
info[:overlap] = smallest_overlap
|
86
|
+
info[:penetration] = smallest_axis.normalize * smallest_overlap
|
87
|
+
|
88
|
+
return info
|
89
|
+
end
|
90
|
+
|
45
91
|
##
|
46
92
|
# Tests a point in space to see whether it is inside the actor's shape or not.
|
47
93
|
#
|
@@ -147,6 +193,11 @@ module Gosling
|
|
147
193
|
end
|
148
194
|
|
149
195
|
def self.projections_overlap?(a, b)
|
196
|
+
overlap = get_overlap(a, b)
|
197
|
+
overlap != nil && overlap > COLLISION_TOLERANCE
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.get_overlap(a, b)
|
150
201
|
type_check(a, Array)
|
151
202
|
type_check(b, Array)
|
152
203
|
a.each { |x| type_check(x, Numeric) }
|
@@ -155,7 +206,11 @@ module Gosling
|
|
155
206
|
|
156
207
|
a.sort! if a[0] > a[1]
|
157
208
|
b.sort! if b[0] > b[1]
|
158
|
-
|
209
|
+
return b[1] - b[0] if a[0] <= b[0] && b[1] <= a[1]
|
210
|
+
return a[1] - a[0] if b[0] <= a[0] && a[1] <= b[1]
|
211
|
+
return a[1] - b[0] if a[0] <= b[0] && b[0] <= a[1]
|
212
|
+
return b[1] - a[0] if b[0] <= a[0] && a[0] <= b[1]
|
213
|
+
nil
|
159
214
|
end
|
160
215
|
end
|
161
216
|
end
|
data/lib/gosling/polygon.rb
CHANGED
@@ -31,16 +31,34 @@ module Gosling
|
|
31
31
|
end
|
32
32
|
|
33
33
|
##
|
34
|
-
# Sets this polygon's vertices. Requires three or more Snow::Vec3
|
34
|
+
# Sets this polygon's vertices. Requires three or more Snow::Vec2, Vec3, Vec4, or Arrays containing 2 or more
|
35
|
+
# numbers.
|
35
36
|
#
|
36
37
|
# Usage:
|
37
38
|
# - polygon.set_vertices([Snow::Vec3[-1, 0, 0], Snow::Vec3[0, -1, 0], Snow::Vec3[1, 1, 0]])
|
38
39
|
#
|
39
40
|
def set_vertices(vertices)
|
40
41
|
type_check(vertices, Array)
|
41
|
-
raise ArgumentError.new("set_vertices() expects an array of at least three
|
42
|
-
vertices.each
|
43
|
-
|
42
|
+
raise ArgumentError.new("set_vertices() expects an array of at least three 2D vectors") unless vertices.length >= 3
|
43
|
+
vertices.each do |v|
|
44
|
+
types_check(v, Snow::Vec2, Snow::Vec3, Snow::Vec4, Array)
|
45
|
+
if v.is_a?(Array)
|
46
|
+
raise ArgumentError.new("set_vertices() expects an array of at least three 2D vectors") unless v.length >= 2
|
47
|
+
v.each { |n| type_check(n, Numeric) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if @vertices.length < vertices.length
|
52
|
+
@vertices.concat(Array.new(vertices.length - @vertices.length) { Snow::Vec3.new })
|
53
|
+
elsif @vertices.length > vertices.length
|
54
|
+
@vertices.pop(@vertices.length - vertices.length)
|
55
|
+
end
|
56
|
+
|
57
|
+
vertices.each_index do |i|
|
58
|
+
@vertices[i].x = vertices[i][0]
|
59
|
+
@vertices[i].y = vertices[i][1]
|
60
|
+
@vertices[i].z = 0
|
61
|
+
end
|
44
62
|
end
|
45
63
|
|
46
64
|
##
|
@@ -9,57 +9,15 @@ module Gosling
|
|
9
9
|
# SnowMath gem to remain performant.
|
10
10
|
#
|
11
11
|
module Transformable
|
12
|
-
##
|
13
|
-
# Wraps Math.sin to produce rationals instead of floats. Common values are returned quickly from a lookup table.
|
14
|
-
#
|
15
|
-
def self.rational_sin(r)
|
16
|
-
type_check(r, Numeric)
|
17
|
-
|
18
|
-
r = r % (2 * Math::PI)
|
19
|
-
case r
|
20
|
-
when 0.0
|
21
|
-
0.to_r
|
22
|
-
when Math::PI / 2
|
23
|
-
1.to_r
|
24
|
-
when Math::PI
|
25
|
-
0.to_r
|
26
|
-
when Math::PI * 3 / 2
|
27
|
-
-1.to_r
|
28
|
-
else
|
29
|
-
Math.sin(r).to_r
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
##
|
34
|
-
# Wraps Math.cos to produce rationals instead of floats. Common values are returned quickly from a lookup table.
|
35
|
-
#
|
36
|
-
def self.rational_cos(r)
|
37
|
-
type_check(r, Numeric)
|
38
|
-
|
39
|
-
r = r % (2 * Math::PI)
|
40
|
-
case r
|
41
|
-
when 0.0
|
42
|
-
1.to_r
|
43
|
-
when Math::PI / 2
|
44
|
-
0.to_r
|
45
|
-
when Math::PI
|
46
|
-
-1.to_r
|
47
|
-
when Math::PI * 3 / 2
|
48
|
-
0.to_r
|
49
|
-
else
|
50
|
-
Math.cos(r).to_r
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
12
|
attr_reader :rotation
|
55
13
|
|
56
14
|
##
|
57
15
|
# Initializes this Transformable to have no transformations (identity matrix).
|
58
16
|
#
|
59
17
|
def initialize
|
60
|
-
@center = Snow::Vec3[0
|
61
|
-
@scale = Snow::Vec2[1
|
62
|
-
@translation = Snow::Vec3[0
|
18
|
+
@center = Snow::Vec3[0, 0, 1]
|
19
|
+
@scale = Snow::Vec2[1, 1]
|
20
|
+
@translation = Snow::Vec3[0, 0, 1]
|
63
21
|
reset
|
64
22
|
end
|
65
23
|
|
@@ -68,10 +26,10 @@ module Gosling
|
|
68
26
|
# matrix.
|
69
27
|
#
|
70
28
|
def reset
|
71
|
-
self.center = 0
|
72
|
-
self.scale = 1
|
73
|
-
self.rotation = 0
|
74
|
-
self.translation = 0
|
29
|
+
self.center = 0, 0
|
30
|
+
self.scale = 1, 1
|
31
|
+
self.rotation = 0
|
32
|
+
self.translation = 0, 0
|
75
33
|
end
|
76
34
|
|
77
35
|
##
|
@@ -258,6 +216,9 @@ module Gosling
|
|
258
216
|
#
|
259
217
|
def rotation=(radians)
|
260
218
|
type_check(radians, Numeric)
|
219
|
+
if radians.is_a?(Float)
|
220
|
+
raise ArgumentError.new("Expected a finite number, but received #{radians.inspect}!") unless radians.finite?
|
221
|
+
end
|
261
222
|
@rotation = radians
|
262
223
|
@rotate_is_dirty = @is_dirty = true
|
263
224
|
end
|
@@ -347,8 +308,8 @@ module Gosling
|
|
347
308
|
def self.transform_point(mat, v)
|
348
309
|
type_check(mat, Snow::Mat3)
|
349
310
|
type_check(v, Snow::Vec3)
|
350
|
-
result = mat * Snow::Vec3[v[0], v[1], 1
|
351
|
-
result[2] = 0
|
311
|
+
result = mat * Snow::Vec3[v[0], v[1], 1]
|
312
|
+
result[2] = 0
|
352
313
|
result
|
353
314
|
end
|
354
315
|
|
@@ -371,8 +332,8 @@ module Gosling
|
|
371
332
|
unless inverse_mat
|
372
333
|
raise "Unable to invert matrix: #{mat}!"
|
373
334
|
end
|
374
|
-
result = mat.inverse * Snow::Vec3[v[0], v[1], 1
|
375
|
-
result[2] = 0
|
335
|
+
result = mat.inverse * Snow::Vec3[v[0], v[1], 1]
|
336
|
+
result[2] = 0
|
376
337
|
result
|
377
338
|
end
|
378
339
|
|
@@ -397,18 +358,17 @@ module Gosling
|
|
397
358
|
def update_scale_matrix
|
398
359
|
return unless @scale_is_dirty || @scale_mat.nil?
|
399
360
|
@scale_mat ||= Snow::Mat3.new
|
400
|
-
@scale_mat[0] = @scale[0]
|
401
|
-
@scale_mat[4] = @scale[1]
|
361
|
+
@scale_mat[0] = @scale[0]
|
362
|
+
@scale_mat[4] = @scale[1]
|
402
363
|
@scale_is_dirty = false
|
403
364
|
end
|
404
365
|
|
405
366
|
def update_rotate_matrix
|
406
367
|
return unless @rotate_is_dirty || @rotate_mat.nil?
|
407
368
|
@rotate_mat ||= Snow::Mat3.new
|
408
|
-
@rotate_mat[0] =
|
409
|
-
@rotate_mat[1] =
|
410
|
-
@rotate_mat[3] =
|
411
|
-
@rotate_mat[4] = Transformable.rational_cos(@rotation)
|
369
|
+
@rotate_mat[4] = @rotate_mat[0] = Math.cos(@rotation)
|
370
|
+
@rotate_mat[1] = Math.sin(@rotation)
|
371
|
+
@rotate_mat[3] = -@rotate_mat[1]
|
412
372
|
@rotate_is_dirty = false
|
413
373
|
end
|
414
374
|
|
data/lib/gosling/version.rb
CHANGED