Repository: Genesis-Embodied-AI/Genesis
File: genesis/ext/pyrender/interaction/mouse_spring.py (Python)

Code before:
        self.held_point_in_local: Vec3 | None = None
        self.prev_control_point: Vec3 | None = None

    def attach(self, picked_link: RigidLink, control_point: Vec3):
        # for now, we just pick the first geometry
        self.held_link = picked_link
        pose: Pose = Pose.from_link(self.held_link)
        self.held_point_in_local = pose.inverse_transform_point(control_point)
        self.prev_control_point = control_point

    def detach(self):
        self.held_link = None

    def apply_force(self, control_point: Vec3, delta_time: float):
        _ensure_torch_imported()

        # note when threaded: apply_force is called before attach!
        # note2: that was before we added a lock to ViewerInteraction; this migth be fixed now
        if not self.held_link:
            return
        
        self.prev_control_point = control_point

        # do simple force on COM only:
        link: RigidLink = self.held_link
        assert link
        lin_vel: Vec3 = Vec3.from_tensor(link.get_vel())
        ang_vel: Vec3 = Vec3.from_tensor(link.get_ang())
        link_pose: Pose = Pose.from_link(link)
        held_point_in_world: Vec3 = link_pose.transform_point(self.held_point_in_local)

        # note: we should assert earlier that link inertial_pos/quat are not None
        # todo: verify inertial_pos/quat are stored in local frame
        link_T_principal: Pose = Pose(Vec3.from_arraylike(link.inertial_pos), Quat.from_arraylike(link.inertial_quat))
        world_T_principal: Pose = link_pose * link_T_principal

        arm_in_principal: Vec3 = link_T_principal.get_inverse() * self.held_point_in_local   # for non-spherical inertia
        arm_in_world: Vec3 = world_T_principal.rot * arm_in_principal  # for spherical inertia

        pos_err_v: Vec3 = control_point - held_point_in_world
        inv_mass: float = float(1.0 / link.get_mass() if link.get_mass() > 0.0 else 0.0)
        inv_spherical_inertia: float = float(1.0 / link.inertial_i[0, 0] if link.inertial_i[0, 0] > 0.0 else 0.0)

        inv_dt: float = 1.0 / delta_time
        tau: float = MOUSE_SPRING_POSITION_CORRECTION_FACTOR
        damp: float = MOUSE_SPRING_VELOCITY_CORRECTION_FACTOR

        total_impulse: Vec3 = Vec3.zero()
        total_torque_impulse: Vec3 = Vec3.zero()

        for i in range(3*4):

Review comment (suggestion):
This makes no sense if the type of link is `RigidLink`. Either the assert should be removed or typing should be replaced by `RigidLink | None`

Code after:
        self.held_point_in_local: Vec3 | None = None
        self.prev_control_point: Vec3 | None = None

    def attach(self, picked_link: RigidLink, control_point: Vec3) -> None:
        # for now, we just pick the first geometry
        self.held_link = picked_link
        pose: Pose = Pose.from_link(self.held_link)
        self.held_point_in_local = pose.inverse_transform_point(control_point)
        self.prev_control_point = control_point

    def detach(self) -> None:
        self.held_link = None

    def apply_force(self, control_point: Vec3, delta_time: float) -> None:
        _ensure_torch_imported()

        # note when threaded: apply_force is called before attach!
        # note2: that was before we added a lock to ViewerInteraction; this migth be fixed now
        if not self.held_link:
            return
        
        self.prev_control_point = control_point

        # do simple force on COM only:
        link: RigidLink = self.held_link
        lin_vel: Vec3 = Vec3.from_tensor(link.get_vel())
        ang_vel: Vec3 = Vec3.from_tensor(link.get_ang())
        link_pose: Pose = Pose.from_link(link)
        held_point_in_world: Vec3 = link_pose.transform_point(self.held_point_in_local)

        # note: we should assert earlier that link inertial_pos/quat are not None
        # todo: verify inertial_pos/quat are stored in local frame
        link_T_principal: Pose = Pose(Vec3.from_arraylike(link.inertial_pos), Quat.from_arraylike(link.inertial_quat))
        world_T_principal: Pose = link_pose * link_T_principal

        arm_in_principal: Vec3 = link_T_principal.inverse_transform_point(self.held_point_in_local)   # for non-spherical inertia
        arm_in_world: Vec3 = world_T_principal.rot * arm_in_principal  # for spherical inertia

        pos_err_v: Vec3 = control_point - held_point_in_world
        inv_mass: float = float(1.0 / link.get_mass() if link.get_mass() > 0.0 else 0.0)
        inv_spherical_inertia: float = float(1.0 / link.inertial_i[0, 0] if link.inertial_i[0, 0] > 0.0 else 0.0)

        inv_dt: float = 1.0 / delta_time
        tau: float = MOUSE_SPRING_POSITION_CORRECTION_FACTOR
        damp: float = MOUSE_SPRING_VELOCITY_CORRECTION_FACTOR

        total_impulse: Vec3 = Vec3.zero()
        total_torque_impulse: Vec3 = Vec3.zero()

        for i in range(3*4):
            body_point_vel: Vec3 = lin_vel + ang_vel.cross(arm_in_world)