technohippy-Pongo 0.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.
- data/History.txt +17 -0
- data/Manifest.txt +52 -0
- data/README.txt +93 -0
- data/lib/pongo.rb +33 -0
- data/lib/pongo/abstract_collection.rb +135 -0
- data/lib/pongo/abstract_constraint.rb +25 -0
- data/lib/pongo/abstract_item.rb +93 -0
- data/lib/pongo/abstract_particle.rb +226 -0
- data/lib/pongo/angular_constraint.rb +108 -0
- data/lib/pongo/apengine.rb +157 -0
- data/lib/pongo/circle_particle.rb +36 -0
- data/lib/pongo/collision.rb +11 -0
- data/lib/pongo/collision_detector.rb +222 -0
- data/lib/pongo/collision_event.rb +23 -0
- data/lib/pongo/collision_resolver.rb +39 -0
- data/lib/pongo/composite.rb +52 -0
- data/lib/pongo/container/container.rb +7 -0
- data/lib/pongo/container/shoes_container.rb +14 -0
- data/lib/pongo/group.rb +166 -0
- data/lib/pongo/iforce.rb +7 -0
- data/lib/pongo/logger/logger.rb +21 -0
- data/lib/pongo/logger/shoes_logger.rb +15 -0
- data/lib/pongo/logger/standard_logger.rb +11 -0
- data/lib/pongo/rectangle_particle.rb +79 -0
- data/lib/pongo/renderer/renderer.rb +61 -0
- data/lib/pongo/renderer/shoes_renderer.rb +51 -0
- data/lib/pongo/renderer/tk_renderer.rb +73 -0
- data/lib/pongo/rim_particle.rb +53 -0
- data/lib/pongo/spring_constraint.rb +128 -0
- data/lib/pongo/spring_constraint_particle.rb +235 -0
- data/lib/pongo/util/interval.rb +13 -0
- data/lib/pongo/util/math_util.rb +27 -0
- data/lib/pongo/util/numeric_ext.rb +4 -0
- data/lib/pongo/util/vector.rb +133 -0
- data/lib/pongo/vector_force.rb +34 -0
- data/lib/pongo/wheel_particle.rb +103 -0
- metadata +90 -0
data/lib/pongo/iforce.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pongo
|
2
|
+
module Logger
|
3
|
+
class Logger
|
4
|
+
def print(message)
|
5
|
+
puts(message)
|
6
|
+
end
|
7
|
+
|
8
|
+
def error(message)
|
9
|
+
print("Error: #{message}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def warn(message)
|
13
|
+
print("Warn: #{message}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def info(message)
|
17
|
+
print("Info: #{message}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'pongo/abstract_particle'
|
2
|
+
require 'pongo/util/math_util'
|
3
|
+
|
4
|
+
module Pongo
|
5
|
+
class RectangleParticle < AbstractParticle
|
6
|
+
attr_accessor :extents, :axes, :radian
|
7
|
+
|
8
|
+
def initialize(x, y, width, height, options={})
|
9
|
+
options = {:rotation => 0.0, :fixed => false, :mass => 1.0, :elasticity => 0.3, :friction => 0.0}.update(options)
|
10
|
+
super(x, y, options[:fixed], options[:mass], options[:elasticity], options[:friction])
|
11
|
+
@extents = [width/2.0, height/2.0]
|
12
|
+
@axes = [Vector.new, Vector.new]
|
13
|
+
self.radian = options[:rotation]
|
14
|
+
end
|
15
|
+
|
16
|
+
# The rotation of the RectangleParticle in radians. For drawing methods you may
|
17
|
+
# want to use the <code>angle</code> property which gives the rotation in
|
18
|
+
# degrees from 0 to 360.
|
19
|
+
#
|
20
|
+
# Note that while the RectangleParticle can be rotated, it does not have angular
|
21
|
+
# velocity. In otherwords, during collisions, the rotation is not altered,
|
22
|
+
# and the energy of the rotation is not applied to other colliding particles.
|
23
|
+
def radian=(rad)
|
24
|
+
@radian = rad
|
25
|
+
set_axes(rad)
|
26
|
+
end
|
27
|
+
|
28
|
+
# The rotation of the RectangleParticle in degrees.
|
29
|
+
def angle
|
30
|
+
self.radian * MathUtil::ONE_EIGHTY_OVER_PI
|
31
|
+
end
|
32
|
+
alias degree angle
|
33
|
+
|
34
|
+
def angle=(deg)
|
35
|
+
self.radian = deg * MathUtil::PI_OVER_ONE_EIGHTY
|
36
|
+
end
|
37
|
+
alias degree= angle=
|
38
|
+
|
39
|
+
def width=(w)
|
40
|
+
@extents[0] = w / 2.0
|
41
|
+
end
|
42
|
+
|
43
|
+
def width
|
44
|
+
@extents[0] * 2.0
|
45
|
+
end
|
46
|
+
|
47
|
+
def height=(h)
|
48
|
+
@extents[1] = h / 2.0
|
49
|
+
end
|
50
|
+
|
51
|
+
def height
|
52
|
+
@extents[1] * 2.0
|
53
|
+
end
|
54
|
+
|
55
|
+
def projection(axis)
|
56
|
+
radius =
|
57
|
+
self.extents[0] * axis.dot(axes[0]).abs +
|
58
|
+
self.extents[1] * axis.dot(axes[1]).abs
|
59
|
+
|
60
|
+
c = @samp.dot(axis)
|
61
|
+
|
62
|
+
@interval.min = c - radius
|
63
|
+
@interval.max = c + radius
|
64
|
+
@interval
|
65
|
+
end
|
66
|
+
alias get_projection projection
|
67
|
+
|
68
|
+
def set_axes(rad)
|
69
|
+
s = Math.sin(rad)
|
70
|
+
c = Math.cos(rad)
|
71
|
+
|
72
|
+
self.axes[0].x = c
|
73
|
+
self.axes[0].y = s
|
74
|
+
self.axes[1].x = -s
|
75
|
+
self.axes[1].y = c
|
76
|
+
end
|
77
|
+
#alias axes= set_axes
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Pongo
|
2
|
+
module Renderer
|
3
|
+
class Renderer
|
4
|
+
def cleanup(item)
|
5
|
+
case item
|
6
|
+
when CircleParticle
|
7
|
+
cleanup_circle(item)
|
8
|
+
when RectangleParticle
|
9
|
+
cleanup_rectangle(item)
|
10
|
+
when WheelParticle
|
11
|
+
cleanup_wheel(item)
|
12
|
+
when SpringConstraint
|
13
|
+
cleanup_spring(item)
|
14
|
+
else
|
15
|
+
raise UnknownItemError.new(item.class.name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def cleanup_circle(item); end
|
19
|
+
def cleanup_rectangle(item); end
|
20
|
+
def cleanup_wheel(item); end
|
21
|
+
def cleanup_spring(item); end
|
22
|
+
|
23
|
+
def draw(item)
|
24
|
+
return unless item.visible?
|
25
|
+
case item
|
26
|
+
when CircleParticle
|
27
|
+
draw_circle(item)
|
28
|
+
when RectangleParticle
|
29
|
+
draw_rectangle(item)
|
30
|
+
when WheelParticle
|
31
|
+
draw_wheel(item)
|
32
|
+
when SpringConstraint
|
33
|
+
draw_spring(item)
|
34
|
+
when AbstractCollection
|
35
|
+
item.particles.each {|p| p.draw if p.always_redraw? or not p.fixed?}
|
36
|
+
item.constraints.each {|c| c.draw if c.always_redraw? or not c.fixed?}
|
37
|
+
else
|
38
|
+
raise UnknownItemError.new(item.class.name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
def draw_circle(item); end
|
42
|
+
def draw_rectangle(item); end
|
43
|
+
def draw_wheel(item); end
|
44
|
+
def draw_spring(item); end
|
45
|
+
|
46
|
+
def with(options, &block)
|
47
|
+
@shoes.nostroke if options[:nostroke]
|
48
|
+
@shoes.stroke(options[:stroke]) if options[:stroke]
|
49
|
+
@shoes.fill(options[:fill]) if options[:fill]
|
50
|
+
@shoes.transform(options[:transform]) if options[:transform]
|
51
|
+
@shoes.rotate(options[:rotate]) if options[:rotate]
|
52
|
+
block.call(@shoes)
|
53
|
+
@shoes.rotate(-options[:rotate]) if options[:rotate]
|
54
|
+
@shoes.transform(:corner) if options[:transform]
|
55
|
+
@shoes.fill(@shoes.black) if options[:fill]
|
56
|
+
@shoes.stroke(@shoes.black) if options[:stroke]
|
57
|
+
@shoes.stroke(@shoes.black) if options[:nostroke]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'pongo/renderer/renderer'
|
2
|
+
|
3
|
+
module Pongo
|
4
|
+
module Renderer
|
5
|
+
class ShoesRenderer < Renderer
|
6
|
+
attr_reader :shoes
|
7
|
+
|
8
|
+
def initialize(shoes)
|
9
|
+
@shoes = shoes
|
10
|
+
end
|
11
|
+
|
12
|
+
def draw_circle(item)
|
13
|
+
if item.user_data[:shape]
|
14
|
+
item.user_data[:shape].style(
|
15
|
+
:left => item.px - item.radius,
|
16
|
+
:top => item.py - item.radius
|
17
|
+
)
|
18
|
+
else
|
19
|
+
item.user_data[:shape] = @shoes.oval(
|
20
|
+
item.px - item.radius,
|
21
|
+
item.py - item.radius,
|
22
|
+
item.radius * 2
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias draw_wheel draw_circle
|
27
|
+
|
28
|
+
def draw_rectangle(item)
|
29
|
+
item.user_data[:shape].remove if item.user_data[:shape]
|
30
|
+
with(:transform => :center, :rotate => -item.angle) do
|
31
|
+
item.user_data[:shape] =
|
32
|
+
@shoes.rect(
|
33
|
+
:left => (item.px - item.width/2),
|
34
|
+
:top => (item.py - item.height/2),
|
35
|
+
:width => item.width,
|
36
|
+
:height => item.height
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def draw_spring(item)
|
42
|
+
if item.collidable?
|
43
|
+
draw_rectangle(item.scp)
|
44
|
+
else
|
45
|
+
item.user_data[:shape].remove if item.user_data[:shape]
|
46
|
+
item.user_data[:shape] = @shoes.line(item.p1.px, item.p1.py, item.p2.px, item.p2.py)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'pongo/renderer/renderer'
|
2
|
+
|
3
|
+
module Pongo
|
4
|
+
module Renderer
|
5
|
+
class TkRenderer < Renderer
|
6
|
+
def initialize(canvas)
|
7
|
+
@canvas = canvas
|
8
|
+
end
|
9
|
+
|
10
|
+
def draw_circle(item)
|
11
|
+
if item.user_data[:shape]
|
12
|
+
item.user_data[:shape].coords(
|
13
|
+
item.px - item.radius,
|
14
|
+
item.py - item.radius,
|
15
|
+
item.px + item.radius,
|
16
|
+
item.py + item.radius
|
17
|
+
)
|
18
|
+
else
|
19
|
+
item.user_data[:shape] =TkcOval.new(@canvas,
|
20
|
+
item.px - item.radius,
|
21
|
+
item.py - item.radius,
|
22
|
+
item.px + item.radius,
|
23
|
+
item.py + item.radius,
|
24
|
+
:fill => 'black'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
alias draw_wheel draw_circle
|
29
|
+
|
30
|
+
def draw_rectangle(item)
|
31
|
+
c = Vector.new(item.px, item.py)
|
32
|
+
x1 = -item.width/2
|
33
|
+
y1 = -item.height/2
|
34
|
+
x2 = item.width/2
|
35
|
+
y2 = item.height/2
|
36
|
+
p1 = Vector.new(x1, y1).rotate!(item.radian) + c
|
37
|
+
p2 = Vector.new(x1, y2).rotate!(item.radian) + c
|
38
|
+
p3 = Vector.new(x2, y2).rotate!(item.radian) + c
|
39
|
+
p4 = Vector.new(x2, y1).rotate!(item.radian) + c
|
40
|
+
if item.user_data[:shape]
|
41
|
+
item.user_data[:shape].coords(
|
42
|
+
p1.x, p1.y,
|
43
|
+
p2.x, p2.y,
|
44
|
+
p3.x, p3.y,
|
45
|
+
p4.x, p4.y
|
46
|
+
)
|
47
|
+
else
|
48
|
+
item.user_data[:shape] = TkcPolygon.new(@canvas,
|
49
|
+
p1.x, p1.y,
|
50
|
+
p2.x, p2.y,
|
51
|
+
p3.x, p3.y,
|
52
|
+
p4.x, p4.y,
|
53
|
+
:fill => 'black'
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def draw_spring(item)
|
59
|
+
if item.collidable?
|
60
|
+
draw_rectangle(item.scp)
|
61
|
+
else
|
62
|
+
if item.user_data[:shape]
|
63
|
+
item.user_data[:shape].coords(item.p1.px,
|
64
|
+
item.p1.py, item.p2.px, item.p2.py)
|
65
|
+
else
|
66
|
+
item.user_data[:shape] =TkcLine.new(@canvas, item.p1.px,
|
67
|
+
item.p1.py, item.p2.px, item.p2.py)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'pongo/util/math_util'
|
2
|
+
|
3
|
+
module Pongo
|
4
|
+
class RimParticle
|
5
|
+
attr_accessor :curr, :prev, :wr, :angular_velocity, :speed, :max_torque
|
6
|
+
|
7
|
+
# The RimParticle is really just a second component of the wheel model.
|
8
|
+
# The rim particle is simulated in a coordsystem relative to the wheel's
|
9
|
+
# center, not in worldspace.
|
10
|
+
#
|
11
|
+
# Origins of this code are from Raigan Burns, Metanet Software
|
12
|
+
def initialize(r, mt)
|
13
|
+
@curr = Vector.new(r, 0)
|
14
|
+
@prev = Vector.new(0, 0)
|
15
|
+
@speed = 0
|
16
|
+
@angular_velocity = 0
|
17
|
+
@max_torque = mt
|
18
|
+
@wr = r
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(dt)
|
22
|
+
@speed = MathUtil.max(-@max_torque, MathUtil.min(@max_torque, @speed + @angular_velocity))
|
23
|
+
|
24
|
+
# apply torque
|
25
|
+
# this is the tangent vector at the rim particle
|
26
|
+
dx = -@curr.y
|
27
|
+
dy = @curr.x
|
28
|
+
|
29
|
+
# normalize so we can scale by the rotational speed
|
30
|
+
len = Math.sqrt(dx * dx + dy * dy)
|
31
|
+
dx /= len
|
32
|
+
dy /= len
|
33
|
+
|
34
|
+
@curr.x += @speed * dx
|
35
|
+
@curr.y += @speed * dy
|
36
|
+
|
37
|
+
ox = @prev.x
|
38
|
+
oy = @prev.y
|
39
|
+
px = @prev.x = @curr.x
|
40
|
+
py = @prev.y = @curr.y
|
41
|
+
|
42
|
+
@curr.x += APEngine.damping * (px - ox)
|
43
|
+
@curr.y += APEngine.damping * (py - oy)
|
44
|
+
|
45
|
+
# hold the rim particle in place
|
46
|
+
clen = @curr.magnitude
|
47
|
+
diff = (clen - wr) / clen
|
48
|
+
|
49
|
+
@curr.x -= @curr.x * diff
|
50
|
+
@curr.y -= @curr.y * diff
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'pongo/abstract_constraint'
|
2
|
+
require 'pongo/spring_constraint_particle'
|
3
|
+
require 'pongo/util/math_util'
|
4
|
+
|
5
|
+
module Pongo
|
6
|
+
class SpringConstraint < AbstractConstraint
|
7
|
+
attr_accessor :p1, :p2, :rest_length, :collidable, :scp
|
8
|
+
|
9
|
+
def initialize(p1, p2, options={})
|
10
|
+
options = {:stiffness => 0.5, :collidable => false, :rect_height => 1, :rect_scale => 1, :scale_to_length => false}.update(options)
|
11
|
+
super(options[:stiffness])
|
12
|
+
@p1 = p1
|
13
|
+
@p2 = p2
|
14
|
+
check_particles_location
|
15
|
+
@rest_length = curr_length
|
16
|
+
set_collidable(options[:collidable], options[:rect_height], options[:rect_scale], options[:scale_to_length])
|
17
|
+
end
|
18
|
+
|
19
|
+
def radian
|
20
|
+
d = delta
|
21
|
+
Math.atan2(d.y, d.x)
|
22
|
+
end
|
23
|
+
|
24
|
+
def angle
|
25
|
+
radian * MathUtil::ONE_EIGHTY_OVER_PI
|
26
|
+
end
|
27
|
+
|
28
|
+
def center
|
29
|
+
(@p1.curr + @p2.curr) / 2
|
30
|
+
end
|
31
|
+
|
32
|
+
def rect_scale=(s)
|
33
|
+
if @scp
|
34
|
+
@scp.rect_scale = s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def rect_scale
|
39
|
+
@scp.rect_scale
|
40
|
+
end
|
41
|
+
|
42
|
+
def curr_length
|
43
|
+
@p1.curr.distance(@p2.curr)
|
44
|
+
end
|
45
|
+
|
46
|
+
def rect_height
|
47
|
+
@scp.rect_height
|
48
|
+
end
|
49
|
+
|
50
|
+
def rect_height=(h)
|
51
|
+
if @scp
|
52
|
+
@scp.rect_height = h
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def rest_length=(r)
|
57
|
+
raise ArgumentError.new('restLength must be greater than 0') if r <= 0
|
58
|
+
@rest_length = r
|
59
|
+
end
|
60
|
+
|
61
|
+
def collidable?
|
62
|
+
@collidable
|
63
|
+
end
|
64
|
+
|
65
|
+
def fixed_end_limit
|
66
|
+
@scp.fixed_end_limit
|
67
|
+
end
|
68
|
+
|
69
|
+
def fixed_end_limit=(f)
|
70
|
+
if @scp
|
71
|
+
@scp.fixed_end_limit = f
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_collidable(b, rect_height, rect_scale, scale_to_length)
|
76
|
+
if @collidable = b
|
77
|
+
@scp = SpringConstraintParticle.new(@p1, @p2, self, rect_height, rect_scale, scale_to_length)
|
78
|
+
else
|
79
|
+
@scp = nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def is_connected_to?(p)
|
84
|
+
[@p1, @p2].include? p
|
85
|
+
end
|
86
|
+
|
87
|
+
def fixed
|
88
|
+
@p1.fixed? and @p2.fixed?
|
89
|
+
end
|
90
|
+
alias fixed? fixed
|
91
|
+
|
92
|
+
def init
|
93
|
+
cleanup
|
94
|
+
if collidable?
|
95
|
+
@scp.init
|
96
|
+
else
|
97
|
+
init_display
|
98
|
+
end
|
99
|
+
draw
|
100
|
+
end
|
101
|
+
|
102
|
+
def resolve
|
103
|
+
return if fixed?
|
104
|
+
|
105
|
+
delta_length = curr_length
|
106
|
+
diff = (delta_length - rest_length) /
|
107
|
+
(delta_length * (@p1.inv_mass + @p2.inv_mass))
|
108
|
+
dmds = delta * (diff * stiffness)
|
109
|
+
|
110
|
+
@p1.curr.minus!(dmds * @p1.inv_mass)
|
111
|
+
@p2.curr.plus!( dmds * @p2.inv_mass)
|
112
|
+
end
|
113
|
+
|
114
|
+
def init_display
|
115
|
+
#raise NotImplementedError
|
116
|
+
end
|
117
|
+
|
118
|
+
def delta
|
119
|
+
@p1.curr - @p2.curr
|
120
|
+
end
|
121
|
+
|
122
|
+
def check_particles_location
|
123
|
+
if @p1.curr.x == @p2.curr.x and @p1.curr.y == @p2.curr.y
|
124
|
+
@p2.curr.x += 0.0001
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|