Skip to content

Commit

Permalink
Merge pull request #51 from pariterre/master
Browse files Browse the repository at this point in the history
Added wrapping objects
  • Loading branch information
pariterre authored Mar 20, 2021
2 parents 6b4e62a + fd07c74 commit a1061da
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 23 deletions.
88 changes: 70 additions & 18 deletions bioviz/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ def __init__(
show_markers=True,
markers_size=0.010,
show_muscles=True,
show_wrappings=True,
show_analyses_panel=True,
background_color=(0.5, 0.5, 0.5),
**kwargs,
Expand Down Expand Up @@ -292,6 +293,9 @@ def __init__(
self.show_muscles = show_muscles
else:
self.show_muscles = False
show_wrappings = False
self.show_wrappings = show_wrappings

if sum([len(i) for i in self.model.meshPoints(np.zeros(self.model.nbQ()))]) > 0:
self.show_meshes = show_meshes
else:
Expand All @@ -313,25 +317,61 @@ def __init__(
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")
for k, patch in enumerate(self.model.meshFaces()[i]):
triangles[k, :] = patch.face()
triangles = np.array([p.face() for p in self.model.meshFaces()[i]], dtype="int32") \
if len(self.model.meshFaces()[i]) else np.ndarray((0, 3), dtype="int32")
self.mesh.append(Mesh(vertex=vertices, triangles=triangles.T))
self.model.updateMuscles(self.Q, True)
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 = []
self.allGlobalJCS = InterfacesCollections.AllGlobalJCS(self.model)
for rt in self.allGlobalJCS.get_data(Q=self.Q, compute_kin=False):
self.rt.append(Rototrans(rt))

if self.show_global_ref_frame:
self.vtk_model.create_global_ref_frame()
if self.show_muscles:
self.model.updateMuscles(self.Q, True)
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)
if self.show_local_ref_frame or self.show_global_ref_frame:
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))

if self.show_global_ref_frame:
self.vtk_model.create_global_ref_frame()
if self.show_wrappings:
self.wraps_base = []
self.wraps_current = []
for m in range(self.model.nbMuscles()):
path_modifier = self.model.muscle(m).pathModifier()
wraps = []
wraps_current = []
for w in range(path_modifier.nbWraps()):
wrap = path_modifier.object(w)
if wrap.typeOfNode() == biorbd.VIA_POINT:
continue # Do not show via points
elif wrap.typeOfNode() == biorbd.WRAPPING_HALF_CYLINDER:
wrap_cylinder = biorbd.WrappingHalfCylinder(wrap)
res = 11 # resolution
x = np.sin(np.linspace(0, np.pi, res)) * wrap_cylinder.radius()
y = np.cos(np.linspace(0, np.pi, res)) * wrap_cylinder.radius()
z = np.ones((res, )) * wrap_cylinder.length()
vertices = np.concatenate([np.array([0, 0, z[0]])[:, np.newaxis], [x, y, z],
np.array([0, 0, -z[0]])[:, np.newaxis], [x, y, -z]], axis=1)

tri_0_0 = np.zeros((res-1, 1))
tri_1_0 = np.arange(1, res)[:, np.newaxis]
tri_2_0 = np.arange(2, res+1)[:, np.newaxis]
tri_0 = np.concatenate([tri_0_0, tri_1_0, tri_2_0], axis=1)
tri_1 = tri_0 + res + 1
tri_2 = np.concatenate([tri_1_0, tri_2_0, tri_1_0 + res + 1], axis=1)
tri_3 = np.concatenate([tri_1_0 + res + 1, tri_1_0 + res + 2, tri_2_0], axis=1)
tri_4 = np.array([[1, res, res + 2], [res, res + 2, res + res + 1]])
triangles = np.array(np.concatenate((tri_0, tri_1, tri_2, tri_3, tri_4)), dtype="int32").T
else:
raise NotImplementedError("The wrapping object is not implemented in bioviz")
wraps.append(Mesh(vertex=vertices[:, :, np.newaxis], triangles=triangles))
wraps_current.append(Mesh(vertex=vertices[:, :, np.newaxis], triangles=triangles))
self.wraps_base.append(wraps)
self.wraps_current.append(wraps_current)

