Diff of /app/AnybodyResults.py [000000] .. [4de1c7]

Switch to unified view

a b/app/AnybodyResults.py
1
import re
2
import numpy as np
3
import copy
4
import matplotlib.pyplot as plt
5
from matplotlib.widgets import CheckButtons
6
from matplotlib.lines import Line2D
7
from matplotlib.cm import get_cmap
8
9
10
LINE_STYLES = ['--', '-.', '--', ':']
11
COLORS = [i for i in get_cmap('tab20').colors]
12
13
LEAPMOTION = 'Leap Motion'
14
INTERPOLATION = 'AnyBody'
15
FLEXION = 'Flexion'
16
ABDUCTION = 'Abduction'
17
DEVIATION = 'Deviation'
18
19
20
class AnybodyResults:
21
    def __init__(self, output):
22
        self.output = output
23
        self.fig = None
24
        self.ax = None
25
26
        data_dict = {INTERPOLATION: None,
27
                     LEAPMOTION: None}
28
        rotation_dict = {FLEXION: copy.deepcopy(data_dict),
29
                         ABDUCTION: copy.deepcopy(data_dict),
30
                         DEVIATION: copy.deepcopy(data_dict)}
31
        self.joint_mapping = {
32
            'CMC1': copy.deepcopy(rotation_dict),
33
            'MCP1': copy.deepcopy(rotation_dict),
34
            'DIP1': copy.deepcopy(rotation_dict),
35
            # 'CMC2': copy.deepcopy(rotation_dict),
36
            'MCP2': copy.deepcopy(rotation_dict),
37
            'PIP2': copy.deepcopy(rotation_dict),
38
            'DIP2': copy.deepcopy(rotation_dict),
39
            # 'CMC3': copy.deepcopy(rotation_dict),
40
            'MCP3': copy.deepcopy(rotation_dict),
41
            'PIP3': copy.deepcopy(rotation_dict),
42
            'DIP3': copy.deepcopy(rotation_dict),
43
            # 'CMC4': copy.deepcopy(rotation_dict),
44
            'MCP4': copy.deepcopy(rotation_dict),
45
            'PIP4': copy.deepcopy(rotation_dict),
46
            'DIP4': copy.deepcopy(rotation_dict),
47
            # 'CMC5': copy.deepcopy(rotation_dict),
48
            'MCP5': copy.deepcopy(rotation_dict),
49
            'PIP5': copy.deepcopy(rotation_dict),
50
            'DIP5': copy.deepcopy(rotation_dict)}
51
52
        self.checkbuttons = {'check_finger': {'status': (False, True, False, False, False),
53
                                              'labels': ('Thumb', 'Index', 'Middle', 'Ring', 'Pinky'),
54
                                              'buttons': None},
55
                             'check_joint': {'status': (False, True, False, False),
56
                                             'labels': ('CMC', 'MCP', 'PIP', 'DIP'),
57
                                             'buttons': None},
58
                             'check_rotation': {'status': (True, False, False),
59
                                                'labels': (FLEXION, ABDUCTION, DEVIATION),
60
                                                'buttons': None},
61
                             'check_data': {'status': (True, True),
62
                                            'labels': (INTERPOLATION, LEAPMOTION),
63
                                            'buttons': None}}
64
65
    def plot(self):
66
        """Call this method to initialize and open the plot"""
67
        self.plot_setup()
68
        self.generate_lines_interpolation()
69
        self.update_plot()
70
        plt.show()
71
72
    def plot_setup(self):
73
        """create the plot, add checkbuttons, legend, title, labels"""
74
        self.fig, self.ax = plt.subplots()
75
        plt.subplots_adjust(left=0.2)
76
        plt.grid()
77
        plt.title('comparison of angles')
78
        plt.xlabel('frame')
79
        plt.ylabel('angle in degree')
80
        custom_legend = [Line2D([0], [0], linestyle='None', color='k', marker='$A$', lw=2, label=INTERPOLATION),
81
                         Line2D([0], [0], linestyle='None', color='tab:gray', marker='$L$', lw=2, label=LEAPMOTION),
82
                         Line2D([0], [0], linestyle='None', color=COLORS[0], marker='$1$', lw=2, label='Thumb'),
83
                         Line2D([0], [0], linestyle='None', color=COLORS[2], marker='$2$', lw=2, label='Index'),
84
                         Line2D([0], [0], linestyle='None', color=COLORS[4], marker='$3$', lw=2, label='Middle'),
85
                         Line2D([0], [0], linestyle='None', color=COLORS[6], marker='$4$', lw=2, label='Ring'),
86
                         Line2D([0], [0], linestyle='None', color=COLORS[8], marker='$5$', lw=2, label='Pinky'),
87
                         Line2D([0], [0], linestyle=LINE_STYLES[0], color='k', lw=2, label='CMC'),
88
                         Line2D([0], [0], linestyle=LINE_STYLES[1], color='k', lw=2, label='MCP'),
89
                         Line2D([0], [0], linestyle=LINE_STYLES[2], color='k', lw=2, label='PIP'),
90
                         Line2D([0], [0], linestyle=LINE_STYLES[3], color='k', lw=2, label='DIP'),
91
                         Line2D([0], [0], linestyle='None', color='k', marker='$F$', lw=2, label=FLEXION),
92
                         Line2D([0], [0], linestyle='None', color='k', marker='$A$', lw=2, label=ABDUCTION),
93
                         Line2D([0], [0], linestyle='None', color='k', marker='$D$', lw=2, label=DEVIATION)]
94
        leg = self.ax.legend(handles=custom_legend)
95
        # change the font colors to match the line colors:
