Source code for src.utils.visualization.robot_points

from __future__ import annotations

from typing import Optional

import numpy as np
import pyvista as pv

from src.kinematics.fk import link_transforms
from src.robots.protocols import RobotTreeLike


[docs] def tool_position_world( robot: RobotTreeLike, theta: np.ndarray, base_transform: Optional[np.ndarray] = None, ) -> np.ndarray: """Compute tool-tip position in world coordinates. Runs forward kinematics up to ``robot.tool_link`` and applies the optional base transform to produce a 3-vector in world frame. Args: robot: Robot model exposing :attr:`tool_link` and joint structure. theta: Joint configuration, shape ``(dof,)``. base_transform: Optional ``(4, 4)`` world-from-base transform. Defaults to identity. Returns: Position vector :math:`\\mathbf{p} \\in \\mathbb{R}^3`. """ if base_transform is None: T_base = np.eye(4) else: T_base = np.asarray(base_transform, dtype=float).reshape(4, 4) T_links = link_transforms(robot, np.asarray(theta, dtype=float).reshape(-1)) return (T_base @ T_links[robot.tool_link])[:3, 3]
[docs] def create_point_poly(point: np.ndarray) -> pv.PolyData: """Create a single-point :class:`pyvista.PolyData` marker. Args: point: 3-D point coordinates, shape ``(3,)``. Returns: :class:`pyvista.PolyData` containing exactly one point. """ return pv.PolyData(np.array([np.asarray(point, dtype=float)], dtype=float))
[docs] def update_point_poly(poly: pv.PolyData, point: np.ndarray) -> None: """Update an existing single-point :class:`pyvista.PolyData` marker in-place. Replaces ``poly.points`` and calls ``poly.Modified()`` so that the PyVista renderer picks up the change without re-adding the actor. Args: poly: Marker previously created by :func:`create_point_poly`. point: New 3-D point coordinates, shape ``(3,)``. """ poly.points = np.array([np.asarray(point, dtype=float)], dtype=float) poly.Modified()