self.show_analyses_panel = show_analyses_panel
if self.show_analyses_panel:
Expand Down Expand Up @@ -401,6 +441,8 @@ 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_wrappings:
self.__set_wrapping_from_q()

# Update the sliders
if self.show_analyses_panel:
Expand Down Expand Up @@ -822,6 +864,16 @@ def __set_muscles_from_q(self):
idx += 1
self.vtk_model.update_muscle(self.muscles)

def __set_wrapping_from_q(self):
for i, wraps in enumerate(self.wraps_base):
for j, wrap in enumerate(wraps):
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()
self.wraps_current[i][j][0:3, :, 0] = np.dot(rt, wrap[:, :, 0])[0:3, :]
else:
raise NotImplementedError("__set_wrapping_from_q is not ready for these wrapping object")
self.vtk_model.update_wrapping(self.wraps_current)

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)
Expand Down
2 changes: 1 addition & 1 deletion bioviz/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.0.1"
__version__ = "2.1.0"
8 changes: 4 additions & 4 deletions bioviz/analyses.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,15 @@ def __compute_all_values(self):
emg = biorbd.State(0, self.active_forces_slider.value() / 100)
for i, q_mod in enumerate(all_q):
self.model.UpdateKinematicsCustom(biorbd.GeneralizedCoordinates(q_mod))
muscles_length_jacobian = self.model.musclesLengthJacobian().to_array()
for m in range(self.n_mus):
if self.checkboxes_muscle[m].isChecked():
mus_group_idx, mus_idx, _ = self.muscle_mapping[self.checkboxes_muscle[m].text()]
mus_group_idx, mus_idx, cmp_mus = self.muscle_mapping[self.checkboxes_muscle[m].text()]
mus = self.model.muscleGroup(mus_group_idx).muscle(mus_idx)
mus.updateOrientations(self.model, q_mod, 1)
muscles_length_jacobian = self.model.musclesLengthJacobian().to_array()

