robotic 0.2.9.dev1__cp311-cp311-manylinux2014_x86_64.whl → 0.3.0__cp311-cp311-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of robotic might be problematic. Click here for more details.
- robotic/__init__.py +4 -0
- robotic/_robotic.pyi +13 -13
- robotic/_robotic.so +0 -0
- robotic/include/rai/Algo/rungeKutta.h +1 -1
- robotic/include/rai/Control/TimingMPC.h +2 -2
- robotic/include/rai/Core/array.h +64 -40
- robotic/include/rai/Core/array.ipp +247 -81
- robotic/include/rai/Core/arrayDouble.h +10 -13
- robotic/include/rai/Core/graph.h +22 -2
- robotic/include/rai/Core/h5.h +3 -1
- robotic/include/rai/Core/util.h +8 -7
- robotic/include/rai/Geo/fclInterface.h +3 -1
- robotic/include/rai/Geo/geo.h +6 -2
- robotic/include/rai/Geo/mesh.h +11 -5
- robotic/include/rai/Geo/pairCollision.h +4 -4
- robotic/include/rai/Gui/RenderData.h +4 -3
- robotic/include/rai/Gui/opengl.h +1 -1
- robotic/include/rai/KOMO/komo.h +1 -0
- robotic/include/rai/KOMO/manipTools.h +2 -2
- robotic/include/rai/Kin/F_forces.h +1 -1
- robotic/include/rai/Kin/dof_forceExchange.h +4 -4
- robotic/include/rai/Kin/frame.h +6 -5
- robotic/include/rai/Kin/kin.h +26 -17
- robotic/include/rai/Kin/kin_physx.h +2 -2
- robotic/include/rai/Logic/folWorld.h +1 -1
- robotic/include/rai/Optim/testProblems_Opt.h +2 -2
- robotic/include/rai/Optim/utils.h +2 -2
- robotic/include/rai/PathAlgos/ConfigurationProblem.h +3 -2
- robotic/include/rai/PathAlgos/RRT_PathFinder.h +1 -1
- robotic/include/rai/ry/types.h +3 -2
- robotic/librai.so +0 -0
- robotic/meshTool +0 -0
- robotic/mujoco-import.py +10 -0
- robotic/rai-robotModels/g1/g1.g +11 -2
- robotic/rai-robotModels/g1/g1_clean.g +38 -73
- robotic/rai-robotModels/g1/meshes/head_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_ankle_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_ankle_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_elbow_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_hip_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_hip_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_hip_yaw_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_knee_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_rubber_hand.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_shoulder_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_shoulder_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_shoulder_yaw_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_wrist_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_wrist_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/left_wrist_yaw_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/logo_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/pelvis.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/pelvis_contour_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_ankle_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_ankle_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_elbow_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_hip_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_hip_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_hip_yaw_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_knee_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_rubber_hand.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_shoulder_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_shoulder_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_shoulder_yaw_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_wrist_pitch_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_wrist_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/right_wrist_yaw_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/torso_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/waist_roll_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/waist_support_link.h5 +0 -0
- robotic/rai-robotModels/g1/meshes/waist_yaw_link.h5 +0 -0
- robotic/rai-robotModels/objects/shelf.g +1 -1
- robotic/rai-robotModels/panda/meshes/finger.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/hand.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link0.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link1.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link2.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link3.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link4.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link5.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link6.h5 +0 -0
- robotic/rai-robotModels/panda/meshes/link7.h5 +0 -0
- robotic/rai-robotModels/panda/panda.g +1 -1
- robotic/rai-robotModels/panda/panda_arm_hand_conv.g +24 -0
- robotic/rai-robotModels/panda/panda_arm_hand_conv.yml +24 -0
- robotic/rai-robotModels/panda/panda_clean.g +21 -45
- robotic/rai-robotModels/panda/panda_gripper.g +3 -3
- robotic/rai-robotModels/pr2/meshes/base.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/base_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/caster.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/elbow_flex.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/elbow_flex_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/forearm.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/forearm_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/forearm_roll.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/gripper_palm.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/gripper_palm_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/head_pan.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/head_pan_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/head_tilt.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/head_tilt_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/l_finger.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/l_finger_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/l_finger_tip.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/l_finger_tip_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/shoulder_lift.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/shoulder_lift_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/shoulder_pan.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/shoulder_pan_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/tilting_hokuyo.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/tilting_hokuyo_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/torso_lift.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/torso_lift_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/upper_arm.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/upper_arm_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/upper_arm_roll.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/upper_arm_roll_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/wheel.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/wheel_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/wrist_color.png +0 -0
- robotic/rai-robotModels/pr2/meshes/wrist_flex.h5 +0 -0
- robotic/rai-robotModels/pr2/meshes/wrist_roll.h5 +0 -0
- robotic/rai-robotModels/pr2/pr2.g +7 -7
- robotic/rai-robotModels/pr2/pr2_clean.g +119 -115
- robotic/rai-robotModels/pr2/pr2_conv.g +218 -0
- robotic/rai-robotModels/pr2/pr2_modifications.g +2 -2
- robotic/rai-robotModels/ranger/meshes/ranger_mini3.h5 +0 -0
- robotic/rai-robotModels/ranger/meshes/ranger_mini_v3_wheel.h5 +0 -0
- robotic/rai-robotModels/ranger/meshes/ranger_mini_v3_wheel_right.h5 +0 -0
- robotic/rai-robotModels/ranger/ranger.g +8 -8
- robotic/rai-robotModels/ranger/ranger_clean.g +5 -5
- robotic/rai-robotModels/ranger/ranger_mini_conv.g +14 -0
- robotic/rai-robotModels/robotiq/meshes/robotiq_arg2f_85_base_link.h5 +0 -0
- robotic/rai-robotModels/robotiq/meshes/robotiq_arg2f_85_inner_finger.h5 +0 -0
- robotic/rai-robotModels/robotiq/meshes/robotiq_arg2f_85_inner_knuckle.h5 +0 -0
- robotic/rai-robotModels/robotiq/meshes/robotiq_arg2f_85_outer_finger.h5 +0 -0
- robotic/rai-robotModels/robotiq/meshes/robotiq_arg2f_85_outer_knuckle.h5 +0 -0
- robotic/rai-robotModels/robotiq/robotiq.g +2 -2
- robotic/rai-robotModels/robotiq/robotiq_clean.g +16 -16
- robotic/rai-robotModels/scenarios/ballFinger.g +2 -2
- robotic/rai-robotModels/scenarios/panda_fixRobotiq.g +3 -3
- robotic/rai-robotModels/tests/arm.g +11 -11
- robotic/rai-robotModels/tests/compound.g +3 -6
- robotic/rai-robotModels/ur10/meshes/base.h5 +0 -0
- robotic/rai-robotModels/ur10/meshes/forearm.h5 +0 -0
- robotic/rai-robotModels/ur10/meshes/shoulder.h5 +0 -0
- robotic/rai-robotModels/ur10/meshes/upperarm.h5 +0 -0
- robotic/rai-robotModels/ur10/meshes/wrist1.h5 +0 -0
- robotic/rai-robotModels/ur10/meshes/wrist2.h5 +0 -0
- robotic/rai-robotModels/ur10/meshes/wrist3.h5 +0 -0
- robotic/rai-robotModels/ur10/ur10.g +2 -2
- robotic/rai-robotModels/ur10/ur10_clean.g +8 -8
- robotic/rai-robotModels/ur10/ur10_conv.g +17 -0
- robotic/ry-h5info +2 -2
- robotic/ry-urdfConvert.py +74 -0
- robotic/src/cleanMeshes.py +59 -0
- robotic/src/config_urdf.py +237 -0
- robotic/src/mesh_helper.py +395 -0
- robotic/{rai-robotModels/ranger/meshes/cleanMeshes.mlx → src/meshlabFilters.mlx} +0 -3
- robotic/src/mujoco_io.py +242 -0
- robotic/src/yaml_helper.py +19 -0
- robotic/test.py +15 -0
- robotic/version.py +1 -1
- {robotic-0.2.9.dev1.data → robotic-0.3.0.data}/scripts/ry-h5info +2 -2
- robotic-0.3.0.data/scripts/ry-urdfConvert.py +74 -0
- {robotic-0.2.9.dev1.dist-info → robotic-0.3.0.dist-info}/METADATA +3 -7
- robotic-0.3.0.dist-info/RECORD +367 -0
- {robotic-0.2.9.dev1.dist-info → robotic-0.3.0.dist-info}/WHEEL +1 -1
- robotic/import.py +0 -0
- robotic/rai-robotModels/baxter/baxter.g +0 -49
- robotic/rai-robotModels/baxter/baxter_clean.g +0 -116
- robotic/rai-robotModels/baxter/baxter_clean2.g +0 -205
- robotic/rai-robotModels/baxter/baxter_clean3.g +0 -223
- robotic/rai-robotModels/baxter/baxter_description/meshes/base/PEDESTAL.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/base/pedestal_link_collision.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/head/H0.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/head/H1.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/lower_elbow/E1.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/lower_forearm/W1.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/lower_shoulder/S1.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/torso/base_link.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/torso/base_link_collision.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/upper_elbow/E0.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/upper_forearm/W0.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/upper_shoulder/S0.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_description/meshes/wrist/W2.ply +0 -0
- robotic/rai-robotModels/baxter/baxter_new.g +0 -53
- robotic/rai-robotModels/baxter/rethink_ee_description/meshes/electric_gripper/electric_gripper_base.ply +0 -0
- robotic/rai-robotModels/baxter/rethink_ee_description/meshes/electric_gripper/fingers/extended_narrow.ply +0 -0
- robotic/rai-robotModels/baxter/rethink_ee_description/meshes/electric_gripper/fingers/half_round_tip.ply +0 -0
- robotic/rai-robotModels/baxter/rethink_ee_description/meshes/electric_gripper/fingers/paddle_tip.ply +0 -0
- robotic/rai-robotModels/baxter/rethink_ee_description/meshes/pneumatic_gripper/pneumatic_gripper_base.ply +0 -0
- robotic/rai-robotModels/baxter/rethink_ee_description/meshes/pneumatic_gripper/pneumatic_gripper_w_cup.ply +0 -0
- robotic/rai-robotModels/g1/meshes/head_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_ankle_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_ankle_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_elbow_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_index_0_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_index_1_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_middle_0_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_middle_1_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_palm_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_thumb_0_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_thumb_1_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hand_thumb_2_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hip_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hip_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_hip_yaw_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_knee_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_rubber_hand.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_shoulder_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_shoulder_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_shoulder_yaw_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_wrist_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_wrist_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_wrist_roll_rubber_hand.ply +0 -0
- robotic/rai-robotModels/g1/meshes/left_wrist_yaw_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/logo_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/pelvis.ply +0 -0
- robotic/rai-robotModels/g1/meshes/pelvis_contour_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_ankle_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_ankle_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_elbow_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_index_0_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_index_1_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_middle_0_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_middle_1_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_palm_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_thumb_0_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_thumb_1_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hand_thumb_2_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hip_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hip_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_hip_yaw_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_knee_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_rubber_hand.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_shoulder_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_shoulder_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_shoulder_yaw_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_wrist_pitch_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_wrist_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_wrist_roll_rubber_hand.ply +0 -0
- robotic/rai-robotModels/g1/meshes/right_wrist_yaw_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/torso_constraint_L_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/torso_constraint_L_rod_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/torso_constraint_R_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/torso_constraint_R_rod_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/torso_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/waist_constraint_L.ply +0 -0
- robotic/rai-robotModels/g1/meshes/waist_constraint_R.ply +0 -0
- robotic/rai-robotModels/g1/meshes/waist_roll_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/waist_support_link.ply +0 -0
- robotic/rai-robotModels/g1/meshes/waist_yaw_link.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/finger.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/hand.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link0.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link1.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link2.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link3.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link4.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link5.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link6.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/collision/link7.stl +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/HOWTO.sh +0 -10
- robotic/rai-robotModels/panda/franka_description/meshes/visual/HOWTO2.sh +0 -7
- robotic/rai-robotModels/panda/franka_description/meshes/visual/convMeshes.mlx +0 -38
- robotic/rai-robotModels/panda/franka_description/meshes/visual/finger.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/hand.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link0.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link1.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link2.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link3.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link4.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link5.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link6.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/link7.ply +0 -0
- robotic/rai-robotModels/panda/franka_description/meshes/visual/script.mlx +0 -28
- robotic/rai-robotModels/pr2/pr2_description/meshes/base_v0/base.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/base_v0/base_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/base_v0/caster.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/base_v0/caster_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/base_v0/pr2_wheel.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/base_v0/wheel.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/forearm_v0/forearm.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/forearm_v0/wrist_flex.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/forearm_v0/wrist_roll.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/forearm_v0/wrist_roll_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/gripper_v0/gripper_palm.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/gripper_v0/l_finger.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/gripper_v0/l_finger_tip.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/head_v0/head_pan.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/head_v0/head_pan_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/head_v0/head_tilt.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/head_v0/head_tilt_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/shoulder_v0/shoulder_lift.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/shoulder_v0/shoulder_pan.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/shoulder_v0/shoulder_yaw.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/shoulder_v0/upper_arm_roll.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/shoulder_v0/upper_arm_roll_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/tilting_laser_v0/hok_tilt.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/tilting_laser_v0/tilting_hokuyo.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/tilting_laser_v0/tilting_hokuyo_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/torso_v0/torso.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/torso_v0/torso_lift.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/torso_v0/torso_lift_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/upper_arm_v0/elbow_flex.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/upper_arm_v0/forearm_roll.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/upper_arm_v0/forearm_roll_L.ply +0 -0
- robotic/rai-robotModels/pr2/pr2_description/meshes/upper_arm_v0/upper_arm.ply +0 -0
- robotic/rai-robotModels/ranger/meshes/cleanMeshes.sh +0 -8
- robotic/rai-robotModels/ranger/meshes/ranger_mini3.ply +0 -0
- robotic/rai-robotModels/ranger/meshes/ranger_mini_v3_wheel.ply +0 -0
- robotic/rai-robotModels/ranger/meshes/ranger_mini_v3_wheel_right.ply +0 -0
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_arg2f_85_base_link.ply +0 -0
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_arg2f_85_base_link_x.ply +0 -10
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_arg2f_85_inner_finger.ply +0 -0
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_arg2f_85_inner_knuckle.ply +0 -0
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_arg2f_85_outer_finger.ply +0 -0
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_arg2f_85_outer_knuckle.ply +0 -0
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_arg2f_85_pad.ply +0 -0
- robotic/rai-robotModels/robotiq/meshes/visual/robotiq_gripper_coupling.ply +0 -0
- robotic/rai-robotModels/ur10/ur_description/meshes/ur10/visual/Base.ply +0 -0
- robotic/rai-robotModels/ur10/ur_description/meshes/ur10/visual/Forearm.ply +0 -0
- robotic/rai-robotModels/ur10/ur_description/meshes/ur10/visual/Shoulder.ply +0 -0
- robotic/rai-robotModels/ur10/ur_description/meshes/ur10/visual/UpperArm.ply +0 -0
- robotic/rai-robotModels/ur10/ur_description/meshes/ur10/visual/Wrist1.ply +0 -0
- robotic/rai-robotModels/ur10/ur_description/meshes/ur10/visual/Wrist2.ply +0 -0
- robotic/rai-robotModels/ur10/ur_description/meshes/ur10/visual/Wrist3.ply +0 -0
- robotic/ry-urdf2rai +0 -222
- robotic/ry-urdf2yaml +0 -250
- robotic-0.2.9.dev1.data/scripts/ry-urdf2rai +0 -222
- robotic-0.2.9.dev1.data/scripts/ry-urdf2yaml +0 -250
- robotic-0.2.9.dev1.dist-info/RECORD +0 -421
- /robotic/rai-robotModels/robotiq/meshes/{visual/robotiq_ft300.ply → robotiq_ft300.ply} +0 -0
- {robotic-0.2.9.dev1.data → robotic-0.3.0.data}/scripts/ry-bot +0 -0
- {robotic-0.2.9.dev1.data → robotic-0.3.0.data}/scripts/ry-info +0 -0
- {robotic-0.2.9.dev1.data → robotic-0.3.0.data}/scripts/ry-meshTool +0 -0
- {robotic-0.2.9.dev1.data → robotic-0.3.0.data}/scripts/ry-test +0 -0
- {robotic-0.2.9.dev1.data → robotic-0.3.0.data}/scripts/ry-view +0 -0
- {robotic-0.2.9.dev1.dist-info → robotic-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {robotic-0.2.9.dev1.dist-info → robotic-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
#import matplotlib.pyplot as plt
|
|
4
|
+
import trimesh
|
|
5
|
+
import base64
|
|
6
|
+
import yaml
|
|
7
|
+
import h5py
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
from PIL import Image
|
|
10
|
+
|
|
11
|
+
def write_arr(X, fil, type='float32'):
|
|
12
|
+
data = (type, list(X.shape), )
|
|
13
|
+
fil.write(f'[ "{type}", {list(X.shape)}, "')
|
|
14
|
+
fil.write(base64.b64encode(X.astype(type)).decode('utf-8'))
|
|
15
|
+
fil.write('" ]')
|
|
16
|
+
|
|
17
|
+
def conv_tuple_arr(data_tuple):
|
|
18
|
+
X = np.frombuffer(base64.decodebytes(bytearray(data_tuple[2].encode('utf-8'))), dtype=data_tuple[0])
|
|
19
|
+
X = X.reshape(data_tuple[1])
|
|
20
|
+
return X
|
|
21
|
+
|
|
22
|
+
class MeshHelper():
|
|
23
|
+
def __init__(self, file):
|
|
24
|
+
self.failed = False
|
|
25
|
+
self.inertiaIsDiagonal = False
|
|
26
|
+
if file[-3:]==".h5":
|
|
27
|
+
self.load_h5(file)
|
|
28
|
+
else:
|
|
29
|
+
self.load(file)
|
|
30
|
+
self.file = file
|
|
31
|
+
|
|
32
|
+
def load(self, file):
|
|
33
|
+
print('=== file: ', file)
|
|
34
|
+
self.filebase = os.path.splitext(file)[0]
|
|
35
|
+
self.mesh = trimesh.load(file, force='mesh')
|
|
36
|
+
try:
|
|
37
|
+
scene_or_mesh = trimesh.load(file, force='mesh')
|
|
38
|
+
except Exception as e:
|
|
39
|
+
print(e)
|
|
40
|
+
print(' load failed:', file)
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
if isinstance(scene_or_mesh, trimesh.Scene):
|
|
44
|
+
if len(scene_or_mesh.geometry) == 0:
|
|
45
|
+
self.mesh = None
|
|
46
|
+
self.failed = True
|
|
47
|
+
else:
|
|
48
|
+
# we lose texture information here
|
|
49
|
+
self.mesh = trimesh.util.concatenate(
|
|
50
|
+
tuple(trimesh.Trimesh(vertices=g.vertices, faces=g.faces)
|
|
51
|
+
for g in scene_or_mesh.geometry.values()))
|
|
52
|
+
else:
|
|
53
|
+
assert(isinstance(scene_or_mesh, trimesh.Trimesh))
|
|
54
|
+
self.mesh = scene_or_mesh
|
|
55
|
+
|
|
56
|
+
def view(self):
|
|
57
|
+
self.mesh.show(resolution=(300,300))
|
|
58
|
+
|
|
59
|
+
def report(self):
|
|
60
|
+
print(' filebase:', self.filebase)
|
|
61
|
+
print(' #vertices:', self.mesh.vertices.shape)
|
|
62
|
+
print(' #faces:', self.mesh.faces.shape)
|
|
63
|
+
if hasattr(self.mesh.visual, 'uv'):
|
|
64
|
+
print('#uv:', self.mesh.visual.uv.shape)
|
|
65
|
+
|
|
66
|
+
def autoScale(self):
|
|
67
|
+
scale = 1
|
|
68
|
+
#if self.mesh.scale > 10000:
|
|
69
|
+
# scale = 1e-5
|
|
70
|
+
#elif self.mesh.scale > 1000:
|
|
71
|
+
# scale = 1e-4
|
|
72
|
+
if self.mesh.scale > 200:
|
|
73
|
+
scale = 1e-3
|
|
74
|
+
elif self.mesh.scale > 20:
|
|
75
|
+
scale = 1e-2
|
|
76
|
+
elif self.mesh.scale > 2:
|
|
77
|
+
scale = 1e-1
|
|
78
|
+
#translate = -mesh.centroid
|
|
79
|
+
translate = -.5*(self.mesh.bounds[0]+self.mesh.bounds[1])
|
|
80
|
+
matrix = np.eye(4)
|
|
81
|
+
matrix[0:3, 3] = translate
|
|
82
|
+
matrix[0:3, 0:4] *= scale
|
|
83
|
+
self.mesh.apply_transform(matrix)
|
|
84
|
+
|
|
85
|
+
def transformInertia(self, verbose=False):
|
|
86
|
+
print("prior COM" , self.mesh.center_mass)
|
|
87
|
+
print("prior inertia\n" , self.mesh.moment_inertia)
|
|
88
|
+
|
|
89
|
+
U, D, V = np.linalg.svd(self.mesh.moment_inertia)
|
|
90
|
+
|
|
91
|
+
# Ensure proper rotation
|
|
92
|
+
if np.linalg.det(V) < 0:
|
|
93
|
+
V[:, -1] *= -1
|
|
94
|
+
|
|
95
|
+
matrix = np.eye(4)
|
|
96
|
+
matrix[0:3, 3] = V @ (-self.mesh.center_mass) # Move center of mass to origin
|
|
97
|
+
matrix[0:3, 0:3] = V # Rotate the mesh to align with principal axes
|
|
98
|
+
|
|
99
|
+
self.mesh.apply_transform(matrix)
|
|
100
|
+
|
|
101
|
+
if verbose:
|
|
102
|
+
print("COM after transformation:" , self.mesh.center_mass)
|
|
103
|
+
print("Inertia after transfromation:\n" , self.mesh.moment_inertia)
|
|
104
|
+
self.inertiaIsDiagonal = True
|
|
105
|
+
|
|
106
|
+
return np.linalg.inv(matrix)
|
|
107
|
+
|
|
108
|
+
def repair(self, mergeTolerance=1e-6):
|
|
109
|
+
try:
|
|
110
|
+
trimesh.constants.tol.merge = mergeTolerance
|
|
111
|
+
self.mesh.process(validate=True)
|
|
112
|
+
trimesh.repair.fill_holes(self.mesh)
|
|
113
|
+
trimesh.repair.fix_inversion(self.mesh, multibody=True)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(' --- repair failed ---', e)
|
|
116
|
+
print(' --- this might be a trimesh bug: change order within mesh.process method')
|
|
117
|
+
self.failed = True
|
|
118
|
+
exit(0) # this might be a trimesh bug: change order within mesh.process method
|
|
119
|
+
|
|
120
|
+
# if self.mesh.visual != None:
|
|
121
|
+
# self.mesh.visual.uv = np.array([[0,0]])
|
|
122
|
+
|
|
123
|
+
def texture2vertexColors(self):
|
|
124
|
+
if hasattr(self.mesh.visual, 'uv'):
|
|
125
|
+
colors = self.mesh.visual.material.to_color(self.mesh.visual.uv)
|
|
126
|
+
vis = trimesh.visual.ColorVisuals(mesh=self.mesh, vertex_colors=colors)
|
|
127
|
+
self.mesh.visual = vis
|
|
128
|
+
# else:
|
|
129
|
+
# raise ValueError("Mesh does not have UV coordinates!")
|
|
130
|
+
|
|
131
|
+
def export_ply(self, filename=None):
|
|
132
|
+
if filename is None:
|
|
133
|
+
filename = self.filebase+'-.ply'
|
|
134
|
+
print(' exporting', filename)
|
|
135
|
+
self.mesh.export(filename)
|
|
136
|
+
|
|
137
|
+
def export_scene(self, convex=False):
|
|
138
|
+
with open(self.filebase+'.g', 'w', encoding='utf-8') as fil:
|
|
139
|
+
if self.inertiaIsDiagonal:
|
|
140
|
+
if convex:
|
|
141
|
+
fil.write(f'obj: {{ X:[0., 0., 1.], mesh_decomp: <{self.filebase}.h5>, mass: {self.mesh.center_mass.tolist()}, inertia: {np.diagonal(self.mesh.moment_inertia).tolist()} }}\n')
|
|
142
|
+
else:
|
|
143
|
+
fil.write(f'obj: {{ X:[0., 0., 12.], mass: {self.mesh.center_mass.tolist()}, inertia: {self.mesh.moment_inertia.reshape([9]).tolist()} }}\n')
|
|
144
|
+
fil.write(f'obj_mesh (obj): {{ mesh: <{self.filebase}.h5> }}\n')
|
|
145
|
+
#fil.write(f'obj_points (obj): {{ mesh_points: <{self.filebase}.h5>, color: [1 1 0], size: [2.] }}\n')
|
|
146
|
+
return self.filebase+'.g'
|
|
147
|
+
|
|
148
|
+
def createPoints(self):
|
|
149
|
+
self.pts, faces = trimesh.sample.sample_surface(self.mesh, 20000)
|
|
150
|
+
self.normals = self.mesh.face_normals[faces]
|
|
151
|
+
#bary = trimesh.triangles.points_to_barycentric(self.mesh.triangles[faces], pts)
|
|
152
|
+
#normals = trimesh.unitize((self.mesh.vertex_normals[self.mesh.faces[faces]] *
|
|
153
|
+
# trimesh.unitize(bary).reshape((-1, 3, 1))).sum(axis=1))
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def createDecomposition(self):
|
|
157
|
+
|
|
158
|
+
convex_hulls = self.mesh.convex_decomposition()
|
|
159
|
+
|
|
160
|
+
self.decomp_parts = [0]
|
|
161
|
+
self.decomp_vertices = np.asarray(convex_hulls[0].vertices)
|
|
162
|
+
self.decomp_faces = np.asarray(convex_hulls[0].faces)
|
|
163
|
+
vert_len = len(convex_hulls[0].vertices)
|
|
164
|
+
for i, part in enumerate(convex_hulls[1:]):
|
|
165
|
+
|
|
166
|
+
self.decomp_vertices = np.concatenate([self.decomp_vertices, part.vertices])
|
|
167
|
+
self.decomp_faces = np.concatenate([self.decomp_faces, part.faces + vert_len])
|
|
168
|
+
self.decomp_parts.append(len(part.vertices)+self.decomp_parts[i])
|
|
169
|
+
|
|
170
|
+
vert_len+=len(part.vertices)
|
|
171
|
+
|
|
172
|
+
self.decomp_parts = np.asarray(self.decomp_parts)
|
|
173
|
+
self.decomp_colors = [0,255,255] # fixed blue for now
|
|
174
|
+
|
|
175
|
+
def createDecomposition_lowlevel(self):
|
|
176
|
+
### create decomposition
|
|
177
|
+
ret = os.system('ry-meshTool.sh ' + self.filebase+'.mesh' + ' -decomp -hide -quiet'
|
|
178
|
+
' && mv z.arr ' + self.filebase+'.decomp' )
|
|
179
|
+
if ret>0:
|
|
180
|
+
print(' --- decomposition failed --- return:', ret)
|
|
181
|
+
self.failed=True
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
### load decomposition
|
|
185
|
+
with open(self.filebase+'.decomp', 'r') as fil:
|
|
186
|
+
decomp = yaml.safe_load(fil)
|
|
187
|
+
self.decomp_vertices = conv_tuple_arr(decomp['V'])
|
|
188
|
+
self.decomp_faces = conv_tuple_arr(decomp['T'])
|
|
189
|
+
self.decomp_colors = conv_tuple_arr(decomp['C'])
|
|
190
|
+
self.decomp_parts = conv_tuple_arr(decomp['cvxParts'])
|
|
191
|
+
|
|
192
|
+
def export_h5(self, filename=None, inertia=False):
|
|
193
|
+
if filename is None:
|
|
194
|
+
filename = self.filebase+'.h5'
|
|
195
|
+
print(' exporting', filename)
|
|
196
|
+
with h5py.File(filename, 'w') as fil:
|
|
197
|
+
fil.create_dataset('mesh/vertices', data=self.mesh.vertices, dtype='float32')
|
|
198
|
+
assert self.mesh.faces.shape[1]==3, 'can only export triangle meshes'
|
|
199
|
+
if(self.mesh.vertices.shape[0]<65535):
|
|
200
|
+
fil.create_dataset('mesh/faces', data=self.mesh.faces, dtype='uint16')
|
|
201
|
+
else:
|
|
202
|
+
fil.create_dataset('mesh/faces', data=self.mesh.faces, dtype='uint32')
|
|
203
|
+
|
|
204
|
+
if hasattr(self.mesh.visual, 'vertex_colors'):
|
|
205
|
+
colors = np.asarray(self.mesh.visual.vertex_colors)[:,0:3]
|
|
206
|
+
fil.create_dataset('mesh/colors', data=colors, dtype='uint8')
|
|
207
|
+
|
|
208
|
+
if hasattr(self.mesh.visual, 'uv'):
|
|
209
|
+
texCoords = self.mesh.visual.uv.copy()
|
|
210
|
+
texCoords[:, 1] = 1 - texCoords[:, 1]
|
|
211
|
+
fil.create_dataset('mesh/textureCoords', data=texCoords, dtype='float32')
|
|
212
|
+
|
|
213
|
+
if self.textureFile is not None:
|
|
214
|
+
file = np.frombuffer(self.textureFile.encode(), dtype=np.int8)
|
|
215
|
+
file = np.append(file, [0])
|
|
216
|
+
fil.create_dataset('mesh/textureFile', data=file, dtype='int8')
|
|
217
|
+
else:
|
|
218
|
+
if hasattr(self.mesh.visual.material, 'baseColorTexture'):
|
|
219
|
+
img = self.mesh.visual.material.baseColorTexture
|
|
220
|
+
else:
|
|
221
|
+
img = self.mesh.visual.material.image
|
|
222
|
+
if img is not None:
|
|
223
|
+
texImg = 255.*np.asanyarray(img.convert("RGB"))
|
|
224
|
+
fil.create_dataset('mesh/textureImg', data=texImg, dtype='uint8')
|
|
225
|
+
|
|
226
|
+
if hasattr(self, 'pts') and self.pts.shape[1]==3:
|
|
227
|
+
fil.create_dataset('points/vertices', data=self.pts, dtype='float32')
|
|
228
|
+
fil.create_dataset('points/normals', data=self.normals, dtype='float32')
|
|
229
|
+
|
|
230
|
+
if hasattr(self, 'decomp_vertices') and self.decomp_vertices.shape[1]==3:
|
|
231
|
+
fil.create_dataset('decomp/vertices', data=self.decomp_vertices, dtype='float32')
|
|
232
|
+
assert self.decomp_faces.shape[0]<65535
|
|
233
|
+
fil.create_dataset('decomp/faces', data=self.decomp_faces, dtype='uint16')
|
|
234
|
+
fil.create_dataset('decomp/colors', data=self.decomp_colors, dtype='uint8')
|
|
235
|
+
assert self.decomp_parts.shape[ 0]<65535
|
|
236
|
+
fil.create_dataset('decomp/parts', data=self.decomp_parts, dtype='uint16')
|
|
237
|
+
|
|
238
|
+
if inertia:
|
|
239
|
+
fil.create_dataset('inertia/mass', data=[self.mesh.mass], dtype='float32')
|
|
240
|
+
fil.create_dataset('inertia/com', data=self.mesh.center_mass, dtype='float32')
|
|
241
|
+
if self.inertiaIsDiagonal:
|
|
242
|
+
fil.create_dataset('inertia/tensor', data=np.diagonal(self.mesh.moment_inertia), dtype='float32')
|
|
243
|
+
else:
|
|
244
|
+
fil.create_dataset('inertia/tensor', data=self.mesh.moment_inertia, dtype='float32')
|
|
245
|
+
|
|
246
|
+
def load_h5(self, file):
|
|
247
|
+
print('=== file: ', file)
|
|
248
|
+
self.filebase = os.path.splitext(file)[0]
|
|
249
|
+
path, _ = os.path.split(file)
|
|
250
|
+
with h5py.File(file, 'r') as fil:
|
|
251
|
+
V = fil['mesh/vertices'][()]
|
|
252
|
+
T = fil['mesh/faces'][()]
|
|
253
|
+
if 'mesh/colors' in fil:
|
|
254
|
+
C = fil['mesh/colors'][()]
|
|
255
|
+
else:
|
|
256
|
+
C = None
|
|
257
|
+
|
|
258
|
+
texture = None
|
|
259
|
+
texImage = None
|
|
260
|
+
if 'mesh/textureFile' in fil:
|
|
261
|
+
self.textureFile = fil['mesh/textureFile'][()]
|
|
262
|
+
self.textureFile = ''.join([chr(i) for i in self.textureFile])[:-1] #cut the last embedding zero
|
|
263
|
+
print(self.textureFile)
|
|
264
|
+
texImage = Image.open(os.path.join(path, self.textureFile))
|
|
265
|
+
material = trimesh.visual.texture.SimpleMaterial(image=texImage)
|
|
266
|
+
if 'mesh/textureImg' in fil:
|
|
267
|
+
texImage = fil['mesh/textureImg'][()]
|
|
268
|
+
material = trimesh.visual.texture.SimpleMaterial(image=texImage)
|
|
269
|
+
if 'mesh/textureCoords' in fil and texImage is not None:
|
|
270
|
+
uv = fil['mesh/textureCoords'][()]
|
|
271
|
+
uv[:, 1] = 1 - uv[:, 1]
|
|
272
|
+
texture = trimesh.visual.TextureVisuals(uv=uv, image=texImage)
|
|
273
|
+
|
|
274
|
+
if texture is None:
|
|
275
|
+
self.mesh = trimesh.Trimesh(vertices=V, faces=T, vertex_colors=C, process=True)
|
|
276
|
+
else:
|
|
277
|
+
self.mesh = trimesh.Trimesh(vertices=V, faces=T, visual=texture, material=material, process=True)
|
|
278
|
+
|
|
279
|
+
# colors = self.mesh.visual.material.to_color(self.mesh.visual.uv)
|
|
280
|
+
# colors = mat.to_color(uv)
|
|
281
|
+
# vis = trimesh.visual.ColorVisuals(mesh=self.mesh, vertex_colors=colors)
|
|
282
|
+
# self.mesh.visual = vis
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def apply_texture(self, texture_path):
|
|
286
|
+
try:
|
|
287
|
+
texture_image = Image.open(texture_path)
|
|
288
|
+
texture = trimesh.visual.TextureVisuals(image=texture_image, uv=self.mesh.visual.uv)
|
|
289
|
+
self.mesh.visual = texture
|
|
290
|
+
|
|
291
|
+
# texture = np.array(texture_image).astype(float) / 255.0
|
|
292
|
+
# if texture.shape[-1] == 3:
|
|
293
|
+
# alpha = np.ones((texture.shape[0], texture.shape[1], 1))
|
|
294
|
+
# texture = np.concatenate([texture, alpha], axis=-1)
|
|
295
|
+
|
|
296
|
+
# if not hasattr(self.mesh.visual, 'uv'):
|
|
297
|
+
# raise ValueError("Mesh does not have UV coordinates!")
|
|
298
|
+
|
|
299
|
+
# uv_coords = self.mesh.visual.uv
|
|
300
|
+
# uv_coords_copy = uv_coords.copy()
|
|
301
|
+
# uv_coords_copy[:, 1] = 1 - uv_coords_copy[:, 1]
|
|
302
|
+
|
|
303
|
+
# pixel_coords = (uv_coords_copy * [texture.shape[1] - 1, texture.shape[0] - 1]).astype(int)
|
|
304
|
+
# vertex_colors = texture[pixel_coords[:, 1], pixel_coords[:, 0]]
|
|
305
|
+
|
|
306
|
+
# self.mesh.visual = trimesh.visual.ColorVisuals(mesh=self.mesh, vertex_colors=vertex_colors)
|
|
307
|
+
except Exception as e:
|
|
308
|
+
print(texture_path, "is not a valid texture path or could not be applied to the mesh.", e)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# def apply_scaling(self, tri_obj, scale):
|
|
312
|
+
# if scale != 1.0:
|
|
313
|
+
# print(scale)
|
|
314
|
+
# scaling_mat = scale * np.eye(4)
|
|
315
|
+
# scaling_mat[-1, -1] = 1.0
|
|
316
|
+
# tri_obj.apply_transform(scaling_mat)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
# def export_mesh(self, tri_obj, meshfile, ply_out, transformInertia, cvxDecomp):
|
|
320
|
+
# tri_obj.export(ply_out)
|
|
321
|
+
# print(f"Converted {meshfile}")
|
|
322
|
+
# M = MeshHelper(ply_out)
|
|
323
|
+
# if transformInertia:
|
|
324
|
+
# self.pose = M.transformInertia()
|
|
325
|
+
# if cvxDecomp:
|
|
326
|
+
# M.createDecomposition()
|
|
327
|
+
# M.export_h5(meshfile[:-3]+'h5', inertia=False)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# def obj2ply(self, ply_out: str, scale: float=1.0, texture_path: str="none", transformInertia = False, cvxDecomp = False) -> bool:
|
|
331
|
+
# tri_obj = self.mesh
|
|
332
|
+
# if hasattr(tri_obj.visual, 'to_color'):
|
|
333
|
+
# if texture_path == "none":
|
|
334
|
+
# tri_obj.visual = tri_obj.visual.to_color()
|
|
335
|
+
# else:
|
|
336
|
+
# self.apply_texture(tri_obj, texture_path)
|
|
337
|
+
|
|
338
|
+
# elif hasattr(tri_obj.visual, 'vertex_colors'):
|
|
339
|
+
# print("Mesh already has vertex colors.")
|
|
340
|
+
|
|
341
|
+
# else:
|
|
342
|
+
# print(f"Failed on {tri_obj}")
|
|
343
|
+
# return False
|
|
344
|
+
|
|
345
|
+
# self.apply_scaling(tri_obj, scale)
|
|
346
|
+
# self.export_mesh(tri_obj, self.file, ply_out, transformInertia, cvxDecomp)
|
|
347
|
+
|
|
348
|
+
# return True
|
|
349
|
+
|
|
350
|
+
def timeout(signum, frame):
|
|
351
|
+
raise Exception("timeout handler")
|
|
352
|
+
|
|
353
|
+
def get_sdf(mesh, N=30):
|
|
354
|
+
#scale_mesh(mesh, .1)
|
|
355
|
+
# get bounds
|
|
356
|
+
bounds = mesh.bounds.copy()
|
|
357
|
+
size = bounds[1] - bounds[0]
|
|
358
|
+
# enlarge by 10%
|
|
359
|
+
bounds[0] = bounds[0] - .1 * size
|
|
360
|
+
bounds[1] = bounds[1] + .1 * size
|
|
361
|
+
size = bounds[1] - bounds[0]
|
|
362
|
+
# decide on a voxel size
|
|
363
|
+
voxelSize = 1. / (N * np.power(np.prod(size), -1. / 3))
|
|
364
|
+
gridDim = (size / voxelSize).astype(int) + 2
|
|
365
|
+
# change bounds[1] to be on the grid
|
|
366
|
+
bounds[1] = bounds[0] + voxelSize * (gridDim - 1)
|
|
367
|
+
print(' sdf voxel:', voxelSize, 'dim:', gridDim, 'mem:', np.prod(gridDim))
|
|
368
|
+
# create grid
|
|
369
|
+
x_range = np.linspace(bounds[0, 0], bounds[1, 0], num=gridDim[0])
|
|
370
|
+
y_range = np.linspace(bounds[0, 1], bounds[1, 1], num=gridDim[1])
|
|
371
|
+
z_range = np.linspace(bounds[0, 2], bounds[1, 2], num=gridDim[2])
|
|
372
|
+
grid = np.stack(np.meshgrid(x_range, y_range, z_range, indexing='ij'), axis=-1)
|
|
373
|
+
# get sdf
|
|
374
|
+
#sdf = -mesh.nearest.signed_distance(grid).reshape(gridDim)
|
|
375
|
+
sdf = np.empty(gridDim)
|
|
376
|
+
for z in range(0, sdf.shape[2]):
|
|
377
|
+
print('\r slice', z, end=' ', flush=True)
|
|
378
|
+
sdf[:,:,z] = -mesh.nearest.signed_distance(grid[:,:,z,:].reshape(-1, 3)).reshape(gridDim[:2])
|
|
379
|
+
print('- done')
|
|
380
|
+
#scale_mesh(mesh, 10.)
|
|
381
|
+
#bounds *= 10.
|
|
382
|
+
return [sdf, bounds]
|
|
383
|
+
|
|
384
|
+
def display_sdf(sdf):
|
|
385
|
+
ax = plt.subplot()
|
|
386
|
+
cax = plt.axes([0.85, 0.1, 0.075, 0.8])
|
|
387
|
+
for z in range(0, sdf.shape[2]):
|
|
388
|
+
print(z)
|
|
389
|
+
print(sdf[:, :, z])
|
|
390
|
+
im = ax.imshow(sdf[:, :, z])
|
|
391
|
+
plt.colorbar(im, cax=cax)
|
|
392
|
+
plt.pause(.2)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE FilterScript>
|
|
2
2
|
<FilterScript>
|
|
3
|
-
<filter name="Merge Close Vertices">
|
|
4
|
-
<Param tooltip="All the vertices that closer than this threshold are merged together. Use very small values, default values is 1/10000 of bounding box diagonal. " type="RichAbsPerc" description="Merging distance" min="0" value="0.001" name="Threshold" max="0.00938277"/>
|
|
5
|
-
</filter>
|
|
6
3
|
<filter name="Repair non Manifold Edges by removing faces"/>
|
|
7
4
|
<filter name="Simplification: Quadric Edge Collapse Decimation">
|
|
8
5
|
<Param tooltip="The desired final number of faces." type="RichInt" description="Target number of faces" value="20000" name="TargetFaceNum"/>
|
robotic/src/mujoco_io.py
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import numpy as np
|
|
4
|
+
import robotic as ry
|
|
5
|
+
import xml.etree.ElementTree as ET
|
|
6
|
+
from copy import copy
|
|
7
|
+
|
|
8
|
+
class MujocoLoader():
|
|
9
|
+
|
|
10
|
+
def __init__(self, file, visualsOnly=True, defaultConType='0', basePos=[0,0,0], baseQuat=[0,0,0,1]):
|
|
11
|
+
ry.params_add({'cd_into_mesh_files': False})
|
|
12
|
+
|
|
13
|
+
self.visualsOnly = visualsOnly
|
|
14
|
+
self.defaultConType = defaultConType
|
|
15
|
+
self.debug_counter = 0
|
|
16
|
+
self.muj2rai_joint_map = {
|
|
17
|
+
'1 0 0': ry.JT.hingeX,
|
|
18
|
+
'0 1 0': ry.JT.hingeY,
|
|
19
|
+
'0 0 1': ry.JT.hingeZ,
|
|
20
|
+
'-1 0 0': ry.JT.hingeX,
|
|
21
|
+
'0 -1 0': ry.JT.hingeY,
|
|
22
|
+
'0 0 -1': ry.JT.hingeZ,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
tree = ET.parse(file)
|
|
26
|
+
path, _ = os.path.split(file)
|
|
27
|
+
root = tree.getroot()
|
|
28
|
+
|
|
29
|
+
self.materials = {}
|
|
30
|
+
self.textures = {}
|
|
31
|
+
self.meshes = {}
|
|
32
|
+
self.load_assets(root, path)
|
|
33
|
+
self.bodyCount = -1
|
|
34
|
+
|
|
35
|
+
self.C = ry.Config()
|
|
36
|
+
self.base = self.C.addFrame('base')
|
|
37
|
+
self.base.addAttributes({'multibody':True})
|
|
38
|
+
self.base.setPosition(basePos)
|
|
39
|
+
self.base.setQuaternion(baseQuat)
|
|
40
|
+
self.add_node(root, self.base, path, 0)
|
|
41
|
+
|
|
42
|
+
def as_floats(self, input_string):
|
|
43
|
+
return [float(num) for num in input_string.replace(',', ' ').split()]
|
|
44
|
+
|
|
45
|
+
def load_assets(self, root, path):
|
|
46
|
+
texs = root.findall('.//texture')
|
|
47
|
+
for tex in texs:
|
|
48
|
+
name = tex.attrib.get('name', '')
|
|
49
|
+
file = tex.attrib.get('file', '')
|
|
50
|
+
self.textures[name] = os.path.join(path, file)
|
|
51
|
+
|
|
52
|
+
maters = root.findall('.//material')
|
|
53
|
+
for mater in maters:
|
|
54
|
+
name = mater.attrib.get('name', '')
|
|
55
|
+
color = mater.attrib.get('rgba', '')
|
|
56
|
+
texture_name = mater.attrib.get('texture', '')
|
|
57
|
+
if texture_name:
|
|
58
|
+
self.materials[name] = self.textures[texture_name]
|
|
59
|
+
elif color:
|
|
60
|
+
self.materials[name] = color
|
|
61
|
+
else:
|
|
62
|
+
self.materials[name] = ''
|
|
63
|
+
|
|
64
|
+
for mesh in root.findall('.//mesh'):
|
|
65
|
+
name = mesh.attrib.get('name', '')
|
|
66
|
+
file = mesh.attrib.get('file', '')
|
|
67
|
+
if file.startswith('visual') or file.startswith('collision'): #HACK: the true path is hidden in some compiler attribute
|
|
68
|
+
file = 'meshes/'+file
|
|
69
|
+
mesh.attrib['file'] = file
|
|
70
|
+
self.meshes[name] = mesh.attrib
|
|
71
|
+
|
|
72
|
+
def add_node(self, node, f_parent, path, level):
|
|
73
|
+
if 'file' in node.attrib:
|
|
74
|
+
file = node.attrib['file']
|
|
75
|
+
node.attrib['file'] = os.path.join(path, file)
|
|
76
|
+
|
|
77
|
+
print('|'+level*' ', node.tag, node.attrib)
|
|
78
|
+
|
|
79
|
+
if node.tag == 'include':
|
|
80
|
+
file = node.attrib['file']
|
|
81
|
+
tree = ET.parse(file)
|
|
82
|
+
root = tree.getroot()
|
|
83
|
+
path, _ = os.path.split(file)
|
|
84
|
+
self.load_assets(root, path)
|
|
85
|
+
self.add_node(root, f_parent, path, level+1)
|
|
86
|
+
|
|
87
|
+
f_body = None
|
|
88
|
+
|
|
89
|
+
if node.tag == 'body':
|
|
90
|
+
f_body = self.add_body(node, f_parent)
|
|
91
|
+
|
|
92
|
+
for child in node:
|
|
93
|
+
if f_body is None:
|
|
94
|
+
self.add_node(child, f_parent, path, level+1)
|
|
95
|
+
else:
|
|
96
|
+
self.add_node(child, f_body, path, level+1)
|
|
97
|
+
|
|
98
|
+
def add_body(self, body, f_parent):
|
|
99
|
+
self.bodyCount += 1
|
|
100
|
+
body_name = body.attrib.get('name', f'body_{self.bodyCount}')
|
|
101
|
+
|
|
102
|
+
# if self.C.getFrame(body_name, False):
|
|
103
|
+
body_name = f'{body_name}_{self.bodyCount}'
|
|
104
|
+
f_body = self.C.addFrame(body_name)
|
|
105
|
+
f_body.setParent(f_parent)
|
|
106
|
+
self.setRelativePose(f_body, body.attrib)
|
|
107
|
+
|
|
108
|
+
for i, joint in enumerate(body.findall('./joint')):
|
|
109
|
+
axis = joint.attrib.get('axis', None)
|
|
110
|
+
limits = joint.attrib.get('range', None)
|
|
111
|
+
joint_name = joint.attrib.get('name', f'{body_name}_joint{i*"_"}')
|
|
112
|
+
f_origin = self.C.addFrame(f'{joint_name}_origin')
|
|
113
|
+
f_origin.setParent(f_body)
|
|
114
|
+
self.setRelativePose(f_origin, joint.attrib)
|
|
115
|
+
f_origin.unLink()
|
|
116
|
+
f_origin.setParent(f_parent, True)
|
|
117
|
+
|
|
118
|
+
if axis:
|
|
119
|
+
if axis in self.muj2rai_joint_map:
|
|
120
|
+
axis = self.muj2rai_joint_map[axis]
|
|
121
|
+
else:
|
|
122
|
+
vec1 = np.array([0., 0., 1.])
|
|
123
|
+
vec2 = np.array(self.as_floats(axis))
|
|
124
|
+
quat = ry.Quaternion().setDiff(vec1, vec2).getArr()
|
|
125
|
+
f_origin.setRelativeQuaternion(quat)
|
|
126
|
+
axis = ry.JT.hingeZ
|
|
127
|
+
else:
|
|
128
|
+
axis = ry.JT.hingeZ
|
|
129
|
+
|
|
130
|
+
if joint.attrib.get('type', 'hinge')=='slide':
|
|
131
|
+
trans_map = {
|
|
132
|
+
ry.JT.hingeX: ry.JT.transX,
|
|
133
|
+
ry.JT.hingeY: ry.JT.transY,
|
|
134
|
+
ry.JT.hingeZ: ry.JT.transZ}
|
|
135
|
+
axis = trans_map[axis]
|
|
136
|
+
|
|
137
|
+
if not limits:
|
|
138
|
+
limits = '-1 1'
|
|
139
|
+
|
|
140
|
+
# if self.C.getFrame(joint_name, False):
|
|
141
|
+
# joint_name = f'{joint_name}_{self.bodyCount}'
|
|
142
|
+
f_joint = self.C.addFrame(joint_name)
|
|
143
|
+
f_joint.setParent(f_origin)
|
|
144
|
+
f_joint.setJoint(axis, self.as_floats(limits))
|
|
145
|
+
|
|
146
|
+
# relink body:
|
|
147
|
+
f_parent = f_joint
|
|
148
|
+
f_body.unLink()
|
|
149
|
+
f_body.setParent(f_parent, True)
|
|
150
|
+
|
|
151
|
+
for i, geom in enumerate(body.findall('./geom')):
|
|
152
|
+
isColl = geom.attrib.get('contype', self.defaultConType)!='0' or 'coll' in geom.attrib.get('class','')
|
|
153
|
+
if self.visualsOnly and isColl:
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
f_shape = self.C.addFrame(f'{body_name}_shape{i}')
|
|
157
|
+
f_shape.setParent(f_body)
|
|
158
|
+
|
|
159
|
+
if 'mesh' in geom.attrib:
|
|
160
|
+
mesh = geom.attrib.get('mesh', '')
|
|
161
|
+
material_name = geom.attrib.get('material', '')
|
|
162
|
+
texture_path = self.materials.get(material_name, None)
|
|
163
|
+
meshfile = self.meshes[mesh]['file']
|
|
164
|
+
scale = self.as_floats(self.meshes[mesh].get('scale', '1 1 1'))
|
|
165
|
+
|
|
166
|
+
f_shape.setMeshFile(meshfile, scale[0])
|
|
167
|
+
|
|
168
|
+
if texture_path:
|
|
169
|
+
if len(texture_path.split()) == 4: # Is a color rgba
|
|
170
|
+
f_shape.setColor(self.as_floats(texture_path))
|
|
171
|
+
|
|
172
|
+
elif 'type' in geom.attrib:
|
|
173
|
+
size = self.as_floats(geom.attrib['size'])
|
|
174
|
+
if geom.attrib['type']=='capsule':
|
|
175
|
+
if 'fromto' in geom.attrib:
|
|
176
|
+
fromto = self.as_floats(geom.attrib['fromto'])
|
|
177
|
+
a, b = np.array(fromto[:3]), np.array(fromto[3:])
|
|
178
|
+
l = np.linalg.norm(b-a)
|
|
179
|
+
q = ry.Quaternion().setDiff([0,0,1],(b-a)/l)
|
|
180
|
+
f_shape.setRelativePosition(0.5*(a+b))
|
|
181
|
+
f_shape.setRelativeQuaternion(q.getArr())
|
|
182
|
+
f_shape.setShape(ry.ST.capsule, [l, size[0]])
|
|
183
|
+
elif len(size)==2:
|
|
184
|
+
f_shape.setShape(ry.ST.capsule, [2.*size[1], size[0]])
|
|
185
|
+
|
|
186
|
+
elif geom.attrib['type']=='cylinder':
|
|
187
|
+
if len(size)==2:
|
|
188
|
+
f_shape.setShape(ry.ST.cylinder, [2.*size[1], size[0]])
|
|
189
|
+
|
|
190
|
+
elif geom.attrib['type']=='box':
|
|
191
|
+
assert len(size)==3
|
|
192
|
+
size = [2.*f for f in size]
|
|
193
|
+
f_shape.setShape(ry.ST.box, size)
|
|
194
|
+
if geom.attrib.get('material', None):
|
|
195
|
+
texture_path = self.materials[geom.attrib.get('material', None)]
|
|
196
|
+
if len(texture_path.split()) == 4: # Is a color rgba
|
|
197
|
+
f_shape.setColor(self.as_floats(texture_path))
|
|
198
|
+
else:
|
|
199
|
+
print('applying to box:', texture_path)
|
|
200
|
+
#TODO incorperate <texrepeat> tag correctly
|
|
201
|
+
uv_coords = np.array([
|
|
202
|
+
[0, 0], # vertex 0
|
|
203
|
+
[size[0], 0], # vertex 1
|
|
204
|
+
[size[0], size[1]], # vertex 2
|
|
205
|
+
[0, size[1]], # vertex 3
|
|
206
|
+
[0, 0], # vertex 0
|
|
207
|
+
[size[0], 0], # vertex 1
|
|
208
|
+
[size[0], size[1]], # vertex 2
|
|
209
|
+
[0, size[1]], # vertex 3
|
|
210
|
+
])
|
|
211
|
+
|
|
212
|
+
f_shape.setTextureFile(texture_path, uv_coords)
|
|
213
|
+
|
|
214
|
+
elif geom.attrib['type']=='sphere':
|
|
215
|
+
if len(size)==1:
|
|
216
|
+
f_shape.setShape(ry.ST.sphere, size)
|
|
217
|
+
|
|
218
|
+
self.setRelativePose(f_shape, geom.attrib)
|
|
219
|
+
|
|
220
|
+
if geom.attrib.get('rgba', None):
|
|
221
|
+
if geom.attrib.get('material', None) is None:
|
|
222
|
+
f_shape.setColor(self.as_floats(geom.attrib['rgba']))
|
|
223
|
+
|
|
224
|
+
elif isColl:
|
|
225
|
+
f_shape.setColor([1,0,0,.2])
|
|
226
|
+
|
|
227
|
+
return f_body
|
|
228
|
+
|
|
229
|
+
def setRelativePose(self, f, attrib):
|
|
230
|
+
pos = attrib.get('pos', None)
|
|
231
|
+
if pos:
|
|
232
|
+
f.setRelativePosition(self.as_floats(pos))
|
|
233
|
+
|
|
234
|
+
quat = attrib.get('quat', None)
|
|
235
|
+
if quat:
|
|
236
|
+
f.setRelativeQuaternion(self.as_floats(quat))
|
|
237
|
+
|
|
238
|
+
rpy = attrib.get('euler', None)
|
|
239
|
+
if rpy:
|
|
240
|
+
q = ry.Quaternion()
|
|
241
|
+
q.setRollPitchYaw(self.as_floats(rpy))
|
|
242
|
+
f.setRelativeQuaternion(q.getArr())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import yaml
|
|
2
|
+
#from ruamel import yaml
|
|
3
|
+
|
|
4
|
+
# yaml dump tweaks
|
|
5
|
+
class noflow_dict(dict):
|
|
6
|
+
pass
|
|
7
|
+
def noflow_dict_rep(dumper, data):
|
|
8
|
+
return dumper.represent_mapping("tag:yaml.org,2002:map", data, flow_style=False)
|
|
9
|
+
yaml.add_representer(noflow_dict, noflow_dict_rep)
|
|
10
|
+
|
|
11
|
+
class quoted_string(str):
|
|
12
|
+
pass
|
|
13
|
+
def quoted_string_rep(dumper, data):
|
|
14
|
+
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"')
|
|
15
|
+
yaml.add_representer(quoted_string, quoted_string_rep)
|
|
16
|
+
|
|
17
|
+
def yaml_write_dict(data, filename):
|
|
18
|
+
with open(filename, 'w') as fil:
|
|
19
|
+
yaml.dump(noflow_dict(data), fil, default_flow_style=True, sort_keys=False, width=500)
|
robotic/test.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import point_cloud_utils as pcu
|
|
2
|
+
|
|
3
|
+
# v is a [n, 3] shaped NumPy array of vertices
|
|
4
|
+
# f is a [m, 3] shaped integer NumPy array of indices into v
|
|
5
|
+
# n is a [n, 3] shaped NumPy array of vertex normals
|
|
6
|
+
v, f, n = pcu.load_mesh_vfn("bunny.ply")
|
|
7
|
+
|
|
8
|
+
# Generate barycentric coordinates of random samples
|
|
9
|
+
num_samples = 1000
|
|
10
|
+
fid, bc = pcu.sample_mesh_random(v, f, num_samples)
|
|
11
|
+
|
|
12
|
+
# Interpolate the vertex positions and normals using the returned barycentric coordinates
|
|
13
|
+
# to get sample positions and normals
|
|
14
|
+
rand_positions = pcu.interpolate_barycentric_coords(f, fid, bc, v)
|
|
15
|
+
rand_normals = pcu.interpolate_barycentric_coords(f, fid, bc, n)
|
robotic/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.
|
|
1
|
+
__version__ = '0.3.0'
|