├── .gitignore ├── README.md ├── controllers └── index_controller.py ├── img ├── animation.gif ├── lego.jpg ├── voxel.jpg └── webapp.jpg ├── main.py ├── meshes ├── model_01.obj ├── model_02.obj ├── model_03.obj ├── model_04.obj ├── model_05.obj └── model_06.obj ├── models ├── lego.pkl └── voxel_26.pkl ├── services ├── generator.py └── voxelizer.py ├── src ├── __init__.py ├── common.py ├── data_loader.py ├── env │ ├── __init__.py │ ├── base_env.py │ ├── lego_env.py │ └── voxel_env.py ├── model │ ├── __init__.py │ └── no_clipping_td3.py └── policy.py ├── static ├── css │ ├── index.css │ └── reset.css ├── img │ ├── icon │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── manifest.json │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ └── ms-icon-70x70.png │ ├── model_01.jpg │ ├── model_02.jpg │ ├── model_03.jpg │ ├── model_04.jpg │ ├── model_05.jpg │ ├── model_06.jpg │ └── ogp.png ├── js │ ├── api.js │ ├── lib │ │ ├── OBJExporter.js │ │ ├── OBJLoader.js │ │ └── OrbitControls.js │ └── renderer.js └── model │ ├── 1.obj │ ├── 2.obj │ └── 3.obj └── templates ├── index.html └── playful_ai.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | MANIFEST 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | db.sqlite3 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # Environments 84 | .env 85 | .venv 86 | venv/ 87 | env.bak/ 88 | venv.bak/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | .DS_Store 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Generative Modelling with Design Constraints 2 | 3 | Generative design has been explored in architecture to produce unprecedented geometries, however, in most cases, design constraints are limited during the design process. 4 | Limitations of existing generative design strategies include topological inconsistencies of the output geometries, dense design outputs, as the format used is often voxels or point clouds, and finally out-of-scope design constraints. 5 | In order to overcome such shortcomings, a novel reinforcement learning (RL) framework is explored in order to design a series of furniture with embedded design and fabrication constraints. 6 | 7 | ![LEGO Block animation](https://github.com/ytakzk/generative_modelling_with_design_constraints/raw/master/img/animation.gif) 8 | 9 | ## Demo 10 | 11 | You can try it out on our web application [here](https://rl.ytakzk.me/)! 12 | 13 | ![Screenshot](https://github.com/ytakzk/generative_modelling_with_design_constraints/raw/master/img/webapp.jpg) 14 | 15 | ## Dependences 16 | 17 | * TensorFlow 1.14.0 18 | * Stable Baselines 2.7.0 19 | * OpenAI Gym 0.14.0 20 | * Trimesh 3.2.13 21 | * OpenMPI 3.2.1 22 | 23 | ## Works 24 | 25 | ![LEGO](https://github.com/ytakzk/generative_modelling_with_design_constraints/raw/master/img/lego.jpg) 26 | ![Voxel](https://github.com/ytakzk/generative_modelling_with_design_constraints/raw/master/img/voxel.jpg) 27 | 28 | -------------------------------------------------------------------------------- /controllers/index_controller.py: -------------------------------------------------------------------------------- 1 | from flask import render_template, request, redirect, url_for, session, jsonify, abort 2 | import services.voxelizer as voxelizer 3 | import services.generator as generator 4 | from werkzeug.utils import secure_filename 5 | import os 6 | import numpy as np 7 | 8 | ALLOWED_EXTENSIONS = set(['dae', 'off', 'ply', 'obj', 'stl']) 9 | 10 | def allowed_file(filename): 11 | return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS 12 | 13 | def index(): 14 | 15 | return render_template('index.html') 16 | 17 | def playful_ai(): 18 | 19 | return render_template('playful_ai.html') 20 | 21 | def predefined(model_id, model_type): 22 | 23 | path = './meshes/model_0%d.obj' % model_id 24 | 25 | voxels = voxelizer.voxelize(path=path) 26 | 27 | if not isinstance(voxels, (np.ndarray, np.generic)): 28 | return jsonify({'error': 'The file might have several meshes in one file.'}) 29 | 30 | voxels = voxelizer.voxelize(path=path) 31 | configuration = generator.generate(voxels=voxels, model_type=model_type) 32 | return jsonify({'result': configuration, 'model_type': model_type, 'error': None}) 33 | 34 | def generate(): 35 | 36 | if not 'model' in request.files: 37 | abort(403) 38 | return 39 | 40 | model = request.files['model'] 41 | model_type = request.form['model_type'] 42 | 43 | if model and allowed_file(model.filename): 44 | filename = secure_filename(model.filename) 45 | path = os.path.join('/tmp', filename) 46 | model.save(path) 47 | 48 | voxels = voxelizer.voxelize(path=path) 49 | 50 | if not isinstance(voxels, (np.ndarray, np.generic)): 51 | return jsonify({'error': 'The file might have several meshes in one file.'}) 52 | 53 | voxels = voxelizer.voxelize(path=path) 54 | configuration = generator.generate(voxels=voxels, model_type=model_type) 55 | return jsonify({'result': configuration, 'model_type': model_type, 'error': None}) 56 | 57 | else: 58 | 59 | abort(403) 60 | return -------------------------------------------------------------------------------- /img/animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/img/animation.gif -------------------------------------------------------------------------------- /img/lego.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/img/lego.jpg -------------------------------------------------------------------------------- /img/voxel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/img/voxel.jpg -------------------------------------------------------------------------------- /img/webapp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/img/webapp.jpg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, redirect, url_for, session 2 | import controllers.index_controller as index_controller 3 | from flask_cors import CORS 4 | 5 | app = Flask(__name__) 6 | cors = CORS(app, resources={"/generate": {"origins": "*"}}) 7 | 8 | @app.route('/') 9 | def index(): 10 | return index_controller.index() 11 | 12 | @app.route('/playful-ai') 13 | def playful_ai(): 14 | return index_controller.playful_ai() 15 | 16 | @app.route('/predefined//') 17 | def predefined(model_id, model_type): 18 | return index_controller.predefined(model_id, model_type) 19 | 20 | @app.route('/generate', methods=['POST']) 21 | def generate(): 22 | return index_controller.generate() 23 | 24 | if __name__ == '__main__': 25 | app.run(host= '127.0.0.1', port=5008, debug=False) 26 | -------------------------------------------------------------------------------- /meshes/model_01.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.71 (sub 0) OBJ File: 'blank.blend' 2 | # www.blender.org 3 | mtllib model.mtl 4 | o mesh7_mesh7-geometry 5 | o mesh6_mesh6-geometry 6 | o mesh5_mesh5-geometry 7 | v -0.267261 0.0593911 -0.326653 8 | v 0.267261 0.0593911 -0.400892 9 | v -0.267261 0.0593911 -0.400892 10 | v 0.267261 0.0593911 -0.326653 11 | v -0.267261 -0.133631 -0.400892 12 | v 0.267261 -0.133631 -0.326653 13 | v 0.267261 -0.133631 -0.400892 14 | v -0.267261 -0.133631 -0.326653 15 | usemtl material_0_24 16 | s off 17 | f 1 2 3 18 | f 2 1 4 19 | f 3 2 1 20 | f 4 1 2 21 | f 2 5 3 22 | f 3 5 2 23 | f 5 1 3 24 | f 3 1 5 25 | f 1 6 4 26 | f 4 6 1 27 | f 6 2 4 28 | f 4 2 6 29 | f 5 2 7 30 | f 7 2 5 31 | f 1 5 8 32 | f 8 5 1 33 | f 6 1 8 34 | f 8 1 6 35 | f 2 6 7 36 | f 7 6 2 37 | f 6 5 7 38 | f 7 5 6 39 | f 5 6 8 40 | f 8 6 5 41 | o mesh4_mesh4-geometry 42 | o mesh3_mesh3-geometry 43 | v -0.267261 0.0593911 0.400892 44 | v 0.267261 0.0593911 0.326653 45 | v -0.267261 0.0593911 0.326653 46 | v 0.267261 0.0593911 0.400892 47 | v -0.267261 -0.133631 0.326653 48 | v 0.267261 -0.133631 0.400892 49 | v 0.267261 -0.133631 0.326653 50 | v -0.267261 -0.133631 0.400892 51 | usemtl material_0_24 52 | s off 53 | f 9 10 11 54 | f 10 9 12 55 | f 11 10 9 56 | f 12 9 10 57 | f 10 13 11 58 | f 11 13 10 59 | f 13 9 11 60 | f 11 9 13 61 | f 9 14 12 62 | f 12 14 9 63 | f 14 10 12 64 | f 12 10 14 65 | f 13 10 15 66 | f 15 10 13 67 | f 9 13 16 68 | f 16 13 9 69 | f 14 9 16 70 | f 16 9 14 71 | f 10 14 15 72 | f 15 14 10 73 | f 14 13 15 74 | f 15 13 14 75 | f 13 14 16 76 | f 16 14 13 77 | o mesh2_mesh2-geometry 78 | o mesh1_mesh1-geometry 79 | v 0.267261 0.0593911 0.400892 80 | v -0.267261 0.0593911 -0.400892 81 | v 0.267261 0.0593911 -0.400892 82 | v -0.267261 0.0593911 0.400892 83 | v 0.267261 0.133631 -0.400892 84 | v -0.267261 0.133631 0.400892 85 | v -0.267261 0.133631 -0.400892 86 | v 0.267261 0.133631 0.400892 87 | usemtl material_0_24 88 | s off 89 | f 17 18 19 90 | f 18 17 20 91 | f 19 18 17 92 | f 20 17 18 93 | f 18 21 19 94 | f 19 21 18 95 | f 21 17 19 96 | f 19 17 21 97 | f 17 22 20 98 | f 20 22 17 99 | f 22 18 20 100 | f 20 18 22 101 | f 21 18 23 102 | f 23 18 21 103 | f 17 21 24 104 | f 24 21 17 105 | f 22 17 24 106 | f 24 17 22 107 | f 18 22 23 108 | f 23 22 18 109 | f 22 21 23 110 | f 23 21 22 111 | f 21 22 24 112 | f 24 22 21 113 | -------------------------------------------------------------------------------- /meshes/model_03.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.71 (sub 0) OBJ File: 'blank.blend' 2 | # www.blender.org 3 | mtllib model.mtl 4 | o mesh1_mesh1-geometry 5 | v 0.283682 0.222432 0.283682 6 | v 0.309471 0.222432 -0.309471 7 | v 0.309471 0.222432 0.309471 8 | v 0.283682 0.222432 0.206314 9 | v 0.309471 0.241775 -0.309471 10 | v -0.283682 0.222432 0.283682 11 | v 0.283682 0.145065 0.206314 12 | v 0.283682 0.222432 -0.206314 13 | v 0.309471 0.241775 0.309471 14 | v -0.309471 0.222432 -0.309471 15 | v -0.309471 0.222432 0.309471 16 | v -0.206314 0.222432 0.283682 17 | v 0.283682 -0.112828 0.206314 18 | v 0.283682 0.156791 0.173145 19 | v 0.283682 0.183749 0 20 | v 0.283682 0.222432 -0.283682 21 | v 0.257893 0.241775 0.257893 22 | v -0.309471 0.241775 0.309471 23 | v -0.309471 0.241775 -0.309471 24 | v -0.206314 0.145065 0.283682 25 | v 0.206314 0.222432 0.283682 26 | v 0.283682 -0.164407 0.206314 27 | v 0.26434 -0.112828 0.206314 28 | v 0.283682 0.166445 0.139316 29 | v 0.26434 0.156791 0.173145 30 | v 0.283682 0.182662 0.0351639 31 | v 0.283682 0.182662 -0.0351639 32 | v 0.283682 0.145065 -0.206314 33 | v 0.257893 0.241775 -0.257893 34 | v -0.257893 0.241775 -0.257893 35 | v 0.206314 0.222432 -0.283682 36 | v -0.283682 0.222432 0.206314 37 | v -0.206314 -0.112828 0.283682 38 | v -0.173145 0.156791 0.283682 39 | v 0.0701934 0.179406 0.283682 40 | v 0.283682 -0.241775 0.283682 41 | v 0.283682 -0.112828 -0.206314 42 | v 0.26434 0.145065 0.206314 43 | v 0.26434 0.166445 0.139316 44 | v 0.283682 0.173994 0.104954 45 | v 0.283682 0.179406 0.0701924 46 | v 0.26434 0.183749 0 47 | v 0.283682 0.179406 -0.0701934 48 | v 0.26434 0.182662 -0.0351639 49 | v 0.283682 0.156791 -0.173145 50 | v -0.257893 0.241775 0.257893 51 | v -0.206314 0.222432 -0.283682 52 | v 0.206314 -0.241775 -0.283682 53 | v -0.283682 0.222432 -0.206314 54 | v -0.283682 0.145065 0.206314 55 | v -0.206314 -0.164407 0.283682 56 | v -0.206314 0.145065 0.26434 57 | v -0.139316 0.166445 0.283682 58 | v 0.104955 0.173994 0.283682 59 | v 0.0351639 0.182662 0.283682 60 | v 0.206314 -0.241775 0.283682 61 | v 0.283682 -0.241775 0.206314 62 | v 0.283682 -0.164407 -0.206314 63 | v 0.26434 0.145065 -0.206314 64 | v 0.26434 -0.112828 -0.206314 65 | v 0.26434 0.173994 0.104954 66 | v 0.26434 0.156791 -0.173145 67 | v 0.26434 0.166445 -0.139316 68 | v 0.26434 0.182662 0.0351639 69 | v 0.283682 0.173994 -0.104955 70 | v 0.26434 0.179406 -0.0701934 71 | v 0.283682 0.166445 -0.139316 72 | v -0.283682 0.222432 -0.283682 73 | v 0.0701934 0.179406 -0.283682 74 | v 0.283682 -0.241775 -0.283682 75 | v 0.206314 -0.164407 -0.283682 76 | v -0.283682 0.183749 0 77 | v -0.283682 -0.112828 0.206314 78 | v -0.283682 0.156791 0.173145 79 | v -0.206314 -0.241775 0.283682 80 | v 0.206314 -0.164407 0.283682 81 | v -0.206314 -0.112828 0.26434 82 | v -0.104954 0.173994 0.283682 83 | v -0.173145 0.156791 0.26434 84 | v 0.139316 0.166445 0.283682 85 | v 0.0701934 0.179406 0.26434 86 | v -0 0.183749 0.283682 87 | v 0.0351639 0.182662 0.26434 88 | v 0.206314 0.145065 0.283682 89 | v 0.206314 -0.241775 0.26434 90 | v 0.26434 -0.241775 0.26434 91 | v 0.26434 -0.241775 0.206314 92 | v 0.26434 -0.164407 -0.206314 93 | v 0.26434 0.173994 -0.104955 94 | v 0.26434 0.179406 0.0701924 95 | v 0.283682 -0.241775 -0.206314 96 | v -0.283682 -0.241775 -0.283682 97 | v 0.104955 0.173994 -0.283682 98 | v 0.0351639 0.182662 -0.283682 99 | v 0.26434 -0.241775 -0.26434 100 | v 0.206314 -0.164407 -0.26434 101 | v 0.206314 -0.112828 -0.283682 102 | v -0.283682 0.145065 -0.206314 103 | v -0.283682 0.182662 -0.0351639 104 | v -0.283682 0.182662 0.0351639 105 | v -0.283682 -0.164407 0.206314 106 | v -0.26434 0.145065 0.206314 107 | v -0.283682 0.166445 0.139316 108 | v -0.283682 -0.241775 0.283682 109 | v -0.206314 -0.164407 0.26434 110 | v 0.206314 -0.112828 0.283682 111 | v 0.206314 -0.112828 0.26434 112 | v -0.0701924 0.179406 0.283682 113 | v -0.139316 0.166445 0.26434 114 | v 0.173145 0.156791 0.283682 115 | v 0.104955 0.173994 0.26434 116 | v -0.0351639 0.182662 0.283682 117 | v -0 0.183749 0.26434 118 | v 0.26434 -0.164407 0.206314 119 | v 0.26434 -0.241775 -0.206314 120 | v -0.206314 0.145065 -0.283682 121 | v -0.283682 -0.164407 -0.206314 122 | v 0.139316 0.166445 -0.283682 123 | v 0.104955 0.173994 -0.26434 124 | v 0.0701934 0.179406 -0.26434 125 | v -0 0.183749 -0.283682 126 | v 0.206314 -0.241775 -0.26434 127 | v -0.206314 -0.164407 -0.283682 128 | v 0.206314 0.145065 -0.283682 129 | v -0.283682 -0.112828 -0.206314 130 | v -0.283682 0.156791 -0.173145 131 | v -0.283682 0.179406 -0.0701934 132 | v -0.26434 0.183749 0 133 | v -0.283682 0.179406 0.0701924 134 | v -0.26434 0.182662 0.0351639 135 | v -0.283682 -0.241775 0.206314 136 | v -0.26434 -0.112828 0.206314 137 | v -0.283682 0.173994 0.104954 138 | v -0.26434 0.156791 0.173145 139 | v -0.26434 -0.241775 0.26434 140 | v -0.206314 -0.241775 0.26434 141 | v 0.206314 -0.164407 0.26434 142 | v 0.173145 0.156791 0.26434 143 | v -0.104954 0.173994 0.26434 144 | v 0.139316 0.166445 0.26434 145 | v -0.0351639 0.182662 0.26434 146 | v 0.26434 -0.164407 0.26434 147 | v 0.244998 -0.164407 -0.206314 148 | v -0.206314 -0.112828 -0.283682 149 | v -0.173145 0.156791 -0.283682 150 | v -0.283682 -0.241775 -0.206314 151 | v 0.173145 0.156791 -0.283682 152 | v 0.139316 0.166445 -0.26434 153 | v 0.0351639 0.182662 -0.26434 154 | v -0.0351639 0.182662 -0.283682 155 | v 0.244998 -0.164407 -0.26434 156 | v -0.206314 -0.164407 -0.26434 157 | v 0.206314 0.145065 -0.26434 158 | v -0.26434 -0.112828 -0.206314 159 | v -0.283682 0.166445 -0.139316 160 | v -0.26434 0.156791 -0.173145 161 | v -0.283682 0.173994 -0.104955 162 | v -0.26434 0.182662 -0.0351639 163 | v -0.26434 0.179406 0.0701924 164 | v -0.26434 -0.164407 0.206314 165 | v -0.26434 0.166445 0.139316 166 | v -0.26434 0.145065 -0.206314 167 | v -0.244998 -0.164407 0.244998 168 | v 0.206314 0.145065 0.26434 169 | v -0.0701924 0.179406 0.26434 170 | v 0.244998 -0.164407 0.26434 171 | v 0.244998 -0.164407 0.244998 172 | v 0.244998 -0.164407 0.206314 173 | v 0.244998 -0.164407 -0.244998 174 | v 0.26434 -0.164407 -0.26434 175 | v -0.206314 -0.112828 -0.26434 176 | v -0.139316 0.166445 -0.283682 177 | v -0.173145 0.156791 -0.26434 178 | v -0.26434 -0.241775 -0.206314 179 | v 0.173145 0.156791 -0.26434 180 | v -0 0.183749 -0.26434 181 | v -0.0701924 0.179406 -0.283682 182 | v -0.206314 -0.241775 -0.26434 183 | v 0.206314 -0.112828 -0.26434 184 | v -0.26434 0.166445 -0.139316 185 | v -0.26434 0.179406 -0.0701934 186 | v -0.26434 0.173994 0.104954 187 | v -0.26434 -0.241775 0.206314 188 | v -0.26434 -0.164407 -0.206314 189 | v -0.244998 -0.164407 0.26434 190 | v -0.244998 -0.164407 -0.206314 191 | v -0.244998 -0.164407 -0.244998 192 | v -0.206314 -0.241775 -0.283682 193 | v -0.206314 0.145065 -0.26434 194 | v -0.104954 0.173994 -0.283682 195 | v -0.139316 0.166445 -0.26434 196 | v -0.26434 -0.241775 -0.26434 197 | v -0.0351639 0.182662 -0.26434 198 | v -0.244998 -0.164407 -0.26434 199 | v -0.26434 0.173994 -0.104955 200 | v -0.244998 -0.164407 0.206314 201 | v -0.26434 -0.164407 0.26434 202 | v -0.206314 -0.164407 -0.244998 203 | v -0.104954 0.173994 -0.26434 204 | v -0.26434 -0.164407 -0.26434 205 | v -0.0701924 0.179406 -0.26434 206 | v -0.206314 -0.164407 0.244998 207 | v 0.206314 -0.164407 0.244998 208 | v 0.206314 -0.164407 -0.244998 209 | vt 0.574020 0.831035 210 | vt 0.254576 0.817146 211 | vt 0.587909 0.817146 212 | vt 0.532353 0.831035 213 | vt -0.254576 0.010417 214 | vt -0.587909 0.000000 215 | vt -0.254576 0.000000 216 | vt 0.574020 1.136591 217 | vt -0.574020 -0.000000 218 | vt -0.532353 -0.041667 219 | vt -0.532353 -0.000000 220 | vt 0.310131 0.831035 221 | vt -0.587909 0.010417 222 | vt 1.150479 0.000000 223 | vt 0.817146 0.010417 224 | vt 0.817146 0.000000 225 | vt 0.587909 1.150479 226 | vt 0.574020 1.094924 227 | vt -0.532353 -0.180556 228 | vt -0.514490 -0.035352 229 | vt -0.421242 -0.020833 230 | vt -0.310131 -0.000000 231 | vt 0.268464 0.831035 232 | vt -0.254576 0.817146 233 | vt -0.560131 0.844924 234 | vt -0.587909 0.817146 235 | vt -1.150479 0.010417 236 | vt -0.817146 0.000000 237 | vt -0.817146 0.010417 238 | vt 1.150479 0.010417 239 | vt 0.254576 1.150479 240 | vt -1.150479 0.000000 241 | vt -1.136591 0.000000 242 | vt -1.094924 -0.041667 243 | vt -1.094924 0.000000 244 | vt 0.574020 0.872702 245 | vt -0.532353 -0.208333 246 | vt 0.841452 -0.180556 247 | vt 0.831035 -0.041667 248 | vt 0.831035 -0.180556 249 | vt -0.496271 -0.030152 250 | vt -0.440180 -0.021419 251 | vt -0.402305 -0.021419 252 | vt -0.310131 -0.041667 253 | vt -0.268464 -0.000000 254 | vt -0.282353 0.844924 255 | vt -0.587909 1.150479 256 | vt 0.254576 0.000000 257 | vt 0.587909 0.010417 258 | vt 0.254576 0.010417 259 | vt -0.282353 1.122702 260 | vt -0.254576 1.150479 261 | vt 0.268464 0.872702 262 | vt 0.587909 0.000000 263 | vt 0.532353 1.136591 264 | vt -1.094924 -0.180556 265 | vt -1.077061 -0.035352 266 | vt -0.946010 -0.023172 267 | vt -0.872702 0.000000 268 | vt -0.831035 -0.250000 269 | vt -0.831035 0.000000 270 | vt -0.574020 -0.250000 271 | vt -0.310131 -0.180556 272 | vt 0.841452 -0.041667 273 | vt -0.310131 0.831035 274 | vt -0.532353 0.841452 275 | vt -0.532353 0.831035 276 | vt -0.477766 -0.026087 277 | vt -0.459045 -0.023172 278 | vt -0.383439 -0.023172 279 | vt -0.327994 -0.035352 280 | vt -0.560131 1.122702 281 | vt 0.268464 1.094924 282 | vt 0.831035 0.000000 283 | vt 0.872702 -0.250000 284 | vt 0.872702 0.000000 285 | vt 0.310131 1.136591 286 | vt 0.532353 -0.041667 287 | vt 0.574020 0.000000 288 | vt 0.532353 0.000000 289 | vt -1.094924 -0.208333 290 | vt -0.574020 -0.180556 291 | vt -0.563603 -0.041667 292 | vt -0.574020 -0.041667 293 | vt -1.058842 -0.030152 294 | vt -0.927289 -0.026087 295 | vt -0.964875 -0.021419 296 | vt -0.872702 -0.250000 297 | vt -0.532353 -0.250000 298 | vt -0.310131 -0.208333 299 | vt -0.310131 0.841452 300 | vt -0.346213 -0.030152 301 | vt -0.364719 -0.026087 302 | vt -0.841452 -0.041667 303 | vt -0.831035 -0.180556 304 | vt -0.831035 -0.041667 305 | vt 0.268464 1.136591 306 | vt 0.946010 -0.023172 307 | vt 1.094924 -0.000000 308 | vt 0.831035 -0.250000 309 | vt 0.872702 -0.208333 310 | vt 0.310131 0.000000 311 | vt 0.421242 -0.020833 312 | vt 0.532353 -0.180556 313 | vt 0.514490 -0.035352 314 | vt -1.094924 -0.250000 315 | vt -0.872702 -0.208333 316 | vt -0.563603 -0.180556 317 | vt -1.040336 -0.026087 318 | vt -0.908784 -0.030152 319 | vt -0.983813 -0.020833 320 | vt -0.872702 -0.041667 321 | vt 0.563603 0.872702 322 | vt 0.563603 0.841452 323 | vt 0.841452 -0.250000 324 | vt 0.831035 -0.208333 325 | vt 0.310131 0.841452 326 | vt -0.841452 -0.180556 327 | vt -0.310131 -0.250000 328 | vt 1.136591 -0.250000 329 | vt 1.136591 0.000000 330 | vt 0.927289 -0.026087 331 | vt 0.964875 -0.021419 332 | vt -0.268464 -0.250000 333 | vt 0.278881 0.841452 334 | vt 0.268464 -0.250000 335 | vt 0.278881 -0.208333 336 | vt 0.268464 -0.208333 337 | vt 0.872702 -0.180556 338 | vt 0.268464 0.000000 339 | vt 0.310131 -0.041667 340 | vt 0.402305 -0.021419 341 | vt 0.440180 -0.021419 342 | vt 0.532353 -0.208333 343 | vt 1.126174 -0.041667 344 | vt 1.136591 -0.180556 345 | vt 1.136591 -0.041667 346 | vt 0.496271 -0.030152 347 | vt -1.136591 -0.250000 348 | vt -0.563603 -0.208333 349 | vt -0.574020 -0.208333 350 | vt -0.872702 -0.180556 351 | vt 0.563603 1.094924 352 | vt -0.574020 1.094924 353 | vt -0.563603 0.872702 354 | vt -0.563603 1.094924 355 | vt -1.021615 -0.023172 356 | vt -0.890565 -0.035352 357 | vt -1.002750 -0.021419 358 | vt 0.574020 -0.208333 359 | vt 0.563603 -0.250000 360 | vt 0.574020 -0.250000 361 | vt 0.532353 0.841452 362 | vt 0.841452 -0.208333 363 | vt -0.841452 -0.250000 364 | vt -0.831035 -0.208333 365 | vt -0.841452 -0.208333 366 | vt 1.094924 -0.041667 367 | vt 0.310131 -0.208333 368 | vt 0.908784 -0.030152 369 | vt 0.983813 -0.020833 370 | vt 0.278881 0.872702 371 | vt 0.278881 -0.250000 372 | vt 1.094924 -0.208333 373 | vt 0.872702 -0.041667 374 | vt 0.310131 -0.180556 375 | vt 0.327994 -0.035352 376 | vt 0.383439 -0.023172 377 | vt 0.459045 -0.023172 378 | vt 0.532353 -0.250000 379 | vt 1.126174 -0.180556 380 | vt 0.477766 -0.026087 381 | vt 0.563603 1.126174 382 | vt -0.563603 -0.250000 383 | vt -0.574020 0.872702 384 | vt 0.574020 -0.041667 385 | vt 0.563603 -0.180556 386 | vt 0.574020 -0.180556 387 | vt 0.563603 -0.208333 388 | vt 0.310131 0.851868 389 | vt 0.310131 -0.250000 390 | vt 1.094924 -0.180556 391 | vt 1.077061 -0.035352 392 | vt 0.890565 -0.035352 393 | vt 1.002750 -0.021419 394 | vt -0.851868 -0.208333 395 | vt 0.278881 1.094924 396 | vt 0.268464 -0.180556 397 | vt 0.278881 -0.041667 398 | vt 0.268464 -0.041667 399 | vt -1.126174 -0.180556 400 | vt -1.136591 -0.041667 401 | vt -1.136591 -0.180556 402 | vt 0.346213 -0.030152 403 | vt 0.364719 -0.026087 404 | vt 1.126174 -0.208333 405 | vt 1.136591 -0.208333 406 | vt 0.532353 1.126174 407 | vt -0.310131 1.126174 408 | vt -0.532353 1.136591 409 | vt -0.532353 1.126174 410 | vt 1.126174 -0.250000 411 | vt 1.094924 -0.250000 412 | vt 0.553187 1.115757 413 | vt 0.563603 -0.041667 414 | vt 0.851868 -0.208333 415 | vt 0.553187 0.851868 416 | vt 0.532353 0.851868 417 | vt 0.289298 0.851868 418 | vt -0.268464 -0.041667 419 | vt -0.278881 -0.180556 420 | vt -0.268464 -0.180556 421 | vt 1.058842 -0.030152 422 | vt 0.310131 1.126174 423 | vt -1.126174 -0.250000 424 | vt -1.136591 -0.208333 425 | vt 1.021615 -0.023172 426 | vt 0.278881 0.851868 427 | vt -0.278881 -0.250000 428 | vt -0.268464 -0.208333 429 | vt -0.278881 -0.208333 430 | vt -0.278881 1.094924 431 | vt -0.268464 0.872702 432 | vt -0.268464 1.094924 433 | vt 0.278881 -0.180556 434 | vt -1.126174 -0.041667 435 | vt -0.310131 1.136591 436 | vt 1.115757 -0.208333 437 | vt 0.563603 1.115757 438 | vt 0.563603 0.851868 439 | vt 0.310131 1.115757 440 | vt 0.289298 1.115757 441 | vt -0.278881 -0.041667 442 | vt 1.040336 -0.026087 443 | vt 0.278881 1.126174 444 | vt -1.126174 -0.208333 445 | vt 0.278881 1.115757 446 | vt -1.115757 -0.208333 447 | vt -0.278881 0.872702 448 | vt 0.532353 1.115757 449 | vt 0.289298 1.094924 450 | vt 0.553187 1.094924 451 | vt 0.553187 0.872702 452 | vt 0.289298 0.872702 453 | vt 0.227394 0.000000 454 | vt 0.208445 0.010417 455 | vt 0.208445 0.000000 456 | vt 0.189495 0.010417 457 | vt 0.189495 0.000000 458 | vt 0.227394 0.010417 459 | vt 0.132647 0.000000 460 | vt 0.113697 0.010417 461 | vt 0.113697 0.000000 462 | vt 0.094748 0.010417 463 | vt 0.094748 0.000000 464 | vt 0.170546 0.010417 465 | vt 0.170546 0.000000 466 | vt 0.151596 -0.000000 467 | vt 0.132647 0.010417 468 | vt 0.075798 0.010417 469 | vt 0.075798 0.000000 470 | vt 0.018950 0.000000 471 | vt -0.000000 0.010417 472 | vt -0.000000 0.000000 473 | vt 0.056849 -0.000000 474 | vt 0.151596 0.010417 475 | vt 0.056849 0.010417 476 | vt 0.037899 -0.000000 477 | vt 0.018950 0.010417 478 | vt 0.037899 0.010417 479 | usemtl material_0_1_8 480 | s off 481 | f 1/1 2/2 3/3 482 | f 2/2 1/1 4/4 483 | f 5/5 3/6 2/7 484 | f 3/3 6/8 1/1 485 | f 1/9 7/10 4/11 486 | f 2/2 4/4 8/12 487 | f 3/6 5/5 9/13 488 | f 10/14 5/15 2/16 489 | f 6/8 3/3 11/17 490 | f 1/1 6/8 12/18 491 | f 1/9 13/19 7/10 492 | f 14/20 4/11 7/10 493 | f 4/11 15/21 8/22 494 | f 2/2 8/12 16/23 495 | f 5/24 17/25 9/26 496 | f 18/27 3/28 9/29 497 | f 5/15 10/14 19/30 498 | f 16/23 10/31 2/2 499 | f 3/28 18/27 11/32 500 | f 10/31 6/8 11/17 501 | f 6/33 20/34 12/35 502 | f 1/1 12/18 21/36 503 | f 1/9 22/37 13/19 504 | f 23/38 7/39 13/40 505 | f 4/11 14/20 24/41 506 | f 4/11 26/42 15/21 507 | f 27/43 8/22 15/21 508 | f 28/44 16/45 8/22 509 | f 17/25 5/24 29/46 510 | f 17/25 18/47 9/26 511 | f 10/48 18/49 19/50 512 | f 30/51 5/24 19/52 513 | f 31/53 10/31 16/23 514 | f 18/49 10/48 11/54 515 | f 6/8 10/31 32/55 516 | f 20/34 6/33 33/56 517 | f 34/57 12/35 20/34 518 | f 35/58 21/59 12/35 519 | f 21/59 36/60 1/61 520 | f 36/62 22/37 1/9 521 | f 22/37 37/63 13/19 522 | f 7/39 23/38 38/64 523 | f 37/65 23/66 13/67 524 | f 4/11 24/41 40/68 525 | f 4/11 41/69 26/42 526 | f 43/70 8/22 27/43 527 | f 37/63 16/45 28/44 528 | f 8/22 45/71 28/44 529 | f 5/24 30/51 29/46 530 | f 29/46 46/72 17/25 531 | f 18/47 17/25 46/72 532 | f 46/72 19/52 18/47 533 | f 19/52 46/72 30/51 534 | f 47/73 10/31 31/53 535 | f 16/74 48/75 31/76 536 | f 32/55 10/31 49/77 537 | f 50/78 6/79 32/80 538 | f 33/56 6/33 51/81 539 | f 33/82 52/83 20/84 540 | f 53/85 12/35 34/57 541 | f 54/86 21/59 35/58 542 | f 35/58 12/35 55/87 543 | f 36/60 21/59 56/88 544 | f 22/37 36/62 57/89 545 | f 37/63 22/37 58/90 546 | f 23/19 59/44 38/10 547 | f 23/66 37/65 60/91 548 | f 4/11 40/68 41/69 549 | f 38/10 62/71 25/20 550 | f 25/20 63/92 39/41 551 | f 65/93 8/22 43/70 552 | f 58/90 16/45 37/63 553 | f 59/94 37/95 28/96 554 | f 45/71 8/22 67/92 555 | f 46/72 29/46 30/51 556 | f 10/31 47/73 68/97 557 | f 31/76 69/98 47/99 558 | f 48/75 16/74 70/100 559 | f 31/76 48/75 71/101 560 | f 49/77 10/31 68/97 561 | f 49/102 72/103 32/80 562 | f 73/104 6/79 50/78 563 | f 32/80 74/105 50/78 564 | f 51/81 6/33 75/106 565 | f 76/107 33/56 51/81 566 | f 52/83 33/82 77/108 567 | f 78/109 12/35 53/85 568 | f 80/110 21/59 54/86 569 | f 55/87 12/35 82/111 570 | f 56/88 21/59 84/112 571 | f 85/113 36/1 56/36 572 | f 86/114 57/4 36/1 573 | f 87/115 22/116 57/100 574 | f 22/4 88/117 58/12 575 | f 59/44 23/19 60/63 576 | f 38/10 59/44 62/71 577 | f 37/95 59/94 60/118 578 | f 39/41 89/93 61/68 579 | f 25/20 62/71 63/92 580 | f 39/41 63/92 89/93 581 | f 64/42 44/43 42/21 582 | f 67/92 8/22 65/93 583 | f 90/69 66/70 44/43 584 | f 91/119 16/45 58/90 585 | f 47/99 92/120 68/121 586 | f 31/76 93/122 69/98 587 | f 47/99 69/98 94/123 588 | f 16/45 91/119 70/124 589 | f 95/125 48/53 70/23 590 | f 48/126 96/127 71/128 591 | f 31/76 71/101 97/129 592 | f 68/130 98/131 49/102 593 | f 49/102 99/132 72/103 594 | f 100/133 32/80 72/103 595 | f 101/134 6/79 73/104 596 | f 102/135 73/136 50/137 597 | f 74/105 32/80 103/138 598 | f 75/106 6/33 104/139 599 | f 75/62 105/140 51/141 600 | f 33/56 76/107 106/142 601 | f 105/143 76/36 51/18 602 | f 33/144 107/145 77/146 603 | f 77/56 79/57 52/34 604 | f 108/147 12/35 78/109 605 | f 110/148 21/59 80/110 606 | f 82/111 12/35 112/149 607 | f 21/59 110/148 84/112 608 | f 56/88 84/112 106/142 609 | f 36/1 85/113 86/114 610 | f 76/150 85/151 56/152 611 | f 57/4 86/114 87/153 612 | f 22/116 87/115 114/154 613 | f 88/117 22/4 114/153 614 | f 115/155 58/156 88/157 615 | f 61/68 89/93 66/70 616 | f 61/68 66/70 90/69 617 | f 90/69 44/43 64/42 618 | f 58/156 115/155 91/60 619 | f 116/158 92/120 47/99 620 | f 92/126 117/159 68/130 621 | f 31/76 118/160 93/122 622 | f 47/99 94/123 121/161 623 | f 91/12 95/125 70/23 624 | f 48/53 95/125 122/162 625 | f 96/127 48/126 122/163 626 | f 96/162 123/73 71/53 627 | f 123/164 97/129 71/101 628 | f 31/76 97/129 124/165 629 | f 68/130 125/166 98/131 630 | f 126/167 49/102 98/131 631 | f 49/102 127/168 99/132 632 | f 129/169 32/80 100/133 633 | f 131/170 6/79 101/134 634 | f 73/104 117/159 101/134 635 | f 73/136 102/135 132/171 636 | f 103/138 32/80 133/172 637 | f 6/79 131/170 104/152 638 | f 135/173 75/18 104/8 639 | f 105/140 75/62 136/174 640 | f 56/88 106/142 76/107 641 | f 107/145 33/144 106/175 642 | f 76/36 105/143 137/113 643 | f 107/142 138/148 77/56 644 | f 77/56 109/85 79/57 645 | f 112/149 12/35 108/147 646 | f 77/56 111/86 81/58 647 | f 77/56 81/58 83/87 648 | f 77/56 83/87 113/111 649 | f 84/176 107/177 106/178 650 | f 85/75 142/154 86/115 651 | f 85/151 76/150 137/179 652 | f 142/179 87/170 86/151 653 | f 87/170 142/179 114/134 654 | f 114/153 143/180 88/117 655 | f 88/159 95/163 115/181 656 | f 95/125 91/12 115/117 657 | f 144/182 92/120 116/158 658 | f 47/99 145/183 116/158 659 | f 117/159 92/126 146/181 660 | f 68/130 117/159 125/166 661 | f 31/76 147/184 118/160 662 | f 47/99 121/161 150/185 663 | f 95/155 151/186 122/88 664 | f 122/88 151/186 96/107 665 | f 123/73 96/162 152/187 666 | f 97/129 123/164 144/182 667 | f 97/188 153/189 124/190 668 | f 147/184 31/76 124/165 669 | f 154/191 98/192 125/193 670 | f 49/102 126/167 155/194 671 | f 49/102 157/195 127/168 672 | f 133/172 32/80 129/169 673 | f 160/196 131/120 101/197 674 | f 117/159 73/104 125/166 675 | f 117/77 160/198 101/55 676 | f 102/78 154/166 132/104 677 | f 154/199 73/200 132/201 678 | f 162/131 102/78 134/105 679 | f 131/55 135/173 104/8 680 | f 75/18 135/173 136/143 681 | f 135/202 105/164 136/203 682 | f 105/143 163/204 137/113 683 | f 138/148 107/142 164/112 684 | f 77/56 138/148 140/110 685 | f 77/56 139/109 109/85 686 | f 77/56 140/110 111/86 687 | f 77/56 113/111 141/149 688 | f 107/177 84/176 164/205 689 | f 142/154 85/75 166/206 690 | f 166/206 85/75 137/101 691 | f 142/114 167/207 114/153 692 | f 114/153 168/208 143/180 693 | f 88/117 143/180 169/209 694 | f 95/163 88/159 170/127 695 | f 123/164 92/120 144/182 696 | f 116/210 171/211 144/212 697 | f 47/99 172/213 145/183 698 | f 92/97 174/214 146/77 699 | f 174/215 117/216 146/139 700 | f 119/122 171/182 120/98 701 | f 148/160 171/182 119/122 702 | f 120/98 171/182 149/123 703 | f 47/99 150/185 177/217 704 | f 151/186 95/155 170/157 705 | f 151/218 152/187 96/162 706 | f 178/219 123/220 152/221 707 | f 171/222 97/223 144/224 708 | f 153/189 97/188 179/225 709 | f 98/192 154/191 162/226 710 | f 73/200 154/199 125/227 711 | f 49/102 155/194 157/195 712 | f 158/132 130/133 128/103 713 | f 181/168 159/169 130/133 714 | f 131/120 160/196 183/202 715 | f 160/198 117/77 184/214 716 | f 154/166 102/78 162/131 717 | f 156/167 134/105 161/138 718 | f 162/131 134/105 156/167 719 | f 135/173 131/55 183/198 720 | f 105/164 135/202 185/228 721 | f 163/204 105/143 185/229 722 | f 137/113 163/204 166/230 723 | f 77/56 165/147 139/109 724 | f 77/56 141/149 165/147 725 | f 167/207 142/114 166/230 726 | f 114/153 167/207 168/208 727 | f 168/208 186/231 143/180 728 | f 143/180 187/232 169/209 729 | f 88/117 169/209 170/125 730 | f 92/120 123/164 188/203 731 | f 171/211 116/210 189/233 732 | f 47/99 190/234 172/213 733 | f 174/214 92/97 192/235 734 | f 117/216 174/215 184/236 735 | f 175/184 171/182 148/160 736 | f 149/123 171/182 176/161 737 | f 47/99 177/217 190/234 738 | f 170/125 169/209 151/218 739 | f 151/218 194/237 152/187 740 | f 123/220 178/219 188/124 741 | f 194/238 178/106 152/81 742 | f 97/223 171/222 179/239 743 | f 171/182 153/165 179/129 744 | f 156/167 161/138 180/194 745 | f 181/168 130/133 158/132 746 | f 195/195 182/172 159/169 747 | f 195/195 159/169 181/168 748 | f 160/37 135/174 183/89 749 | f 196/240 160/198 184/214 750 | f 180/194 161/138 182/172 751 | f 185/228 135/202 197/196 752 | f 185/229 160/198 163/204 753 | f 166/230 163/204 167/207 754 | f 167/207 196/240 168/208 755 | f 168/208 196/240 186/231 756 | f 143/180 186/231 187/232 757 | f 169/209 187/232 198/241 758 | f 178/187 92/97 188/73 759 | f 173/183 171/182 189/158 760 | f 191/213 171/182 173/183 761 | f 92/97 178/187 192/235 762 | f 200/221 174/119 192/219 763 | f 174/119 200/221 184/90 764 | f 153/165 171/182 175/184 765 | f 176/161 171/182 193/185 766 | f 169/209 194/237 151/218 767 | f 178/106 194/238 192/215 768 | f 180/194 182/172 195/195 769 | f 135/174 160/37 197/140 770 | f 163/204 160/198 196/240 771 | f 196/240 184/214 186/231 772 | f 160/198 185/229 197/173 773 | f 167/207 163/204 202/242 774 | f 203/243 196/240 167/207 775 | f 186/231 200/235 187/232 776 | f 194/237 198/241 187/232 777 | f 169/209 198/241 204/244 778 | f 199/234 171/182 191/213 779 | f 192/215 194/238 200/236 780 | f 186/231 184/214 200/235 781 | f 193/185 171/182 201/217 782 | f 204/244 194/237 169/209 783 | f 196/240 202/242 163/204 784 | f 167/207 202/242 203/243 785 | f 202/242 196/240 203/243 786 | f 187/232 200/235 194/237 787 | f 198/241 194/237 204/244 788 | f 201/217 171/182 199/234 789 | s 1 790 | f 7/245 25/246 14/247 791 | f 14/247 39/248 24/249 792 | f 25/246 7/245 38/250 793 | f 39/248 14/247 25/246 794 | f 26/251 42/252 15/253 795 | f 15/253 44/254 27/255 796 | f 52/250 34/247 20/245 797 | f 61/256 24/249 39/248 798 | f 24/249 61/256 40/257 799 | f 41/258 64/259 26/251 800 | f 42/252 26/251 64/259 801 | f 44/254 15/253 42/252 802 | f 27/255 66/260 43/261 803 | f 66/260 27/255 44/254 804 | f 45/262 59/263 28/264 805 | f 79/246 53/249 34/247 806 | f 34/247 52/250 79/246 807 | f 81/260 54/265 35/261 808 | f 83/254 35/261 55/255 809 | f 90/266 40/257 61/256 810 | f 40/257 90/266 41/258 811 | f 64/259 41/258 90/266 812 | f 43/261 89/267 65/265 813 | f 89/267 43/261 66/260 814 | f 67/268 62/269 45/262 815 | f 59/263 45/262 62/269 816 | f 74/262 102/263 50/264 817 | f 109/248 78/257 53/249 818 | f 53/249 79/246 109/248 819 | f 111/267 80/268 54/265 820 | f 54/265 81/260 111/267 821 | f 35/261 83/254 81/260 822 | f 113/252 55/255 82/253 823 | f 55/255 113/252 83/254 824 | f 62/269 67/268 63/270 825 | f 63/270 65/265 89/267 826 | f 65/265 63/270 67/268 827 | f 119/265 69/260 93/267 828 | f 120/261 94/254 69/260 829 | f 99/251 128/252 72/253 830 | f 72/253 130/254 100/255 831 | f 103/268 134/269 74/262 832 | f 102/263 74/262 134/269 833 | f 139/256 108/258 78/257 834 | f 78/257 109/248 139/256 835 | f 140/270 110/262 80/268 836 | f 80/268 111/267 140/270 837 | f 141/259 82/253 112/251 838 | f 82/253 141/259 113/252 839 | f 138/269 84/264 110/262 840 | f 148/268 93/267 118/270 841 | f 69/260 119/265 120/261 842 | f 93/267 148/268 119/265 843 | f 94/254 120/261 149/255 844 | f 149/255 121/252 94/254 845 | f 98/245 156/246 126/247 846 | f 127/258 158/259 99/251 847 | f 128/252 99/251 158/259 848 | f 130/254 72/253 128/252 849 | f 100/255 159/260 129/261 850 | f 159/260 100/255 130/254 851 | f 133/265 161/270 103/268 852 | f 134/269 103/268 161/270 853 | f 165/266 112/251 108/258 854 | f 108/258 139/256 165/266 855 | f 110/262 140/270 138/269 856 | f 112/251 165/266 141/259 857 | f 84/264 138/269 164/263 858 | f 173/247 116/250 145/246 859 | f 175/262 118/270 147/269 860 | f 118/270 175/262 148/268 861 | f 121/252 149/255 176/253 862 | f 176/253 150/259 121/252 863 | f 153/264 147/269 124/263 864 | f 126/247 180/248 155/249 865 | f 156/246 98/245 162/250 866 | f 180/248 126/247 156/246 867 | f 157/257 181/266 127/258 868 | f 158/259 127/258 181/266 869 | f 129/261 182/267 133/265 870 | f 182/267 129/261 159/260 871 | f 161/270 133/265 182/267 872 | f 191/249 145/246 172/248 873 | f 116/250 173/247 189/245 874 | f 145/246 191/249 173/247 875 | f 147/269 153/264 175/262 876 | f 150/259 176/253 193/251 877 | f 193/251 177/266 150/259 878 | f 195/256 155/249 180/248 879 | f 155/249 195/256 157/257 880 | f 181/266 157/257 195/256 881 | f 199/257 172/248 190/256 882 | f 172/248 199/257 191/249 883 | f 177/266 193/251 201/258 884 | f 201/258 190/256 177/266 885 | f 190/256 201/258 199/257 886 | usemtl material_1_24 887 | s off 888 | f 3/264 2/264 1/264 889 | f 4/264 1/264 2/264 890 | f 2/264 3/264 5/264 891 | f 1/264 6/264 3/264 892 | f 4/264 7/264 1/264 893 | f 8/264 4/264 2/264 894 | f 9/264 5/264 3/264 895 | f 2/264 5/264 10/264 896 | f 11/264 3/264 6/264 897 | f 12/264 6/264 1/264 898 | f 7/264 13/264 1/264 899 | f 7/264 4/264 14/264 900 | f 8/264 15/264 4/264 901 | f 16/264 8/264 2/264 902 | f 9/264 17/264 5/264 903 | f 9/264 3/264 18/264 904 | f 19/264 10/264 5/264 905 | f 2/264 10/264 16/264 906 | f 11/264 18/264 3/264 907 | f 11/264 6/264 10/264 908 | f 12/264 20/264 6/264 909 | f 21/264 12/264 1/264 910 | f 13/264 22/264 1/264 911 | f 13/264 7/264 23/264 912 | f 24/264 14/264 4/264 913 | f 15/264 26/264 4/264 914 | f 15/264 8/264 27/264 915 | f 8/264 16/264 28/264 916 | f 29/264 5/264 17/264 917 | f 9/264 18/264 17/264 918 | f 19/264 18/264 10/264 919 | f 19/264 5/264 30/264 920 | f 16/264 10/264 31/264 921 | f 11/264 10/264 18/264 922 | f 32/264 10/264 6/264 923 | f 33/264 6/264 20/264 924 | f 20/264 12/264 34/264 925 | f 12/264 21/264 35/264 926 | f 1/264 36/264 21/264 927 | f 1/264 22/264 36/264 928 | f 13/264 37/264 22/264 929 | f 38/264 23/264 7/264 930 | f 13/264 23/264 37/264 931 | f 40/264 24/264 4/264 932 | f 26/264 41/264 4/264 933 | f 27/264 8/264 43/264 934 | f 28/264 16/264 37/264 935 | f 28/264 45/264 8/264 936 | f 29/264 30/264 5/264 937 | f 17/264 46/264 29/264 938 | f 46/264 17/264 18/264 939 | f 18/264 19/264 46/264 940 | f 30/264 46/264 19/264 941 | f 31/264 10/264 47/264 942 | f 31/264 48/264 16/264 943 | f 49/264 10/264 32/264 944 | f 32/264 6/264 50/264 945 | f 51/264 6/264 33/264 946 | f 20/264 52/264 33/264 947 | f 34/264 12/264 53/264 948 | f 35/264 21/264 54/264 949 | f 55/264 12/264 35/264 950 | f 56/264 21/264 36/264 951 | f 57/264 36/264 22/264 952 | f 58/264 22/264 37/264 953 | f 38/264 59/264 23/264 954 | f 60/264 37/264 23/264 955 | f 41/264 40/264 4/264 956 | f 25/264 62/264 38/264 957 | f 39/264 63/264 25/264 958 | f 43/264 8/264 65/264 959 | f 37/264 16/264 58/264 960 | f 28/264 37/264 59/264 961 | f 67/264 8/264 45/264 962 | f 30/264 29/264 46/264 963 | f 68/264 47/264 10/264 964 | f 47/264 69/264 31/264 965 | f 70/264 16/264 48/264 966 | f 71/264 48/264 31/264 967 | f 68/264 10/264 49/264 968 | f 32/264 72/264 49/264 969 | f 50/264 6/264 73/264 970 | f 50/264 74/264 32/264 971 | f 75/264 6/264 51/264 972 | f 51/264 33/264 76/264 973 | f 77/264 33/264 52/264 974 | f 53/264 12/264 78/264 975 | f 54/264 21/264 80/264 976 | f 82/264 12/264 55/264 977 | f 84/264 21/264 56/264 978 | f 56/264 36/264 85/264 979 | f 36/264 57/264 86/264 980 | f 57/264 22/264 87/264 981 | f 58/264 88/264 22/264 982 | f 60/264 23/264 59/264 983 | f 62/264 59/264 38/264 984 | f 60/264 59/264 37/264 985 | f 61/264 89/264 39/264 986 | f 63/264 62/264 25/264 987 | f 89/264 63/264 39/264 988 | f 42/264 44/264 64/264 989 | f 65/264 8/264 67/264 990 | f 44/264 66/264 90/264 991 | f 58/264 16/264 91/264 992 | f 68/264 92/264 47/264 993 | f 69/264 93/264 31/264 994 | f 94/264 69/264 47/264 995 | f 70/264 91/264 16/264 996 | f 70/264 48/264 95/264 997 | f 71/264 96/264 48/264 998 | f 97/264 71/264 31/264 999 | f 49/264 98/264 68/264 1000 | f 72/264 99/264 49/264 1001 | f 72/264 32/264 100/264 1002 | f 73/264 6/264 101/264 1003 | f 50/264 73/264 102/264 1004 | f 103/264 32/264 74/264 1005 | f 104/264 6/264 75/264 1006 | f 51/264 105/264 75/264 1007 | f 106/264 76/264 33/264 1008 | f 51/264 76/264 105/264 1009 | f 77/264 107/264 33/264 1010 | f 52/264 79/264 77/264 1011 | f 78/264 12/264 108/264 1012 | f 80/264 21/264 110/264 1013 | f 112/264 12/264 82/264 1014 | f 84/264 110/264 21/264 1015 | f 106/264 84/264 56/264 1016 | f 86/264 85/264 36/264 1017 | f 56/264 85/264 76/264 1018 | f 87/264 86/264 57/264 1019 | f 114/264 87/264 22/264 1020 | f 114/264 22/264 88/264 1021 | f 88/264 58/264 115/264 1022 | f 66/264 89/264 61/264 1023 | f 90/264 66/264 61/264 1024 | f 64/264 44/264 90/264 1025 | f 91/264 115/264 58/264 1026 | f 47/264 92/264 116/264 1027 | f 68/264 117/264 92/264 1028 | f 93/264 118/264 31/264 1029 | f 121/264 94/264 47/264 1030 | f 70/264 95/264 91/264 1031 | f 122/264 95/264 48/264 1032 | f 122/264 48/264 96/264 1033 | f 71/264 123/264 96/264 1034 | f 71/264 97/264 123/264 1035 | f 124/264 97/264 31/264 1036 | f 98/264 125/264 68/264 1037 | f 98/264 49/264 126/264 1038 | f 99/264 127/264 49/264 1039 | f 100/264 32/264 129/264 1040 | f 101/264 6/264 131/264 1041 | f 101/264 117/264 73/264 1042 | f 132/264 102/264 73/264 1043 | f 133/264 32/264 103/264 1044 | f 104/264 131/264 6/264 1045 | f 104/264 75/264 135/264 1046 | f 136/264 75/264 105/264 1047 | f 76/264 106/264 56/264 1048 | f 106/264 33/264 107/264 1049 | f 137/264 105/264 76/264 1050 | f 77/264 138/264 107/264 1051 | f 79/264 109/264 77/264 1052 | f 108/264 12/264 112/264 1053 | f 81/264 111/264 77/264 1054 | f 83/264 81/264 77/264 1055 | f 113/264 83/264 77/264 1056 | f 106/264 107/264 84/264 1057 | f 86/264 142/264 85/264 1058 | f 137/264 76/264 85/264 1059 | f 86/264 87/264 142/264 1060 | f 114/264 142/264 87/264 1061 | f 88/264 143/264 114/264 1062 | f 115/264 95/264 88/264 1063 | f 115/264 91/264 95/264 1064 | f 116/264 92/264 144/264 1065 | f 116/264 145/264 47/264 1066 | f 146/264 92/264 117/264 1067 | f 125/264 117/264 68/264 1068 | f 118/264 147/264 31/264 1069 | f 150/264 121/264 47/264 1070 | f 122/264 151/264 95/264 1071 | f 96/264 151/264 122/264 1072 | f 152/264 96/264 123/264 1073 | f 144/264 123/264 97/264 1074 | f 124/264 153/264 97/264 1075 | f 124/264 31/264 147/264 1076 | f 125/264 98/264 154/264 1077 | f 155/264 126/264 49/264 1078 | f 127/264 157/264 49/264 1079 | f 129/264 32/264 133/264 1080 | f 101/264 131/264 160/264 1081 | f 125/264 73/264 117/264 1082 | f 101/264 160/264 117/264 1083 | f 132/264 154/264 102/264 1084 | f 132/264 73/264 154/264 1085 | f 134/264 102/264 162/264 1086 | f 104/264 135/264 131/264 1087 | f 136/264 135/264 75/264 1088 | f 136/264 105/264 135/264 1089 | f 137/264 163/264 105/264 1090 | f 164/264 107/264 138/264 1091 | f 140/264 138/264 77/264 1092 | f 109/264 139/264 77/264 1093 | f 111/264 140/264 77/264 1094 | f 141/264 113/264 77/264 1095 | f 164/264 84/264 107/264 1096 | f 166/264 85/264 142/264 1097 | f 137/264 85/264 166/264 1098 | f 114/264 167/264 142/264 1099 | f 143/264 168/264 114/264 1100 | f 169/264 143/264 88/264 1101 | f 170/264 88/264 95/264 1102 | f 144/264 92/264 123/264 1103 | f 144/264 171/264 116/264 1104 | f 145/264 172/264 47/264 1105 | f 146/264 174/264 92/264 1106 | f 146/264 117/264 174/264 1107 | f 120/264 171/264 119/264 1108 | f 119/264 171/264 148/264 1109 | f 149/264 171/264 120/264 1110 | f 177/264 150/264 47/264 1111 | f 170/264 95/264 151/264 1112 | f 96/264 152/264 151/264 1113 | f 152/264 123/264 178/264 1114 | f 144/264 97/264 171/264 1115 | f 179/264 97/264 153/264 1116 | f 162/264 154/264 98/264 1117 | f 125/264 154/264 73/264 1118 | f 157/264 155/264 49/264 1119 | f 128/264 130/264 158/264 1120 | f 130/264 159/264 181/264 1121 | f 183/264 160/264 131/264 1122 | f 184/264 117/264 160/264 1123 | f 162/264 102/264 154/264 1124 | f 161/264 134/264 156/264 1125 | f 156/264 134/264 162/264 1126 | f 183/264 131/264 135/264 1127 | f 185/264 135/264 105/264 1128 | f 185/264 105/264 163/264 1129 | f 166/264 163/264 137/264 1130 | f 139/264 165/264 77/264 1131 | f 165/264 141/264 77/264 1132 | f 166/264 142/264 167/264 1133 | f 168/264 167/264 114/264 1134 | f 143/264 186/264 168/264 1135 | f 169/264 187/264 143/264 1136 | f 170/264 169/264 88/264 1137 | f 188/264 123/264 92/264 1138 | f 189/264 116/264 171/264 1139 | f 172/264 190/264 47/264 1140 | f 192/264 92/264 174/264 1141 | f 184/264 174/264 117/264 1142 | f 148/264 171/264 175/264 1143 | f 176/264 171/264 149/264 1144 | f 190/264 177/264 47/264 1145 | f 151/264 169/264 170/264 1146 | f 152/264 194/264 151/264 1147 | f 188/264 178/264 123/264 1148 | f 152/264 178/264 194/264 1149 | f 179/264 171/264 97/264 1150 | f 179/264 153/264 171/264 1151 | f 180/264 161/264 156/264 1152 | f 158/264 130/264 181/264 1153 | f 159/264 182/264 195/264 1154 | f 181/264 159/264 195/264 1155 | f 183/264 135/264 160/264 1156 | f 184/264 160/264 196/264 1157 | f 182/264 161/264 180/264 1158 | f 197/264 135/264 185/264 1159 | f 163/264 160/264 185/264 1160 | f 167/264 163/264 166/264 1161 | f 168/264 196/264 167/264 1162 | f 186/264 196/264 168/264 1163 | f 187/264 186/264 143/264 1164 | f 198/264 187/264 169/264 1165 | f 188/264 92/264 178/264 1166 | f 189/264 171/264 173/264 1167 | f 173/264 171/264 191/264 1168 | f 192/264 178/264 92/264 1169 | f 192/264 174/264 200/264 1170 | f 184/264 200/264 174/264 1171 | f 175/264 171/264 153/264 1172 | f 193/264 171/264 176/264 1173 | f 151/264 194/264 169/264 1174 | f 192/264 194/264 178/264 1175 | f 195/264 182/264 180/264 1176 | f 197/264 160/264 135/264 1177 | f 196/264 160/264 163/264 1178 | f 186/264 184/264 196/264 1179 | f 197/264 185/264 160/264 1180 | f 202/264 163/264 167/264 1181 | f 167/264 196/264 203/264 1182 | f 187/264 200/264 186/264 1183 | f 187/264 198/264 194/264 1184 | f 204/264 198/264 169/264 1185 | f 191/264 171/264 199/264 1186 | f 200/264 194/264 192/264 1187 | f 200/264 184/264 186/264 1188 | f 201/264 171/264 193/264 1189 | f 169/264 194/264 204/264 1190 | f 163/264 202/264 196/264 1191 | f 203/264 202/264 167/264 1192 | f 203/264 196/264 202/264 1193 | f 194/264 200/264 187/264 1194 | f 204/264 194/264 198/264 1195 | f 199/264 171/264 201/264 1196 | s 1 1197 | f 14/264 25/264 7/264 1198 | f 24/264 39/264 14/264 1199 | f 38/264 7/264 25/264 1200 | f 25/264 14/264 39/264 1201 | f 15/264 42/264 26/264 1202 | f 27/264 44/264 15/264 1203 | f 20/264 34/264 52/264 1204 | f 39/264 24/264 61/264 1205 | f 40/264 61/264 24/264 1206 | f 26/264 64/264 41/264 1207 | f 64/264 26/264 42/264 1208 | f 42/264 15/264 44/264 1209 | f 43/264 66/264 27/264 1210 | f 44/264 27/264 66/264 1211 | f 28/264 59/264 45/264 1212 | f 34/264 53/264 79/264 1213 | f 79/264 52/264 34/264 1214 | f 35/264 54/264 81/264 1215 | f 55/264 35/264 83/264 1216 | f 61/264 40/264 90/264 1217 | f 41/264 90/264 40/264 1218 | f 90/264 41/264 64/264 1219 | f 65/264 89/264 43/264 1220 | f 66/264 43/264 89/264 1221 | f 45/264 62/264 67/264 1222 | f 62/264 45/264 59/264 1223 | f 50/264 102/264 74/264 1224 | f 53/264 78/264 109/264 1225 | f 109/264 79/264 53/264 1226 | f 54/264 80/264 111/264 1227 | f 111/264 81/264 54/264 1228 | f 81/264 83/264 35/264 1229 | f 82/264 55/264 113/264 1230 | f 83/264 113/264 55/264 1231 | f 63/264 67/264 62/264 1232 | f 89/264 65/264 63/264 1233 | f 67/264 63/264 65/264 1234 | f 93/264 69/264 119/264 1235 | f 69/264 94/264 120/264 1236 | f 72/264 128/264 99/264 1237 | f 100/264 130/264 72/264 1238 | f 74/264 134/264 103/264 1239 | f 134/264 74/264 102/264 1240 | f 78/264 108/264 139/264 1241 | f 139/264 109/264 78/264 1242 | f 80/264 110/264 140/264 1243 | f 140/264 111/264 80/264 1244 | f 112/264 82/264 141/264 1245 | f 113/264 141/264 82/264 1246 | f 110/264 84/264 138/264 1247 | f 118/264 93/264 148/264 1248 | f 120/264 119/264 69/264 1249 | f 119/264 148/264 93/264 1250 | f 149/264 120/264 94/264 1251 | f 94/264 121/264 149/264 1252 | f 126/264 156/264 98/264 1253 | f 99/264 158/264 127/264 1254 | f 158/264 99/264 128/264 1255 | f 128/264 72/264 130/264 1256 | f 129/264 159/264 100/264 1257 | f 130/264 100/264 159/264 1258 | f 103/264 161/264 133/264 1259 | f 161/264 103/264 134/264 1260 | f 108/264 112/264 165/264 1261 | f 165/264 139/264 108/264 1262 | f 138/264 140/264 110/264 1263 | f 141/264 165/264 112/264 1264 | f 164/264 138/264 84/264 1265 | f 145/264 116/264 173/264 1266 | f 147/264 118/264 175/264 1267 | f 148/264 175/264 118/264 1268 | f 176/264 149/264 121/264 1269 | f 121/264 150/264 176/264 1270 | f 124/264 147/264 153/264 1271 | f 155/264 180/264 126/264 1272 | f 162/264 98/264 156/264 1273 | f 156/264 126/264 180/264 1274 | f 127/264 181/264 157/264 1275 | f 181/264 127/264 158/264 1276 | f 133/264 182/264 129/264 1277 | f 159/264 129/264 182/264 1278 | f 182/264 133/264 161/264 1279 | f 172/264 145/264 191/264 1280 | f 189/264 173/264 116/264 1281 | f 173/264 191/264 145/264 1282 | f 175/264 153/264 147/264 1283 | f 193/264 176/264 150/264 1284 | f 150/264 177/264 193/264 1285 | f 180/264 155/264 195/264 1286 | f 157/264 195/264 155/264 1287 | f 195/264 157/264 181/264 1288 | f 190/264 172/264 199/264 1289 | f 191/264 199/264 172/264 1290 | f 201/264 193/264 177/264 1291 | f 177/264 190/264 201/264 1292 | f 199/264 201/264 190/264 1293 | -------------------------------------------------------------------------------- /models/lego.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/models/lego.pkl -------------------------------------------------------------------------------- /models/voxel_26.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/models/voxel_26.pkl -------------------------------------------------------------------------------- /services/generator.py: -------------------------------------------------------------------------------- 1 | from src.env.lego_env import LegoEnv 2 | from src.env.voxel_env import VoxelEnv 3 | from src.data_loader import load_dummy, Target 4 | from stable_baselines import TD3 5 | from stable_baselines.td3.policies import MlpPolicy, FeedForwardPolicy 6 | from stable_baselines.ddpg.noise import NormalActionNoise, OrnsteinUhlenbeckActionNoise 7 | import numpy as np 8 | from src.policy import LnCnn3dPolicy, cnn_3d 9 | from src.common import Bound 10 | from flask import Flask, request, jsonify 11 | import math 12 | from collections import OrderedDict 13 | from src.common import Vector 14 | from src.env.base_env import SamplingDistrubution 15 | 16 | RESOLUTION = 16 17 | 18 | WINDOW_WIDTH = 5 19 | IS_CNN = False 20 | MLP_LAYERS = [256, 128] 21 | 22 | targets, _ = load_dummy(resolution=RESOLUTION, num=1) 23 | 24 | bound = Bound(x_len=RESOLUTION, y_len=RESOLUTION, z_len=RESOLUTION) 25 | lego_env = LegoEnv(bound=bound, targets=targets, window_width=WINDOW_WIDTH, horizontal_steps=[1, 2], vertical_steps=[1]) 26 | voxel_env = VoxelEnv(bound=bound, targets=targets, window_width=WINDOW_WIDTH, possible_actions=26) 27 | 28 | class CustomMlpPolicy(MlpPolicy): 29 | 30 | def __init__(self, sess, ob_space, ac_space, n_env=1, n_steps=1, n_batch=None, reuse=False, **_kwargs): 31 | super(CustomMlpPolicy, self).__init__(sess, ob_space, ac_space, layers=MLP_LAYERS, **_kwargs) 32 | 33 | lego_model = TD3.load('./models/lego.pkl', policy=CustomMlpPolicy) 34 | voxel_model = TD3.load('./models/voxel_26.pkl', policy=CustomMlpPolicy) 35 | 36 | def generate(voxels, model_type='lego'): 37 | 38 | if model_type == 'lego': 39 | 40 | model = lego_model 41 | env = lego_env 42 | max_failure = 0 43 | iteration = 1000 44 | 45 | elif model_type == 'voxel': 46 | 47 | model = voxel_model 48 | env = voxel_env 49 | max_failure = 50 50 | iteration = 1200 51 | 52 | else: 53 | 54 | return None 55 | 56 | target = Target(grid=voxels) 57 | env.targets = np.array([target]) 58 | 59 | env.target_index = 0 60 | env.is_train = False 61 | env.max_failure = max_failure 62 | env.sampling_distrubution = SamplingDistrubution.BOTTOM 63 | env.possible_steps = 2000 64 | 65 | obs = env.reset() 66 | 67 | best_path = None 68 | best_length = -1 69 | 70 | for i in range(iteration): 71 | 72 | action, _states = model.predict(obs) 73 | obs, rewards, done, info = env.step(action) 74 | 75 | if done: 76 | 77 | if len(env.path_dic) > best_length: 78 | best_length = len(env.path_dic) 79 | best_path = env.path_dic 80 | obs = env.reset() 81 | continue 82 | 83 | return best_path -------------------------------------------------------------------------------- /services/voxelizer.py: -------------------------------------------------------------------------------- 1 | import trimesh 2 | import numpy as np 3 | from trimesh.util import concatenate 4 | from trimesh import Scene 5 | 6 | RESOLUTION = 16 7 | SIZE = RESOLUTION - 2 8 | 9 | def voxelize(path): 10 | 11 | mesh = trimesh.load_mesh(path, process=True, validate=True) 12 | 13 | if isinstance(mesh, Scene): 14 | 15 | geometries = list(mesh.geometry.values()) 16 | mesh = concatenate(geometries) 17 | 18 | x_size = mesh.bounds[1][0] - mesh.bounds[0][0] 19 | y_size = mesh.bounds[1][1] - mesh.bounds[0][1] 20 | z_size = mesh.bounds[1][2] - mesh.bounds[0][2] 21 | 22 | max_size = max([x_size, y_size, z_size]) 23 | 24 | mesh.bounds, x_size, y_size, z_size, max_size 25 | 26 | scale = SIZE / max_size 27 | 28 | mesh.apply_scale(scale) 29 | 30 | voxels = mesh.voxelized(pitch=1.0) 31 | voxels = voxels.fill().matrix 32 | 33 | grid = np.zeros((RESOLUTION, RESOLUTION, RESOLUTION)) 34 | 35 | coordinates = np.argwhere(voxels == True) 36 | 37 | for c in coordinates: 38 | grid[c[0], c[2], c[1]] = 1 39 | 40 | return grid 41 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/src/__init__.py -------------------------------------------------------------------------------- /src/common.py: -------------------------------------------------------------------------------- 1 | class Vector(): 2 | 3 | def __init__(self, x=0, y=0, z=0): 4 | 5 | self.x = x 6 | self.y = y 7 | self.z = z 8 | 9 | def __eq__(self, other): 10 | 11 | if not isinstance(other, Vector): 12 | return False 13 | 14 | return self.x == other.x and self.y == other.y and self.z == other.z 15 | 16 | def copy(self): 17 | 18 | return Vector(self.x, self.y, self.z) 19 | 20 | def __repr__(self): 21 | 22 | return '%.3f, %.3f, %.3f' % (self.x, self.y, self.z) 23 | 24 | def dist(self, v): 25 | 26 | xd = self.x - v.x 27 | yd = self.y - v.y 28 | zd = self.z - v.z 29 | 30 | return math.sqrt(xd*xd + yd*yd + zd*zd) 31 | 32 | @property 33 | def length(self): 34 | return math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z) 35 | 36 | @property 37 | def unit(self): 38 | l=self.length 39 | if l==0: return self 40 | return self.divide(l) 41 | 42 | def add(self,vector): 43 | self.x+=int(vector.x) 44 | self.y+=int(vector.y) 45 | self.z+=int(vector.z) 46 | return self 47 | 48 | def divide(self,factor): 49 | self.x/=factor 50 | self.y/=factor 51 | self.z/=factor 52 | return self 53 | 54 | def __add__(self, other): 55 | vector = Vector(self.x, self.y, self.z) 56 | return vector.add(other) 57 | 58 | def scale(self,factor): 59 | self.x*=factor 60 | self.y*=factor 61 | self.z*=factor 62 | return self 63 | 64 | def __mul__(self, factor): 65 | vector = Vector(self.x, self.y, self.z) 66 | return vector.scale(factor) 67 | 68 | # for python 3 69 | def __truediv__(self, factor): 70 | vector = Vector(self.x, self.y, self.z) 71 | return vector.divide(factor) 72 | 73 | # for python 2 74 | def __div__(self, factor): 75 | vector = Vector(self.x, self.y, self.z) 76 | return vector.divide(factor) 77 | 78 | class Bound(): 79 | 80 | def __init__(self, x_len=0, y_len=0, z_len=1): 81 | 82 | self.x_len = x_len 83 | self.y_len = y_len 84 | self.z_len = z_len 85 | 86 | def is_inside(self, vector): 87 | 88 | if 0 <= vector.x < self.x_len and 0 <= vector.y < self.y_len and 0<= vector.z < self.z_len: 89 | 90 | return True 91 | 92 | else: 93 | 94 | return False -------------------------------------------------------------------------------- /src/data_loader.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import numpy as np 3 | 4 | class Target(): 5 | 6 | def __init__(self, grid): 7 | 8 | self.grid = grid 9 | 10 | @property 11 | def augmented(self): 12 | 13 | grid = self.grid 14 | 15 | if np.random.random() < 0.5: 16 | grid = np.swapaxes(grid, 0, 1) 17 | 18 | if np.random.random() < 0.5: 19 | grid = np.swapaxes(grid, 0, 2) 20 | 21 | if np.random.random() < 0.5: 22 | grid = np.swapaxes(grid, 1, 2) 23 | 24 | return Target(grid=grid) 25 | 26 | def load_voxels(resolution=32, path='../voxelizer/32', furniture_list = ['table', 'bookshelf', 'chair', 'lamp'], num=400): 27 | 28 | # targets = [] 29 | 30 | # voxel = np.zeros((resolution, resolution, resolution)) 31 | 32 | # voxel[0:6, 0:50, 0:6] = 1 33 | # target = Target(grid=voxel) 34 | # targets.append(target) 35 | 36 | # return np.array(targets) 37 | 38 | voxels = [] 39 | 40 | paths = [] 41 | 42 | for f in furniture_list: 43 | 44 | p = '%s/%s/*' % (path, f) 45 | files = glob.glob(p)[:num] 46 | paths.extend(files) 47 | 48 | voxel_dic = {} 49 | average = [] 50 | 51 | for i, path in enumerate(paths): 52 | 53 | voxel = np.zeros((resolution, resolution, resolution)) 54 | indexes = np.load(path) 55 | 56 | if len(indexes) < 5: 57 | print('FILE NOT FOUND') 58 | continue 59 | 60 | for index in indexes: 61 | 62 | voxel[index[0], index[1], index[2]] = 1 63 | 64 | voxel = np.swapaxes(voxel, 0, 1) 65 | voxel = np.swapaxes(voxel, 0, 2) 66 | 67 | if voxel.sum() == 0: 68 | print('no voxel') 69 | continue 70 | 71 | average.append(voxel.sum()) 72 | voxels.append(voxel) 73 | 74 | average = np.array(average) 75 | 76 | if len(average) == 0: 77 | raise ValueError('Data not found') 78 | 79 | print('average:', average.mean(), 'max:', average.max(), 'min:', average.min(), 'median:', np.median(average)) 80 | 81 | targets = [] 82 | target_map = {} 83 | 84 | for i, v in enumerate(voxels): 85 | 86 | target = Target(grid=v) 87 | targets.append(target) 88 | target_map[i] = paths[i] 89 | 90 | return np.array(targets), target_map 91 | 92 | def load_dummy(resolution=32, num=1): 93 | 94 | targets = [] 95 | 96 | voxel = np.zeros((resolution, resolution, resolution)) 97 | 98 | for i in range(num): 99 | voxel[0:6, 0:50, 0:6] = 1 100 | target = Target(grid=voxel) 101 | targets.append(target) 102 | 103 | return np.array(targets), {} -------------------------------------------------------------------------------- /src/env/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/src/env/__init__.py -------------------------------------------------------------------------------- /src/env/base_env.py: -------------------------------------------------------------------------------- 1 | import gym 2 | import numpy as np 3 | import gym.spaces 4 | from src.common import Vector 5 | import math 6 | import itertools 7 | from enum import Enum 8 | 9 | class SamplingDistrubution(Enum): 10 | RANDOM = 1 11 | EDGE = 2 12 | BOTTOM = 3 13 | 14 | class BaseEnv(gym.Env): 15 | 16 | metadata = {'render.modes': ['human', 'ansi']} 17 | 18 | def __init__(self, bound, targets, window_width=3, max_failure=5, is_cnn=False, action_space=6, vertical_steps=[1], horizontal_steps=[1], sampling_distrubution=SamplingDistrubution.EDGE): 19 | 20 | super().__init__() 21 | 22 | if window_width % 2 == 0: 23 | 24 | raise ValueError('window_width must be odd number') 25 | 26 | self.bound = bound 27 | self.targets = targets 28 | self.target_index = 0 29 | self.max_failure = max_failure 30 | self.grid = np.zeros((self.bound.x_len, self.bound.y_len, self.bound.z_len)) 31 | self.window_width = window_width 32 | self.is_cnn = is_cnn 33 | self.sampling_distrubution = sampling_distrubution 34 | 35 | self.vertical_steps = vertical_steps 36 | self.horizontal_steps = horizontal_steps 37 | 38 | if isinstance(self.horizontal_steps, int): 39 | self.vertical_steps = [self.horizontal_steps] 40 | 41 | if isinstance(self.vertical_steps, int): 42 | self.vertical_steps = [self.vertical_steps] 43 | 44 | observation_space = (window_width, window_width, window_width, 3) if is_cnn else (window_width, window_width, window_width) 45 | 46 | self.action_space = gym.spaces.Box(low=-1, high=1, shape=(action_space,), dtype=np.float32) 47 | self.observation_space = gym.spaces.Box( 48 | low=0 if is_cnn else -1, 49 | high=1, 50 | shape=observation_space, 51 | dtype=np.float 52 | ) 53 | 54 | self.is_train = True 55 | self.map = {} 56 | 57 | self.test_possible_steps = 999 58 | 59 | self.reset() 60 | 61 | def reset(self): 62 | 63 | if self.is_train: 64 | 65 | indexes = np.arange(0, len(self.targets)) 66 | 67 | self.target_index = np.random.choice(indexes) 68 | self.target = self.targets[self.target_index].augmented 69 | 70 | else: 71 | 72 | self.target = self.targets[self.target_index] 73 | 74 | self.position = self.sample() 75 | 76 | self.grid = np.zeros_like(self.grid) 77 | self.done = False 78 | self.steps = 0 79 | self.path = [] 80 | self.prev_action = -1 81 | self.prev_pos = None 82 | self.failed = 0 83 | self._pass() 84 | 85 | return self.state 86 | 87 | def step(self, action): 88 | 89 | d = self._get_vector(action) 90 | 91 | s = '%d%d%d' % (d.x, d.y, d.z) 92 | if not s in self.map: 93 | self.map[s] = 1 94 | print(self.map) 95 | else: 96 | self.map[s] += 1 97 | 98 | remained = True if d.x == 0 and d.y == 0 and d.z == 0 else False 99 | 100 | next_last_position = self.position + d 101 | next_positions = self.get_next_positions(next_last_position) 102 | 103 | is_outside = False 104 | 105 | for p in next_positions: 106 | is_outside = self.bound.is_inside(p) == False 107 | 108 | if not is_outside: 109 | self.position = next_last_position 110 | 111 | reward = self._get_reward(next_positions, is_outside, remained) 112 | 113 | if not is_outside and not remained: 114 | self._pass(next_positions) 115 | 116 | done = False 117 | 118 | if reward == -1: 119 | 120 | # oustide 121 | done = True 122 | 123 | elif reward <= 0: 124 | 125 | # self-intersection or remaining at the same position 126 | self.failed += 1 127 | if self.failed >= self.max_failure: 128 | 129 | done = True 130 | 131 | if not self.is_train and len(self.path) > self.test_possible_steps: 132 | done = True 133 | 134 | self.prev_action = d 135 | # print('v', d, 'reward', reward, 'action', action, 'done', done) 136 | 137 | return self.state, reward, done, {} 138 | 139 | def _get_vector(self, action): 140 | 141 | raise NotImplementedError('not implemented') 142 | 143 | def render(self, mode='human', close=False): 144 | 145 | return '' 146 | 147 | def close(self): 148 | pass 149 | 150 | def _seed(self, seed=None): 151 | pass 152 | 153 | def _get_reward(self, next_positions, is_outside, remained): 154 | 155 | raise NotImplementedError('not implemented') 156 | 157 | def _pass(self, positions=None): 158 | 159 | if not positions: 160 | positions = [self.position] 161 | 162 | self.path.append(positions) 163 | 164 | for p in positions: 165 | self.grid[p.x][p.y][p.z] = 1 166 | 167 | self.prev_pos = positions[-1] 168 | 169 | 170 | def _get_binary_value(self, grid, position): 171 | 172 | if not position: 173 | position = self.position 174 | 175 | if len(grid.shape) == 2: 176 | 177 | return grid[position.x][position.y] 178 | 179 | else: 180 | 181 | return grid[position.x][position.y][position.z] 182 | 183 | @property 184 | def state(self): 185 | 186 | w = int(self.window_width / 2) 187 | 188 | if self.is_cnn: 189 | window = np.zeros((self.window_width, self.window_width, self.window_width, 3)) 190 | else: 191 | window = np.zeros((self.window_width, self.window_width, self.window_width)) 192 | 193 | vals = np.arange(-w, w+1, 1) 194 | 195 | for i, dx in enumerate(vals): 196 | 197 | for j, dy in enumerate(vals): 198 | 199 | for k, dz in enumerate(vals): 200 | 201 | if dx == 0 and dy == 0 and dz == 0: 202 | 203 | if self.is_cnn: 204 | window[i, j, k, :] = 1 205 | else: 206 | window[i, j, k] = 0 207 | 208 | else: 209 | 210 | x = self.position.x + dx 211 | y = self.position.y + dy 212 | z = self.position.z + dz 213 | 214 | if 0 <= x < self.bound.x_len and 0 <= y < self.bound.y_len and 0 <= z < self.bound.z_len: 215 | 216 | is_target_inside = self.target.grid[x, y, z] == 1 217 | is_self_intersection = self.grid[x, y, z] == 1 218 | 219 | if is_target_inside and not is_self_intersection: 220 | 221 | if self.is_cnn: 222 | window[i, j, k, 0] = 1 223 | else: 224 | window[i, j, k] = 1 225 | 226 | elif is_target_inside and is_self_intersection: 227 | 228 | if self.is_cnn: 229 | window[i, j, k, 1] = 1 230 | else: 231 | window[i, j, k] = -0.5 232 | 233 | else: 234 | 235 | if self.is_cnn: 236 | window[i, j, k, 2] = 1 237 | else: 238 | window[i, j, k] = -1 239 | else: 240 | 241 | if self.is_cnn: 242 | window[i, j, k, 2] = 1 243 | else: 244 | window[i, j, k] = -1 245 | return window 246 | 247 | def sample(self): 248 | 249 | if self.sampling_distrubution == SamplingDistrubution.EDGE: 250 | 251 | candidates = np.argwhere(self.target.grid == 1) 252 | 253 | while True: 254 | 255 | i = np.random.choice(list(range(len(candidates)))) 256 | 257 | x = candidates[i][0] 258 | y = candidates[i][1] 259 | z = candidates[i][2] 260 | 261 | has_outside = False 262 | 263 | for dx in [-1, 0, 1]: 264 | for dy in [-1, 0, 1]: 265 | for dz in [-1, 0, 1]: 266 | 267 | xx = x + dx 268 | yy = y + dy 269 | zz = z + dz 270 | 271 | if 0 <= xx < self.bound.x_len and 0 <= yy < self.bound.y_len and 0 <= zz < self.bound.z_len: 272 | 273 | if self.target.grid[xx, yy, zz] == 1: 274 | has_outside = True 275 | 276 | else: 277 | 278 | has_outside = True 279 | 280 | if has_outside: 281 | break 282 | 283 | return Vector(x, y, z) 284 | 285 | elif self.sampling_distrubution == SamplingDistrubution.RANDOM: 286 | 287 | candidates = np.argwhere(self.target.grid == 1) 288 | 289 | i = np.random.choice(list(range(len(candidates)))) 290 | 291 | x = candidates[i][0] 292 | y = candidates[i][1] 293 | z = candidates[i][2] 294 | 295 | return Vector(x, y, z) 296 | 297 | else: 298 | 299 | candidates = np.argwhere(self.target.grid[:,:, 0] == 1) 300 | i = np.random.choice(list(range(len(candidates)))) 301 | x = candidates[i][0] 302 | y = candidates[i][1] 303 | return Vector(x, y, 0) 304 | 305 | def get_next_positions(self, position): 306 | return [position] 307 | 308 | @property 309 | def flattened_path(self): 310 | return list(itertools.chain(*self.path)) 311 | 312 | @property 313 | def path_dic(self): 314 | 315 | dic = {} 316 | 317 | is_1d = isinstance(self.path[0], Vector) 318 | 319 | if is_1d: 320 | path = [self.path] 321 | 322 | for i, positions in enumerate(self.path): 323 | 324 | key = str(i) 325 | 326 | dic[key] = [] 327 | 328 | for p in positions: 329 | dic[key].append({"x": str(p.x), "y": str(p.y), "z": str(p.z)}) 330 | 331 | return dic -------------------------------------------------------------------------------- /src/env/lego_env.py: -------------------------------------------------------------------------------- 1 | from src.env.base_env import BaseEnv 2 | from src.common import Vector 3 | import numpy as np 4 | import math 5 | 6 | class LegoEnv(BaseEnv): 7 | 8 | def __init__(self, bound, targets, window_width=3, vertical_steps=[1,2], horizontal_steps=[1, 2], is_cnn=False): 9 | 10 | super().__init__(bound=bound, targets=targets, window_width=window_width, max_failure=0, is_cnn=is_cnn, action_space=6, horizontal_steps=horizontal_steps, vertical_steps=vertical_steps) 11 | 12 | self.unit_length = 1 / math.sqrt(len(self.horizontal_steps) * 4 + 1) 13 | 14 | def _get_vector(self, action): 15 | 16 | # x = action[0] 17 | # y = action[1] 18 | # z = action[2] 19 | 20 | px = (action[0] + 1.0) * 0.5 # 0 to 1 21 | py = (action[1] + 1.0) * 0.5 # 0 to 1 22 | pz = (action[2] + 1.0) * 0.5 # 0 to 1 23 | nx = (action[3] + 1.0) * 0.5 # 0 to 1 24 | ny = (action[4] + 1.0) * 0.5 # 0 to 1 25 | nz = (action[5] + 1.0) * 0.5 # 0 to 1 26 | 27 | x = px - nx 28 | y = py - ny 29 | z = pz - nz 30 | 31 | if abs(x) <= self.unit_length and abs(y) <= self.unit_length: 32 | 33 | x = 0 34 | y = 0 35 | 36 | elif y > x and y > -x: 37 | 38 | x = 0 39 | y = self._get_horizontal_value(y) 40 | 41 | elif y > x and y <= -x: 42 | 43 | x = -self._get_horizontal_value(x) 44 | y = 0 45 | 46 | elif y <= x and y > -x: 47 | 48 | x = self._get_horizontal_value(x) 49 | y = 0 50 | 51 | elif y <= x and y <= -x: 52 | 53 | x = 0 54 | y = -self._get_horizontal_value(y) 55 | 56 | else: 57 | raise ValueError('NA') 58 | 59 | num = len(self.vertical_steps) 60 | index = int(abs(z) * num) 61 | if index == num: 62 | index = num - 1 63 | 64 | if z <= 0: 65 | z = -self.vertical_steps[index] 66 | else: 67 | z = self.vertical_steps[index] 68 | 69 | return Vector(x, y, z) 70 | 71 | def _get_reward(self, next_positions, is_outside, remained): 72 | 73 | scale = 1 74 | 75 | if is_outside: 76 | 77 | return -1 78 | 79 | if remained: 80 | return -1 81 | 82 | worst_reward = 1 83 | 84 | for position in next_positions: 85 | 86 | if self._get_binary_value(self.target.grid, position) == 1 and self._get_binary_value(self.grid, position) == 0: 87 | 88 | reward = scale 89 | 90 | else: 91 | 92 | reward = -1 93 | 94 | if reward < worst_reward: 95 | worst_reward = reward 96 | 97 | return worst_reward 98 | 99 | def _get_horizontal_value(self, v): 100 | 101 | v = abs(v) 102 | 103 | if v <= self.unit_length: 104 | return self.horizontal_steps[0] 105 | 106 | if v >= 1: 107 | return self.horizontal_steps[-1] 108 | 109 | num = len(self.horizontal_steps) 110 | step = (1.0 - self.unit_length) / num 111 | index = int(float(v - self.unit_length) / step) 112 | 113 | return self.horizontal_steps[index] 114 | 115 | def get_next_positions(self, position): 116 | 117 | if self.prev_pos: 118 | 119 | positions = [] 120 | 121 | def generate_diff(current_value, prev_value): 122 | 123 | d = current_value - prev_value 124 | if d > 0: 125 | d = 1 126 | elif d < 0: 127 | d = -1 128 | return d 129 | 130 | dx = generate_diff(position.x, self.prev_pos.x) 131 | dy = generate_diff(position.y, self.prev_pos.y) 132 | dz = generate_diff(position.z, self.prev_pos.z) 133 | 134 | x = self.prev_pos.x 135 | y = self.prev_pos.y 136 | z = self.prev_pos.z 137 | j = 0 138 | 139 | while True: 140 | z += dz 141 | p = Vector(x, y, z) 142 | positions.append(p) 143 | 144 | if z == position.z: 145 | break 146 | 147 | j += 1 148 | if j > 100: 149 | raise ValueError('misconfiguration') 150 | 151 | is_horizontal = False 152 | 153 | if dx != 0 or dy != 0: 154 | dz = 0 155 | is_horizontal = True 156 | 157 | j = 0 158 | while is_horizontal: 159 | 160 | x = positions[-1].x + dx 161 | y = positions[-1].y + dy 162 | z = positions[-1].z 163 | 164 | p = Vector(x, y, z) 165 | positions.append(p) 166 | 167 | if p == position: 168 | break 169 | 170 | j += 1 171 | 172 | if j > 100: 173 | raise ValueError('misconfiguration') 174 | 175 | else: 176 | positions = [position] 177 | 178 | return positions -------------------------------------------------------------------------------- /src/env/voxel_env.py: -------------------------------------------------------------------------------- 1 | from src.env.base_env import BaseEnv 2 | from src.common import Vector 3 | import numpy as np 4 | import math 5 | 6 | class VoxelEnv(BaseEnv): 7 | 8 | def __init__(self, bound, targets, window_width=3, max_failure=5, is_cnn=False, vertical_steps=[1], horizontal_steps=[1], possible_actions=6): 9 | 10 | super().__init__(bound=bound, targets=targets, window_width=window_width, max_failure=max_failure, is_cnn=is_cnn, action_space=6) 11 | self.xx = [] 12 | self.yy = [] 13 | self.zz = [] 14 | self.num = 0 15 | 16 | # TODO: Voxel environment is not compatible with multiple steps now 17 | vertical_steps = [1] 18 | horizontal_steps = [1] 19 | 20 | self.possible_actions = possible_actions 21 | 22 | def _get_vector(self, action): 23 | 24 | px = (action[0] + 1.0) * 0.5 # 0 to 1 25 | py = (action[1] + 1.0) * 0.5 # 0 to 1 26 | pz = (action[2] + 1.0) * 0.5 # 0 to 1 27 | nx = (action[3] + 1.0) * 0.5 # 0 to 1 28 | ny = (action[4] + 1.0) * 0.5 # 0 to 1 29 | nz = (action[5] + 1.0) * 0.5 # 0 to 1 30 | 31 | xx = px - nx 32 | yy = py - ny 33 | zz = pz - nz 34 | 35 | if self.possible_actions == 6: 36 | 37 | ax = abs(xx) 38 | ay = abs(yy) 39 | az = abs(zz) 40 | 41 | if ax >= max([ay, az]): 42 | 43 | return Vector(1, 0, 0) if xx >= 0 else Vector(-1, 0, 0) 44 | 45 | if ay >= max([ax, az]): 46 | 47 | return Vector(0, 1, 0) if yy >= 0 else Vector(0, -1, 0) 48 | 49 | if az >= max([ay, ax]): 50 | 51 | return Vector(0, 0, 1) if zz >= 0 else Vector(0, 0, -1) 52 | 53 | print(xx, yy, zz) 54 | 55 | elif self.possible_actions == 26: 56 | 57 | def round_to_voxel(threshold): 58 | 59 | if xx < -threshold: 60 | x = -1 61 | elif xx > threshold: 62 | x = 1 63 | else: 64 | x = 0 65 | 66 | if yy < -threshold: 67 | y = -1 68 | elif yy > threshold: 69 | y = 1 70 | else: 71 | y = 0 72 | 73 | if zz < -threshold: 74 | z = -1 75 | elif zz > threshold: 76 | z = 1 77 | else: 78 | z = 0 79 | 80 | return x, y, z 81 | 82 | i = 1 83 | while i <= 2: 84 | 85 | threshold = 1.0 / (3**i) 86 | x, y, z = round_to_voxel(threshold) 87 | if x != 0 or y != 0 or z != 0: 88 | break 89 | i += 1 90 | 91 | while x == y == z == 0: 92 | 93 | x = np.random.choice([-1, 0, 1]) 94 | y = np.random.choice([-1, 0, 1]) 95 | z = np.random.choice([-1, 0, 1]) 96 | 97 | return Vector(x, y, z) 98 | 99 | elif self.possible_actions == 27: 100 | 101 | if xx < -1.0 / 3: 102 | 103 | x = -1.0 104 | 105 | elif xx > 1.0 / 3: 106 | 107 | x = 1.0 108 | 109 | else: 110 | 111 | x = 0 112 | 113 | if yy < -1.0 / 3: 114 | 115 | y = -1.0 116 | 117 | elif yy > 1.0 / 3: 118 | 119 | y = 1.0 120 | 121 | else: 122 | 123 | y = 0 124 | 125 | if zz < -1.0 / 3: 126 | 127 | z = -1.0 128 | 129 | elif zz > 1.0 / 3: 130 | 131 | z = 1.0 132 | 133 | else: 134 | 135 | z = 0 136 | 137 | return Vector(x, y, z) 138 | 139 | else: 140 | 141 | raise ValueError('possible_actions must be 6, 26 or 27.') 142 | 143 | def _get_reward(self, next_positions, is_outside, remained): 144 | 145 | if is_outside: 146 | 147 | return -1 148 | 149 | if remained: 150 | return -1 151 | 152 | worst_reward = 1 153 | 154 | for position in next_positions: 155 | 156 | if self._get_binary_value(self.target.grid, position) == 1 and self._get_binary_value(self.grid, position) == 0: 157 | 158 | reward = 1 159 | 160 | elif self._get_binary_value(self.target.grid, position) == 1 and self._get_binary_value(self.grid, position) == 1: 161 | 162 | reward = -0.5 163 | 164 | else: 165 | 166 | reward = -1 167 | 168 | if worst_reward > reward: 169 | worst_reward = reward 170 | 171 | return worst_reward -------------------------------------------------------------------------------- /src/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/src/model/__init__.py -------------------------------------------------------------------------------- /src/model/no_clipping_td3.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import multiprocessing 4 | from collections import deque 5 | import warnings 6 | 7 | import numpy as np 8 | import tensorflow as tf 9 | 10 | from stable_baselines.a2c.utils import total_episode_reward_logger 11 | from stable_baselines.common import tf_util, OffPolicyRLModel, SetVerbosity, TensorboardWriter 12 | from stable_baselines.common.vec_env import VecEnv 13 | from stable_baselines.deepq.replay_buffer import ReplayBuffer 14 | from stable_baselines.ppo2.ppo2 import safe_mean, get_schedule_fn 15 | from stable_baselines.sac.sac import get_vars 16 | from stable_baselines.td3.policies import TD3Policy 17 | from stable_baselines.td3.td3 import TD3 18 | from stable_baselines import logger 19 | 20 | 21 | class NoClippingTD3(TD3): 22 | """ 23 | Twin Delayed DDPG (TD3) 24 | Addressing Function Approximation Error in Actor-Critic Methods. 25 | Original implementation: https://github.com/sfujim/TD3 26 | Paper: https://arxiv.org/pdf/1802.09477.pdf 27 | Introduction to TD3: https://spinningup.openai.com/en/latest/algorithms/td3.html 28 | :param policy: (TD3Policy or str) The policy model to use (MlpPolicy, CnnPolicy, LnMlpPolicy, ...) 29 | :param env: (Gym environment or str) The environment to learn from (if registered in Gym, can be str) 30 | :param gamma: (float) the discount factor 31 | :param learning_rate: (float or callable) learning rate for adam optimizer, 32 | the same learning rate will be used for all networks (Q-Values and Actor networks) 33 | it can be a function of the current progress (from 1 to 0) 34 | :param buffer_size: (int) size of the replay buffer 35 | :param batch_size: (int) Minibatch size for each gradient update 36 | :param tau: (float) the soft update coefficient ("polyak update" of the target networks, between 0 and 1) 37 | :param policy_delay: (int) Policy and target networks will only be updated once every policy_delay steps 38 | per training steps. The Q values will be updated policy_delay more often (update every training step). 39 | :param action_noise: (ActionNoise) the action noise type. Cf DDPG for the different action noise type. 40 | :param target_policy_noise: (float) Standard deviation of gaussian noise added to target policy 41 | (smoothing noise) 42 | :param target_noise_clip: (float) Limit for absolute value of target policy smoothing noise. 43 | :param train_freq: (int) Update the model every `train_freq` steps. 44 | :param learning_starts: (int) how many steps of the model to collect transitions for before learning starts 45 | :param gradient_steps: (int) How many gradient update after each step 46 | :param random_exploration: (float) Probability of taking a random action (as in an epsilon-greedy strategy) 47 | This is not needed for TD3 normally but can help exploring when using HER + TD3. 48 | This hack was present in the original OpenAI Baselines repo (DDPG + HER) 49 | :param verbose: (int) the verbosity level: 0 none, 1 training information, 2 tensorflow debug 50 | :param tensorboard_log: (str) the log location for tensorboard (if None, no logging) 51 | :param _init_setup_model: (bool) Whether or not to build the network at the creation of the instance 52 | :param policy_kwargs: (dict) additional arguments to be passed to the policy on creation 53 | :param full_tensorboard_log: (bool) enable additional logging when using tensorboard 54 | Note: this has no effect on TD3 logging for now 55 | """ 56 | 57 | def __init__(self, policy, env, gamma=0.99, learning_rate=3e-4, buffer_size=50000, 58 | learning_starts=100, train_freq=100, gradient_steps=100, batch_size=128, 59 | tau=0.005, policy_delay=2, action_noise=None, 60 | target_policy_noise=0.2, target_noise_clip=0.5, 61 | random_exploration=0.0, verbose=0, tensorboard_log=None, 62 | _init_setup_model=True, policy_kwargs=None, full_tensorboard_log=False): 63 | 64 | super(NoClippingTD3, self).__init__(policy, env, gamma, learning_rate, buffer_size, 65 | learning_starts, train_freq, gradient_steps, batch_size, 66 | tau, policy_delay, action_noise, 67 | target_policy_noise, target_noise_clip, 68 | random_exploration, verbose, tensorboard_log, 69 | _init_setup_model, policy_kwargs, full_tensorboard_log) 70 | 71 | def learn(self, total_timesteps, callback=None, seed=None, 72 | log_interval=4, tb_log_name="TD3", reset_num_timesteps=True, replay_wrapper=None): 73 | 74 | new_tb_log = self._init_num_timesteps(reset_num_timesteps) 75 | 76 | if replay_wrapper is not None: 77 | self.replay_buffer = replay_wrapper(self.replay_buffer) 78 | 79 | with SetVerbosity(self.verbose), TensorboardWriter(self.graph, self.tensorboard_log, tb_log_name, new_tb_log) \ 80 | as writer: 81 | 82 | self._setup_learn(seed) 83 | 84 | # Transform to callable if needed 85 | self.learning_rate = get_schedule_fn(self.learning_rate) 86 | # Initial learning rate 87 | current_lr = self.learning_rate(1) 88 | 89 | start_time = time.time() 90 | episode_rewards = [0.0] 91 | episode_successes = [] 92 | if self.action_noise is not None: 93 | self.action_noise.reset() 94 | obs = self.env.reset() 95 | self.episode_reward = np.zeros((1,)) 96 | ep_info_buf = deque(maxlen=100) 97 | n_updates = 0 98 | infos_values = [] 99 | 100 | for step in range(total_timesteps): 101 | if callback is not None: 102 | # Only stop training if return value is False, not when it is None. This is for backwards 103 | # compatibility with callbacks that have no return statement. 104 | if callback(locals(), globals()) is False: 105 | break 106 | 107 | # Before training starts, randomly sample actions 108 | # from a uniform distribution for better exploration. 109 | # Afterwards, use the learned policy 110 | # if random_exploration is set to 0 (normal setting) 111 | if (self.num_timesteps < self.learning_starts 112 | or np.random.rand() < self.random_exploration): 113 | # No need to rescale when sampling random action 114 | rescaled_action = action = self.env.action_space.sample() 115 | else: 116 | action = self.policy_tf.step(obs[None]).flatten() 117 | # Add noise to the action, as the policy 118 | # is deterministic, this is required for exploration 119 | if self.action_noise is not None: 120 | # action = np.clip(action + self.action_noise(), -1, 1) 121 | action = action + self.action_noise() 122 | 123 | # Rescale from [-1, 1] to the correct bounds 124 | # rescaled_action = action * np.abs(self.action_space.low) 125 | rescaled_action = action 126 | 127 | assert action.shape == self.env.action_space.shape 128 | 129 | new_obs, reward, done, info = self.env.step(rescaled_action) 130 | 131 | # Store transition in the replay buffer. 132 | self.replay_buffer.add(obs, action, reward, new_obs, float(done)) 133 | obs = new_obs 134 | 135 | # Retrieve reward and episode length if using Monitor wrapper 136 | maybe_ep_info = info.get('episode') 137 | if maybe_ep_info is not None: 138 | ep_info_buf.extend([maybe_ep_info]) 139 | 140 | if writer is not None: 141 | # Write reward per episode to tensorboard 142 | ep_reward = np.array([reward]).reshape((1, -1)) 143 | ep_done = np.array([done]).reshape((1, -1)) 144 | self.episode_reward = total_episode_reward_logger(self.episode_reward, ep_reward, 145 | ep_done, writer, self.num_timesteps) 146 | 147 | if step % self.train_freq == 0: 148 | mb_infos_vals = [] 149 | # Update policy, critics and target networks 150 | for grad_step in range(self.gradient_steps): 151 | # Break if the warmup phase is not over 152 | # or if there are not enough samples in the replay buffer 153 | if not self.replay_buffer.can_sample(self.batch_size) \ 154 | or self.num_timesteps < self.learning_starts: 155 | break 156 | n_updates += 1 157 | # Compute current learning_rate 158 | frac = 1.0 - step / total_timesteps 159 | current_lr = self.learning_rate(frac) 160 | # Update policy and critics (q functions) 161 | # Note: the policy is updated less frequently than the Q functions 162 | # this is controlled by the `policy_delay` parameter 163 | mb_infos_vals.append( 164 | self._train_step(step, writer, current_lr, (step + grad_step) % self.policy_delay == 0)) 165 | 166 | # Log losses and entropy, useful for monitor training 167 | if len(mb_infos_vals) > 0: 168 | infos_values = np.mean(mb_infos_vals, axis=0) 169 | 170 | episode_rewards[-1] += reward 171 | if done: 172 | if self.action_noise is not None: 173 | self.action_noise.reset() 174 | if not isinstance(self.env, VecEnv): 175 | obs = self.env.reset() 176 | episode_rewards.append(0.0) 177 | 178 | maybe_is_success = info.get('is_success') 179 | if maybe_is_success is not None: 180 | episode_successes.append(float(maybe_is_success)) 181 | 182 | if len(episode_rewards[-101:-1]) == 0: 183 | mean_reward = -np.inf 184 | else: 185 | mean_reward = round(float(np.mean(episode_rewards[-101:-1])), 1) 186 | 187 | num_episodes = len(episode_rewards) 188 | self.num_timesteps += 1 189 | # Display training infos 190 | if self.verbose >= 1 and done and log_interval is not None and len(episode_rewards) % log_interval == 0: 191 | fps = int(step / (time.time() - start_time)) 192 | logger.logkv("episodes", num_episodes) 193 | logger.logkv("mean 100 episode reward", mean_reward) 194 | if len(ep_info_buf) > 0 and len(ep_info_buf[0]) > 0: 195 | logger.logkv('ep_rewmean', safe_mean([ep_info['r'] for ep_info in ep_info_buf])) 196 | logger.logkv('eplenmean', safe_mean([ep_info['l'] for ep_info in ep_info_buf])) 197 | logger.logkv("n_updates", n_updates) 198 | logger.logkv("current_lr", current_lr) 199 | logger.logkv("fps", fps) 200 | logger.logkv('time_elapsed', int(time.time() - start_time)) 201 | if len(episode_successes) > 0: 202 | logger.logkv("success rate", np.mean(episode_successes[-100:])) 203 | if len(infos_values) > 0: 204 | for (name, val) in zip(self.infos_names, infos_values): 205 | logger.logkv(name, val) 206 | logger.logkv("total timesteps", self.num_timesteps) 207 | logger.dumpkvs() 208 | # Reset infos: 209 | infos_values = [] 210 | return self 211 | -------------------------------------------------------------------------------- /src/policy.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | from stable_baselines.a2c.utils import linear, conv_to_fc 4 | from stable_baselines.common.policies import register_policy 5 | from stable_baselines.td3.policies import FeedForwardPolicy 6 | 7 | 8 | def ortho_init(scale=1.0): 9 | """ 10 | Orthogonal initialization for the policy weights 11 | :param scale: (float) Scaling factor for the weights. 12 | :return: (function) an initialization function for the weights 13 | """ 14 | 15 | # _ortho_init(shape, dtype, partition_info=None) 16 | def _ortho_init(shape, *_, **_kwargs): 17 | """Intialize weights as Orthogonal matrix. 18 | Orthogonal matrix initialization [1]_. For n-dimensional shapes where 19 | n > 2, the n-1 trailing axes are flattened. For convolutional layers, this 20 | corresponds to the fan-in, so this makes the initialization usable for 21 | both dense and convolutional layers. 22 | References 23 | ---------- 24 | .. [1] Saxe, Andrew M., James L. McClelland, and Surya Ganguli. 25 | "Exact solutions to the nonlinear dynamics of learning in deep 26 | linear 27 | """ 28 | # lasagne ortho init for tf 29 | shape = tuple(shape) 30 | if len(shape) == 2: 31 | flat_shape = shape 32 | elif len(shape) == 4: # assumes NHWC 33 | flat_shape = (np.prod(shape[:-1]), shape[-1]) 34 | elif len(shape) == 5: # assumes NDHWC 35 | flat_shape = (np.prod(shape[:-1]), shape[-1]) 36 | else: 37 | raise NotImplementedError 38 | gaussian_noise = np.random.normal(0.0, 1.0, flat_shape) 39 | u, _, v = np.linalg.svd(gaussian_noise, full_matrices=False) 40 | weights = u if u.shape == flat_shape else v # pick the one with the correct shape 41 | weights = weights.reshape(shape) 42 | return (scale * weights[:shape[0], :shape[1]]).astype(np.float32) 43 | 44 | return _ortho_init 45 | 46 | 47 | def conv3d(input_tensor, scope, *, n_filters, filter_size, stride, 48 | pad='VALID', init_scale=1.0, data_format='NDHWC', one_dim_bias=False): 49 | """ 50 | Creates a 3d convolutional layer for TensorFlow 51 | :param input_tensor: (TensorFlow Tensor) The input tensor for the convolution 52 | :param scope: (str) The TensorFlow variable scope 53 | :param n_filters: (int) The number of filters 54 | :param filter_size: (Union[int, [int], tuple]) The filter size for the squared kernel matrix, 55 | or the height and width of kernel filter if the input is a list or tuple 56 | :param stride: (int) The stride of the convolution 57 | :param pad: (str) The padding type ('VALID' or 'SAME') 58 | :param init_scale: (int) The initialization scale 59 | :param data_format: (str) The data format for the convolution weights 60 | :param one_dim_bias: (bool) If the bias should be one dimentional or not 61 | :return: (TensorFlow Tensor) 3d convolutional layer 62 | """ 63 | if isinstance(filter_size, list) or isinstance(filter_size, tuple): 64 | assert len(filter_size) == 2, \ 65 | "Filter size must have 2 elements (height, width), {} were given".format(len(filter_size)) 66 | filter_height = filter_size[0] 67 | filter_width = filter_size[1] 68 | filter_depth = filter_size[2] 69 | else: 70 | filter_height = filter_size 71 | filter_width = filter_size 72 | filter_depth = filter_size 73 | 74 | if data_format == 'NDHWC': 75 | channel_ax = 4 76 | strides = [1, stride, stride, stride, 1] 77 | bshape = [1, 1, 1, 1, n_filters] 78 | elif data_format == 'NHWDC': 79 | channel_ax = 1 80 | strides = [1, 1, stride, stride, stride] 81 | bshape = [1, n_filters, 1, 1, 1] 82 | else: 83 | raise NotImplementedError 84 | bias_var_shape = [n_filters] if one_dim_bias else [1, n_filters, 1, 1, 1] 85 | n_input = input_tensor.get_shape()[channel_ax].value 86 | wshape = [filter_height, filter_width, filter_depth, n_input, n_filters] 87 | with tf.variable_scope(scope): 88 | weight = tf.get_variable("w", wshape, initializer=ortho_init(init_scale)) 89 | bias = tf.get_variable("b", bias_var_shape, initializer=tf.constant_initializer(0.0)) 90 | if not one_dim_bias and data_format == 'NDHWC': 91 | bias = tf.reshape(bias, bshape) 92 | return bias + tf.nn.conv3d(input_tensor, weight, strides=strides, padding=pad, data_format=data_format) 93 | 94 | def cnn_3d(scaled_voxels, n_hidden, filters, filter_sizes, strides, **kwargs): 95 | """ 96 | CNN in 3D. 97 | :param scaled_voxels: (TensorFlow Tensor) Voxel input placeholder 98 | :param n_hidden: (int) Number of nodes in the last linear layer 99 | :param filters: (array) Filter numbers for the convolutional layers of the CNN 100 | :param filter_sizes: (array) Filter sizes for the convolutional layers of the CNN 101 | :param strides: (array) Strides for the convolutional layers of the CNN 102 | :param kwargs: (dict) Extra keywords parameters for the convolutional layers of the CNN 103 | :return: (TensorFlow Tensor) The CNN output layer 104 | """ 105 | activ = tf.tanh 106 | 107 | layers = [] 108 | 109 | for i, (n_filter, filter_size, stride) in enumerate(zip(filters, filter_sizes, strides)): 110 | 111 | input_layer = scaled_voxels if i == 0 else layers[-1] 112 | label = 'c%d' % (i + 1) 113 | layer = activ(conv3d(input_layer, label, n_filters=n_filter, filter_size=filter_size, stride=stride, init_scale=np.sqrt(2), **kwargs)) 114 | layers.append(layer) 115 | print('layer_%d' % (i + 1), layer.shape) 116 | 117 | layer = conv_to_fc(layers[-1]) 118 | 119 | return tf.tanh(linear(layer, 'fc1', n_hidden=n_hidden, init_scale=np.sqrt(2))) 120 | 121 | class LnCnn3dPolicy(FeedForwardPolicy): 122 | 123 | def __init__(self, sess, ob_space, ac_space, n_env=1, n_steps=1, n_batch=None, reuse=False, n_hidden=128, filters=[128, 256], filter_sizes=[3, 3], strides=[2, 1], **_kwargs): 124 | super(LnCnn3dPolicy, self).__init__(sess, ob_space, ac_space, n_env, n_steps, n_batch, reuse, 125 | feature_extraction="cnn", cnn_extractor=cnn_3d, layer_norm=True, n_hidden=n_hidden, filters=filters, filter_sizes=filter_sizes, strides=strides, **_kwargs) 126 | 127 | # register_policy("LnCnn3dPolicy", LnCnn3dPolicy) 128 | -------------------------------------------------------------------------------- /static/css/index.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | color: black; 7 | font-family: 'Open Sans', sans-serif; 8 | position: relative; 9 | font-size: 14px; 10 | } 11 | 12 | h1 { 13 | padding-bottom: 10px; 14 | font-weight: 800; 15 | } 16 | 17 | h2 { 18 | font-weight: 600; 19 | } 20 | 21 | .menu { 22 | background: #fffb00; 23 | max-width: 280px; 24 | width: 100%; 25 | border-radius: 2px; 26 | position: absolute; 27 | left: 20px; 28 | top: 20px; 29 | bottom: 20px; 30 | transition: transform 0.1s ease-out; 31 | box-sizing: border-box; 32 | overflow-y: scroll; 33 | } 34 | 35 | .menu_inner { 36 | padding: 20px; 37 | box-sizing: border-box; 38 | } 39 | 40 | .form { 41 | border: 1px solid white; 42 | background: #ffe600; 43 | text-align: center; 44 | line-height: 25px; 45 | margin-top: 15px; 46 | } 47 | 48 | .file { 49 | display: none; 50 | } 51 | 52 | .file_label { 53 | cursor: pointer; 54 | padding: 28px 20px; 55 | display: block; 56 | font-size: 0.9rem; 57 | } 58 | 59 | .caption { 60 | padding-top: 8px; 61 | font-size: 0.8rem; 62 | line-height: 1.0rem; 63 | } 64 | 65 | .sk-cube-grid { 66 | width: 40px; 67 | height: 40px; 68 | margin: 0 auto; 69 | position: absolute; 70 | top: 48%; 71 | left: 50%; 72 | -webkit-transform : translate(-50%,-50%); 73 | transform : translate(-50%,-50%); 74 | display: none; 75 | } 76 | 77 | .sk-cube-grid .sk-cube { 78 | width: 33%; 79 | height: 33%; 80 | background-color: #ffe600; 81 | float: left; 82 | -webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; 83 | animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; 84 | } 85 | .sk-cube-grid .sk-cube1 { 86 | -webkit-animation-delay: 0.2s; 87 | animation-delay: 0.2s; } 88 | .sk-cube-grid .sk-cube2 { 89 | -webkit-animation-delay: 0.3s; 90 | animation-delay: 0.3s; } 91 | .sk-cube-grid .sk-cube3 { 92 | -webkit-animation-delay: 0.4s; 93 | animation-delay: 0.4s; } 94 | .sk-cube-grid .sk-cube4 { 95 | -webkit-animation-delay: 0.1s; 96 | animation-delay: 0.1s; } 97 | .sk-cube-grid .sk-cube5 { 98 | -webkit-animation-delay: 0.2s; 99 | animation-delay: 0.2s; } 100 | .sk-cube-grid .sk-cube6 { 101 | -webkit-animation-delay: 0.3s; 102 | animation-delay: 0.3s; } 103 | .sk-cube-grid .sk-cube7 { 104 | -webkit-animation-delay: 0s; 105 | animation-delay: 0s; } 106 | .sk-cube-grid .sk-cube8 { 107 | -webkit-animation-delay: 0.1s; 108 | animation-delay: 0.1s; } 109 | .sk-cube-grid .sk-cube9 { 110 | -webkit-animation-delay: 0.2s; 111 | animation-delay: 0.2s; } 112 | 113 | @-webkit-keyframes sk-cubeGridScaleDelay { 114 | 0%, 70%, 100% { 115 | -webkit-transform: scale3D(1, 1, 1); 116 | transform: scale3D(1, 1, 1); 117 | } 35% { 118 | -webkit-transform: scale3D(0, 0, 1); 119 | transform: scale3D(0, 0, 1); 120 | } 121 | } 122 | 123 | @keyframes sk-cubeGridScaleDelay { 124 | 0%, 70%, 100% { 125 | -webkit-transform: scale3D(1, 1, 1); 126 | transform: scale3D(1, 1, 1); 127 | } 35% { 128 | -webkit-transform: scale3D(0, 0, 1); 129 | transform: scale3D(0, 0, 1); 130 | } 131 | } 132 | 133 | .output_representation, .predefined_models, .custom_model, footer { 134 | padding-top: 20px; 135 | margin-top: 20px; 136 | border-top: 1px dashed #666; 137 | } 138 | 139 | .output_representation ul { 140 | width: 100%; 141 | display: flex; 142 | border: 2px solid white; 143 | border-radius: 2px; 144 | margin-top: 20px; 145 | } 146 | 147 | .output_representation li { 148 | display: block; 149 | width: 50%; 150 | text-align: center; 151 | padding: 20px 0; 152 | box-sizing: border-box; 153 | cursor: pointer; 154 | } 155 | 156 | .output_representation li:first-child { 157 | border-right: 2px solid white; 158 | } 159 | 160 | .output_representation .selected { 161 | background: white; 162 | } 163 | 164 | .pre_models { 165 | display: flex; 166 | flex-wrap: wrap; 167 | padding-top: 10px; 168 | } 169 | 170 | .pre_models li { 171 | width: 33%; 172 | display: block; 173 | padding: 5px; 174 | box-sizing: border-box; 175 | } 176 | 177 | .pre_models img { 178 | width: 100%; 179 | display: block; 180 | cursor: pointer; 181 | } 182 | 183 | footer { 184 | color: #333; 185 | font-size: 0.8rem; 186 | } 187 | 188 | footer a { 189 | text-decoration: none; 190 | color: rgb(66, 170, 255); 191 | } 192 | 193 | .close { 194 | width: 18px; 195 | height: 18px; 196 | padding: 0 10px 10px 10px 0; 197 | margin-bottom: 20px; 198 | cursor: pointer; 199 | } 200 | 201 | .close svg { 202 | color: #999; 203 | } 204 | 205 | .open { 206 | position: absolute; 207 | left: 40px; 208 | top: 36px; 209 | width: 12px; 210 | height: 12px; 211 | cursor: pointer; 212 | } 213 | 214 | 215 | .download { 216 | text-align: center; 217 | width: 100%; 218 | position: absolute; 219 | bottom: 30px; 220 | background: rgba(0, 0, 0, 0); 221 | display: none; 222 | } 223 | 224 | .download p { 225 | display: block; 226 | width: 300px; 227 | margin: 0 auto; 228 | background: #fffb00; 229 | padding: 15px; 230 | border-radius: 40px; 231 | cursor: pointer; 232 | } -------------------------------------------------------------------------------- /static/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0-modified | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | 27 | /* make sure to set some focus styles for accessibility */ 28 | :focus { 29 | outline: 0; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | body { 39 | line-height: 1; 40 | } 41 | 42 | ol, ul { 43 | list-style: none; 44 | } 45 | 46 | blockquote, q { 47 | quotes: none; 48 | } 49 | 50 | blockquote:before, blockquote:after, 51 | q:before, q:after { 52 | content: ''; 53 | content: none; 54 | } 55 | 56 | table { 57 | border-collapse: collapse; 58 | border-spacing: 0; 59 | } 60 | 61 | input[type=search]::-webkit-search-cancel-button, 62 | input[type=search]::-webkit-search-decoration, 63 | input[type=search]::-webkit-search-results-button, 64 | input[type=search]::-webkit-search-results-decoration { 65 | -webkit-appearance: none; 66 | -moz-appearance: none; 67 | } 68 | 69 | input[type=search] { 70 | -webkit-appearance: none; 71 | -moz-appearance: none; 72 | -webkit-box-sizing: content-box; 73 | -moz-box-sizing: content-box; 74 | box-sizing: content-box; 75 | } 76 | 77 | textarea { 78 | overflow: auto; 79 | vertical-align: top; 80 | resize: vertical; 81 | } 82 | 83 | /** 84 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 85 | */ 86 | 87 | audio, 88 | canvas, 89 | video { 90 | display: inline-block; 91 | *display: inline; 92 | *zoom: 1; 93 | max-width: 100%; 94 | } 95 | 96 | /** 97 | * Prevent modern browsers from displaying `audio` without controls. 98 | * Remove excess height in iOS 5 devices. 99 | */ 100 | 101 | audio:not([controls]) { 102 | display: none; 103 | height: 0; 104 | } 105 | 106 | /** 107 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 108 | * Known issue: no IE 6 support. 109 | */ 110 | 111 | [hidden] { 112 | display: none; 113 | } 114 | 115 | /** 116 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 117 | * `em` units. 118 | * 2. Prevent iOS text size adjust after orientation change, without disabling 119 | * user zoom. 120 | */ 121 | 122 | html { 123 | font-size: 100%; /* 1 */ 124 | -webkit-text-size-adjust: 100%; /* 2 */ 125 | -ms-text-size-adjust: 100%; /* 2 */ 126 | } 127 | 128 | /** 129 | * Address `outline` inconsistency between Chrome and other browsers. 130 | */ 131 | 132 | a:focus { 133 | outline: thin dotted; 134 | } 135 | 136 | /** 137 | * Improve readability when focused and also mouse hovered in all browsers. 138 | */ 139 | 140 | a:active, 141 | a:hover { 142 | outline: 0; 143 | } 144 | 145 | /** 146 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 147 | * 2. Improve image quality when scaled in IE 7. 148 | */ 149 | 150 | img { 151 | border: 0; /* 1 */ 152 | -ms-interpolation-mode: bicubic; /* 2 */ 153 | } 154 | 155 | /** 156 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 157 | */ 158 | 159 | figure { 160 | margin: 0; 161 | } 162 | 163 | /** 164 | * Correct margin displayed oddly in IE 6/7. 165 | */ 166 | 167 | form { 168 | margin: 0; 169 | } 170 | 171 | /** 172 | * Define consistent border, margin, and padding. 173 | */ 174 | 175 | fieldset { 176 | border: 1px solid #c0c0c0; 177 | margin: 0 2px; 178 | padding: 0.35em 0.625em 0.75em; 179 | } 180 | 181 | /** 182 | * 1. Correct color not being inherited in IE 6/7/8/9. 183 | * 2. Correct text not wrapping in Firefox 3. 184 | * 3. Correct alignment displayed oddly in IE 6/7. 185 | */ 186 | 187 | legend { 188 | border: 0; /* 1 */ 189 | padding: 0; 190 | white-space: normal; /* 2 */ 191 | *margin-left: -7px; /* 3 */ 192 | } 193 | 194 | /** 195 | * 1. Correct font size not being inherited in all browsers. 196 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 197 | * and Chrome. 198 | * 3. Improve appearance and consistency in all browsers. 199 | */ 200 | 201 | button, 202 | input, 203 | select, 204 | textarea { 205 | font-size: 100%; /* 1 */ 206 | margin: 0; /* 2 */ 207 | vertical-align: baseline; /* 3 */ 208 | *vertical-align: middle; /* 3 */ 209 | } 210 | 211 | /** 212 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 213 | * the UA stylesheet. 214 | */ 215 | 216 | button, 217 | input { 218 | line-height: normal; 219 | } 220 | 221 | /** 222 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 223 | * All other form control elements do not inherit `text-transform` values. 224 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 225 | * Correct `select` style inheritance in Firefox 4+ and Opera. 226 | */ 227 | 228 | button, 229 | select { 230 | text-transform: none; 231 | } 232 | 233 | /** 234 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 235 | * and `video` controls. 236 | * 2. Correct inability to style clickable `input` types in iOS. 237 | * 3. Improve usability and consistency of cursor style between image-type 238 | * `input` and others. 239 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 240 | * Known issue: inner spacing remains in IE 6. 241 | */ 242 | 243 | button, 244 | html input[type="button"], /* 1 */ 245 | input[type="reset"], 246 | input[type="submit"] { 247 | -webkit-appearance: button; /* 2 */ 248 | cursor: pointer; /* 3 */ 249 | *overflow: visible; /* 4 */ 250 | } 251 | 252 | /** 253 | * Re-set default cursor for disabled elements. 254 | */ 255 | 256 | button[disabled], 257 | html input[disabled] { 258 | cursor: default; 259 | } 260 | 261 | /** 262 | * 1. Address box sizing set to content-box in IE 8/9. 263 | * 2. Remove excess padding in IE 8/9. 264 | * 3. Remove excess padding in IE 7. 265 | * Known issue: excess padding remains in IE 6. 266 | */ 267 | 268 | input[type="checkbox"], 269 | input[type="radio"] { 270 | box-sizing: border-box; /* 1 */ 271 | padding: 0; /* 2 */ 272 | *height: 13px; /* 3 */ 273 | *width: 13px; /* 3 */ 274 | } 275 | 276 | /** 277 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 278 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 279 | * (include `-moz` to future-proof). 280 | */ 281 | 282 | input[type="search"] { 283 | -webkit-appearance: textfield; /* 1 */ 284 | -moz-box-sizing: content-box; 285 | -webkit-box-sizing: content-box; /* 2 */ 286 | box-sizing: content-box; 287 | } 288 | 289 | /** 290 | * Remove inner padding and search cancel button in Safari 5 and Chrome 291 | * on OS X. 292 | */ 293 | 294 | input[type="search"]::-webkit-search-cancel-button, 295 | input[type="search"]::-webkit-search-decoration { 296 | -webkit-appearance: none; 297 | } 298 | 299 | /** 300 | * Remove inner padding and border in Firefox 3+. 301 | */ 302 | 303 | button::-moz-focus-inner, 304 | input::-moz-focus-inner { 305 | border: 0; 306 | padding: 0; 307 | } 308 | 309 | /** 310 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 311 | * 2. Improve readability and alignment in all browsers. 312 | */ 313 | 314 | textarea { 315 | overflow: auto; /* 1 */ 316 | vertical-align: top; /* 2 */ 317 | } 318 | 319 | /** 320 | * Remove most spacing between table cells. 321 | */ 322 | 323 | table { 324 | border-collapse: collapse; 325 | border-spacing: 0; 326 | } 327 | 328 | html, 329 | button, 330 | input, 331 | select, 332 | textarea { 333 | color: #222; 334 | } 335 | 336 | 337 | ::-moz-selection { 338 | background: #b3d4fc; 339 | text-shadow: none; 340 | } 341 | 342 | ::selection { 343 | background: #b3d4fc; 344 | text-shadow: none; 345 | } 346 | 347 | img { 348 | vertical-align: middle; 349 | } 350 | 351 | fieldset { 352 | border: 0; 353 | margin: 0; 354 | padding: 0; 355 | } 356 | 357 | textarea { 358 | resize: vertical; 359 | } 360 | 361 | .chromeframe { 362 | margin: 0.2em 0; 363 | background: #ccc; 364 | color: #000; 365 | padding: 0.2em 0; 366 | } 367 | -------------------------------------------------------------------------------- /static/img/icon/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/android-icon-144x144.png -------------------------------------------------------------------------------- /static/img/icon/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/android-icon-192x192.png -------------------------------------------------------------------------------- /static/img/icon/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/android-icon-36x36.png -------------------------------------------------------------------------------- /static/img/icon/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/android-icon-48x48.png -------------------------------------------------------------------------------- /static/img/icon/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/android-icon-72x72.png -------------------------------------------------------------------------------- /static/img/icon/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/android-icon-96x96.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-114x114.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-120x120.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-144x144.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-152x152.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-180x180.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-57x57.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-60x60.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-72x72.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-76x76.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon-precomposed.png -------------------------------------------------------------------------------- /static/img/icon/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/apple-icon.png -------------------------------------------------------------------------------- /static/img/icon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /static/img/icon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/favicon-16x16.png -------------------------------------------------------------------------------- /static/img/icon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/favicon-32x32.png -------------------------------------------------------------------------------- /static/img/icon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/favicon-96x96.png -------------------------------------------------------------------------------- /static/img/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/favicon.ico -------------------------------------------------------------------------------- /static/img/icon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /static/img/icon/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/ms-icon-144x144.png -------------------------------------------------------------------------------- /static/img/icon/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/ms-icon-150x150.png -------------------------------------------------------------------------------- /static/img/icon/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/ms-icon-310x310.png -------------------------------------------------------------------------------- /static/img/icon/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/icon/ms-icon-70x70.png -------------------------------------------------------------------------------- /static/img/model_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/model_01.jpg -------------------------------------------------------------------------------- /static/img/model_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/model_02.jpg -------------------------------------------------------------------------------- /static/img/model_03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/model_03.jpg -------------------------------------------------------------------------------- /static/img/model_04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/model_04.jpg -------------------------------------------------------------------------------- /static/img/model_05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/model_05.jpg -------------------------------------------------------------------------------- /static/img/model_06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/model_06.jpg -------------------------------------------------------------------------------- /static/img/ogp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakzk/generative_modelling_with_design_constraints/7f1d770613c638bdeac17ccc7bd30be122562220/static/img/ogp.png -------------------------------------------------------------------------------- /static/js/api.js: -------------------------------------------------------------------------------- 1 | 2 | function api_post_generate(files, type) { 3 | 4 | var file = files[0]; 5 | var data = new FormData(); 6 | data.append('model', file); 7 | data.append('model_type', type); 8 | 9 | spinner.style.display = 'block'; 10 | 11 | var request = new XMLHttpRequest(); 12 | request.addEventListener('load', function(e) { 13 | 14 | spinner.style.display = 'none'; 15 | 16 | if (request.response == null) { 17 | alert('The mesh is not compatible with this system or the server is down. Try it again later.'); 18 | return; 19 | } 20 | 21 | if (request.response['error']) { 22 | alert(request.response['error']); 23 | } else { 24 | build(request.response['result'], request.response['model_type']); 25 | } 26 | }); 27 | 28 | 29 | request.upload.addEventListener('progress', function(e) { 30 | var percent_complete = (e.loaded / e.total) * 100; 31 | console.log(percent_complete); 32 | }); 33 | 34 | request.responseType = 'json'; 35 | request.open('post', '/generate'); 36 | request.send(data); 37 | 38 | } 39 | 40 | function api_get_predefined(index, type) { 41 | 42 | spinner.style.display = 'block'; 43 | 44 | var request = new XMLHttpRequest(); 45 | 46 | request.addEventListener('load', function(e) { 47 | 48 | spinner.style.display = 'none'; 49 | 50 | if (request.response == null) { 51 | alert('The mesh is not compatible with this system or the server is down. Try it again later.'); 52 | return; 53 | } 54 | 55 | if (request.response['error']) { 56 | alert(request.response['error']); 57 | } else { 58 | build(request.response['result'], request.response['model_type']); 59 | } 60 | }); 61 | 62 | request.upload.addEventListener('progress', function(e) { 63 | var percent_complete = (e.loaded / e.total) * 100; 64 | console.log(percent_complete); 65 | }); 66 | 67 | request.responseType = 'json'; 68 | request.open('get', '/predefined/' + index + '/' + type); 69 | request.send(); 70 | 71 | } -------------------------------------------------------------------------------- /static/js/lib/OBJExporter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.OBJExporter = function () {}; 6 | 7 | THREE.OBJExporter.prototype = { 8 | 9 | constructor: THREE.OBJExporter, 10 | 11 | parse: function ( object ) { 12 | 13 | var output = ''; 14 | 15 | var indexVertex = 0; 16 | var indexVertexUvs = 0; 17 | var indexNormals = 0; 18 | 19 | var vertex = new THREE.Vector3(); 20 | var normal = new THREE.Vector3(); 21 | var uv = new THREE.Vector2(); 22 | 23 | var i, j, l, m, face = []; 24 | 25 | var parseMesh = function ( mesh ) { 26 | 27 | var nbVertex = 0; 28 | var nbNormals = 0; 29 | var nbVertexUvs = 0; 30 | 31 | var geometry = mesh.geometry; 32 | 33 | var normalMatrixWorld = new THREE.Matrix3(); 34 | 35 | if ( geometry instanceof THREE.Geometry ) { 36 | 37 | geometry = new THREE.BufferGeometry().setFromObject( mesh ); 38 | 39 | } 40 | 41 | if ( geometry instanceof THREE.BufferGeometry ) { 42 | 43 | // shortcuts 44 | var vertices = geometry.getAttribute( 'position' ); 45 | var normals = geometry.getAttribute( 'normal' ); 46 | var uvs = geometry.getAttribute( 'uv' ); 47 | var indices = geometry.getIndex(); 48 | 49 | // name of the mesh object 50 | output += 'o ' + mesh.name + '\n'; 51 | 52 | // vertices 53 | 54 | if( vertices !== undefined ) { 55 | 56 | for ( i = 0, l = vertices.count; i < l; i ++, nbVertex++ ) { 57 | 58 | vertex.x = vertices.getX( i ); 59 | vertex.y = vertices.getY( i ); 60 | vertex.z = vertices.getZ( i ); 61 | 62 | // transfrom the vertex to world space 63 | vertex.applyMatrix4( mesh.matrixWorld ); 64 | 65 | // transform the vertex to export format 66 | output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n'; 67 | 68 | } 69 | 70 | } 71 | 72 | // uvs 73 | 74 | if( uvs !== undefined ) { 75 | 76 | for ( i = 0, l = uvs.count; i < l; i ++, nbVertexUvs++ ) { 77 | 78 | uv.x = uvs.getX( i ); 79 | uv.y = uvs.getY( i ); 80 | 81 | // transform the uv to export format 82 | output += 'vt ' + uv.x + ' ' + uv.y + '\n'; 83 | 84 | } 85 | 86 | } 87 | 88 | // normals 89 | 90 | if( normals !== undefined ) { 91 | 92 | normalMatrixWorld.getNormalMatrix( mesh.matrixWorld ); 93 | 94 | for ( i = 0, l = normals.count; i < l; i ++, nbNormals++ ) { 95 | 96 | normal.x = normals.getX( i ); 97 | normal.y = normals.getY( i ); 98 | normal.z = normals.getZ( i ); 99 | 100 | // transfrom the normal to world space 101 | normal.applyMatrix3( normalMatrixWorld ); 102 | 103 | // transform the normal to export format 104 | output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n'; 105 | 106 | } 107 | 108 | } 109 | 110 | // faces 111 | 112 | if( indices !== null ) { 113 | 114 | for ( i = 0, l = indices.count; i < l; i += 3 ) { 115 | 116 | for( m = 0; m < 3; m ++ ){ 117 | 118 | j = indices.getX( i + m ) + 1; 119 | 120 | face[ m ] = ( indexVertex + j ) + '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ); 121 | 122 | } 123 | 124 | // transform the face to export format 125 | output += 'f ' + face.join( ' ' ) + "\n"; 126 | 127 | } 128 | 129 | } else { 130 | 131 | for ( i = 0, l = vertices.count; i < l; i += 3 ) { 132 | 133 | for( m = 0; m < 3; m ++ ){ 134 | 135 | j = i + m + 1; 136 | 137 | face[ m ] = ( indexVertex + j ) + '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ); 138 | 139 | } 140 | 141 | // transform the face to export format 142 | output += 'f ' + face.join( ' ' ) + "\n"; 143 | 144 | } 145 | 146 | } 147 | 148 | } else { 149 | 150 | console.warn( 'THREE.OBJExporter.parseMesh(): geometry type unsupported', geometry ); 151 | 152 | } 153 | 154 | // update index 155 | indexVertex += nbVertex; 156 | indexVertexUvs += nbVertexUvs; 157 | indexNormals += nbNormals; 158 | 159 | }; 160 | 161 | var parseLine = function( line ) { 162 | 163 | var nbVertex = 0; 164 | 165 | var geometry = line.geometry; 166 | var type = line.type; 167 | 168 | if ( geometry instanceof THREE.Geometry ) { 169 | 170 | geometry = new THREE.BufferGeometry().setFromObject( line ); 171 | 172 | } 173 | 174 | if ( geometry instanceof THREE.BufferGeometry ) { 175 | 176 | // shortcuts 177 | var vertices = geometry.getAttribute( 'position' ); 178 | var indices = geometry.getIndex(); 179 | 180 | // name of the line object 181 | output += 'o ' + line.name + '\n'; 182 | 183 | if( vertices !== undefined ) { 184 | 185 | for ( i = 0, l = vertices.count; i < l; i ++, nbVertex++ ) { 186 | 187 | vertex.x = vertices.getX( i ); 188 | vertex.y = vertices.getY( i ); 189 | vertex.z = vertices.getZ( i ); 190 | 191 | // transfrom the vertex to world space 192 | vertex.applyMatrix4( line.matrixWorld ); 193 | 194 | // transform the vertex to export format 195 | output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n'; 196 | 197 | } 198 | 199 | } 200 | 201 | if ( type === 'Line' ) { 202 | 203 | output += 'l '; 204 | 205 | for ( j = 1, l = vertices.count; j <= l; j++ ) { 206 | 207 | output += ( indexVertex + j ) + ' '; 208 | 209 | } 210 | 211 | output += '\n'; 212 | 213 | } 214 | 215 | if ( type === 'LineSegments' ) { 216 | 217 | for ( j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1 ) { 218 | 219 | output += 'l ' + ( indexVertex + j ) + ' ' + ( indexVertex + k ) + '\n'; 220 | 221 | } 222 | 223 | } 224 | 225 | } else { 226 | 227 | console.warn('THREE.OBJExporter.parseLine(): geometry type unsupported', geometry ); 228 | 229 | } 230 | 231 | // update index 232 | indexVertex += nbVertex; 233 | 234 | }; 235 | 236 | object.traverse( function ( child ) { 237 | 238 | if ( child instanceof THREE.Mesh ) { 239 | 240 | parseMesh( child ); 241 | 242 | } 243 | 244 | if ( child instanceof THREE.Line ) { 245 | 246 | parseLine( child ); 247 | 248 | } 249 | 250 | } ); 251 | 252 | return output; 253 | 254 | } 255 | 256 | }; -------------------------------------------------------------------------------- /static/js/lib/OBJLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.OBJLoader = ( function () { 6 | 7 | // o object_name | g group_name 8 | var object_pattern = /^[og]\s*(.+)?/; 9 | // mtllib file_reference 10 | var material_library_pattern = /^mtllib /; 11 | // usemtl material_name 12 | var material_use_pattern = /^usemtl /; 13 | 14 | function ParserState() { 15 | 16 | var state = { 17 | objects: [], 18 | object: {}, 19 | 20 | vertices: [], 21 | normals: [], 22 | colors: [], 23 | uvs: [], 24 | 25 | materialLibraries: [], 26 | 27 | startObject: function ( name, fromDeclaration ) { 28 | 29 | // If the current object (initial from reset) is not from a g/o declaration in the parsed 30 | // file. We need to use it for the first parsed g/o to keep things in sync. 31 | if ( this.object && this.object.fromDeclaration === false ) { 32 | 33 | this.object.name = name; 34 | this.object.fromDeclaration = ( fromDeclaration !== false ); 35 | return; 36 | 37 | } 38 | 39 | var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined ); 40 | 41 | if ( this.object && typeof this.object._finalize === 'function' ) { 42 | 43 | this.object._finalize( true ); 44 | 45 | } 46 | 47 | this.object = { 48 | name: name || '', 49 | fromDeclaration: ( fromDeclaration !== false ), 50 | 51 | geometry: { 52 | vertices: [], 53 | normals: [], 54 | colors: [], 55 | uvs: [] 56 | }, 57 | materials: [], 58 | smooth: true, 59 | 60 | startMaterial: function ( name, libraries ) { 61 | 62 | var previous = this._finalize( false ); 63 | 64 | // New usemtl declaration overwrites an inherited material, except if faces were declared 65 | // after the material, then it must be preserved for proper MultiMaterial continuation. 66 | if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) { 67 | 68 | this.materials.splice( previous.index, 1 ); 69 | 70 | } 71 | 72 | var material = { 73 | index: this.materials.length, 74 | name: name || '', 75 | mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ), 76 | smooth: ( previous !== undefined ? previous.smooth : this.smooth ), 77 | groupStart: ( previous !== undefined ? previous.groupEnd : 0 ), 78 | groupEnd: - 1, 79 | groupCount: - 1, 80 | inherited: false, 81 | 82 | clone: function ( index ) { 83 | 84 | var cloned = { 85 | index: ( typeof index === 'number' ? index : this.index ), 86 | name: this.name, 87 | mtllib: this.mtllib, 88 | smooth: this.smooth, 89 | groupStart: 0, 90 | groupEnd: - 1, 91 | groupCount: - 1, 92 | inherited: false 93 | }; 94 | cloned.clone = this.clone.bind( cloned ); 95 | return cloned; 96 | 97 | } 98 | }; 99 | 100 | this.materials.push( material ); 101 | 102 | return material; 103 | 104 | }, 105 | 106 | currentMaterial: function () { 107 | 108 | if ( this.materials.length > 0 ) { 109 | 110 | return this.materials[ this.materials.length - 1 ]; 111 | 112 | } 113 | 114 | return undefined; 115 | 116 | }, 117 | 118 | _finalize: function ( end ) { 119 | 120 | var lastMultiMaterial = this.currentMaterial(); 121 | if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) { 122 | 123 | lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3; 124 | lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart; 125 | lastMultiMaterial.inherited = false; 126 | 127 | } 128 | 129 | // Ignore objects tail materials if no face declarations followed them before a new o/g started. 130 | if ( end && this.materials.length > 1 ) { 131 | 132 | for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) { 133 | 134 | if ( this.materials[ mi ].groupCount <= 0 ) { 135 | 136 | this.materials.splice( mi, 1 ); 137 | 138 | } 139 | 140 | } 141 | 142 | } 143 | 144 | // Guarantee at least one empty material, this makes the creation later more straight forward. 145 | if ( end && this.materials.length === 0 ) { 146 | 147 | this.materials.push( { 148 | name: '', 149 | smooth: this.smooth 150 | } ); 151 | 152 | } 153 | 154 | return lastMultiMaterial; 155 | 156 | } 157 | }; 158 | 159 | // Inherit previous objects material. 160 | // Spec tells us that a declared material must be set to all objects until a new material is declared. 161 | // If a usemtl declaration is encountered while this new object is being parsed, it will 162 | // overwrite the inherited material. Exception being that there was already face declarations 163 | // to the inherited material, then it will be preserved for proper MultiMaterial continuation. 164 | 165 | if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) { 166 | 167 | var declared = previousMaterial.clone( 0 ); 168 | declared.inherited = true; 169 | this.object.materials.push( declared ); 170 | 171 | } 172 | 173 | this.objects.push( this.object ); 174 | 175 | }, 176 | 177 | finalize: function () { 178 | 179 | if ( this.object && typeof this.object._finalize === 'function' ) { 180 | 181 | this.object._finalize( true ); 182 | 183 | } 184 | 185 | }, 186 | 187 | parseVertexIndex: function ( value, len ) { 188 | 189 | var index = parseInt( value, 10 ); 190 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; 191 | 192 | }, 193 | 194 | parseNormalIndex: function ( value, len ) { 195 | 196 | var index = parseInt( value, 10 ); 197 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; 198 | 199 | }, 200 | 201 | parseUVIndex: function ( value, len ) { 202 | 203 | var index = parseInt( value, 10 ); 204 | return ( index >= 0 ? index - 1 : index + len / 2 ) * 2; 205 | 206 | }, 207 | 208 | addVertex: function ( a, b, c ) { 209 | 210 | var src = this.vertices; 211 | var dst = this.object.geometry.vertices; 212 | 213 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); 214 | dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); 215 | dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); 216 | 217 | }, 218 | 219 | addVertexPoint: function ( a ) { 220 | 221 | var src = this.vertices; 222 | var dst = this.object.geometry.vertices; 223 | 224 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); 225 | 226 | }, 227 | 228 | addVertexLine: function ( a ) { 229 | 230 | var src = this.vertices; 231 | var dst = this.object.geometry.vertices; 232 | 233 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); 234 | 235 | }, 236 | 237 | addNormal: function ( a, b, c ) { 238 | 239 | var src = this.normals; 240 | var dst = this.object.geometry.normals; 241 | 242 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); 243 | dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); 244 | dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); 245 | 246 | }, 247 | 248 | addColor: function ( a, b, c ) { 249 | 250 | var src = this.colors; 251 | var dst = this.object.geometry.colors; 252 | 253 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); 254 | dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); 255 | dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); 256 | 257 | }, 258 | 259 | addUV: function ( a, b, c ) { 260 | 261 | var src = this.uvs; 262 | var dst = this.object.geometry.uvs; 263 | 264 | dst.push( src[ a + 0 ], src[ a + 1 ] ); 265 | dst.push( src[ b + 0 ], src[ b + 1 ] ); 266 | dst.push( src[ c + 0 ], src[ c + 1 ] ); 267 | 268 | }, 269 | 270 | addUVLine: function ( a ) { 271 | 272 | var src = this.uvs; 273 | var dst = this.object.geometry.uvs; 274 | 275 | dst.push( src[ a + 0 ], src[ a + 1 ] ); 276 | 277 | }, 278 | 279 | addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) { 280 | 281 | var vLen = this.vertices.length; 282 | 283 | var ia = this.parseVertexIndex( a, vLen ); 284 | var ib = this.parseVertexIndex( b, vLen ); 285 | var ic = this.parseVertexIndex( c, vLen ); 286 | 287 | this.addVertex( ia, ib, ic ); 288 | 289 | if ( this.colors.length > 0 ) { 290 | 291 | this.addColor( ia, ib, ic ); 292 | 293 | } 294 | 295 | if ( ua !== undefined && ua !== '' ) { 296 | 297 | var uvLen = this.uvs.length; 298 | ia = this.parseUVIndex( ua, uvLen ); 299 | ib = this.parseUVIndex( ub, uvLen ); 300 | ic = this.parseUVIndex( uc, uvLen ); 301 | this.addUV( ia, ib, ic ); 302 | 303 | } 304 | 305 | if ( na !== undefined && na !== '' ) { 306 | 307 | // Normals are many times the same. If so, skip function call and parseInt. 308 | var nLen = this.normals.length; 309 | ia = this.parseNormalIndex( na, nLen ); 310 | 311 | ib = na === nb ? ia : this.parseNormalIndex( nb, nLen ); 312 | ic = na === nc ? ia : this.parseNormalIndex( nc, nLen ); 313 | 314 | this.addNormal( ia, ib, ic ); 315 | 316 | } 317 | 318 | }, 319 | 320 | addPointGeometry: function ( vertices ) { 321 | 322 | this.object.geometry.type = 'Points'; 323 | 324 | var vLen = this.vertices.length; 325 | 326 | for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) { 327 | 328 | this.addVertexPoint( this.parseVertexIndex( vertices[ vi ], vLen ) ); 329 | 330 | } 331 | 332 | }, 333 | 334 | addLineGeometry: function ( vertices, uvs ) { 335 | 336 | this.object.geometry.type = 'Line'; 337 | 338 | var vLen = this.vertices.length; 339 | var uvLen = this.uvs.length; 340 | 341 | for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) { 342 | 343 | this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) ); 344 | 345 | } 346 | 347 | for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) { 348 | 349 | this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) ); 350 | 351 | } 352 | 353 | } 354 | 355 | }; 356 | 357 | state.startObject( '', false ); 358 | 359 | return state; 360 | 361 | } 362 | 363 | // 364 | 365 | function OBJLoader( manager ) { 366 | 367 | THREE.Loader.call( this, manager ); 368 | 369 | this.materials = null; 370 | 371 | } 372 | 373 | OBJLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), { 374 | 375 | constructor: OBJLoader, 376 | 377 | load: function ( url, onLoad, onProgress, onError ) { 378 | 379 | var scope = this; 380 | 381 | var loader = new THREE.FileLoader( scope.manager ); 382 | loader.setPath( this.path ); 383 | loader.load( url, function ( text ) { 384 | 385 | onLoad( scope.parse( text ) ); 386 | 387 | }, onProgress, onError ); 388 | 389 | }, 390 | 391 | setMaterials: function ( materials ) { 392 | 393 | this.materials = materials; 394 | 395 | return this; 396 | 397 | }, 398 | 399 | parse: function ( text ) { 400 | 401 | console.time( 'OBJLoader' ); 402 | 403 | var state = new ParserState(); 404 | 405 | if ( text.indexOf( '\r\n' ) !== - 1 ) { 406 | 407 | // This is faster than String.split with regex that splits on both 408 | text = text.replace( /\r\n/g, '\n' ); 409 | 410 | } 411 | 412 | if ( text.indexOf( '\\\n' ) !== - 1 ) { 413 | 414 | // join lines separated by a line continuation character (\) 415 | text = text.replace( /\\\n/g, '' ); 416 | 417 | } 418 | 419 | var lines = text.split( '\n' ); 420 | var line = '', lineFirstChar = ''; 421 | var lineLength = 0; 422 | var result = []; 423 | 424 | // Faster to just trim left side of the line. Use if available. 425 | var trimLeft = ( typeof ''.trimLeft === 'function' ); 426 | 427 | for ( var i = 0, l = lines.length; i < l; i ++ ) { 428 | 429 | line = lines[ i ]; 430 | 431 | line = trimLeft ? line.trimLeft() : line.trim(); 432 | 433 | lineLength = line.length; 434 | 435 | if ( lineLength === 0 ) continue; 436 | 437 | lineFirstChar = line.charAt( 0 ); 438 | 439 | // @todo invoke passed in handler if any 440 | if ( lineFirstChar === '#' ) continue; 441 | 442 | if ( lineFirstChar === 'v' ) { 443 | 444 | var data = line.split( /\s+/ ); 445 | 446 | switch ( data[ 0 ] ) { 447 | 448 | case 'v': 449 | state.vertices.push( 450 | parseFloat( data[ 1 ] ), 451 | parseFloat( data[ 2 ] ), 452 | parseFloat( data[ 3 ] ) 453 | ); 454 | if ( data.length >= 7 ) { 455 | 456 | state.colors.push( 457 | parseFloat( data[ 4 ] ), 458 | parseFloat( data[ 5 ] ), 459 | parseFloat( data[ 6 ] ) 460 | 461 | ); 462 | 463 | } 464 | break; 465 | case 'vn': 466 | state.normals.push( 467 | parseFloat( data[ 1 ] ), 468 | parseFloat( data[ 2 ] ), 469 | parseFloat( data[ 3 ] ) 470 | ); 471 | break; 472 | case 'vt': 473 | state.uvs.push( 474 | parseFloat( data[ 1 ] ), 475 | parseFloat( data[ 2 ] ) 476 | ); 477 | break; 478 | 479 | } 480 | 481 | } else if ( lineFirstChar === 'f' ) { 482 | 483 | var lineData = line.substr( 1 ).trim(); 484 | var vertexData = lineData.split( /\s+/ ); 485 | var faceVertices = []; 486 | 487 | // Parse the face vertex data into an easy to work with format 488 | 489 | for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) { 490 | 491 | var vertex = vertexData[ j ]; 492 | 493 | if ( vertex.length > 0 ) { 494 | 495 | var vertexParts = vertex.split( '/' ); 496 | faceVertices.push( vertexParts ); 497 | 498 | } 499 | 500 | } 501 | 502 | // Draw an edge between the first vertex and all subsequent vertices to form an n-gon 503 | 504 | var v1 = faceVertices[ 0 ]; 505 | 506 | for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) { 507 | 508 | var v2 = faceVertices[ j ]; 509 | var v3 = faceVertices[ j + 1 ]; 510 | 511 | state.addFace( 512 | v1[ 0 ], v2[ 0 ], v3[ 0 ], 513 | v1[ 1 ], v2[ 1 ], v3[ 1 ], 514 | v1[ 2 ], v2[ 2 ], v3[ 2 ] 515 | ); 516 | 517 | } 518 | 519 | } else if ( lineFirstChar === 'l' ) { 520 | 521 | var lineParts = line.substring( 1 ).trim().split( " " ); 522 | var lineVertices = [], lineUVs = []; 523 | 524 | if ( line.indexOf( "/" ) === - 1 ) { 525 | 526 | lineVertices = lineParts; 527 | 528 | } else { 529 | 530 | for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) { 531 | 532 | var parts = lineParts[ li ].split( "/" ); 533 | 534 | if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] ); 535 | if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] ); 536 | 537 | } 538 | 539 | } 540 | state.addLineGeometry( lineVertices, lineUVs ); 541 | 542 | } else if ( lineFirstChar === 'p' ) { 543 | 544 | var lineData = line.substr( 1 ).trim(); 545 | var pointData = lineData.split( " " ); 546 | 547 | state.addPointGeometry( pointData ); 548 | 549 | } else if ( ( result = object_pattern.exec( line ) ) !== null ) { 550 | 551 | // o object_name 552 | // or 553 | // g group_name 554 | 555 | // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869 556 | // var name = result[ 0 ].substr( 1 ).trim(); 557 | var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 ); 558 | 559 | state.startObject( name ); 560 | 561 | } else if ( material_use_pattern.test( line ) ) { 562 | 563 | // material 564 | 565 | state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries ); 566 | 567 | } else if ( material_library_pattern.test( line ) ) { 568 | 569 | // mtl file 570 | 571 | state.materialLibraries.push( line.substring( 7 ).trim() ); 572 | 573 | } else if ( lineFirstChar === 's' ) { 574 | 575 | result = line.split( ' ' ); 576 | 577 | // smooth shading 578 | 579 | // @todo Handle files that have varying smooth values for a set of faces inside one geometry, 580 | // but does not define a usemtl for each face set. 581 | // This should be detected and a dummy material created (later MultiMaterial and geometry groups). 582 | // This requires some care to not create extra material on each smooth value for "normal" obj files. 583 | // where explicit usemtl defines geometry groups. 584 | // Example asset: examples/models/obj/cerberus/Cerberus.obj 585 | 586 | /* 587 | * http://paulbourke.net/dataformats/obj/ 588 | * or 589 | * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf 590 | * 591 | * From chapter "Grouping" Syntax explanation "s group_number": 592 | * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off. 593 | * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form 594 | * surfaces, smoothing groups are either turned on or off; there is no difference between values greater 595 | * than 0." 596 | */ 597 | if ( result.length > 1 ) { 598 | 599 | var value = result[ 1 ].trim().toLowerCase(); 600 | state.object.smooth = ( value !== '0' && value !== 'off' ); 601 | 602 | } else { 603 | 604 | // ZBrush can produce "s" lines #11707 605 | state.object.smooth = true; 606 | 607 | } 608 | var material = state.object.currentMaterial(); 609 | if ( material ) material.smooth = state.object.smooth; 610 | 611 | } else { 612 | 613 | // Handle null terminated files without exception 614 | if ( line === '\0' ) continue; 615 | 616 | throw new Error( 'THREE.OBJLoader: Unexpected line: "' + line + '"' ); 617 | 618 | } 619 | 620 | } 621 | 622 | state.finalize(); 623 | 624 | var container = new THREE.Group(); 625 | container.materialLibraries = [].concat( state.materialLibraries ); 626 | 627 | for ( var i = 0, l = state.objects.length; i < l; i ++ ) { 628 | 629 | var object = state.objects[ i ]; 630 | var geometry = object.geometry; 631 | var materials = object.materials; 632 | var isLine = ( geometry.type === 'Line' ); 633 | var isPoints = ( geometry.type === 'Points' ); 634 | var hasVertexColors = false; 635 | 636 | // Skip o/g line declarations that did not follow with any faces 637 | if ( geometry.vertices.length === 0 ) continue; 638 | 639 | var buffergeometry = new THREE.BufferGeometry(); 640 | 641 | buffergeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) ); 642 | 643 | if ( geometry.normals.length > 0 ) { 644 | 645 | buffergeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) ); 646 | 647 | } else { 648 | 649 | buffergeometry.computeVertexNormals(); 650 | 651 | } 652 | 653 | if ( geometry.colors.length > 0 ) { 654 | 655 | hasVertexColors = true; 656 | buffergeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) ); 657 | 658 | } 659 | 660 | if ( geometry.uvs.length > 0 ) { 661 | 662 | buffergeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) ); 663 | 664 | } 665 | 666 | // Create materials 667 | 668 | var createdMaterials = []; 669 | 670 | for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { 671 | 672 | var sourceMaterial = materials[ mi ]; 673 | var material = undefined; 674 | 675 | if ( this.materials !== null ) { 676 | 677 | material = this.materials.create( sourceMaterial.name ); 678 | 679 | // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material. 680 | if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) { 681 | 682 | var materialLine = new THREE.LineBasicMaterial(); 683 | THREE.Material.prototype.copy.call( materialLine, material ); 684 | materialLine.color.copy( material.color ); 685 | material = materialLine; 686 | 687 | } else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) { 688 | 689 | var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } ); 690 | THREE.Material.prototype.copy.call( materialPoints, material ); 691 | materialPoints.color.copy( material.color ); 692 | materialPoints.map = material.map; 693 | material = materialPoints; 694 | 695 | } 696 | 697 | } 698 | 699 | if ( ! material ) { 700 | 701 | if ( isLine ) { 702 | 703 | material = new THREE.LineBasicMaterial(); 704 | 705 | } else if ( isPoints ) { 706 | 707 | material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } ); 708 | 709 | } else { 710 | 711 | material = new THREE.MeshPhongMaterial(); 712 | 713 | } 714 | 715 | material.name = sourceMaterial.name; 716 | 717 | } 718 | 719 | material.flatShading = sourceMaterial.smooth ? false : true; 720 | material.vertexColors = hasVertexColors ? THREE.VertexColors : THREE.NoColors; 721 | 722 | createdMaterials.push( material ); 723 | 724 | } 725 | 726 | // Create mesh 727 | 728 | var mesh; 729 | 730 | if ( createdMaterials.length > 1 ) { 731 | 732 | for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { 733 | 734 | var sourceMaterial = materials[ mi ]; 735 | buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi ); 736 | 737 | } 738 | 739 | if ( isLine ) { 740 | 741 | mesh = new THREE.LineSegments( buffergeometry, createdMaterials ); 742 | 743 | } else if ( isPoints ) { 744 | 745 | mesh = new THREE.Points( buffergeometry, createdMaterials ); 746 | 747 | } else { 748 | 749 | mesh = new THREE.Mesh( buffergeometry, createdMaterials ); 750 | 751 | } 752 | 753 | } else { 754 | 755 | if ( isLine ) { 756 | 757 | mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ); 758 | 759 | } else if ( isPoints ) { 760 | 761 | mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] ); 762 | 763 | } else { 764 | 765 | mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ); 766 | 767 | } 768 | 769 | } 770 | 771 | mesh.name = object.name; 772 | 773 | container.add( mesh ); 774 | 775 | } 776 | 777 | console.timeEnd( 'OBJLoader' ); 778 | 779 | return container; 780 | 781 | } 782 | 783 | } ); 784 | 785 | return OBJLoader; 786 | 787 | } )(); 788 | -------------------------------------------------------------------------------- /static/js/lib/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 | * @author ScieCode / http://github.com/sciecode 8 | */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. 11 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 12 | // 13 | // Orbit - left mouse / touch: one-finger move 14 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish 15 | // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move 16 | 17 | THREE.OrbitControls = function ( object, domElement ) { 18 | 19 | this.object = object; 20 | 21 | this.domElement = ( domElement !== undefined ) ? domElement : document; 22 | 23 | // Set to false to disable this control 24 | this.enabled = true; 25 | 26 | // "target" sets the location of focus, where the object orbits around 27 | this.target = new THREE.Vector3(); 28 | 29 | // How far you can dolly in and out ( PerspectiveCamera only ) 30 | this.minDistance = 0; 31 | this.maxDistance = Infinity; 32 | 33 | // How far you can zoom in and out ( OrthographicCamera only ) 34 | this.minZoom = 0; 35 | this.maxZoom = Infinity; 36 | 37 | // How far you can orbit vertically, upper and lower limits. 38 | // Range is 0 to Math.PI radians. 39 | this.minPolarAngle = 0; // radians 40 | this.maxPolarAngle = Math.PI; // radians 41 | 42 | // How far you can orbit horizontally, upper and lower limits. 43 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 44 | this.minAzimuthAngle = - Infinity; // radians 45 | this.maxAzimuthAngle = Infinity; // radians 46 | 47 | // Set to true to enable damping (inertia) 48 | // If damping is enabled, you must call controls.update() in your animation loop 49 | this.enableDamping = false; 50 | this.dampingFactor = 0.05; 51 | 52 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. 53 | // Set to false to disable zooming 54 | this.enableZoom = true; 55 | this.zoomSpeed = 1.0; 56 | 57 | // Set to false to disable rotating 58 | this.enableRotate = true; 59 | this.rotateSpeed = 1.0; 60 | 61 | // Set to false to disable panning 62 | this.enablePan = true; 63 | this.panSpeed = 1.0; 64 | this.screenSpacePanning = false; // if true, pan in screen-space 65 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 66 | 67 | // Set to true to automatically rotate around the target 68 | // If auto-rotate is enabled, you must call controls.update() in your animation loop 69 | this.autoRotate = false; 70 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 71 | 72 | // Set to false to disable use of the keys 73 | this.enableKeys = true; 74 | 75 | // The four arrow keys 76 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 77 | 78 | // Mouse buttons 79 | this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN }; 80 | 81 | // Touch fingers 82 | this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN }; 83 | 84 | // for reset 85 | this.target0 = this.target.clone(); 86 | this.position0 = this.object.position.clone(); 87 | this.zoom0 = this.object.zoom; 88 | 89 | // 90 | // public methods 91 | // 92 | 93 | this.getPolarAngle = function () { 94 | 95 | return spherical.phi; 96 | 97 | }; 98 | 99 | this.getAzimuthalAngle = function () { 100 | 101 | return spherical.theta; 102 | 103 | }; 104 | 105 | this.saveState = function () { 106 | 107 | scope.target0.copy( scope.target ); 108 | scope.position0.copy( scope.object.position ); 109 | scope.zoom0 = scope.object.zoom; 110 | 111 | }; 112 | 113 | this.reset = function () { 114 | 115 | scope.target.copy( scope.target0 ); 116 | scope.object.position.copy( scope.position0 ); 117 | scope.object.zoom = scope.zoom0; 118 | 119 | scope.object.updateProjectionMatrix(); 120 | scope.dispatchEvent( changeEvent ); 121 | 122 | scope.update(); 123 | 124 | state = STATE.NONE; 125 | 126 | }; 127 | 128 | // this method is exposed, but perhaps it would be better if we can make it private... 129 | this.update = function () { 130 | 131 | var offset = new THREE.Vector3(); 132 | 133 | // so camera.up is the orbit axis 134 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 0, -1 ) ); 135 | var quatInverse = quat.clone().inverse(); 136 | 137 | var lastPosition = new THREE.Vector3(); 138 | var lastQuaternion = new THREE.Quaternion(); 139 | 140 | return function update() { 141 | 142 | var position = scope.object.position; 143 | 144 | offset.copy( position ).sub( scope.target ); 145 | 146 | // rotate offset to "y-axis-is-up" space 147 | offset.applyQuaternion( quat ); 148 | 149 | // angle from z-axis around y-axis 150 | spherical.setFromVector3( offset ); 151 | 152 | if ( scope.autoRotate && state === STATE.NONE ) { 153 | 154 | rotateLeft( getAutoRotationAngle() ); 155 | 156 | } 157 | 158 | if ( scope.enableDamping ) { 159 | 160 | spherical.theta += sphericalDelta.theta * scope.dampingFactor; 161 | spherical.phi += sphericalDelta.phi * scope.dampingFactor; 162 | 163 | } else { 164 | 165 | spherical.theta += sphericalDelta.theta; 166 | spherical.phi += sphericalDelta.phi; 167 | 168 | } 169 | 170 | // restrict theta to be between desired limits 171 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); 172 | 173 | // restrict phi to be between desired limits 174 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); 175 | 176 | spherical.makeSafe(); 177 | 178 | 179 | spherical.radius *= scale; 180 | 181 | // restrict radius to be between desired limits 182 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); 183 | 184 | // move target to panned location 185 | 186 | if ( scope.enableDamping === true ) { 187 | 188 | scope.target.addScaledVector( panOffset, scope.dampingFactor ); 189 | 190 | } else { 191 | 192 | scope.target.add( panOffset ); 193 | 194 | } 195 | 196 | offset.setFromSpherical( spherical ); 197 | 198 | // rotate offset back to "camera-up-vector-is-up" space 199 | offset.applyQuaternion( quatInverse ); 200 | 201 | position.copy( scope.target ).add( offset ); 202 | 203 | scope.object.lookAt( scope.target ); 204 | 205 | if ( scope.enableDamping === true ) { 206 | 207 | sphericalDelta.theta *= ( 1 - scope.dampingFactor ); 208 | sphericalDelta.phi *= ( 1 - scope.dampingFactor ); 209 | 210 | panOffset.multiplyScalar( 1 - scope.dampingFactor ); 211 | 212 | } else { 213 | 214 | sphericalDelta.set( 0, 0, 0 ); 215 | 216 | panOffset.set( 0, 0, 0 ); 217 | 218 | } 219 | 220 | scale = 1; 221 | 222 | // update condition is: 223 | // min(camera displacement, camera rotation in radians)^2 > EPS 224 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 225 | 226 | if ( zoomChanged || 227 | lastPosition.distanceToSquared( scope.object.position ) > EPS || 228 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { 229 | 230 | scope.dispatchEvent( changeEvent ); 231 | 232 | lastPosition.copy( scope.object.position ); 233 | lastQuaternion.copy( scope.object.quaternion ); 234 | zoomChanged = false; 235 | 236 | return true; 237 | 238 | } 239 | 240 | return false; 241 | 242 | }; 243 | 244 | }(); 245 | 246 | this.dispose = function () { 247 | 248 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); 249 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); 250 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); 251 | 252 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); 253 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); 254 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); 255 | 256 | document.removeEventListener( 'mousemove', onMouseMove, false ); 257 | document.removeEventListener( 'mouseup', onMouseUp, false ); 258 | 259 | window.removeEventListener( 'keydown', onKeyDown, false ); 260 | 261 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? 262 | 263 | }; 264 | 265 | // 266 | // internals 267 | // 268 | 269 | var scope = this; 270 | 271 | var changeEvent = { type: 'change' }; 272 | var startEvent = { type: 'start' }; 273 | var endEvent = { type: 'end' }; 274 | 275 | var STATE = { 276 | NONE: - 1, 277 | ROTATE: 0, 278 | DOLLY: 1, 279 | PAN: 2, 280 | TOUCH_ROTATE: 3, 281 | TOUCH_PAN: 4, 282 | TOUCH_DOLLY_PAN: 5, 283 | TOUCH_DOLLY_ROTATE: 6 284 | }; 285 | 286 | var state = STATE.NONE; 287 | 288 | var EPS = 0.000001; 289 | 290 | // current position in spherical coordinates 291 | var spherical = new THREE.Spherical(); 292 | var sphericalDelta = new THREE.Spherical(); 293 | 294 | var scale = 1; 295 | var panOffset = new THREE.Vector3(); 296 | var zoomChanged = false; 297 | 298 | var rotateStart = new THREE.Vector2(); 299 | var rotateEnd = new THREE.Vector2(); 300 | var rotateDelta = new THREE.Vector2(); 301 | 302 | var panStart = new THREE.Vector2(); 303 | var panEnd = new THREE.Vector2(); 304 | var panDelta = new THREE.Vector2(); 305 | 306 | var dollyStart = new THREE.Vector2(); 307 | var dollyEnd = new THREE.Vector2(); 308 | var dollyDelta = new THREE.Vector2(); 309 | 310 | function getAutoRotationAngle() { 311 | 312 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 313 | 314 | } 315 | 316 | function getZoomScale() { 317 | 318 | return Math.pow( 0.95, scope.zoomSpeed ); 319 | 320 | } 321 | 322 | function rotateLeft( angle ) { 323 | 324 | sphericalDelta.theta -= angle; 325 | 326 | } 327 | 328 | function rotateUp( angle ) { 329 | 330 | sphericalDelta.phi -= angle; 331 | 332 | } 333 | 334 | var panLeft = function () { 335 | 336 | var v = new THREE.Vector3(); 337 | 338 | return function panLeft( distance, objectMatrix ) { 339 | 340 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix 341 | v.multiplyScalar( - distance ); 342 | 343 | panOffset.add( v ); 344 | 345 | }; 346 | 347 | }(); 348 | 349 | var panUp = function () { 350 | 351 | var v = new THREE.Vector3(); 352 | 353 | return function panUp( distance, objectMatrix ) { 354 | 355 | if ( scope.screenSpacePanning === true ) { 356 | 357 | v.setFromMatrixColumn( objectMatrix, 1 ); 358 | 359 | } else { 360 | 361 | v.setFromMatrixColumn( objectMatrix, 0 ); 362 | v.crossVectors( scope.object.up, v ); 363 | 364 | } 365 | 366 | v.multiplyScalar( distance ); 367 | 368 | panOffset.add( v ); 369 | 370 | }; 371 | 372 | }(); 373 | 374 | // deltaX and deltaY are in pixels; right and down are positive 375 | var pan = function () { 376 | 377 | var offset = new THREE.Vector3(); 378 | 379 | return function pan( deltaX, deltaY ) { 380 | 381 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 382 | 383 | if ( scope.object.isPerspectiveCamera ) { 384 | 385 | // perspective 386 | var position = scope.object.position; 387 | offset.copy( position ).sub( scope.target ); 388 | var targetDistance = offset.length(); 389 | 390 | // half of the fov is center to top of screen 391 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 392 | 393 | // we use only clientHeight here so aspect ratio does not distort speed 394 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); 395 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); 396 | 397 | } else if ( scope.object.isOrthographicCamera ) { 398 | 399 | // orthographic 400 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); 401 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); 402 | 403 | } else { 404 | 405 | // camera neither orthographic nor perspective 406 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 407 | scope.enablePan = false; 408 | 409 | } 410 | 411 | }; 412 | 413 | }(); 414 | 415 | function dollyIn( dollyScale ) { 416 | 417 | if ( scope.object.isPerspectiveCamera ) { 418 | 419 | scale /= dollyScale; 420 | 421 | } else if ( scope.object.isOrthographicCamera ) { 422 | 423 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); 424 | scope.object.updateProjectionMatrix(); 425 | zoomChanged = true; 426 | 427 | } else { 428 | 429 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 430 | scope.enableZoom = false; 431 | 432 | } 433 | 434 | } 435 | 436 | function dollyOut( dollyScale ) { 437 | 438 | if ( scope.object.isPerspectiveCamera ) { 439 | 440 | scale *= dollyScale; 441 | 442 | } else if ( scope.object.isOrthographicCamera ) { 443 | 444 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); 445 | scope.object.updateProjectionMatrix(); 446 | zoomChanged = true; 447 | 448 | } else { 449 | 450 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 451 | scope.enableZoom = false; 452 | 453 | } 454 | 455 | } 456 | 457 | // 458 | // event callbacks - update the object state 459 | // 460 | 461 | function handleMouseDownRotate( event ) { 462 | 463 | rotateStart.set( event.clientX, event.clientY ); 464 | 465 | } 466 | 467 | function handleMouseDownDolly( event ) { 468 | 469 | dollyStart.set( event.clientX, event.clientY ); 470 | 471 | } 472 | 473 | function handleMouseDownPan( event ) { 474 | 475 | panStart.set( event.clientX, event.clientY ); 476 | 477 | } 478 | 479 | function handleMouseMoveRotate( event ) { 480 | 481 | rotateEnd.set( event.clientX, event.clientY ); 482 | 483 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); 484 | 485 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 486 | 487 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height 488 | 489 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); 490 | 491 | rotateStart.copy( rotateEnd ); 492 | 493 | scope.update(); 494 | 495 | } 496 | 497 | function handleMouseMoveDolly( event ) { 498 | 499 | dollyEnd.set( event.clientX, event.clientY ); 500 | 501 | dollyDelta.subVectors( dollyEnd, dollyStart ); 502 | 503 | if ( dollyDelta.y > 0 ) { 504 | 505 | dollyIn( getZoomScale() ); 506 | 507 | } else if ( dollyDelta.y < 0 ) { 508 | 509 | dollyOut( getZoomScale() ); 510 | 511 | } 512 | 513 | dollyStart.copy( dollyEnd ); 514 | 515 | scope.update(); 516 | 517 | } 518 | 519 | function handleMouseMovePan( event ) { 520 | 521 | panEnd.set( event.clientX, event.clientY ); 522 | 523 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); 524 | 525 | pan( panDelta.x, panDelta.y ); 526 | 527 | panStart.copy( panEnd ); 528 | 529 | scope.update(); 530 | 531 | } 532 | 533 | function handleMouseUp( /*event*/ ) { 534 | 535 | // no-op 536 | 537 | } 538 | 539 | function handleMouseWheel( event ) { 540 | 541 | if ( event.deltaY < 0 ) { 542 | 543 | dollyOut( getZoomScale() ); 544 | 545 | } else if ( event.deltaY > 0 ) { 546 | 547 | dollyIn( getZoomScale() ); 548 | 549 | } 550 | 551 | scope.update(); 552 | 553 | } 554 | 555 | function handleKeyDown( event ) { 556 | 557 | var needsUpdate = false; 558 | 559 | switch ( event.keyCode ) { 560 | 561 | case scope.keys.UP: 562 | pan( 0, scope.keyPanSpeed ); 563 | needsUpdate = true; 564 | break; 565 | 566 | case scope.keys.BOTTOM: 567 | pan( 0, - scope.keyPanSpeed ); 568 | needsUpdate = true; 569 | break; 570 | 571 | case scope.keys.LEFT: 572 | pan( scope.keyPanSpeed, 0 ); 573 | needsUpdate = true; 574 | break; 575 | 576 | case scope.keys.RIGHT: 577 | pan( - scope.keyPanSpeed, 0 ); 578 | needsUpdate = true; 579 | break; 580 | 581 | } 582 | 583 | if ( needsUpdate ) { 584 | 585 | // prevent the browser from scrolling on cursor keys 586 | event.preventDefault(); 587 | 588 | scope.update(); 589 | 590 | } 591 | 592 | 593 | } 594 | 595 | function handleTouchStartRotate( event ) { 596 | 597 | if ( event.touches.length == 1 ) { 598 | 599 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 600 | 601 | } else { 602 | 603 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); 604 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); 605 | 606 | rotateStart.set( x, y ); 607 | 608 | } 609 | 610 | } 611 | 612 | function handleTouchStartPan( event ) { 613 | 614 | if ( event.touches.length == 1 ) { 615 | 616 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 617 | 618 | } else { 619 | 620 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); 621 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); 622 | 623 | panStart.set( x, y ); 624 | 625 | } 626 | 627 | } 628 | 629 | function handleTouchStartDolly( event ) { 630 | 631 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 632 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 633 | 634 | var distance = Math.sqrt( dx * dx + dy * dy ); 635 | 636 | dollyStart.set( 0, distance ); 637 | 638 | } 639 | 640 | function handleTouchStartDollyPan( event ) { 641 | 642 | if ( scope.enableZoom ) handleTouchStartDolly( event ); 643 | 644 | if ( scope.enablePan ) handleTouchStartPan( event ); 645 | 646 | } 647 | 648 | function handleTouchStartDollyRotate( event ) { 649 | 650 | if ( scope.enableZoom ) handleTouchStartDolly( event ); 651 | 652 | if ( scope.enableRotate ) handleTouchStartRotate( event ); 653 | 654 | } 655 | 656 | function handleTouchMoveRotate( event ) { 657 | 658 | if ( event.touches.length == 1 ) { 659 | 660 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 661 | 662 | } else { 663 | 664 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); 665 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); 666 | 667 | rotateEnd.set( x, y ); 668 | 669 | } 670 | 671 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); 672 | 673 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 674 | 675 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height 676 | 677 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); 678 | 679 | rotateStart.copy( rotateEnd ); 680 | 681 | } 682 | 683 | function handleTouchMovePan( event ) { 684 | 685 | if ( event.touches.length == 1 ) { 686 | 687 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 688 | 689 | } else { 690 | 691 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); 692 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); 693 | 694 | panEnd.set( x, y ); 695 | 696 | } 697 | 698 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); 699 | 700 | pan( panDelta.x, panDelta.y ); 701 | 702 | panStart.copy( panEnd ); 703 | 704 | } 705 | 706 | function handleTouchMoveDolly( event ) { 707 | 708 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 709 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 710 | 711 | var distance = Math.sqrt( dx * dx + dy * dy ); 712 | 713 | dollyEnd.set( 0, distance ); 714 | 715 | dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); 716 | 717 | dollyIn( dollyDelta.y ); 718 | 719 | dollyStart.copy( dollyEnd ); 720 | 721 | } 722 | 723 | function handleTouchMoveDollyPan( event ) { 724 | 725 | if ( scope.enableZoom ) handleTouchMoveDolly( event ); 726 | 727 | if ( scope.enablePan ) handleTouchMovePan( event ); 728 | 729 | } 730 | 731 | function handleTouchMoveDollyRotate( event ) { 732 | 733 | if ( scope.enableZoom ) handleTouchMoveDolly( event ); 734 | 735 | if ( scope.enableRotate ) handleTouchMoveRotate( event ); 736 | 737 | } 738 | 739 | function handleTouchEnd( /*event*/ ) { 740 | 741 | // no-op 742 | 743 | } 744 | 745 | // 746 | // event handlers - FSM: listen for events and reset state 747 | // 748 | 749 | function onMouseDown( event ) { 750 | 751 | if ( scope.enabled === false ) return; 752 | 753 | // Prevent the browser from scrolling. 754 | 755 | event.preventDefault(); 756 | 757 | // Manually set the focus since calling preventDefault above 758 | // prevents the browser from setting it automatically. 759 | 760 | scope.domElement.focus ? scope.domElement.focus() : window.focus(); 761 | 762 | switch ( event.button ) { 763 | 764 | case 0: 765 | 766 | switch ( scope.mouseButtons.LEFT ) { 767 | 768 | case THREE.MOUSE.ROTATE: 769 | 770 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) { 771 | 772 | if ( scope.enablePan === false ) return; 773 | 774 | handleMouseDownPan( event ); 775 | 776 | state = STATE.PAN; 777 | 778 | } else { 779 | 780 | if ( scope.enableRotate === false ) return; 781 | 782 | handleMouseDownRotate( event ); 783 | 784 | state = STATE.ROTATE; 785 | 786 | } 787 | 788 | break; 789 | 790 | case THREE.MOUSE.PAN: 791 | 792 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) { 793 | 794 | if ( scope.enableRotate === false ) return; 795 | 796 | handleMouseDownRotate( event ); 797 | 798 | state = STATE.ROTATE; 799 | 800 | } else { 801 | 802 | if ( scope.enablePan === false ) return; 803 | 804 | handleMouseDownPan( event ); 805 | 806 | state = STATE.PAN; 807 | 808 | } 809 | 810 | break; 811 | 812 | default: 813 | 814 | state = STATE.NONE; 815 | 816 | } 817 | 818 | break; 819 | 820 | 821 | case 1: 822 | 823 | switch ( scope.mouseButtons.MIDDLE ) { 824 | 825 | case THREE.MOUSE.DOLLY: 826 | 827 | if ( scope.enableZoom === false ) return; 828 | 829 | handleMouseDownDolly( event ); 830 | 831 | state = STATE.DOLLY; 832 | 833 | break; 834 | 835 | 836 | default: 837 | 838 | state = STATE.NONE; 839 | 840 | } 841 | 842 | break; 843 | 844 | case 2: 845 | 846 | switch ( scope.mouseButtons.RIGHT ) { 847 | 848 | case THREE.MOUSE.ROTATE: 849 | 850 | if ( scope.enableRotate === false ) return; 851 | 852 | handleMouseDownRotate( event ); 853 | 854 | state = STATE.ROTATE; 855 | 856 | break; 857 | 858 | case THREE.MOUSE.PAN: 859 | 860 | if ( scope.enablePan === false ) return; 861 | 862 | handleMouseDownPan( event ); 863 | 864 | state = STATE.PAN; 865 | 866 | break; 867 | 868 | default: 869 | 870 | state = STATE.NONE; 871 | 872 | } 873 | 874 | break; 875 | 876 | } 877 | 878 | if ( state !== STATE.NONE ) { 879 | 880 | document.addEventListener( 'mousemove', onMouseMove, false ); 881 | document.addEventListener( 'mouseup', onMouseUp, false ); 882 | 883 | scope.dispatchEvent( startEvent ); 884 | 885 | } 886 | 887 | } 888 | 889 | function onMouseMove( event ) { 890 | 891 | if ( scope.enabled === false ) return; 892 | 893 | event.preventDefault(); 894 | 895 | switch ( state ) { 896 | 897 | case STATE.ROTATE: 898 | 899 | if ( scope.enableRotate === false ) return; 900 | 901 | handleMouseMoveRotate( event ); 902 | 903 | break; 904 | 905 | case STATE.DOLLY: 906 | 907 | if ( scope.enableZoom === false ) return; 908 | 909 | handleMouseMoveDolly( event ); 910 | 911 | break; 912 | 913 | case STATE.PAN: 914 | 915 | if ( scope.enablePan === false ) return; 916 | 917 | handleMouseMovePan( event ); 918 | 919 | break; 920 | 921 | } 922 | 923 | } 924 | 925 | function onMouseUp( event ) { 926 | 927 | if ( scope.enabled === false ) return; 928 | 929 | handleMouseUp( event ); 930 | 931 | document.removeEventListener( 'mousemove', onMouseMove, false ); 932 | document.removeEventListener( 'mouseup', onMouseUp, false ); 933 | 934 | scope.dispatchEvent( endEvent ); 935 | 936 | state = STATE.NONE; 937 | 938 | } 939 | 940 | function onMouseWheel( event ) { 941 | 942 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; 943 | 944 | event.preventDefault(); 945 | event.stopPropagation(); 946 | 947 | scope.dispatchEvent( startEvent ); 948 | 949 | handleMouseWheel( event ); 950 | 951 | scope.dispatchEvent( endEvent ); 952 | 953 | } 954 | 955 | function onKeyDown( event ) { 956 | 957 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; 958 | 959 | handleKeyDown( event ); 960 | 961 | } 962 | 963 | function onTouchStart( event ) { 964 | 965 | if ( scope.enabled === false ) return; 966 | 967 | event.preventDefault(); 968 | 969 | switch ( event.touches.length ) { 970 | 971 | case 1: 972 | 973 | switch ( scope.touches.ONE ) { 974 | 975 | case THREE.TOUCH.ROTATE: 976 | 977 | if ( scope.enableRotate === false ) return; 978 | 979 | handleTouchStartRotate( event ); 980 | 981 | state = STATE.TOUCH_ROTATE; 982 | 983 | break; 984 | 985 | case THREE.TOUCH.PAN: 986 | 987 | if ( scope.enablePan === false ) return; 988 | 989 | handleTouchStartPan( event ); 990 | 991 | state = STATE.TOUCH_PAN; 992 | 993 | break; 994 | 995 | default: 996 | 997 | state = STATE.NONE; 998 | 999 | } 1000 | 1001 | break; 1002 | 1003 | case 2: 1004 | 1005 | switch ( scope.touches.TWO ) { 1006 | 1007 | case THREE.TOUCH.DOLLY_PAN: 1008 | 1009 | if ( scope.enableZoom === false && scope.enablePan === false ) return; 1010 | 1011 | handleTouchStartDollyPan( event ); 1012 | 1013 | state = STATE.TOUCH_DOLLY_PAN; 1014 | 1015 | break; 1016 | 1017 | case THREE.TOUCH.DOLLY_ROTATE: 1018 | 1019 | if ( scope.enableZoom === false && scope.enableRotate === false ) return; 1020 | 1021 | handleTouchStartDollyRotate( event ); 1022 | 1023 | state = STATE.TOUCH_DOLLY_ROTATE; 1024 | 1025 | break; 1026 | 1027 | default: 1028 | 1029 | state = STATE.NONE; 1030 | 1031 | } 1032 | 1033 | break; 1034 | 1035 | default: 1036 | 1037 | state = STATE.NONE; 1038 | 1039 | } 1040 | 1041 | if ( state !== STATE.NONE ) { 1042 | 1043 | scope.dispatchEvent( startEvent ); 1044 | 1045 | } 1046 | 1047 | } 1048 | 1049 | function onTouchMove( event ) { 1050 | 1051 | if ( scope.enabled === false ) return; 1052 | 1053 | event.preventDefault(); 1054 | event.stopPropagation(); 1055 | 1056 | switch ( state ) { 1057 | 1058 | case STATE.TOUCH_ROTATE: 1059 | 1060 | if ( scope.enableRotate === false ) return; 1061 | 1062 | handleTouchMoveRotate( event ); 1063 | 1064 | scope.update(); 1065 | 1066 | break; 1067 | 1068 | case STATE.TOUCH_PAN: 1069 | 1070 | if ( scope.enablePan === false ) return; 1071 | 1072 | handleTouchMovePan( event ); 1073 | 1074 | scope.update(); 1075 | 1076 | break; 1077 | 1078 | case STATE.TOUCH_DOLLY_PAN: 1079 | 1080 | if ( scope.enableZoom === false && scope.enablePan === false ) return; 1081 | 1082 | handleTouchMoveDollyPan( event ); 1083 | 1084 | scope.update(); 1085 | 1086 | break; 1087 | 1088 | case STATE.TOUCH_DOLLY_ROTATE: 1089 | 1090 | if ( scope.enableZoom === false && scope.enableRotate === false ) return; 1091 | 1092 | handleTouchMoveDollyRotate( event ); 1093 | 1094 | scope.update(); 1095 | 1096 | break; 1097 | 1098 | default: 1099 | 1100 | state = STATE.NONE; 1101 | 1102 | } 1103 | 1104 | } 1105 | 1106 | function onTouchEnd( event ) { 1107 | 1108 | if ( scope.enabled === false ) return; 1109 | 1110 | handleTouchEnd( event ); 1111 | 1112 | scope.dispatchEvent( endEvent ); 1113 | 1114 | state = STATE.NONE; 1115 | 1116 | } 1117 | 1118 | function onContextMenu( event ) { 1119 | 1120 | if ( scope.enabled === false ) return; 1121 | 1122 | event.preventDefault(); 1123 | 1124 | } 1125 | 1126 | // 1127 | 1128 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); 1129 | 1130 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); 1131 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); 1132 | 1133 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); 1134 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); 1135 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); 1136 | 1137 | window.addEventListener( 'keydown', onKeyDown, false ); 1138 | 1139 | // force an update at start 1140 | 1141 | this.update(); 1142 | 1143 | }; 1144 | 1145 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 1146 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; 1147 | 1148 | 1149 | // This set of controls performs orbiting, dollying (zooming), and panning. 1150 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 1151 | // This is very similar to OrbitControls, another set of touch behavior 1152 | // 1153 | // Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate 1154 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish 1155 | // Pan - left mouse, or arrow keys / touch: one-finger move 1156 | 1157 | THREE.MapControls = function ( object, domElement ) { 1158 | 1159 | THREE.OrbitControls.call( this, object, domElement ); 1160 | 1161 | this.mouseButtons.LEFT = THREE.MOUSE.PAN; 1162 | this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE; 1163 | 1164 | this.touches.ONE = THREE.TOUCH.PAN; 1165 | this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE; 1166 | 1167 | }; 1168 | 1169 | THREE.MapControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 1170 | THREE.MapControls.prototype.constructor = THREE.MapControls; 1171 | -------------------------------------------------------------------------------- /static/js/renderer.js: -------------------------------------------------------------------------------- 1 | var scene = new THREE.Scene(); 2 | var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 10000); 3 | 4 | var renderer = new THREE.WebGLRenderer({antialias: true}); 5 | renderer.shadowMap.enabled = true; 6 | renderer.shadowMapType = THREE.PCFSoftShadowMap; 7 | renderer.setSize( window.innerWidth, window.innerHeight ); 8 | document.body.appendChild( renderer.domElement ); 9 | 10 | var spotLight = new THREE.SpotLight( 0xFFFFFF, 0.7); 11 | spotLight.position.set(2, 2, 50); 12 | spotLight.target.position.set(0, 0, 0); 13 | spotLight.castShadow = true; 14 | scene.add(spotLight.target); 15 | scene.add(spotLight); 16 | spotLight.shadow.mapSize.width = 512; // default 17 | spotLight.shadow.mapSize.height = 512; // default 18 | spotLight.shadow.camera.near = 0.5; // default 19 | spotLight.shadow.camera.far = 15000; // default 20 | 21 | 22 | var ambientLight = new THREE.AmbientLight( 0x888888 ); 23 | scene.add(ambientLight); 24 | 25 | renderer.setClearColor( 0xffffff, 1 ); 26 | 27 | var controls = new THREE.OrbitControls(camera, renderer.domElement); 28 | camera.position.set(24, 0, 12); 29 | camera.lookAt(0, 0, 0); 30 | camera.up.set( 0, 0, 1 ); 31 | controls.update(); 32 | 33 | var geometry = new THREE.PlaneGeometry(60, 60, 1, 1); 34 | var material = new THREE.MeshLambertMaterial( {color: 0xffffff} ); 35 | var plane = new THREE.Mesh(geometry, material); 36 | plane.position.set(0, 0, 0); 37 | plane.receiveShadow = true; 38 | scene.add(plane); 39 | 40 | // var axes = new THREE.AxisHelper(50); 41 | // scene.add(axes); 42 | 43 | var loader = new THREE.OBJLoader(); 44 | 45 | var block1 = null; 46 | var block2 = null; 47 | var block3 = null; 48 | 49 | var object = null; 50 | 51 | [1, 2, 3].forEach((val, idx) => { 52 | 53 | loader.load( 54 | '/static/model/' + val + '.obj', 55 | function (object) { 56 | 57 | var obj = object.children[0]; 58 | 59 | if (val == 1) { 60 | block1 = obj; 61 | } else if (val == 2) { 62 | block2 = obj; 63 | } else if (val == 3) { 64 | block3 = obj; 65 | } 66 | 67 | obj.castShadow = true; 68 | obj.receiveShadow = true; 69 | 70 | }, 71 | function (xhr) { 72 | 73 | console.log(val + ( xhr.loaded / xhr.total * 100 ) + '% loaded' ); 74 | 75 | }, 76 | function (error) { 77 | 78 | console.log( 'An error happened for ' + val); 79 | 80 | } 81 | ); 82 | 83 | }); 84 | 85 | var animate = function () { 86 | requestAnimationFrame(animate); 87 | 88 | if (object) { 89 | object.rotation.z += 0.006; 90 | } 91 | 92 | controls.update(); 93 | renderer.render(scene, camera); 94 | }; 95 | 96 | animate(); 97 | 98 | function getRandomInt(max) { 99 | return Math.floor(Math.random() * Math.floor(max)); 100 | } 101 | 102 | function generate_lego(dic) { 103 | 104 | var total = Object.keys(dic).length - 2; 105 | 106 | var configurations = []; 107 | 108 | for (var i = 0; i < total; i++) { 109 | 110 | var positions = dic[i]; 111 | var n = positions.length; 112 | var rotation = 0; 113 | 114 | if (n == 1) { 115 | 116 | rotation = 0 117 | 118 | } else { 119 | 120 | var x1 = parseInt(positions[0]['x']); 121 | var y1 = parseInt(positions[0]['y']); 122 | var x2 = parseInt(positions[1]['x']); 123 | var y2 = parseInt(positions[1]['y']); 124 | 125 | if (x2 > x1) { 126 | rotation = 0; 127 | } else if (y2 > y1) { 128 | rotation = 90; 129 | } else if (x2 < x1) { 130 | rotation = 180; 131 | } else if (y2 < y1) { 132 | rotation = 270; 133 | } 134 | 135 | } 136 | 137 | var configuration = { 138 | 139 | 'num': n, 140 | 'rotation': rotation, 141 | 'x': parseInt(positions[0]['x']), 142 | 'y': parseInt(positions[0]['y']), 143 | 'z': parseInt(positions[0]['z']) 144 | } 145 | 146 | configurations.push(configuration) 147 | 148 | } 149 | 150 | var legos = []; 151 | 152 | var prev_h = -1; 153 | 154 | var x_sum = 0; 155 | var y_sum = 0; 156 | var z_sum = 0; 157 | 158 | for (var i = 0; i < configurations.length; i++) { 159 | 160 | var c = configurations[i]; 161 | 162 | var rotation = c['rotation']; 163 | var num = c['num']; 164 | var x = c['x']; 165 | var y = c['y']; 166 | var z = c['z']; 167 | 168 | var mesh; 169 | 170 | if (num == 1) { 171 | mesh = block1.clone(); 172 | } else if (num == 2) { 173 | mesh = block2.clone(); 174 | } else if (num == 3) { 175 | mesh = block3.clone(); 176 | } 177 | 178 | if (rotation != 0) { 179 | var radian = rotation * Math.PI / 180; 180 | mesh.rotateZ(radian); 181 | } 182 | 183 | var h, s, l; 184 | 185 | while (true) { 186 | 187 | var candidates = [0, 9, 18, 35, 64, 85]; 188 | var r = getRandomInt(candidates.length); 189 | var h = candidates[r]; 190 | 191 | if (h != prev_h) { 192 | prev_h = h; 193 | break 194 | } 195 | } 196 | 197 | h = (h * 360 / 100) | 0; 198 | l = 50; 199 | s = 100; 200 | 201 | var color = new THREE.Color('hsl(' + h + ', ' + s + '%, ' + l + '%)'); 202 | 203 | var material = new THREE.MeshPhongMaterial( { color: color, reflectivity: 1.0 } ); 204 | 205 | mesh.material = material; 206 | 207 | mesh.position.x = x + 0.5; 208 | mesh.position.y = y + 0.5; 209 | mesh.position.z = z + 0.5; 210 | 211 | legos.push(mesh); 212 | 213 | x_sum += mesh.position.x; 214 | y_sum += mesh.position.y; 215 | z_sum += mesh.position.z; 216 | 217 | } 218 | 219 | var average_x = (x_sum / legos.length) | 0; 220 | var average_y = (y_sum / legos.length) | 0; 221 | var average_z = (z_sum / legos.length) | 0; 222 | 223 | plane.position.z = -average_z; 224 | 225 | var index = 0; 226 | function add() { 227 | 228 | if (index >= legos.length - 1) { 229 | clearInterval(interval); 230 | return; 231 | } 232 | 233 | var lego = legos[index]; 234 | lego.position.x -= average_x; 235 | lego.position.y -= average_y; 236 | lego.position.z -= average_z; 237 | object.add(lego); 238 | 239 | index += 1; 240 | } 241 | var interval = setInterval(add, 40); 242 | 243 | scene.add(object); 244 | } 245 | 246 | function generate_pipe(dic) { 247 | 248 | var total = Object.keys(dic).length - 2; 249 | 250 | var vectors = []; 251 | 252 | var sum_x = 0; 253 | var sum_y = 0; 254 | var sum_z = 0; 255 | 256 | for (var i = 0; i < total; i++) { 257 | 258 | var positions = dic[i]; 259 | 260 | var x = parseInt(positions[0]['x']); 261 | var y = parseInt(positions[0]['y']); 262 | var z = parseInt(positions[0]['z']); 263 | 264 | var vec = new THREE.Vector3(x, y, z); 265 | 266 | vectors.push(vec); 267 | 268 | sum_x += x; 269 | sum_y += y; 270 | sum_z += z; 271 | } 272 | 273 | var average_x = (sum_x / vectors.length) | 0; 274 | var average_y = (sum_y / vectors.length) | 0; 275 | var average_z = (sum_z / vectors.length) | 0; 276 | 277 | for (var i = 0; i < vectors.length; i++) { 278 | 279 | var vec = vectors[i]; 280 | vec.x -= average_x; 281 | vec.y -= average_y; 282 | vec.z -= average_z; 283 | } 284 | 285 | var path = new THREE.CatmullRomCurve3(vectors, false, 'catmullrom', 10); 286 | 287 | var geometry = new THREE.TubeGeometry(path, 30, 0.2, 30, false); 288 | var material = new THREE.MeshPhongMaterial( { color: 0x666666, emissive: 0x000000 } ); 289 | var mesh = new THREE.Mesh(geometry, material); 290 | 291 | mesh.castShadow = true; 292 | mesh.receiveShadow = true; 293 | 294 | plane.position.z = -average_z - 1; 295 | 296 | object.add(mesh); 297 | scene.add(object); 298 | } 299 | 300 | 301 | function build(dic, model_type) { 302 | 303 | if (object != null) { 304 | for (var i = 0; i < object.children.length; i++) { 305 | var child = object.children[i]; 306 | scene.remove(child); 307 | child.geometry.dispose(); 308 | child.material.dispose(); 309 | } 310 | scene.remove(object); 311 | object = undefined; 312 | } 313 | 314 | object = new THREE.Group(); 315 | 316 | if (model_type == 'lego') { 317 | generate_lego(dic); 318 | } else if (model_type == 'voxel') { 319 | generate_pipe(dic); 320 | } else { 321 | alert('The specified type of ' + model_type + ' is not available.') 322 | } 323 | 324 | camera.position.set(24, 0, 12); 325 | camera.lookAt(0, 0, 0); 326 | camera.up.set( 0, 0, 1 ); 327 | 328 | } 329 | 330 | function download_model() { 331 | 332 | if (object == null) { return; } 333 | 334 | var exporter = new THREE.OBJExporter(); 335 | var data = exporter.parse(object); 336 | var filename = 'model.obj'; 337 | 338 | var blob = new Blob([data], {type: 'text/csv'}); 339 | if(window.navigator.msSaveOrOpenBlob) { 340 | window.navigator.msSaveBlob(blob, filename); 341 | } 342 | else{ 343 | var elem = window.document.createElement('a'); 344 | elem.href = window.URL.createObjectURL(blob); 345 | elem.download = filename; 346 | document.body.appendChild(elem); 347 | elem.click(); 348 | document.body.removeChild(elem); 349 | } 350 | } -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | AI Generative Modelling 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | 57 | 58 | 59 | 60 |
61 | 62 | 63 | 64 | 67 | 68 | 71 | 72 | 73 |
74 | 75 | 142 | 143 |
144 |

download this model

145 |
146 | 147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /templates/playful_ai.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Playful AI 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 29 | 30 | 33 | 34 | 35 |
36 | 37 | 93 | 94 |
95 |

メッシュをダウンロード

96 |
97 | 98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 212 | 213 | 214 | --------------------------------------------------------------------------------