Skip to content

Commit 98ea525

Browse files
committed
expand pbox2d examples
1 parent adb3e34 commit 98ea525

36 files changed

+1674
-50
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env jruby -v -W2
2+
require 'propane'
3+
require 'pbox2d'
4+
require_relative 'lib/surface'
5+
6+
# The Nature of Code
7+
# PBox2D example
8+
# An uneven surface
9+
class BumpySurfaceNoise < Propane::App
10+
attr_reader :srface, :box2d, :particles
11+
12+
def setup
13+
sketch_title 'Bumpy Surface Noise'
14+
# Initialize box2d physics and create the world
15+
@box2d = WorldBuilder.build(app: self, gravity: [0, -20])
16+
# to later set a custom gravity
17+
# box2d.gravity([0, -20])
18+
# Create the empty list
19+
@particles = []
20+
# Create the srface
21+
@srface = Surface.new(self)
22+
end
23+
24+
def draw
25+
# If the mouse is pressed, we make new particles
26+
# We must always step through time!
27+
background(138, 66, 54)
28+
# Draw the srface
29+
srface.display
30+
# NB ? reqd to call mouse_pressed value, else method gets called.
31+
particles << Particle.new(self, mouse_x, mouse_y, rand(2.0..6)) if mouse_pressed?
32+
# Draw all particles
33+
particles.each(&:display)
34+
# Particles that leave the screen, we delete them
35+
# (note they have to be deleted from both the box2d world and our list
36+
particles.reject!(&:done)
37+
# Just drawing the framerate to see how many particles it can handle
38+
fill(0)
39+
text(format('framerate: %d', frame_rate), 12, 16)
40+
end
41+
42+
def settings
43+
size(500, 300)
44+
smooth 4
45+
end
46+
end
47+
48+
BumpySurfaceNoise.new
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env jruby -v -W2
2+
require 'propane'
3+
require 'pbox2d'
4+
require 'forwardable'
5+
require_relative 'lib/custom_listener'
6+
require_relative 'lib/particle'
7+
require_relative 'lib/boundary'
8+
9+
# Holder for vector values
10+
Vect = Struct.new(:x, :y)
11+
# The sketch class
12+
class CollisionListening < Propane::App
13+
attr_reader :box2d, :particles, :wall
14+
15+
def setup
16+
sketch_title 'Collision Listening'
17+
@box2d = WorldBuilder.build(app: self)
18+
box2d.add_listener(CustomListener.new)
19+
@particles = []
20+
@wall = Boundary.new(box2d, Vect.new(width / 2, height - 5), Vect.new(width, 10))
21+
end
22+
23+
def draw
24+
background(255)
25+
particles << Particle.new(self, rand(width), 20, rand(4..8)) if rand < 0.1
26+
particles.each(&:display)
27+
particles.reject!(&:done)
28+
wall.display
29+
end
30+
31+
def settings
32+
size 400, 400
33+
end
34+
end
35+
36+
CollisionListening.new
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# The Nature of Code
2+
# Daniel Shiffman
3+
# http://natureofcode.com
4+
5+
CENTER ||= Java::ProcessingCore::PConstants::CENTER
6+
7+
# A fixed boundary class
8+
class Boundary
9+
extend Forwardable
10+
def_delegators(:@app, :box2d, :rect_mode, :rect, :fill, :stroke)
11+
# A boundary is a simple rectangle with x, y, width, and height
12+
attr_reader :x, :y, :w, :h
13+
14+
def initialize(x, y, w, h)
15+
@x, @y, @w, @h = x, y, w, h
16+
@app = $app
17+
# Define the polygon
18+
sd = PolygonShape.new
19+
# Figure out the box2d coordinates
20+
box2dw = box2d.scale_to_world(w / 2)
21+
box2dh = box2d.scale_to_world(h / 2)
22+
# We're just a box
23+
sd.setAsBox(box2dw, box2dh)
24+
# Create the body
25+
bd = BodyDef.new
26+
bd.type = BodyType::STATIC
27+
bd.position.set(box2d.processing_to_world(x, y))
28+
b = box2d.createBody(bd)
29+
# Attached the shape to the body using a Fixture
30+
b.create_fixture(sd, 1)
31+
end
32+
33+
# Draw the boundary, if it were at an angle we'd have to do something fancier
34+
def display
35+
fill(0)
36+
stroke(0)
37+
rect_mode(CENTER)
38+
rect(x, y, w, h)
39+
end
40+
end
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env jruby -v -W2
2+
require 'propane'
3+
require 'pbox2d'
4+
require 'forwardable'
5+
require_relative 'boundary'
6+
require_relative 'pair'
7+
require_relative 'particle'
8+
require_relative 'particle_system'
9+
10+
# The Nature of Code
11+
# Daniel Shiffman
12+
# http://natureofcode.com
13+
14+
# Example demonstrating distance joints
15+
# A bridge is formed by connected a series of particles with joints
16+
class DistanceJoint < Propane::App
17+
18+
attr_reader :box2d, :boundaries, :system
19+
20+
def settings
21+
size(640, 360)
22+
end
23+
24+
def setup
25+
sketch_title 'Distance Joint'
26+
# Initialize box2d physics and create the world
27+
@box2d = WorldBuilder.build(app: self)
28+
@system = ParticleSystem.new
29+
@boundaries = []
30+
# Add a bunch of fixed boundaries
31+
boundaries << Boundary.new(width / 4, height - 5, width / 2 - 50, 10)
32+
boundaries << Boundary.new(3 * width / 4, height - 50, width / 2 - 50, 10)
33+
end
34+
35+
def draw
36+
background(255)
37+
system.run
38+
# Display all the boundaries
39+
boundaries.each(&:display)
40+
fill(0)
41+
text('Click mouse to add connected particles.', 10, 20)
42+
end
43+
44+
def mouse_pressed
45+
system.add_pair(mouse_x, mouse_y)
46+
end
47+
end
48+
49+
DistanceJoint.new
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# The Nature of Code
2+
# Daniel Shiffman
3+
# http://natureofcode.com
4+
5+
# Series of Particles connected with distance joints
6+
class Pair
7+
extend Forwardable
8+
def_delegators(:@app, :box2d, :stroke, :line, :stroke_weight)
9+
attr_reader :p1, :p2, :len, :joint
10+
# Chain constructor
11+
def initialize(x, y)
12+
@app = $app
13+
@len = 32
14+
@p1 = Particle.new(x, y)
15+
@p2 = Particle.new(x + rand(-1..1.0), y + rand(-1..1.0))
16+
djd = DistanceJointDef.new
17+
# Connection between previous particle and this one
18+
djd.bodyA = p1.body
19+
djd.bodyB = p2.body
20+
# Equilibrium length
21+
djd.length = box2d.scale_to_world(len)
22+
# These properties affect how springy the joint is
23+
djd.frequencyHz = 3 # Try a value less than 5 (0 for no elasticity)
24+
djd.dampingRatio = 0.1 # Ranges between 0 and 1 (1 for no springiness)
25+
# Make the joint.
26+
@joint = box2d.world.create_joint(djd)
27+
end
28+
29+
def kill_bodies
30+
box2d.world.destroy_joint(joint)
31+
@joint = nil
32+
box2d.destroy_body(p1.body)
33+
box2d.destroy_body(p2.body)
34+
end
35+
36+
# Is the pair ready for deletion?
37+
def done?
38+
# Let's find the screen position of the particle
39+
pos1 = box2d.body_coord(p1.body)
40+
pos2 = box2d.body_coord(p2.body)
41+
# Is it off the screen?
42+
if (0..@app.width).include?(pos1.x) || (0..@app.width).include?(pos2.x)
43+
if (0..@app.height).include?(pos1.y) || (0..@app.height).include?(pos2.y)
44+
return false
45+
end
46+
end
47+
kill_bodies
48+
true
49+
end
50+
51+
def display
52+
pos1 = box2d.body_coord(p1.body)
53+
pos2 = box2d.body_coord(p2.body)
54+
stroke(0)
55+
stroke_weight(2)
56+
line(pos1.x, pos1.y, pos2.x, pos2.y)
57+
p1.display
58+
p2.display
59+
end
60+
end
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# The Nature of Code
2+
# Daniel Shiffman
3+
# http://natureofcode.com
4+
5+
# A circular particle
6+
class Particle
7+
extend Forwardable
8+
def_delegators(:@app, :fill, :stroke, :stroke_weight, :box2d, :height, :line,
9+
:push_matrix, :pop_matrix, :ellipse, :rotate, :translate)
10+
# We need to keep track of a Body and a radius
11+
attr_reader :body, :r
12+
13+
def initialize(x, y)
14+
@r = 8
15+
@app = $app
16+
# Define a body
17+
bd = BodyDef.new
18+
# Set its position
19+
bd.position = box2d.processing_to_world(x, y)
20+
bd.type = BodyType::DYNAMIC
21+
@body = box2d.world.createBody(bd)
22+
23+
# Make the body's shape a circle
24+
cs = CircleShape.new
25+
cs.m_radius = box2d.scale_to_world(r)
26+
27+
fd = FixtureDef.new
28+
fd.shape = cs
29+
# Parameters that affect physics
30+
fd.density = 1
31+
fd.friction = 0.01
32+
fd.restitution = 0.3
33+
34+
# Attach fixture to body
35+
body.createFixture(fd)
36+
body.setLinearVelocity(Vec2.new(rand(-5..5), rand(2..5)))
37+
end
38+
39+
# This function removes the particle from the box2d world
40+
def kill_body
41+
box2d.destroy_body(body)
42+
end
43+
44+
# Is the particle ready for deletion?
45+
def done
46+
# Let's find the screen position of the particle
47+
pos = box2d.body_coord(body)
48+
# Is it off the bottom of the screen?
49+
if pos.y > height + r * 2
50+
kill_body
51+
return true
52+
end
53+
false
54+
end
55+
56+
def display
57+
# We look at each body and get its screen position
58+
pos = box2d.body_coord(body)
59+
# Get its angle of rotation
60+
a = body.get_angle
61+
push_matrix
62+
translate(pos.x, pos.y)
63+
rotate(a)
64+
fill(127)
65+
stroke(0)
66+
stroke_weight(2)
67+
ellipse(0, 0, r * 2, r * 2)
68+
# Let's add a line so we can see the rotation
69+
line(0, 0, r, 0)
70+
pop_matrix
71+
end
72+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# run system with a single command
2+
module Runnable
3+
def run
4+
reject!(&:done?)
5+
each(&:display)
6+
end
7+
end
8+
9+
# A custom enumerable class, it is so easy in ruby
10+
class ParticleSystem
11+
include Enumerable, Runnable
12+
extend Forwardable
13+
def_delegators(:@pairs, :each, :reject!, :<<)
14+
15+
def initialize
16+
@pairs = []
17+
end
18+
19+
def add_pair(x, y)
20+
self << Pair.new(x, y)
21+
end
22+
end