length[i, m] = mus.length(self.model, q_mod, False)
moment_arm[i, m] = muscles_length_jacobian[mus_idx, q_idx]
moment_arm[i, m] = -1 * muscles_length_jacobian[cmp_mus, q_idx]
if mus.type() != biorbd.IDEALIZED_ACTUATOR:
passive_forces[i, m] = biorbd.HillType(mus).FlPE()
else:
Expand Down Expand Up @@ -241,7 +241,7 @@ def __update_specific_plot(self, canvas, ax, x, y, skip=False):
# Add vertical bar to show current dof (it must be done after relim so we know the new lims)
q_idx = self.combobox_dof.currentIndex()
if self.animation_checkbox.isChecked():
x = int(self.main_window.movement_slider[1].text()) # Frame label
x = int(self.main_window.movement_slider[1].text()) - 1 # Frame label
else:
x = self.__get_q_from_slider()[q_idx]
ax.get_lines()[-1].set_data([x, x], ax.get_ylim())
Expand Down
130 changes: 130 additions & 0 deletions bioviz/biorbd_vtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ def __init__(
segments_center_of_mass_opacity=1.0,
mesh_color=(0, 0, 0),
mesh_opacity=1.0,
wrapping_color=(0, 0, 1),
wrapping_opacity=1.0,
muscle_color=(150 / 255, 15 / 255, 15 / 255),
muscle_opacity=1.0,
rt_length=0.1,
Expand Down Expand Up @@ -221,6 +223,11 @@ def __init__(
self.muscle_opacity = muscle_opacity
self.muscle_actors = list()

self.all_wrappings = []
self.wrapping_color = wrapping_color
self.wrapping_opacity = wrapping_opacity
self.wrapping_actors = list()

def set_markers_color(self, markers_color):
"""
Dynamically change the color of the markers
Expand Down Expand Up @@ -759,6 +766,129 @@ def update_muscle(self, all_muscles):
poly_line = self.muscle_actors[i].GetMapper().GetInput()
poly_line.SetPoints(points)

def set_wrapping_color(self, wrapping_color):
"""
Dynamically change the color of the wrapping
Parameters
----------
wrapping_color : tuple(int)
Color the wrapping should be drawn (1 is max brightness)
"""
self.wrapping_color = wrapping_color
self.update_wrapping(self.all_wrappings)

def set_wrapping_opacity(self, wrapping_opacity):
"""
Dynamically change the opacity of the wrapping
Parameters
----------
wrapping_opacity : float
Opacity of the wrapping (0.0 is completely transparent, 1.0 completely opaque)
Returns
-------
"""
self.wrapping_opacity = wrapping_opacity
self.update_wrapping(self.all_wrappings)

def new_wrapping_set(self, all_wrappings, seg):
"""
Define a new wrapping set. This function must be called each time the number of wrappings change
Parameters
----------
all_wrappings : MeshCollection
One frame of wrapping
"""
if isinstance(all_wrappings, Mesh):
wrapping_tp = []
wrapping_tp.append(all_wrappings)
all_wrappings = wrapping_tp

if not isinstance(all_wrappings, list):
raise TypeError("Please send a list of wrapping to update_wrapping")
self.all_wrappings[seg] = all_wrappings

# Remove previous actors from the scene
for actor in self.wrapping_actors[seg]:
self.parent_window.ren.RemoveActor(actor)
self.wrapping_actors[seg] = list()

# Create the geometry of a point (the coordinate) points = vtkPoints()
for (i, wrapping) in enumerate(self.all_wrappings[seg]):
if wrapping.time.size != 1:
raise IndexError("Mesh should be from one frame only")

points = vtkPoints()
for j in range(wrapping.channel.size):
points.InsertNextPoint([0, 0, 0])

# Create an array for each triangle
cell = vtkCellArray()
for j in range(wrapping.triangles.shape[1]): # For each triangle
line = vtkPolyLine()
line.GetPointIds().SetNumberOfIds(4)
for k in range(len(wrapping.triangles[:, j])): # For each index
line.GetPointIds().SetId(k, wrapping.triangles[k, j])
line.GetPointIds().SetId(3, wrapping.triangles[0, j]) # Close the triangle
cell.InsertNextCell(line)
poly_line = vtkPolyData()
poly_line.SetPoints(points)
poly_line.SetLines(cell)

# Create a mapper
mapper = vtkPolyDataMapper()
mapper.SetInputData(poly_line)

# Create an actor
self.wrapping_actors[seg].append(vtkActor())
self.wrapping_actors[seg][i].SetMapper(mapper)
self.wrapping_actors[seg][i].GetProperty().SetColor(self.wrapping_color)
self.wrapping_actors[seg][i].GetProperty().SetOpacity(self.wrapping_opacity)

self.parent_window.ren.AddActor(self.wrapping_actors[seg][i])
self.parent_window.ren.ResetCamera()

# # Update marker position
# self.update_wrapping(self.all_wrappings)

def update_wrapping(self, all_wrappings):
"""
Update position of the wrapping on the screen (but do not repaint)
Parameters
----------
all_wrappings : MeshCollection
One frame of wrapping
"""
if not self.all_wrappings:
self.all_wrappings = [[]] * len(all_wrappings)
self.wrapping_actors = [[]] * len(all_wrappings)

for seg, wrappings in enumerate(all_wrappings):
for i, wrapping in enumerate(wrappings):
if wrapping.time.size != 1:
raise IndexError("Mesh should be from one frame only")

if len(self.all_wrappings[seg]) <= i or wrapping.channel.size != self.all_wrappings[seg][i].channel.size:
self.new_wrapping_set(wrappings, seg)
# return # Prevent calling update_markers recursively

if not isinstance(wrappings, list):
raise TypeError("Please send a list of wrapping to update_wrapping")

self.all_wrappings[seg] = wrappings

for (i, wrapping) in enumerate(self.all_wrappings[seg]):
points = vtkPoints()
n_vertex = wrapping.channel.size
wrapping = np.array(wrapping)
for j in range(n_vertex):
points.InsertNextPoint(wrapping[0:3, j])

poly_line = self.wrapping_actors[seg][i].GetMapper().GetInput()
poly_line.SetPoints(points)

def new_rt_set(self, all_rt):
"""
Define a new rt set. This function must be called each time the number of rt change
Expand Down

0 comments on commit a1061da

Please sign in to comment.