Source code for manim_pymunk.constraints.VPinJoint

from typing import Optional
from manim import *
from manim_pymunk.constraints import VConstraint
from pymunk.constraints import PinJoint
from pymunk import Space


[docs] class VPinJoint(VConstraint): """A pin joint keeps a fixed distance between two anchor points on two rigid bodies. It acts like a solid, weightless rod connecting two points, allowing them to rotate freely around the anchors while maintaining the specified `distance`. Parameters ---------- a_mob The first Mobject to be connected. b_mob The second Mobject to be connected. anchor_a_local The local anchor point on `a_mob` where the pin is attached, relative to the Mobject's center. anchor_b_local The local anchor point on `b_mob` where the pin is attached, relative to the Mobject's center. distance The fixed distance to maintain between the two anchors. If `None`, it is automatically calculated as the initial distance between anchors. anchor_a_appearance The Mobject used to visually represent the anchor point on `a_mob` (e.g., a red `Dot`). anchor_b_appearance The Mobject used to visually represent the anchor point on `b_mob` (e.g., a red `Dot`). connect_line_class The class used to draw the connecting rod (e.g., `Line`). Defaults to `None` for no visible connection. connect_line_config A dictionary defining the visual style of the connecting line, such as `color` and `stroke_width`. Examples -------- .. manim:: VPinJointExample from manim_pymunk import * class VPinJointExample(SpaceScene): def construct(self): static_dot = Dot(ORIGIN) square = Square().move_to(static_dot) square2 = Square().move_to(static_dot.get_center() + UP * 2).scale(0.5) constraints = [ VPinJoint(static_dot, square), VPinJoint( square, square2, anchor_a_local=square.get_corner(UR) - square.get_center(), distance=2, connect_line_class=Line, ), ] self.add_static_body(static_dot) self.add_dynamic_body(square, square2, angular_velocity=PI * 2) self.add_shapes_filter(static_dot, square, square2, group=2) self.add_constraints(*constraints) self.wait(3) """ def __init__( self, a_mob: Mobject, b_mob: Mobject, anchor_a_local: list[float, float, float] = ORIGIN, anchor_b_local: list[float, float, float] = ORIGIN, distance: Optional[float] = None, anchor_a_appearance: Mobject = Dot(color=RED), anchor_b_appearance: Mobject = Dot(color=RED), connect_line_class: Optional[Line] = None, connect_line_config: dict = { "color": YELLOW, "stroke_width": 2, }, **kwargs, ): super().__init__(**kwargs) self.a_mob = a_mob self.b_mob = b_mob self.anchor_a_local = anchor_a_local self.anchor_b_local = anchor_b_local self.anchor_a_appearance = anchor_a_appearance self.anchor_b_appearance = anchor_b_appearance self.connect_line_class = connect_line_class self.connect_line_config = connect_line_config self.connect_line = None self.constraint: Optional[PinJoint] = None self.init_distance = distance self.__check_data() def __check_data(self): """Verify the validity of constraint parameters.""" if self.a_mob is None or self.b_mob is None: raise ValueError( "Constraints cannot be created without both a_mob and b_mob." ) dist = np.linalg.norm(self.a_mob.get_center() - self.b_mob.get_center()) if dist < 0.000001: if self.connect_line_class is not None: raise ValueError( f"Points {self.a_mob} and {self.b_mob} are at the same location ({dist:.8f}). " "Connecting them with a line makes no sense." )
[docs] def install(self, space: Space): a_body = getattr(self.a_mob, "body", None) b_body = getattr(self.b_mob, "body", None) if not a_body or not b_body: raise ValueError("VPinJoint 连接的物体必须先执行 add_dynamic_body") # 1. 创建约束 self.constraint = PinJoint( a_body, b_body, tuple(self.anchor_a_local[:2]), tuple(self.anchor_b_local[:2]), ) if self.init_distance is not None: self.constraint.distance = self.init_distance pos_a = a_body.local_to_world(tuple(self.anchor_a_local[:2])) pos_b = b_body.local_to_world(tuple(self.anchor_b_local[:2])) p1 = [pos_a.x, pos_a.y, 0] p2 = [pos_b.x, pos_b.y, 0] if self.connect_line_class: self.connect_line = self.connect_line_class( p1, p2, **self.connect_line_config ) self.add(self.connect_line) self.anchor_a_appearance.move_to(p1) self.anchor_b_appearance.move_to(p2) self.add(self.anchor_a_appearance, self.anchor_b_appearance) space.add(self.constraint) # 4. 绑定实时更新 self.add_updater(self.mob_updater)
[docs] def mob_updater(self, mob, dt): """Visual control updater""" if not self.constraint: return a_body = self.constraint.a b_body = self.constraint.b wa = a_body.local_to_world(self.constraint.anchor_a) wb = b_body.local_to_world(self.constraint.anchor_b) p1 = [wa.x, wa.y, 0] p2 = [wb.x, wb.y, 0] self.anchor_a_appearance.move_to(p1) self.anchor_b_appearance.move_to(p2) if isinstance(self.connect_line, Line): self.connect_line.put_start_and_end_on(p1, p2)