Пример #1
0
def test_rototrans_creation():
    dims = ("row", "col", "time")
    array = Rototrans()
    np.testing.assert_array_equal(x=array,
                                  y=xr.DataArray(np.eye(4)[..., np.newaxis]))
    assert array.dims == dims

    data = Markers(MARKERS_DATA.values)
    array = Rototrans.from_markers(
        origin=data.isel(channel=[0]),
        axis_1=data.isel(channel=[0, 1]),
        axis_2=data.isel(channel=[0, 2]),
        axes_name="xy",
        axis_to_recalculate="y",
    )
    is_expected_array(array, **EXPECTED_VALUES[67])

    size = 4, 4, 100
    array = Rototrans.from_random_data(size=size)
    assert array.shape == size
    assert array.dims == dims

    with pytest.raises(ValueError):
        Angles(ANALOGS_DATA)
Пример #2
0
class Viz:
    def __init__(
            self,
            model_path=None,
            loaded_model=None,
            show_meshes=True,
            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,
            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),
            **kwargs,
    ):
        """
        Class that easily shows a biorbd model
        Args:
            loaded_model: reference to a biorbd loaded model (if both loaded_model and model_path, load_model is selected
            model_path: path of the model to load
        """

        # Load and store the model
        if loaded_model is not None:
            if not isinstance(loaded_model, biorbd.Model):
                raise TypeError(
                    "loaded_model should be of a biorbd.Model type")
            self.model = loaded_model
        elif model_path is not None:
            self.model = biorbd.Model(model_path)
        else:
            raise ValueError("loaded_model or model_path must be provided")

        # Create the plot
        self.vtk_window = VtkWindow(background_color=background_color)
        self.vtk_model = VtkModel(
            self.vtk_window,
            markers_color=(0, 0, 1),
            markers_size=markers_size,
            contacts_size=contacts_size,
            segments_center_of_mass_size=segments_center_of_mass_size)
        self.is_executing = False
        self.animation_warning_already_shown = False

        # Set Z vertical
        cam = self.vtk_window.ren.GetActiveCamera()
        cam.SetFocalPoint(0, 0, 0)
        cam.SetPosition(5, 0, 0)
        cam.SetRoll(-90)

        # Get the options
        self.show_markers = show_markers
        self.show_contacts = show_contacts
        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
        self.show_local_ref_frame = show_local_ref_frame
        if self.model.nbMuscles() > 0:
            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:
            self.show_meshes = 0

        # Create all the reference to the things to plot
        self.nQ = self.model.nbQ()
        self.Q = np.zeros(self.nQ)
        if self.show_markers:
            self.Markers = InterfacesCollections.Markers(self.model)
            self.markers = Markers(np.ndarray((3, self.model.nbMarkers(), 1)))
        if self.show_contacts:
            self.Contacts = InterfacesCollections.Contact(self.model)
            self.contacts = Markers(np.ndarray(
                (3, self.model.nbContacts(), 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)))
        if self.show_segments_center_of_mass:
            self.CoMbySegment = InterfacesCollections.CoMbySegment(self.model)
            self.segments_center_of_mass = Markers(
                np.ndarray((3, self.model.nbSegment(), 1)))
        if self.show_meshes:
            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.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))
        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:
            self.muscle_analyses = []
            self.palette_active = QPalette()
            self.palette_inactive = QPalette()
            self.set_viz_palette()
            self.animated_Q = []

            self.play_stop_push_button = []
            self.is_animating = False
            self.is_recording = False
            self.start_icon = QIcon(
                QPixmap(f"{os.path.dirname(__file__)}/ressources/start.png"))
            self.pause_icon = QIcon(
                QPixmap(f"{os.path.dirname(__file__)}/ressources/pause.png"))
            self.record_icon = QIcon(
                QPixmap(f"{os.path.dirname(__file__)}/ressources/record.png"))
            self.add_icon = QIcon(
                QPixmap(f"{os.path.dirname(__file__)}/ressources/add.png"))
            self.stop_icon = QIcon(
                QPixmap(f"{os.path.dirname(__file__)}/ressources/stop.png"))

            self.double_factor = 10000
            self.sliders = list()
            self.movement_slider = []

            self.active_analyses_widget = None
            self.analyses_layout = QHBoxLayout()
            self.analyses_muscle_widget = QWidget()
            self.add_options_panel()

        # Update everything at the position Q=0
        self.set_q(self.Q)

    def reset_q(self):
        self.Q = np.zeros(self.Q.shape)
        for slider in self.sliders:
            slider[1].setValue(0)
            slider[2].setText(f"{0:.2f}")
        self.set_q(self.Q)

        # Reset also muscle analyses graphs
        self.__update_muscle_analyses_graphs(False, False, False, False)

    def copy_q_to_clipboard(self):
        pandas.DataFrame(self.Q[np.newaxis, :]).to_clipboard(sep=',',
                                                             index=False,
                                                             header=False)

    def set_q(self, Q, refresh_window=True):
        """
        Manually update
        Args:
            Q: np.array
                Generalized coordinate
            refresh_window: bool
                If the window should be refreshed now or not
        """
        if isinstance(Q, (tuple, list)):
            Q = np.array(Q)

        if not isinstance(
                Q, np.ndarray) and len(Q.shape) > 1 and Q.shape[0] != self.nQ:
            raise TypeError(f"Q should be a {self.nQ} column vector")
        self.Q = Q

        self.model.UpdateKinematicsCustom(self.Q)
        if self.show_muscles:
            self.__set_muscles_from_q()
        if self.show_local_ref_frame:
            self.__set_rt_from_q()
        if self.show_meshes:
            self.__set_meshes_from_q()
        if self.show_global_center_of_mass:
            self.__set_global_center_of_mass_from_q()
        if self.show_segments_center_of_mass:
            self.__set_segments_center_of_mass_from_q()
        if self.show_markers:
            self.__set_markers_from_q()
        if self.show_contacts:
            self.__set_contacts_from_q()
        if self.show_wrappings:
            self.__set_wrapping_from_q()

        # Update the sliders
        if self.show_analyses_panel:
            for i, slide in enumerate(self.sliders):
                slide[1].blockSignals(True)
                slide[1].setValue(int(self.Q[i] * self.double_factor))
                slide[1].blockSignals(False)
                slide[2].setText(f"{self.Q[i]:.2f}")

        if refresh_window:
            self.refresh_window()

    def refresh_window(self):
        """
        Manually refresh the window. One should be aware when manually managing the window, that the plot won't even
        rotate if not refreshed

        """
        self.vtk_window.update_frame()

    def update(self):
        if self.show_analyses_panel and self.is_animating:
            self.movement_slider[0].setValue(
                (self.movement_slider[0].value() + 1) %
                (self.movement_slider[0].maximum() + 1))

            if self.is_recording:
                self.__record()
                if self.movement_slider[0].value() + 1 == (
                        self.movement_slider[0].maximum() + 1):
                    self.__start_stop_animation()
        self.refresh_window()

    def exec(self):
        self.is_executing = True
        while self.vtk_window.is_active:
            self.update()
        self.is_executing = False

    def set_viz_palette(self):
        self.palette_active.setColor(QPalette.WindowText, QColor(Qt.black))
        self.palette_active.setColor(QPalette.ButtonText, QColor(Qt.black))

        self.palette_inactive.setColor(QPalette.WindowText, QColor(Qt.gray))

    def add_options_panel(self):
        # Prepare the sliders
        options_layout = QVBoxLayout()

        options_layout.addStretch()  # Centralize the sliders
        sliders_layout = QVBoxLayout()
        max_label_width = -1

        # Get min and max for all dof
        ranges = []
        for i in range(self.model.nbSegment()):
            seg = self.model.segment(i)
            for r in seg.QRanges():
                ranges.append([r.min(), r.max()])

        for i in range(self.model.nbQ()):
            slider_layout = QHBoxLayout()
            sliders_layout.addLayout(slider_layout)

            # Add a name
            name_label = QLabel()
            name = f"{self.model.nameDof()[i].to_string()}"
            name_label.setText(name)
            name_label.setPalette(self.palette_active)
            label_width = name_label.fontMetrics().boundingRect(
                name_label.text()).width()
            if label_width > max_label_width:
                max_label_width = label_width
            slider_layout.addWidget(name_label)

            # Add the slider
            slider = QSlider(Qt.Horizontal)
            slider.setMinimumSize(100, 0)
            slider.setMinimum(int(ranges[i][0] * self.double_factor))
            slider.setMaximum(int(ranges[i][1] * self.double_factor))
            slider.setPageStep(self.double_factor)
            slider.setValue(0)
            slider.valueChanged.connect(self.__move_avatar_from_sliders)
            slider.sliderReleased.connect(
                partial(self.__update_muscle_analyses_graphs, False, False,
                        False, False))
            slider_layout.addWidget(slider)

            # Add the value
            value_label = QLabel()
            value_label.setText(f"{0:.2f}")
            value_label.setPalette(self.palette_active)
            slider_layout.addWidget(value_label)

            # Add to the main sliders
            self.sliders.append((name_label, slider, value_label))
        # Adjust the size of the names
        for name_label, _, _ in self.sliders:
            name_label.setFixedWidth(max_label_width + 1)

        # Put the sliders in a scrollable area
        sliders_widget = QWidget()
        sliders_widget.setLayout(sliders_layout)
        sliders_scroll = QScrollArea()
        sliders_scroll.setFrameShape(0)
        sliders_scroll.setWidgetResizable(True)
        sliders_scroll.setWidget(sliders_widget)
        options_layout.addWidget(sliders_scroll)

        # Add reset button
        button_layout = QHBoxLayout()
        options_layout.addLayout(button_layout)
        reset_push_button = QPushButton("Reset")
        reset_push_button.setPalette(self.palette_active)
        reset_push_button.released.connect(self.reset_q)
        button_layout.addWidget(reset_push_button)
        copyq_push_button = QPushButton("Copy Q to clipboard")
        copyq_push_button.setPalette(self.palette_active)
        copyq_push_button.released.connect(self.copy_q_to_clipboard)
        button_layout.addWidget(copyq_push_button)

        # Add the radio button for analyses
        option_analyses_group = QGroupBox()
        option_analyses_layout = QVBoxLayout()
        # Add text
        analyse_text = QLabel()
        analyse_text.setPalette(self.palette_active)
        analyse_text.setText("Analyses")
        option_analyses_layout.addWidget(analyse_text)
        # Add the no analyses
        radio_none = QRadioButton()
        radio_none.setPalette(self.palette_active)
        radio_none.setChecked(True)
        radio_none.toggled.connect(
            lambda: self.__select_analyses_panel(radio_none, 0))
        radio_none.setText("None")
        option_analyses_layout.addWidget(radio_none)
        # Add the muscles analyses
        radio_muscle = QRadioButton()
        radio_muscle.setPalette(self.palette_active)
        radio_muscle.toggled.connect(
            lambda: self.__select_analyses_panel(radio_muscle, 1))
        radio_muscle.setText("Muscles")
        option_analyses_layout.addWidget(radio_muscle)
        # Add the layout to the interface
        option_analyses_group.setLayout(option_analyses_layout)
        options_layout.addWidget(option_analyses_group)

        # Finalize the options panel
        options_layout.addStretch()  # Centralize the sliders

        # Animation panel
        animation_layout = QVBoxLayout()
        animation_layout.addWidget(self.vtk_window.avatar_widget)

        # Add the animation slider
        animation_slider_layout = QHBoxLayout()
        animation_layout.addLayout(animation_slider_layout)
        load_push_button = QPushButton("Load movement")
        load_push_button.setPalette(self.palette_active)
        load_push_button.released.connect(self.__load_movement_from_button)
        animation_slider_layout.addWidget(load_push_button)

        # Controllers
        self.play_stop_push_button = QPushButton()
        self.play_stop_push_button.setIcon(self.start_icon)
        self.play_stop_push_button.setPalette(self.palette_active)
        self.play_stop_push_button.setEnabled(False)
        self.play_stop_push_button.released.connect(
            self.__start_stop_animation)
        animation_slider_layout.addWidget(self.play_stop_push_button)

        slider = QSlider(Qt.Horizontal)
        slider.setMinimum(0)
        slider.setMaximum(100)
        slider.setValue(0)
        slider.setEnabled(False)
        slider.valueChanged.connect(self.__animate_from_slider)
        animation_slider_layout.addWidget(slider)

        self.record_push_button = QPushButton()
        self.record_push_button.setIcon(self.record_icon)
        self.record_push_button.setPalette(self.palette_active)
        self.record_push_button.setEnabled(True)
        self.record_push_button.released.connect(self.__record)
        animation_slider_layout.addWidget(self.record_push_button)

        self.stop_record_push_button = QPushButton()
        self.stop_record_push_button.setIcon(self.stop_icon)
        self.stop_record_push_button.setPalette(self.palette_active)
        self.stop_record_push_button.setEnabled(False)
        self.stop_record_push_button.released.connect(self.__stop_record, True)
        animation_slider_layout.addWidget(self.stop_record_push_button)

        # Add the frame count
        frame_label = QLabel()
        frame_label.setText(f"{0}")
        frame_label.setPalette(self.palette_inactive)
        animation_slider_layout.addWidget(frame_label)

        self.movement_slider = (slider, frame_label)

        # Global placement of the window
        self.vtk_window.main_layout.addLayout(options_layout, 0, 0)
        self.vtk_window.main_layout.addLayout(animation_layout, 0, 1)
        self.vtk_window.main_layout.setColumnStretch(0, 1)
        self.vtk_window.main_layout.setColumnStretch(1, 2)

        # Change the size of the window to account for the new sliders
        self.vtk_window.resize(self.vtk_window.size().width() * 2,
                               self.vtk_window.size().height())

        # Prepare all the analyses panel
        self.muscle_analyses = MuscleAnalyses(self.analyses_muscle_widget,
                                              self)
        if biorbd.currentLinearAlgebraBackend() == 1:
            radio_muscle.setEnabled(False)
        else:
            if self.model.nbMuscles() == 0:
                radio_muscle.setEnabled(False)
        self.__select_analyses_panel(radio_muscle, 1)

    def __select_analyses_panel(self, radio_button, panel_to_activate):
        if not radio_button.isChecked():
            return

        # Hide previous analyses panel if necessary
        self.__hide_analyses_panel()

        size_factor_none = 1
        size_factor_muscle = 1.40

        # Find the size factor to get back to normal size
        if self.active_analyses_widget is None:
            reduction_factor = size_factor_none
        elif self.active_analyses_widget == self.analyses_muscle_widget:
            reduction_factor = size_factor_muscle
        else:
            raise RuntimeError(
                "Non-existing panel asked... This should never happen, please report this issue!"
            )

        # Prepare the analyses panel and new size of window
        if panel_to_activate == 0:
            self.active_analyses_widget = None
            enlargement_factor = size_factor_none
        elif panel_to_activate == 1:
            self.active_analyses_widget = self.analyses_muscle_widget
            enlargement_factor = size_factor_muscle
        else:
            raise RuntimeError(
                "Non-existing panel asked... This should never happen, please report this issue!"
            )

        # Activate the required panel
        self.__show_local_ref_frame()

        # Enlarge the main window
        self.vtk_window.resize(
            int(self.vtk_window.size().width() * enlargement_factor /
                reduction_factor),
            self.vtk_window.size().height())

    def __hide_analyses_panel(self):
        if self.active_analyses_widget is None:
            return
        # Remove from main window
        self.active_analyses_widget.setVisible(False)
        self.vtk_window.main_layout.removeWidget(self.active_analyses_widget)
        self.vtk_window.main_layout.setColumnStretch(2, 0)

    def __show_local_ref_frame(self):
        # Give the parent as main window
        if self.active_analyses_widget is not None:
            self.vtk_window.main_layout.addWidget(self.active_analyses_widget,
                                                  0, 2)
            self.vtk_window.main_layout.setColumnStretch(2, 4)
            self.active_analyses_widget.setVisible(True)

        # Update graphs if needed
        self.__update_muscle_analyses_graphs(False, False, False, False)

    def __move_avatar_from_sliders(self):
        for i, slide in enumerate(self.sliders):
            self.Q[i] = slide[1].value() / self.double_factor
            slide[2].setText(f" {self.Q[i]:.2f}")
        self.set_q(self.Q)

    def __update_muscle_analyses_graphs(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:
            self.muscle_analyses.update_all_graphs(skip_muscle_length,
                                                   skip_moment_arm,
                                                   skip_passive_forces,
                                                   skip_active_forces)

    def __animate_from_slider(self):
        # Move the avatar
        self.movement_slider[1].setText(f"{self.movement_slider[0].value()}")
        self.Q = copy.copy(self.animated_Q[self.movement_slider[0].value() -
                                           1])  # 1-based
        self.set_q(self.Q)

        # Update graph of muscle analyses
        self.__update_muscle_analyses_graphs(True, True, True, True)

    def __start_stop_animation(self):
        if not self.is_executing and not self.animation_warning_already_shown:
            QMessageBox.warning(
                self.vtk_window,
                "Not executing",
                "bioviz has detected that it is not actually executing.\n\n"
                "Unless you know what you are doing, the automatic play of the animation will "
                "therefore not work. Please call the bioviz.exec() method to be able to play "
                "the animation.\n\nPlease note that the animation slider will work in any case.",
            )
            self.animation_warning_already_shown = True
        if self.is_animating:
            self.is_animating = False
            self.play_stop_push_button.setIcon(self.start_icon)
            self.record_push_button.setEnabled(True)
            self.stop_record_push_button.setEnabled(self.is_recording)
        else:
            self.is_animating = True
            self.play_stop_push_button.setIcon(self.pause_icon)
            self.record_push_button.setEnabled(False)
            self.stop_record_push_button.setEnabled(False)

    def __stop_record(self):
        self.__record(finish=True)

    def __record(self, finish=False):
        file_name = None
        if not self.is_recording:
            options = QFileDialog.Options()
            options |= QFileDialog.DontUseNativeDialog
            file_name = QFileDialog.getSaveFileName(self.vtk_window,
                                                    "Save the video",
                                                    "",
                                                    "OGV files (*.ogv)",
                                                    options=options)
            file_name, file_extension = os.path.splitext(file_name[0])
            if file_name == "":
                return
            file_name += ".ogv"

            self.record_push_button.setIcon(self.add_icon)
            self.stop_record_push_button.setEnabled(True)
            self.is_recording = True

        self.vtk_window.record(button_to_block=[
            self.record_push_button, self.stop_record_push_button
        ],
                               finish=finish,
                               file_name=file_name)

        if finish:
            self.is_recording = False
            self.record_push_button.setIcon(self.record_icon)
            self.stop_record_push_button.setEnabled(False)

    def __load_movement_from_button(self):
        # Load the actual movement
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        file_name = QFileDialog.getOpenFileName(self.vtk_window,
                                                "Movement to load",
                                                "",
                                                "All Files (*)",
                                                options=options)
        if not file_name[0]:
            return
        if os.path.splitext(
                file_name[0]
        )[1] == ".Q1":  # If it is from a Matlab reconstruction QLD
            self.animated_Q = scipy.io.loadmat(file_name[0])["Q1"].transpose()
        elif os.path.splitext(
                file_name[0]
        )[1] == ".Q2":  # If it is from a Matlab reconstruction Kalman
            self.animated_Q = scipy.io.loadmat(file_name[0])["Q2"].transpose()
        else:  # Otherwise assume this is a numpy array
            self.animated_Q = np.load(file_name[0]).T
        self.__load_movement()

    def load_movement(self,
                      all_q,
                      auto_start=True,
                      ignore_animation_warning=True):
        self.animated_Q = all_q.T
        self.__load_movement()
        if ignore_animation_warning:
            self.animation_warning_already_shown = True
        if auto_start:
            self.__start_stop_animation()

    def __load_movement(self):
        # Activate the start button
        self.is_animating = False
        self.play_stop_push_button.setEnabled(True)
        self.play_stop_push_button.setIcon(self.start_icon)

        # Update the slider bar and frame count
        self.movement_slider[0].setEnabled(True)
        self.movement_slider[0].setMinimum(1)
        self.movement_slider[0].setMaximum(self.animated_Q.shape[0])
        pal = QPalette()
        pal.setColor(QPalette.WindowText, QColor(Qt.black))
        self.movement_slider[1].setPalette(pal)

        # Put back to first frame
        self.movement_slider[0].setValue(1)

        # Add the combobox in muscle analyses
        self.muscle_analyses.add_movement_to_dof_choice()

    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.isel(time=[0]))

    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_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()
        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.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)):
            self.mesh[m][0:3, :, :] = meshes
        self.vtk_model.update_mesh(self.mesh)

    def __set_muscles_from_q(self):
        muscles = self.musclesPointsInGlobal.get_data(Q=self.Q)

        idx = 0
        cmp = 0
        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)
                for k, pts in enumerate(
                        musc.position().musclesPointsInGlobal()):
                    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_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)
        self.vtk_model.update_rt(self.rt)
