diff --git a/BiorbdViz/__init__.py b/BiorbdViz/__init__.py index f4650a9..f493d48 100644 --- a/BiorbdViz/__init__.py +++ b/BiorbdViz/__init__.py @@ -9,8 +9,8 @@ if biorbd.currentLinearAlgebraBackend() == 1: import casadi -from pyomeca import Markers3d -from .biorbd_vtk import VtkModel, VtkWindow, Mesh, MeshCollection, RotoTrans, RotoTransCollection +from pyomeca import Markers +from .biorbd_vtk import VtkModel, VtkWindow, Mesh, Rototrans from PyQt5.QtWidgets import ( QSlider, QVBoxLayout, @@ -83,7 +83,7 @@ def _get_data_from_casadi(self, Q=None, compute_kin=True): class CoM(BiorbdFunc): def __init__(self, model): super().__init__(model) - self.data = np.ndarray((3, 1, 1)) + self.data = np.ones((4, 1, 1)) def _prepare_function_for_casadi(self): Qsym = casadi.MX.sym("Q", self.m.nbQ(), 1) @@ -95,10 +95,10 @@ def _get_data_from_eigen(self, Q=None, compute_kin=True): else: CoM = self.m.CoM(Q, False) for i in range(self.m.nbSegment()): - self.data[:, 0, 0] = CoM.to_array() + self.data[:3, 0, 0] = CoM.to_array() def _get_data_from_casadi(self, Q=None, compute_kin=True): - self.data[:, :, 0] = self.CoM(Q) + self.data[:3, :, 0] = self.CoM(Q) class CoMbySegment(BiorbdFunc): def __init__(self, model): @@ -116,12 +116,12 @@ def _get_data_from_eigen(self, Q=None, compute_kin=True): else: allCoM = self.m.CoMbySegment(Q, False) for com in allCoM: - self.data.append(com.to_array()) + self.data.append(np.append(com.to_array(), 1)) def _get_data_from_casadi(self, Q=None, compute_kin=True): self.data = [] for i in range(self.m.nbSegment()): - self.data.append(np.array(self.CoMs(Q)[:, i])) + self.data.append(np.append(self.CoMs(Q)[:, i], 1)) class MusclesPointsInGlobal(BiorbdFunc): def __init__(self, model): @@ -276,17 +276,17 @@ def __init__( # Create all the reference to the things to plot self.nQ = self.model.nbQ() self.Q = np.zeros(self.nQ) - self.markers = Markers3d(np.ndarray((3, self.model.nbMarkers(), 1))) + self.markers = Markers(np.ndarray((3, self.model.nbMarkers(), 1))) if self.show_markers: self.Markers = InterfacesCollections.Markers(self.model) - self.global_center_of_mass = Markers3d(np.ndarray((3, 1, 1))) + self.global_center_of_mass = Markers(np.ndarray((3, 1, 1))) if self.show_global_center_of_mass: self.CoM = InterfacesCollections.CoM(self.model) - self.segments_center_of_mass = Markers3d(np.ndarray((3, self.model.nbSegment(), 1))) + self.segments_center_of_mass = Markers(np.ndarray((3, self.model.nbSegment(), 1))) if self.show_segments_center_of_mass: self.CoMbySegment = InterfacesCollections.CoMbySegment(self.model) if self.show_meshes: - self.mesh = MeshCollection() + self.mesh = [] self.meshPointsInMatrix = InterfacesCollections.MeshPointsInMatrix(self.model) for i, vertices in enumerate(self.meshPointsInMatrix.get_data(Q=self.Q, compute_kin=False)): triangles = np.ndarray((len(self.model.meshFaces()[i]), 3), dtype="int32") @@ -294,17 +294,17 @@ def __init__( triangles[k, :] = patch.face() self.mesh.append(Mesh(vertex=vertices, triangles=triangles.T)) self.model.updateMuscles(self.Q, True) - self.muscles = MeshCollection() + self.muscles = [] for group_idx in range(self.model.nbMuscleGroups()): for muscle_idx in range(self.model.muscleGroup(group_idx).nbMuscles()): musc = self.model.muscleGroup(group_idx).muscle(muscle_idx) tp = np.zeros((3, len(musc.position().musclesPointsInGlobal()), 1)) self.muscles.append(Mesh(vertex=tp)) self.musclesPointsInGlobal = InterfacesCollections.MusclesPointsInGlobal(self.model) - self.rt = RotoTransCollection() + self.rt = [] self.allGlobalJCS = InterfacesCollections.AllGlobalJCS(self.model) for rt in self.allGlobalJCS.get_data(Q=self.Q, compute_kin=False): - self.rt.append(RotoTrans(rt)) + self.rt.append(Rototrans(rt)) if self.show_global_ref_frame: self.vtk_model.create_global_ref_frame() @@ -757,18 +757,18 @@ def __load_movement(self): def __set_markers_from_q(self): self.markers[0:3, :, :] = self.Markers.get_data(Q=self.Q, compute_kin=False) - self.vtk_model.update_markers(self.markers.get_frame(0)) + self.vtk_model.update_markers(self.markers.isel(time=[0])) def __set_global_center_of_mass_from_q(self): com = self.CoM.get_data(Q=self.Q, compute_kin=False) - self.global_center_of_mass[0:3, 0, 0] = com.reshape(-1, 1) - self.vtk_model.update_global_center_of_mass(self.global_center_of_mass.get_frame(0)) + self.global_center_of_mass.loc[{"channel": 0, "time": 0}] = com.squeeze() + self.vtk_model.update_global_center_of_mass(self.global_center_of_mass.isel(time=[0])) def __set_segments_center_of_mass_from_q(self): coms = self.CoMbySegment.get_data(Q=self.Q, compute_kin=False) for k, com in enumerate(coms): - self.segments_center_of_mass[0:3, k, 0] = com.reshape(-1, 1) - self.vtk_model.update_segments_center_of_mass(self.segments_center_of_mass.get_frame(0)) + self.segments_center_of_mass.loc[{"channel": k, "time": 0}] = com.squeeze() + self.vtk_model.update_segments_center_of_mass(self.segments_center_of_mass.isel(time=[0])) def __set_meshes_from_q(self): for m, meshes in enumerate(self.meshPointsInMatrix.get_data(Q=self.Q, compute_kin=False)): @@ -784,12 +784,12 @@ def __set_muscles_from_q(self): for muscle_idx in range(self.model.muscleGroup(group_idx).nbMuscles()): musc = self.model.muscleGroup(group_idx).muscle(muscle_idx) for k, pts in enumerate(musc.position().musclesPointsInGlobal()): - self.muscles.get_frame(0)[idx][0:3, k, 0] = muscles[cmp] + self.muscles[idx].loc[{"channel": k, "time": 0}] = np.append(muscles[cmp], 1) cmp += 1 idx += 1 self.vtk_model.update_muscle(self.muscles) def __set_rt_from_q(self): for k, rt in enumerate(self.allGlobalJCS.get_data(Q=self.Q, compute_kin=False)): - self.rt[k] = RotoTrans(rt) + self.rt[k] = Rototrans(rt) self.vtk_model.update_rt(self.rt) diff --git a/BiorbdViz/biorbd_vtk.py b/BiorbdViz/biorbd_vtk.py index ead6545..c2046f1 100644 --- a/BiorbdViz/biorbd_vtk.py +++ b/BiorbdViz/biorbd_vtk.py @@ -26,8 +26,8 @@ ) from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor -from pyomeca import Markers3d, RotoTrans, RotoTransCollection -from .mesh import Mesh, MeshCollection +from pyomeca import Markers, Rototrans +from .mesh import Mesh first = True if first: @@ -182,7 +182,7 @@ def __init__( self.setAutoFillBackground(True) self.setPalette(palette) - self.markers = Markers3d() + self.markers = Markers() self.markers_size = markers_size self.markers_color = markers_color self.markers_opacity = markers_opacity @@ -192,31 +192,31 @@ def __init__( self.global_ref_frame_length = global_ref_frame_length self.global_ref_frame_width = global_ref_frame_width - self.global_center_of_mass = Markers3d() + self.global_center_of_mass = Markers() self.global_center_of_mass_size = global_center_of_mass_size self.global_center_of_mass_color = global_center_of_mass_color self.global_center_of_mass_opacity = global_center_of_mass_opacity self.global_center_of_mass_actors = list() - self.segments_center_of_mass = Markers3d() + self.segments_center_of_mass = Markers() self.segments_center_of_mass_size = segments_center_of_mass_size self.segments_center_of_mass_color = segments_center_of_mass_color self.segments_center_of_mass_opacity = segments_center_of_mass_opacity self.segments_center_of_mass_actors = list() - self.all_rt = RotoTransCollection() + self.all_rt = [] self.n_rt = 0 self.rt_length = rt_length self.rt_width = rt_width self.rt_actors = list() self.parent_window.should_reset_camera = True - self.all_meshes = MeshCollection() + self.all_meshes = [] self.mesh_color = mesh_color self.mesh_opacity = mesh_opacity self.mesh_actors = list() - self.all_muscles = MeshCollection() + self.all_muscles = [] self.muscle_color = muscle_color self.muscle_opacity = muscle_opacity self.muscle_actors = list() @@ -266,7 +266,7 @@ def new_marker_set(self, markers): One frame of markers """ - if markers.get_num_frames() != 1: + if markers.time.size != 1: raise IndexError("Markers should be from one frame only") self.markers = markers @@ -276,7 +276,7 @@ def new_marker_set(self, markers): self.markers_actors = list() # Create the geometry of a point (the coordinate) points = vtk.vtkPoints() - for i in range(markers.get_num_markers()): + for i in range(markers.channel.size): # Create a mapper mapper = vtkPolyDataMapper() @@ -300,9 +300,9 @@ def update_markers(self, markers): """ - if markers.get_num_frames() != 1: + if markers.time.size != 1: raise IndexError("Markers should be from one frame only") - if markers.get_num_markers() != self.markers.get_num_markers(): + if markers.channel.size != self.markers.channel.size: self.new_marker_set(markers) return # Prevent calling update_markers recursively self.markers = markers @@ -364,7 +364,7 @@ def new_global_center_of_mass_set(self, global_center_of_mass): One frame of segment center of mas """ - if global_center_of_mass.get_num_frames() != 1: + if global_center_of_mass.channel.size != 1: raise IndexError("Global center of mass should be from one frame only") self.global_center_of_mass = global_center_of_mass @@ -374,7 +374,7 @@ def new_global_center_of_mass_set(self, global_center_of_mass): self.global_center_of_mass_actors = list() # Create the geometry of a point (the coordinate) points = vtk.vtkPoints() - for i in range(global_center_of_mass.get_num_markers()): + for i in range(global_center_of_mass.channel.size): # Create a mapper mapper = vtkPolyDataMapper() @@ -398,9 +398,9 @@ def update_global_center_of_mass(self, global_center_of_mass): """ - if global_center_of_mass.get_num_frames() != 1: + if global_center_of_mass.time.size != 1: raise IndexError("Segment center of mass should be from one frame only") - if global_center_of_mass.get_num_markers() != self.global_center_of_mass.get_num_markers(): + if global_center_of_mass.channel.size != self.global_center_of_mass.channel.size: self.new_global_center_of_mass_set(global_center_of_mass) return # Prevent calling update_center_of_mass recursively self.global_center_of_mass = global_center_of_mass @@ -461,7 +461,7 @@ def new_segments_center_of_mass_set(self, segments_center_of_mass): One frame of segment center of mas """ - if segments_center_of_mass.get_num_frames() != 1: + if segments_center_of_mass.time.size != 1: raise IndexError("Segments center of mass should be from one frame only") self.segments_center_of_mass = segments_center_of_mass @@ -471,7 +471,7 @@ def new_segments_center_of_mass_set(self, segments_center_of_mass): self.segments_center_of_mass_actors = list() # Create the geometry of a point (the coordinate) points = vtk.vtkPoints() - for i in range(segments_center_of_mass.get_num_markers()): + for i in range(segments_center_of_mass.channel.size): # Create a mapper mapper = vtkPolyDataMapper() @@ -495,9 +495,9 @@ def update_segments_center_of_mass(self, segments_center_of_mass): """ - if segments_center_of_mass.get_num_frames() != 1: + if segments_center_of_mass.time.size != 1: raise IndexError("Segment center of mass should be from one frame only") - if segments_center_of_mass.get_num_markers() != self.segments_center_of_mass.get_num_markers(): + if segments_center_of_mass.channel.size != self.segments_center_of_mass.channel.size: self.new_segments_center_of_mass_set(segments_center_of_mass) return # Prevent calling update_center_of_mass recursively self.segments_center_of_mass = segments_center_of_mass @@ -547,14 +547,11 @@ def new_mesh_set(self, all_meshes): """ if isinstance(all_meshes, Mesh): - mesh_tp = MeshCollection() + mesh_tp = [] mesh_tp.append(all_meshes) all_meshes = mesh_tp - if all_meshes.get_num_frames() != 1: - raise IndexError("Mesh should be from one frame only") - - if not isinstance(all_meshes, MeshCollection): + if not isinstance(all_meshes, list): raise TypeError("Please send a list of mesh to update_mesh") self.all_meshes = all_meshes @@ -565,13 +562,16 @@ def new_mesh_set(self, all_meshes): # Create the geometry of a point (the coordinate) points = vtkPoints() for (i, mesh) in enumerate(self.all_meshes): + if mesh.time.size != 1: + raise IndexError("Mesh should be from one frame only") + points = vtkPoints() - for j in range(mesh.get_num_vertex()): + for j in range(mesh.channel.size): points.InsertNextPoint([0, 0, 0]) # Create an array for each triangle cell = vtkCellArray() - for j in range(mesh.get_num_triangles()): # For each triangle + for j in range(mesh.triangles.shape[1]): # For each triangle line = vtkPolyLine() line.GetPointIds().SetNumberOfIds(4) for k in range(len(mesh.triangles[:, j])): # For each index @@ -608,26 +608,26 @@ def update_mesh(self, all_meshes): """ if isinstance(all_meshes, Mesh): - mesh_tp = MeshCollection() + mesh_tp = [] mesh_tp.append(all_meshes) all_meshes = mesh_tp - if all_meshes.get_num_frames() != 1: - raise IndexError("Mesh should be from one frame only") + for i, mesh in enumerate(all_meshes): + if mesh.time.size != 1: + raise IndexError("Mesh should be from one frame only") - for i in range(len(all_meshes)): - if all_meshes.get_mesh(i).get_num_vertex() != self.all_meshes.get_mesh(i).get_num_vertex(): + if len(self.all_meshes) <= i or mesh.channel.size != self.all_meshes[i].channel.size: self.new_mesh_set(all_meshes) return # Prevent calling update_markers recursively - if not isinstance(all_meshes, MeshCollection): + if not isinstance(all_meshes, list): raise TypeError("Please send a list of mesh to update_mesh") self.all_meshes = all_meshes for (i, mesh) in enumerate(self.all_meshes): points = vtkPoints() - n_vertex = mesh.get_num_vertex() + n_vertex = mesh.channel.size mesh = np.array(mesh) for j in range(n_vertex): points.InsertNextPoint(mesh[0:3, j]) @@ -670,14 +670,11 @@ def new_muscle_set(self, all_muscles): """ if isinstance(all_muscles, Mesh): - musc_tp = MeshCollection() + musc_tp = [] musc_tp.append(all_muscles) all_muscles = musc_tp - if all_muscles.get_num_frames() != 1: - raise IndexError("Muscles should be from one frame only") - - if not isinstance(all_muscles, MeshCollection): + if not isinstance(all_muscles, list): raise TypeError("Please send a list of muscle to update_muscle") self.all_muscles = all_muscles @@ -688,13 +685,16 @@ def new_muscle_set(self, all_muscles): # Create the geometry of a point (the coordinate) points = vtkPoints() for (i, mesh) in enumerate(self.all_muscles): + if mesh.time.size != 1: + raise IndexError("Muscles should be from one frame only") + points = vtkPoints() - for j in range(mesh.get_num_vertex()): + for j in range(mesh.channel.size): points.InsertNextPoint([0, 0, 0]) # Create an array for each triangle cell = vtkCellArray() - for j in range(mesh.get_num_triangles()): # For each triangle + for j in range(mesh.triangles.shape[1]): # For each triangle line = vtkPolyLine() line.GetPointIds().SetNumberOfIds(4) for k in range(len(mesh.triangles[:, j])): # For each index @@ -732,26 +732,26 @@ def update_muscle(self, all_muscles): """ if isinstance(all_muscles, Mesh): - musc_tp = MeshCollection() + musc_tp = [] musc_tp.append(all_muscles) all_muscles = musc_tp - if all_muscles.get_num_frames() != 1: - raise IndexError("Muscle should be from one frame only") + for i, muscle in enumerate(all_muscles): + if muscle.time.size != 1: + raise IndexError("Muscle should be from one frame only") - for i in range(len(all_muscles)): - if all_muscles.get_mesh(i).get_num_vertex() != self.all_muscles.get_mesh(i).get_num_vertex(): + if len(self.all_muscles) <= i or muscle.channel.size != self.all_muscles[i].channel.size: self.new_muscle_set(all_muscles) return # Prevent calling update_markers recursively - if not isinstance(all_muscles, MeshCollection): + if not isinstance(all_muscles, list): raise TypeError("Please send a list of muscles to update_muscle") self.all_muscles = all_muscles for (i, mesh) in enumerate(self.all_muscles): points = vtkPoints() - n_vertex = mesh.get_num_vertex() + n_vertex = mesh.channel.size mesh = np.array(mesh) for j in range(n_vertex): points.InsertNextPoint(mesh[0:3, j]) @@ -764,16 +764,16 @@ def new_rt_set(self, all_rt): Define a new rt set. This function must be called each time the number of rt change Parameters ---------- - all_rt : RotoTransCollection - One frame of all RotoTrans to draw + all_rt : Rototrans + One frame of all Rototrans to draw """ - if isinstance(all_rt, RotoTrans): - rt_tp = RotoTransCollection() + if isinstance(all_rt, Rototrans): + rt_tp = [] rt_tp.append(all_rt[:, :]) all_rt = rt_tp - if not isinstance(all_rt, RotoTransCollection): + if not isinstance(all_rt, list): raise TypeError("Please send a list of rt to new_rt_set") # Remove previous actors from the scene @@ -782,7 +782,7 @@ def new_rt_set(self, all_rt): self.rt_actors = list() for i, rt in enumerate(all_rt): - if rt.get_num_frames() != 1: + if rt.time.size != 1: raise IndexError("RT should be from one frame only") # Create the polyline which will hold the actors @@ -841,42 +841,42 @@ def new_rt_set(self, all_rt): self.parent_window.ren.ResetCamera() # Set rt orientations - self.n_rt = all_rt.get_num_rt() + self.n_rt = len(all_rt) self.update_rt(all_rt) def update_rt(self, all_rt): """ - Update position of the RotoTrans on the screen (but do not repaint) + Update position of the Rototrans on the screen (but do not repaint) Parameters ---------- - all_rt : RotoTransCollection - One frame of all RotoTrans to draw + all_rt : RototransCollection + One frame of all Rototrans to draw """ - if isinstance(all_rt, RotoTrans): - rt_tp = RotoTransCollection() + if isinstance(all_rt, Rototrans): + rt_tp = [] rt_tp.append(all_rt[:, :]) all_rt = rt_tp - if all_rt.get_num_rt() != self.n_rt: + if len(all_rt) != self.n_rt: self.new_rt_set(all_rt) return # Prevent calling update_rt recursively - if not isinstance(all_rt, RotoTransCollection): + if not isinstance(all_rt, list): raise TypeError("Please send a list of rt to new_rt_set") self.all_rt = all_rt for i, rt in enumerate(self.all_rt): - if rt.get_num_frames() != 1: + if rt.time.size != 1: raise IndexError("RT should be from one frame only") # Update the end points of the axes and the origin pts = vtkPoints() - pts.InsertNextPoint(rt.translation()) - pts.InsertNextPoint(rt.translation() + rt[0:3, 0, :] * self.rt_length) - pts.InsertNextPoint(rt.translation() + rt[0:3, 1, :] * self.rt_length) - pts.InsertNextPoint(rt.translation() + rt[0:3, 2, :] * self.rt_length) + pts.InsertNextPoint(rt.meca.translation) + pts.InsertNextPoint(rt.meca.translation + rt.isel(col=0)[0:3] * self.rt_length) + pts.InsertNextPoint(rt.meca.translation + rt.isel(col=1)[0:3] * self.rt_length) + pts.InsertNextPoint(rt.meca.translation + rt.isel(col=2)[0:3] * self.rt_length) # Update polydata in mapper lines_poly_data = self.rt_actors[i].GetMapper().GetInput() @@ -887,8 +887,8 @@ def create_global_ref_frame(self): Define a new global reference frame set. This function must be called once Parameters ---------- - global_ref_frame : RotoTrans - One frame of all RotoTrans to draw + global_ref_frame : Rototrans + One frame of all Rototrans to draw """ diff --git a/BiorbdViz/mesh.py b/BiorbdViz/mesh.py index 457fa30..5a1f9b3 100644 --- a/BiorbdViz/mesh.py +++ b/BiorbdViz/mesh.py @@ -1,20 +1,14 @@ import numpy as np -from pyomeca import FrameDependentNpArrayCollection, Markers3d +from pyomeca.markers import Markers -class Mesh(Markers3d): - def __new__(cls, vertex=np.ndarray((3, 0, 0)), triangles=np.ndarray((3, 0)), *args, **kwargs): - """ - Parameters - ---------- - vertex : np.ndarray - 3xNxF matrix of vertex positions - triangles : np.ndarray, list - Nx3 indexes matrix where N is the number of triangles and the row are the vertex to connect - names : list of string - name of the marker that correspond to second dimension of the positions matrix - """ - +class Mesh(Markers): + def __new__( + cls, + vertex=None, + triangles=np.ndarray((3, 0)), + **kwargs, + ): if isinstance(triangles, list): triangles = np.array(triangles) @@ -28,80 +22,5 @@ def __new__(cls, vertex=np.ndarray((3, 0, 0)), triangles=np.ndarray((3, 0)), *ar for i in range(vertex.shape[1] - 1): triangles[:, i] = [i, i + 1, i] - obj = super(Mesh, cls).__new__(cls, data=vertex, *args, **kwargs) - obj.triangles = triangles - return obj - - def __array_finalize__(self, obj): - super().__array_finalize__(obj) - # Allow slicing - if obj is None or not isinstance(obj, Mesh): - return - self.triangles = getattr(obj, "triangles") - - # --- Get metadata methods - - def get_num_triangles(self): - return self.triangles.shape[1] - - def get_num_vertex(self): - """ - Returns - ------- - The number of vertex - """ - return super().get_num_markers() - - -class MeshCollection(FrameDependentNpArrayCollection): - """ - List of Mesh - """ - - def append(self, mesh): - return super().append(mesh) - - def get_frame(self, f): - """ - Get fth frame of the collection - Parameters - ---------- - f : int - Frame to get - Returns - ------- - Collection of frame f - """ - coll = MeshCollection() - for element in self: - coll.append(element.get_frame(f)) - return coll - - def get_mesh(self, i): - """ - Get a specific Mesh of the collection - Parameters - ---------- - i : int - Index of the Mesh in the collection - - Returns - ------- - All frame of Mesh of index i - """ - if i >= len(self): - return Mesh() - - return self[i] - - # --- Get metadata methods - - def get_num_mesh(self): - """ - Get the number of Mesh in the collection - Returns - ------- - n : int - Number of Mesh in the collection - """ - return self.get_num_segments() + attrs = {'triangles': triangles} + return Markers.__new__(cls, vertex, None, None, attrs=attrs, **kwargs) diff --git a/environment.yml b/environment.yml index 42a757a..dd73ecf 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,10 @@ name: pyoviz channels: - conda-forge dependencies: + - python - pyomeca - vtk - pyqt - - biorbd + - casadi + - rbdl=*=*casadi* + - biorbd=*=*casadi*