external_library/gem/pbox2d/library/particles/lib/boundary.rb renamed to external_library/gem/pbox2d/lib/boundary.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
# Re-usable Boundary class
21
class Boundary
32
include Propane::Proxy
43
attr_reader :box2d, :b, :pos, :size, :a
5-
4+
65
def initialize(b2d, pos, sz, a = 0)
76
@box2d, @pos, @size, @a = b2d, pos, sz, a
87
# Define the polygon
@@ -22,7 +21,7 @@ def initialize(b2d, pos, sz, a = 0)
2221
b.create_fixture(sd, 1)
2322
end
2423

25-
# Draw the boundary, it doesn't move so we don't ask for location
24+
# Draw the boundary, it doesn't move so we don't have to ask the Body for location
2625
def display
2726
fill(0)
2827
stroke(0)
@@ -32,7 +31,7 @@ def display
3231
push_matrix
3332
translate(pos.x, pos.y)
3433
rotate(-a)
35-
rect(0, 0, size.x, size.y)
34+
rect(0, 0, size.x,size.y)
3635
pop_matrix
3736
end
3837
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# A Box class, note how to access class ParticleGroupDef in jruby
2+
# which is imported to PB module to avoid namespace clashes
3+
class Box
4+
def initialize(b2d, x, y)
5+
w = rand(1..3)
6+
h = rand(1..3)
7+
shape = PolygonShape.new
8+
pos = b2d.processing_to_world(x, y)
9+
shape.setAsBox(w, h, pos, 0)
10+
pd = PB::ParticleGroupDef.new
11+
pd.shape = shape
12+
b2d.world.create_particle_group(pd)
13+
end
14+
end
15+
16+

0 commit comments

Comments
 (0)