Skip to content

Commit 9161683

Browse files
committed
verlet integration
1 parent 2f63ee8 commit 9161683

File tree

4 files changed

+139
-0
lines changed

4 files changed

+139
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# frozen_string_literal: true
2+
# Chain link end as a ball
3+
class VerletBall
4+
include Propane::Proxy
5+
attr_reader :pos, :pos_old, :push, :radius, :x_bound, :y_bound
6+
7+
def initialize(pos, push, radius)
8+
@pos = pos
9+
@push = push
10+
@radius = radius
11+
@pos_old = Vec2D.new(pos.x, pos.y)
12+
# start motion
13+
@pos += push
14+
@x_bound = Boundary.new(radius, width - radius)
15+
@y_bound = Boundary.new(radius, height - radius)
16+
end
17+
18+
def run
19+
verlet
20+
render
21+
bounds_collision
22+
end
23+
24+
def adjust(vec)
25+
@pos += vec
26+
end
27+
28+
private
29+
30+
def verlet
31+
pos_temp = Vec2D.new(pos.x, pos.y)
32+
@pos += (pos - pos_old)
33+
@pos_old = pos_temp
34+
end
35+
36+
def render
37+
ellipse(pos.x, pos.y, radius, radius)
38+
end
39+
40+
def bounds_collision
41+
if x_bound.exclude? pos.x
42+
pos.x = constrain pos.x, x_bound.lower, x_bound.upper
43+
pos_old.x = pos.x
44+
pos.x = (pos.x <= radius)? pos.x + push.x : pos.x - push.x
45+
end
46+
return unless y_bound.exclude? pos.y
47+
pos.y = constrain pos.y, y_bound.lower, y_bound.upper
48+
pos_old.y = pos.y
49+
pos.y = (pos.y <= radius)? pos.y + push.y : pos.y - push.y
50+
end
51+
end
52+
53+
# We can easily create and test bounds in ruby
54+
Boundary = Struct.new(:lower, :upper) do
55+
def exclude? val
56+
true unless (lower..upper).cover? val
57+
end
58+
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
# Chain link end as a ball
3+
class VerletStick
4+
include Propane::Proxy
5+
attr_reader :ball_1, :ball_2, :stiffness, :len
6+
7+
def initialize(ball_1, ball_2, stiffness)
8+
@ball_1 = ball_1
9+
@ball_2 = ball_2
10+
@stiffness = stiffness
11+
@len = ball_1.pos.dist(ball_2.pos)
12+
end
13+
14+
def run
15+
render
16+
constrain_len
17+
end
18+
19+
private
20+
21+
def render
22+
begin_shape
23+
vertex(ball_1.pos.x, ball_1.pos.y)
24+
vertex(ball_2.pos.x, ball_2.pos.y)
25+
end_shape
26+
end
27+
28+
def constrain_len
29+
delta = ball_2.pos - ball_1.pos
30+
delta_length = delta.mag
31+
difference = ((delta_length - len) / delta_length)
32+
ball_1.adjust delta * (0.5 * stiffness * difference)
33+
ball_2.adjust delta * (-0.5 * stiffness * difference)
34+
end
35+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# frozen_string_literal: true
2+
require_relative 'lib/verlet_ball'
3+
require_relative 'lib/verlet_stick'
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env jruby -v -W2
2+
# frozen_string_literal: true
3+
#
4+
# Verlet Integration - ragdoll chain
5+
# after a sketch by Ira Greenberg
6+
#
7+
require 'propane'
8+
9+
class VerletIntegration < Propane::App
10+
load_library :verlet_chain
11+
PARTICLES = 5
12+
LINKS = PARTICLES - 1
13+
14+
attr_reader :sticks, :balls
15+
16+
def settings
17+
size(400, 400)
18+
end
19+
20+
def setup
21+
sketch_title 'Verlet Chain'
22+
ellipse_mode(RADIUS)
23+
rshape = 40
24+
tension = 0.05
25+
@sticks = []
26+
@balls = []
27+
(0..PARTICLES).each do |i|
28+
push = Vec2D.new(rand(3..6.5), rand(3..6.5))
29+
pos = Vec2D.new(width / 2 + (rshape * i), height / 2)
30+
balls << VerletBall.new(pos, push, 5.0)
31+
next if i.zero?
32+
sticks << VerletStick.new(balls[i - 1], balls[i], tension)
33+
end
34+
end
35+
36+
def draw
37+
background(255)
38+
sticks.each(&:run)
39+
balls.each(&:run)
40+
end
41+
end
42+
43+
VerletIntegration.new

0 commit comments

Comments
 (0)