├── .gitignore
├── LICENSE
├── MANIFEST.in
├── README.md
├── _config.yml
├── assets
├── foot_updown.png
├── footcontact_signal.png
├── mocap_player.png
└── viz_skel_2d.png
├── demos
├── PlayMocap.ipynb
├── Position.ipynb
└── data
│ └── AV_8Walk_Meredith_HVHA_Rep1.bvh
├── pymo
├── __init__.py
├── data.py
├── features.py
├── mocapplayer
│ ├── data-template.js
│ ├── js
│ │ └── skeletonFactory.js
│ ├── libs
│ │ ├── jquery.min.js
│ │ ├── math.min.js
│ │ ├── mocapjs.js
│ │ ├── pace.min.js
│ │ ├── papaparse.min.js
│ │ └── threejs
│ │ │ ├── Detector.js
│ │ │ ├── OrbitControls.js
│ │ │ ├── dat.gui.min.js
│ │ │ └── three.min.js
│ ├── playBuffer.html
│ ├── playURL.html
│ └── styles
│ │ └── pace.css
├── parsers.py
├── preprocessing.py
├── rotation_tools.py
├── rotation_tools.py!
├── viz_tools.py
└── writers.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | pymo/mocapplayer/data.js
2 | .vscode/
3 | scraps/*
4 | others/*
5 | bvhs/
6 |
7 | pymo/mocapplayer/data.js
8 |
9 | *.ckpt*
10 | *.pkl
11 | *.swp
12 |
13 | #*.png
14 | #*.jpg
15 |
16 | # Created by https://www.gitignore.io/api/python
17 |
18 | ### Python ###
19 | # Byte-compiled / optimized / DLL files
20 | __pycache__/
21 | *.py[cod]
22 | *$py.class
23 |
24 | # C extensions
25 | *.so
26 |
27 | # Distribution / packaging
28 | .Python
29 | env/
30 | build/
31 | develop-eggs/
32 | dist/
33 | downloads/
34 | eggs/
35 | .eggs/
36 | lib/
37 | lib64/
38 | parts/
39 | sdist/
40 | var/
41 | wheels/
42 | *.egg-info/
43 | .installed.cfg
44 | *.egg
45 |
46 | # PyInstaller
47 | # Usually these files are written by a python script from a template
48 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
49 | *.manifest
50 | *.spec
51 |
52 | # Installer logs
53 | pip-log.txt
54 | pip-delete-this-directory.txt
55 |
56 | # Unit test / coverage reports
57 | htmlcov/
58 | .tox/
59 | .coverage
60 | .coverage.*
61 | .cache
62 | nosetests.xml
63 | coverage.xml
64 | *,cover
65 | .hypothesis/
66 |
67 | # Translations
68 | *.mo
69 | *.pot
70 |
71 | # Django stuff:
72 | *.log
73 | local_settings.py
74 |
75 | # Flask stuff:
76 | instance/
77 | .webassets-cache
78 |
79 | # Scrapy stuff:
80 | .scrapy
81 |
82 | # Sphinx documentation
83 | docs/_build/
84 |
85 | # PyBuilder
86 | target/
87 |
88 | # Jupyter Notebook
89 | .ipynb_checkpoints
90 |
91 | # pyenv
92 | .python-version
93 |
94 | # celery beat schedule file
95 | celerybeat-schedule
96 |
97 | # SageMath parsed files
98 | *.sage.py
99 |
100 | # dotenv
101 | .env
102 |
103 | # virtualenv
104 | .venv
105 | venv/
106 | ENV/
107 |
108 | # Spyder project settings
109 | .spyderproject
110 | .spyproject
111 |
112 | # Rope project settings
113 | .ropeproject
114 |
115 | # mkdocs documentation
116 | /site
117 |
118 | # End of https://www.gitignore.io/api/python
119 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016-2017 Omid Alemi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include pymo/mocapplayer *
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PyMO
2 | A library for using motion capture data for machine learning
3 |
4 | **This library is currently highly experimental and everything is subject to change :)**
5 |
6 |
7 | ## Roadmap
8 | * Mocap Data Parsers and Writers
9 | * Common mocap pre-processing algorithms
10 | * Feature extraction library
11 | * Visualization tools
12 |
13 | ## Current Features
14 | * [Read BVH Files](#read-bvh-files)
15 | * Write BVH Files
16 | * Pre-processing pipelines
17 | * [Supporting `scikit-learn` API](#scikit-learn-pipeline-api)
18 | * Convert data representations
19 | * [Euler angles to positions](#convert-to-positions)
20 | * Euler angles to exponential maps
21 | * Exponential maps to euler angles
22 | * Body-oriented global translation and rotation calculation with inverse tranform
23 | * Root-centric position normalizer with inverse tranform
24 | * Standard scaler
25 | * Joint selectors
26 | * Visualization tools
27 | * [Skeleton hierarchy](#get-skeleton-info)
28 | * [2D frame visualization](#visualize-a-single-2d-frame)
29 | * [3D webgl-based animation](#animate-in-3d-inside-a-jupyter-notebook)
30 | * Annotations
31 | * Foot/ground contact detector
32 |
33 |
34 | ### Read BVH Files
35 |
36 | ```python
37 | from pymo.parsers import BVHParser
38 |
39 | parser = BVHParser()
40 |
41 | parsed_data = parser.parse('demos/data/AV_8Walk_Meredith_HVHA_Rep1.bvh')
42 | ```
43 |
44 | ### Get Skeleton Info
45 |
46 | ```python
47 | from pymo.viz_tools import *
48 |
49 | print_skel(parsed_data)
50 | ```
51 | Will print the skeleton hierarchy:
52 | ```
53 | - Hips (None)
54 | | | - RightUpLeg (Hips)
55 | | | - RightLeg (RightUpLeg)
56 | | | - RightFoot (RightLeg)
57 | | | - RightToeBase (RightFoot)
58 | | | - RightToeBase_Nub (RightToeBase)
59 | | - LeftUpLeg (Hips)
60 | | - LeftLeg (LeftUpLeg)
61 | | - LeftFoot (LeftLeg)
62 | | - LeftToeBase (LeftFoot)
63 | | - LeftToeBase_Nub (LeftToeBase)
64 | - Spine (Hips)
65 | | | - RightShoulder (Spine)
66 | | | - RightArm (RightShoulder)
67 | | | - RightForeArm (RightArm)
68 | | | - RightHand (RightForeArm)
69 | | | | - RightHand_End (RightHand)
70 | | | | - RightHand_End_Nub (RightHand_End)
71 | | | - RightHandThumb1 (RightHand)
72 | | | - RightHandThumb1_Nub (RightHandThumb1)
73 | | - LeftShoulder (Spine)
74 | | - LeftArm (LeftShoulder)
75 | | - LeftForeArm (LeftArm)
76 | | - LeftHand (LeftForeArm)
77 | | | - LeftHand_End (LeftHand)
78 | | | - LeftHand_End_Nub (LeftHand_End)
79 | | - LeftHandThumb1 (LeftHand)
80 | | - LeftHandThumb1_Nub (LeftHandThumb1)
81 | - Head (Spine)
82 | - Head_Nub (Head)
83 | ```
84 |
85 |
86 | ### scikit-learn Pipeline API
87 |
88 | ```python
89 |
90 | from pymo.preprocessing import *
91 | from sklearn.pipeline import Pipeline
92 |
93 | data_pipe = Pipeline([
94 | ('param', MocapParameterizer('position')),
95 | ('rcpn', RootCentricPositionNormalizer()),
96 | ('delta', RootTransformer('abdolute_translation_deltas')),
97 | ('const', ConstantsRemover()),
98 | ('np', Numpyfier()),
99 | ('down', DownSampler(2)),
100 | ('stdscale', ListStandardScaler())
101 | ])
102 |
103 | piped_data = data_pipe.fit_transform([parsed_data])
104 | ```
105 |
106 | ### Convert to Positions
107 |
108 | ```python
109 | mp = MocapParameterizer('position')
110 |
111 | positions = mp.fit_transform([parsed_data])
112 | ```
113 |
114 | ### Visualize a single 2D Frame
115 |
116 | ```python
117 | draw_stickfigure(positions[0], frame=10)
118 | ```
119 |
120 | 
121 |
122 | ### Animate in 3D (inside a Jupyter Notebook)
123 |
124 | ```python
125 | nb_play_mocap(positions[0], 'pos',
126 | scale=2, camera_z=800, frame_time=1/120,
127 | base_url='pymo/mocapplayer/playBuffer.html')
128 | ```
129 |
130 | 
131 |
132 |
133 | ### Foot/Ground Contact Detector
134 | ```python
135 | from pymo.features import *
136 |
137 | plot_foot_up_down(positions[0], 'RightFoot_Yposition')
138 | ```
139 |
140 | 
141 |
142 | ```python
143 | signal = create_foot_contact_signal(positions[0], 'RightFoot_Yposition')
144 | plt.figure(figsize=(12,5))
145 | plt.plot(signal, 'r')
146 | plt.plot(positions[0].values['RightFoot_Yposition'].values, 'g')
147 | ```
148 |
149 | 
150 |
151 | ## Feedback, Bugs, and Questions
152 | For any questions, feedback, and bug reports, please use the [Github Issues](https://github.com/omimo/PyMO/issues).
153 |
154 | ## Credits
155 | Created by [Omid Alemi](https://omid.al/projects/)
156 |
157 |
158 | ## License
159 | This code is available under the [MIT license](http://opensource.org/licenses/MIT).
160 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | exclude: pymo, notebooks
--------------------------------------------------------------------------------
/assets/foot_updown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omimo/PyMO/ba24f5f8d8911173f41e2ea1a367fa7955a6da9f/assets/foot_updown.png
--------------------------------------------------------------------------------
/assets/footcontact_signal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omimo/PyMO/ba24f5f8d8911173f41e2ea1a367fa7955a6da9f/assets/footcontact_signal.png
--------------------------------------------------------------------------------
/assets/mocap_player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omimo/PyMO/ba24f5f8d8911173f41e2ea1a367fa7955a6da9f/assets/mocap_player.png
--------------------------------------------------------------------------------
/assets/viz_skel_2d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omimo/PyMO/ba24f5f8d8911173f41e2ea1a367fa7955a6da9f/assets/viz_skel_2d.png
--------------------------------------------------------------------------------
/demos/PlayMocap.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {
7 | "collapsed": false
8 | },
9 | "outputs": [],
10 | "source": [
11 | "%matplotlib inline\n",
12 | "%load_ext autoreload\n",
13 | "# %load_ext toc2\n",
14 | "%autoreload 2\n",
15 | "\n",
16 | "import matplotlib\n",
17 | "import matplotlib.pyplot as plt\n",
18 | "from mpl_toolkits.mplot3d import Axes3D\n",
19 | "\n",
20 | "import tensorflow as tf\n",
21 | "import numpy as np\n",
22 | "import pandas as pd\n",
23 | "from sklearn.pipeline import Pipeline\n",
24 | "from sklearn.preprocessing import StandardScaler\n",
25 | "import pickle\n",
26 | "import itertools\n",
27 | "from datetime import datetime\n",
28 | "\n",
29 | "\n",
30 | "import glob\n",
31 | "import os\n",
32 | "import sys\n",
33 | "module_path = os.path.abspath(os.path.join('..'))\n",
34 | "if module_path not in sys.path:\n",
35 | " sys.path.append(module_path)\n",
36 | " \n",
37 | "import IPython\n",
38 | "\n",
39 | "from pymo.parsers import BVHParser\n",
40 | "from pymo.preprocessing import *\n",
41 | "from pymo.viz_tools import *\n",
42 | "from pymo.writers import *"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": 8,
48 | "metadata": {
49 | "collapsed": false
50 | },
51 | "outputs": [],
52 | "source": [
53 | "p = BVHParser()\n",
54 | "\n",
55 | "data_all = [p.parse('./data/AV_8Walk_Meredith_HVHA_Rep1.bvh')]"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": 9,
61 | "metadata": {
62 | "collapsed": false
63 | },
64 | "outputs": [],
65 | "source": [
66 | "BVH2Pos = MocapParameterizer('position')\n",
67 | "data_pos = BVH2Pos.fit_transform(data_all)"
68 | ]
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": 33,
73 | "metadata": {
74 | "collapsed": false,
75 | "scrolled": false
76 | },
77 | "outputs": [
78 | {
79 | "data": {
80 | "text/html": [
81 | "New Window"
82 | ],
83 | "text/plain": [
84 | ""
85 | ]
86 | },
87 | "execution_count": 33,
88 | "metadata": {},
89 | "output_type": "execute_result"
90 | }
91 | ],
92 | "source": [
93 | "nb_play_mocap(data_pos[0], 'pos', \n",
94 | " scale=2, camera_z=800, frame_time=1/120, \n",
95 | " base_url='../pymo/mocapplayer/playBuffer.html')"
96 | ]
97 | },
98 | {
99 | "cell_type": "code",
100 | "execution_count": null,
101 | "metadata": {
102 | "collapsed": true
103 | },
104 | "outputs": [],
105 | "source": []
106 | }
107 | ],
108 | "metadata": {
109 | "kernelspec": {
110 | "display_name": "Python 3",
111 | "language": "python",
112 | "name": "python3"
113 | },
114 | "language_info": {
115 | "codemirror_mode": {
116 | "name": "ipython",
117 | "version": 3
118 | },
119 | "file_extension": ".py",
120 | "mimetype": "text/x-python",
121 | "name": "python",
122 | "nbconvert_exporter": "python",
123 | "pygments_lexer": "ipython3",
124 | "version": "3.5.2"
125 | }
126 | },
127 | "nbformat": 4,
128 | "nbformat_minor": 2
129 | }
130 |
--------------------------------------------------------------------------------
/pymo/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omimo/PyMO/ba24f5f8d8911173f41e2ea1a367fa7955a6da9f/pymo/__init__.py
--------------------------------------------------------------------------------
/pymo/data.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | class Joint():
4 | def __init__(self, name, parent=None, children=None):
5 | self.name = name
6 | self.parent = parent
7 | self.children = children
8 |
9 | class MocapData():
10 | def __init__(self):
11 | self.skeleton = {}
12 | self.values = None
13 | self.channel_names = []
14 | self.framerate = 0.0
15 | self.root_name = ''
16 |
17 | def traverse(self, j=None):
18 | stack = [self.root_name]
19 | while stack:
20 | joint = stack.pop()
21 | yield joint
22 | for c in self.skeleton[joint]['children']:
23 | stack.append(c)
24 |
25 | def clone(self):
26 | import copy
27 | new_data = MocapData()
28 | new_data.skeleton = copy.copy(self.skeleton)
29 | new_data.values = copy.copy(self.values)
30 | new_data.channel_names = copy.copy(self.channel_names)
31 | new_data.root_name = copy.copy(self.root_name)
32 | new_data.framerate = copy.copy(self.framerate)
33 | return new_data
34 |
35 | def get_all_channels(self):
36 | '''Returns all of the channels parsed from the file as a 2D numpy array'''
37 |
38 | frames = [f[1] for f in self.values]
39 | return np.asarray([[channel[2] for channel in frame] for frame in frames])
40 |
41 | def get_skeleton_tree(self):
42 | tree = []
43 | root_key = [j for j in self.skeleton if self.skeleton[j]['parent']==None][0]
44 |
45 | root_joint = Joint(root_key)
46 |
47 | def get_empty_channels(self):
48 | #TODO
49 | pass
50 |
51 | def get_constant_channels(self):
52 | #TODO
53 | pass
54 |
--------------------------------------------------------------------------------
/pymo/features.py:
--------------------------------------------------------------------------------
1 | '''
2 | A set of mocap feature extraction functions
3 |
4 | Created by Omid Alemi | Nov 17 2017
5 |
6 | '''
7 | import numpy as np
8 | import pandas as pd
9 | import peakutils
10 | import matplotlib.pyplot as plt
11 |
12 | def get_foot_contact_idxs(signal, t=0.02, min_dist=120):
13 | up_idxs = peakutils.indexes(signal, thres=t/max(signal), min_dist=min_dist)
14 | down_idxs = peakutils.indexes(-signal, thres=t/min(signal), min_dist=min_dist)
15 |
16 | return [up_idxs, down_idxs]
17 |
18 |
19 | def create_foot_contact_signal(mocap_track, col_name, start=1, t=0.02, min_dist=120):
20 | signal = mocap_track.values[col_name].values
21 | idxs = get_foot_contact_idxs(signal, t, min_dist)
22 |
23 | step_signal = []
24 |
25 | c = start
26 | for f in range(len(signal)):
27 | if f in idxs[1]:
28 | c = 0
29 | elif f in idxs[0]:
30 | c = 1
31 |
32 | step_signal.append(c)
33 |
34 | return step_signal
35 |
36 | def plot_foot_up_down(mocap_track, col_name, t=0.02, min_dist=120):
37 |
38 | signal = mocap_track.values[col_name].values
39 | idxs = get_foot_contact_idxs(signal, t, min_dist)
40 |
41 | plt.plot(mocap_track.values.index, signal)
42 | plt.plot(mocap_track.values.index[idxs[0]], signal[idxs[0]], 'ro')
43 | plt.plot(mocap_track.values.index[idxs[1]], signal[idxs[1]], 'go')
44 |
--------------------------------------------------------------------------------
/pymo/mocapplayer/data-template.js:
--------------------------------------------------------------------------------
1 | var dataBuffer = `$$DATA$$`;
2 |
3 | start(dataBuffer);
--------------------------------------------------------------------------------
/pymo/mocapplayer/js/skeletonFactory.js:
--------------------------------------------------------------------------------
1 | bm_v = new THREE.MeshPhongMaterial({
2 | color: 0x08519c,
3 | emissive: 0x08306b,
4 | specular: 0x08519c,
5 | shininess: 10,
6 | side: THREE.DoubleSide
7 | });
8 |
9 | jm_v = new THREE.MeshPhongMaterial({
10 | color: 0x08306b,
11 | emissive: 0x000000,
12 | specular: 0x111111,
13 | shininess: 90,
14 | side: THREE.DoubleSide
15 | });
16 |
17 | bm_a = new THREE.MeshPhongMaterial({
18 | color: 0x980043,
19 | emissive: 0x67001f,
20 | specular: 0x6a51a3,
21 | shininess: 10,
22 | side: THREE.DoubleSide
23 | });
24 |
25 | jm_a = new THREE.MeshPhongMaterial({
26 | color: 0x67001f,
27 | emissive: 0x000000,
28 | specular: 0x111111,
29 | shininess: 90,
30 | side: THREE.DoubleSide
31 | });
32 |
33 | bm_b = new THREE.MeshPhongMaterial({
34 | color: 0x3f007d,
35 | emissive: 0x3f007d,
36 | specular: 0x807dba,
37 | shininess: 2,
38 | side: THREE.DoubleSide
39 | });
40 |
41 | jm_b = new THREE.MeshPhongMaterial({
42 | color: 0x3f007d,
43 | emissive: 0x000000,
44 | specular: 0x807dba,
45 | shininess: 90,
46 | side: THREE.DoubleSide
47 | });
48 |
49 | //------------------
50 |
51 |
52 | jointmaterial = new THREE.MeshLambertMaterial({
53 | color: 0xc57206,
54 | emissive: 0x271c18,
55 | side: THREE.DoubleSide,
56 | // shading: THREE.FlatShading,
57 | wireframe: false,
58 | shininess: 90,
59 | });
60 |
61 | bonematerial = new THREE.MeshPhongMaterial({
62 | color: 0xbd9a6d,
63 | emissive: 0x271c18,
64 | side: THREE.DoubleSide,
65 | // shading: THREE.FlatShading,
66 | wireframe: false
67 | });
68 |
69 | jointmaterial2 = new THREE.MeshPhongMaterial({
70 | color: 0x1562a2,
71 | emissive: 0x000000,
72 | specular: 0x111111,
73 | shininess: 30,
74 | side: THREE.DoubleSide
75 | });
76 |
77 | bonematerial2 = new THREE.MeshPhongMaterial({
78 | color: 0x552211,
79 | emissive: 0x882211,
80 | // emissive: 0x000000,
81 | specular: 0x111111,
82 | shininess: 30,
83 | side: THREE.DoubleSide
84 | });
85 |
86 | bonematerial3 = new THREE.MeshPhongMaterial({
87 | color: 0x176793,
88 | emissive: 0x000000,
89 | specular: 0x111111,
90 | shininess: 90,
91 | side: THREE.DoubleSide
92 | });
93 |
94 |
95 |
96 | jointmaterial4 = new THREE.MeshPhongMaterial({
97 | color: 0xFF8A00,
98 | emissive: 0x000000,
99 | specular: 0x111111,
100 | shininess: 90,
101 | side: THREE.DoubleSide
102 | });
103 |
104 |
105 | bonematerial4 = new THREE.MeshPhongMaterial({
106 | color: 0x53633D,
107 | emissive: 0x000000,
108 | specular: 0xFFC450,
109 | shininess: 90,
110 | side: THREE.DoubleSide
111 | });
112 |
113 |
114 |
115 | bonematerial44 = new THREE.MeshPhongMaterial({
116 | color: 0x582A72,
117 | emissive: 0x000000,
118 | specular: 0xFFC450,
119 | shininess: 90,
120 | side: THREE.DoubleSide
121 | });
122 |
123 | jointmaterial5 = new THREE.MeshPhongMaterial({
124 | color: 0xAA5533,
125 | emissive: 0x000000,
126 | specular: 0x111111,
127 | shininess: 30,
128 | side: THREE.DoubleSide
129 | });
130 |
131 | bonematerial5 = new THREE.MeshPhongMaterial({
132 | color: 0x552211,
133 | emissive: 0x772211,
134 | specular: 0x111111,
135 | shininess: 30,
136 | side: THREE.DoubleSide
137 | });
138 |
139 |
140 | markermaterial = new THREE.MeshPhongMaterial({
141 | color: 0xc57206,
142 | emissive: 0x271c18,
143 | side: THREE.DoubleSide,
144 | // shading: THREE.FlatShading,
145 | wireframe: false,
146 | shininess: 20,
147 | });
148 |
149 | markermaterial2 = new THREE.MeshPhongMaterial({
150 | color: 0x1562a2,
151 | emissive: 0x271c18,
152 | side: THREE.DoubleSide,
153 | // shading: THREE.FlatShading,
154 | wireframe: false,
155 | shininess: 20,
156 | });
157 |
158 | markermaterial3 = new THREE.MeshPhongMaterial({
159 | color: 0x555555,
160 | emissive: 0x999999,
161 | side: THREE.DoubleSide,
162 | // shading: THREE.FlatShading,
163 | wireframe: false,
164 | shininess: 20,
165 | });
166 |
167 |
168 | var makeMarkerGeometry_Sphere10 = function(markerName, scale) {
169 | return new THREE.SphereGeometry(10, 60, 60);
170 | };
171 |
172 | var makeMarkerGeometry_Sphere3 = function(markerName, scale) {
173 | return new THREE.SphereGeometry(3, 60, 60);
174 | };
175 |
176 | var makeMarkerGeometry_SphereX = function(markerName, scale) {
177 | return new THREE.SphereGeometry(5, 60, 60);
178 | };
179 |
180 | var makeJointGeometry_SphereX = function(X) {
181 | return function(jointName, scale) {
182 | return new THREE.SphereGeometry(X, 60, 60);
183 | };
184 | };
185 |
186 |
187 | var makeJointGeometry_Sphere1 = function(jointName, scale) {
188 | return new THREE.SphereGeometry(2 / scale, 60, 60);
189 | };
190 |
191 | var makeJointGeometry_Sphere2 = function(jointName, scale) {
192 | return new THREE.SphereGeometry(1 / scale, 60, 60);
193 | };
194 |
195 | var makeJointGeometry_Dode = function(jointName, scale) {
196 | return new THREE.DodecahedronGeometry(1 / scale, 0);
197 | };
198 |
199 | var makeBoneGeometry_Cylinder1 = function(joint1Name, joint2Name, length, scale) {
200 | return new THREE.CylinderGeometry(1.5 / scale, 0.7 / scale, length, 40);
201 | };
202 |
203 | var makeBoneGeometry_Cylinder2 = function(joint1Name, joint2Name, length, scale) {
204 | // if (joint1Name.includes("LeftHip"))
205 | // length = 400;
206 | return new THREE.CylinderGeometry(1.5 / scale, 0.2 / scale, length, 40);
207 | };
208 |
209 | var makeBoneGeometry_Cylinder3 = function(joint1Name, joint2Name, length, scale) {
210 | var c1 = new THREE.CylinderGeometry(1.5 / scale, 0.2 / scale, length / 1, 20);
211 | var c2 = new THREE.CylinderGeometry(0.2 / scale, 1.5 / scale, length / 1, 40);
212 |
213 | var material = new THREE.MeshPhongMaterial({
214 | color: 0xF7FE2E
215 | });
216 | var mmesh = new THREE.Mesh(c1, material);
217 | mmesh.updateMatrix();
218 | c2.merge(mmesh.geometry, mmesh.matrix);
219 | return c2;
220 | };
221 |
222 | var makeBoneGeometry_Box1 = function(joint1Name, joint2Name, length, scale) {
223 | return new THREE.BoxGeometry(1 / scale, length, 1 / scale, 40);
224 | };
225 |
226 |
227 | var makeJointGeometry_Empty = function(jointName, scale) {
228 | return new THREE.SphereGeometry(0.001, 60, 60);
229 | };
230 |
231 | var makeBoneGeometry_Empty = function(joint1Name, joint2Name, length, scale) {
232 | return new THREE.CylinderGeometry(0.001, 0.001, 0.001, 40);
233 | };
234 |
--------------------------------------------------------------------------------
/pymo/mocapplayer/libs/mocapjs.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 Omid Alemi
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | *
24 | */
25 | /******/ (function(modules) { // webpackBootstrap
26 | /******/ // The module cache
27 | /******/ var installedModules = {};
28 | /******/
29 | /******/ // The require function
30 | /******/ function __webpack_require__(moduleId) {
31 | /******/
32 | /******/ // Check if module is in cache
33 | /******/ if(installedModules[moduleId])
34 | /******/ return installedModules[moduleId].exports;
35 | /******/
36 | /******/ // Create a new module (and put it into the cache)
37 | /******/ var module = installedModules[moduleId] = {
38 | /******/ exports: {},
39 | /******/ id: moduleId,
40 | /******/ loaded: false
41 | /******/ };
42 | /******/
43 | /******/ // Execute the module function
44 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
45 | /******/
46 | /******/ // Flag the module as loaded
47 | /******/ module.loaded = true;
48 | /******/
49 | /******/ // Return the exports of the module
50 | /******/ return module.exports;
51 | /******/ }
52 | /******/
53 | /******/
54 | /******/ // expose the modules object (__webpack_modules__)
55 | /******/ __webpack_require__.m = modules;
56 | /******/
57 | /******/ // expose the module cache
58 | /******/ __webpack_require__.c = installedModules;
59 | /******/
60 | /******/ // __webpack_public_path__
61 | /******/ __webpack_require__.p = "";
62 | /******/
63 | /******/ // Load entry module and return exports
64 | /******/ return __webpack_require__(0);
65 | /******/ })
66 | /************************************************************************/
67 | /******/ ([
68 | /* 0 */
69 | /***/ function(module, exports, __webpack_require__) {
70 |
71 | BVHCharacter = __webpack_require__(1);
72 | C3DCharacter = __webpack_require__(5);
73 | MocapParsers = __webpack_require__(2);
74 |
75 | /***/ },
76 | /* 1 */
77 | /***/ function(module, exports, __webpack_require__) {
78 |
79 | var parsers = __webpack_require__(2);
80 |
81 | var BVHCharacter = BVHCharacter || {};
82 |
83 |
84 | BVHCharacter = function(n, jm, bm, jg, bg) {
85 | this.name = n;
86 |
87 | this.jointMaterial = jm;
88 | this.boneMaterial = bm;
89 | this.makeJointGeometryFCN = jg;
90 | this.makeBoneGeometryFCN = bg;
91 |
92 | this.bvh = [];
93 | this.skeleton = new THREE.Group();
94 |
95 | this.skelScale = 1;
96 | this.jointMeshes = [];
97 | this.boneMeshes = [];
98 | this.rootMeshes = [];
99 |
100 | this.originPosition = new THREE.Vector3(0, 0, 0);
101 |
102 | this.ready = false;
103 | this.frameTime = 1 / 30;
104 | this.frameCount = 0;
105 | this.animIndex = 0;
106 | this.animStartTimeRef = 0;
107 | this.animOffset = 0;
108 | this.playing = true;
109 |
110 | this.debug = true;
111 | this.useWorker = true;
112 |
113 | this.webSocket = [];
114 | this.streamProtocol = "BVHStream";
115 | this.keepStreamedFrames = true;
116 | this.isStreaming = false;
117 |
118 | var self = this;
119 |
120 | //
121 |
122 | this.log = function(m) {
123 | if (self.debug)
124 | console.log(self.name + ": " + m.toString());
125 | };
126 |
127 | this.loadFromURL = function(url, callback) {
128 | self.log("Loading the mocap file ...");
129 | //Pace.start();
130 | reader = new parsers.bvhParser(this.name + "READER");
131 | this.url = url;
132 | reader.load(url, self.createSkel, self.fillFrames);
133 |
134 | this.callb = callback;
135 | };
136 |
137 | this.fillFrames = function() {
138 | // self.log("Ready!");
139 | self.ready = true;
140 | self.playing = true;
141 |
142 | if (self.callb)
143 | self.callb();
144 | }
145 |
146 | this.createSkel = function(data) {
147 | self.bvh = data;
148 | self.frameCount = data.frameCount;
149 | self.frameTime = data.frameTime;
150 |
151 | self.log("Mocap file loaded.");
152 |
153 | self.log("Creating the WebGL Joints.");
154 | self.buildSkelJoints(self.bvh.getSkeleton(), 0);
155 |
156 | self.log("Creating the WebGL Bones.");
157 | self.buildSkelBones(self.jointMeshes[0]);
158 |
159 | self.skeleton.add(self.jointMeshes[0]);
160 | self.setSkeletonScale(self.skelScale);
161 | self.setSkelUp();
162 | };
163 |
164 |
165 | // Beginning of the Stream Code
166 | this.onHeaderReceived = function(data) {
167 | self.log("Loading the mocap header (skeleton) from the stream...");
168 | headerReader = new parsers.bvhStreamParser();
169 | headerReader.readHeader(data, self.createSkel);
170 |
171 | if (self.callb)
172 | self.callb();
173 |
174 | Pace.stop();
175 | }
176 |
177 | this.onDataChunckReceived = function(rawFrames) {
178 | var aa = [];
179 |
180 | for (f = 1; f < rawFrames.length; f++) {
181 | var parts = rawFrames[f].trim().split(" ");
182 | for (var j = 0; j < parts.length; j++)
183 | parts[j] = +parts[j];
184 | aa.push(parts);
185 | }
186 | diff = self.bvh.fillFrameArray(aa);
187 | self.frameCount = self.bvh.frameArray.length;
188 |
189 |
190 | if (!self.playing) {
191 | self.animStartTimeRef = Date.now();
192 | // self.animOffset -= rawFrames.length;
193 | }
194 | /*
195 | // else
196 | // self.animOffset = self.animIndex;
197 | if (diff > 0)
198 | self.animOffset -= rawFrames.length + 1;
199 | // self.animIndex -= rawFrames.length; //math.max(0,math.min(rawFrames.length, self.bvh.bufferSize));
200 | */
201 | self.fillFrames();
202 | Pace.stop();
203 | }
204 |
205 | this.loadFromStream = function(url, callback) {
206 | self.log("Connecting to the stream server...");
207 | self.isStreaming = true;
208 | this.callb = callback;
209 | self.webSocket = new WebSocket(url);
210 |
211 | self.webSocket.onerror = function(event) {
212 | self.log("Error connecting to the stream server " + event.origin);
213 | };
214 |
215 | self.webSocket.onopen = function(event) {
216 | self.log("Connected to the stream server " + event.origin);
217 | Pace.stop();
218 | };
219 |
220 | self.webSocket.onmessage = function(event) {
221 | // I'm not doing much of a type and content checking here. Let's just trust the sender for now!
222 | // Protocol for header:
223 | // $HEADER$
224 | // BVH...
225 | // Protocl for data chunk with id#:
226 | // $FRAMES$id#$
227 |
228 | var messageLines = event.data.split('\n');
229 |
230 | // self.log("Received somthing!");
231 | // self.log("The first line is : " + messageLines[0]);
232 |
233 | if (messageLines.length < 1)
234 | return;
235 |
236 | if (messageLines[0] == "$HEADER$") {
237 | self.onHeaderReceived(event.data);
238 |
239 | } else if (messageLines[0].startsWith("$FRAMES$")) {
240 | chunckID = parseInt(messageLines[0].split("$")[2]);
241 | self.onDataChunckReceived(messageLines, chunckID);
242 | }
243 | };
244 |
245 | };
246 |
247 | this.requestFrames = function(i) {
248 | self.webSocket.send("$GETFRAMES" + i + "$");
249 | }
250 |
251 | // End of the Stream Code
252 |
253 | this.setOriginPosition = function(x, y, z) {
254 | self.originPosition.set(x, y, z);
255 | };
256 |
257 | this.setSkeletonScale = function(s) {
258 | self.rootMeshes.forEach(function(c) {
259 | c.scale.set(s, s, s);
260 | });
261 | self.jointMeshes[0].scale.set(s, s, s);
262 | self.jointMeshes[0].position.multiplyScalar(s);
263 | };
264 |
265 | this.buildSkelJoints = function(joint, parent) {
266 | var jointMesh = new THREE.Mesh(self.makeJointGeometryFCN(joint.name, self.skelScale), self.jointMaterial);
267 | jointMesh.bvhIndex = joint.jointIndex;
268 | jointMesh.offsetVec = new THREE.Vector3(joint.offset[0], joint.offset[1], joint.offset[2]);
269 | jointMesh.name = joint.name;
270 | jointMesh.jointparent = parent;
271 | var a, b, c;
272 | if (!joint.isEndSite()) {
273 | a = joint.channelNames[joint.channelNames.length - 3][0];
274 | b = joint.channelNames[joint.channelNames.length - 2][0];
275 | c = joint.channelNames[joint.channelNames.length - 1][0];
276 | }
277 | jointMesh.rotOrder = a + b + c;
278 | self.jointMeshes.push(jointMesh);
279 |
280 | jointMesh.position.set(jointMesh.offsetVec.x, jointMesh.offsetVec.y, jointMesh.offsetVec.z);
281 |
282 | // var axisHelper = new THREE.AxisHelper( 10 / self.skelScale );
283 | // jointMesh.add( axisHelper );
284 |
285 |
286 | joint.children.forEach(function(child) {
287 | jointMesh.add(self.buildSkelJoints(child, 1));
288 | });
289 |
290 | return jointMesh;
291 | };
292 |
293 | this.buildSkelBones = function(rootJointMesh) {
294 | rootJointMesh.traverse(function(childJointMesh) {
295 | if (childJointMesh.parent !== null)
296 | {
297 | if (typeof childJointMesh.bvhIndex === "undefined")
298 | return;
299 | // move origin (.translate)
300 | // rotate
301 | // translate (offset + position)
302 | h = math.abs(childJointMesh.offsetVec.length());
303 | var bgeometry = self.makeBoneGeometryFCN(childJointMesh.parent.name, childJointMesh.name, h, self.skelScale);
304 |
305 | //BEGIN - Universal
306 | if (childJointMesh.offsetVec.y !== 0)
307 | // bgeometry.translate(0, Math.sign(childJointMesh.offsetVec.y) * h / 2, 0);
308 | bgeometry.translate(0, -h/2, 0);
309 | else
310 | bgeometry.translate(0, -h / 2, 0);
311 |
312 |
313 | dx = Math.atan2(childJointMesh.offsetVec.z,childJointMesh.offsetVec.y);
314 | dy = Math.atan2(childJointMesh.offsetVec.x,childJointMesh.offsetVec.z);
315 | dz = Math.atan2(childJointMesh.offsetVec.x,childJointMesh.offsetVec.y);
316 |
317 |
318 | osx = math.sign(childJointMesh.offsetVec.x) === 0 ? 0: math.sign(childJointMesh.offsetVec.x);
319 | osy = math.sign(childJointMesh.offsetVec.y) === 0 ? 0: math.sign(childJointMesh.offsetVec.y);
320 | osz = math.sign(childJointMesh.offsetVec.z) === 0 ? 0: math.sign(childJointMesh.offsetVec.z);
321 |
322 | osxy = math.sign(childJointMesh.offsetVec.x) === 0 ? 0: math.sign(childJointMesh.offsetVec.y);
323 | osyx = math.sign(childJointMesh.offsetVec.y) === 0 ? 0: math.sign(childJointMesh.offsetVec.x);
324 | osyz = math.sign(childJointMesh.offsetVec.y) === 0 ? 0: math.sign(childJointMesh.offsetVec.z);
325 | oszy = math.sign(childJointMesh.offsetVec.z) === 0 ? 0: math.sign(childJointMesh.offsetVec.y);
326 |
327 |
328 | if (osz <0)
329 | bgeometry.rotateZ(1*(math.pi-dz));
330 | else if (osz === 0)
331 | bgeometry.rotateZ(1*(math.pi-dz));
332 | // console.log();
333 | else if (osz > 0)
334 | bgeometry.rotateZ(1*(2*math.pi-dz));
335 |
336 |
337 | if (oszy >0)
338 | bgeometry.rotateX(-1 *(2*math.pi-dx));
339 | else if (childJointMesh.offsetVec.z === 0)
340 | // bgeometry.rotateX(-1*(math.pi-dx));
341 | console.log();
342 | else if (oszy < 0)
343 | bgeometry.rotateX(-1*(2*math.pi-dx));
344 |
345 | // bgeometry.rotateY(math.pi-dy);
346 |
347 | //END - Universal
348 |
349 | var boneMesh = new THREE.Mesh(bgeometry, self.boneMaterial);
350 |
351 | boneMesh.joint = childJointMesh.parent;
352 | boneMesh.name = childJointMesh.parent.name + " > " + childJointMesh.name;
353 |
354 | childJointMesh.parent.add(boneMesh);
355 | self.boneMeshes.push(boneMesh);
356 | }
357 | });
358 | };
359 |
360 | this.animFrame = function(frame) {
361 | var torad = Math.PI / 180;
362 |
363 | if (frame >= self.frameCount) {
364 | self.playing = false;
365 | return;
366 | }
367 |
368 |
369 | this.jointMeshes[0].traverse(function(joint) {
370 |
371 | if (typeof joint.bvhIndex === "undefined") {
372 | return;
373 | }
374 |
375 |
376 | var bj = self.bvh.jointArray[joint.bvhIndex];
377 | var offsetVec = joint.offsetVec;
378 |
379 | var thisEuler = [];
380 |
381 |
382 | thisEuler = new THREE.Euler(
383 | (bj.channels[frame][bj.rotationIndex.x] * torad),
384 | (bj.channels[frame][bj.rotationIndex.y] * torad),
385 | (bj.channels[frame][bj.rotationIndex.z] * torad), joint.rotOrder);
386 |
387 |
388 | joint.localRotMat = new THREE.Matrix4();
389 | joint.localRotMat.makeRotationFromEuler(thisEuler);
390 | joint.rotation.setFromRotationMatrix(joint.localRotMat);
391 |
392 | if (joint.jointparent !== 0) {
393 | // joint.position.set(offsetVec.x, offsetVec.y, offsetVec.z);
394 | } else { // root
395 | joint.position.set(
396 | bj.channels[frame][bj.positionIndex.x] * self.skelScale + self.originPosition.x,
397 | bj.channels[frame][bj.positionIndex.y] * self.skelScale + self.originPosition.y,
398 | bj.channels[frame][bj.positionIndex.z] * self.skelScale + self.originPosition.z);
399 | }
400 | });
401 |
402 | if (self.isStreaming) {
403 | self.bvh.consumeFrames(frame);
404 | self.frameCount = self.bvh.frameArray.length;
405 | // console.log(self.frameCount);
406 | if (self.frameCount <= 0)
407 | self.playing = false;
408 |
409 | self.animOffset = 0; // self.animOffset - frame;
410 | self.animStartTimeRef = Date.now();
411 | }
412 | };
413 |
414 | this.setSkelUp = function() {
415 | this.jointMeshes[0].traverse(function(joint) {
416 | if (typeof joint.bvhIndex === "undefined")
417 | return;
418 |
419 | var bj = self.bvh.jointArray[joint.bvhIndex];
420 |
421 | var offsetVec = joint.offsetVec;
422 | var torad = Math.PI / 180;
423 | var thisEuler = [];
424 |
425 | thisEuler = new THREE.Euler(0, 0, 0, joint.rotOrder);
426 |
427 | joint.localRotMat = new THREE.Matrix4();
428 | joint.localRotMat.makeRotationFromEuler(thisEuler);
429 | joint.rotation.setFromRotationMatrix(joint.localRotMat);
430 |
431 | if (joint.jointparent !== 0) {
432 | // joint.position.set(offsetVec.x, offsetVec.y, offsetVec.z);
433 | } else { // root
434 | joint.position.set(self.originPosition.x, self.originPosition.y, self.originPosition.z);
435 | }
436 | });
437 | };
438 | };
439 |
440 |
441 | module.exports = BVHCharacter;
442 |
443 | /***/ },
444 | /* 2 */
445 | /***/ function(module, exports, __webpack_require__) {
446 |
447 | module.exports ={
448 | bvhParser: __webpack_require__(3),
449 | bvhStreamParser: __webpack_require__(4)
450 | };
451 |
452 | /***/ },
453 | /* 3 */
454 | /***/ function(module, exports) {
455 |
456 | // By Ankit
457 | var BVHReader = function () {
458 | this.load = function (url, callbackHeader, callbackFrameArray) {
459 | $.get(url, function (str) {
460 |
461 | var dataReturn = parse(str);
462 |
463 |
464 | var jointStack = dataReturn[0];
465 | var jointMap = dataReturn[1];
466 | var jointArray = dataReturn[2];
467 | var connectivityMatrix = dataReturn[3]
468 | _bvh = new BVHReader.BVH.Skeleton(jointStack[0], jointMap, jointArray, dataReturn[3], dataReturn[4], dataReturn[5], []);
469 |
470 | if (callbackHeader)
471 | callbackHeader(_bvh,'BVH');
472 | console.log("Blah");
473 | _bvh.fillFrameArray(dataReturn[6]);
474 |
475 | if (callbackFrameArray)
476 | callbackFrameArray();
477 |
478 | });
479 | };
480 |
481 | function parse(str) {
482 | var lines = str.split('\n');
483 | var jointStack = [];
484 | var jointMap = {};
485 | var jointArray = [];
486 | var connectivityMatrix = [];
487 | var frameCount, frameTime, frameArray = [];
488 | var i = 0;
489 | //parse structure
490 | for (i = 1; i < lines.length; i++) {
491 | if (!parseLine(lines[i], jointStack, jointMap, jointArray, connectivityMatrix)) {
492 | break;
493 | }
494 | }
495 |
496 | for (i = i + 1; i < lines.length; i++) {
497 | var line = lines[i].trim();
498 | //when encountering last line
499 | if (line === "")
500 | break;
501 | if (line.indexOf("Frames") === 0) {
502 | frameCount = +(line.split(/\b/)[2]);
503 | } else if (line.indexOf("Frame Time") === 0) {
504 | frameTime = +( line.substr(line.indexOf(":") + 1).trim() )
505 | } else {
506 | var parts = line.split(" ");
507 | for (var j = 0; j < parts.length; j++)
508 | parts[j] = +parts[j];
509 | frameArray.push(parts);
510 | }
511 | }
512 |
513 | //parse motion
514 | return [jointStack, jointMap, jointArray, connectivityMatrix, frameCount, frameTime, frameArray];
515 | }
516 |
517 | //parses individual line in the bvh file.
518 | var parseLine = function (line, jointStack, jointMap, jointArray, connectivityMatrix) {
519 | line = line.trim();
520 | if (line.indexOf("ROOT") > -1 || line.indexOf("JOINT") > -1 || line.indexOf("End") > -1) {
521 | var parts = line.split(" ");
522 | var title = parts[1]; //temporary variable to be used after creating the joint object
523 | parts[1] = parts[1] + "-" + jointArray.length;
524 | var joint = new BVHReader.BVH.Joint(parts[1]);
525 | joint.title = title;
526 | jointStack.push(joint);
527 |
528 | joint.jointIndex = Object.keys(jointMap).length;
529 | jointMap[parts[1]] = joint;
530 | jointArray.push(joint);
531 | //if the joint is not an end site
532 | if( line.indexOf("End") != 0 ){
533 | if (jointArray.length == 1) {
534 | joint.channelOffset = 0;
535 | } else {
536 | joint.channelOffset = jointArray[jointArray.length - 2].channelOffset + jointArray[jointArray.length - 2].channelLength;
537 | }
538 | }else{
539 | //channelLength is 0 for end joints
540 | joint.channelLength = 0;
541 | joint.channelOffset = jointArray[jointArray.length - 2].channelOffset + jointArray[jointArray.length - 2].channelLength;
542 | }
543 |
544 | } else if (line.indexOf("{") === 0) {
545 |
546 | } else if (line.indexOf("OFFSET") === 0) {
547 | var parts = line.split(" ");
548 | jointStack[jointStack.length - 1]["offset"] = parts.slice(1);
549 | for(x in jointStack[jointStack.length - 1]["offset"]){
550 | jointStack[jointStack.length - 1]["offset"][x] = +jointStack[jointStack.length - 1]["offset"][x]
551 | }
552 | } else if (line.indexOf("CHANNELS") === 0) {
553 | var parts = line.split(" ");
554 | jointStack[jointStack.length - 1].setChannelNames(parts.slice(2));
555 | jointStack[jointStack.length - 1]["channelLength"] = +parts[1];
556 | } else if (line.indexOf("}") === 0) {
557 | if (jointStack.length > 1) {
558 | child = jointStack.pop();
559 | jointStack[jointStack.length - 1].children.push(child);
560 | child.parent = jointStack[jointStack.length - 1];
561 |
562 | connectivityMatrix.push([child.parent, child])
563 |
564 | // if(!connectivityMatrix[child.name]){
565 | // connectivityMatrix[child.name] = {}
566 | // }
567 | // connectivityMatrix[child.name][child.parent.name] = 1;
568 |
569 | // if(!connectivityMatrix[child.parent.name]){
570 | // connectivityMatrix[child.parent.name] = {}
571 | // }
572 | // connectivityMatrix[child.parent.name][child.name] = 1;
573 | }
574 | } else if (line.indexOf("MOTION") == 0) {
575 | return false;
576 | }
577 |
578 | return true;
579 | };
580 | };
581 |
582 | BVHReader.BVH = BVHReader.BVH || {};
583 |
584 | BVHReader.BVH.Joint = function (name, index) {
585 |
586 | this.name = name;
587 | this.children = [];
588 | this.isEndSite = function () {
589 | return this.children.length == 0;
590 | };
591 | this.rotationIndex = {};
592 | this.positionIndex = {};
593 |
594 | this.getChannels = function () {
595 | var allChannels = [];
596 | for (i = 0; i < this.skeleton.frameArray.length; i++) {
597 | allChannels.push(this.getChannelsAt(i));
598 | }
599 | return allChannels;
600 | };
601 | this.getChannelsAt = function (frameNum) {
602 | var channelsAtFrame = this.skeleton.frameArray[frameNum];
603 | return channelsAtFrame.slice(this.channelOffset, this.channelOffset + this.channelLength);
604 | };
605 |
606 | this.setChannelNames = function (nameArr){
607 | this.channelNames = nameArr;
608 | for(i in this.channelNames){
609 | var name = this.channelNames[i];
610 | switch(name){
611 | case "Xposition": this.positionIndex.x = i; break;
612 | case "Yposition": this.positionIndex.y = i; break;
613 | case "Zposition": this.positionIndex.z = i; break;
614 |
615 | case "Xrotation": this.rotationIndex.x = i; break;
616 | case "Yrotation": this.rotationIndex.y = i; break;
617 | case "Zrotation": this.rotationIndex.z = i; break;
618 | }
619 | }
620 | }
621 | };
622 |
623 | BVHReader.BVH.Skeleton = function (root, map, arr, connectivityMatrix, frameCount, frameTime, frameArray) {
624 | thisSkeleton = this;
625 | this.root = root;
626 | this.jointMap = map;
627 | this.jointArray = arr;
628 | this.connectivityMatrix = connectivityMatrix;
629 | this.frameCount = frameCount;
630 | this.frameTime = frameTime;
631 | this.frameArray = frameArray;
632 |
633 | for (i = 0; i < this.jointArray.length; i++) {
634 | this.jointArray[i].skeleton = thisSkeleton;
635 | }
636 |
637 |
638 |
639 | this.fillFrameArray = function (fa) {
640 | this.frameArray = fa;
641 | this.frameCount = fa.length;
642 | //all the structures are ready. let's calculate the positions
643 | for(j=0; j < this.jointArray.length; j++){
644 | var joint = this.jointArray[j];
645 | updateWithPositions(joint);
646 | }
647 | }
648 |
649 | this.getChannels = function () {
650 | return frameArray;
651 | };
652 | this.getChannelsAt = function (frameNum) {
653 | //How do I know which column is what?
654 | //Why do you need the column index?
655 | return frameArray[frameNum];
656 | };
657 | this.getFrameRate = function () {
658 | return frameCount / frameTime;
659 | };
660 | this.getSkeleton = function () {
661 | return root;
662 | };
663 |
664 | this.getHeadJoint = function () {
665 | // do a quick search in the joint names to see if any of them matches head, else return the something!!!!
666 | return jointMap["Head"];
667 | };
668 | this.getPositionsAt = function (frameNum) {
669 | //for each joint, calculate its position in XYZ
670 | //return an array of joints, each with .x, .y, and .z properties
671 | posFrame = [];
672 |
673 | for (j=0;j -1 || line.indexOf("JOINT") > -1 || line.indexOf("End") > -1) {
919 | var parts = line.split(" ");
920 | var title = parts[1]; //temporary variable to be used after creating the joint object
921 | parts[1] = parts[1] + "-" + jointArray.length;
922 | var joint = new BVHStreamParser.BVH.Joint(parts[1]);
923 | joint.title = title;
924 | jointStack.push(joint);
925 |
926 | joint.jointIndex = Object.keys(jointMap).length;
927 | jointMap[parts[1]] = joint;
928 | jointArray.push(joint);
929 | //if the joint is not an end site
930 | if( line.indexOf("End") != 0 ){
931 | if (jointArray.length == 1) {
932 | joint.channelOffset = 0;
933 | } else {
934 | joint.channelOffset = jointArray[jointArray.length - 2].channelOffset + jointArray[jointArray.length - 2].channelLength;
935 | }
936 | }else{
937 | //channelLength is 0 for end joints
938 | joint.channelLength = 0;
939 | joint.channelOffset = jointArray[jointArray.length - 2].channelOffset + jointArray[jointArray.length - 2].channelLength;
940 | }
941 |
942 | } else if (line.indexOf("{") === 0) {
943 |
944 | } else if (line.indexOf("OFFSET") === 0) {
945 | var parts = line.split(" ");
946 | jointStack[jointStack.length - 1]["offset"] = parts.slice(1);
947 | for(x in jointStack[jointStack.length - 1]["offset"]){
948 | jointStack[jointStack.length - 1]["offset"][x] = +jointStack[jointStack.length - 1]["offset"][x]
949 | }
950 | } else if (line.indexOf("CHANNELS") === 0) {
951 | var parts = line.split(" ");
952 | jointStack[jointStack.length - 1].setChannelNames(parts.slice(2));
953 | jointStack[jointStack.length - 1]["channelLength"] = +parts[1];
954 | } else if (line.indexOf("}") === 0) {
955 | if (jointStack.length > 1) {
956 | child = jointStack.pop();
957 | jointStack[jointStack.length - 1].children.push(child);
958 | child.parent = jointStack[jointStack.length - 1];
959 |
960 | connectivityMatrix.push([child.parent, child])
961 | }
962 | } else if (line.indexOf("MOTION") == 0) {
963 | return false;
964 | }
965 |
966 | return true;
967 | };
968 | };
969 |
970 | BVHStreamParser.BVH = BVHStreamParser.BVH || {};
971 |
972 | BVHStreamParser.BVH.Joint = function (name, index) {
973 |
974 | this.name = name;
975 | this.children = [];
976 | this.isEndSite = function () {
977 | return this.children.length == 0;
978 | };
979 | this.rotationIndex = {};
980 | this.positionIndex = {};
981 |
982 | this.getChannels = function () {
983 | var allChannels = [];
984 | for (i = 0; i < this.skeleton.frameArray.length; i++) {
985 | allChannels.push(this.getChannelsAt(i));
986 | }
987 | return allChannels;
988 | };
989 | this.getChannelsAt = function (frameNum) {
990 | var channelsAtFrame = this.skeleton.frameArray[frameNum];
991 | return channelsAtFrame.slice(this.channelOffset, this.channelOffset + this.channelLength);
992 | };
993 |
994 | this.setChannelNames = function (nameArr){
995 | this.channelNames = nameArr;
996 | for(i in this.channelNames){
997 | var name = this.channelNames[i];
998 | switch(name){
999 | case "Xposition": this.positionIndex.x = i; break;
1000 | case "Yposition": this.positionIndex.y = i; break;
1001 | case "Zposition": this.positionIndex.z = i; break;
1002 |
1003 | case "Xrotation": this.rotationIndex.x = i; break;
1004 | case "Yrotation": this.rotationIndex.y = i; break;
1005 | case "Zrotation": this.rotationIndex.z = i; break;
1006 | }
1007 | }
1008 | }
1009 | };
1010 |
1011 | BVHStreamParser.BVH.Skeleton = function (root, map, arr, connectivityMatrix, frameCount, frameTime, frameArray) {
1012 | thisSkeleton = this;
1013 | this.root = root;
1014 | this.jointMap = map;
1015 | this.jointArray = arr;
1016 | this.connectivityMatrix = connectivityMatrix;
1017 | this.frameCount = frameCount;
1018 | this.frameTime = frameTime;
1019 | this.frameArray = frameArray;
1020 | this.bufferSize = 500;
1021 |
1022 | for (i = 0; i < this.jointArray.length; i++) {
1023 | this.jointArray[i].skeleton = thisSkeleton;
1024 | }
1025 |
1026 | this.fillFrameArray = function (fa) {
1027 | this.frameArray.push.apply(this.frameArray,fa);
1028 | //this.frameArray.push.apply(this.frameArray,fa);
1029 |
1030 | diff = this.frameArray.length - this.bufferSize;
1031 | // console.log('diff = ' + diff);
1032 |
1033 | /*
1034 | if (diff > 0)
1035 | for (i=0;i 0)
1042 | addedCount = this.frameCount;
1043 | else
1044 | addedCount = fa.length;
1045 |
1046 | for(j=0; j < this.jointArray.length; j++){
1047 | var joint = this.jointArray[j];
1048 | updateWithPositionsSinceLast(joint, addedCount);
1049 | }
1050 |
1051 | return diff;
1052 | }
1053 |
1054 | this.consumeFrames = function (index) {
1055 | for (i=0;i<=index;i++) {
1056 | this.frameArray.shift();
1057 | for (j=0;jb;b++)if(b in this&&this[b]===a)return b;return-1};for(u={catchupTime:100,initialRate:.03,minTime:250,ghostTime:100,maxProgressPerFrame:20,easeFactor:1.25,startOnPageLoad:!0,restartOnPushState:!0,restartOnRequestAfter:500,target:"body",elements:{checkInterval:100,selectors:["body"]},eventLag:{minSamples:10,sampleCount:3,lagThreshold:3},ajax:{trackMethods:["GET"],trackWebSockets:!0,ignoreURLs:[]}},C=function(){var a;return null!=(a="undefined"!=typeof performance&&null!==performance&&"function"==typeof performance.now?performance.now():void 0)?a:+new Date},E=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame,t=window.cancelAnimationFrame||window.mozCancelAnimationFrame,null==E&&(E=function(a){return setTimeout(a,50)},t=function(a){return clearTimeout(a)}),G=function(a){var b,c;return b=C(),(c=function(){var d;return d=C()-b,d>=33?(b=C(),a(d,function(){return E(c)})):setTimeout(c,33-d)})()},F=function(){var a,b,c;return c=arguments[0],b=arguments[1],a=3<=arguments.length?X.call(arguments,2):[],"function"==typeof c[b]?c[b].apply(c,a):c[b]},v=function(){var a,b,c,d,e,f,g;for(b=arguments[0],d=2<=arguments.length?X.call(arguments,1):[],f=0,g=d.length;g>f;f++)if(c=d[f])for(a in c)Y.call(c,a)&&(e=c[a],null!=b[a]&&"object"==typeof b[a]&&null!=e&&"object"==typeof e?v(b[a],e):b[a]=e);return b},q=function(a){var b,c,d,e,f;for(c=b=0,e=0,f=a.length;f>e;e++)d=a[e],c+=Math.abs(d),b++;return c/b},x=function(a,b){var c,d,e;if(null==a&&(a="options"),null==b&&(b=!0),e=document.querySelector("[data-pace-"+a+"]")){if(c=e.getAttribute("data-pace-"+a),!b)return c;try{return JSON.parse(c)}catch(f){return d=f,"undefined"!=typeof console&&null!==console?console.error("Error parsing inline pace options",d):void 0}}},g=function(){function a(){}return a.prototype.on=function(a,b,c,d){var e;return null==d&&(d=!1),null==this.bindings&&(this.bindings={}),null==(e=this.bindings)[a]&&(e[a]=[]),this.bindings[a].push({handler:b,ctx:c,once:d})},a.prototype.once=function(a,b,c){return this.on(a,b,c,!0)},a.prototype.off=function(a,b){var c,d,e;if(null!=(null!=(d=this.bindings)?d[a]:void 0)){if(null==b)return delete this.bindings[a];for(c=0,e=[];cQ;Q++)K=U[Q],D[K]===!0&&(D[K]=u[K]);i=function(a){function b(){return V=b.__super__.constructor.apply(this,arguments)}return Z(b,a),b}(Error),b=function(){function a(){this.progress=0}return a.prototype.getElement=function(){var a;if(null==this.el){if(a=document.querySelector(D.target),!a)throw new i;this.el=document.createElement("div"),this.el.className="pace pace-active",document.body.className=document.body.className.replace(/pace-done/g,""),document.body.className+=" pace-running",this.el.innerHTML='\n',null!=a.firstChild?a.insertBefore(this.el,a.firstChild):a.appendChild(this.el)}return this.el},a.prototype.finish=function(){var a;return a=this.getElement(),a.className=a.className.replace("pace-active",""),a.className+=" pace-inactive",document.body.className=document.body.className.replace("pace-running",""),document.body.className+=" pace-done"},a.prototype.update=function(a){return this.progress=a,this.render()},a.prototype.destroy=function(){try{this.getElement().parentNode.removeChild(this.getElement())}catch(a){i=a}return this.el=void 0},a.prototype.render=function(){var a,b,c,d,e,f,g;if(null==document.querySelector(D.target))return!1;for(a=this.getElement(),d="translate3d("+this.progress+"%, 0, 0)",g=["webkitTransform","msTransform","transform"],e=0,f=g.length;f>e;e++)b=g[e],a.children[0].style[b]=d;return(!this.lastRenderedProgress||this.lastRenderedProgress|0!==this.progress|0)&&(a.children[0].setAttribute("data-progress-text",""+(0|this.progress)+"%"),this.progress>=100?c="99":(c=this.progress<10?"0":"",c+=0|this.progress),a.children[0].setAttribute("data-progress",""+c)),this.lastRenderedProgress=this.progress},a.prototype.done=function(){return this.progress>=100},a}(),h=function(){function a(){this.bindings={}}return a.prototype.trigger=function(a,b){var c,d,e,f,g;if(null!=this.bindings[a]){for(f=this.bindings[a],g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(c.call(this,b));return g}},a.prototype.on=function(a,b){var c;return null==(c=this.bindings)[a]&&(c[a]=[]),this.bindings[a].push(b)},a}(),P=window.XMLHttpRequest,O=window.XDomainRequest,N=window.WebSocket,w=function(a,b){var c,d,e;e=[];for(d in b.prototype)try{e.push(null==a[d]&&"function"!=typeof b[d]?"function"==typeof Object.defineProperty?Object.defineProperty(a,d,{get:function(){return b.prototype[d]},configurable:!0,enumerable:!0}):a[d]=b.prototype[d]:void 0)}catch(f){c=f}return e},A=[],j.ignore=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?X.call(arguments,1):[],A.unshift("ignore"),c=b.apply(null,a),A.shift(),c},j.track=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?X.call(arguments,1):[],A.unshift("track"),c=b.apply(null,a),A.shift(),c},J=function(a){var b;if(null==a&&(a="GET"),"track"===A[0])return"force";if(!A.length&&D.ajax){if("socket"===a&&D.ajax.trackWebSockets)return!0;if(b=a.toUpperCase(),$.call(D.ajax.trackMethods,b)>=0)return!0}return!1},k=function(a){function b(){var a,c=this;b.__super__.constructor.apply(this,arguments),a=function(a){var b;return b=a.open,a.open=function(d,e){return J(d)&&c.trigger("request",{type:d,url:e,request:a}),b.apply(a,arguments)}},window.XMLHttpRequest=function(b){var c;return c=new P(b),a(c),c};try{w(window.XMLHttpRequest,P)}catch(d){}if(null!=O){window.XDomainRequest=function(){var b;return b=new O,a(b),b};try{w(window.XDomainRequest,O)}catch(d){}}if(null!=N&&D.ajax.trackWebSockets){window.WebSocket=function(a,b){var d;return d=null!=b?new N(a,b):new N(a),J("socket")&&c.trigger("request",{type:"socket",url:a,protocols:b,request:d}),d};try{w(window.WebSocket,N)}catch(d){}}}return Z(b,a),b}(h),R=null,y=function(){return null==R&&(R=new k),R},I=function(a){var b,c,d,e;for(e=D.ajax.ignoreURLs,c=0,d=e.length;d>c;c++)if(b=e[c],"string"==typeof b){if(-1!==a.indexOf(b))return!0}else if(b.test(a))return!0;return!1},y().on("request",function(b){var c,d,e,f,g;return f=b.type,e=b.request,g=b.url,I(g)?void 0:j.running||D.restartOnRequestAfter===!1&&"force"!==J(f)?void 0:(d=arguments,c=D.restartOnRequestAfter||0,"boolean"==typeof c&&(c=0),setTimeout(function(){var b,c,g,h,i,k;if(b="socket"===f?e.readyState<2:0<(h=e.readyState)&&4>h){for(j.restart(),i=j.sources,k=[],c=0,g=i.length;g>c;c++){if(K=i[c],K instanceof a){K.watch.apply(K,d);break}k.push(void 0)}return k}},c))}),a=function(){function a(){var a=this;this.elements=[],y().on("request",function(){return a.watch.apply(a,arguments)})}return a.prototype.watch=function(a){var b,c,d,e;return d=a.type,b=a.request,e=a.url,I(e)?void 0:(c="socket"===d?new n(b):new o(b),this.elements.push(c))},a}(),o=function(){function a(a){var b,c,d,e,f,g,h=this;if(this.progress=0,null!=window.ProgressEvent)for(c=null,a.addEventListener("progress",function(a){return h.progress=a.lengthComputable?100*a.loaded/a.total:h.progress+(100-h.progress)/2},!1),g=["load","abort","timeout","error"],d=0,e=g.length;e>d;d++)b=g[d],a.addEventListener(b,function(){return h.progress=100},!1);else f=a.onreadystatechange,a.onreadystatechange=function(){var b;return 0===(b=a.readyState)||4===b?h.progress=100:3===a.readyState&&(h.progress=50),"function"==typeof f?f.apply(null,arguments):void 0}}return a}(),n=function(){function a(a){var b,c,d,e,f=this;for(this.progress=0,e=["error","open"],c=0,d=e.length;d>c;c++)b=e[c],a.addEventListener(b,function(){return f.progress=100},!1)}return a}(),d=function(){function a(a){var b,c,d,f;for(null==a&&(a={}),this.elements=[],null==a.selectors&&(a.selectors=[]),f=a.selectors,c=0,d=f.length;d>c;c++)b=f[c],this.elements.push(new e(b))}return a}(),e=function(){function a(a){this.selector=a,this.progress=0,this.check()}return a.prototype.check=function(){var a=this;return document.querySelector(this.selector)?this.done():setTimeout(function(){return a.check()},D.elements.checkInterval)},a.prototype.done=function(){return this.progress=100},a}(),c=function(){function a(){var a,b,c=this;this.progress=null!=(b=this.states[document.readyState])?b:100,a=document.onreadystatechange,document.onreadystatechange=function(){return null!=c.states[document.readyState]&&(c.progress=c.states[document.readyState]),"function"==typeof a?a.apply(null,arguments):void 0}}return a.prototype.states={loading:0,interactive:50,complete:100},a}(),f=function(){function a(){var a,b,c,d,e,f=this;this.progress=0,a=0,e=[],d=0,c=C(),b=setInterval(function(){var g;return g=C()-c-50,c=C(),e.push(g),e.length>D.eventLag.sampleCount&&e.shift(),a=q(e),++d>=D.eventLag.minSamples&&a=100&&(this.done=!0),b===this.last?this.sinceLastUpdate+=a:(this.sinceLastUpdate&&(this.rate=(b-this.last)/this.sinceLastUpdate),this.catchup=(b-this.progress)/D.catchupTime,this.sinceLastUpdate=0,this.last=b),b>this.progress&&(this.progress+=this.catchup*a),c=1-Math.pow(this.progress/100,D.easeFactor),this.progress+=c*this.rate*a,this.progress=Math.min(this.lastProgress+D.maxProgressPerFrame,this.progress),this.progress=Math.max(0,this.progress),this.progress=Math.min(100,this.progress),this.lastProgress=this.progress,this.progress},a}(),L=null,H=null,r=null,M=null,p=null,s=null,j.running=!1,z=function(){return D.restartOnPushState?j.restart():void 0},null!=window.history.pushState&&(T=window.history.pushState,window.history.pushState=function(){return z(),T.apply(window.history,arguments)}),null!=window.history.replaceState&&(W=window.history.replaceState,window.history.replaceState=function(){return z(),W.apply(window.history,arguments)}),l={ajax:a,elements:d,document:c,eventLag:f},(B=function(){var a,c,d,e,f,g,h,i;for(j.sources=L=[],g=["ajax","elements","document","eventLag"],c=0,e=g.length;e>c;c++)a=g[c],D[a]!==!1&&L.push(new l[a](D[a]));for(i=null!=(h=D.extraSources)?h:[],d=0,f=i.length;f>d;d++)K=i[d],L.push(new K(D));return j.bar=r=new b,H=[],M=new m})(),j.stop=function(){return j.trigger("stop"),j.running=!1,r.destroy(),s=!0,null!=p&&("function"==typeof t&&t(p),p=null),B()},j.restart=function(){return j.trigger("restart"),j.stop(),j.start()},j.go=function(){var a;return j.running=!0,r.render(),a=C(),s=!1,p=G(function(b,c){var d,e,f,g,h,i,k,l,n,o,p,q,t,u,v,w;for(l=100-r.progress,e=p=0,f=!0,i=q=0,u=L.length;u>q;i=++q)for(K=L[i],o=null!=H[i]?H[i]:H[i]=[],h=null!=(w=K.elements)?w:[K],k=t=0,v=h.length;v>t;k=++t)g=h[k],n=null!=o[k]?o[k]:o[k]=new m(g),f&=n.done,n.done||(e++,p+=n.tick(b));return d=p/e,r.update(M.tick(b,d)),r.done()||f||s?(r.update(100),j.trigger("done"),setTimeout(function(){return r.finish(),j.running=!1,j.trigger("hide")},Math.max(D.ghostTime,Math.max(D.minTime-(C()-a),0)))):c()})},j.start=function(a){v(D,a),j.running=!0;try{r.render()}catch(b){i=b}return document.querySelector(".pace")?(j.trigger("start"),j.go()):setTimeout(j.start,50)},"function"==typeof define&&define.amd?define(["pace"],function(){return j}):"object"==typeof exports?module.exports=j:D.startOnPageLoad&&j.start()}).call(this);
--------------------------------------------------------------------------------
/pymo/mocapplayer/libs/papaparse.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | Papa Parse
3 | v4.1.2
4 | https://github.com/mholt/PapaParse
5 | */
6 | !function(e){"use strict";function t(t,r){if(r=r||{},r.worker&&S.WORKERS_SUPPORTED){var n=f();return n.userStep=r.step,n.userChunk=r.chunk,n.userComplete=r.complete,n.userError=r.error,r.step=m(r.step),r.chunk=m(r.chunk),r.complete=m(r.complete),r.error=m(r.error),delete r.worker,void n.postMessage({input:t,config:r,workerId:n.id})}var o=null;return"string"==typeof t?o=r.download?new i(r):new a(r):(e.File&&t instanceof File||t instanceof Object)&&(o=new s(r)),o.stream(t)}function r(e,t){function r(){"object"==typeof t&&("string"==typeof t.delimiter&&1==t.delimiter.length&&-1==S.BAD_DELIMITERS.indexOf(t.delimiter)&&(u=t.delimiter),("boolean"==typeof t.quotes||t.quotes instanceof Array)&&(o=t.quotes),"string"==typeof t.newline&&(h=t.newline))}function n(e){if("object"!=typeof e)return[];var t=[];for(var r in e)t.push(r);return t}function i(e,t){var r="";"string"==typeof e&&(e=JSON.parse(e)),"string"==typeof t&&(t=JSON.parse(t));var n=e instanceof Array&&e.length>0,i=!(t[0]instanceof Array);if(n){for(var a=0;a0&&(r+=u),r+=s(e[a],a);t.length>0&&(r+=h)}for(var o=0;oc;c++){c>0&&(r+=u);var d=n&&i?e[c]:c;r+=s(t[o][d],c)}o-1||" "==e.charAt(0)||" "==e.charAt(e.length-1);return r?'"'+e+'"':e}function a(e,t){for(var r=0;r-1)return!0;return!1}var o=!1,u=",",h="\r\n";if(r(),"string"==typeof e&&(e=JSON.parse(e)),e instanceof Array){if(!e.length||e[0]instanceof Array)return i(null,e);if("object"==typeof e[0])return i(n(e[0]),e)}else if("object"==typeof e)return"string"==typeof e.data&&(e.data=JSON.parse(e.data)),e.data instanceof Array&&(e.fields||(e.fields=e.data[0]instanceof Array?e.fields:n(e.data[0])),e.data[0]instanceof Array||"object"==typeof e.data[0]||(e.data=[e.data])),i(e.fields||[],e.data||[]);throw"exception: Unable to serialize unrecognized input"}function n(t){function r(e){var t=_(e);t.chunkSize=parseInt(t.chunkSize),e.step||e.chunk||(t.chunkSize=null),this._handle=new o(t),this._handle.streamer=this,this._config=t}this._handle=null,this._paused=!1,this._finished=!1,this._input=null,this._baseIndex=0,this._partialLine="",this._rowCount=0,this._start=0,this._nextChunk=null,this.isFirstChunk=!0,this._completeResults={data:[],errors:[],meta:{}},r.call(this,t),this.parseChunk=function(t){if(this.isFirstChunk&&m(this._config.beforeFirstChunk)){var r=this._config.beforeFirstChunk(t);void 0!==r&&(t=r)}this.isFirstChunk=!1;var n=this._partialLine+t;this._partialLine="";var i=this._handle.parse(n,this._baseIndex,!this._finished);if(!this._handle.paused()&&!this._handle.aborted()){var s=i.meta.cursor;this._finished||(this._partialLine=n.substring(s-this._baseIndex),this._baseIndex=s),i&&i.data&&(this._rowCount+=i.data.length);var a=this._finished||this._config.preview&&this._rowCount>=this._config.preview;if(y)e.postMessage({results:i,workerId:S.WORKER_ID,finished:a});else if(m(this._config.chunk)){if(this._config.chunk(i,this._handle),this._paused)return;i=void 0,this._completeResults=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(i.data),this._completeResults.errors=this._completeResults.errors.concat(i.errors),this._completeResults.meta=i.meta),!a||!m(this._config.complete)||i&&i.meta.aborted||this._config.complete(this._completeResults),a||i&&i.meta.paused||this._nextChunk(),i}},this._sendError=function(t){m(this._config.error)?this._config.error(t):y&&this._config.error&&e.postMessage({workerId:S.WORKER_ID,error:t,finished:!1})}}function i(e){function t(e){var t=e.getResponseHeader("Content-Range");return parseInt(t.substr(t.lastIndexOf("/")+1))}e=e||{},e.chunkSize||(e.chunkSize=S.RemoteChunkSize),n.call(this,e);var r;this._nextChunk=k?function(){this._readChunk(),this._chunkLoaded()}:function(){this._readChunk()},this.stream=function(e){this._input=e,this._nextChunk()},this._readChunk=function(){if(this._finished)return void this._chunkLoaded();if(r=new XMLHttpRequest,k||(r.onload=g(this._chunkLoaded,this),r.onerror=g(this._chunkError,this)),r.open("GET",this._input,!k),this._config.chunkSize){var e=this._start+this._config.chunkSize-1;r.setRequestHeader("Range","bytes="+this._start+"-"+e),r.setRequestHeader("If-None-Match","webkit-no-cache")}try{r.send()}catch(t){this._chunkError(t.message)}k&&0==r.status?this._chunkError():this._start+=this._config.chunkSize},this._chunkLoaded=function(){if(4==r.readyState){if(r.status<200||r.status>=400)return void this._chunkError();this._finished=!this._config.chunkSize||this._start>t(r),this.parseChunk(r.responseText)}},this._chunkError=function(e){var t=r.statusText||e;this._sendError(t)}}function s(e){e=e||{},e.chunkSize||(e.chunkSize=S.LocalChunkSize),n.call(this,e);var t,r,i="undefined"!=typeof FileReader;this.stream=function(e){this._input=e,r=e.slice||e.webkitSlice||e.mozSlice,i?(t=new FileReader,t.onload=g(this._chunkLoaded,this),t.onerror=g(this._chunkError,this)):t=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(e.target.result)},this._chunkError=function(){this._sendError(t.error)}}function a(e){e=e||{},n.call(this,e);var t,r;this.stream=function(e){return t=e,r=e,this._nextChunk()},this._nextChunk=function(){if(!this._finished){var e=this._config.chunkSize,t=e?r.substr(0,e):r;return r=e?r.substr(e):"",this._finished=!r,this.parseChunk(t)}}}function o(e){function t(){if(b&&d&&(h("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+S.DefaultDelimiter+"'"),d=!1),e.skipEmptyLines)for(var t=0;t=y.length?(r.__parsed_extra||(r.__parsed_extra=[]),r.__parsed_extra.push(b.data[t][n])):r[y[n]]=b.data[t][n])}e.header&&(b.data[t]=r,n>y.length?h("FieldMismatch","TooManyFields","Too many fields: expected "+y.length+" fields but parsed "+n,t):n1&&(h+=Math.abs(l-i),i=l):i=l}c.data.length>0&&(f/=c.data.length),("undefined"==typeof n||n>h)&&f>1.99&&(n=h,r=o)}return e.delimiter=r,{successful:!!r,bestDelimiter:r}}function a(e){e=e.substr(0,1048576);var t=e.split("\r");if(1==t.length)return"\n";for(var r=0,n=0;n=t.length/2?"\r\n":"\r"}function o(e){var t=l.test(e);return t?parseFloat(e):e}function h(e,t,r,n){b.errors.push({type:e,code:t,message:r,row:n})}var f,c,d,l=/^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i,p=this,g=0,v=!1,k=!1,y=[],b={data:[],errors:[],meta:{}};if(m(e.step)){var R=e.step;e.step=function(n){if(b=n,r())t();else{if(t(),0==b.data.length)return;g+=n.data.length,e.preview&&g>e.preview?c.abort():R(b,p)}}}this.parse=function(r,n,i){if(e.newline||(e.newline=a(r)),d=!1,!e.delimiter){var o=s(r);o.successful?e.delimiter=o.bestDelimiter:(d=!0,e.delimiter=S.DefaultDelimiter),b.meta.delimiter=e.delimiter}var h=_(e);return e.preview&&e.header&&h.preview++,f=r,c=new u(h),b=c.parse(f,n,i),t(),v?{meta:{paused:!0}}:b||{meta:{paused:!1}}},this.paused=function(){return v},this.pause=function(){v=!0,c.abort(),f=f.substr(c.getCharIndex())},this.resume=function(){v=!1,p.streamer.parseChunk(f)},this.aborted=function(){return k},this.abort=function(){k=!0,c.abort(),b.meta.aborted=!0,m(e.complete)&&e.complete(b),f=""}}function u(e){e=e||{};var t=e.delimiter,r=e.newline,n=e.comments,i=e.step,s=e.preview,a=e.fastMode;if(("string"!=typeof t||S.BAD_DELIMITERS.indexOf(t)>-1)&&(t=","),n===t)throw"Comment character same as delimiter";n===!0?n="#":("string"!=typeof n||S.BAD_DELIMITERS.indexOf(n)>-1)&&(n=!1),"\n"!=r&&"\r"!=r&&"\r\n"!=r&&(r="\n");var o=0,u=!1;this.parse=function(e,h,f){function c(e){b.push(e),S=o}function d(t){return f?p():("undefined"==typeof t&&(t=e.substr(o)),w.push(t),o=g,c(w),y&&_(),p())}function l(t){o=t,c(w),w=[],O=e.indexOf(r,o)}function p(e){return{data:b,errors:R,meta:{delimiter:t,linebreak:r,aborted:u,truncated:!!e,cursor:S+(h||0)}}}function _(){i(p()),b=[],R=[]}if("string"!=typeof e)throw"Input must be a string";var g=e.length,m=t.length,v=r.length,k=n.length,y="function"==typeof i;o=0;var b=[],R=[],w=[],S=0;if(!e)return p();if(a||a!==!1&&-1===e.indexOf('"')){for(var C=e.split(r),E=0;E=s)return b=b.slice(0,s),p(!0)}}return p()}for(var x=e.indexOf(t,o),O=e.indexOf(r,o);;)if('"'!=e[o])if(n&&0===w.length&&e.substr(o,k)===n){if(-1==O)return p();o=O+v,O=e.indexOf(r,o),x=e.indexOf(t,o)}else if(-1!==x&&(O>x||-1===O))w.push(e.substring(o,x)),o=x+m,x=e.indexOf(t,o);else{if(-1===O)break;if(w.push(e.substring(o,O)),l(O+v),y&&(_(),u))return p();if(s&&b.length>=s)return p(!0)}else{var I=o;for(o++;;){var I=e.indexOf('"',I+1);if(-1===I)return f||R.push({type:"Quotes",code:"MissingQuotes",message:"Quoted field unterminated",row:b.length,index:o}),d();if(I===g-1){var D=e.substring(o,I).replace(/""/g,'"');return d(D)}if('"'!=e[I+1]){if(e[I+1]==t){w.push(e.substring(o,I).replace(/""/g,'"')),o=I+1+m,x=e.indexOf(t,o),O=e.indexOf(r,o);break}if(e.substr(I+1,v)===r){if(w.push(e.substring(o,I).replace(/""/g,'"')),l(I+1+v),x=e.indexOf(t,o),y&&(_(),u))return p();if(s&&b.length>=s)return p(!0);break}}else I++}}return d()},this.abort=function(){u=!0},this.getCharIndex=function(){return o}}function h(){var e=document.getElementsByTagName("script");return e.length?e[e.length-1].src:""}function f(){if(!S.WORKERS_SUPPORTED)return!1;if(!b&&null===S.SCRIPT_PATH)throw new Error("Script path cannot be determined automatically when Papa Parse is loaded asynchronously. You need to set Papa.SCRIPT_PATH manually.");var t=S.SCRIPT_PATH||v;t+=(-1!==t.indexOf("?")?"&":"?")+"papaworker";var r=new e.Worker(t);return r.onmessage=c,r.id=w++,R[r.id]=r,r}function c(e){var t=e.data,r=R[t.workerId],n=!1;if(t.error)r.userError(t.error,t.file);else if(t.results&&t.results.data){var i=function(){n=!0,d(t.workerId,{data:[],errors:[],meta:{aborted:!0}})},s={abort:i,pause:l,resume:l};if(m(r.userStep)){for(var a=0;aWebGL.
',
43 | 'Find out how to get it here.'
44 | ].join( '\n' ) : [
45 | 'Your browser does not seem to support WebGL.
',
46 | 'Find out how to get it here.'
47 | ].join( '\n' );
48 |
49 | }
50 |
51 | return element;
52 |
53 | },
54 |
55 | addGetWebGLMessage: function ( parameters ) {
56 |
57 | var parent, id, element;
58 |
59 | parameters = parameters || {};
60 |
61 | parent = parameters.parent !== undefined ? parameters.parent : document.body;
62 | id = parameters.id !== undefined ? parameters.id : 'oldie';
63 |
64 | element = Detector.getWebGLErrorMessage();
65 | element.id = id;
66 |
67 | parent.appendChild( element );
68 |
69 | }
70 |
71 | };
72 |
73 | // browserify support
74 | if ( typeof module === 'object' ) {
75 |
76 | module.exports = Detector;
77 |
78 | }
--------------------------------------------------------------------------------
/pymo/mocapplayer/libs/threejs/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 |
9 | // This set of controls performs orbiting, dollying (zooming), and panning.
10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
11 | //
12 | // Orbit - left mouse / touch: one finger move
13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
14 | // Pan - right mouse, or arrow keys / touch: three finter swipe
15 |
16 | THREE.OrbitControls = function ( object, domElement ) {
17 |
18 | this.object = object;
19 |
20 | this.domElement = ( domElement !== undefined ) ? domElement : document;
21 |
22 | // Set to false to disable this control
23 | this.enabled = true;
24 |
25 | // "target" sets the location of focus, where the object orbits around
26 | this.target = new THREE.Vector3();
27 |
28 | // How far you can dolly in and out ( PerspectiveCamera only )
29 | this.minDistance = 0;
30 | this.maxDistance = Infinity;
31 |
32 | // How far you can zoom in and out ( OrthographicCamera only )
33 | this.minZoom = 0;
34 | this.maxZoom = Infinity;
35 |
36 | // How far you can orbit vertically, upper and lower limits.
37 | // Range is 0 to Math.PI radians.
38 | this.minPolarAngle = 0; // radians
39 | this.maxPolarAngle = Math.PI; // radians
40 |
41 | // How far you can orbit horizontally, upper and lower limits.
42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
43 | this.minAzimuthAngle = - Infinity; // radians
44 | this.maxAzimuthAngle = Infinity; // radians
45 |
46 | // Set to true to enable damping (inertia)
47 | // If damping is enabled, you must call controls.update() in your animation loop
48 | this.enableDamping = false;
49 | this.dampingFactor = 0.25;
50 |
51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
52 | // Set to false to disable zooming
53 | this.enableZoom = true;
54 | this.zoomSpeed = 1.0;
55 |
56 | // Set to false to disable rotating
57 | this.enableRotate = true;
58 | this.rotateSpeed = 1.0;
59 |
60 | // Set to false to disable panning
61 | this.enablePan = true;
62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
63 |
64 | // Set to true to automatically rotate around the target
65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
66 | this.autoRotate = false;
67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
68 |
69 | // Set to false to disable use of the keys
70 | this.enableKeys = true;
71 |
72 | // The four arrow keys
73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
74 |
75 | // Mouse buttons
76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
77 |
78 | // for reset
79 | this.target0 = this.target.clone();
80 | this.position0 = this.object.position.clone();
81 | this.zoom0 = this.object.zoom;
82 |
83 | //
84 | // public methods
85 | //
86 |
87 | this.getPolarAngle = function () {
88 |
89 | return spherical.phi;
90 |
91 | };
92 |
93 | this.getAzimuthalAngle = function () {
94 |
95 | return spherical.theta;
96 |
97 | };
98 |
99 | this.reset = function () {
100 |
101 | scope.target.copy( scope.target0 );
102 | scope.object.position.copy( scope.position0 );
103 | scope.object.zoom = scope.zoom0;
104 |
105 | scope.object.updateProjectionMatrix();
106 | scope.dispatchEvent( changeEvent );
107 |
108 | scope.update();
109 |
110 | state = STATE.NONE;
111 |
112 | };
113 |
114 | // this method is exposed, but perhaps it would be better if we can make it private...
115 | this.update = function() {
116 |
117 | var offset = new THREE.Vector3();
118 |
119 | // so camera.up is the orbit axis
120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
121 | var quatInverse = quat.clone().inverse();
122 |
123 | var lastPosition = new THREE.Vector3();
124 | var lastQuaternion = new THREE.Quaternion();
125 |
126 | return function () {
127 |
128 | var position = scope.object.position;
129 |
130 | offset.copy( position ).sub( scope.target );
131 |
132 | // rotate offset to "y-axis-is-up" space
133 | offset.applyQuaternion( quat );
134 |
135 | // angle from z-axis around y-axis
136 | spherical.setFromVector3( offset );
137 |
138 | if ( scope.autoRotate && state === STATE.NONE ) {
139 |
140 | rotateLeft( getAutoRotationAngle() );
141 |
142 | }
143 |
144 | spherical.theta += sphericalDelta.theta;
145 | spherical.phi += sphericalDelta.phi;
146 |
147 | // restrict theta to be between desired limits
148 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
149 |
150 | // restrict phi to be between desired limits
151 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
152 |
153 | spherical.makeSafe();
154 |
155 |
156 | spherical.radius *= scale;
157 |
158 | // restrict radius to be between desired limits
159 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
160 |
161 | // move target to panned location
162 | scope.target.add( panOffset );
163 |
164 | offset.setFromSpherical( spherical );
165 |
166 | // rotate offset back to "camera-up-vector-is-up" space
167 | offset.applyQuaternion( quatInverse );
168 |
169 | position.copy( scope.target ).add( offset );
170 |
171 | scope.object.lookAt( scope.target );
172 |
173 | if ( scope.enableDamping === true ) {
174 |
175 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
176 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
177 |
178 | } else {
179 |
180 | sphericalDelta.set( 0, 0, 0 );
181 |
182 | }
183 |
184 | scale = 1;
185 | panOffset.set( 0, 0, 0 );
186 |
187 | // update condition is:
188 | // min(camera displacement, camera rotation in radians)^2 > EPS
189 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
190 |
191 | if ( zoomChanged ||
192 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
193 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
194 |
195 | scope.dispatchEvent( changeEvent );
196 |
197 | lastPosition.copy( scope.object.position );
198 | lastQuaternion.copy( scope.object.quaternion );
199 | zoomChanged = false;
200 |
201 | return true;
202 |
203 | }
204 |
205 | return false;
206 |
207 | };
208 |
209 | }();
210 |
211 | this.dispose = function() {
212 |
213 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
214 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
215 | scope.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
216 | scope.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
217 |
218 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
219 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
220 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
221 |
222 | document.removeEventListener( 'mousemove', onMouseMove, false );
223 | document.removeEventListener( 'mouseup', onMouseUp, false );
224 | document.removeEventListener( 'mouseout', onMouseUp, false );
225 |
226 | window.removeEventListener( 'keydown', onKeyDown, false );
227 |
228 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
229 |
230 | };
231 |
232 | //
233 | // internals
234 | //
235 |
236 | var scope = this;
237 |
238 | var changeEvent = { type: 'change' };
239 | var startEvent = { type: 'start' };
240 | var endEvent = { type: 'end' };
241 |
242 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
243 |
244 | var state = STATE.NONE;
245 |
246 | var EPS = 0.000001;
247 |
248 | // current position in spherical coordinates
249 | var spherical = new THREE.Spherical();
250 | var sphericalDelta = new THREE.Spherical();
251 |
252 | var scale = 1;
253 | var panOffset = new THREE.Vector3();
254 | var zoomChanged = false;
255 |
256 | var rotateStart = new THREE.Vector2();
257 | var rotateEnd = new THREE.Vector2();
258 | var rotateDelta = new THREE.Vector2();
259 |
260 | var panStart = new THREE.Vector2();
261 | var panEnd = new THREE.Vector2();
262 | var panDelta = new THREE.Vector2();
263 |
264 | var dollyStart = new THREE.Vector2();
265 | var dollyEnd = new THREE.Vector2();
266 | var dollyDelta = new THREE.Vector2();
267 |
268 | function getAutoRotationAngle() {
269 |
270 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
271 |
272 | }
273 |
274 | function getZoomScale() {
275 |
276 | return Math.pow( 0.95, scope.zoomSpeed );
277 |
278 | }
279 |
280 | function rotateLeft( angle ) {
281 |
282 | sphericalDelta.theta -= angle;
283 |
284 | }
285 |
286 | function rotateUp( angle ) {
287 |
288 | sphericalDelta.phi -= angle;
289 |
290 | }
291 |
292 | var panLeft = function() {
293 |
294 | var v = new THREE.Vector3();
295 |
296 | return function panLeft( distance, objectMatrix ) {
297 |
298 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
299 | v.multiplyScalar( - distance );
300 |
301 | panOffset.add( v );
302 |
303 | };
304 |
305 | }();
306 |
307 | var panUp = function() {
308 |
309 | var v = new THREE.Vector3();
310 |
311 | return function panUp( distance, objectMatrix ) {
312 |
313 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix
314 | v.multiplyScalar( distance );
315 |
316 | panOffset.add( v );
317 |
318 | };
319 |
320 | }();
321 |
322 | // deltaX and deltaY are in pixels; right and down are positive
323 | var pan = function() {
324 |
325 | var offset = new THREE.Vector3();
326 |
327 | return function( deltaX, deltaY ) {
328 |
329 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
330 |
331 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
332 |
333 | // perspective
334 | var position = scope.object.position;
335 | offset.copy( position ).sub( scope.target );
336 | var targetDistance = offset.length();
337 |
338 | // half of the fov is center to top of screen
339 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
340 |
341 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
342 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
343 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
344 |
345 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
346 |
347 | // orthographic
348 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
349 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
350 |
351 | } else {
352 |
353 | // camera neither orthographic nor perspective
354 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
355 | scope.enablePan = false;
356 |
357 | }
358 |
359 | };
360 |
361 | }();
362 |
363 | function dollyIn( dollyScale ) {
364 |
365 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
366 |
367 | scale /= dollyScale;
368 |
369 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
370 |
371 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
372 | scope.object.updateProjectionMatrix();
373 | zoomChanged = true;
374 |
375 | } else {
376 |
377 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
378 | scope.enableZoom = false;
379 |
380 | }
381 |
382 | }
383 |
384 | function dollyOut( dollyScale ) {
385 |
386 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
387 |
388 | scale *= dollyScale;
389 |
390 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
391 |
392 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
393 | scope.object.updateProjectionMatrix();
394 | zoomChanged = true;
395 |
396 | } else {
397 |
398 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
399 | scope.enableZoom = false;
400 |
401 | }
402 |
403 | }
404 |
405 | //
406 | // event callbacks - update the object state
407 | //
408 |
409 | function handleMouseDownRotate( event ) {
410 |
411 | //console.log( 'handleMouseDownRotate' );
412 |
413 | rotateStart.set( event.clientX, event.clientY );
414 |
415 | }
416 |
417 | function handleMouseDownDolly( event ) {
418 |
419 | //console.log( 'handleMouseDownDolly' );
420 |
421 | dollyStart.set( event.clientX, event.clientY );
422 |
423 | }
424 |
425 | function handleMouseDownPan( event ) {
426 |
427 | //console.log( 'handleMouseDownPan' );
428 |
429 | panStart.set( event.clientX, event.clientY );
430 |
431 | }
432 |
433 | function handleMouseMoveRotate( event ) {
434 |
435 | //console.log( 'handleMouseMoveRotate' );
436 |
437 | rotateEnd.set( event.clientX, event.clientY );
438 | rotateDelta.subVectors( rotateEnd, rotateStart );
439 |
440 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
441 |
442 | // rotating across whole screen goes 360 degrees around
443 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
444 |
445 | // rotating up and down along whole screen attempts to go 360, but limited to 180
446 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
447 |
448 | rotateStart.copy( rotateEnd );
449 |
450 | scope.update();
451 |
452 | }
453 |
454 | function handleMouseMoveDolly( event ) {
455 |
456 | //console.log( 'handleMouseMoveDolly' );
457 |
458 | dollyEnd.set( event.clientX, event.clientY );
459 |
460 | dollyDelta.subVectors( dollyEnd, dollyStart );
461 |
462 | if ( dollyDelta.y > 0 ) {
463 |
464 | dollyIn( getZoomScale() );
465 |
466 | } else if ( dollyDelta.y < 0 ) {
467 |
468 | dollyOut( getZoomScale() );
469 |
470 | }
471 |
472 | dollyStart.copy( dollyEnd );
473 |
474 | scope.update();
475 |
476 | }
477 |
478 | function handleMouseMovePan( event ) {
479 |
480 | //console.log( 'handleMouseMovePan' );
481 |
482 | panEnd.set( event.clientX, event.clientY );
483 |
484 | panDelta.subVectors( panEnd, panStart );
485 |
486 | pan( panDelta.x, panDelta.y );
487 |
488 | panStart.copy( panEnd );
489 |
490 | scope.update();
491 |
492 | }
493 |
494 | function handleMouseUp( event ) {
495 |
496 | //console.log( 'handleMouseUp' );
497 |
498 | }
499 |
500 | function handleMouseWheel( event ) {
501 |
502 | //console.log( 'handleMouseWheel' );
503 |
504 | var delta = 0;
505 |
506 | if ( event.wheelDelta !== undefined ) {
507 |
508 | // WebKit / Opera / Explorer 9
509 |
510 | delta = event.wheelDelta;
511 |
512 | } else if ( event.detail !== undefined ) {
513 |
514 | // Firefox
515 |
516 | delta = - event.detail;
517 |
518 | }
519 |
520 | if ( delta > 0 ) {
521 |
522 | dollyOut( getZoomScale() );
523 |
524 | } else if ( delta < 0 ) {
525 |
526 | dollyIn( getZoomScale() );
527 |
528 | }
529 |
530 | scope.update();
531 |
532 | }
533 |
534 | function handleKeyDown( event ) {
535 |
536 | //console.log( 'handleKeyDown' );
537 |
538 | switch ( event.keyCode ) {
539 |
540 | case scope.keys.UP:
541 | pan( 0, scope.keyPanSpeed );
542 | scope.update();
543 | break;
544 |
545 | case scope.keys.BOTTOM:
546 | pan( 0, - scope.keyPanSpeed );
547 | scope.update();
548 | break;
549 |
550 | case scope.keys.LEFT:
551 | pan( scope.keyPanSpeed, 0 );
552 | scope.update();
553 | break;
554 |
555 | case scope.keys.RIGHT:
556 | pan( - scope.keyPanSpeed, 0 );
557 | scope.update();
558 | break;
559 |
560 | }
561 |
562 | }
563 |
564 | function handleTouchStartRotate( event ) {
565 |
566 | //console.log( 'handleTouchStartRotate' );
567 |
568 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
569 |
570 | }
571 |
572 | function handleTouchStartDolly( event ) {
573 |
574 | //console.log( 'handleTouchStartDolly' );
575 |
576 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
577 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
578 |
579 | var distance = Math.sqrt( dx * dx + dy * dy );
580 |
581 | dollyStart.set( 0, distance );
582 |
583 | }
584 |
585 | function handleTouchStartPan( event ) {
586 |
587 | //console.log( 'handleTouchStartPan' );
588 |
589 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
590 |
591 | }
592 |
593 | function handleTouchMoveRotate( event ) {
594 |
595 | //console.log( 'handleTouchMoveRotate' );
596 |
597 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
598 | rotateDelta.subVectors( rotateEnd, rotateStart );
599 |
600 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
601 |
602 | // rotating across whole screen goes 360 degrees around
603 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
604 |
605 | // rotating up and down along whole screen attempts to go 360, but limited to 180
606 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
607 |
608 | rotateStart.copy( rotateEnd );
609 |
610 | scope.update();
611 |
612 | }
613 |
614 | function handleTouchMoveDolly( event ) {
615 |
616 | //console.log( 'handleTouchMoveDolly' );
617 |
618 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
619 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
620 |
621 | var distance = Math.sqrt( dx * dx + dy * dy );
622 |
623 | dollyEnd.set( 0, distance );
624 |
625 | dollyDelta.subVectors( dollyEnd, dollyStart );
626 |
627 | if ( dollyDelta.y > 0 ) {
628 |
629 | dollyOut( getZoomScale() );
630 |
631 | } else if ( dollyDelta.y < 0 ) {
632 |
633 | dollyIn( getZoomScale() );
634 |
635 | }
636 |
637 | dollyStart.copy( dollyEnd );
638 |
639 | scope.update();
640 |
641 | }
642 |
643 | function handleTouchMovePan( event ) {
644 |
645 | //console.log( 'handleTouchMovePan' );
646 |
647 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
648 |
649 | panDelta.subVectors( panEnd, panStart );
650 |
651 | pan( panDelta.x, panDelta.y );
652 |
653 | panStart.copy( panEnd );
654 |
655 | scope.update();
656 |
657 | }
658 |
659 | function handleTouchEnd( event ) {
660 |
661 | //console.log( 'handleTouchEnd' );
662 |
663 | }
664 |
665 | //
666 | // event handlers - FSM: listen for events and reset state
667 | //
668 |
669 | function onMouseDown( event ) {
670 |
671 | if ( scope.enabled === false ) return;
672 |
673 | event.preventDefault();
674 |
675 | if ( event.button === scope.mouseButtons.ORBIT ) {
676 |
677 | if ( scope.enableRotate === false ) return;
678 |
679 | handleMouseDownRotate( event );
680 |
681 | state = STATE.ROTATE;
682 |
683 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
684 |
685 | if ( scope.enableZoom === false ) return;
686 |
687 | handleMouseDownDolly( event );
688 |
689 | state = STATE.DOLLY;
690 |
691 | } else if ( event.button === scope.mouseButtons.PAN ) {
692 |
693 | if ( scope.enablePan === false ) return;
694 |
695 | handleMouseDownPan( event );
696 |
697 | state = STATE.PAN;
698 |
699 | }
700 |
701 | if ( state !== STATE.NONE ) {
702 |
703 | document.addEventListener( 'mousemove', onMouseMove, false );
704 | document.addEventListener( 'mouseup', onMouseUp, false );
705 | document.addEventListener( 'mouseout', onMouseUp, false );
706 |
707 | scope.dispatchEvent( startEvent );
708 |
709 | }
710 |
711 | }
712 |
713 | function onMouseMove( event ) {
714 |
715 | if ( scope.enabled === false ) return;
716 |
717 | event.preventDefault();
718 |
719 | if ( state === STATE.ROTATE ) {
720 |
721 | if ( scope.enableRotate === false ) return;
722 |
723 | handleMouseMoveRotate( event );
724 |
725 | } else if ( state === STATE.DOLLY ) {
726 |
727 | if ( scope.enableZoom === false ) return;
728 |
729 | handleMouseMoveDolly( event );
730 |
731 | } else if ( state === STATE.PAN ) {
732 |
733 | if ( scope.enablePan === false ) return;
734 |
735 | handleMouseMovePan( event );
736 |
737 | }
738 |
739 | }
740 |
741 | function onMouseUp( event ) {
742 |
743 | if ( scope.enabled === false ) return;
744 |
745 | handleMouseUp( event );
746 |
747 | document.removeEventListener( 'mousemove', onMouseMove, false );
748 | document.removeEventListener( 'mouseup', onMouseUp, false );
749 | document.removeEventListener( 'mouseout', onMouseUp, false );
750 |
751 | scope.dispatchEvent( endEvent );
752 |
753 | state = STATE.NONE;
754 |
755 | }
756 |
757 | function onMouseWheel( event ) {
758 |
759 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
760 |
761 | event.preventDefault();
762 | event.stopPropagation();
763 |
764 | handleMouseWheel( event );
765 |
766 | scope.dispatchEvent( startEvent ); // not sure why these are here...
767 | scope.dispatchEvent( endEvent );
768 |
769 | }
770 |
771 | function onKeyDown( event ) {
772 |
773 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
774 |
775 | handleKeyDown( event );
776 |
777 | }
778 |
779 | function onTouchStart( event ) {
780 |
781 | if ( scope.enabled === false ) return;
782 |
783 | switch ( event.touches.length ) {
784 |
785 | case 1: // one-fingered touch: rotate
786 |
787 | if ( scope.enableRotate === false ) return;
788 |
789 | handleTouchStartRotate( event );
790 |
791 | state = STATE.TOUCH_ROTATE;
792 |
793 | break;
794 |
795 | case 2: // two-fingered touch: dolly
796 |
797 | if ( scope.enableZoom === false ) return;
798 |
799 | handleTouchStartDolly( event );
800 |
801 | state = STATE.TOUCH_DOLLY;
802 |
803 | break;
804 |
805 | case 3: // three-fingered touch: pan
806 |
807 | if ( scope.enablePan === false ) return;
808 |
809 | handleTouchStartPan( event );
810 |
811 | state = STATE.TOUCH_PAN;
812 |
813 | break;
814 |
815 | default:
816 |
817 | state = STATE.NONE;
818 |
819 | }
820 |
821 | if ( state !== STATE.NONE ) {
822 |
823 | scope.dispatchEvent( startEvent );
824 |
825 | }
826 |
827 | }
828 |
829 | function onTouchMove( event ) {
830 |
831 | if ( scope.enabled === false ) return;
832 |
833 | event.preventDefault();
834 | event.stopPropagation();
835 |
836 | switch ( event.touches.length ) {
837 |
838 | case 1: // one-fingered touch: rotate
839 |
840 | if ( scope.enableRotate === false ) return;
841 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
842 |
843 | handleTouchMoveRotate( event );
844 |
845 | break;
846 |
847 | case 2: // two-fingered touch: dolly
848 |
849 | if ( scope.enableZoom === false ) return;
850 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
851 |
852 | handleTouchMoveDolly( event );
853 |
854 | break;
855 |
856 | case 3: // three-fingered touch: pan
857 |
858 | if ( scope.enablePan === false ) return;
859 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
860 |
861 | handleTouchMovePan( event );
862 |
863 | break;
864 |
865 | default:
866 |
867 | state = STATE.NONE;
868 |
869 | }
870 |
871 | }
872 |
873 | function onTouchEnd( event ) {
874 |
875 | if ( scope.enabled === false ) return;
876 |
877 | handleTouchEnd( event );
878 |
879 | scope.dispatchEvent( endEvent );
880 |
881 | state = STATE.NONE;
882 |
883 | }
884 |
885 | function onContextMenu( event ) {
886 |
887 | event.preventDefault();
888 |
889 | }
890 |
891 | //
892 |
893 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
894 |
895 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
896 | scope.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
897 | scope.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
898 |
899 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
900 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
901 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
902 |
903 | window.addEventListener( 'keydown', onKeyDown, false );
904 |
905 | // force an update at start
906 |
907 | this.update();
908 |
909 | };
910 |
911 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
912 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
913 |
914 | Object.defineProperties( THREE.OrbitControls.prototype, {
915 |
916 | center: {
917 |
918 | get: function () {
919 |
920 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
921 | return this.target;
922 |
923 | }
924 |
925 | },
926 |
927 | // backward compatibility
928 |
929 | noZoom: {
930 |
931 | get: function () {
932 |
933 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
934 | return ! this.enableZoom;
935 |
936 | },
937 |
938 | set: function ( value ) {
939 |
940 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
941 | this.enableZoom = ! value;
942 |
943 | }
944 |
945 | },
946 |
947 | noRotate: {
948 |
949 | get: function () {
950 |
951 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
952 | return ! this.enableRotate;
953 |
954 | },
955 |
956 | set: function ( value ) {
957 |
958 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
959 | this.enableRotate = ! value;
960 |
961 | }
962 |
963 | },
964 |
965 | noPan: {
966 |
967 | get: function () {
968 |
969 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
970 | return ! this.enablePan;
971 |
972 | },
973 |
974 | set: function ( value ) {
975 |
976 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
977 | this.enablePan = ! value;
978 |
979 | }
980 |
981 | },
982 |
983 | noKeys: {
984 |
985 | get: function () {
986 |
987 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
988 | return ! this.enableKeys;
989 |
990 | },
991 |
992 | set: function ( value ) {
993 |
994 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
995 | this.enableKeys = ! value;
996 |
997 | }
998 |
999 | },
1000 |
1001 | staticMoving : {
1002 |
1003 | get: function () {
1004 |
1005 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1006 | return ! this.enableDamping;
1007 |
1008 | },
1009 |
1010 | set: function ( value ) {
1011 |
1012 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1013 | this.enableDamping = ! value;
1014 |
1015 | }
1016 |
1017 | },
1018 |
1019 | dynamicDampingFactor : {
1020 |
1021 | get: function () {
1022 |
1023 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1024 | return this.dampingFactor;
1025 |
1026 | },
1027 |
1028 | set: function ( value ) {
1029 |
1030 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1031 | this.dampingFactor = value;
1032 |
1033 | }
1034 |
1035 | }
1036 |
1037 | } );
1038 |
--------------------------------------------------------------------------------
/pymo/mocapplayer/playBuffer.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | BVH Player
9 |
10 |
11 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
68 |
69 |
113 |
114 |
414 |
415 |
416 |
417 |
418 |
419 |
--------------------------------------------------------------------------------
/pymo/mocapplayer/playURL.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | BVH Player
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
69 |
70 |
267 |
268 |
269 |
270 |
--------------------------------------------------------------------------------
/pymo/mocapplayer/styles/pace.css:
--------------------------------------------------------------------------------
1 | .pace {
2 | -webkit-pointer-events: none;
3 | pointer-events: none;
4 | -webkit-user-select: none;
5 | -moz-user-select: none;
6 | user-select: none;
7 | }
8 |
9 | .pace-inactive {
10 | display: none;
11 | }
12 |
13 | .pace .pace-progress {
14 | background: #29d;
15 | position: fixed;
16 | z-index: 2000;
17 | top: 0;
18 | right: 100%;
19 | width: 100%;
20 | height: 2px;
21 | }
22 |
23 | .pace .pace-progress-inner {
24 | display: block;
25 | position: absolute;
26 | right: 0px;
27 | width: 100px;
28 | height: 100%;
29 | box-shadow: 0 0 10px #29d, 0 0 5px #29d;
30 | opacity: 1.0;
31 | -webkit-transform: rotate(3deg) translate(0px, -4px);
32 | -moz-transform: rotate(3deg) translate(0px, -4px);
33 | -ms-transform: rotate(3deg) translate(0px, -4px);
34 | -o-transform: rotate(3deg) translate(0px, -4px);
35 | transform: rotate(3deg) translate(0px, -4px);
36 | }
37 |
38 | .pace .pace-activity {
39 | display: block;
40 | position: fixed;
41 | z-index: 2000;
42 | top: 15px;
43 | right: 20px;
44 | width: 34px;
45 | height: 34px;
46 | border: solid 2px transparent;
47 | border-top-color: #9ea7ac;
48 | border-left-color: #9ea7ac;
49 | border-radius: 30px;
50 | -webkit-animation: pace-spinner 700ms linear infinite;
51 | -moz-animation: pace-spinner 700ms linear infinite;
52 | -ms-animation: pace-spinner 700ms linear infinite;
53 | -o-animation: pace-spinner 700ms linear infinite;
54 | animation: pace-spinner 700ms linear infinite;
55 | }
56 |
57 | @-webkit-keyframes pace-spinner {
58 | 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
59 | 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }
60 | }
61 | @-moz-keyframes pace-spinner {
62 | 0% { -moz-transform: rotate(0deg); transform: rotate(0deg); }
63 | 100% { -moz-transform: rotate(360deg); transform: rotate(360deg); }
64 | }
65 | @-o-keyframes pace-spinner {
66 | 0% { -o-transform: rotate(0deg); transform: rotate(0deg); }
67 | 100% { -o-transform: rotate(360deg); transform: rotate(360deg); }
68 | }
69 | @-ms-keyframes pace-spinner {
70 | 0% { -ms-transform: rotate(0deg); transform: rotate(0deg); }
71 | 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); }
72 | }
73 | @keyframes pace-spinner {
74 | 0% { transform: rotate(0deg); transform: rotate(0deg); }
75 | 100% { transform: rotate(360deg); transform: rotate(360deg); }
76 | }
--------------------------------------------------------------------------------
/pymo/parsers.py:
--------------------------------------------------------------------------------
1 | '''
2 | BVH Parser Class
3 |
4 | By Omid Alemi
5 | Created: June 12, 2017
6 |
7 | Based on: https://gist.github.com/johnfredcee/2007503
8 |
9 | '''
10 | import re
11 | import numpy as np
12 | from pymo.data import Joint, MocapData
13 |
14 | class BVHScanner():
15 | '''
16 | A wrapper class for re.Scanner
17 | '''
18 | def __init__(self):
19 |
20 | def identifier(scanner, token):
21 | return 'IDENT', token
22 |
23 | def operator(scanner, token):
24 | return 'OPERATOR', token
25 |
26 | def digit(scanner, token):
27 | return 'DIGIT', token
28 |
29 | def open_brace(scanner, token):
30 | return 'OPEN_BRACE', token
31 |
32 | def close_brace(scanner, token):
33 | return 'CLOSE_BRACE', token
34 |
35 | self.scanner = re.Scanner([
36 | (r'[a-zA-Z_]\w*', identifier),
37 | #(r'-*[0-9]+(\.[0-9]+)?', digit), # won't work for .34
38 | #(r'[-+]?[0-9]*\.?[0-9]+', digit), # won't work for 4.56e-2
39 | #(r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', digit),
40 | (r'-*[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', digit),
41 | (r'}', close_brace),
42 | (r'}', close_brace),
43 | (r'{', open_brace),
44 | (r':', None),
45 | (r'\s+', None)
46 | ])
47 |
48 | def scan(self, stuff):
49 | return self.scanner.scan(stuff)
50 |
51 |
52 |
53 | class BVHParser():
54 | '''
55 | A class to parse a BVH file.
56 |
57 | Extracts the skeleton and channel values
58 | '''
59 | def __init__(self, filename=None):
60 | self.reset()
61 |
62 | def reset(self):
63 | self._skeleton = {}
64 | self.bone_context = []
65 | self._motion_channels = []
66 | self._motions = []
67 | self.current_token = 0
68 | self.framerate = 0.0
69 | self.root_name = ''
70 |
71 | self.scanner = BVHScanner()
72 |
73 | self.data = MocapData()
74 |
75 |
76 | def parse(self, filename):
77 | self.reset()
78 |
79 | with open(filename, 'r') as bvh_file:
80 | raw_contents = bvh_file.read()
81 | tokens, remainder = self.scanner.scan(raw_contents)
82 | self._parse_hierarchy(tokens)
83 | self.current_token = self.current_token + 1
84 | self._parse_motion(tokens)
85 |
86 | self.data.skeleton = self._skeleton
87 | self.data.channel_names = self._motion_channels
88 | self.data.values = self._to_DataFrame()
89 | self.data.root_name = self.root_name
90 | self.data.framerate = self.framerate
91 |
92 | return self.data
93 |
94 | def _to_DataFrame(self):
95 | '''Returns all of the channels parsed from the file as a pandas DataFrame'''
96 |
97 | import pandas as pd
98 | time_index = pd.to_timedelta([f[0] for f in self._motions], unit='s')
99 | frames = [f[1] for f in self._motions]
100 | channels = np.asarray([[channel[2] for channel in frame] for frame in frames])
101 | column_names = ['%s_%s'%(c[0], c[1]) for c in self._motion_channels]
102 |
103 | return pd.DataFrame(data=channels, index=time_index, columns=column_names)
104 |
105 |
106 | def _new_bone(self, parent, name):
107 | bone = {'parent': parent, 'channels': [], 'offsets': [],'children': []}
108 | return bone
109 |
110 | def _push_bone_context(self,name):
111 | self.bone_context.append(name)
112 |
113 | def _get_bone_context(self):
114 | return self.bone_context[len(self.bone_context)-1]
115 |
116 | def _pop_bone_context(self):
117 | self.bone_context = self.bone_context[:-1]
118 | return self.bone_context[len(self.bone_context)-1]
119 |
120 | def _read_offset(self, bvh, token_index):
121 | if bvh[token_index] != ('IDENT', 'OFFSET'):
122 | return None, None
123 | token_index = token_index + 1
124 | offsets = [0.0] * 3
125 | for i in range(3):
126 | offsets[i] = float(bvh[token_index][1])
127 | token_index = token_index + 1
128 | return offsets, token_index
129 |
130 | def _read_channels(self, bvh, token_index):
131 | if bvh[token_index] != ('IDENT', 'CHANNELS'):
132 | return None, None
133 | token_index = token_index + 1
134 | channel_count = int(bvh[token_index][1])
135 | token_index = token_index + 1
136 | channels = [""] * channel_count
137 | for i in range(channel_count):
138 | channels[i] = bvh[token_index][1]
139 | token_index = token_index + 1
140 | return channels, token_index
141 |
142 | def _parse_joint(self, bvh, token_index):
143 | end_site = False
144 | joint_id = bvh[token_index][1]
145 | token_index = token_index + 1
146 | joint_name = bvh[token_index][1]
147 | token_index = token_index + 1
148 |
149 | parent_name = self._get_bone_context()
150 |
151 | if (joint_id == "End"):
152 | joint_name = parent_name+ '_Nub'
153 | end_site = True
154 | joint = self._new_bone(parent_name, joint_name)
155 | if bvh[token_index][0] != 'OPEN_BRACE':
156 | print('Was expecting brance, got ', bvh[token_index])
157 | return None
158 | token_index = token_index + 1
159 | offsets, token_index = self._read_offset(bvh, token_index)
160 | joint['offsets'] = offsets
161 | if not end_site:
162 | channels, token_index = self._read_channels(bvh, token_index)
163 | joint['channels'] = channels
164 | for channel in channels:
165 | self._motion_channels.append((joint_name, channel))
166 |
167 | self._skeleton[joint_name] = joint
168 | self._skeleton[parent_name]['children'].append(joint_name)
169 |
170 | while (bvh[token_index][0] == 'IDENT' and bvh[token_index][1] == 'JOINT') or (bvh[token_index][0] == 'IDENT' and bvh[token_index][1] == 'End'):
171 | self._push_bone_context(joint_name)
172 | token_index = self._parse_joint(bvh, token_index)
173 | self._pop_bone_context()
174 |
175 | if bvh[token_index][0] == 'CLOSE_BRACE':
176 | return token_index + 1
177 |
178 | print('Unexpected token ', bvh[token_index])
179 |
180 | def _parse_hierarchy(self, bvh):
181 | self.current_token = 0
182 | if bvh[self.current_token] != ('IDENT', 'HIERARCHY'):
183 | return None
184 | self.current_token = self.current_token + 1
185 | if bvh[self.current_token] != ('IDENT', 'ROOT'):
186 | return None
187 | self.current_token = self.current_token + 1
188 | if bvh[self.current_token][0] != 'IDENT':
189 | return None
190 |
191 | root_name = bvh[self.current_token][1]
192 | root_bone = self._new_bone(None, root_name)
193 | self.current_token = self.current_token + 2 #skipping open brace
194 | offsets, self.current_token = self._read_offset(bvh, self.current_token)
195 | channels, self.current_token = self._read_channels(bvh, self.current_token)
196 | root_bone['offsets'] = offsets
197 | root_bone['channels'] = channels
198 | self._skeleton[root_name] = root_bone
199 | self._push_bone_context(root_name)
200 |
201 | for channel in channels:
202 | self._motion_channels.append((root_name, channel))
203 |
204 | while bvh[self.current_token][1] == 'JOINT':
205 | self.current_token = self._parse_joint(bvh, self.current_token)
206 |
207 | self.root_name = root_name
208 |
209 | def _parse_motion(self, bvh):
210 | if bvh[self.current_token][0] != 'IDENT':
211 | print('Unexpected text')
212 | return None
213 | if bvh[self.current_token][1] != 'MOTION':
214 | print('No motion section')
215 | return None
216 | self.current_token = self.current_token + 1
217 | if bvh[self.current_token][1] != 'Frames':
218 | return None
219 | self.current_token = self.current_token + 1
220 | frame_count = int(bvh[self.current_token][1])
221 | self.current_token = self.current_token + 1
222 | if bvh[self.current_token][1] != 'Frame':
223 | return None
224 | self.current_token = self.current_token + 1
225 | if bvh[self.current_token][1] != 'Time':
226 | return None
227 | self.current_token = self.current_token + 1
228 | frame_rate = float(bvh[self.current_token][1])
229 |
230 | self.framerate = frame_rate
231 |
232 | self.current_token = self.current_token + 1
233 |
234 | frame_time = 0.0
235 | self._motions = [()] * frame_count
236 | for i in range(frame_count):
237 | channel_values = []
238 | for channel in self._motion_channels:
239 | channel_values.append((channel[0], channel[1], float(bvh[self.current_token][1])))
240 | self.current_token = self.current_token + 1
241 | self._motions[i] = (frame_time, channel_values)
242 | frame_time = frame_time + frame_rate
243 |
--------------------------------------------------------------------------------
/pymo/preprocessing.py:
--------------------------------------------------------------------------------
1 | '''
2 | Preprocessing Tranformers Based on sci-kit's API
3 |
4 | By Omid Alemi
5 | Created on June 12, 2017
6 | '''
7 | import copy
8 | import pandas as pd
9 | import numpy as np
10 | from sklearn.base import BaseEstimator, TransformerMixin
11 |
12 | from pymo.rotation_tools import Rotation
13 |
14 | class MocapParameterizer(BaseEstimator, TransformerMixin):
15 | def __init__(self, param_type = 'euler'):
16 | '''
17 |
18 | param_type = {'euler', 'quat', 'expmap', 'position'}
19 | '''
20 | self.param_type = param_type
21 |
22 | def fit(self, X, y=None):
23 | return self
24 |
25 | def transform(self, X, y=None):
26 | if self.param_type == 'euler':
27 | return X
28 | elif self.param_type == 'expmap':
29 | return self._to_expmap(X)
30 | elif self.param_type == 'quat':
31 | return X
32 | elif self.param_type == 'position':
33 | return self._to_pos(X)
34 | else:
35 | raise UnsupportedParamError('Unsupported param: %s. Valid param types are: euler, quat, expmap, position' % self.param_type)
36 | # return X
37 |
38 | def inverse_transform(self, X, copy=None):
39 | if self.param_type == 'euler':
40 | return X
41 | elif self.param_type == 'expmap':
42 | return self._expmap_to_euler(X)
43 | elif self.param_type == 'quat':
44 | raise UnsupportedParamError('quat2euler is not supported')
45 | elif self.param_type == 'position':
46 | print('positions 2 eulers is not supported')
47 | return X
48 | else:
49 | raise UnsupportedParamError('Unsupported param: %s. Valid param types are: euler, quat, expmap, position' % self.param_type)
50 |
51 | def _to_pos(self, X):
52 | '''Converts joints rotations in Euler angles to joint positions'''
53 |
54 | Q = []
55 | for track in X:
56 | channels = []
57 | titles = []
58 | euler_df = track.values
59 |
60 | # Create a new DataFrame to store the exponential map rep
61 | pos_df = pd.DataFrame(index=euler_df.index)
62 |
63 | # Copy the root rotations into the new DataFrame
64 | # rxp = '%s_Xrotation'%track.root_name
65 | # ryp = '%s_Yrotation'%track.root_name
66 | # rzp = '%s_Zrotation'%track.root_name
67 | # pos_df[rxp] = pd.Series(data=euler_df[rxp], index=pos_df.index)
68 | # pos_df[ryp] = pd.Series(data=euler_df[ryp], index=pos_df.index)
69 | # pos_df[rzp] = pd.Series(data=euler_df[rzp], index=pos_df.index)
70 |
71 | # List the columns that contain rotation channels
72 | rot_cols = [c for c in euler_df.columns if ('rotation' in c)]
73 |
74 | # List the columns that contain position channels
75 | pos_cols = [c for c in euler_df.columns if ('position' in c)]
76 |
77 | # List the joints that are not end sites, i.e., have channels
78 | joints = (joint for joint in track.skeleton)
79 |
80 | tree_data = {}
81 |
82 | for joint in track.traverse():
83 | parent = track.skeleton[joint]['parent']
84 |
85 | # Get the rotation columns that belong to this joint
86 | rc = euler_df[[c for c in rot_cols if joint in c]]
87 |
88 | # Get the position columns that belong to this joint
89 | pc = euler_df[[c for c in pos_cols if joint in c]]
90 |
91 | # Make sure the columns are organized in xyz order
92 | if rc.shape[1] < 3:
93 | euler_values = [[0,0,0] for f in rc.iterrows()]
94 | else:
95 | euler_values = [[f[1]['%s_Xrotation'%joint],
96 | f[1]['%s_Yrotation'%joint],
97 | f[1]['%s_Zrotation'%joint]] for f in rc.iterrows()]
98 |
99 | ################# in euler angle, the order of rotation axis is very important #####################
100 | rotation_order = rc.columns[0][rc.columns[0].find('rotation') - 1] + rc.columns[1][rc.columns[1].find('rotation') - 1] + rc.columns[2][rc.columns[2].find('rotation') - 1] #rotation_order is string : 'XYZ' or'ZYX' or ...
101 | ####################################################################################################
102 |
103 | if pc.shape[1] < 3:
104 | pos_values = [[0,0,0] for f in pc.iterrows()]
105 | else:
106 | pos_values =[[f[1]['%s_Xposition'%joint],
107 | f[1]['%s_Yposition'%joint],
108 | f[1]['%s_Zposition'%joint]] for f in pc.iterrows()]
109 |
110 | #euler_values = [[0,0,0] for f in rc.iterrows()] #for deugging
111 | #pos_values = [[0,0,0] for f in pc.iterrows()] #for deugging
112 |
113 | # Convert the eulers to rotation matrices
114 | ############################ input rotation order as Rotation class's argument #########################
115 | rotmats = np.asarray([Rotation([f[0], f[1], f[2]], 'euler', rotation_order, from_deg=True).rotmat for f in euler_values])
116 | ########################################################################################################
117 | tree_data[joint]=[
118 | [], # to store the rotation matrix
119 | [] # to store the calculated position
120 | ]
121 |
122 | if track.root_name == joint:
123 | tree_data[joint][0] = rotmats
124 | # tree_data[joint][1] = np.add(pos_values, track.skeleton[joint]['offsets'])
125 | tree_data[joint][1] = pos_values
126 | else:
127 | # for every frame i, multiply this joint's rotmat to the rotmat of its parent
128 | tree_data[joint][0] = np.asarray([np.matmul(rotmats[i], tree_data[parent][0][i])
129 | for i in range(len(tree_data[parent][0]))])
130 |
131 | # add the position channel to the offset and store it in k, for every frame i
132 | k = np.asarray([np.add(pos_values[i], track.skeleton[joint]['offsets'])
133 | for i in range(len(tree_data[parent][0]))])
134 |
135 | # multiply k to the rotmat of the parent for every frame i
136 | q = np.asarray([np.matmul(k[i], tree_data[parent][0][i])
137 | for i in range(len(tree_data[parent][0]))])
138 |
139 | # add q to the position of the parent, for every frame i
140 | tree_data[joint][1] = np.asarray([np.add(q[i], tree_data[parent][1][i])
141 | for i in range(len(tree_data[parent][1]))])
142 |
143 |
144 | # Create the corresponding columns in the new DataFrame
145 | pos_df['%s_Xposition'%joint] = pd.Series(data=[e[0] for e in tree_data[joint][1]], index=pos_df.index)
146 | pos_df['%s_Yposition'%joint] = pd.Series(data=[e[1] for e in tree_data[joint][1]], index=pos_df.index)
147 | pos_df['%s_Zposition'%joint] = pd.Series(data=[e[2] for e in tree_data[joint][1]], index=pos_df.index)
148 |
149 |
150 | new_track = track.clone()
151 | new_track.values = pos_df
152 | Q.append(new_track)
153 | return Q
154 |
155 |
156 | def _to_expmap(self, X):
157 | '''Converts Euler angles to Exponential Maps'''
158 |
159 | Q = []
160 | for track in X:
161 | channels = []
162 | titles = []
163 | euler_df = track.values
164 |
165 | # Create a new DataFrame to store the exponential map rep
166 | exp_df = pd.DataFrame(index=euler_df.index)
167 |
168 | # Copy the root positions into the new DataFrame
169 | rxp = '%s_Xposition'%track.root_name
170 | ryp = '%s_Yposition'%track.root_name
171 | rzp = '%s_Zposition'%track.root_name
172 | exp_df[rxp] = pd.Series(data=euler_df[rxp], index=exp_df.index)
173 | exp_df[ryp] = pd.Series(data=euler_df[ryp], index=exp_df.index)
174 | exp_df[rzp] = pd.Series(data=euler_df[rzp], index=exp_df.index)
175 |
176 | # List the columns that contain rotation channels
177 | rots = [c for c in euler_df.columns if ('rotation' in c and 'Nub' not in c)]
178 |
179 | # List the joints that are not end sites, i.e., have channels
180 | joints = (joint for joint in track.skeleton if 'Nub' not in joint)
181 |
182 | for joint in joints:
183 | r = euler_df[[c for c in rots if joint in c]] # Get the columns that belong to this joint
184 | euler = [[f[1]['%s_Xrotation'%joint], f[1]['%s_Yrotation'%joint], f[1]['%s_Zrotation'%joint]] for f in r.iterrows()] # Make sure the columsn are organized in xyz order
185 | exps = [Rotation(f, 'euler', from_deg=True).to_expmap() for f in euler] # Convert the eulers to exp maps
186 |
187 | # Create the corresponding columns in the new DataFrame
188 |
189 | exp_df['%s_alpha'%joint] = pd.Series(data=[e[0] for e in exps], index=exp_df.index)
190 | exp_df['%s_beta'%joint] = pd.Series(data=[e[1] for e in exps], index=exp_df.index)
191 | exp_df['%s_gamma'%joint] = pd.Series(data=[e[2] for e in exps], index=exp_df.index)
192 |
193 | new_track = track.clone()
194 | new_track.values = exp_df
195 | Q.append(new_track)
196 |
197 | return Q
198 |
199 | def _expmap_to_euler(self, X):
200 | Q = []
201 | for track in X:
202 | channels = []
203 | titles = []
204 | exp_df = track.values
205 |
206 | # Create a new DataFrame to store the exponential map rep
207 | euler_df = pd.DataFrame(index=exp_df.index)
208 |
209 | # Copy the root positions into the new DataFrame
210 | rxp = '%s_Xposition'%track.root_name
211 | ryp = '%s_Yposition'%track.root_name
212 | rzp = '%s_Zposition'%track.root_name
213 | euler_df[rxp] = pd.Series(data=exp_df[rxp], index=euler_df.index)
214 | euler_df[ryp] = pd.Series(data=exp_df[ryp], index=euler_df.index)
215 | euler_df[rzp] = pd.Series(data=exp_df[rzp], index=euler_df.index)
216 |
217 | # List the columns that contain rotation channels
218 | exp_params = [c for c in exp_df.columns if ( any(p in c for p in ['alpha', 'beta','gamma']) and 'Nub' not in c)]
219 |
220 | # List the joints that are not end sites, i.e., have channels
221 | joints = (joint for joint in track.skeleton if 'Nub' not in joint)
222 |
223 | for joint in joints:
224 | r = exp_df[[c for c in exp_params if joint in c]] # Get the columns that belong to this joint
225 | expmap = [[f[1]['%s_alpha'%joint], f[1]['%s_beta'%joint], f[1]['%s_gamma'%joint]] for f in r.iterrows()] # Make sure the columsn are organized in xyz order
226 | euler_rots = [Rotation(f, 'expmap').to_euler(True)[0] for f in expmap] # Convert the eulers to exp maps
227 |
228 | # Create the corresponding columns in the new DataFrame
229 |
230 | euler_df['%s_Xrotation'%joint] = pd.Series(data=[e[0] for e in euler_rots], index=euler_df.index)
231 | euler_df['%s_Yrotation'%joint] = pd.Series(data=[e[1] for e in euler_rots], index=euler_df.index)
232 | euler_df['%s_Zrotation'%joint] = pd.Series(data=[e[2] for e in euler_rots], index=euler_df.index)
233 |
234 | new_track = track.clone()
235 | new_track.values = euler_df
236 | Q.append(new_track)
237 |
238 | return Q
239 |
240 |
241 | class JointSelector(BaseEstimator, TransformerMixin):
242 | '''
243 | Allows for filtering the mocap data to include only the selected joints
244 | '''
245 | def __init__(self, joints, include_root=False):
246 | self.joints = joints
247 | self.include_root = include_root
248 |
249 | def fit(self, X, y=None):
250 | return self
251 |
252 | def transform(self, X, y=None):
253 | selected_joints = []
254 | selected_channels = []
255 |
256 | if self.include_root:
257 | selected_joints.append(X[0].root_name)
258 |
259 | selected_joints.extend(self.joints)
260 |
261 | for joint_name in selected_joints:
262 | selected_channels.extend([o for o in X[0].values.columns if joint_name in o])
263 |
264 | Q = []
265 |
266 |
267 | for track in X:
268 | t2 = track.clone()
269 |
270 | for key in track.skeleton.keys():
271 | if key not in selected_joints:
272 | t2.skeleton.pop(key)
273 | t2.values = track.values[selected_channels]
274 |
275 | Q.append(t2)
276 |
277 |
278 | return Q
279 |
280 |
281 | class Numpyfier(BaseEstimator, TransformerMixin):
282 | '''
283 | Just converts the values in a MocapData object into a numpy array
284 | Useful for the final stage of a pipeline before training
285 | '''
286 | def __init__(self):
287 | pass
288 |
289 | def fit(self, X, y=None):
290 | self.org_mocap_ = X[0].clone()
291 | self.org_mocap_.values.drop(self.org_mocap_.values.index, inplace=True)
292 |
293 | return self
294 |
295 | def transform(self, X, y=None):
296 | Q = []
297 |
298 | for track in X:
299 | Q.append(track.values.values)
300 |
301 | return np.array(Q)
302 |
303 | def inverse_transform(self, X, copy=None):
304 | Q = []
305 |
306 | for track in X:
307 |
308 | new_mocap = self.org_mocap_.clone()
309 | time_index = pd.to_timedelta([f for f in range(track.shape[0])], unit='s')
310 |
311 | new_df = pd.DataFrame(data=track, index=time_index, columns=self.org_mocap_.values.columns)
312 |
313 | new_mocap.values = new_df
314 |
315 |
316 | Q.append(new_mocap)
317 |
318 | return Q
319 |
320 | class RootTransformer(BaseEstimator, TransformerMixin):
321 | def __init__(self, method):
322 | """
323 | Accepted methods:
324 | abdolute_translation_deltas
325 | pos_rot_deltas
326 | """
327 | self.method = method
328 |
329 | def fit(self, X, y=None):
330 | return self
331 |
332 | def transform(self, X, y=None):
333 | Q = []
334 |
335 | for track in X:
336 | if self.method == 'abdolute_translation_deltas':
337 | new_df = track.values.copy()
338 | xpcol = '%s_Xposition'%track.root_name
339 | ypcol = '%s_Yposition'%track.root_name
340 | zpcol = '%s_Zposition'%track.root_name
341 |
342 |
343 | dxpcol = '%s_dXposition'%track.root_name
344 | dzpcol = '%s_dZposition'%track.root_name
345 |
346 | dx = track.values[xpcol].diff()
347 | dz = track.values[zpcol].diff()
348 |
349 | dx[0] = 0
350 | dz[0] = 0
351 |
352 | new_df.drop([xpcol, zpcol], axis=1, inplace=True)
353 |
354 | new_df[dxpcol] = dx
355 | new_df[dzpcol] = dz
356 |
357 | new_track = track.clone()
358 | new_track.values = new_df
359 | # end of abdolute_translation_deltas
360 |
361 | elif self.method == 'pos_rot_deltas':
362 | new_track = track.clone()
363 |
364 | # Absolute columns
365 | xp_col = '%s_Xposition'%track.root_name
366 | yp_col = '%s_Yposition'%track.root_name
367 | zp_col = '%s_Zposition'%track.root_name
368 |
369 | xr_col = '%s_Xrotation'%track.root_name
370 | yr_col = '%s_Yrotation'%track.root_name
371 | zr_col = '%s_Zrotation'%track.root_name
372 |
373 | # Delta columns
374 | dxp_col = '%s_dXposition'%track.root_name
375 | dzp_col = '%s_dZposition'%track.root_name
376 |
377 | dxr_col = '%s_dXrotation'%track.root_name
378 | dyr_col = '%s_dYrotation'%track.root_name
379 | dzr_col = '%s_dZrotation'%track.root_name
380 |
381 |
382 | new_df = track.values.copy()
383 |
384 | root_pos_x_diff = pd.Series(data=track.values[xp_col].diff(), index=new_df.index)
385 | root_pos_z_diff = pd.Series(data=track.values[zp_col].diff(), index=new_df.index)
386 |
387 | root_rot_y_diff = pd.Series(data=track.values[yr_col].diff(), index=new_df.index)
388 | root_rot_x_diff = pd.Series(data=track.values[xr_col].diff(), index=new_df.index)
389 | root_rot_z_diff = pd.Series(data=track.values[zr_col].diff(), index=new_df.index)
390 |
391 |
392 | root_pos_x_diff[0] = 0
393 | root_pos_z_diff[0] = 0
394 |
395 | root_rot_y_diff[0] = 0
396 | root_rot_x_diff[0] = 0
397 | root_rot_z_diff[0] = 0
398 |
399 | new_df.drop([xr_col, yr_col, zr_col, xp_col, zp_col], axis=1, inplace=True)
400 |
401 | new_df[dxp_col] = root_pos_x_diff
402 | new_df[dzp_col] = root_pos_z_diff
403 |
404 | new_df[dxr_col] = root_rot_x_diff
405 | new_df[dyr_col] = root_rot_y_diff
406 | new_df[dzr_col] = root_rot_z_diff
407 |
408 | new_track.values = new_df
409 |
410 | Q.append(new_track)
411 |
412 | return Q
413 |
414 | def inverse_transform(self, X, copy=None, start_pos=None):
415 | Q = []
416 |
417 | #TODO: simplify this implementation
418 |
419 | startx = 0
420 | startz = 0
421 |
422 | if start_pos is not None:
423 | startx, startz = start_pos
424 |
425 | for track in X:
426 | new_track = track.clone()
427 | if self.method == 'abdolute_translation_deltas':
428 | new_df = new_track.values
429 | xpcol = '%s_Xposition'%track.root_name
430 | ypcol = '%s_Yposition'%track.root_name
431 | zpcol = '%s_Zposition'%track.root_name
432 |
433 |
434 | dxpcol = '%s_dXposition'%track.root_name
435 | dzpcol = '%s_dZposition'%track.root_name
436 |
437 | dx = track.values[dxpcol].values
438 | dz = track.values[dzpcol].values
439 |
440 | recx = [startx]
441 | recz = [startz]
442 |
443 | for i in range(dx.shape[0]-1):
444 | recx.append(recx[i]+dx[i+1])
445 | recz.append(recz[i]+dz[i+1])
446 |
447 | # recx = [recx[i]+dx[i+1] for i in range(dx.shape[0]-1)]
448 | # recz = [recz[i]+dz[i+1] for i in range(dz.shape[0]-1)]
449 | # recx = dx[:-1] + dx[1:]
450 | # recz = dz[:-1] + dz[1:]
451 |
452 | new_df[xpcol] = pd.Series(data=recx, index=new_df.index)
453 | new_df[zpcol] = pd.Series(data=recz, index=new_df.index)
454 |
455 | new_df.drop([dxpcol, dzpcol], axis=1, inplace=True)
456 |
457 | new_track.values = new_df
458 | # end of abdolute_translation_deltas
459 |
460 | elif self.method == 'pos_rot_deltas':
461 | new_track = track.clone()
462 |
463 | # Absolute columns
464 | xp_col = '%s_Xposition'%track.root_name
465 | yp_col = '%s_Yposition'%track.root_name
466 | zp_col = '%s_Zposition'%track.root_name
467 |
468 | xr_col = '%s_Xrotation'%track.root_name
469 | yr_col = '%s_Yrotation'%track.root_name
470 | zr_col = '%s_Zrotation'%track.root_name
471 |
472 | # Delta columns
473 | dxp_col = '%s_dXposition'%track.root_name
474 | dzp_col = '%s_dZposition'%track.root_name
475 |
476 | dxr_col = '%s_dXrotation'%track.root_name
477 | dyr_col = '%s_dYrotation'%track.root_name
478 | dzr_col = '%s_dZrotation'%track.root_name
479 |
480 |
481 | new_df = track.values.copy()
482 |
483 | dx = track.values[dxp_col].values
484 | dz = track.values[dzp_col].values
485 |
486 | drx = track.values[dxr_col].values
487 | dry = track.values[dyr_col].values
488 | drz = track.values[dzr_col].values
489 |
490 | rec_xp = [startx]
491 | rec_zp = [startz]
492 |
493 | rec_xr = [0]
494 | rec_yr = [0]
495 | rec_zr = [0]
496 |
497 |
498 | for i in range(dx.shape[0]-1):
499 | rec_xp.append(rec_xp[i]+dx[i+1])
500 | rec_zp.append(rec_zp[i]+dz[i+1])
501 |
502 | rec_xr.append(rec_xr[i]+drx[i+1])
503 | rec_yr.append(rec_yr[i]+dry[i+1])
504 | rec_zr.append(rec_zr[i]+drz[i+1])
505 |
506 |
507 | new_df[xp_col] = pd.Series(data=rec_xp, index=new_df.index)
508 | new_df[zp_col] = pd.Series(data=rec_zp, index=new_df.index)
509 |
510 | new_df[xr_col] = pd.Series(data=rec_xr, index=new_df.index)
511 | new_df[yr_col] = pd.Series(data=rec_yr, index=new_df.index)
512 | new_df[zr_col] = pd.Series(data=rec_zr, index=new_df.index)
513 |
514 | new_df.drop([dxr_col, dyr_col, dzr_col, dxp_col, dzp_col], axis=1, inplace=True)
515 |
516 |
517 | new_track.values = new_df
518 |
519 | Q.append(new_track)
520 |
521 | return Q
522 |
523 |
524 | class RootCentricPositionNormalizer(BaseEstimator, TransformerMixin):
525 | def __init__(self):
526 | pass
527 |
528 | def fit(self, X, y=None):
529 | return self
530 |
531 | def transform(self, X, y=None):
532 | Q = []
533 |
534 | for track in X:
535 | new_track = track.clone()
536 |
537 | rxp = '%s_Xposition'%track.root_name
538 | ryp = '%s_Yposition'%track.root_name
539 | rzp = '%s_Zposition'%track.root_name
540 |
541 | projected_root_pos = track.values[[rxp, ryp, rzp]]
542 |
543 | projected_root_pos.loc[:,ryp] = 0 # we want the root's projection on the floor plane as the ref
544 |
545 | new_df = pd.DataFrame(index=track.values.index)
546 |
547 | all_but_root = [joint for joint in track.skeleton if track.root_name not in joint]
548 | # all_but_root = [joint for joint in track.skeleton]
549 | for joint in all_but_root:
550 | new_df['%s_Xposition'%joint] = pd.Series(data=track.values['%s_Xposition'%joint]-projected_root_pos[rxp], index=new_df.index)
551 | new_df['%s_Yposition'%joint] = pd.Series(data=track.values['%s_Yposition'%joint]-projected_root_pos[ryp], index=new_df.index)
552 | new_df['%s_Zposition'%joint] = pd.Series(data=track.values['%s_Zposition'%joint]-projected_root_pos[rzp], index=new_df.index)
553 |
554 |
555 | # keep the root as it is now
556 | new_df[rxp] = track.values[rxp]
557 | new_df[ryp] = track.values[ryp]
558 | new_df[rzp] = track.values[rzp]
559 |
560 | new_track.values = new_df
561 |
562 | Q.append(new_track)
563 |
564 | return Q
565 |
566 | def inverse_transform(self, X, copy=None):
567 | Q = []
568 |
569 | for track in X:
570 | new_track = track.clone()
571 |
572 | rxp = '%s_Xposition'%track.root_name
573 | ryp = '%s_Yposition'%track.root_name
574 | rzp = '%s_Zposition'%track.root_name
575 |
576 | projected_root_pos = track.values[[rxp, ryp, rzp]]
577 |
578 | projected_root_pos.loc[:,ryp] = 0 # we want the root's projection on the floor plane as the ref
579 |
580 | new_df = pd.DataFrame(index=track.values.index)
581 |
582 | for joint in track.skeleton:
583 | new_df['%s_Xposition'%joint] = pd.Series(data=track.values['%s_Xposition'%joint]+projected_root_pos[rxp], index=new_df.index)
584 | new_df['%s_Yposition'%joint] = pd.Series(data=track.values['%s_Yposition'%joint]+projected_root_pos[ryp], index=new_df.index)
585 | new_df['%s_Zposition'%joint] = pd.Series(data=track.values['%s_Zposition'%joint]+projected_root_pos[rzp], index=new_df.index)
586 |
587 |
588 | new_track.values = new_df
589 |
590 | Q.append(new_track)
591 |
592 | return Q
593 |
594 |
595 | class Flattener(BaseEstimator, TransformerMixin):
596 | def __init__(self):
597 | pass
598 |
599 | def fit(self, X, y=None):
600 | return self
601 |
602 | def transform(self, X, y=None):
603 | return np.concatenate(X, axis=0)
604 |
605 | class ConstantsRemover(BaseEstimator, TransformerMixin):
606 | '''
607 | For now it just looks at the first track
608 | '''
609 |
610 | def __init__(self, eps = 10e-10):
611 | self.eps = eps
612 |
613 |
614 | def fit(self, X, y=None):
615 | stds = X[0].values.std()
616 | cols = X[0].values.columns.values
617 | self.const_dims_ = [c for c in cols if (stds[c] < self.eps).any()]
618 | self.const_values_ = {c:X[0].values[c].values[0] for c in cols if (stds[c] < self.eps).any()}
619 | return self
620 |
621 | def transform(self, X, y=None):
622 | Q = []
623 |
624 |
625 | for track in X:
626 | t2 = track.clone()
627 | #for key in t2.skeleton.keys():
628 | # if key in self.ConstDims_:
629 | # t2.skeleton.pop(key)
630 | t2.values = track.values[track.values.columns.difference(self.const_dims_)]
631 | Q.append(t2)
632 |
633 | return Q
634 |
635 | def inverse_transform(self, X, copy=None):
636 | Q = []
637 |
638 | for track in X:
639 | t2 = track.clone()
640 | for d in self.const_dims_:
641 | t2.values[d] = self.const_values_[d]
642 | Q.append(t2)
643 |
644 | return Q
645 |
646 | class ListStandardScaler(BaseEstimator, TransformerMixin):
647 | def __init__(self, is_DataFrame=False):
648 | self.is_DataFrame = is_DataFrame
649 |
650 | def fit(self, X, y=None):
651 | if self.is_DataFrame:
652 | X_train_flat = np.concatenate([m.values for m in X], axis=0)
653 | else:
654 | X_train_flat = np.concatenate([m for m in X], axis=0)
655 |
656 | self.data_mean_ = np.mean(X_train_flat, axis=0)
657 | self.data_std_ = np.std(X_train_flat, axis=0)
658 |
659 | return self
660 |
661 | def transform(self, X, y=None):
662 | Q = []
663 |
664 | for track in X:
665 | if self.is_DataFrame:
666 | normalized_track = track.copy()
667 | normalized_track.values = (track.values - self.data_mean_) / self.data_std_
668 | else:
669 | normalized_track = (track - self.data_mean_) / self.data_std_
670 |
671 | Q.append(normalized_track)
672 |
673 | if self.is_DataFrame:
674 | return Q
675 | else:
676 | return np.array(Q)
677 |
678 | def inverse_transform(self, X, copy=None):
679 | Q = []
680 |
681 | for track in X:
682 |
683 | if self.is_DataFrame:
684 | unnormalized_track = track.copy()
685 | unnormalized_track.values = (track.values * self.data_std_) + self.data_mean_
686 | else:
687 | unnormalized_track = (track * self.data_std_) + self.data_mean_
688 |
689 | Q.append(unnormalized_track)
690 |
691 | if self.is_DataFrame:
692 | return Q
693 | else:
694 | return np.array(Q)
695 |
696 | class DownSampler(BaseEstimator, TransformerMixin):
697 | def __init__(self, rate):
698 | self.rate = rate
699 |
700 |
701 | def fit(self, X, y=None):
702 |
703 | return self
704 |
705 | def transform(self, X, y=None):
706 | Q = []
707 |
708 | for track in X:
709 | #print(track.values.size)
710 | #new_track = track.clone()
711 | #new_track.values = track.values[0:-1:self.rate]
712 | #print(new_track.values.size)
713 | new_track = track[0:-1:self.rate]
714 | Q.append(new_track)
715 |
716 | return Q
717 |
718 | def inverse_transform(self, X, copy=None):
719 | return X
720 |
721 |
722 | #TODO: JointsSelector (x)
723 | #TODO: SegmentMaker
724 | #TODO: DynamicFeaturesAdder
725 | #TODO: ShapeFeaturesAdder
726 | #TODO: DataFrameNumpier (x)
727 |
728 | class TemplateTransform(BaseEstimator, TransformerMixin):
729 | def __init__(self):
730 | pass
731 |
732 | def fit(self, X, y=None):
733 | return self
734 |
735 | def transform(self, X, y=None):
736 | return X
737 |
738 | class UnsupportedParamError(Exception):
739 | def __init__(self, message):
740 | self.message = message
741 |
--------------------------------------------------------------------------------
/pymo/rotation_tools.py:
--------------------------------------------------------------------------------
1 | '''
2 | Tools for Manipulating and Converting 3D Rotations
3 |
4 | By Omid Alemi
5 | Created: June 12, 2017
6 |
7 | Adapted from that matlab file...
8 | '''
9 |
10 | import math
11 | import numpy as np
12 |
13 | def deg2rad(x):
14 | return x/180*math.pi
15 |
16 |
17 | def rad2deg(x):
18 | return x/math.pi*180
19 |
20 | class Rotation():
21 | def __init__(self,rot, param_type, rotation_order, **params):
22 | self.rotmat = []
23 | self.rotation_order = rotation_order
24 | if param_type == 'euler':
25 | self._from_euler(rot[0],rot[1],rot[2], params)
26 | elif param_type == 'expmap':
27 | self._from_expmap(rot[0], rot[1], rot[2], params)
28 |
29 | def _from_euler(self, alpha, beta, gamma, params):
30 | '''Expecting degress'''
31 |
32 | if params['from_deg']==True:
33 | alpha = deg2rad(alpha)
34 | beta = deg2rad(beta)
35 | gamma = deg2rad(gamma)
36 |
37 | ca = math.cos(alpha)
38 | cb = math.cos(beta)
39 | cg = math.cos(gamma)
40 | sa = math.sin(alpha)
41 | sb = math.sin(beta)
42 | sg = math.sin(gamma)
43 |
44 | Rx = np.asarray([[1, 0, 0],
45 | [0, ca, sa],
46 | [0, -sa, ca]
47 | ])
48 |
49 | Ry = np.asarray([[cb, 0, -sb],
50 | [0, 1, 0],
51 | [sb, 0, cb]])
52 |
53 | Rz = np.asarray([[cg, sg, 0],
54 | [-sg, cg, 0],
55 | [0, 0, 1]])
56 |
57 | self.rotmat = np.eye(3)
58 |
59 | ############################ inner product rotation matrix in order defined at BVH file #########################
60 | for axis in self.rotation_order :
61 | if axis == 'X' :
62 | self.rotmat = np.matmul(Rx, self.rotmat)
63 | elif axis == 'Y':
64 | self.rotmat = np.matmul(Ry, self.rotmat)
65 | else :
66 | self.rotmat = np.matmul(Rz, self.rotmat)
67 | ################################################################################################################
68 |
69 | def _from_expmap(self, alpha, beta, gamma, params):
70 | if (alpha == 0 and beta == 0 and gamma == 0):
71 | self.rotmat = np.eye(3)
72 | return
73 |
74 | #TODO: Check exp map params
75 |
76 | theta = np.linalg.norm([alpha, beta, gamma])
77 |
78 | expmap = [alpha, beta, gamma] / theta
79 |
80 | x = expmap[0]
81 | y = expmap[1]
82 | z = expmap[2]
83 |
84 | s = math.sin(theta/2)
85 | c = math.cos(theta/2)
86 |
87 | self.rotmat = np.asarray([
88 | [2*(x**2-1)*s**2+1, 2*x*y*s**2-2*z*c*s, 2*x*z*s**2+2*y*c*s],
89 | [2*x*y*s**2+2*z*c*s, 2*(y**2-1)*s**2+1, 2*y*z*s**2-2*x*c*s],
90 | [2*x*z*s**2-2*y*c*s, 2*y*z*s**2+2*x*c*s , 2*(z**2-1)*s**2+1]
91 | ])
92 |
93 |
94 |
95 | def get_euler_axis(self):
96 | R = self.rotmat
97 | theta = math.acos((self.rotmat.trace() - 1) / 2)
98 | axis = np.asarray([R[2,1] - R[1,2], R[0,2] - R[2,0], R[1,0] - R[0,1]])
99 | axis = axis/(2*math.sin(theta))
100 | return theta, axis
101 |
102 | def to_expmap(self):
103 | theta, axis = self.get_euler_axis()
104 | rot_arr = theta * axis
105 | if np.isnan(rot_arr).any():
106 | rot_arr = [0, 0, 0]
107 | return rot_arr
108 |
109 | def to_euler(self, use_deg=False):
110 | eulers = np.zeros((2, 3))
111 |
112 | if np.absolute(np.absolute(self.rotmat[2, 0]) - 1) < 1e-12:
113 | #GIMBAL LOCK!
114 | print('Gimbal')
115 | if np.absolute(self.rotmat[2, 0]) - 1 < 1e-12:
116 | eulers[:,0] = math.atan2(-self.rotmat[0,1], -self.rotmat[0,2])
117 | eulers[:,1] = -math.pi/2
118 | else:
119 | eulers[:,0] = math.atan2(self.rotmat[0,1], -elf.rotmat[0,2])
120 | eulers[:,1] = math.pi/2
121 |
122 | return eulers
123 |
124 | theta = - math.asin(self.rotmat[2,0])
125 | theta2 = math.pi - theta
126 |
127 | # psi1, psi2
128 | eulers[0,0] = math.atan2(self.rotmat[2,1]/math.cos(theta), self.rotmat[2,2]/math.cos(theta))
129 | eulers[1,0] = math.atan2(self.rotmat[2,1]/math.cos(theta2), self.rotmat[2,2]/math.cos(theta2))
130 |
131 | # theta1, theta2
132 | eulers[0,1] = theta
133 | eulers[1,1] = theta2
134 |
135 | # phi1, phi2
136 | eulers[0,2] = math.atan2(self.rotmat[1,0]/math.cos(theta), self.rotmat[0,0]/math.cos(theta))
137 | eulers[1,2] = math.atan2(self.rotmat[1,0]/math.cos(theta2), self.rotmat[0,0]/math.cos(theta2))
138 |
139 | if use_deg:
140 | eulers = rad2deg(eulers)
141 |
142 | return eulers
143 |
144 | def to_quat(self):
145 | #TODO
146 | pass
147 |
148 | def __str__(self):
149 | return "Rotation Matrix: \n " + self.rotmat.__str__()
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/pymo/rotation_tools.py!:
--------------------------------------------------------------------------------
1 | '''
2 | Tools for Manipulating and Converting 3D Rotations
3 |
4 | By Omid Alemi
5 | Created: June 12, 2017
6 |
7 | Adapted from that matlab file...
8 | '''
9 |
10 | import math
11 | import numpy as np
12 |
13 | def deg2rad(x):
14 | return x/180*math.pi
15 |
16 | class Rotation():
17 | def __init__(self,rot, param_type, **params):
18 | self.rotmat = []
19 | if param_type == 'euler':
20 | self._from_euler(rot[0],rot[1],rot[2], params)
21 |
22 | def _from_euler(self, alpha, beta, gamma, params):
23 | '''Expecting degress'''
24 |
25 | if params['from_deg']==True:
26 | alpha = deg2rad(alpha)
27 | beta = deg2rad(beta)
28 | gamma = deg2rad(gamma)
29 |
30 | Rx = np.asarray([[1, 0, 0],
31 | [0, math.cos(alpha), -math.sin(alpha)],
32 | [0, math.sin(alpha), math.cos(alpha)]
33 | ])
34 |
35 | Ry = np.asarray([[math.cos(beta), 0, math.sin(beta)],
36 | [0, 1, 0],
37 | [-math.sin(beta), 0, math.cos(beta)]])
38 |
39 | Rz = np.asarray([[math.cos(gamma), -math.sin(gamma), 0],
40 | [math.sin(gamma), math.cos(gamma), 0],
41 | [0, 0, 1]])
42 |
43 | self.rotmat = np.matmul(np.matmul(Rz, Ry), Rx).T
44 |
45 | def get_euler_axis(self):
46 | R = self.rotmat
47 | theta = math.acos((self.rotmat.trace() - 1) / 2)
48 | axis = np.asarray([R[2,1] - R[1,2], R[0,2] - R[2,0], R[1,0] - R[0,1]])
49 | axis = axis/(2*math.sin(theta))
50 | return theta, axis
51 |
52 | def to_expmap(self):
53 | theta, axis = self.get_euler_axis()
54 | rot_arr = theta * axis
55 | if np.isnan(rot_arr).any():
56 | rot_arr = [0, 0, 0]
57 | return rot_arr
58 |
59 | def to_euler(self):
60 | #TODO
61 | pass
62 |
63 | def to_quat(self):
64 | #TODO
65 | pass
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/pymo/viz_tools.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 | import IPython
5 | import os
6 |
7 | def save_fig(fig_id, tight_layout=True):
8 | if tight_layout:
9 | plt.tight_layout()
10 | plt.savefig(fig_id + '.png', format='png', dpi=300)
11 |
12 |
13 | def draw_stickfigure(mocap_track, frame, data=None, joints=None, draw_names=False, ax=None, figsize=(8,8)):
14 | if ax is None:
15 | fig = plt.figure(figsize=figsize)
16 | ax = fig.add_subplot(111)
17 |
18 | if joints is None:
19 | joints_to_draw = mocap_track.skeleton.keys()
20 | else:
21 | joints_to_draw = joints
22 |
23 | if data is None:
24 | df = mocap_track.values
25 | else:
26 | df = data
27 |
28 | for joint in joints_to_draw:
29 | ax.scatter(x=df['%s_Xposition'%joint][frame],
30 | y=df['%s_Yposition'%joint][frame],
31 | alpha=0.6, c='b', marker='o')
32 |
33 | parent_x = df['%s_Xposition'%joint][frame]
34 | parent_y = df['%s_Yposition'%joint][frame]
35 |
36 | children_to_draw = [c for c in mocap_track.skeleton[joint]['children'] if c in joints_to_draw]
37 |
38 | for c in children_to_draw:
39 | child_x = df['%s_Xposition'%c][frame]
40 | child_y = df['%s_Yposition'%c][frame]
41 | ax.plot([parent_x, child_x], [parent_y, child_y], 'k-', lw=2)
42 |
43 | if draw_names:
44 | ax.annotate(joint,
45 | (df['%s_Xposition'%joint][frame] + 0.1,
46 | df['%s_Yposition'%joint][frame] + 0.1))
47 |
48 | return ax
49 |
50 | def draw_stickfigure3d(mocap_track, frame, data=None, joints=None, draw_names=False, ax=None, figsize=(8,8)):
51 | from mpl_toolkits.mplot3d import Axes3D
52 |
53 | if ax is None:
54 | fig = plt.figure(figsize=figsize)
55 | ax = fig.add_subplot(111, projection='3d')
56 |
57 | if joints is None:
58 | joints_to_draw = mocap_track.skeleton.keys()
59 | else:
60 | joints_to_draw = joints
61 |
62 | if data is None:
63 | df = mocap_track.values
64 | else:
65 | df = data
66 |
67 | for joint in joints_to_draw:
68 | parent_x = df['%s_Xposition'%joint][frame]
69 | parent_y = df['%s_Zposition'%joint][frame]
70 | parent_z = df['%s_Yposition'%joint][frame]
71 | # ^ In mocaps, Y is the up-right axis
72 |
73 | ax.scatter(xs=parent_x,
74 | ys=parent_y,
75 | zs=parent_z,
76 | alpha=0.6, c='b', marker='o')
77 |
78 |
79 | children_to_draw = [c for c in mocap_track.skeleton[joint]['children'] if c in joints_to_draw]
80 |
81 | for c in children_to_draw:
82 | child_x = df['%s_Xposition'%c][frame]
83 | child_y = df['%s_Zposition'%c][frame]
84 | child_z = df['%s_Yposition'%c][frame]
85 | # ^ In mocaps, Y is the up-right axis
86 |
87 | ax.plot([parent_x, child_x], [parent_y, child_y], [parent_z, child_z], 'k-', lw=2, c='black')
88 |
89 | if draw_names:
90 | ax.text(x=parent_x + 0.1,
91 | y=parent_y + 0.1,
92 | z=parent_z + 0.1,
93 | s=joint,
94 | color='rgba(0,0,0,0.9)')
95 |
96 | return ax
97 |
98 |
99 | def sketch_move(mocap_track, data=None, ax=None, figsize=(16,8)):
100 | if ax is None:
101 | fig = plt.figure(figsize=figsize)
102 | ax = fig.add_subplot(111)
103 |
104 | if data is None:
105 | data = mocap_track.values
106 |
107 | for frame in range(0, data.shape[0], 4):
108 | # draw_stickfigure(mocap_track, f, data=data, ax=ax)
109 |
110 | for joint in mocap_track.skeleton.keys():
111 | children_to_draw = [c for c in mocap_track.skeleton[joint]['children']]
112 |
113 | parent_x = data['%s_Xposition'%joint][frame]
114 | parent_y = data['%s_Yposition'%joint][frame]
115 |
116 | frame_alpha = frame/data.shape[0]
117 |
118 | for c in children_to_draw:
119 | child_x = data['%s_Xposition'%c][frame]
120 | child_y = data['%s_Yposition'%c][frame]
121 |
122 | ax.plot([parent_x, child_x], [parent_y, child_y], '-', lw=1, color='gray', alpha=frame_alpha)
123 |
124 |
125 |
126 | def viz_cnn_filter(feature_to_viz, mocap_track, data, gap=25):
127 | fig = plt.figure(figsize=(16,4))
128 | ax = plt.subplot2grid((1,8),(0,0))
129 | ax.imshow(feature_to_viz.T, aspect='auto', interpolation='nearest')
130 |
131 | ax = plt.subplot2grid((1,8),(0,1), colspan=7)
132 | for frame in range(feature_to_viz.shape[0]):
133 | frame_alpha = 0.2#frame/data.shape[0] * 2 + 0.2
134 |
135 | for joint_i, joint in enumerate(mocap_track.skeleton.keys()):
136 | children_to_draw = [c for c in mocap_track.skeleton[joint]['children']]
137 |
138 | parent_x = data['%s_Xposition'%joint][frame] + frame * gap
139 | parent_y = data['%s_Yposition'%joint][frame]
140 |
141 | ax.scatter(x=parent_x,
142 | y=parent_y,
143 | alpha=0.6,
144 | cmap='RdBu',
145 | c=feature_to_viz[frame][joint_i] * 10000,
146 | marker='o',
147 | s = abs(feature_to_viz[frame][joint_i] * 10000))
148 | plt.axis('off')
149 | for c in children_to_draw:
150 | child_x = data['%s_Xposition'%c][frame] + frame * gap
151 | child_y = data['%s_Yposition'%c][frame]
152 |
153 | ax.plot([parent_x, child_x], [parent_y, child_y], '-', lw=1, color='gray', alpha=frame_alpha)
154 |
155 |
156 | def print_skel(X):
157 | stack = [X.root_name]
158 | tab=0
159 | while stack:
160 | joint = stack.pop()
161 | tab = len(stack)
162 | print('%s- %s (%s)'%('| '*tab, joint, X.skeleton[joint]['parent']))
163 | for c in X.skeleton[joint]['children']:
164 | stack.append(c)
165 |
166 |
167 | def nb_play_mocap_fromurl(mocap, mf, frame_time=1/30, scale=1, base_url='http://titan:8385'):
168 | if mf == 'bvh':
169 | bw = BVHWriter()
170 | with open('test.bvh', 'w') as ofile:
171 | bw.write(mocap, ofile)
172 |
173 | filepath = '../notebooks/test.bvh'
174 | elif mf == 'pos':
175 | c = list(mocap.values.columns)
176 |
177 | for cc in c:
178 | if 'rotation' in cc:
179 | c.remove(cc)
180 | mocap.values.to_csv('test.csv', index=False, columns=c)
181 |
182 | filepath = '../notebooks/test.csv'
183 | else:
184 | return
185 |
186 | url = '%s/mocapplayer/player.html?data_url=%s&scale=%f&cz=200&order=xzyi&frame_time=%f'%(base_url, filepath, scale, frame_time)
187 | iframe = ''
188 | link = 'New Window'%url
189 | return IPython.display.HTML(iframe+link)
190 |
191 | def nb_play_mocap(mocap, mf, meta=None, frame_time=1/30, scale=1, camera_z=500, base_url=None):
192 | data_template = 'var dataBuffer = `$$DATA$$`;'
193 | data_template += 'var metadata = $$META$$;'
194 | data_template += 'start(dataBuffer, metadata, $$CZ$$, $$SCALE$$, $$FRAMETIME$$);'
195 | dir_path = os.path.dirname(os.path.realpath(__file__))
196 |
197 |
198 | if base_url is None:
199 | base_url = os.path.join(dir_path, 'mocapplayer/playBuffer.html')
200 |
201 | # print(dir_path)
202 |
203 | if mf == 'bvh':
204 | pass
205 | elif mf == 'pos':
206 | cols = list(mocap.values.columns)
207 | for c in cols:
208 | if 'rotation' in c:
209 | cols.remove(c)
210 |
211 | data_csv = mocap.values.to_csv(index=False, columns=cols)
212 |
213 | if meta is not None:
214 | lines = [','.join(item) for item in meta.astype('str')]
215 | meta_csv = '[' + ','.join('[%s]'%l for l in lines) +']'
216 | else:
217 | meta_csv = '[]'
218 |
219 | data_assigned = data_template.replace('$$DATA$$', data_csv)
220 | data_assigned = data_assigned.replace('$$META$$', meta_csv)
221 | data_assigned = data_assigned.replace('$$CZ$$', str(camera_z))
222 | data_assigned = data_assigned.replace('$$SCALE$$', str(scale))
223 | data_assigned = data_assigned.replace('$$FRAMETIME$$', str(frame_time))
224 |
225 | else:
226 | return
227 |
228 |
229 |
230 | with open(os.path.join(dir_path, 'mocapplayer/data.js'), 'w') as oFile:
231 | oFile.write(data_assigned)
232 |
233 | url = '%s?&cz=200&order=xzyi&frame_time=%f&scale=%f'%(base_url, frame_time, scale)
234 | iframe = ''
235 | link = 'New Window'%url
236 | return IPython.display.HTML(iframe+link)
237 |
--------------------------------------------------------------------------------
/pymo/writers.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 |
4 | class BVHWriter():
5 | def __init__(self):
6 | pass
7 |
8 | def write(self, X, ofile):
9 |
10 | # Writing the skeleton info
11 | ofile.write('HIERARCHY\n')
12 |
13 | self.motions_ = []
14 | self._printJoint(X, X.root_name, 0, ofile)
15 |
16 | # Writing the motion header
17 | ofile.write('MOTION\n')
18 | ofile.write('Frames: %d\n'%X.values.shape[0])
19 | ofile.write('Frame Time: %f\n'%X.framerate)
20 |
21 | # Writing the data
22 | self.motions_ = np.asarray(self.motions_).T
23 | lines = [" ".join(item) for item in self.motions_.astype(str)]
24 | ofile.write("".join("%s\n"%l for l in lines))
25 |
26 | def _printJoint(self, X, joint, tab, ofile):
27 |
28 | if X.skeleton[joint]['parent'] == None:
29 | ofile.write('ROOT %s\n'%joint)
30 | elif len(X.skeleton[joint]['children']) > 0:
31 | ofile.write('%sJOINT %s\n'%('\t'*(tab), joint))
32 | else:
33 | ofile.write('%sEnd site\n'%('\t'*(tab)))
34 |
35 | ofile.write('%s{\n'%('\t'*(tab)))
36 |
37 | ofile.write('%sOFFSET %3.5f %3.5f %3.5f\n'%('\t'*(tab+1),
38 | X.skeleton[joint]['offsets'][0],
39 | X.skeleton[joint]['offsets'][1],
40 | X.skeleton[joint]['offsets'][2]))
41 | channels = X.skeleton[joint]['channels']
42 | n_channels = len(channels)
43 |
44 | if n_channels > 0:
45 | for ch in channels:
46 | self.motions_.append(np.asarray(X.values['%s_%s'%(joint, ch)].values))
47 |
48 | if len(X.skeleton[joint]['children']) > 0:
49 | ch_str = ''.join(' %s'*n_channels%tuple(channels))
50 | ofile.write('%sCHANNELS %d%s\n' %('\t'*(tab+1), n_channels, ch_str))
51 |
52 | for c in X.skeleton[joint]['children']:
53 | self._printJoint(X, c, tab+1, ofile)
54 |
55 | ofile.write('%s}\n'%('\t'*(tab)))
56 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | setuptools.setup(
7 | name="pymo",
8 | version="0.0.1",
9 | author="Omid Alemi",
10 | author_email="omid.alemi@gmail.com",
11 | description="A library for using motion capture data",
12 | long_description=long_description,
13 | long_description_content_type="text/markdown",
14 | url="https://omid.al/projects/PyMO.html",
15 | packages=setuptools.find_packages(),
16 | include_package_data=True, # include files in MANIFEST.in
17 | classifiers=(
18 | "Programming Language :: Python :: 3",
19 | "License :: OSI Approved :: MIT License",
20 | "Operating System :: OS Independent",
21 | ),
22 | install_requires=[ # dependencies
23 | 'numpy',
24 | 'pandas',
25 | 'matplotlib',
26 | 'scikit-learn'
27 | ]
28 | )
--------------------------------------------------------------------------------