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.
@@ -0,0 +1,235 @@
1
+ require 'pongo/circle_particle'
2
+ require 'pongo/rectangle_particle'
3
+ require 'pongo/util/math_util'
4
+ require 'pongo/util/vector'
5
+
6
+ module Pongo
7
+ class SpringConstraintParticle < RectangleParticle
8
+ attr_accessor :parent, :p1, :p2, :avg_velocity, :lamda, :scale_to_length, :rca, :rcb, :s, :rect_scale, :rect_height, :fixed_end_limit
9
+
10
+ def initialize(p1, p2, p, rect_height, rect_scale, scale_to_length)
11
+ super(0.0, 0.0, 0.0, 0.0, :fixed => false)
12
+
13
+ @p1 = p1
14
+ @p2 = p2
15
+
16
+ self.lamda = Vector.new
17
+ self.avg_velocity = Vector.new
18
+
19
+ @parent = p
20
+ @rect_scale = rect_scale
21
+ @rect_height = rect_height
22
+ @scale_to_length = scale_to_length
23
+
24
+ @fixed_end_limit = 0.0
25
+ @rca = Vector.new
26
+ @rcb = Vector.new
27
+ end
28
+
29
+ def mass
30
+ (@p1.mass * @p2.mass) / 2.0
31
+ end
32
+
33
+ def elasticity
34
+ (@p1.elasticity * @p2.elasticity) / 2.0
35
+ end
36
+
37
+ def friction
38
+ (@p1.friction * @p2.friction) / 2.0
39
+ end
40
+
41
+ def velocity
42
+ p1v = @p1.velocity
43
+ p2v = @p2.velocity
44
+ @avg_velocity.set_to((p1v.x + p2v.x) / 2.0, (p1v.y + p2v.y) / 2.0)
45
+ end
46
+
47
+ def init
48
+ #throw NotImplementedError
49
+ end
50
+
51
+ def draw
52
+ #throw NotImplementedError
53
+ end
54
+
55
+ def init_display
56
+ #throw NotImplementedError
57
+ end
58
+
59
+ def inv_mass
60
+ if @p1.fixed? and @p2.fixed?
61
+ 0
62
+ else
63
+ 1.0 / self.mass
64
+ end
65
+ end
66
+
67
+ def fixed?
68
+ @parent.fixed?
69
+ end
70
+ alias fixed fixed?
71
+
72
+ # called only on collision
73
+ def update_position
74
+ @curr.set_to(@parent.center)
75
+ self.width = @scale_to_length \
76
+ ? @parent.curr_length * @rect_scale \
77
+ : @parent.rest_length * @rect_scale
78
+ self.height = @rect_height
79
+ self.radian = @parent.radian
80
+ end
81
+
82
+ def resolve_collision(mtd, vel, n, d, o, p)
83
+ test_particle_events(p)
84
+ return if fixed? or not p.solid?
85
+
86
+ t = contact_point_param(p)
87
+ c1 = 1 -t
88
+ c2 = t
89
+
90
+ # if one is fixed then move the other particle the entire way out of
91
+ # collision. also, dispose of collisions at the sides of the scp. The higher
92
+ # the fixedEndLimit value, the more of the scp not be effected by collision.
93
+ if @p1.fixed?
94
+ return if c2 <= @fixed_end_limit
95
+ lamda.set_to(mtd.x / c2, mtd.y / c2)
96
+ @p2.curr.plus!(lamda)
97
+ @p2.velocity = vel
98
+ elsif @p2.fixed?
99
+ return if c1 <= @fixed_end_limit
100
+ lamda.set_to(mtd.x / c1, mtd.y / c1)
101
+ @p1.curr.plus!(lamda)
102
+ @p1.velocity = vel
103
+ else
104
+ # else both non fixed - move proportionally out of collision
105
+ denom = c1 * c1 + c2 * c2
106
+ return if denom == 0
107
+ lamda.set_to(mtd.x / denom, mtd.y / denom)
108
+
109
+ @p1.curr.plus!(lamda * c1)
110
+ @p2.curr.plus!(lamda * c2)
111
+
112
+ if t == 0.5
113
+ # if collision is in the middle of SCP set the velocity of both end
114
+ # particles
115
+ @p1.velocity = vel
116
+ @p2.velocity = vel
117
+ else
118
+ # otherwise change the velocity of the particle closest to contact
119
+ corr_particle = t < 0.5 ? @p1 : @p2
120
+ corr_particle.velocity = vel
121
+ end
122
+ end
123
+ end
124
+
125
+ # given point c, returns a parameterized location on this SCP. Note
126
+ # this is just treating the SCP as if it were a line segment (ab).
127
+ def closest_param_point(c)
128
+ ab = @p2.curr - @p1.curr
129
+ t = (ab.dot(c - @p1.curr)) / (ab.dot(ab))
130
+ MathUtil.clamp(t, 0.0, 1.0)
131
+ end
132
+
133
+ # returns a contact location on this SCP expressed as a parametric value in [0,1]
134
+ def get_contact_point_param(p)
135
+ if p.is_a?(CircleParticle)
136
+ closest_param_point(p.curr)
137
+ elsif p.is_a?(RectangleParticle)
138
+ # go through the sides of the colliding rectangle as line segments
139
+ shortest_index = 0
140
+ params_list = []
141
+ shortest_distance = Numeric::POSITIVE_INFINITY
142
+ 4.times do |i|
143
+ set_corners(p, i)
144
+
145
+ # check for closest points on SCP to side of rectangle
146
+ d = closest_pt_segment_segment
147
+ if d < shortest_distance
148
+ shortest_distance = d
149
+ shortest_index = i
150
+ params_list[i] = @s
151
+ end
152
+ params_list[shortest_index]
153
+ end
154
+ end
155
+ end
156
+ alias contact_point_param get_contact_point_param
157
+
158
+ def set_corners(r, i)
159
+ rx = r.curr.x
160
+ ry = r.curr.y
161
+
162
+ axes = r.axes
163
+ extents = r.extents
164
+
165
+ ae0_x = axes[0].x * extents[0]
166
+ ae0_y = axes[0].y * extents[0]
167
+ ae1_x = axes[1].x * extents[1]
168
+ ae1_y = axes[1].y * extents[1]
169
+
170
+ emx = ae0_x - ae1_x
171
+ emy = ae0_y - ae1_y
172
+ epx = ae0_x + ae1_x
173
+ epy = ae0_y + ae1_y
174
+
175
+ case i
176
+ when 0
177
+ # 0 and 1
178
+ @rca.x = rx - epx;
179
+ @rca.y = ry - epy;
180
+ @rcb.x = rx + emx;
181
+ @rcb.y = ry + emy;
182
+ when 1
183
+ # 1 and 2
184
+ @rca.x = rx + emx;
185
+ @rca.y = ry + emy;
186
+ @rcb.x = rx + epx;
187
+ @rcb.y = ry + epy;
188
+ when 2
189
+ # 2 and 3
190
+ @rca.x = rx + epx;
191
+ @rca.y = ry + epy;
192
+ @rcb.x = rx - emx;
193
+ @rcb.y = ry - emy;
194
+ when 3
195
+ # 3 and 0
196
+ @rca.x = rx - emx;
197
+ @rca.y = ry - emy;
198
+ @rcb.x = rx - epx;
199
+ @rcb.y = ry - epy;
200
+ end
201
+ end
202
+
203
+ # pp1-pq1 will be the SCP line segment on which we need parameterized s.
204
+ def closest_pt_segment_segment
205
+ pp1 = @p1.curr
206
+ pq1 = @p2.curr
207
+ pp2 = @rca
208
+ pq2 = @rcb
209
+
210
+ d1 = pq1 - pp1
211
+ d2 = pq2 - pp2
212
+ r = pp1 - pp2
213
+
214
+ a = d1.dot(d1)
215
+ e = d2.dot(d2)
216
+ f = d2.dot(r)
217
+
218
+ c = d1.dot(r)
219
+ b = d1.dot(d2)
220
+ denom = a * e - b * b
221
+
222
+ s = denom != 0.0 ? MathUtil.clamp((b * f - c * e) / denom, 0.0, 1.0) : 0.5
223
+ t = (b * s + f) / e
224
+
225
+ t, s = t < 0 \
226
+ ? [0.0, MathUtil.clamp(-c / a, 0.0, 1.0)] \
227
+ : [1.0, MathUtil.clamp((b - c) / a, 0.0, 1.0)]
228
+
229
+ c1 = pp1 + (d1 * s)
230
+ c2 = pp2 + (d2 * t)
231
+ c1mc2 = c1 - c2
232
+ c1mc2.dot(c1mc2)
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,13 @@
1
+ module Pongo
2
+ class Interval
3
+ attr_accessor :min, :max
4
+
5
+ def initialize(min=0, max=0)
6
+ @min, @max = min, max
7
+ end
8
+
9
+ def to_s
10
+ "#{@min} : #{@max}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ module Pongo
2
+ module MathUtil
3
+ ONE_EIGHTY_OVER_PI = 180.0 / Math::PI;
4
+ PI_OVER_ONE_EIGHTY = Math::PI / 180.0;
5
+
6
+ module_function
7
+
8
+ def clamp(n, min, max)
9
+ if n < min; min
10
+ elsif max < n; max
11
+ else ; n
12
+ end
13
+ end
14
+
15
+ def sign(val)
16
+ val < 0 ? -1 : 1
17
+ end
18
+
19
+ def max(v1, v2)
20
+ v1 < v2 ? v2 : v1
21
+ end
22
+
23
+ def min(v1, v2)
24
+ v1 < v2 ? v1 : v2
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ class Numeric
2
+ POSITIVE_INFINITY = 1.0 / 0
3
+ NEGATIVE_INFINITY = -1.0 / 0
4
+ end
@@ -0,0 +1,133 @@
1
+ module Pongo
2
+ class Vector
3
+ attr_accessor :x, :y
4
+
5
+ def initialize(px=0.0, py=0.0)
6
+ set_to(px, py)
7
+ end
8
+
9
+ def set_to(px, py=nil)
10
+ @x, @y = py ? [px.to_f, py.to_f] : [px.x, px.y]
11
+ self
12
+ end
13
+
14
+ def copy(v)
15
+ set_to(v.x, v.y)
16
+ end
17
+
18
+ def dup
19
+ Vector.new(@x, @y)
20
+ end
21
+
22
+ def dot(v)
23
+ @x * v.x + @y * v.y
24
+ end
25
+
26
+ def cross(v)
27
+ @x * v.y - @y * v.x
28
+ end
29
+
30
+ def +(v)
31
+ Vector.new(@x + v.x, @y + v.y)
32
+ end
33
+ alias plus +
34
+
35
+ def plus!(v)
36
+ @x += v.x
37
+ @y += v.y
38
+ self
39
+ end
40
+ alias plus_equals plus!
41
+
42
+ def -(v)
43
+ Vector.new(@x - v.x, @y - v.y)
44
+ end
45
+ alias minus -
46
+
47
+ def minus!(v)
48
+ @x -= v.x
49
+ @y -= v.y
50
+ self
51
+ end
52
+ alias minus_equals minus!
53
+
54
+ def mult(s)
55
+ Vector.new(@x * s, @y * s)
56
+ end
57
+
58
+ def mult!(s)
59
+ @x *= s
60
+ @y *= s
61
+ self
62
+ end
63
+ alias mult_equals mult!
64
+
65
+ def times(v)
66
+ Vector.new(x * v.x, y * v.y)
67
+ end
68
+
69
+ def *(s_or_v)
70
+ if s_or_v.is_a? Vector
71
+ times(s_or_v)
72
+ else
73
+ mult(s_or_v)
74
+ end
75
+ end
76
+
77
+ def /(s)
78
+ s = 0.0001 if s == 0
79
+ Vector.new(@x / s, @y / s)
80
+ end
81
+ alias div /
82
+
83
+ def div!(s)
84
+ s = 0.0001 if s == 0
85
+ @x /= s
86
+ @y /= s
87
+ self
88
+ end
89
+ alias div_equals div!
90
+
91
+ def magnitude
92
+ Math.sqrt(@x * @x + @y * @y)
93
+ end
94
+
95
+ def distance(v)
96
+ minus(v).magnitude
97
+ end
98
+
99
+ def normalize
100
+ if @x == 0 and @y == 0
101
+ mult(10000.0)
102
+ else
103
+ mult(1.0 / magnitude)
104
+ end
105
+ end
106
+
107
+ def normalize!
108
+ unless @x == 0 and @y == 0
109
+ mult!(1.0 / magnitude)
110
+ end
111
+ self
112
+ end
113
+
114
+ def to_s
115
+ "#{@x} : #{@y}"
116
+ end
117
+
118
+ def rotate(rad)
119
+ Vector.new(
120
+ @x * Math.cos(rad) - @y * Math.sin(rad),
121
+ @x * Math.sin(rad) + @y * Math.cos(rad)
122
+ )
123
+ end
124
+
125
+ def rotate!(rad)
126
+ @x, @y = [
127
+ @x * Math.cos(rad) - @y * Math.sin(rad),
128
+ @x * Math.sin(rad) + @y * Math.cos(rad)
129
+ ]
130
+ self
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,34 @@
1
+ module Pongo
2
+ # A force represented by a 2D vector.
3
+ class VectorForce < IForce
4
+ attr_accessor :fvx, :fvy, :value, :scale_mass
5
+
6
+ def initialize(use_mass, vx, vy)
7
+ @fvx = vx
8
+ @fvy = vy
9
+ @scale_mass = use_mass
10
+ @value = Vector.new(vx, vy)
11
+ end
12
+
13
+ def vx=(x)
14
+ @fvx = x
15
+ @value.x = x
16
+ end
17
+
18
+ def vy=(y)
19
+ @fvy = y
20
+ @value.y = y
21
+ end
22
+
23
+ def use_mass=(b)
24
+ @scale_mass = b
25
+ end
26
+
27
+ def get_value(invmass)
28
+ if @scale_mass
29
+ @value.set_to(@fvx * invmass, @fvy * invmass)
30
+ end
31
+ @value
32
+ end
33
+ end
34
+ end