simvx.core.physics.nodes2d¶
Body + shape-carrier nodes for 2D physics.
The 2D sibling of :mod:~simvx.core.physics.nodes: the user-facing 2D
body/shape/area/joint node taxonomy backed by
- class:
~simvx.core.physics.world2d.Physics2DWorld. Every node extends- class:
~simvx.core.nodes_2d.node2d.Node2D(scalarworld_rotationin radians), resolves its 2D world via :func:resolve_world_2d, and rides the same SceneTree fixed-step plumbing as the 3D nodes.
These nodes reuse the dimension-agnostic pieces by import (:class:BodyMode,
- class:
PhysicsMaterial, :class:Bitmask, :class:Property, :class:Signal) and carry 2D-only state (one_way/one_way_normal, scalarspin). All of them are exported fromsimvx.core.
Module Contents¶
Classes¶
Node-level 2D collision-event payload for |
|
A |
|
Abstract base of every 2D node that owns a body in the physics world. |
|
A 2D physics body whose motion mode is a Property. |
|
A |
|
A 2D pure sensor zone (trigger): broadphase-driven overlap detection. |
|
A 2D force-field zone: an ADDITIVE gravity effector over the bodies it overlaps. |
|
Base class for the 2D constraint nodes (node-agnostic carriers). |
|
Weld two 2D bodies: lock their full relative transform (position + rotation). |
|
Pin two 2D bodies at a single world-space point, rotation free. |
|
Hinge two 2D bodies at :attr: |
|
Soft distance-spring between the two 2D body centres (compliant, not rigid). |
|
Slide |
Data¶
API¶
- simvx.core.physics.nodes2d.log¶
‘getLogger(…)’
- simvx.core.physics.nodes2d.__all__¶
[‘BodyMode’, ‘Contact2D’, ‘CollisionShape2D’, ‘PhysicsObject2D’, ‘PhysicsBody2D’, ‘CharacterBody2D’,…
- class simvx.core.physics.nodes2d.Contact2D[source]¶
Node-level 2D collision-event payload for
collided/separated.The 2D sibling of :class:
~simvx.core.physics.nodes.Contact. Node-typed and built by the tree’s dispatch from a node-agnosticContactEvent2D. The tree fillsotherwith the peer node and reorientsnormal/velocityso they always point toward the RECEIVING body.Distinct from the world-level :class:
~simvx.core.physics.world2d.SweepHit2D(a query/sweep result keyed by handle): this carries the resolved peer node.Attributes: other: The OTHER physics object involved in the collision. point: World-space contact point (
Vec2). normal: Unit normal oriented TOWARD the receiving body (the separating direction). Degenerate (Vec2(0)) onseparated. impulse: Normal impulse magnitude the solver applied this step.0.0onseparatedand on acollidedthe solver did not push apart.Noneiff the resolved backend cannot measure an applied impulse (it does not advertise :attr:~simvx.core.physics.capability.Capability.CONTACT_IMPULSE). impulse_estimate: The portable stand-in forimpulse, one formula on every backend: the impulse it would take to arrest the approach, from the pair’s masses and the difference of the two bodies’ LINEAR velocities. Always a number, and0.0onseparatedand for a pair that is not approaching. It is deliberately not computed fromvelocitybelow, whose at-point meaning would put each backend’s choice of manifold point into the number. See :attr:~simvx.core.physics.world.ContactEvent.impulse_estimate. velocity: Relative velocity ofotherw.r.t. the receiver AT THE CONTACT POINT, pre-solve (Vec2). Spin counts: a wheel skidding on the ground reports the speed of its tread, which its centre is not moving at. Degenerate (Vec2(0)) onseparated.- other: PhysicsObject2D¶
None
- point: simvx.core.math.Vec2¶
None
- normal: simvx.core.math.Vec2¶
None
- impulse: float | None¶
None
- impulse_estimate: float¶
None
- velocity: simvx.core.math.Vec2¶
None
- class simvx.core.physics.nodes2d.CollisionShape2D(**kwargs: object)[source]¶
Bases:
simvx.core.nodes_2d.node2d.Node2DA
Node2Dthat carries a :class:Shape2Dcollision-geometry resource.Geometry lives in a single :class:
Shape2Dresource (CircleShape2D/RectangleShape2D/ …), so the node stays open for new shape kinds. A node that only carries data: collision detection itself belongs to the physics world. It exists so a body can discover its geometry as a child node.Initialization
- shape: simvx.core.physics.shapes2d.Shape2D¶
‘Property(…)’
- build_shape(world: simvx.core.physics.world2d.Physics2DWorld) simvx.core.physics.world2d.ShapeHandle[source]¶
Build this node’s shape into an opaque backend shape handle.
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.PhysicsObject2D(**kwargs: object)[source]¶
Bases:
simvx.core.physics._pose_reconcile._PhysicsPoseReconcile,simvx.core.nodes_2d.node2d.Node2DAbstract base of every 2D node that owns a body in the physics world.
The 2D sibling of :class:
~simvx.core.physics.nodes.PhysicsObject3D; see it for the rationale. It owns the backendhandleand itsworld, the- Attr:
collision_layer/ :attr:collision_maskpair, the :attr:shapeconvenience collider and its resolution, the initial-pose build, the node-pose-to-body teleport, the :attr:collided/ :attr:separatedSignals and the exit-tree teardown. Subclasses own body CREATION only.
Initialization
- collision_layer: int¶
‘Bitmask(…)’
- collision_mask: int¶
‘Bitmask(…)’
- shape: simvx.core.physics.shapes2d.Shape2D | None¶
‘Property(…)’
- collided¶
‘Signal(…)’
- separated¶
‘Signal(…)’
- property handle: simvx.core.physics.world2d.BodyHandle | None[source]¶
This object’s opaque backend body handle, or
Noneif none was created.
- property world: simvx.core.physics.world2d.Physics2DWorld | None[source]¶
The :class:
Physics2DWorldthis object’s body was created in, orNone.
- on_exit_tree() None[source]¶
Retire from the tree’s handle->node map and destroy the backend body.
The 2D twin of
- Meth:
~simvx.core.physics.nodes.PhysicsObject3D.on_exit_tree: retired rather than dropped, so a peer’sseparated/body_exitedcan still name this body on the step after it goes.
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_enter_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.PhysicsBody2D(**kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.PhysicsObject2DA 2D physics body whose motion mode is a Property.
The 2D sibling of :class:
~simvx.core.physics.nodes.PhysicsBody3D: one concrete body node with a runtime-mutable :class:BodyModemodeknob (STATIC | KINEMATIC | DYNAMIC). :class:PhysicsObject2Downs the body lifecycle (resolve 2D world, build shape, unregister + destroy on exit); this class adds the mode-typed create, the mass/material knobs and the 2D-onlyone_way/one_way_normalProperties.velocityis aVec2accessor;spinis a scalar (2D angular is 1-DOF).Parenting, and which mode follows a moving ancestor. A STATIC body follows: move any ancestor and the collider goes with it, because nothing simulates a STATIC body and its pose is authored data. That is the mode to parent under a node whose motion is authoring rather than gameplay – an editor drag, a group positioned once at setup, a level chunk assembled under a common origin.
A DYNAMIC or KINEMATIC body does NOT follow: the simulation owns its pose, and an ancestor’s move would be erased by the next write-back anyway. So for a platform that must carry or push bodies at RUNTIME, do not parent them to it. Make the platform a KINEMATIC body and move that body itself, because a STATIC pose write carries no derived velocity and would slide riders off rather than transport them. This is a deliberate difference from engines that push the composed transform into every body type regardless of mode.
Initialization
- mode: simvx.core.physics.world2d.BodyMode¶
‘Property(…)’
- mass: float¶
‘Property(…)’
- material: simvx.core.physics.material.PhysicsMaterial¶
‘Property(…)’
- linear_damping: float¶
‘Property(…)’
- angular_damping: float¶
‘Property(…)’
- gravity_scale: float¶
‘Property(…)’
- continuous: bool¶
‘Property(…)’
- can_sleep: bool¶
‘Property(…)’
- one_way: bool¶
‘Property(…)’
- one_way_normal: simvx.core.math.Vec2¶
‘Property(…)’
- wake() None[source]¶
Wake this body now, whatever its sleep timer had reached.
Rarely needed, because every write that changes what the solver reads already wakes the body. A no-op when the body is awake, when it is STATIC or KINEMATIC (never asleep), and when inert (not in tree / no body).
- sleep() None[source]¶
Put this body to sleep now, without waiting for it to settle.
Freezes it where it is, still a full collider, until something wakes it. A no-op when the body is STATIC or KINEMATIC, when :attr:
can_sleepis False, when inert, and on a backend that does not advertise- Attr:
~simvx.core.physics.capability.Capability.SLEEP.
- property velocity: simvx.core.math.Vec2[source]¶
Live linear velocity (
Vec2), read/written straight to the physics world.Runtime sim state, NOT a serialized :class:
Property. ReturnsVec2()when inert (not in tree / no body). The setter preserves the current angular velocity (:attr:spin).
- property spin: float[source]¶
Live scalar angular velocity (radians/s, CCW positive), written to the physics world.
Companion to :attr:
velocity; same live-state, never-serialized rules. Returns0.0when inert. The setter preserves linear velocity.
- push(impulse: simvx.core.math.Vec2 | collections.abc.Sequence[float], *, at: simvx.core.math.Vec2 | collections.abc.Sequence[float] | None = None) None[source]¶
Apply an instantaneous linear impulse NOW (DYNAMIC-only, inert otherwise).
atis a world-space point; its offsetr = at - centreadds a scalar angular impulse via the 2D cross product.
- spin_up(angular_impulse: float) None[source]¶
Apply an instantaneous SCALAR angular impulse (DYNAMIC-only, inert otherwise).
Applied via the body’s real inverse moment of inertia (the 2D backend has a per-shape scalar moment, unlike the 3D
inverse_massstand-in).
- add_force(force: simvx.core.math.Vec2 | collections.abc.Sequence[float], *, at: simvx.core.math.Vec2 | collections.abc.Sequence[float] | None = None) None[source]¶
Accumulate a continuous force, applied during the NEXT fixed step.
Auto-cleared each step (re-call per
on_fixed_updateto sustain). DYNAMIC-only, inert otherwise.atadds a scalar torquecross(r, force).
- add_torque(torque: float) None[source]¶
Accumulate a continuous SCALAR torque for the NEXT fixed step.
Auto-cleared each step like :meth:
add_force. DYNAMIC-only, inert otherwise. Applied via the real inverse moment of inertia.
- move_and_collide(velocity: simvx.core.math.Vec2 | collections.abc.Sequence[float], dt: float = 1.0) simvx.core.physics.world2d.SweepHit2D | None[source]¶
Move by
velocity * dt, stop at the first contact, sync, return it.Meaningful for
mode == KINEMATIC. Sweeps the body’s shape and reports the first blocker as a- Class:
~simvx.core.physics.world2d.SweepHit2D. After the sweep the node’s transform is synced SYNCHRONOUSLY from the simulated body. ReturnsNoneon no contact or when inert.
A sweep is a geometric query, not a solver pass, so the result carries the blocker’s handle, contact point, separating normal and guaranteed-clear distance, and no impulse: there is no solver lambda to report.
- collision_layer: int¶
‘Bitmask(…)’
- collision_mask: int¶
‘Bitmask(…)’
- shape: simvx.core.physics.shapes2d.Shape2D | None¶
‘Property(…)’
- collided¶
‘Signal(…)’
- separated¶
‘Signal(…)’
- property handle: simvx.core.physics.world2d.BodyHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.CharacterBody2D(**kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.PhysicsObject2DA
KINEMATIC2D physics body with a swept movement helper.The 2D sibling of :class:
~simvx.core.physics.nodes.CharacterBody3D: an ordinary kinematic body in the same body table, visible to raycasts, shape queries, areas and the contact stream, collided with and rested on by dynamic bodies, and blocking another character when their layers and masks mutually opt in. :meth:move_and_slideis the only thing that distinguishes it. It never carries a body resting on it, but it does shove one: at the default- Attr:
push_factorof1.0a blocking dynamic body takes the arrest impulse of the pair, so it leaves at no more than the speed the character was walking, scaled by the mass ratio. Set the factor to0.0and a dynamic body stops the sweep exactly as a static one does. The contacts it resolved are handed back in- Attr:
collisionseither way, so game code can apply its own impulses.
It is deliberately NOT registered for the dynamic auto bulk-sync;
- Meth:
move_and_slidesyncs the node transform synchronously.- Attr:
velocityis a plain instance attribute, deliberately NOT the world-backed :attr:PhysicsBody2D.velocityproperty: the simulated body’s velocity must stay zero, or the world’s integrator would re-apply the motion- Meth:
move_and_slidehas already performed.
Initialization
- slope_limit: float¶
‘Property(…)’
- step_height: float¶
‘Property(…)’
- max_slides: int¶
‘Property(…)’
- skin_width: float¶
‘Property(…)’
- mass: float¶
‘Property(…)’
- push_factor: float¶
‘Property(…)’
- move_and_slide(dt: float) None[source]¶
Collide-and-slide by
self.velocity * dt, then sync the transform.Runs the shared policy in :mod:
simvx.core.physics.slideagainst the simulated body. Writes the deflected post-slide velocity back to :attr:velocity, caches floor/wall/ceiling state + :attr:floor_normal+- Attr:
collisions, shoves each non-floor blocking contact if- Attr:
push_factoris non-zero, and syncs the node’s transform SYNCHRONOUSLY. No-op if inert (no collider).
- collision_layer: int¶
‘Bitmask(…)’
- collision_mask: int¶
‘Bitmask(…)’
- shape: simvx.core.physics.shapes2d.Shape2D | None¶
‘Property(…)’
- collided¶
‘Signal(…)’
- separated¶
‘Signal(…)’
- property handle: simvx.core.physics.world2d.BodyHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- on_exit_tree() None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.Area2D(**kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.PhysicsObject2DA 2D pure sensor zone (trigger): broadphase-driven overlap detection.
The 2D sibling of :class:
~simvx.core.physics.nodes.Area3D. Owns a SENSOR body (a flag, not a separate class): it participates in the broadphase but is excluded from collision resolution. Detection is ONE-DIRECTIONAL (the area sees another body iffarea.collision_mask & other.collision_layer). Overlap edges arrive as a buffered, deferred event stream drained by the tree; the live overlap sets are maintained from those edges (never a per-frame tree scan).Geometry follows the standard order (
shapeProperty if set, else firstCollisionShape2Dchild, else inert). The sensor body is STATIC.As in 3D, a STATIC body is reported only where
- Attr:
~simvx.core.physics.capability.Capability.SENSOR_DETECTS_STATICis advertised, which is both 2D backends: pymunk forms no static-vs-static pair, so the adapter holds every sensor KINEMATIC instead, which also lets anArea2Dthere report anotherArea2D.
Initialization
- monitoring: bool¶
‘Property(…)’
- shape: simvx.core.physics.shapes2d.Shape2D | None¶
‘Property(…)’
- body_entered¶
‘Signal(…)’
- body_exited¶
‘Signal(…)’
- area_entered¶
‘Signal(…)’
- area_exited¶
‘Signal(…)’
- get_overlapping_bodies(*, group=None, type=None)[source]¶
Bodies currently overlapping this area (live, as of the last step).
A peer destroyed mid-overlap is dropped from the maintained set by the overlap
EXITthe seam reports for it on the step after it goes. Detached peers (handle is None) are also filtered here, covering the one step before that event lands.The optional filters narrow the result without a tree scan: they test the already-maintained overlap set. Both are ANDed when given:
Args: group: When set, keep only bodies that belong to this SceneTree group (
body.is_in_group(group)). The canonical “what of kind X am I touching?” query (e.g.area.get_overlapping_bodies(group="mobs")). type: When set, keep only bodies that are instances of this :class:PhysicsObject2Dsubclass (isinstance(body, type)), and the result is typed as a list of THAT class. A character is an ordinary kinematic body, so it appears here like any other; passtype=PhysicsBody2Dwhen the caller needs the force/mass API a character does not have.Returns: The overlapping bodies (live, detached peers filtered) matching every supplied filter, in arbitrary order.
- get_overlapping_areas() list[simvx.core.physics.nodes2d.Area2D][source]¶
Other areas currently overlapping this area (live, as of the last step).
- collision_layer: int¶
‘Bitmask(…)’
- collision_mask: int¶
‘Bitmask(…)’
- collided¶
‘Signal(…)’
- separated¶
‘Signal(…)’
- property handle: simvx.core.physics.world2d.BodyHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.GravityArea2D(**kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.Area2DA 2D force-field zone: an ADDITIVE gravity effector over the bodies it overlaps.
The 2D sibling of :class:
~simvx.core.physics.nodes.GravityArea3D. Inherits the entire sensor mechanism unchanged and adds an :meth:on_fixed_updatehandler that, each fixed step, applies a field (additive on top of world gravity) to every DYNAMIC overlapping body. Two independent SUMMING components:Directional (:attr:
gravity): a uniform acceleration (Vec2) applied regardless of body mass, exactly like world gravity.Point (:attr:
point_gravity/ :attr:point_strength): a CONSTANT acceleration of magnitude :attr:point_strengthtoward the area centre (:attr:world_position).
The acceleration becomes a force via
add_force(mass * accel)(the integrator divides by mass, so the net effect is mass-INDEPENDENT, like gravity).add_forceis auto-cleared each step, so the field is freshly re-applied every fixed step and consumed once by the followingworld.step.Initialization
- gravity: simvx.core.math.Vec2¶
‘Property(…)’
- point_gravity: bool¶
‘Property(…)’
- point_strength: float¶
‘Property(…)’
- on_fixed_update(dt: float) None[source]¶
Apply the additive gravity field to every DYNAMIC overlapping body.
Runs BEFORE
world.step(the tree drives nodeon_fixed_updatefirst, then steps the worlds), accumulating a fresh force consumed by that step.dtis accepted to match the hook signature but not used to scale (add_forceis a continuous force, not an impulse).A :class:
CharacterBody2Doverlapping the area IS in the overlap set (it is an ordinary kinematic body) but is never force-driven: it is not a- Class:
PhysicsBody2Dand so has no :meth:~PhysicsBody2D.add_force, and the :attr:~CharacterBody2D.massit does carry is read only by its own push arithmetic, never by an integrator. It is position-driven by- Meth:
CharacterBody2D.move_and_slideinstead. Thetype=filter is what excludes it.
- monitoring: bool¶
‘Property(…)’
- shape: simvx.core.physics.shapes2d.Shape2D | None¶
‘Property(…)’
- body_entered¶
‘Signal(…)’
- body_exited¶
‘Signal(…)’
- area_entered¶
‘Signal(…)’
- area_exited¶
‘Signal(…)’
- get_overlapping_bodies(*, group=None, type=None)¶
- get_overlapping_areas() list[simvx.core.physics.nodes2d.Area2D]¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- collision_layer: int¶
‘Bitmask(…)’
- collision_mask: int¶
‘Bitmask(…)’
- collided¶
‘Signal(…)’
- separated¶
‘Signal(…)’
- property handle: simvx.core.physics.world2d.BodyHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.Joint2D(*, body_a: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, body_b: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, **kwargs: object)[source]¶
Bases:
simvx.core.nodes_2d.node2d.Node2DBase class for the 2D constraint nodes (node-agnostic carriers).
The 2D sibling of :class:
~simvx.core.physics.nodes.Joint3D. Constrains two- Class:
PhysicsBody2Dinstances in the 2D world; a thin carrier that owns the constraint lifecycle (create on enter-tree, remove on exit-tree). Body references (:attr:body_a/ :attr:body_b) are PLAIN instance attributes, set programmatically or via the constructor.
Same resolution rules as the 3D base: a missing body is a soft-inert no-op; a cross-world (or missing-world) pair after handles resolve is a
ValueError. ALWAYS add a joint AFTER both of its bodies (or re-enter it).Initialization
- property joint: simvx.core.physics.world2d.JointHandle | None[source]¶
This joint’s opaque backend handle, or
Noneif inert / not created.
- property world: simvx.core.physics.world2d.Physics2DWorld | None[source]¶
The :class:
Physics2DWorldthis joint was created in, orNone.
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.FixedJoint2D(*, body_a: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, body_b: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, **kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.Joint2DWeld two 2D bodies: lock their full relative transform (position + rotation).
Captures the current relative pose at enter-tree, in
body_a’s frame, and holds it there: the two bodies move as one rigid assembly, and the assembly swings round whenbody_aturns. No anchor Properties.Initialization
- property joint: simvx.core.physics.world2d.JointHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.PinJoint2D(*, body_a: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, body_b: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, **kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.Joint2DPin two 2D bodies at a single world-space point, rotation free.
The two bodies cannot separate at :attr:
anchorbut rotate freely about it, so a chain of pins is a rope and a pin to a STATIC body is a pendulum. The anchor is captured into each body’s own frame at enter-tree, so a pin on a spinning body orbits with it.Built-in backend caveat: convergence is a few sequential-impulse iterations, so a loaded chain sags and a hard yank stretches a link before the solver pulls it straight; the pymunk backend hangs the same chain on its rest length. See
docs/core/physics_backends.mdfor the measured sag.Initialization
- anchor: simvx.core.math.Vec2¶
‘Property(…)’
- property joint: simvx.core.physics.world2d.JointHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.HingeJoint2D(*, body_a: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, body_b: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, **kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.Joint2DHinge two 2D bodies at :attr:
anchor(no axis: 2D rotation is 1-DOF).2D rotation is 1-DOF, so a 2D hinge has no
axisargument: this tier it is a pin atanchor(motors / angular limits are a follow-on).Initialization
- anchor: simvx.core.math.Vec2¶
‘Property(…)’
- property joint: simvx.core.physics.world2d.JointHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.SpringJoint2D(*, body_a: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, body_b: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, **kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.Joint2DSoft distance-spring between the two 2D body centres (compliant, not rigid).
Pulls the two body centres toward :attr:
rest_lengthapart with- Attr:
stiffness(N/m) and :attr:damping(N*s/m). Intentionally compliant.
Initialization
- rest_length: float¶
‘Property(…)’
- stiffness: float¶
‘Property(…)’
- damping: float¶
‘Property(…)’
- property joint: simvx.core.physics.world2d.JointHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.physics.nodes2d.GrooveJoint2D(*, body_a: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, body_b: simvx.core.physics.nodes2d.PhysicsBody2D | None = None, **kwargs: object)[source]¶
Bases:
simvx.core.physics.nodes2d.Joint2DSlide
body_b’s anchor along a groove (line segment) fixed onbody_a.The 2D-only pymunk-native slider-on-a-line constraint (no 3D equivalent).
- Attr:
groove_a/ :attr:groove_bare body-local points inbody_a’s frame defining the groove segment; :attr:anchor_bis a body-local point inbody_b’s frame.body_b’s anchor is held on that segment: it slides freely ALONG the groove and is pinned PERPENDICULAR to it, clamped between the endpoints.
The groove turns with
body_aand the anchor withbody_b: both are held in the frames they are declared in.There is no slide motor on any backend: drive the slider with
apply_forceonbody_b. The endpoint stop differs in firmness: pymunk keeps the anchor on the groove under loads that push the built-in solver’s slider past the end and leave it there, the built-in stop being the same soft positional push its other joints use.docs/core/physics_backends.mdhas the measurement.Initialization
- groove_a: simvx.core.math.Vec2¶
‘Property(…)’
- groove_b: simvx.core.math.Vec2¶
‘Property(…)’
- anchor_b: simvx.core.math.Vec2¶
‘Property(…)’
- property joint: simvx.core.physics.world2d.JointHandle | None¶
- property world: simvx.core.physics.world2d.Physics2DWorld | None¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- z_index¶
‘Property(…)’
- z_as_relative¶
‘Property(…)’
- render_layer¶
‘Property(…)’
- set_render_layer(index: int, enabled: bool = True) None¶
- is_on_render_layer(index: int) bool¶
- property absolute_z_index: int¶
- property rotation_degrees: float¶
- property world_position: simvx.core.math.types.Vec2¶
- property world_rotation: float¶
- property world_scale: simvx.core.math.types.Vec2¶
- property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]¶
- property forward: simvx.core.math.types.Vec2¶
- property right: simvx.core.math.types.Vec2¶
- translate(offset: tuple[float, float] | numpy.ndarray)¶
- rotate(radians: float)¶
- rotate_deg(degrees: float)¶
- look_at(target: tuple[float, float] | numpy.ndarray)¶
- transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]¶
- draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)¶
- wrap_screen(margin: float = 20)¶
- hdr¶
‘Property(…)’
- property transform_render_dirty: bool¶
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