Physics Engine
==============
Pre-computed 2D physics simulation that maps trajectories onto time-varying
VObject attributes. Physics bodies are stepped via semi-implicit Euler
integration at a configurable ``dt``, then positions are baked as time
functions so the SVG renderer can sample any frame.
.. admonition:: Example: Basic bouncing ball
:class: example
.. code-block:: python
from vectormation.objects import *
from vectormation._physics import PhysicsSpace
canvas = VectorMathAnim()
canvas.set_background()
space = PhysicsSpace(gravity=(0, 980), dt=1/120)
ball = Circle(r=20, cx=960, cy=100, fill='#58C4DD')
b = space.add_body(ball, mass=1, restitution=0.7)
space.add_wall(y=900) # floor
space.simulate(duration=5)
canvas.add_objects(ball)
canvas.browser_display(fps=60)
----
PhysicsSpace
------------
.. py:class:: PhysicsSpace(gravity=(0, 980), dt=1/120, start=0.0)
The simulation container. Manages bodies, walls, springs, and forces.
:param tuple gravity: Gravity vector in px/s\ :sup:`2`. Default ``(0, 980)``
points downward in SVG coordinates.
:param float dt: Simulation timestep in seconds.
:param float start: Animation start time (offsets baked trajectories).
.. py:method:: add_body(obj, mass=1.0, restitution=0.8, friction=0.0, radius=None, vx=0.0, vy=0.0, fixed=False, angle=0.0, angular_velocity=0.0, moment_of_inertia=None) -> Body
Register a VObject as a physics body and return a :py:class:`Body` handle.
If *radius* is ``None`` it is auto-detected from the object (e.g. ``Circle.r``).
Set *fixed=True* for immovable obstacles.
:param float angle: Initial rotation angle in radians.
:param float angular_velocity: Initial angular velocity (rad/s).
:param float moment_of_inertia: Rotational inertia (auto-computed if ``None``).
.. py:method:: add_wall(x=None, y=None, restitution=0.9, friction=1.0) -> Wall
Add an infinite axis-aligned wall. Specify *x* for a vertical wall or
*y* for a horizontal wall.
.. py:method:: add_walls(left=40, right=1880, top=40, bottom=1040, restitution=0.9)
Convenience method: add four walls forming a bounding box.
.. py:method:: add_spring(a, b, stiffness=0.5, rest_length=None, damping=0.02) -> Spring
Add a spring constraint between two bodies, or between a body and a
fixed ``(x, y)`` anchor point.
.. py:method:: add_force(func)
Add a global force function ``func(body, t) -> (fx, fy)`` that is
evaluated every timestep for every non-fixed body.
.. py:method:: simulate(duration=5.0)
Run the simulation for *duration* seconds and bake trajectories
onto each body's VObject. After calling this, each VObject will
have its position attributes set as time functions.
.. py:method:: add_drag(coefficient=0.01)
Add velocity-proportional drag to all bodies.
.. py:method:: add_attraction(target, strength=50000)
Add gravitational attraction towards a :py:class:`Body` or ``(x, y)`` point.
.. py:method:: add_repulsion(target, strength=50000, max_dist=500)
Add repulsion away from a :py:class:`Body` or ``(x, y)`` point.
.. py:method:: add_mutual_repulsion(strength=5000, max_dist=300)
Add pairwise repulsion between all bodies.
----
Body
----
.. py:class:: Body(obj, mass=1.0, restitution=0.8, friction=0.0, radius=None, vx=0.0, vy=0.0, fixed=False, angle=0.0, angular_velocity=0.0, moment_of_inertia=None)
A physics body wrapping a VObject. Created via
:py:meth:`PhysicsSpace.add_body`.
:param VObject obj: The visual object to move.
:param float mass: Mass in kg. Use ``math.inf`` for static bodies.
:param float restitution: Bounciness coefficient (0--1).
:param float friction: Friction coefficient applied during collisions.
:param float radius: Collision radius (auto-detected from ``Circle``/``Dot`` if ``None``).
:param float vx: Initial horizontal velocity (px/s).
:param float vy: Initial vertical velocity (px/s).
:param bool fixed: If ``True``, the body does not move.
.. py:attribute:: obj
The wrapped VObject.
.. py:attribute:: x
:type: float
Current x position (updated during simulation).
.. py:attribute:: y
:type: float
Current y position.
.. py:method:: apply_force(fx, fy)
Accumulate a force for the current simulation step.
.. py:method:: apply_torque(torque)
Accumulate a torque for the current simulation step.
----
Wall
----
.. py:class:: Wall(x=None, y=None, restitution=0.9)
An infinite axis-aligned wall for collision. Specify *x* for a vertical
wall or *y* for a horizontal wall.
:param float x: X-coordinate of a vertical wall.
:param float y: Y-coordinate of a horizontal wall.
:param float restitution: Bounciness coefficient.
----
Spring
------
.. py:class:: Spring(a, b, stiffness=0.5, rest_length=None, damping=0.02)
A spring constraint between two :py:class:`Body` instances or between a
body and a fixed ``(x, y)`` anchor.
:param a: First body or ``(x, y)`` anchor.
:param b: Second body or ``(x, y)`` anchor.
:param float stiffness: Spring constant *k* (N/px).
:param float rest_length: Natural length in pixels (uses initial distance if ``None``).
:param float damping: Damping coefficient.
.. admonition:: Example: Spring
:class: example
.. raw:: html
.. literalinclude:: ../../../examples/reference/ref_physics_spring.py
:language: python
----
Cloth
-----
.. py:class:: Cloth(x=560, y=200, width=800, height=500, cols=15, rows=10, pin_top=True, stiffness=2.0, color='#58C4DD', creation=0)
A 2D cloth simulation using a grid of particles connected by springs.
The top row can be pinned (fixed) to create a hanging curtain effect.
:param float x: Top-left x position.
:param float y: Top-left y position.
:param float width: Cloth width in pixels.
:param float height: Cloth height in pixels.
:param int cols: Number of columns in the particle grid.
:param int rows: Number of rows in the particle grid.
:param bool pin_top: Pin the top row of particles.
:param float stiffness: Spring constant for structural springs.
.. py:method:: simulate(duration=5.0)
Run the cloth simulation and bake all particle and line trajectories.
.. py:method:: objects()
Return a list of all VObjects (lines and dots) for adding to the canvas.
.. admonition:: Example: Cloth
:class: example
.. raw:: html
.. literalinclude:: ../../../examples/reference/ref_physics_cloth.py
:language: python
.. admonition:: Example: Bouncing balls
:class: example
.. raw:: html
.. literalinclude:: ../../../examples/reference/ref_physics_bounce.py
:language: python