├── .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 | ![2D Skeleton Viz](assets/viz_skel_2d.png) 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 | ![Mocap Player](assets/mocap_player.png) 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 | ![Foot Contact](assets/foot_updown.png) 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 | ![Foot Contact Signal](assets/footcontact_signal.png) 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
\n
\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 |
65 |
66 |
67 |
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 | ) --------------------------------------------------------------------------------