From df878e149881a09e47338139e7cac1ca2c228724 Mon Sep 17 00:00:00 2001 From: Ipuch Date: Wed, 20 Oct 2021 19:40:29 -0400 Subject: [PATCH 1/5] displaying softContacts, only need access to softcontact radius --- bioviz/__init__.py | 99 ++++++++++++++++++++++++++++------------ bioviz/biorbd_vtk.py | 106 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 30 deletions(-) diff --git a/bioviz/__init__.py b/bioviz/__init__.py index 59f679e..31777b1 100644 --- a/bioviz/__init__.py +++ b/bioviz/__init__.py @@ -126,6 +126,27 @@ def _get_data_from_casadi(self, Q=None, compute_kin=True): if self.m.nbContacts(): self.data[:, :, 0] = np.array(self.contacts(Q)) + class SoftContacts(BiorbdFunc): + def __init__(self, model): + super().__init__(model) + self.data = np.ndarray((3, self.m.nbSoftContacts(), 1)) + + def _prepare_function_for_casadi(self): + q_sym = casadi.MX.sym("Q", self.m.nbQ(), 1) + self.soft_contacts = biorbd.to_casadi_func("SoftContacts", self.m.softContacts, q_sym, True) + + def _get_data_from_eigen(self, Q=None, compute_kin=True): + if compute_kin: + soft_contacts = self.m.softContacts(Q, True) + else: + soft_contacts = self.m.softContacts(Q, False) + for i in range(self.m.nbSoftContacts()): + self.data[:, i, 0] = soft_contacts[i].to_array() + + def _get_data_from_casadi(self, Q=None, compute_kin=True): + if self.m.nbContacts(): + self.data[:, :, 0] = np.array(self.soft_contacts(Q)) + class CoM(BiorbdFunc): def __init__(self, model): super().__init__(model) @@ -261,28 +282,30 @@ def _get_data_from_casadi(self, Q=None, compute_kin=True): class Viz: def __init__( - self, - model_path=None, - loaded_model=None, - show_meshes=True, - patch_color=(0.89, 0.855, 0.788), - show_global_center_of_mass=True, - show_segments_center_of_mass=True, - segments_center_of_mass_size=0.005, - show_global_ref_frame=True, - show_local_ref_frame=True, - show_markers=True, - experimental_markers_color=(1, 1, 1), - markers_size=0.010, - show_contacts=True, - contacts_size=0.010, - show_muscles=True, - show_wrappings=True, - show_analyses_panel=True, - background_color=(0.5, 0.5, 0.5), - force_wireframe=False, - experimental_forces_colors=(85, 78, 0), - **kwargs, + self, + model_path=None, + loaded_model=None, + show_meshes=True, + patch_color=(0.89, 0.855, 0.788), + show_global_center_of_mass=True, + show_segments_center_of_mass=True, + segments_center_of_mass_size=0.005, + show_global_ref_frame=True, + show_local_ref_frame=True, + show_markers=True, + experimental_markers_color=(1, 1, 1), + markers_size=0.010, + show_contacts=True, + contacts_size=0.010, + show_soft_contacts=True, + soft_contacts_color=(1, 0.35, 0), + show_muscles=True, + show_wrappings=True, + show_analyses_panel=True, + background_color=(0.5, 0.5, 0.5), + force_wireframe=False, + experimental_forces_colors=(85, 78, 0), + **kwargs, ): """ Class that easily shows a biorbd model @@ -311,10 +334,12 @@ def __init__( show_contacts = False show_muscles = False show_wrappings = False + show_soft_contact = False, # Create the plot self.vtk_window = VtkWindow(background_color=background_color) self.vtk_markers_size = markers_size + soft_contacts_size = [0.1] * self.model.nbSoftContacts() self.vtk_model = VtkModel( self.vtk_window, markers_color=(0, 0, 1), @@ -324,6 +349,8 @@ def __init__( segments_center_of_mass_size=segments_center_of_mass_size, force_wireframe=force_wireframe, force_color=experimental_forces_colors, + soft_contacts_size=soft_contacts_size, + soft_contacts_color=soft_contacts_color, ) self.vtk_model_markers: VtkModel = None self.is_executing = False @@ -347,6 +374,8 @@ def __init__( self.force_normalization_ratio = None self.show_contacts = show_contacts + self.show_soft_contacts = show_soft_contacts + self.soft_contacts_color = soft_contacts_color self.show_global_ref_frame = show_global_ref_frame self.show_global_center_of_mass = show_global_center_of_mass self.show_segments_center_of_mass = show_segments_center_of_mass @@ -375,6 +404,9 @@ def __init__( if self.show_contacts: self.Contacts = InterfacesCollections.Contact(self.model) self.contacts = Markers(np.ndarray((3, self.model.nbContacts(), 1))) + if self.show_soft_contacts: + self.SoftContacts = InterfacesCollections.SoftContacts(self.model) + self.soft_contacts = Markers(np.ndarray((3, self.model.nbSoftContacts(), 1))) if self.show_global_center_of_mass: self.CoM = InterfacesCollections.CoM(self.model) self.global_center_of_mass = Markers(np.ndarray((3, 1, 1))) @@ -523,8 +555,12 @@ def set_q(self, Q, refresh_window=True): self.__set_segments_center_of_mass_from_q() if self.show_markers: self.__set_markers_from_q() + if self.show_markers: + self.__set_markers_from_q() if self.show_contacts: self.__set_contacts_from_q() + if self.show_soft_contacts: + self.__set_soft_contacts_from_q() if self.show_wrappings: self.__set_wrapping_from_q() @@ -816,7 +852,7 @@ def __move_avatar_from_sliders(self): self.set_q(self.Q) def __update_muscle_analyses_graphs( - self, skip_muscle_length, skip_moment_arm, skip_passive_forces, skip_active_forces + self, skip_muscle_length, skip_moment_arm, skip_passive_forces, skip_active_forces ): # Adjust muscle analyses if needed if self.active_analyses_widget == self.analyses_muscle_widget: @@ -977,8 +1013,7 @@ def load_experimental_markers(self, data, auto_start=True, ignore_animation_warn ) self.vtk_model_markers = VtkModel( - self.vtk_window, markers_color=self.experimental_markers_color, markers_size=self.vtk_markers_size - ) + self.vtk_window, markers_color=self.experimental_markers_color, markers_size=self.vtk_markers_size) self.__set_movement_slider() self.show_experimental_markers = True @@ -989,7 +1024,7 @@ def load_experimental_markers(self, data, auto_start=True, ignore_animation_warn self.__start_stop_animation() def load_experimental_forces( - self, data, segments=None, normalization_ratio=0.2, auto_start=True, ignore_animation_warning=True + self, data, segments=None, normalization_ratio=0.2, auto_start=True, ignore_animation_warning=True ): if isinstance(data, (np.ndarray, xr.DataArray)): self.experimental_forces = data if isinstance(data, xr.DataArray) else xr.DataArray(data) @@ -1027,7 +1062,7 @@ def __set_markers_from_q(self): def __set_experimental_markers_from_frame(self): t_slider = self.movement_slider[0].value() - 1 t = t_slider if t_slider < self.experimental_markers.shape[2] else self.experimental_markers.shape[2] - 1 - self.vtk_model_markers.update_markers(self.experimental_markers[:, :, t : t + 1].isel(time=[0])) + self.vtk_model_markers.update_markers(self.experimental_markers[:, :, t: t + 1].isel(time=[0])) def __set_experimental_forces_from_frame(self): segment_names = [] @@ -1062,13 +1097,17 @@ def __set_experimental_forces_from_frame(self): t_slider = self.movement_slider[0].value() - 1 t = t_slider if t_slider < self.experimental_forces.shape[2] else self.experimental_forces.shape[2] - 1 self.vtk_model.update_force( - segment_jcs, self.experimental_forces[:, :, t : t + 1], max_forces, self.force_normalization_ratio + segment_jcs, self.experimental_forces[:, :, t: t + 1], max_forces, self.force_normalization_ratio ) def __set_contacts_from_q(self): self.contacts[0:3, :, :] = self.Contacts.get_data(Q=self.Q, compute_kin=False) self.vtk_model.update_contacts(self.contacts.isel(time=[0])) + def __set_soft_contacts_from_q(self): + self.soft_contacts[0:3, :, :] = self.SoftContacts.get_data(Q=self.Q, compute_kin=False) + self.vtk_model.update_soft_contacts(self.soft_contacts.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.loc[{"channel": 0, "time": 0}] = com.squeeze() @@ -1105,8 +1144,8 @@ def __set_wrapping_from_q(self): if self.model.muscle(i).pathModifier().object(j).typeOfNode() == biorbd.WRAPPING_HALF_CYLINDER: rt = ( biorbd.WrappingHalfCylinder(self.model.muscle(i).pathModifier().object(j)) - .RT(self.model, self.Q) - .to_array() + .RT(self.model, self.Q) + .to_array() ) self.wraps_current[i][j][0:3, :, 0] = np.dot(rt, wrap[:, :, 0])[0:3, :] else: diff --git a/bioviz/biorbd_vtk.py b/bioviz/biorbd_vtk.py index 05df66f..91e0002 100644 --- a/bioviz/biorbd_vtk.py +++ b/bioviz/biorbd_vtk.py @@ -158,6 +158,9 @@ def __init__( contacts_color=(0, 1, 0), contacts_size=0.01, contacts_opacity=1.0, + soft_contacts_color=(1, 0.35, 0), + soft_contacts_size=0.1, + soft_contacts_opacity=0.5, global_ref_frame_length=0.15, global_ref_frame_width=5, global_center_of_mass_size=0.0075, @@ -214,6 +217,12 @@ def __init__( self.contacts_opacity = contacts_opacity self.contacts_actors = list() + self.soft_contacts = Markers() + self.soft_contacts_size = soft_contacts_size + self.soft_contacts_color = soft_contacts_color + self.soft_contacts_opacity = soft_contacts_opacity + self.soft_contacts_actors = list() + self.has_global_ref_frame = False self.global_ref_frame_length = global_ref_frame_length self.global_ref_frame_width = global_ref_frame_width @@ -456,6 +465,103 @@ def update_contacts(self, contacts): source.SetRadius(self.contacts_size) mapper.SetInputConnection(source.GetOutputPort()) + def set_soft_contacts_color(self, soft_contacts_color): + """ + Dynamically change the color of the soft_contacts + Parameters + ---------- + soft_contacts_color : tuple(int) + Color the soft_contacts should be drawn (1 is max brightness) + """ + self.soft_contacts_color = soft_contacts_color + self.update_soft_contacts(self.soft_contacts) + + def set_soft_contacts_size(self, soft_contacts_size): + """ + Dynamically change the size of the soft_contacts + Parameters + ---------- + soft_contacts_size : float + Size the soft_contacts should be drawn + """ + self.soft_contacts_size = soft_contacts_size + self.update_soft_contacts(self.soft_contacts) + + def set_soft_contacts_opacity(self, soft_contacts_opacity): + """ + Dynamically change the opacity of the soft_contacts + Parameters + ---------- + soft_contacts_opacity : float + Opacity of the soft_contacts (0.0 is completely transparent, 1.0 completely opaque) + Returns + ------- + + """ + self.soft_contacts_opacity = soft_contacts_opacity + self.update_soft_contacts(self.soft_contacts) + + def new_soft_contacts_set(self, soft_contacts): + """ + Define a new marker set. This function must be called each time the number of soft_contacts change + Parameters + ---------- + soft_contacts : Markers3d + One frame of soft_contacts + + """ + if soft_contacts.time.size != 1: + raise IndexError("soft_contacts should be from one frame only") + self.soft_contacts = soft_contacts + + # Remove previous actors from the scene + for actor in self.soft_contacts_actors: + self.parent_window.ren.RemoveActor(actor) + self.soft_contacts_actors = list() + + # Create the geometry of a point (the coordinate) points = vtk.vtkPoints() + for i in range(soft_contacts.channel.size): + # Create a mapper + mapper = vtkPolyDataMapper() + + # Create an actor + self.soft_contacts_actors.append(vtkActor()) + self.soft_contacts_actors[i].SetMapper(mapper) + + self.parent_window.ren.AddActor(self.soft_contacts_actors[i]) + self.parent_window.ren.ResetCamera() + + # Update marker position + self.update_soft_contacts(self.soft_contacts) + + def update_soft_contacts(self, soft_contacts): + """ + Update position of the soft_contacts on the screen (but do not repaint) + Parameters + ---------- + soft_contacts : Markers3d + One frame of soft_contacts + + """ + + if soft_contacts.time.size != 1: + raise IndexError("soft_contacts should be from one frame only") + if soft_contacts.channel.size != self.soft_contacts.channel.size: + self.new_soft_contacts_set(soft_contacts) + return # Prevent calling update_soft_contacts recursively + self.soft_contacts = soft_contacts + soft_contacts = np.array(soft_contacts) + + for i, actor in enumerate(self.soft_contacts_actors): + # mapper = actors.GetNextActor().GetMapper() + mapper = actor.GetMapper() + self.soft_contacts_actors[i].GetProperty().SetColor(self.soft_contacts_color) + self.soft_contacts_actors[i].GetProperty().SetOpacity(self.soft_contacts_opacity) + source = vtkSphereSource() + source.SetCenter(soft_contacts[0:3, i]) + source.SetRadius(self.soft_contacts_size[i]) + mapper.SetInputConnection(source.GetOutputPort()) + def set_global_center_of_mass_color(self, global_center_of_mass_color): """ Dynamically change the color of the global center of mass From 2c5adbc7d718330a150ec79cbfe0e983133370da Mon Sep 17 00:00:00 2001 From: Ipuch Date: Thu, 21 Oct 2021 09:49:44 -0400 Subject: [PATCH 2/5] now displaying softcontacts spheres with their own radius --- bioviz/__init__.py | 12 ++++++++++-- bioviz/biorbd_vtk.py | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bioviz/__init__.py b/bioviz/__init__.py index 31777b1..718cdf3 100644 --- a/bioviz/__init__.py +++ b/bioviz/__init__.py @@ -298,7 +298,7 @@ def __init__( show_contacts=True, contacts_size=0.010, show_soft_contacts=True, - soft_contacts_color=(1, 0.35, 0), + soft_contacts_color=(0.11, 0.63, 0.95), show_muscles=True, show_wrappings=True, show_analyses_panel=True, @@ -339,7 +339,15 @@ def __init__( # Create the plot self.vtk_window = VtkWindow(background_color=background_color) self.vtk_markers_size = markers_size - soft_contacts_size = [0.1] * self.model.nbSoftContacts() + + # soft_contact sphere sizes + radius =[] + for i in range(self.model.nbSoftContacts()): + c = self.model.softContact(i) + if c.typeOfNode() == biorbd.SOFT_CONTACT_SPHERE: + radius.append(biorbd.SoftContactSphere(self.model.softContact(i)).radius()) + soft_contacts_size = radius + self.vtk_model = VtkModel( self.vtk_window, markers_color=(0, 0, 1), diff --git a/bioviz/biorbd_vtk.py b/bioviz/biorbd_vtk.py index 91e0002..bb2e3ec 100644 --- a/bioviz/biorbd_vtk.py +++ b/bioviz/biorbd_vtk.py @@ -160,7 +160,7 @@ def __init__( contacts_opacity=1.0, soft_contacts_color=(1, 0.35, 0), soft_contacts_size=0.1, - soft_contacts_opacity=0.5, + soft_contacts_opacity=0.35, global_ref_frame_length=0.15, global_ref_frame_width=5, global_center_of_mass_size=0.0075, From 789d6f7d32289e5d84c5a3dfc0120f723475e436 Mon Sep 17 00:00:00 2001 From: Pariterre Date: Thu, 21 Oct 2021 11:33:42 -0400 Subject: [PATCH 3/5] Force 1.8.1 on biorbd --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 9d70a58..a636bff 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,7 @@ name: bioptim channels: - conda-forge dependencies: - - biorbd >=1.6.1 + - biorbd >=1.8.1 - python >=3.9 - numpy - matplotlib From 6e1a4805fa8882e295f0290f95e1768a8a90bfb4 Mon Sep 17 00:00:00 2001 From: Ipuch Date: Thu, 21 Oct 2021 14:14:41 -0400 Subject: [PATCH 4/5] blacked --- bioviz/__init__.py | 67 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/bioviz/__init__.py b/bioviz/__init__.py index 718cdf3..1059a3f 100644 --- a/bioviz/__init__.py +++ b/bioviz/__init__.py @@ -282,30 +282,30 @@ def _get_data_from_casadi(self, Q=None, compute_kin=True): class Viz: def __init__( - self, - model_path=None, - loaded_model=None, - show_meshes=True, - patch_color=(0.89, 0.855, 0.788), - show_global_center_of_mass=True, - show_segments_center_of_mass=True, - segments_center_of_mass_size=0.005, - show_global_ref_frame=True, - show_local_ref_frame=True, - show_markers=True, - experimental_markers_color=(1, 1, 1), - markers_size=0.010, - show_contacts=True, - contacts_size=0.010, - show_soft_contacts=True, - soft_contacts_color=(0.11, 0.63, 0.95), - show_muscles=True, - show_wrappings=True, - show_analyses_panel=True, - background_color=(0.5, 0.5, 0.5), - force_wireframe=False, - experimental_forces_colors=(85, 78, 0), - **kwargs, + self, + model_path=None, + loaded_model=None, + show_meshes=True, + patch_color=(0.89, 0.855, 0.788), + show_global_center_of_mass=True, + show_segments_center_of_mass=True, + segments_center_of_mass_size=0.005, + show_global_ref_frame=True, + show_local_ref_frame=True, + show_markers=True, + experimental_markers_color=(1, 1, 1), + markers_size=0.010, + show_contacts=True, + contacts_size=0.010, + show_soft_contacts=True, + soft_contacts_color=(0.11, 0.63, 0.95), + show_muscles=True, + show_wrappings=True, + show_analyses_panel=True, + background_color=(0.5, 0.5, 0.5), + force_wireframe=False, + experimental_forces_colors=(85, 78, 0), + **kwargs, ): """ Class that easily shows a biorbd model @@ -334,14 +334,14 @@ def __init__( show_contacts = False show_muscles = False show_wrappings = False - show_soft_contact = False, + show_soft_contact = (False,) # Create the plot self.vtk_window = VtkWindow(background_color=background_color) self.vtk_markers_size = markers_size # soft_contact sphere sizes - radius =[] + radius = [] for i in range(self.model.nbSoftContacts()): c = self.model.softContact(i) if c.typeOfNode() == biorbd.SOFT_CONTACT_SPHERE: @@ -860,7 +860,7 @@ def __move_avatar_from_sliders(self): self.set_q(self.Q) def __update_muscle_analyses_graphs( - self, skip_muscle_length, skip_moment_arm, skip_passive_forces, skip_active_forces + self, skip_muscle_length, skip_moment_arm, skip_passive_forces, skip_active_forces ): # Adjust muscle analyses if needed if self.active_analyses_widget == self.analyses_muscle_widget: @@ -1021,7 +1021,8 @@ def load_experimental_markers(self, data, auto_start=True, ignore_animation_warn ) self.vtk_model_markers = VtkModel( - self.vtk_window, markers_color=self.experimental_markers_color, markers_size=self.vtk_markers_size) + self.vtk_window, markers_color=self.experimental_markers_color, markers_size=self.vtk_markers_size + ) self.__set_movement_slider() self.show_experimental_markers = True @@ -1032,7 +1033,7 @@ def load_experimental_markers(self, data, auto_start=True, ignore_animation_warn self.__start_stop_animation() def load_experimental_forces( - self, data, segments=None, normalization_ratio=0.2, auto_start=True, ignore_animation_warning=True + self, data, segments=None, normalization_ratio=0.2, auto_start=True, ignore_animation_warning=True ): if isinstance(data, (np.ndarray, xr.DataArray)): self.experimental_forces = data if isinstance(data, xr.DataArray) else xr.DataArray(data) @@ -1070,7 +1071,7 @@ def __set_markers_from_q(self): def __set_experimental_markers_from_frame(self): t_slider = self.movement_slider[0].value() - 1 t = t_slider if t_slider < self.experimental_markers.shape[2] else self.experimental_markers.shape[2] - 1 - self.vtk_model_markers.update_markers(self.experimental_markers[:, :, t: t + 1].isel(time=[0])) + self.vtk_model_markers.update_markers(self.experimental_markers[:, :, t : t + 1].isel(time=[0])) def __set_experimental_forces_from_frame(self): segment_names = [] @@ -1105,7 +1106,7 @@ def __set_experimental_forces_from_frame(self): t_slider = self.movement_slider[0].value() - 1 t = t_slider if t_slider < self.experimental_forces.shape[2] else self.experimental_forces.shape[2] - 1 self.vtk_model.update_force( - segment_jcs, self.experimental_forces[:, :, t: t + 1], max_forces, self.force_normalization_ratio + segment_jcs, self.experimental_forces[:, :, t : t + 1], max_forces, self.force_normalization_ratio ) def __set_contacts_from_q(self): @@ -1152,8 +1153,8 @@ def __set_wrapping_from_q(self): if self.model.muscle(i).pathModifier().object(j).typeOfNode() == biorbd.WRAPPING_HALF_CYLINDER: rt = ( biorbd.WrappingHalfCylinder(self.model.muscle(i).pathModifier().object(j)) - .RT(self.model, self.Q) - .to_array() + .RT(self.model, self.Q) + .to_array() ) self.wraps_current[i][j][0:3, :, 0] = np.dot(rt, wrap[:, :, 0])[0:3, :] else: From 9f51a4c6a2708b4fcae98345f263e338b122f9a2 Mon Sep 17 00:00:00 2001 From: Ipuch Date: Thu, 21 Oct 2021 14:31:00 -0400 Subject: [PATCH 5/5] correcting typo and an extra tuple --- bioviz/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bioviz/__init__.py b/bioviz/__init__.py index 1059a3f..58afacd 100644 --- a/bioviz/__init__.py +++ b/bioviz/__init__.py @@ -304,7 +304,7 @@ def __init__( show_analyses_panel=True, background_color=(0.5, 0.5, 0.5), force_wireframe=False, - experimental_forces_colors=(85, 78, 0), + experimental_forces_color=(85, 78, 0), **kwargs, ): """ @@ -334,7 +334,7 @@ def __init__( show_contacts = False show_muscles = False show_wrappings = False - show_soft_contact = (False,) + show_soft_contacts = False # Create the plot self.vtk_window = VtkWindow(background_color=background_color) @@ -356,7 +356,7 @@ def __init__( contacts_size=contacts_size, segments_center_of_mass_size=segments_center_of_mass_size, force_wireframe=force_wireframe, - force_color=experimental_forces_colors, + force_color=experimental_forces_color, soft_contacts_size=soft_contacts_size, soft_contacts_color=soft_contacts_color, ) @@ -378,7 +378,7 @@ def __init__( self.show_experimental_forces = False self.experimental_forces = None self.segment_forces = [] - self.experimental_forces_color = experimental_forces_colors + self.experimental_forces_color = experimental_forces_color self.force_normalization_ratio = None self.show_contacts = show_contacts