def test_rt_from_markers():
    all_m = Markers.from_random_data()

    rt_xy = Rototrans.from_markers(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 1]),
        axis_2=all_m.isel(channel=[0, 2]),
        axes_name="xy",
        axis_to_recalculate="y",
    )

    rt_yx = Rototrans.from_markers(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 2]),
        axis_2=all_m.isel(channel=[0, 1]),
        axes_name="yx",
        axis_to_recalculate="y",
    )

    rt_xy_x_recalc = Rototrans.from_markers(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 1]),
        axis_2=all_m.isel(channel=[0, 2]),
        axes_name="yx",
        axis_to_recalculate="x",
    )
    rt_xy_x_recalc = rt_xy_x_recalc.isel(col=[1, 0, 2, 3])
    rt_xy_x_recalc[:, 2, :] = -rt_xy_x_recalc[:, 2, :]

    rt_yz = Rototrans.from_markers(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 1]),
        axis_2=all_m.isel(channel=[0, 2]),
        axes_name="yz",
        axis_to_recalculate="z",
    )

    rt_zy = Rototrans.from_markers(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 2]),
        axis_2=all_m.isel(channel=[0, 1]),
        axes_name="zy",
        axis_to_recalculate="z",
    )
    rt_xy_from_yz = rt_yz.isel(col=[1, 2, 0, 3])

    rt_xz = Rototrans.from_markers(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 1]),
        axis_2=all_m.isel(channel=[0, 2]),
        axes_name="xz",
        axis_to_recalculate="z",
    )

    rt_zx = Rototrans.from_markers(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 2]),
        axis_2=all_m.isel(channel=[0, 1]),
        axes_name="zx",
        axis_to_recalculate="z",
    )
    rt_xy_from_zx = rt_xz.isel(col=[0, 2, 1, 3])
    rt_xy_from_zx[:, 2, :] = -rt_xy_from_zx[:, 2, :]

    np.testing.assert_array_equal(rt_xy, rt_xy_x_recalc)
    np.testing.assert_array_equal(rt_xy, rt_yx)
    np.testing.assert_array_equal(rt_yz, rt_zy)
    np.testing.assert_array_equal(rt_xz, rt_zx)
    np.testing.assert_array_equal(rt_xy, rt_xy_from_yz)
    np.testing.assert_array_equal(rt_xy, rt_xy_from_zx)

    # Produce one that we know the solution
    ref_m = Markers(
        np.array(((1, 2, 3), (4, 5, 6), (6, 5, 4))).T[:, :, np.newaxis])
    rt_xy_from_known_m = Rototrans.from_markers(
        origin=ref_m.isel(channel=[0]),
        axis_1=ref_m.isel(channel=[0, 1]),
        axis_2=ref_m.isel(channel=[0, 2]),
        axes_name="xy",
        axis_to_recalculate="y",
    )

    rt_xy_expected = Rototrans(
        np.array([
            [0.5773502691896257, 0.7071067811865475, -0.408248290463863, 1.0],
            [0.5773502691896257, 0.0, 0.816496580927726, 2.0],
            [0.5773502691896257, -0.7071067811865475, -0.408248290463863, 3.0],
            [0, 0, 0, 1.0],
        ]))

    np.testing.assert_array_equal(rt_xy_from_known_m, rt_xy_expected)

    exception_default_params = dict(
        origin=all_m.isel(channel=[0]),
        axis_1=all_m.isel(channel=[0, 1]),
        axis_2=all_m.isel(channel=[0, 2]),
        axes_name="xy",
        axis_to_recalculate="y",
    )
    with pytest.raises(ValueError):
        Rototrans.from_markers(**{
            **exception_default_params,
            **dict(origin=all_m.isel(channel=[0, 1]))
        })

    with pytest.raises(ValueError):
        Rototrans.from_markers(**{
            **exception_default_params,
            **dict(axis_1=all_m.isel(channel=[0]))
        })

    with pytest.raises(ValueError):
        Rototrans.from_markers(**{
            **exception_default_params,
            **dict(axis_2=all_m.isel(channel=[0]))
        })

    with pytest.raises(ValueError):
        Rototrans.from_markers(
            **{
                **exception_default_params,
                **dict(axis_1=all_m.isel(channel=[0, 1], time=slice(None, 50))),
            })

    with pytest.raises(ValueError):
        Rototrans.from_markers(**{
            **exception_default_params,
            **dict(axes_name="yyz")
        })

    with pytest.raises(ValueError):
        Rototrans.from_markers(**{
            **exception_default_params,
            **dict(axes_name="xxz")
        })

    with pytest.raises(ValueError):
        Rototrans.from_markers(**{
            **exception_default_params,
            **dict(axes_name="zzz")
        })

    with pytest.raises(ValueError):
        Rototrans.from_markers(**{
            **exception_default_params,
            **dict(axis_to_recalculate="h")
        })