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