Beispiel #1
0
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1248, 737)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.splitter_2 = QtWidgets.QSplitter(self.centralwidget)
        self.splitter_2.setOrientation(QtCore.Qt.Horizontal)
        self.splitter_2.setObjectName("splitter_2")
        self.gridLayoutWidget_2 = QtWidgets.QWidget(self.splitter_2)
        self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2")
        self.gl_container = QtWidgets.QGridLayout(self.gridLayoutWidget_2)
        self.gl_container.setContentsMargins(0, 0, -1, -1)
        self.gl_container.setObjectName("gl_container")
        self.animationViewer = SceneViewerWidget(self.gridLayoutWidget_2)
        self.animationViewer.setMinimumSize(QtCore.QSize(640, 480))
        self.animationViewer.setObjectName("animationViewer")
        self.gl_container.addWidget(self.animationViewer, 0, 0, 1, 1)
        self.splitter = QtWidgets.QSplitter(self.splitter_2)
        self.splitter.setOrientation(QtCore.Qt.Vertical)
        self.splitter.setObjectName("splitter")
        self.sceneObjectTableWidget = SceneObjectTableWidget(self.splitter)
        self.sceneObjectTableWidget.setColumnCount(3)
        self.sceneObjectTableWidget.setObjectName("sceneObjectTableWidget")
        self.sceneObjectTableWidget.setColumnCount(3)
        self.sceneObjectTableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.sceneObjectTableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.sceneObjectTableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.sceneObjectTableWidget.setHorizontalHeaderItem(2, item)
        self.sceneObjectTableWidget.horizontalHeader().setDefaultSectionSize(50)
        self.sceneObjectTableWidget.horizontalHeader().setMinimumSectionSize(200)
        self.sceneObjectTableWidget.horizontalHeader().setStretchLastSection(True)
        self.objectPropertiesGroupBox = QtWidgets.QGroupBox(self.splitter)
        self.objectPropertiesGroupBox.setObjectName("objectPropertiesGroupBox")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.objectPropertiesGroupBox)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.objectPropertiesLayout = QtWidgets.QGridLayout()
        self.objectPropertiesLayout.setObjectName("objectPropertiesLayout")
        self.gridLayout_2.addLayout(self.objectPropertiesLayout, 0, 0, 1, 1)
        self.gridLayout.addWidget(self.splitter_2, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 1248, 21))
        self.menuBar.setObjectName("menuBar")
        MainWindow.setMenuBar(self.menuBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        MainWindow.setStatusBar(self.statusBar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def __init__(self,
                 name,
                 skeleton,
                 share_widget,
                 parent=None,
                 enable_line_edit=False,
                 skeleton_model=None):
        self.initialized = False
        QDialog.__init__(self, parent)
        Ui_Dialog.setupUi(self, self)
        self.view = SceneViewerWidget(parent, share_widget, size=(400, 400))
        self.view.setObjectName("left")
        self.view.setMinimumSize(400, 400)
        self.view.initializeGL()
        self.nameLineEdit.setText(name)
        self.nameLineEdit.setEnabled(enable_line_edit)
        self.name = name
        self.view.enable_mouse_interaction = True
        self.view.mouse_click.connect(self.on_mouse_click)
        self.viewerLayout.addWidget(self.view)

        self.radius = 1.0
        self.fps = 60
        self.dt = 1 / 60
        self.timer = QTimer()
        self.timer.timeout.connect(self.draw)
        self.timer.start(0)
        self.timer.setInterval(1000.0 / self.fps)
        self.skeleton = skeleton
        self.view.makeCurrent()
        self.scene = EditorScene(True)
        self.scene.enable_scene_edit_widget = True

        if skeleton_model is not None:
            self.skeleton_model = skeleton_model
        elif skeleton.skeleton_model is not None:
            self.skeleton_model = skeleton.skeleton_model
        else:
            self.skeleton_model = dict()
            print("create new skeleton model")
        if "cos_map" not in self.skeleton_model:
            self.skeleton_model["cos_map"] = dict()
        if "joints" not in self.skeleton_model:
            self.skeleton_model["joints"] = dict()
        if "joint_constraints" not in self.skeleton_model:
            self.skeleton_model["joint_constraints"] = dict()

        motion_vector = MotionVector()
        self.reference_frame = skeleton.reference_frame
        print(self.reference_frame[:3])
        motion_vector.frames = [skeleton.reference_frame]
        motion_vector.n_frames = 1
        o = self.scene.object_builder.create_object("animation_controller",
                                                    "skeleton", skeleton,
                                                    motion_vector,
                                                    skeleton.frame_time)
        self.controller = o._components["animation_controller"]
        self.skeleton = self.controller.get_skeleton()
        self.skeleton_vis = o._components["skeleton_vis"]
        self.init_joints(self.controller)
        self.fill_joint_map()

        self.selectButton.clicked.connect(self.slot_accept)
        self.cancelButton.clicked.connect(self.slot_reject)
        self.applyTwistRotationButton.clicked.connect(self.slot_set_twist)
        self.applySwingRotationButton.clicked.connect(self.slot_set_swing)

        self.setOrthogonalTwistButton.clicked.connect(
            self.slot_set_orthogonal_twist)
        self.setOrthogonalSwingButton.clicked.connect(
            self.slot_set_orthogonal_swing)
        self.rotateTwistButton.clicked.connect(self.slot_rotate_twist)
        self.rotateSwingButton.clicked.connect(self.slot_rotate_swing)

        self.flipTwistButton.clicked.connect(self.slot_flip_twist)
        self.flipSwingButton.clicked.connect(self.slot_flip_swing)

        self.flipZAxisButton.clicked.connect(self.slot_flip_z_axis)
        self.alignToUpAxisButton.clicked.connect(self.slot_align_to_up_axis)
        self.alignToForwardAxisButton.clicked.connect(
            self.slot_align_to_forward_axis)

        self.guessSelectedButton.clicked.connect(
            self.slot_guess_selected_cos_map)
        self.resetSelectedCosButton.clicked.connect(
            self.slot_reset_selected_cos_map)
        self.guessAllCosButton.clicked.connect(self.slot_guess_cos_map)
        self.resetAllCosButton.clicked.connect(self.slot_reset_cos_map)
        self.loadDefaultPoseButton.clicked.connect(self.slot_load_default_pose)
        self.applyScaleButton.clicked.connect(self.slot_apply_scale)
        self.jointMapComboBox.currentIndexChanged.connect(
            self.slot_update_joint_map)
        self.aligningRootComboBox.currentIndexChanged.connect(
            self.slot_update_aligning_root_joint)

        self.mirrorLeftButton.clicked.connect(self.slot_mirror_left_to_right)
        self.mirrorRightButton.clicked.connect(self.slot_mirror_right_to_left)

        self.is_updating_joint_info = False
        self.success = False
        self.initialized = False
        self.skeleton_data = None
        self.precision = 3
        self.aligning_root_node = self.skeleton.aligning_root_node
        self.fill_root_combobox()
        self.init_aligning_root_node()
class SkeletonEditorDialog(QDialog, Ui_Dialog):
    def __init__(self,
                 name,
                 skeleton,
                 share_widget,
                 parent=None,
                 enable_line_edit=False,
                 skeleton_model=None):
        self.initialized = False
        QDialog.__init__(self, parent)
        Ui_Dialog.setupUi(self, self)
        self.view = SceneViewerWidget(parent, share_widget, size=(400, 400))
        self.view.setObjectName("left")
        self.view.setMinimumSize(400, 400)
        self.view.initializeGL()
        self.nameLineEdit.setText(name)
        self.nameLineEdit.setEnabled(enable_line_edit)
        self.name = name
        self.view.enable_mouse_interaction = True
        self.view.mouse_click.connect(self.on_mouse_click)
        self.viewerLayout.addWidget(self.view)

        self.radius = 1.0
        self.fps = 60
        self.dt = 1 / 60
        self.timer = QTimer()
        self.timer.timeout.connect(self.draw)
        self.timer.start(0)
        self.timer.setInterval(1000.0 / self.fps)
        self.skeleton = skeleton
        self.view.makeCurrent()
        self.scene = EditorScene(True)
        self.scene.enable_scene_edit_widget = True

        if skeleton_model is not None:
            self.skeleton_model = skeleton_model
        elif skeleton.skeleton_model is not None:
            self.skeleton_model = skeleton.skeleton_model
        else:
            self.skeleton_model = dict()
            print("create new skeleton model")
        if "cos_map" not in self.skeleton_model:
            self.skeleton_model["cos_map"] = dict()
        if "joints" not in self.skeleton_model:
            self.skeleton_model["joints"] = dict()
        if "joint_constraints" not in self.skeleton_model:
            self.skeleton_model["joint_constraints"] = dict()

        motion_vector = MotionVector()
        self.reference_frame = skeleton.reference_frame
        print(self.reference_frame[:3])
        motion_vector.frames = [skeleton.reference_frame]
        motion_vector.n_frames = 1
        o = self.scene.object_builder.create_object("animation_controller",
                                                    "skeleton", skeleton,
                                                    motion_vector,
                                                    skeleton.frame_time)
        self.controller = o._components["animation_controller"]
        self.skeleton = self.controller.get_skeleton()
        self.skeleton_vis = o._components["skeleton_vis"]
        self.init_joints(self.controller)
        self.fill_joint_map()

        self.selectButton.clicked.connect(self.slot_accept)
        self.cancelButton.clicked.connect(self.slot_reject)
        self.applyTwistRotationButton.clicked.connect(self.slot_set_twist)
        self.applySwingRotationButton.clicked.connect(self.slot_set_swing)

        self.setOrthogonalTwistButton.clicked.connect(
            self.slot_set_orthogonal_twist)
        self.setOrthogonalSwingButton.clicked.connect(
            self.slot_set_orthogonal_swing)
        self.rotateTwistButton.clicked.connect(self.slot_rotate_twist)
        self.rotateSwingButton.clicked.connect(self.slot_rotate_swing)

        self.flipTwistButton.clicked.connect(self.slot_flip_twist)
        self.flipSwingButton.clicked.connect(self.slot_flip_swing)

        self.flipZAxisButton.clicked.connect(self.slot_flip_z_axis)
        self.alignToUpAxisButton.clicked.connect(self.slot_align_to_up_axis)
        self.alignToForwardAxisButton.clicked.connect(
            self.slot_align_to_forward_axis)

        self.guessSelectedButton.clicked.connect(
            self.slot_guess_selected_cos_map)
        self.resetSelectedCosButton.clicked.connect(
            self.slot_reset_selected_cos_map)
        self.guessAllCosButton.clicked.connect(self.slot_guess_cos_map)
        self.resetAllCosButton.clicked.connect(self.slot_reset_cos_map)
        self.loadDefaultPoseButton.clicked.connect(self.slot_load_default_pose)
        self.applyScaleButton.clicked.connect(self.slot_apply_scale)
        self.jointMapComboBox.currentIndexChanged.connect(
            self.slot_update_joint_map)
        self.aligningRootComboBox.currentIndexChanged.connect(
            self.slot_update_aligning_root_joint)

        self.mirrorLeftButton.clicked.connect(self.slot_mirror_left_to_right)
        self.mirrorRightButton.clicked.connect(self.slot_mirror_right_to_left)

        self.is_updating_joint_info = False
        self.success = False
        self.initialized = False
        self.skeleton_data = None
        self.precision = 3
        self.aligning_root_node = self.skeleton.aligning_root_node
        self.fill_root_combobox()
        self.init_aligning_root_node()

    def init_aligning_root_node(self):
        print("init", self.skeleton.root, self.skeleton.aligning_root_node)
        if self.aligning_root_node is None:
            self.aligning_root_node = self.skeleton.root
        if self.aligning_root_node is not None:
            index = self.aligningRootComboBox.findText(self.aligning_root_node,
                                                       Qt.MatchFixedString)
            print("found index", index, self.aligning_root_node)
            if index >= 0:
                self.aligningRootComboBox.setCurrentIndex(index)

    def closeEvent(self, e):
        self.timer.stop()
        self.view.makeCurrent()
        del self.view

    def on_mouse_click(self, event, ray_start, ray_dir, pos, node_id):
        if event.button() == Qt.LeftButton:
            self.scene.select_object(node_id)
            joint_knob = self.get_selected_joint()
            self.update_joint_info(joint_knob)

    def update_joint_info(self, joint_knob):
        self.is_updating_joint_info = True
        self.scene.scene_edit_widget.reset_rotation()
        self.jointMapComboBox.setCurrentIndex(0)
        label = "Selected Joint: "
        if joint_knob is None:
            label += "None"
            self.jointLabel.setText(label)
            self.is_updating_joint_info = False
            return

        label += joint_knob.joint_name
        joint_name = joint_knob.joint_name
        if "joints" in self.skeleton_model:
            key = find_key(self.skeleton_model["joints"], joint_name)
            print("key", joint_name, key)
            if key is not None:
                index = self.jointMapComboBox.findText(key,
                                                       Qt.MatchFixedString)
                if index >= 0:
                    self.jointMapComboBox.setCurrentIndex(index)

        if "cos_map" in self.skeleton_model and joint_name in self.skeleton_model[
                "cos_map"]:
            x_vector = self.skeleton_model["cos_map"][joint_name]["x"]
            if x_vector is None:
                x_vector = [1, 0, 0]
                self.skeleton_model["cos_map"][joint_name]["x"] = x_vector
            y_vector = self.skeleton_model["cos_map"][joint_name]["y"]
            if y_vector is None:
                y_vector = [0, 1, 0]
                self.skeleton_model["cos_map"][joint_name]["y"] = y_vector
            swing = np.round(x_vector, self.precision)
            twist = np.round(y_vector, self.precision)
            self.set_swing_text(swing)
            self.set_twist_text(twist)
            m = self.skeleton.nodes[joint_name].get_global_matrix(
                self.reference_frame)[:3, :3]
            g_swing = np.dot(m, swing)
            g_swing = normalize(g_swing)
            g_twist = np.dot(m, twist)
            g_twist = normalize(g_twist)
            q = axes_to_q(g_twist, g_swing)
            m = quaternion_matrix(q)
            #print("g_twist", g_twist, twist)
            #print("g_swing", g_swing, swing)
            self.scene.scene_edit_widget.rotation = m[:3, :3].T
        else:
            print("no cos map", self.skeleton_model.keys())

        self.jointLabel.setText(label)
        self.is_updating_joint_info = False

    def set_swing_text(self, swing):
        self.swingXLineEdit.setText(str(swing[0]))
        self.swingYLineEdit.setText(str(swing[1]))
        self.swingZLineEdit.setText(str(swing[2]))

    def set_twist_text(self, twist):
        self.twistXLineEdit.setText(str(twist[0]))
        self.twistYLineEdit.setText(str(twist[1]))
        self.twistZLineEdit.setText(str(twist[2]))

    def fill_joint_map(self):
        self.jointMapComboBox.clear()
        for idx, joint in enumerate(STANDARD_JOINTS):
            print("add", joint)
            self.jointMapComboBox.addItem(joint, idx)

    def fill_root_combobox(self):
        self.aligningRootComboBox.clear()
        for idx, joint in enumerate(self.controller.get_animated_joints()):
            self.aligningRootComboBox.addItem(joint, idx)

    def init_joints(self, controller):
        for joint_name in controller.get_animated_joints():
            if len(self.skeleton.nodes[joint_name].children
                   ) > 0:  # filter out end site joints
                node = self.skeleton.nodes[joint_name]
                if joint_name == self.skeleton.root or np.linalg.norm(
                        node.offset) > 0:
                    self.scene.object_builder.create_object(
                        "joint_control_knob", controller, joint_name,
                        self.radius * self.skeleton_vis.box_scale)

    def draw(self):
        """ draw current scene on the given view
        (note before calling this function the context of the view has to be set as current using makeCurrent() and afterwards the doubble buffer has to swapped to display the current frame swapBuffers())
        """
        if not self.initialized:
            if self.view.graphics_context is not None:
                self.view.resize(400, 400)
                self.initialized = True
        self.scene.update(self.dt)
        self.view.makeCurrent()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.view.graphics_context.render(self.scene)
        self.view.swapBuffers()

    def left_display_changed(self, frame_idx):
        if self.controller is not None:
            self.controller.setCurrentFrameNumber(frame_idx)
            self.leftDisplayFrameSpinBox.setValue(frame_idx)

    def left_spinbox_frame_changed(self, frame):
        self.leftStartFrameSlider.setValue(self.leftStartFrameSpinBox.value())
        self.leftEndFrameSlider.setValue(self.leftEndFrameSpinBox.value())

    def left_slider_frame_changed(self, frame):
        self.leftStartFrameSpinBox.setValue(self.leftStartFrameSlider.value())
        self.leftEndFrameSpinBox.setValue(self.leftEndFrameSlider.value())

    def slot_accept(self):
        self.name = str(self.nameLineEdit.text())
        if self.name != "":
            print("accept")
            self.success = True
            self.skeleton = self.controller.get_skeleton()
            self.skeleton.set_reference_frame(self.reference_frame)
            if self.aligning_root_node is not None:
                self.skeleton.aligning_root_node = self.aligning_root_node
            self.skeleton_data = self.skeleton.to_unity_format()
            if "cos_map" in self.skeleton_model:
                for k in self.skeleton_model["cos_map"]:
                    for l in self.skeleton_model["cos_map"][k]:
                        if type(self.skeleton_model["cos_map"][k]
                                [l]) == np.ndarray:
                            self.skeleton_model["cos_map"][k][
                                l] = self.skeleton_model["cos_map"][k][
                                    l].tolist()
                        else:
                            self.skeleton_model["cos_map"][k][
                                l] = self.skeleton_model["cos_map"][k][l]
            self.close()
        else:
            print("Please provide a name")

    def slot_reject(self):
        self.close()

    def get_selected_joint(self):
        joint_knob = None
        o = self.scene.selected_scene_object
        if o is not None and "joint_control_knob" in o._components:
            joint_knob = o._components["joint_control_knob"]
        return joint_knob

    def set_twist(self, joint_name):
        x = round(float(self.twistXLineEdit.text()), self.precision)
        y = round(float(self.twistYLineEdit.text()), self.precision)
        z = round(float(self.twistZLineEdit.text()), self.precision)
        #set twist axis
        twist = np.array([x, y, z])
        magnitude = np.linalg.norm(twist)
        if magnitude > 0:
            twist /= magnitude
        self.skeleton_model["cos_map"][joint_name]["y"] = twist

    def set_swing(self, joint_name):
        x = round(float(self.swingXLineEdit.text()), self.precision)
        y = round(float(self.swingYLineEdit.text()), self.precision)
        z = round(float(self.swingZLineEdit.text()), self.precision)
        #set swing axis
        swing = np.array([x, y, z])
        magnitude = np.linalg.norm(swing)
        if magnitude > 0:
            swing /= magnitude
        self.skeleton_model["cos_map"][joint_name]["x"] = swing.tolist()

    def slot_set_twist(self):
        plot = False
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        self.set_swing(joint_knob.joint_name)
        self.set_twist(joint_knob.joint_name)
        self.update_joint_info(joint_knob)

    def slot_set_swing(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        self.set_swing(joint_knob.joint_name)
        self.set_twist(joint_knob.joint_name)
        self.update_joint_info(joint_knob)

    def slot_set_orthogonal_twist(self):
        """ https://stackoverflow.com/questions/33658620/generating-two-orthogonal-vectors-that-are-orthogonal-to-a-particular-direction """
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        #get swing axis
        swing = np.array(self.skeleton_model["cos_map"][joint_name]["x"])
        # find orthogonal vector
        y = np.array(self.skeleton_model["cos_map"][joint_name]["y"])

        #y = np.random.randn(3)  # take a random vector
        y -= y.dot(swing) * swing  # make it orthogonal to twist
        y /= np.linalg.norm(y)  # normalize it

        #replace twist axis
        self.set_twist_text(y)
        self.skeleton_model["cos_map"][joint_name]["y"] = y
        self.update_joint_info(joint_knob)

    def slot_set_orthogonal_swing(self):
        """ https://stackoverflow.com/questions/33658620/generating-two-orthogonal-vectors-that-are-orthogonal-to-a-particular-direction """
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        #get twist axis
        twist = np.array(self.skeleton_model["cos_map"][joint_name]["y"])
        x = np.array(self.skeleton_model["cos_map"][joint_name]["x"])
        x -= x.dot(twist) * twist  # make it orthogonal to twist
        x /= np.linalg.norm(x)  # normalize it
        #replace twist axis
        self.set_swing_text(x)
        self.skeleton_model["cos_map"][joint_name]["x"] = x
        self.update_joint_info(joint_knob)

    def slot_flip_twist(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        #get twist axis
        twist = np.array(self.skeleton_model["cos_map"][joint_name]["y"])
        twist *= -1
        self.skeleton_model["cos_map"][joint_name]["y"] = twist
        self.set_twist_text(twist)
        self.update_joint_info(joint_knob)

    def slot_flip_swing(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        swing = np.array(self.skeleton_model["cos_map"][joint_name]["x"])
        swing *= -1
        self.skeleton_model["cos_map"][joint_name]["x"] = swing
        self.set_swing_text(swing)
        self.update_joint_info(joint_knob)

    def slot_rotate_twist(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        #get twist axis
        angle = round(float(self.twistRotationLineEdit.text()), self.precision)
        rotation_axis = np.array(
            self.skeleton_model["cos_map"][joint_name]["x"])
        q = quaternion_about_axis(np.deg2rad(angle), rotation_axis)
        q = normalize(q)
        twist = np.array(self.skeleton_model["cos_map"][joint_name]["y"])
        twist = rotate_vector(q, twist)
        twist = normalize(twist)
        self.skeleton_model["cos_map"][joint_name]["y"] = twist
        self.set_twist_text(twist)
        self.update_joint_info(joint_knob)

    def slot_rotate_swing(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name

        angle = round(float(self.swingRotationLineEdit.text()), self.precision)
        rotation_axis = np.array(
            self.skeleton_model["cos_map"][joint_name]["y"])
        q = quaternion_about_axis(np.deg2rad(angle), rotation_axis)
        q = normalize(q)
        swing = np.array(self.skeleton_model["cos_map"][joint_name]["x"])
        swing = rotate_vector(q, swing)
        swing = normalize(swing)

        self.skeleton_model["cos_map"][joint_name]["x"] = swing
        self.set_swing_text(swing)
        self.update_joint_info(joint_knob)

    def slot_flip_z_axis(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        twist = np.array(self.skeleton_model["cos_map"][joint_name]["y"])
        swing = np.array(self.skeleton_model["cos_map"][joint_name]["x"])
        new_swing = twist
        new_twist = swing
        #print("new swing", new_swing, swing)
        self.skeleton_model["cos_map"][joint_name]["y"] = new_twist
        self.skeleton_model["cos_map"][joint_name]["x"] = new_swing
        self.set_swing_text(new_swing)
        self.set_twist_text(new_twist)
        self.update_joint_info(joint_knob)

    def slot_guess_cos_map(self):
        """ creates a guess for the coordinate system for all joints"""
        temp_skeleton = copy(self.skeleton)
        temp_skeleton.skeleton_model = self.skeleton_model
        cos_map = create_local_cos_map_from_skeleton_axes_with_map(
            temp_skeleton)
        self.skeleton_model["cos_map"] = cos_map
        joint_knob = self.get_selected_joint()
        if joint_knob is not None:
            self.update_joint_info(joint_knob)

    def slot_reset_cos_map(self):
        """ resets the coordinate systems for all joints"""
        for joint_name in self.skeleton_model["cos_map"]:
            up_vector = self.skeleton_model["cos_map"][joint_name]["y"]
            x_vector = self.skeleton_model["cos_map"][joint_name]["x"]
            if up_vector is not None and x_vector is not None:
                new_up_vector, new_x_vector = self.reset_joint_cos(
                    joint_name, up_vector, x_vector)
                self.skeleton_model["cos_map"][joint_name]["y"] = new_up_vector
                self.skeleton_model["cos_map"][joint_name]["x"] = new_x_vector
        joint_knob = self.get_selected_joint()
        if joint_knob is not None:
            self.update_joint_info(joint_knob)

    def slot_guess_selected_cos_map(self):
        """ creates a guess for the for the selected joint"""
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        temp_skeleton = copy(self.skeleton)
        temp_skeleton.skeleton_model = self.skeleton_model
        cos_map = create_local_cos_map_from_skeleton_axes_with_map(
            temp_skeleton)
        self.skeleton_model["cos_map"][joint_name] = cos_map[joint_name]
        self.update_joint_info(joint_knob)

    def slot_reset_selected_cos_map(self):
        """ creates resetrs the coordinate system for the selected joint"""
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        if joint_name in self.skeleton_model["cos_map"]:
            up_vector = self.skeleton_model["cos_map"][joint_name]["y"]
            x_vector = self.skeleton_model["cos_map"][joint_name]["x"]
            new_up_vector, new_x_vector = self.reset_joint_cos(
                joint_name, up_vector, x_vector)
            self.skeleton_model["cos_map"][joint_name]["y"] = new_up_vector
            self.skeleton_model["cos_map"][joint_name]["x"] = new_x_vector
            self.update_joint_info(joint_knob)

    def slot_update_joint_map(self):
        if not self.is_updating_joint_info and "joints" in self.skeleton_model:
            joint_knob = self.get_selected_joint()
            if joint_knob is not None:
                new_joint_key = str(self.jointMapComboBox.currentText())
                old_joint_key = find_key(self.skeleton_model["joints"],
                                         joint_knob.joint_name)
                if old_joint_key in self.skeleton_model["joints"]:
                    self.skeleton_model["joints"][old_joint_key] = None
                self.skeleton_model["joints"][
                    new_joint_key] = joint_knob.joint_name
                print("update joint mapping", joint_knob.joint_name,
                      new_joint_key)
            else:
                print("is updating joint info")

    def reset_joint_cos(self,
                        joint_name,
                        up_vector,
                        x_vector,
                        target_up_vector=DEFAULT_TARGET_CS_UP):
        """ rotates the up_vector to look towards target_up_vector and rotates the x_vector with the same rotation """
        m = self.skeleton.nodes[joint_name].get_global_matrix(
            self.skeleton.reference_frame)[:3, :3]
        m_inv = np.linalg.inv(m)
        target_up_vector = normalize(target_up_vector)
        local_target = np.dot(m_inv, target_up_vector)
        local_target = normalize(local_target)
        q = quaternion_from_vector_to_vector(up_vector, local_target)
        x_vector = rotate_vector(q, x_vector)

        x_vector -= x_vector.dot(
            local_target) * local_target  # make it orthogonal to twist
        x_vector /= np.linalg.norm(x_vector)  # normalize it
        x_vector = normalize(x_vector)
        return local_target, x_vector

    def slot_update_aligning_root_joint(self):
        if not self.is_updating_joint_info and "joints" in self.skeleton_model:
            self.aligning_root_node = str(
                self.aligningRootComboBox.currentText())

    def slot_load_default_pose(self):
        filename = QFileDialog.getOpenFileName(self, 'Load From File', '.')[0]
        filename = str(filename)
        if os.path.isfile(filename):
            motion = load_motion_from_bvh(filename)
            if len(motion.frames):
                self.reference_frame = motion.frames[0]
                frames = [self.reference_frame]
                self.controller.replace_frames(frames)
                self.controller.set_reference_frame(0)
                self.controller.updateTransformation()
                print("replaced frames")

    def slot_apply_scale(self):
        scale = float(self.scaleLineEdit.text())
        if scale > 0:
            self.controller.set_scale(scale)
            frames = [self.reference_frame]
            self.controller.replace_frames(frames)
            self.controller.currentFrameNumber = 0
            self.controller.updateTransformation()

    def slot_align_to_up_axis(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        if joint_name in self.skeleton_model["cos_map"]:
            up_vector = self.skeleton_model["cos_map"][joint_name]["y"]
            x_vector = self.skeleton_model["cos_map"][joint_name]["x"]
            q_offset = get_axis_correction(self.skeleton, joint_name,
                                           up_vector, OPENGL_UP_AXIS)
            up_vector = rotate_vector(q_offset, up_vector)
            x_vector = rotate_vector(q_offset, x_vector)
            self.skeleton_model["cos_map"][joint_name]["x"] = normalize(
                x_vector)
            self.skeleton_model["cos_map"][joint_name]["y"] = normalize(
                up_vector)
            self.update_joint_info(joint_knob)

    def slot_align_to_forward_axis(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        if joint_name in self.skeleton_model["cos_map"]:
            up_vector = self.skeleton_model["cos_map"][joint_name]["y"]

            m = self.skeleton.nodes[joint_name].get_global_matrix(
                self.skeleton.reference_frame)[:3, :3]
            m_inv = np.linalg.inv(m)
            target_vector = np.dot(m, up_vector)
            target_vector[1] = 0
            target_vector = normalize(target_vector)
            local_up = np.dot(m_inv, target_vector)
            local_up = normalize(local_up)
            self.skeleton_model["cos_map"][joint_name]["y"] = local_up

            x_vector = self.skeleton_model["cos_map"][joint_name]["x"]
            q = quaternion_from_vector_to_vector(up_vector, local_up)
            x_vector = rotate_vector(q, x_vector)

            x_vector -= x_vector.dot(
                local_up) * local_up  # make it orthogonal to twist
            x_vector /= np.linalg.norm(x_vector)  # normalize it
            self.skeleton_model["cos_map"][joint_name]["x"] = normalize(
                x_vector)
            self.update_joint_info(joint_knob)

    def slot_mirror_left_to_right(self):
        self.skeleton_model = mirror_join_map(self.skeleton,
                                              self.skeleton_model,
                                              STANDARD_MIRROR_MAP_LEFT)
        print("mirrored left to right")
        print(self.skeleton_model["joints"])

    def slot_mirror_right_to_left(self):
        self.skeleton_model = mirror_join_map(self.skeleton,
                                              self.skeleton_model,
                                              STANDARD_MIRROR_MAP_RIGHT)
        print("mirrored right to left")
    def __init__(self, controller, scene, share_widget, parent=None):
        QDialog.__init__(self, parent)
        Ui_Dialog.setupUi(self, self)

        self.leftView = SceneViewerWidget(parent,
                                          share_widget,
                                          size=(400, 400),
                                          use_frame_buffer=False)
        self.leftView.setObjectName("left")
        self.leftView.setMinimumSize(400, 400)
        self.leftView.initializeGL()
        self.leftView.enable_mouse_interaction = False
        self.leftViewerLayout.addWidget(self.leftView)

        self.rightView = SceneViewerWidget(parent,
                                           self.leftView,
                                           size=(400, 400),
                                           use_frame_buffer=False)
        self.rightView.setObjectName("right")
        self.rightView.setMinimumSize(400, 400)
        self.rightView.initializeGL()
        self.rightView.enable_mouse_interaction = False
        self.rightViewerLayout.addWidget(self.rightView)

        self.fps = 60
        self.dt = 1 / 60
        self.timer = QTimer()
        self.timer.timeout.connect(self.draw)
        self.timer.start(0)
        self.timer.setInterval(1000.0 / self.fps)
        self.scene = scene
        self.right_controller = None
        self.skeleton = None
        self.rightView.makeCurrent()
        self.right_scene = EditorScene(True)
        if controller is not None:
            self.right_controller = self.copy_controller(
                controller, self.right_scene)
            self.skeleton = self.right_controller.get_skeleton()
            n_frames = self.right_controller.getNumberOfFrames()
            self._fill_list_with_joints(controller)
        else:
            n_frames = 0

        self.leftView.makeCurrent()
        self.left_scene = EditorScene(True)

        self.copyButton.clicked.connect(self.copy_from_left_to_right)
        self.left_controller = None
        self.controllers = dict()
        self._fill_list_with_scene_objects(scene)

        self.selectButton.clicked.connect(self.slot_accept)
        self.cancelButton.clicked.connect(self.slot_reject)

        self.selectAllJointsButton.clicked.connect(self.slot_select_all_joints)
        self.selectJointChildrenButton.clicked.connect(
            self.slot_select_joint_children)
        self.deselectJointChildrenButton.clicked.connect(
            self.slot_deselect_joint_children)
        self.clearSelectedJointsButton.clicked.connect(
            self.slot_clear_all_joints)

        self.leftStartFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftEndFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftStartFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)
        self.leftEndFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)

        self.leftDisplayFrameSlider.valueChanged.connect(
            self.left_display_changed)
        self.leftDisplayFrameSpinBox.valueChanged.connect(
            self.left_display_changed)

        self.rightStartFrameSlider.valueChanged.connect(
            self.right_slider_frame_changed)
        self.rightStartFrameSpinBox.valueChanged.connect(
            self.right_spinbox_frame_changed)
        self.rightEndFrameSlider.valueChanged.connect(
            self.right_slider_frame_changed)
        self.rightEndFrameSpinBox.valueChanged.connect(
            self.right_spinbox_frame_changed)

        self.rightDisplayFrameSpinBox.valueChanged.connect(
            self.right_display_changed)
        self.rightDisplayFrameSlider.valueChanged.connect(
            self.right_display_changed)

        self.sceneObjectListWidget.itemClicked.connect(
            self.load_src_controller)
        self.success = False
        self.n_frames = n_frames
        self.start_frame = 0
        self.end_frame = n_frames - 1  #
        self.set_right_frame_range()
        self.initialized = False
class CopyFromSourceDialog(QDialog, Ui_Dialog):
    def __init__(self, controller, scene, share_widget, parent=None):
        QDialog.__init__(self, parent)
        Ui_Dialog.setupUi(self, self)

        self.leftView = SceneViewerWidget(parent,
                                          share_widget,
                                          size=(400, 400),
                                          use_frame_buffer=False)
        self.leftView.setObjectName("left")
        self.leftView.setMinimumSize(400, 400)
        self.leftView.initializeGL()
        self.leftView.enable_mouse_interaction = False
        self.leftViewerLayout.addWidget(self.leftView)

        self.rightView = SceneViewerWidget(parent,
                                           self.leftView,
                                           size=(400, 400),
                                           use_frame_buffer=False)
        self.rightView.setObjectName("right")
        self.rightView.setMinimumSize(400, 400)
        self.rightView.initializeGL()
        self.rightView.enable_mouse_interaction = False
        self.rightViewerLayout.addWidget(self.rightView)

        self.fps = 60
        self.dt = 1 / 60
        self.timer = QTimer()
        self.timer.timeout.connect(self.draw)
        self.timer.start(0)
        self.timer.setInterval(1000.0 / self.fps)
        self.scene = scene
        self.right_controller = None
        self.skeleton = None
        self.rightView.makeCurrent()
        self.right_scene = EditorScene(True)
        if controller is not None:
            self.right_controller = self.copy_controller(
                controller, self.right_scene)
            self.skeleton = self.right_controller.get_skeleton()
            n_frames = self.right_controller.getNumberOfFrames()
            self._fill_list_with_joints(controller)
        else:
            n_frames = 0

        self.leftView.makeCurrent()
        self.left_scene = EditorScene(True)

        self.copyButton.clicked.connect(self.copy_from_left_to_right)
        self.left_controller = None
        self.controllers = dict()
        self._fill_list_with_scene_objects(scene)

        self.selectButton.clicked.connect(self.slot_accept)
        self.cancelButton.clicked.connect(self.slot_reject)

        self.selectAllJointsButton.clicked.connect(self.slot_select_all_joints)
        self.selectJointChildrenButton.clicked.connect(
            self.slot_select_joint_children)
        self.deselectJointChildrenButton.clicked.connect(
            self.slot_deselect_joint_children)
        self.clearSelectedJointsButton.clicked.connect(
            self.slot_clear_all_joints)

        self.leftStartFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftEndFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftStartFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)
        self.leftEndFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)

        self.leftDisplayFrameSlider.valueChanged.connect(
            self.left_display_changed)
        self.leftDisplayFrameSpinBox.valueChanged.connect(
            self.left_display_changed)

        self.rightStartFrameSlider.valueChanged.connect(
            self.right_slider_frame_changed)
        self.rightStartFrameSpinBox.valueChanged.connect(
            self.right_spinbox_frame_changed)
        self.rightEndFrameSlider.valueChanged.connect(
            self.right_slider_frame_changed)
        self.rightEndFrameSpinBox.valueChanged.connect(
            self.right_spinbox_frame_changed)

        self.rightDisplayFrameSpinBox.valueChanged.connect(
            self.right_display_changed)
        self.rightDisplayFrameSlider.valueChanged.connect(
            self.right_display_changed)

        self.sceneObjectListWidget.itemClicked.connect(
            self.load_src_controller)
        self.success = False
        self.n_frames = n_frames
        self.start_frame = 0
        self.end_frame = n_frames - 1  #
        self.set_right_frame_range()
        self.initialized = False

    def closeEvent(self, e):
        self.timer.stop()
        self.leftView.makeCurrent()
        del self.leftView
        self.rightView.makeCurrent()
        del self.rightView

    def copy_from_left_to_right(self):
        """ copy values from left start to end to right start to end and interpolate with the rest """
        if self.left_controller is not None and self.right_controller is not None and self.skeleton is not None:
            left_frames = self.left_controller.get_frames()
            right_frames = self.right_controller.get_frames()

            n_right_frames = self.right_controller.getNumberOfFrames()
            print("loaded", len(right_frames), n_right_frames)
            dest_start = self.rightStartFrameSlider.value()
            src_start = self.leftStartFrameSlider.value()
            src_end = self.leftEndFrameSlider.value() + 1
            dest_end = self.rightEndFrameSlider.value() + 1

            n_dest_frames = dest_end - dest_start
            if n_dest_frames <= 0:
                print("wrong dest frames", dest_start, dest_end)
                return

            #extract number of copied frames from source
            n_copied_frames = src_end - src_start
            if n_copied_frames <= 0:
                print("wrong src frames", src_start, src_end)
                return

            joint_list, joint_index_list = self.get_selected_joints()
            print("copy", joint_list, joint_index_list)

            modified_frames = self.copy_joint_values(left_frames, right_frames,
                                                     joint_list,
                                                     joint_index_list,
                                                     src_start, src_end,
                                                     dest_start, dest_end)
            n_blend_range = int(self.blendRangeLineEdit.text())
            if n_blend_range > 0:
                modified_frames = self.apply_blending(modified_frames,
                                                      joint_list,
                                                      joint_index_list,
                                                      dest_start, dest_end - 1,
                                                      n_blend_range)
            self.right_controller.replace_frames(modified_frames)
            n_new_right_frames = self.right_controller.getNumberOfFrames()
            print("finished overwriting", n_right_frames, n_new_right_frames)

    def copy_joint_values(self, left_frames, right_frames, joint_list,
                          joint_index_list, src_start, src_end, dest_start,
                          dest_end):
        n_copied_frames = src_end - src_start
        n_dest_frames = dest_end - dest_start
        modified_frames = np.array(right_frames)
        if n_copied_frames > 1:
            src_frames = stretch_motion(self.skeleton,
                                        left_frames[src_start:src_end],
                                        n_dest_frames)
        else:
            src_frames = []
            for i in range(n_dest_frames):
                src_frames.append(left_frames[src_start])
            src_frames = np.array(src_frames)
        print("copy ", n_copied_frames, n_dest_frames)
        for frame_idx in range(n_dest_frames):
            modified_frames[dest_start + frame_idx][
                joint_index_list] = src_frames[frame_idx][joint_index_list]
        return modified_frames

    def apply_blending(self, frames, joint_list, joint_index_list, dest_start,
                       dest_end, n_blend_range):
        n_frames = len(frames)
        blend_start = max(dest_start - n_blend_range, 0)
        start_window = dest_start - blend_start
        blend_end = min(dest_end + n_blend_range, n_frames - 1)
        end_window = blend_end - dest_end
        #remove root indices
        print("blend ", dest_start, dest_end, n_blend_range, start_window,
              end_window)
        quat_joint_index_list = list(joint_index_list)
        if self.skeleton.root in joint_list:
            # apply root smnoothing and remove from index list
            if start_window > 0:
                frames = smooth_translation_in_quat_frames(
                    frames, dest_start, start_window)
            if end_window > 0:
                frames = smooth_translation_in_quat_frames(
                    frames, dest_end, end_window)
            for i in range(3):
                quat_joint_index_list.remove(i)

        if len(quat_joint_index_list) > 0:
            o = 0
            for j in joint_list:
                q_indices = quat_joint_index_list[o:o + 4]
                if start_window > 0:
                    frames = create_transition_for_joints_using_slerp(
                        frames, q_indices, blend_start, dest_start,
                        start_window, BLEND_DIRECTION_FORWARD)
                if end_window > 0:
                    print(j, q_indices)
                    frames = create_transition_for_joints_using_slerp(
                        frames, q_indices, dest_end, blend_end, end_window,
                        BLEND_DIRECTION_BACKWARD)
                o += 4

        return frames

    def get_selected_joints(self):
        joint_list = []
        joint_index_list = []
        for row_idx in range(self.jointTableWidget.rowCount()):
            index_cell = self.jointTableWidget.item(row_idx, 0)
            if index_cell.checkState() == Qt.Checked:
                name_cell = self.jointTableWidget.item(row_idx, 1)
                joint_name = str(name_cell.text())
                joint_list.append(joint_name)
                if joint_name == self.skeleton.root:
                    offset = 0
                    n_channels = 7
                else:
                    offset = self.skeleton.nodes[
                        joint_name].quaternion_frame_index * 4 + 3
                    n_channels = 4
                joint_index_list += list(range(offset, offset + n_channels))
        return joint_list, joint_index_list

    def _fill_list_with_joints(self, controller):
        #self.jointTableWidget.clear()
        for joint_name in controller.get_animated_joints():
            insertRow = self.jointTableWidget.rowCount()
            self.jointTableWidget.insertRow(insertRow)
            indexItem = QTableWidgetItem("")
            indexItem.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
            indexItem.setCheckState(Qt.Unchecked)
            self.jointTableWidget.setItem(insertRow, 0, indexItem)
            self.jointTableWidget.setItem(insertRow, 1,
                                          QTableWidgetItem(str(joint_name)))

    def copy_controller(self, controller, target_scene):
        skeleton = controller.get_skeleton_copy()
        mv = controller.get_motion_vector_copy()
        o = target_scene.object_builder.create_object("animation_controller",
                                                      "",
                                                      skeleton,
                                                      mv,
                                                      mv.frame_time,
                                                      semantic_annotation=None)
        return o._components["animation_controller"]

    def load_src_controller(self, item):
        if item is None:
            return
        selected_item = str(self.sceneObjectListWidget.currentItem().text())
        if self.left_controller is not None:
            node_id = self.left_controller.scene_object.node_id
            self.left_scene.removeObject(node_id)
        src_controller = self.controllers[selected_item]
        self.left_controller = self.copy_controller(src_controller,
                                                    self.left_scene)

        n_frames = src_controller.getNumberOfFrames()

        self.leftStartFrameSlider.setRange(0, n_frames - 1)
        self.leftStartFrameSlider.setValue(0)
        self.leftEndFrameSlider.setRange(0, n_frames - 1)
        self.leftEndFrameSlider.setValue(n_frames - 1)
        self.leftDisplayFrameSlider.setRange(0, n_frames - 1)
        self.leftDisplayFrameSlider.setRange(0, n_frames - 1)

    def draw(self):
        """ draw current scene on the given view
        (note before calling this function the context of the view has to be set as current using makeCurrent() and afterwards the doubble buffer has to swapped to display the current frame swapBuffers())
        """
        if not self.initialized:
            if self.leftView.graphics_context is not None and self.rightView.graphics_context is not None:
                self.leftView.resize(400, 400)
                self.rightView.resize(400, 400)
                self.initialized = True
        self.left_scene.update(self.dt)
        self.leftView.makeCurrent()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.leftView.graphics_context.render(self.left_scene)
        self.leftView.swapBuffers()

        self.right_scene.update(self.dt)
        self.rightView.makeCurrent()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.rightView.graphics_context.render(self.right_scene)
        self.rightView.swapBuffers()
        #print(len(self.right_scene.lightSources))

    def right_display_changed(self, frame_idx):
        if self.right_controller is not None:
            self.right_controller.setCurrentFrameNumber(frame_idx)
            self.rightDisplayFrameSpinBox.setValue(frame_idx)

    def left_display_changed(self, frame_idx):
        if self.left_controller is not None:
            self.left_controller.setCurrentFrameNumber(frame_idx)
            self.leftDisplayFrameSpinBox.setValue(frame_idx)

    def _fill_list_with_scene_objects(self, scene):
        for sceneObject in get_animation_controllers(scene):
            item = QListWidgetItem()
            item.setText(sceneObject.name)
            item.setData(Qt.UserRole, sceneObject.node_id)
            self.sceneObjectListWidget.addItem(item)
            self.controllers[sceneObject.name] = sceneObject._components[
                "animation_controller"]

    def set_right_frame_range(self):
        self.leftStartFrameSlider.setRange(0, self.n_frames - 1)
        self.leftEndFrameSlider.setRange(0, self.n_frames - 1)
        self.leftStartFrameSpinBox.setRange(0, self.n_frames - 1)
        self.leftEndFrameSpinBox.setRange(0, self.n_frames - 1)

        self.leftEndFrameSlider.setValue(self.n_frames - 1)
        self.leftEndFrameSpinBox.setValue(self.n_frames - 1)

        self.rightStartFrameSlider.setRange(0, self.n_frames - 1)
        self.rightStartFrameSpinBox.setRange(0, self.n_frames - 1)

        self.rightEndFrameSlider.setRange(0, self.n_frames - 1)
        self.rightEndFrameSpinBox.setRange(0, self.n_frames - 1)
        self.rightEndFrameSlider.setValue(self.n_frames - 1)
        self.rightEndFrameSpinBox.setValue(self.n_frames - 1)

        self.rightDisplayFrameSlider.setRange(0, self.n_frames - 1)
        self.rightDisplayFrameSpinBox.setRange(0, self.n_frames - 1)

    def left_spinbox_frame_changed(self, frame):
        self.leftStartFrameSlider.setValue(self.leftStartFrameSpinBox.value())
        self.leftEndFrameSlider.setValue(self.leftEndFrameSpinBox.value())

    def left_slider_frame_changed(self, frame):
        self.leftStartFrameSpinBox.setValue(self.leftStartFrameSlider.value())
        self.leftEndFrameSpinBox.setValue(self.leftEndFrameSlider.value())

    def right_spinbox_frame_changed(self, frame):
        self.rightStartFrameSlider.setValue(
            self.rightStartFrameSpinBox.value())
        self.rightEndFrameSlider.setValue(self.rightEndFrameSpinBox.value())

    def right_slider_frame_changed(self, frame):
        self.rightStartFrameSpinBox.setValue(
            self.rightStartFrameSlider.value())
        self.rightEndFrameSpinBox.setValue(self.rightEndFrameSlider.value())

    def slot_accept(self):
        self.success = True
        self.close()

    def slot_reject(self):
        self.close()

    def slot_select_all_joints(self):
        for row_idx in range(self.jointTableWidget.rowCount()):
            index_cell = self.jointTableWidget.item(row_idx, 0)
            index_cell.setCheckState(Qt.Checked)

    def slot_clear_all_joints(self):
        for row_idx in range(self.jointTableWidget.rowCount()):
            index_cell = self.jointTableWidget.item(row_idx, 0)
            index_cell.setCheckState(Qt.Unchecked)

    def slot_select_joint_children(self):
        for row_idx in range(self.jointTableWidget.rowCount()):
            index_cell = self.jointTableWidget.item(row_idx, 0)
            name_cell = self.jointTableWidget.item(row_idx, 1)
            if index_cell.isSelected() or name_cell.isSelected():
                joint_name = str(name_cell.text())
                self.change_joint_children_state(joint_name, Qt.Checked)

    def slot_deselect_joint_children(self):
        for row_idx in range(self.jointTableWidget.rowCount()):
            index_cell = self.jointTableWidget.item(row_idx, 0)
            name_cell = self.jointTableWidget.item(row_idx, 1)
            if index_cell.isSelected() or name_cell.isSelected():
                joint_name = str(name_cell.text())
                self.change_joint_children_state(joint_name, Qt.Unchecked)

    def change_joint_children_state(self, joint_name, state):
        self.change_joint_state(joint_name, state)
        if joint_name in self.skeleton.nodes:
            for n in self.skeleton.nodes[joint_name].children:
                self.change_joint_children_state(n.node_name, state)

    def change_joint_state(self, joint_name, state):
        print("select", joint_name)
        for row_idx in range(self.jointTableWidget.rowCount()):
            name_cell = self.jointTableWidget.item(row_idx, 1)
            if joint_name == str(name_cell.text()):
                index_cell = self.jointTableWidget.item(row_idx, 0)
                index_cell.setCheckState(state)
                break
    def __init__(self, controller, scene, share_widget, parent=None):
        QDialog.__init__(self, parent)
        Ui_Dialog.setupUi(self, self)
        self.leftView = SceneViewerWidget(parent,
                                          share_widget,
                                          size=(400, 400))
        self.leftView.setObjectName("left")
        self.leftView.setMinimumSize(400, 400)
        self.leftView.initializeGL()
        self.leftView.enable_mouse_interaction = True
        self.leftView.mouse_click.connect(self.on_mouse_click)
        self.leftView.mouse_move.connect(self.on_mouse_move)
        self.leftView.mouse_release.connect(self.on_mouse_release)
        self.leftViewerLayout.addWidget(self.leftView)

        self.radius = 1.0
        self.fps = 60
        self.dt = 1 / 60
        self.timer = QTimer()
        self.timer.timeout.connect(self.draw)
        self.timer.start(0)
        self.timer.setInterval(1000.0 / self.fps)
        self.scene = scene
        self.original_controller = controller
        self.controller = None
        self.skeleton = None
        self.leftView.makeCurrent()
        self.left_scene = EditorScene(True)
        self.left_scene.enable_scene_edit_widget = True
        self.left_scene.scene_edit_widget.register_move_callback(
            self.on_move_widget)
        self._animation_editor = None
        if controller is not None:
            self.controller = self.copy_controller(controller, self.left_scene)
            fps = int(1.0 / self.controller._motion.mv.frame_time)
            self.fpsLineEdit.setText(str(fps))
            self.skeleton = self.controller.get_skeleton()
            n_frames = self.controller.getNumberOfFrames()
            self.skeleton_vis = self.controller.scene_object._components[
                "skeleton_vis"]
            self.init_joints(self.controller)
        else:
            n_frames = 0

        self.selectButton.clicked.connect(self.slot_accept)
        self.cancelButton.clicked.connect(self.slot_reject)

        self.deleteBeforeButton.clicked.connect(self.slot_delete_frames_before)
        self.deleteAfterButton.clicked.connect(self.slot_delete_frames_after)
        self.concatenateButton.clicked.connect(self.slot_concatenate)
        self.translateJointButton.clicked.connect(self.slot_translate_joint)
        self.rotateJointButton.clicked.connect(self.slot_rotate_joint)
        self.smoothFramesButton.clicked.connect(self.slot_smooth_frames)
        self.mirrorAnimationButton.clicked.connect(self.slot_mirror_animation)
        self.fixJointButton.clicked.connect(self.slot_fix_joint)
        self.clearConstraintsButton.clicked.connect(
            self.slot_clear_constraints)
        self.undoButton.clicked.connect(self.slot_undo)
        self.exportCommandsButton.clicked.connect(
            self.slot_export_command_history)
        self.applyConstraintsButton.clicked.connect(
            self.slot_apply_constraints)
        self.resampleButton.clicked.connect(self.slot_resample_motion)
        self.setFPSButton.clicked.connect(self.slot_set_fps)
        self.flipBlenderCoordinateSystemButton.clicked.connect(
            self.flip_blender_coordinate_systems)

        self.leftStartFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftEndFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftStartFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)
        self.leftEndFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)

        self.leftDisplayFrameSlider.valueChanged.connect(
            self.left_display_changed)
        self.leftDisplayFrameSpinBox.valueChanged.connect(
            self.left_display_changed)
        #self.close.triggered.connect(self.on_close)

        self.guessGroundHeightButton.clicked.connect(self.guess_ground_height)
        self.moveToGroundButton.clicked.connect(self.move_to_ground)

        self.detectFootContactsButton.clicked.connect(
            self.detect_foot_contacts)
        self.groundFeetButton.clicked.connect(self.ground_feet)

        self.setAnnotationStartButton.clicked.connect(
            self.set_annotation_edit_start)
        self.createAnnotationButton.clicked.connect(
            self.create_annotation_section)
        self.removeAnnotationButton.clicked.connect(
            self.remove_annotation_section)

        self.edited_knob = None
        self.success = False
        self.n_frames = n_frames
        self.start_frame = 0
        self.end_frame = n_frames - 1  #
        self.set_frame_range()
        self.initialized = False
        self.collect_constraints = True
        self.original_frames = np.array(self.controller.get_frames())
        self.annotation_editor = AnnotationEditor()
        self.contactLabelView.setTimeLineParameters(100000, 10)
        self.contactLabelView.initScene()
        self.contactLabelView.show()
        if not self._animation_editor.motion_grounding.initialized:
            self.detectFootContactsButton.setEnabled(False)
            self.groundFeetButton.setEnabled(False)
        else:
            ground_annotation = collections.OrderedDict()
            color_map = collections.OrderedDict()
            for label in self._animation_editor.foot_constraint_generator.contact_joints:
                color_map[label] = get_random_color()
                ground_annotation[label] = []
            self.annotation_editor.set_annotation(ground_annotation, color_map)
            self.fill_label_combobox()
        self.init_label_time_line()
class AnimationEditorDialog(QDialog, Ui_Dialog):
    def __init__(self, controller, scene, share_widget, parent=None):
        QDialog.__init__(self, parent)
        Ui_Dialog.setupUi(self, self)
        self.leftView = SceneViewerWidget(parent,
                                          share_widget,
                                          size=(400, 400))
        self.leftView.setObjectName("left")
        self.leftView.setMinimumSize(400, 400)
        self.leftView.initializeGL()
        self.leftView.enable_mouse_interaction = True
        self.leftView.mouse_click.connect(self.on_mouse_click)
        self.leftView.mouse_move.connect(self.on_mouse_move)
        self.leftView.mouse_release.connect(self.on_mouse_release)
        self.leftViewerLayout.addWidget(self.leftView)

        self.radius = 1.0
        self.fps = 60
        self.dt = 1 / 60
        self.timer = QTimer()
        self.timer.timeout.connect(self.draw)
        self.timer.start(0)
        self.timer.setInterval(1000.0 / self.fps)
        self.scene = scene
        self.original_controller = controller
        self.controller = None
        self.skeleton = None
        self.leftView.makeCurrent()
        self.left_scene = EditorScene(True)
        self.left_scene.enable_scene_edit_widget = True
        self.left_scene.scene_edit_widget.register_move_callback(
            self.on_move_widget)
        self._animation_editor = None
        if controller is not None:
            self.controller = self.copy_controller(controller, self.left_scene)
            fps = int(1.0 / self.controller._motion.mv.frame_time)
            self.fpsLineEdit.setText(str(fps))
            self.skeleton = self.controller.get_skeleton()
            n_frames = self.controller.getNumberOfFrames()
            self.skeleton_vis = self.controller.scene_object._components[
                "skeleton_vis"]
            self.init_joints(self.controller)
        else:
            n_frames = 0

        self.selectButton.clicked.connect(self.slot_accept)
        self.cancelButton.clicked.connect(self.slot_reject)

        self.deleteBeforeButton.clicked.connect(self.slot_delete_frames_before)
        self.deleteAfterButton.clicked.connect(self.slot_delete_frames_after)
        self.concatenateButton.clicked.connect(self.slot_concatenate)
        self.translateJointButton.clicked.connect(self.slot_translate_joint)
        self.rotateJointButton.clicked.connect(self.slot_rotate_joint)
        self.smoothFramesButton.clicked.connect(self.slot_smooth_frames)
        self.mirrorAnimationButton.clicked.connect(self.slot_mirror_animation)
        self.fixJointButton.clicked.connect(self.slot_fix_joint)
        self.clearConstraintsButton.clicked.connect(
            self.slot_clear_constraints)
        self.undoButton.clicked.connect(self.slot_undo)
        self.exportCommandsButton.clicked.connect(
            self.slot_export_command_history)
        self.applyConstraintsButton.clicked.connect(
            self.slot_apply_constraints)
        self.resampleButton.clicked.connect(self.slot_resample_motion)
        self.setFPSButton.clicked.connect(self.slot_set_fps)
        self.flipBlenderCoordinateSystemButton.clicked.connect(
            self.flip_blender_coordinate_systems)

        self.leftStartFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftEndFrameSlider.valueChanged.connect(
            self.left_slider_frame_changed)
        self.leftStartFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)
        self.leftEndFrameSpinBox.valueChanged.connect(
            self.left_spinbox_frame_changed)

        self.leftDisplayFrameSlider.valueChanged.connect(
            self.left_display_changed)
        self.leftDisplayFrameSpinBox.valueChanged.connect(
            self.left_display_changed)
        #self.close.triggered.connect(self.on_close)

        self.guessGroundHeightButton.clicked.connect(self.guess_ground_height)
        self.moveToGroundButton.clicked.connect(self.move_to_ground)

        self.detectFootContactsButton.clicked.connect(
            self.detect_foot_contacts)
        self.groundFeetButton.clicked.connect(self.ground_feet)

        self.setAnnotationStartButton.clicked.connect(
            self.set_annotation_edit_start)
        self.createAnnotationButton.clicked.connect(
            self.create_annotation_section)
        self.removeAnnotationButton.clicked.connect(
            self.remove_annotation_section)

        self.edited_knob = None
        self.success = False
        self.n_frames = n_frames
        self.start_frame = 0
        self.end_frame = n_frames - 1  #
        self.set_frame_range()
        self.initialized = False
        self.collect_constraints = True
        self.original_frames = np.array(self.controller.get_frames())
        self.annotation_editor = AnnotationEditor()
        self.contactLabelView.setTimeLineParameters(100000, 10)
        self.contactLabelView.initScene()
        self.contactLabelView.show()
        if not self._animation_editor.motion_grounding.initialized:
            self.detectFootContactsButton.setEnabled(False)
            self.groundFeetButton.setEnabled(False)
        else:
            ground_annotation = collections.OrderedDict()
            color_map = collections.OrderedDict()
            for label in self._animation_editor.foot_constraint_generator.contact_joints:
                color_map[label] = get_random_color()
                ground_annotation[label] = []
            self.annotation_editor.set_annotation(ground_annotation, color_map)
            self.fill_label_combobox()
        self.init_label_time_line()

    def closeEvent(self, e):
        self.timer.stop()
        self.leftView.makeCurrent()
        try:
            del self.leftView
        except:
            print("ignore the error and keep going")

    def slot_undo(self):
        frames = self._animation_editor.undo()
        if frames is not None:
            self.n_frames = len(frames)
            self.set_frame_range()
            self.original_controller.replace_frames(frames)
            self.original_controller.updateTransformation()
            print("undo")
        else:
            print("nothing to undo")

    def on_mouse_click(self, event, ray_start, ray_dir, pos, node_id):
        if event.button() == Qt.LeftButton:
            self.left_scene.select_object(node_id, (ray_start, ray_dir))
            joint_knob = self.get_selected_joint()
            label = "Selected Joint: "
            if joint_knob is not None:
                label += joint_knob.joint_name
            else:
                label += "None"
            self.jointLabel.setText(label)

    def on_mouse_release(self, event):
        self.left_scene.deactivate_axis()
        if self.edited_knob is None:
            return
        position = self.edited_knob.scene_object.getPosition()
        offset = position - self.left_scene.scene_edit_widget.original_position
        joint_name = self.edited_knob.joint_name
        ik_frame_idx = self.leftDisplayFrameSlider.value()
        copy_start = self.leftStartFrameSlider.value()
        copy_end = self.leftEndFrameSlider.value() + 1
        if copy_start > copy_end:
            print("frame range is wrong", copy_start, copy_end)
            return
        frame_range = copy_start, copy_end

        self.edited_knob.edit_mode = True
        use_ccd = self.ccdCheckBox.checkState() == Qt.Checked

        blend_window_size = int(self.blendRangeLineEdit.text())
        self._animation_editor.translate_joint(joint_name,
                                               offset,
                                               ik_frame_idx,
                                               frame_range,
                                               blend_window_size,
                                               use_ccd,
                                               plot=False,
                                               apply=True)
        self.edited_knob.edit_mode = False
        self.show_change()
        self.edited_knob = None

    def on_mouse_move(self, event, last_mouse_pos, cam_pos, cam_ray):
        self.left_scene.handle_mouse_movement(cam_pos, cam_ray)

    def init_joints(self, controller):
        for joint_name in controller.get_animated_joints():
            if len(self.skeleton.nodes[joint_name].children
                   ) > 0:  # filter out end site joints
                child_node = self.skeleton.nodes[joint_name].children[0]
                if np.linalg.norm(child_node.offset) > 0:
                    self.left_scene.object_builder.create_object(
                        "joint_control_knob", controller, joint_name,
                        self.radius * self.skeleton_vis.box_scale)

    def copy_controller(self, controller, target_scene):
        skeleton = controller.get_skeleton_copy()
        mv = controller.get_motion_vector_copy()
        print("copied", mv.n_frames, len(mv.frames),
              controller.getNumberOfFrames())
        o = target_scene.object_builder.create_object("animation_controller",
                                                      "",
                                                      skeleton,
                                                      mv,
                                                      mv.frame_time,
                                                      semantic_annotation=None)

        #target_scene.object_builder.create_component("animation_editor", o)
        self._animation_editor = AnimationEditor(
            o)  #o._components["animation_editor"]
        return o._components["animation_controller"]

    def draw(self):
        """ draw current scene on the given view
        (note before calling this function the context of the view has to be set as current using makeCurrent() and afterwards the doubble buffer has to swapped to display the current frame swapBuffers())
        """
        try:
            if not self.initialized:
                if self.leftView.graphics_context is not None:
                    self.leftView.resize(400, 400)
                    self.initialized = True
        except:
            import sys
            sys.exit(0)
        self.left_scene.update(self.dt)
        self.leftView.makeCurrent()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.leftView.graphics_context.render(self.left_scene)
        self.leftView.swapBuffers()

    def left_display_changed(self, frame_idx):
        if self.controller is not None:
            self.controller.setCurrentFrameNumber(frame_idx)
            self.leftDisplayFrameSpinBox.setValue(frame_idx)
            self.contactLabelView.setFrame(frame_idx)

    def left_spinbox_frame_changed(self, frame):
        self.leftStartFrameSlider.setValue(self.leftStartFrameSpinBox.value())
        self.leftEndFrameSlider.setValue(self.leftEndFrameSpinBox.value())

    def left_slider_frame_changed(self, frame):
        self.leftStartFrameSpinBox.setValue(self.leftStartFrameSlider.value())
        self.leftEndFrameSpinBox.setValue(self.leftEndFrameSlider.value())

    def slot_accept(self):
        self.success = True
        self.close()

    def slot_reject(self):
        self.close()

    def set_frame_range(self):
        self.leftDisplayFrameSlider.setRange(0, self.n_frames - 1)
        self.leftDisplayFrameSpinBox.setRange(0, self.n_frames - 1)

        self.leftStartFrameSlider.setRange(0, self.n_frames - 1)
        self.leftEndFrameSlider.setRange(0, self.n_frames - 1)
        self.leftStartFrameSpinBox.setRange(0, self.n_frames - 1)
        self.leftEndFrameSpinBox.setRange(0, self.n_frames - 1)

        self.leftEndFrameSlider.setValue(self.n_frames - 1)
        self.leftEndFrameSpinBox.setValue(self.n_frames - 1)

    def get_selected_joint(self):
        joint_knob = None
        o = self.left_scene.selected_scene_object
        if o is not None and "joint_control_knob" in o._components:
            joint_knob = o._components["joint_control_knob"]
        return joint_knob

    def slot_translate_joint(self):
        plot = False
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        x = float(self.translateXLineEdit.text())
        y = float(self.translateYLineEdit.text())
        z = float(self.translateZLineEdit.text())
        offset = [x, y, z]
        joint_name = joint_knob.joint_name
        ik_frame_idx = self.leftDisplayFrameSlider.value()
        copy_start = self.leftStartFrameSlider.value()
        copy_end = self.leftEndFrameSlider.value() + 1
        if copy_start > copy_end:
            print("frame range is wrong", copy_start, copy_end)
            return
        frame_range = copy_start, copy_end

        joint_knob.edit_mode = True
        apply = self.collectConstraintsCheckBox.checkState() == Qt.Unchecked
        use_ccd = self.ccdCheckBox.checkState() == Qt.Checked

        blend_window_size = int(self.blendRangeLineEdit.text())
        self._animation_editor.translate_joint(joint_name, offset,
                                               ik_frame_idx, frame_range,
                                               blend_window_size, use_ccd,
                                               plot, apply)
        joint_knob.edit_mode = False
        self.show_change()

    def slot_clear_constraints(self):
        self._animation_editor.clear_constraints()

    def slot_rotate_joint(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        x = float(self.rotateXLineEdit.text())
        y = float(self.rotateYLineEdit.text())
        z = float(self.rotateZLineEdit.text())

        edit_start = self.leftStartFrameSlider.value()
        edit_end = self.leftEndFrameSlider.value() + 1
        if edit_start > edit_end:
            print("frame range is wrong", edit_start, edit_end)
            return
        frame_range = edit_start, edit_end
        offset = [x, y, z]
        window_size = int(self.blendRangeLineEdit.text())
        self._animation_editor.rotate_joint(joint_name, offset, frame_range,
                                            window_size)
        self.show_change()

    def show_change(self):
        frames = np.array(self.controller.get_frames())
        self.original_controller.replace_frames(frames)
        self.original_controller.updateTransformation()
        self.controller.updateTransformation()

    def slot_smooth_frames(self):
        window_size = int(self.smoothWindowSizeLineEdit.text())
        if window_size >= 5:
            print("smooth frames using a window size of", window_size)
            self._animation_editor.smooth_using_moving_average(window_size)
        else:
            print("Error: window size must be >= 5")

    def slot_concatenate(self):
        options = dict()
        options["activate_smoothing"] = True
        options["window"] = 20
        select_animation_dialog = SelectSceneObjectsDialog(
            self.scene,
            get_animation_controllers,
            self,
            name="Concatenate",
            properties=options)
        select_animation_dialog.exec_()
        if select_animation_dialog.success:
            o = self.scene.getObject(select_animation_dialog.selected_node_id)
            options = select_animation_dialog.properties
            animation_controller = o._components["animation_controller"]
            self.controller._motion.mv.skeleton = self.controller.get_skeleton(
            )
            self._animation_editor.concatenate(animation_controller,
                                               options["activate_smoothing"],
                                               options["window"])
            self.n_frames = self.controller.getNumberOfFrames()
            self.set_frame_range()
            self.show_change()

    def slot_mirror_animation(self):
        self._animation_editor.mirror_animation()
        self.show_change()

    def slot_delete_frames_before(self):
        frame_idx = self._animation_editor.get_current_frame_number()
        self._animation_editor.delete_frames_before(frame_idx)

    def slot_delete_frames_after(self):
        frame_idx = self._animation_editor.get_current_frame_number()
        self._animation_editor.delete_frames_after(frame_idx)

    def slot_fix_joint(self):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return
        joint_name = joint_knob.joint_name
        apply = self.collectConstraintsCheckBox.checkState() == Qt.Unchecked
        edit_start = self.leftStartFrameSlider.value()
        edit_end = self.leftEndFrameSlider.value() + 1
        joint_knob.edit_mode = True
        frame = self.controller.get_current_frame()
        p = self.controller.get_joint_position(joint_name, frame)
        p = p.tolist()
        if edit_start < edit_end:
            frame_range = edit_start, edit_end
            self._animation_editor.fix_joint(joint_name, p, frame_range, apply)
        else:
            print("frame range is wrong", edit_start, edit_end)

        joint_knob.edit_mode = False
        self.show_change()

    def slot_export_command_history(self):
        filename = QFileDialog.getSaveFileName(self, 'Save To File', '.')[0]
        save_json_file(self._animation_editor.command_history, filename)

    def slot_apply_constraints(self, plot=False):
        use_ccd = self.ccdCheckBox.checkState() == Qt.Checked
        if use_ccd:
            self._animation_editor.apply_constraints_using_ccd(plot)
        else:
            self._animation_editor.apply_constraints(plot)

    def slot_resample_motion(self):
        resample_factor = float(self.resampleFactorLineEdit.text())
        if resample_factor >= 0:
            print("resample frames using a factor of", resample_factor)
            self._animation_editor.resample_motion(resample_factor)
            self.n_frames = self.controller.getNumberOfFrames()
            self.set_frame_range()
            self.show_change()
        else:
            print("Error: resample factor must be > 0", resample_factor)

    def slot_set_fps(self):
        fps = float(self.fpsLineEdit.text())
        if fps >= 0:
            print("set fps to", fps)
            self.controller._motion.mv.frame_time = 1.0 / fps
            self.show_change()
        else:
            print("Error: window size must be > 0", fps)

    def flip_blender_coordinate_systems(self):
        self._animation_editor.flip_blender_coordinate_systems()

    def guess_ground_height(self):
        source_ground_height = self._animation_editor.guess_ground_height(
            False)
        self.sourceGroundHeightLineEdit.setText(str(source_ground_height))

    def move_to_ground(self):
        source_ground_height = float(self.sourceGroundHeightLineEdit.text())
        target_ground_height = float(self.targetGroundHeightLineEdit.text())
        self._animation_editor.move_to_ground(source_ground_height,
                                              target_ground_height)
        self.show_change()

    def detect_foot_contacts(self):
        source_ground_height = float(self.sourceGroundHeightLineEdit.text())
        ground_contacts = self._animation_editor.detect_ground_contacts(
            source_ground_height)
        ground_annotation = collections.OrderedDict()
        color_map = collections.OrderedDict()
        n_frames = self.controller.getNumberOfFrames()
        for idx in range(n_frames):
            for label in ground_contacts[idx]:
                if label not in ground_annotation:
                    color_map[label] = get_random_color()
                    ground_annotation[label] = [[]]
                ground_annotation[label][0].append(idx)
        self.annotation_editor.set_annotation(ground_annotation, color_map)
        self.init_label_time_line()

    def ground_feet(self):
        target_ground_height = float(self.targetGroundHeightLineEdit.text())
        n_frames = self.controller.getNumberOfFrames()
        ground_contacts = [[] for f in range(n_frames)]
        ground_annotation = self.annotation_editor._semantic_annotation
        for label in ground_annotation:
            if label not in self.skeleton.nodes:
                print("ignore", label)
                continue
            for entry in ground_annotation[label]:
                for idx in entry:
                    ground_contacts[idx].append(label)
        self._animation_editor.apply_foot_constraints(ground_contacts,
                                                      target_ground_height)
        self.show_change()

    def init_label_time_line(self):
        n_frames = self.controller.getNumberOfFrames()
        self.contactLabelView.clearScene()
        self.contactLabelView.create_frame_indicator()
        self.contactLabelView.setTimeLineParameters(n_frames, 10)
        self.contactLabelView.set_edit_start_frame(0)
        ground_annotation = self.annotation_editor._semantic_annotation
        color_map = self.annotation_editor._label_color_map
        if len(ground_annotation) > 0:
            for label, indices in ground_annotation.items():
                color = [0, 0, 1]
                if label in color_map:
                    color = color_map[label]
                joint_indices = []
                n_entries = len(indices)
                if n_entries > 0 and type(indices[0]) == list:
                    for i in range(n_entries):
                        joint_indices += indices[i]
                else:
                    joint_indices = indices

                self.contactLabelView.addLabel(label, joint_indices, color)
        else:
            self.contactLabelView.addLabel("empty", [], [0, 0, 0])

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_G:
            self.create_annotation_section()
        elif event.key() == Qt.Key_H:
            self.remove_annotation_section()
        elif event.key() == Qt.Key_K:
            self.set_annotation_edit_start()

    def create_annotation_section(self):
        if self.labelComboBox.count() < 1:
            return
        frame_idx = self.controller._motion.frame_idx
        start_idx = self.annotation_editor.prev_annotation_edit_frame_idx
        label = str(self.labelComboBox.currentText())
        self.annotation_editor.create_annotation_section(frame_idx, label)
        self.annotation_editor.set_annotation_edit_start(start_idx)
        self.init_label_time_line()

    def remove_annotation_section(self):
        if self.labelComboBox.count() < 1:
            return
        frame_idx = self.controller._motion.frame_idx
        start_idx = self.annotation_editor.prev_annotation_edit_frame_idx
        label = str(self.labelComboBox.currentText())
        self.annotation_editor.remove_annotation_section(frame_idx, label)
        self.annotation_editor.set_annotation_edit_start(start_idx)
        self.init_label_time_line()

    def set_annotation_edit_start(self):
        frame_idx = self.controller._motion.frame_idx
        self.annotation_editor.set_annotation_edit_start(frame_idx)
        self.contactLabelView.set_edit_start_frame(frame_idx)

    def on_move_widget(self, position):
        joint_knob = self.get_selected_joint()
        if joint_knob is None:
            return

        joint_knob.edit_mode = True
        joint_knob.scene_object.setPosition(position)
        self.edited_knob = joint_knob

    def fill_label_combobox(self):
        self.labelComboBox.clear()
        for idx, label in enumerate(
                self.annotation_editor._semantic_annotation):
            self.labelComboBox.addItem(label, idx)