@@ -69,6 +69,50 @@ def draw_points(sim: Sim, points: NDArray, rgba: NDArray | None = None, size: fl
6969 )
7070
7171
72+ def draw_capsule (
73+ sim : Sim ,
74+ p1 : NDArray ,
75+ p2 : NDArray ,
76+ radius : float = 0.05 ,
77+ rgba : NDArray | None = None ,
78+ is_cylinder : bool = False ,
79+ ):
80+ """Draw a capsule (pill) or cylinder between two points.
81+
82+ Args:
83+ sim: The simulation.
84+ p1: Start point [3,]
85+ p2: End point [3,]
86+ radius: The thickness of the geom in [m].
87+ rgba: The color of the object.
88+ is_cylinder: If True, draws a flat-ended cylinder.
89+ If False, draws a pill-shaped capsule.
90+ """
91+ if sim .viewer is None :
92+ return
93+
94+ # 1. Calculate Midpoint (Center of the geom)
95+ pos = (p1 + p2 ) / 2.0
96+
97+ # 2. Calculate Half-length (MuJoCo uses half-extents)
98+ dist = np .linalg .norm (p2 - p1 )
99+ half_length = dist / 2.0
100+
101+ # 3. Define Size: [radius, radius, half_length]
102+ # Note: For capsules, size[2] is the half-length of the *cylindrical* part.
103+ # MuJoCo adds the hemispherical caps on top of this.
104+ size = np .array ([radius , half_length , 0 ])
105+
106+ # 4. Get Rotation (Align Z-axis to the vector p2-p1)
107+ # Using your existing helper (wrapped in a list for the reshape)
108+ mat = _rotation_matrix_from_points (p1 [None , :], p2 [None , :]).as_matrix ().flatten ()
109+
110+ geom_type = mujoco .mjtGeom .mjGEOM_CYLINDER if is_cylinder else mujoco .mjtGeom .mjGEOM_CAPSULE
111+ rgba = rgba if rgba is not None else np .array ([0 , 1.0 , 0 , 1 ])
112+
113+ sim .viewer .viewer .add_marker (type = geom_type , pos = pos , size = size , mat = mat , rgba = rgba )
114+
115+
72116def change_material (
73117 sim : Sim ,
74118 mat_name : str ,
0 commit comments