96
        for line, text in zip(leg.get_lines(), leg.get_texts()):
97
            text.set_color(line.get_color())
98
99
        """checkboxes"""
100
        rax_data = plt.axes([0.05, 0.7, 0.1, 0.15])
101
        self.checkbuttons['check_data']['buttons'] = CheckButtons(
102
            rax_data, self.checkbuttons['check_data']['labels'], self.checkbuttons['check_data']['status'])
103
104
        rax_finger = plt.axes([0.05, 0.5, 0.1, 0.15])
105
        self.checkbuttons['check_finger']['buttons'] = CheckButtons(
106
            rax_finger, self.checkbuttons['check_finger']['labels'], self.checkbuttons['check_finger']['status'])
107
108
        rax_joint = plt.axes([0.05, 0.3, 0.1, 0.15])
109
        self.checkbuttons['check_joint']['buttons'] = CheckButtons(
110
            rax_joint, self.checkbuttons['check_joint']['labels'], self.checkbuttons['check_joint']['status'])
111
112
        rax_rotation = plt.axes([0.05, 0.1, 0.1, 0.15])
113
        self.checkbuttons['check_rotation']['buttons'] = CheckButtons(
114
            rax_rotation, self.checkbuttons['check_rotation']['labels'], self.checkbuttons['check_rotation']['status'])
115
116
        # activate on_clicked function for all button groups
117
        for button_group, button_values in self.checkbuttons.items():
118
            button_values['buttons'].on_clicked(self.change_label)
119
120
    def save_status(self):
121
        """fetch status from all checkbuttons and save"""
122
        for b_group, b_values in self.checkbuttons.items():
123
            self.checkbuttons[b_group]['status'] = b_values['buttons'].get_status()
124
125
    def change_label(self, label):
126
        self.save_status()
127
        self.update_plot()
128
        plt.draw()
129
130
    def generate_lines_interpolation(self):
131
        rotation_index = {'Flexion': 0,
132
                          'Abduction': 1,
133
                          'Deviation': 2}
134
        markers_rotation = ['$F{}{}$', '$A{}{}$', '$D{}{}$']
135
136
        def joint_line_style(joint_name):
137
            if 'CMC' in joint_name:
138
                return LINE_STYLES[0]
139
            if 'MCP' in joint_name:
140
                return LINE_STYLES[1]
141
            if 'PIP' in joint_name:
142
                return LINE_STYLES[2]
143
            if 'DIP' in joint_name:
144
                return LINE_STYLES[3]
145
146
        # save plots into joint_mapping
147
        for joint_name, joint_values in self.joint_mapping.items():
148
            line_style = joint_line_style(joint_name)
149
            _, finger_number = AnybodyResults.split_joint(joint_name)
150
            for rotation_channel, data_source in joint_values.items():
151
                index = rotation_index[rotation_channel]
152
                offset = 0
153
                for data_name in data_source:
154
                    x, y = self.get_plot_data(joint_name, index, data_name)
155
                    self.joint_mapping[joint_name][rotation_channel][data_name] = self.ax.plot(
156
                        x, y,
157
                        visible=False,
158
                        lw=1.5,
159
                        marker=markers_rotation[index].format(finger_number, data_name[0]),
160
                        markersize=14,
161
                        markevery=(0.03 * index + finger_number / 50 + 0.02 * offset, 0.1),
162
                        linestyle=line_style,
163
                        color=COLORS[2*(finger_number - 1) + offset]
164
                    )[0]
165
                    offset += 1
166
167
    def update_plot(self):
168
        for joint_name, joint_values in self.joint_mapping.items():
169
            for rotation, data_source in joint_values.items():
170
                for data_name, line in data_source.items():
171
                    joint_type, finger_index = AnybodyResults.split_joint(joint_name)
172
                    plot_status = \
173
                        self.checkbuttons['check_finger']['status'][finger_index - 1] and \
174
                        self.checkbuttons['check_joint']['status'][
175
                            self.checkbuttons['check_joint']['labels'].index(joint_type)] and \
176
                        self.checkbuttons['check_rotation']['status'][
177
                            self.checkbuttons['check_rotation']['labels'].index(rotation)] and \
178
                        self.checkbuttons['check_data']['status'][
179
                            self.checkbuttons['check_data']['labels'].index(data_name)]
180
                    line.set_visible(plot_status)
181
182
    @staticmethod
183
    def split_joint(joint_name):
184
        joint_split = re.split(r'(\d)', joint_name)
185
        return joint_split[0], int(joint_split[1])
186
187
    def get_plot_data(self, joint_name, index, data_name):
188
        frames_interpolation = self.output['Main.Study.nStep']
189
        t_interpolation = np.arange(1, frames_interpolation + 1, 1)
190
        if data_name == INTERPOLATION:
191
            output_path = 'Main.Study.Output.JointAngleOutputs.{}'
192
            return \
193
                t_interpolation, \
194
                np.rad2deg(self.output[output_path.format(joint_name)][0][:, index])
195
196
        if data_name == LEAPMOTION:
197
            output_path = 'Main.HumanModel.Mannequin.Posture.Right.Finger{}.{}'
198
199
            frames_leap_motion = len(self.output[output_path.format(2, 'MCP2')][0][:, 0])
200
            t_leap_motion = np.arange(1, frames_interpolation + 1, frames_interpolation / frames_leap_motion)
201
202
            _, finger_number = AnybodyResults.split_joint(joint_name)
203
            return \
204
                t_leap_motion, \
205
                self.output[output_path.format(finger_number, joint_name)][0][:, index]
206
207
        raise ValueError('Unknown data source. Available sources: {}'.format([INTERPOLATION, LEAPMOTION]))