Last active
April 7, 2022 23:31
-
-
Save villares/cf44d5d191a698564321ccba24e7a060 to your computer and use it in GitHub Desktop.
Noite de Processing - Simulações Físicas - com John e Alexandre https://www.youtube.com/watch?v=5u49PR9ICMg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# You'll need py5 -https://py5.ixora.io | |
# Also needs http://pymunk.org | |
# Similar to the previous one but with some monkey patching and changes the softbody grid construction | |
import py5 | |
import pymunk as pm | |
space = pm.Space() | |
space.gravity = (0, 600) | |
constraints = [] | |
current_poly = [] | |
stiffness = 1000 | |
damping = 100 | |
mass = 1 | |
def setup(): | |
py5.size(500, 500) | |
pm.Poly.draw = draw_poly # this is called "monkeypatching" | |
pm.Circle.draw = draw_circle | |
pm.Segment.draw = draw_segment | |
pm.DampedSpring.draw = draw_link | |
gap = 5 | |
walls = ( | |
((gap, py5.height - gap), (py5.width - gap, py5.height - gap)), | |
((gap, py5.height - gap), (gap, gap)), | |
((py5.width - gap, py5.height - gap), (py5.width - gap, gap)), | |
) | |
for pa, pb in walls: | |
wall = pm.Segment(space.static_body, pa, pb, 2) | |
wall.friction = 0.2 | |
space.add(wall) | |
def draw(): | |
py5.background(0, 100, 20) | |
py5.no_stroke() | |
py5.fill(255, 100) | |
for shp in space.shapes: | |
shp.draw() | |
py5.stroke(0) | |
for link in constraints: | |
link.draw() | |
if current_poly: | |
py5.fill(255, 0, 0, 100) | |
with py5.begin_closed_shape(): | |
py5.vertices(current_poly) | |
space.step(0.01) | |
def draw_poly(obj): | |
with py5.push_matrix(): | |
py5.translate(obj.body.position.x, obj.body.position.y) | |
py5.rotate(obj.body.angle) | |
pts = obj.get_vertices() | |
with py5.begin_closed_shape(): | |
py5.vertices(pts) | |
def draw_segment(obj): | |
with py5.push(): | |
py5.stroke(255) | |
py5.stroke_weight(obj.radius*2) | |
py5.line(obj.a.x, obj.a.y, obj.b.x, obj.b.y) | |
def draw_link(obj): | |
xa, ya = obj.a.position | |
xb, yb = obj.b.position | |
py5.line(xa, ya, xb, yb) | |
def draw_circle(obj): | |
py5.circle(obj.body.position.x, | |
obj.body.position.y, | |
obj.radius * 2) | |
def build_polybody(poly): | |
(xa, ya), (xb, yb) = min_max(poly) | |
centroid = (xa + xb) / 2, (ya + yb) / 2 | |
cx, cy = centroid | |
poly = [(x - cx, y - cy) for x, y in poly] | |
mass = poly_area(poly) * 0.1 | |
moi = pm.moment_for_poly(mass, poly) | |
body = pm.Body(mass, moi) | |
body.position = centroid | |
shp = pm.Poly(body, poly) | |
shp.friction = 0.2 | |
space.add(body, shp) | |
def build_softbody(x, y, cols, rows, ex, radius, mass): # ex é o espaçamento | |
ey = py5.sqrt(3) * 0.5 * ex | |
moi = pm.moment_for_circle(mass, 0, radius, (0, 0)) | |
bodies = [] | |
for j in range(rows): | |
p = j % 2 | |
for i in range(cols + p): | |
body = pm.Body(mass, moi) | |
body.position = (x - (p * 0.5 * ex) + i * ex, | |
y + j * ey) | |
shp = pm.Circle(body, radius) | |
shp.elasticity = 0.6 | |
shp.friction = 0.6 | |
space.add(body, shp) | |
bodies.append(body) | |
k = 0 | |
for j in range(rows - 1): | |
p = j % 2 | |
for i in range(cols + p): | |
jleft = jright = 1 | |
if i == 0 and p: | |
jleft = 0 | |
if i == cols and p: | |
jright = 0 | |
if jleft: | |
link = pm.DampedSpring(bodies[k], bodies[k + cols], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
if jright: | |
link = pm.DampedSpring(bodies[k], bodies[k + cols + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
if p or (not p and i < cols - 1): | |
link = pm.DampedSpring(bodies[k], bodies[k + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
k += 1 | |
p = rows % 2 | |
for i in range(cols - p): | |
link = pm.DampedSpring(bodies[k], bodies[k + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
k += 1 | |
def min_max(pts): | |
""" | |
Return two tuples with the most extreme coordinates, | |
resulting in "bounding box" corners. | |
""" | |
coords = tuple(zip(*pts)) | |
return tuple(map(min, coords)), tuple(map(max, coords)) | |
def poly_area(pts): | |
pts = list(pts) | |
area = 0.0 | |
for (ax, ay), (bx, by) in zip(pts, pts[1:] + [pts[0]]): | |
area += ax * by | |
area -= bx * ay | |
return abs(area) / 2.0 | |
def key_pressed(): | |
if py5.key == ' ': | |
for obj in reversed(space.shapes): | |
if not is_segment(obj): | |
space.remove(obj) | |
def mouse_pressed(): | |
if py5.mouse_button == py5.RIGHT: | |
build_softbody(py5.mouse_x, py5.mouse_y, 8, 4, 20, 10, mass) | |
def mouse_dragged(): | |
current_poly.append((py5.mouse_x, py5.mouse_y)) | |
def mouse_released(): | |
if len(current_poly) > 3: | |
build_polybody(current_poly[:]) | |
current_poly[:] = [] | |
def is_circle(obj): return isinstance(obj, pm.Circle) | |
def is_poly(obj): return isinstance(obj, pm.Poly) | |
def is_segment(obj): return isinstance(obj, pm.Segment) | |
py5.run_sketch() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# You'll need py5 - made to run in py5 imported mode, more info at https://py5.ixora.io | |
# Also needs http://pymunk.org | |
import pymunk as pm | |
space = pm.Space() | |
space.gravity = (0, 600) | |
shapes, bodies, constraints = [], [], [] | |
polys = [] | |
current_poly = [] | |
stiffness = 600 | |
damping = 20 | |
mass = 0.5 | |
def setup(): | |
size(500, 500) | |
build_softbody(50, 50, 4, 5, 20, 8, mass) | |
build_softbody(150, 50, 5, 5, 20, 8, mass) | |
build_softbody(50, 150, 6, 5, 20, 8, mass) | |
walls = ( | |
((0, height), (width, height)), | |
((0, height), (0, 0)), | |
((width, height), (width, 0)), | |
) | |
for pa, pb in walls: | |
wall = pm.Segment(space.static_body, pa, pb, 2) | |
space.add(wall) | |
def draw(): | |
background(0, 0, 100) | |
stroke(255) | |
for link in constraints: | |
xa, ya = link.a.position | |
xb, yb = link.b.position | |
line(xa, ya, xb, yb) | |
no_stroke() | |
fill(255, 100) | |
for b in bodies: | |
circle(b.position.x, b.position.y, 16) | |
for shp in polys: | |
draw_poly(shp) | |
if current_poly: | |
fill(255, 0, 0, 100) | |
with begin_closed_shape(): | |
vertices(current_poly) | |
space.step(0.01) | |
def draw_poly(obj): | |
push_matrix() | |
translate(obj.body.position.x, obj.body.position.y) | |
rotate(obj.body.angle) | |
pts = obj.get_vertices() | |
# begin_shape() | |
# for x, y in pts: | |
# vertex(x, y) | |
# end_shape(CLOSE) | |
with begin_closed_shape(): | |
vertices(pts) | |
pop_matrix() | |
def mouse_pressed(): | |
if mouse_button == RIGHT: | |
build_softbody(mouse_x, mouse_y, 4, 8, 20, 8, mass) | |
def mouse_dragged(): | |
current_poly.append((mouse_x, mouse_y)) | |
def mouse_released(): | |
if len(current_poly) > 3: | |
raw_poly = current_poly[:] | |
(xa, ya), (xb, yb) = min_max(raw_poly) | |
centroid = (xa + xb) / 2, (ya + yb) / 2 | |
cx, cy = centroid | |
poly = [(x - cx, y - cy) for x, y in raw_poly] | |
mass = poly_area(poly) * 0.1 | |
moi = pm.moment_for_poly(mass, poly) | |
body = pm.Body(mass, moi) | |
body.position = centroid | |
shp = pm.Poly(body, poly) | |
space.add(body, shp) | |
polys.append(shp) | |
current_poly[:] = [] | |
def build_softbody(x, y, col, row, ex, radius, mass): # ex é o espaçamento | |
ey = sqrt(3) * 0.5 * ex | |
moi = pm.moment_for_circle(mass, 0, radius, (0, 0)) | |
body_count = len(bodies) | |
for j in range(col): | |
p = j % 2 | |
for i in range(row + p): | |
body = pm.Body(mass, moi) | |
body.position = (x - (p * 0.5 * ex) + i * ex, | |
y + j * ey) | |
shp = pm.Circle(body, radius) | |
shp.elasticity = 0.6 | |
shp.friction = 0.8 | |
space.add(body, shp) | |
bodies.append(body) | |
shapes.append(shp) | |
k = body_count | |
for j in range(col - 1): | |
p = j % 2 | |
for i in range(row + p): | |
jleft = jright = 1 | |
if i == 0 and p: | |
jleft = 0 | |
if i == row and p: | |
jright = 0 | |
if jleft: | |
link = pm.DampedSpring(bodies[k], bodies[k + row], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
if jright: | |
link = pm.DampedSpring(bodies[k], bodies[k + row + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
if p or (not p and i < row - 1): | |
link = pm.DampedSpring(bodies[k], bodies[k + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
k += 1 | |
p = col % 2 | |
for i in range(row - p): | |
link = pm.DampedSpring(bodies[k], bodies[k + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
k += 1 | |
def min_max(points): | |
""" | |
Return two tuples with the most extreme coordinates, | |
resulting in "bounding box" corners. | |
""" | |
coords = tuple(zip(*points)) | |
return tuple(map(min, coords)), tuple(map(max, coords)) | |
def poly_area(pts): | |
pts = list(pts) | |
area = 0.0 | |
for (ax, ay), (bx, by) in zip(pts, pts[1:] + [pts[0]]): | |
area += ax * by | |
area -= bx * ay | |
return abs(area) / 2.0 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# You'll need py5 -https://py5.ixora.io | |
# Also needs http://pymunk.org | |
# this is the same as the previous sketch but using py5 "module mode" | |
import py5 | |
import pymunk as pm | |
space = pm.Space() | |
space.gravity = (0, 600) | |
shapes, bodies, constraints = [], [], [] | |
polys = [] | |
current_poly = [] | |
stiffness = 600 | |
damping = 20 | |
mass = 0.5 | |
def setup(): | |
py5.size(500, 500) | |
build_softbody(50, 50, 4, 5, 20, 8, mass) | |
build_softbody(150, 50, 5, 5, 20, 8, mass) | |
build_softbody(50, 150, 6, 5, 20, 8, mass) | |
walls = ( | |
((0, py5.height), (py5.width, py5.height)), | |
((0, py5.height), (0, 0)), | |
((py5.width, py5.height), (py5.width, 0)), | |
) | |
for pa, pb in walls: | |
wall = pm.Segment(space.static_body, pa, pb, 2) | |
space.add(wall) | |
def draw(): | |
py5.background(0, 0, 100) | |
py5.stroke(255) | |
for link in constraints: | |
xa, ya = link.a.position | |
xb, yb = link.b.position | |
py5.line(xa, ya, xb, yb) | |
py5.no_stroke() | |
py5.fill(255, 100) | |
for b in bodies: | |
py5.circle(b.position.x, b.position.y, 16) | |
for shp in polys: | |
draw_poly(shp) | |
if current_poly: | |
py5.fill(255, 0, 0, 100) | |
with py5.begin_closed_shape(): | |
py5.vertices(current_poly) | |
space.step(0.01) | |
def draw_poly(obj): | |
py5.push_matrix() | |
py5.translate(obj.body.position.x, obj.body.position.y) | |
py5.rotate(obj.body.angle) | |
pts = obj.get_vertices() | |
with py5.begin_closed_shape(): | |
py5.vertices(pts) | |
py5.pop_matrix() | |
def mouse_pressed(): | |
if py5.mouse_button == py5.RIGHT: | |
build_softbody(py5.mouse_x, py5.mouse_y, 4, 8, 20, 8, mass) | |
def mouse_dragged(): | |
current_poly.append((py5.mouse_x, py5.mouse_y)) | |
def mouse_released(): | |
if len(current_poly) > 3: | |
raw_poly = current_poly[:] | |
(xa, ya), (xb, yb) = min_max(raw_poly) | |
centroid = (xa + xb) / 2, (ya + yb) / 2 | |
cx, cy = centroid | |
poly = [(x - cx, y - cy) for x, y in raw_poly] | |
mass = poly_area(poly) * 0.1 | |
moi = pm.moment_for_poly(mass, poly) | |
body = pm.Body(mass, moi) | |
body.position = centroid | |
shp = pm.Poly(body, poly) | |
space.add(body, shp) | |
polys.append(shp) | |
current_poly[:] = [] | |
def build_softbody(x, y, col, row, ex, radius, mass): # ex é o espaçamento | |
ey = py5.sqrt(3) * 0.5 * ex | |
moi = pm.moment_for_circle(mass, 0, radius, (0, 0)) | |
body_count = len(bodies) | |
for j in range(col): | |
p = j % 2 | |
for i in range(row + p): | |
body = pm.Body(mass, moi) | |
body.position = (x - (p * 0.5 * ex) + i * ex, | |
y + j * ey) | |
shp = pm.Circle(body, radius) | |
shp.elasticity = 0.6 | |
shp.friction = 0.8 | |
space.add(body, shp) | |
bodies.append(body) | |
shapes.append(shp) | |
k = body_count | |
for j in range(col - 1): | |
p = j % 2 | |
for i in range(row + p): | |
jleft = jright = 1 | |
if i == 0 and p: | |
jleft = 0 | |
if i == row and p: | |
jright = 0 | |
if jleft: | |
link = pm.DampedSpring(bodies[k], bodies[k + row], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
if jright: | |
link = pm.DampedSpring(bodies[k], bodies[k + row + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
if p or (not p and i < row - 1): | |
link = pm.DampedSpring(bodies[k], bodies[k + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
k += 1 | |
p = col % 2 | |
for i in range(row - p): | |
link = pm.DampedSpring(bodies[k], bodies[k + 1], | |
(0, 0), (0, 0), | |
ex, stiffness, damping) | |
space.add(link) | |
constraints.append(link) | |
k += 1 | |
def min_max(pts): | |
""" | |
Return two tuples with the most extreme coordinates, | |
resulting in "bounding box" corners. | |
""" | |
coords = tuple(zip(*pts)) | |
return tuple(map(min, coords)), tuple(map(max, coords)) | |
def poly_area(pts): | |
pts = list(pts) | |
area = 0.0 | |
for (ax, ay), (bx, by) in zip(pts, pts[1:] + [pts[0]]): | |
area += ax * by | |
area -= bx * ay | |
return abs(area) / 2.0 | |
py5.run_sketch() | |
Thanks @hx2A! I'll try to incorporate your nice suggestions!
@villares , the context manager idea was your suggestion!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is great!
Note py5 now lets you use the
begin_closed_shape()
andpush_matrix()
methods as context managers. There's also avertices()
method. You can use these to simplify the code a bit in a few places. For example:The
vertices()
method was designed to accept numpy arrays as inputs, with one vertex in each row. It will also work for things like a list of tuples, etc.If the number of vertices is large, this will also be much faster.