@@ -15,66 +15,84 @@ def _ensure_torch_imported() -> None:
1515 import torch
1616
1717class MouseSpring :
18- def __init__ (self ):
19- self .held_geom : RigidGeom | None = None
18+ def __init__ (self ) -> None :
19+ self .held_link : RigidLink | None = None
2020 self .held_point_in_local : Vec3 | None = None
2121 self .prev_control_point : Vec3 | None = None
2222
23- def attach (self , picked_entity : RigidEntity , control_point : Vec3 ):
23+ def attach (self , picked_link : RigidLink , control_point : Vec3 ) -> None :
2424 # for now, we just pick the first geometry
25- self .held_geom = picked_entity . geoms [ 0 ]
26- pose : Pose = Pose .from_geom (self .held_geom )
25+ self .held_link = picked_link
26+ pose : Pose = Pose .from_link (self .held_link )
2727 self .held_point_in_local = pose .inverse_transform_point (control_point )
2828 self .prev_control_point = control_point
2929
30- def detach (self ):
31- self .held_geom = None
30+ def detach (self ) -> None :
31+ self .held_link = None
3232
33- def apply_force (self , control_point : Vec3 , delta_time : float ):
33+ def apply_force (self , control_point : Vec3 , delta_time : float ) -> None :
3434 _ensure_torch_imported ()
35+
36+ # note when threaded: apply_force is called before attach!
37+ # note2: that was before we added a lock to ViewerInteraction; this migth be fixed now
38+ if not self .held_link :
39+ return
3540
36- # works ok:
37- # delta: Vec3 = control_point - self.prev_control_point
38- # pos = Vec3.from_tensor(self.held_geom.entity.get_pos())
39- # pos = pos + delta
40- # self.held_geom.entity.set_pos(pos.as_tensor())
4141 self .prev_control_point = control_point
4242
4343 # do simple force on COM only:
44- link : RigidLink = self .held_geom .link
45- link_pos : Vec3 = Vec3 .from_tensor (link .get_pos ())
44+ link : RigidLink = self .held_link
4645 lin_vel : Vec3 = Vec3 .from_tensor (link .get_vel ())
4746 ang_vel : Vec3 = Vec3 .from_tensor (link .get_ang ())
47+ link_pose : Pose = Pose .from_link (link )
48+ held_point_in_world : Vec3 = link_pose .transform_point (self .held_point_in_local )
49+
50+ # note: we should assert earlier that link inertial_pos/quat are not None
51+ # todo: verify inertial_pos/quat are stored in local frame
52+ link_T_principal : Pose = Pose (Vec3 .from_arraylike (link .inertial_pos ), Quat .from_arraylike (link .inertial_quat ))
53+ world_T_principal : Pose = link_pose * link_T_principal
54+
55+ arm_in_principal : Vec3 = link_T_principal .inverse_transform_point (self .held_point_in_local ) # for non-spherical inertia
56+ arm_in_world : Vec3 = world_T_principal .rot * arm_in_principal # for spherical inertia
4857
49- pos_err_v : Vec3 = control_point - link_pos
50- vel_err_v : Vec3 = Vec3 .zero () - lin_vel
58+ pos_err_v : Vec3 = control_point - held_point_in_world
5159 inv_mass : float = float (1.0 / link .get_mass () if link .get_mass () > 0.0 else 0.0 )
60+ inv_spherical_inertia : float = float (1.0 / link .inertial_i [0 , 0 ] if link .inertial_i [0 , 0 ] > 0.0 else 0.0 )
5261
5362 inv_dt : float = 1.0 / delta_time
54- # these are temporary values, till we fix an issue with apply_links_external_force.
55- # after fixing it, use tau = damp = 1.0:
5663 tau : float = MOUSE_SPRING_POSITION_CORRECTION_FACTOR
5764 damp : float = MOUSE_SPRING_VELOCITY_CORRECTION_FACTOR
5865
5966 total_impulse : Vec3 = Vec3 .zero ()
67+ total_torque_impulse : Vec3 = Vec3 .zero ()
68+
69+ for i in range (3 * 4 ):
70+ body_point_vel : Vec3 = lin_vel + ang_vel .cross (arm_in_world )
71+ vel_err_v : Vec3 = Vec3 .zero () - body_point_vel
6072
61- for i in range (3 ):
6273 dir : Vec3 = Vec3 .zero ()
63- dir .v [i ] = 1.0
74+ dir .v [i % 3 ] = 1.0
6475 pos_err : float = dir .dot (pos_err_v )
6576 vel_err : float = dir .dot (vel_err_v )
6677 error : float = tau * pos_err * inv_dt + damp * vel_err
67- virtual_mass : float = 1.0 / (inv_mass + 1e-24 )
78+
79+ arm_x_dir : Vec3 = arm_in_world .cross (dir )
80+ virtual_mass : float = 1.0 / (inv_mass + arm_x_dir .sqr_magnitude () * inv_spherical_inertia + 1e-24 )
6881 impulse : float = error * virtual_mass
6982
70- lin_vel += impulse * dir * inv_mass
71- total_impulse .v [i ] = impulse
83+ lin_vel += impulse * inv_mass * dir
84+ ang_vel += impulse * inv_spherical_inertia * arm_x_dir
85+ total_impulse .v [i % 3 ] += impulse
86+ total_torque_impulse += impulse * arm_x_dir
7287
7388 # Apply the new force
7489 total_force = total_impulse * inv_dt
90+ total_torque = total_torque_impulse * inv_dt
7591 force_tensor : torch .Tensor = total_force .as_tensor ().unsqueeze (0 )
92+ torque_tensor : torch .Tensor = total_torque .as_tensor ().unsqueeze (0 )
7693 link .solver .apply_links_external_force (force_tensor , (link .idx ,), ref = 'link_com' , local = False )
94+ link .solver .apply_links_external_torque (torque_tensor , (link .idx ,), ref = 'link_com' , local = False )
7795
7896 @property
7997 def is_attached (self ) -> bool :
80- return self .held_geom is not None
98+ return self .held_link is not None
0 commit comments