from sympy import latex, S from perfect_physics import World, Circle, Wall, Timeline from perfect_physics._timeline import Collision def run_in_place(world, steps, target, fig_factor=1, clear=True): if clear: js.document.getElementById(target).innerHTML = '' world.run_in_place(steps, show=True, show_fun=lambda figure: display(figure, target=target, append=True), figsize=(19.2 / 4, 10.8 / 4 * fig_factor) ) def count_collisions(right_mass, target, show=False, font_scale=.5, clear=True): if clear: js.document.getElementById(target).innerHTML = '' show_fun=lambda figure: display(figure, target=target, append=True) world_width = 5 left = Circle(x=2, y=0, r=1, vx=0, vy=0, m=1) right = Circle(x=6 if right_mass > 1 else 5, y=0, r=2 if right_mass > 1 else 1, vx=-1, vy=0, m=right_mass) circle_list = [left, right] wall_list = [ Wall(x0=0, y0=0, x1=0, y1=1)] world = World(circle_list, wall_list, xlim=(-1, world_width + 1), ylim=(-1 - 1, 1 + 1)) count = 0 world.show(show=show, font_scale=font_scale) hint_ssca_list = [] timeline = Timeline() while True: ss_calist, hint_ssca_list = world._tick(timeline, hint_ssca_list, default_tick=0) world.show( show=show and ss_calist[0] is not S.Zero, font_scale=font_scale, show_fun=show_fun, ) world._tock(ss_calist, timeline) world.show(show=show, font_scale=font_scale) # print(f"x {timeline.events[-1]}") if not isinstance(timeline.events[-1],Collision): break count += 1 return count packages = ["matplotlib", "pandas", "seaborn", "cloudpickle", "perfect-physics==0.1.9"]
Carl M. Kadie

Perfect Physics: Live Demo

Perfect Physics is a Python physics simulator that uses computer algebra (SymPy) to do all calculations exactly. For details, read "Perfect, Infinite-Precision, Game Physics in Python" in Towards Data Science or watch the PyData Conference Presentation .

This page lets you run your own simulations in the browser. Just press a green arrow (and wait a while). If you like, you can change the examples completely and run arbitrary Python code.

The outputs shows snapshots at the next collision(s) with a "Clock". (Full animations are not yet available.)

The page uses PyScript. Its source code is simple.


Newton's Cradle

To run, click the triangle or press shift-enter. May take 10's of seconds to run.

Ideas: Change the number of balls or number of steps.

circle_list = [Circle(x=1, y=0, r=1, vx=1, vy=0, m=1)] for i in range(1, 5): circle_list.append(Circle(x=i * 2 + 4, y=0, r=1, vx=0, vy=0, m=1)) wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=20, y0=0, x1=20, y1=1)] world = World(circle_list, wall_list, xlim=(-1, 21), ylim=(-2, 2)) run_in_place(world, steps=5, target="newtonOutput", fig_factor=0.5)

Tennis Ball and Basketball

It prints the velocity of the tennis ball at the end.

Ideas: Change 'big_radius', the size of the basketball, or the number of steps.

big_radius = 10 world_width = 40 big = Circle(x=world_width // 2, y=0, r=big_radius, vx=1, vy=0, m=big_radius**3) little = Circle(x=big.x - big_radius - 1, y=0, r=1, vx=1, vy=0, m=1) circle_list = [big, little] wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=world_width, y0=0, x1=world_width, y1=1)] world = World(circle_list, wall_list, xlim=(-1, world_width + 1), ylim=(-big_radius - 1, big_radius + 1)) run_in_place(world, steps=2, target="tennisOutput") display(f"little vx is {little.vx} (approx. {float(little.vx):.2f})", target="tennisOutput", append=True)

Three Circles on a Line

It runs with two different seeds. This is based on an example from Prof. Edward A. Lee's Fundamental Limits of Cyber-Physical Systems Modeling.

Ideas: Change the mass of the right ball.

js.document.getElementById("onLineOutput").innerHTML = '' for seed in [0,1]: left = Circle(x=-3, y=0, r=1, vx=1, vy=0, m=1, id="left") middle = Circle(x=0, y=0, r=1, vx=0, vy=0, m=1, id="middle") right = Circle(x=4, y=0, r=2, vx=-1, vy=0, m=4, id="right") world = World([left, middle, right], rng=seed, xlim=(-10, 10), ylim=(-3, 3)) display(f"seed {seed}", target="onLineOutput", append=True) run_in_place(world, steps=3, target="onLineOutput", fig_factor=.66, clear=False)

Billiards

Ideas: Change the # of rows or number of steps.

rows = -2 # positive for a 1st ball of triangle seed = 0 world = World.billiards(rows=rows, rng=seed) run_in_place(world, steps=5, target="billiardsOutput")

Computing π with Collisions

According to a paper by G. Galperin, two videos by 3Blue1Brown, and a New York Times item, you can compute π by counting the number of collisions between two balls of different mass and a wall.

larger_mass = 1 # try 100 and 10_000 show = False count_collisions(larger_mass, target="piOutput", show=show)