tremble %!s(int64=6) %!d(string=hai) anos
pai
achega
0fe319293a
Modificáronse 21 ficheiros con 56057 adicións e 0 borrados
  1. 4 0
      assets/jquery.min.js
  2. 8 0
      assets/jquery.mousewheel.min.js
  3. 1 0
      assets/numbers.min.js
  4. 90 0
      css/index.css
  5. 33 0
      index.html
  6. 1163 0
      js/CanvasRenderer.js
  7. 568 0
      js/MTLLoader.js
  8. 793 0
      js/OBJLoader.js
  9. 1042 0
      js/OrbitControls.js
  10. 38 0
      js/SceneUtils.js
  11. 486 0
      js/THREE.MeshLine.js
  12. 627 0
      js/d3_threeD.js
  13. 14 0
      js/dat.gui.min.js
  14. 138 0
      js/main.js
  15. 5 0
      js/stats.min.js
  16. 47349 0
      js/three.js
  17. 892 0
      js/three.min.js
  18. 519 0
      js/threeBSP.js
  19. 1289 0
      js/webgl-utils.js
  20. 384 0
      model/data.js
  21. 614 0
      utils/utils.js

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 4 - 0
assets/jquery.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 8 - 0
assets/jquery.mousewheel.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
assets/numbers.min.js


+ 90 - 0
css/index.css

@@ -0,0 +1,90 @@
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	font-size: 100%;
+	font: inherit;
+	vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section {
+	display: block;
+}
+body {
+	line-height: 1;
+}
+ol, ul {
+	list-style: none;
+}
+blockquote, q {
+	quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}
+html,body{
+  width: 100%;
+  height: 100%;
+}
+.body{
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+.left{
+  width: 50%;
+  height: 100%;
+  float: left;
+  position: relative;
+}
+.left #myCanvas{
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%)
+}
+.right{
+  width: 50%;
+  height: 100%;
+  background: #ccc;
+  float: right;
+}
+.clear{
+  clear: both;
+}
+
+#divide{
+  background: #ccc;
+  display: inline-block;
+  width: 100px;
+  height: 30px;
+  line-height: 30px;
+  text-align: center;
+  cursor: pointer;
+  user-select: none;
+  display: none;
+}
+.active{
+  background: #000!important;
+  color: #fff;
+}

+ 33 - 0
index.html

@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <title>Page Title</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <script src="assets/jquery.min.js"></script>
+    <script src="assets/jquery.mousewheel.min.js"></script>
+    <script src="model/data.js"></script>
+    <script src="utils/utils.js"></script>
+    <script src="js/main.js"></script>
+    <link rel="stylesheet" href="./css/index.css" />
+  </head>
+  <body>
+    <div class="body">
+      <div class="left">
+        <div id="divide">分隔线段</div>
+        <canvas
+          id="myCanvas"
+          width="800"
+          height="800"
+          style="border:1px solid #d3d3d3;background:#ffffff;"
+        >
+          Your browser does not support the HTML5 canvas tag.
+        </canvas>
+      </div>
+      <div id="geo-cav" class="right">
+      </div>
+      <div class="clear"></div>
+    </div>
+  </body>
+</html>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1163 - 0
js/CanvasRenderer.js


+ 568 - 0
js/MTLLoader.js

@@ -0,0 +1,568 @@
+/**
+ * Loads a Wavefront .mtl file specifying materials
+ *
+ * @author angelxuanchang
+ */
+
+THREE.MTLLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.MTLLoader.prototype = {
+
+	constructor: THREE.MTLLoader,
+
+	/**
+	 * Loads and parses a MTL asset from a URL.
+	 *
+	 * @param {String} url - URL to the MTL file.
+	 * @param {Function} [onLoad] - Callback invoked with the loaded object.
+	 * @param {Function} [onProgress] - Callback for download progress.
+	 * @param {Function} [onError] - Callback for download errors.
+	 *
+	 * @see setPath setResourcePath
+	 *
+	 * @note In order for relative texture references to resolve correctly
+	 * you must call setResourcePath() explicitly prior to load.
+	 */
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var path = ( this.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : this.path;
+
+		var loader = new THREE.FileLoader( this.manager );
+		loader.setPath( this.path );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( text, path ) );
+
+		}, onProgress, onError );
+
+	},
+
+	/**
+	 * Set base path for resolving references.
+	 * If set this path will be prepended to each loaded and found reference.
+	 *
+	 * @see setResourcePath
+	 * @param {String} path
+	 * @return {THREE.MTLLoader}
+	 *
+	 * @example
+	 *     mtlLoader.setPath( 'assets/obj/' );
+	 *     mtlLoader.load( 'my.mtl', ... );
+	 */
+	setPath: function ( path ) {
+
+		this.path = path;
+		return this;
+
+	},
+
+	/**
+	 * Set base path for additional resources like textures.
+	 *
+	 * @see setPath
+	 * @param {String} path
+	 * @return {THREE.MTLLoader}
+	 *
+	 * @example
+	 *     mtlLoader.setPath( 'assets/obj/' );
+	 *     mtlLoader.setResourcePath( 'assets/textures/' );
+	 *     mtlLoader.load( 'my.mtl', ... );
+	 */
+	setResourcePath: function ( path ) {
+
+		this.resourcePath = path;
+		return this;
+
+	},
+
+	setTexturePath: function ( path ) {
+
+		console.warn( 'THREE.MTLLoader: .setTexturePath() has been renamed to .setResourcePath().' );
+		return this.setResourcePath( path );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+		return this;
+
+	},
+
+	setMaterialOptions: function ( value ) {
+
+		this.materialOptions = value;
+		return this;
+
+	},
+
+	/**
+	 * Parses a MTL file.
+	 *
+	 * @param {String} text - Content of MTL file
+	 * @return {THREE.MTLLoader.MaterialCreator}
+	 *
+	 * @see setPath setResourcePath
+	 *
+	 * @note In order for relative texture references to resolve correctly
+	 * you must call setResourcePath() explicitly prior to parse.
+	 */
+	parse: function ( text, path ) {
+
+		var lines = text.split( '\n' );
+		var info = {};
+		var delimiter_pattern = /\s+/;
+		var materialsInfo = {};
+
+		for ( var i = 0; i < lines.length; i ++ ) {
+
+			var line = lines[ i ];
+			line = line.trim();
+
+			if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
+
+				// Blank line or comment ignore
+				continue;
+
+			}
+
+			var pos = line.indexOf( ' ' );
+
+			var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
+			key = key.toLowerCase();
+
+			var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
+			value = value.trim();
+
+			if ( key === 'newmtl' ) {
+
+				// New material
+
+				info = { name: value };
+				materialsInfo[ value ] = info;
+
+			} else {
+
+				if ( key === 'ka' || key === 'kd' || key === 'ks' ) {
+
+					var ss = value.split( delimiter_pattern, 3 );
+					info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
+
+				} else {
+
+					info[ key ] = value;
+
+				}
+
+			}
+
+		}
+
+		var materialCreator = new THREE.MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions );
+		materialCreator.setCrossOrigin( this.crossOrigin );
+		materialCreator.setManager( this.manager );
+		materialCreator.setMaterials( materialsInfo );
+		return materialCreator;
+
+	}
+
+};
+
+/**
+ * Create a new THREE-MTLLoader.MaterialCreator
+ * @param baseUrl - Url relative to which textures are loaded
+ * @param options - Set of options on how to construct the materials
+ *                  side: Which side to apply the material
+ *                        THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
+ *                  wrap: What type of wrapping to apply for textures
+ *                        THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
+ *                  normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
+ *                                Default: false, assumed to be already normalized
+ *                  ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
+ *                                  Default: false
+ * @constructor
+ */
+
+THREE.MTLLoader.MaterialCreator = function ( baseUrl, options ) {
+
+	this.baseUrl = baseUrl || '';
+	this.options = options;
+	this.materialsInfo = {};
+	this.materials = {};
+	this.materialsArray = [];
+	this.nameLookup = {};
+
+	this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
+	this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
+
+};
+
+THREE.MTLLoader.MaterialCreator.prototype = {
+
+	constructor: THREE.MTLLoader.MaterialCreator,
+
+	crossOrigin: 'anonymous',
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+		return this;
+
+	},
+
+	setManager: function ( value ) {
+
+		this.manager = value;
+
+	},
+
+	setMaterials: function ( materialsInfo ) {
+
+		this.materialsInfo = this.convert( materialsInfo );
+		this.materials = {};
+		this.materialsArray = [];
+		this.nameLookup = {};
+
+	},
+
+	convert: function ( materialsInfo ) {
+
+		if ( ! this.options ) return materialsInfo;
+
+		var converted = {};
+
+		for ( var mn in materialsInfo ) {
+
+			// Convert materials info into normalized form based on options
+
+			var mat = materialsInfo[ mn ];
+
+			var covmat = {};
+
+			converted[ mn ] = covmat;
+
+			for ( var prop in mat ) {
+
+				var save = true;
+				var value = mat[ prop ];
+				var lprop = prop.toLowerCase();
+
+				switch ( lprop ) {
+
+					case 'kd':
+					case 'ka':
+					case 'ks':
+
+						// Diffuse color (color under white light) using RGB values
+
+						if ( this.options && this.options.normalizeRGB ) {
+
+							value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
+
+						}
+
+						if ( this.options && this.options.ignoreZeroRGBs ) {
+
+							if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
+
+								// ignore
+
+								save = false;
+
+							}
+
+						}
+
+						break;
+
+					default:
+
+						break;
+
+				}
+
+				if ( save ) {
+
+					covmat[ lprop ] = value;
+
+				}
+
+			}
+
+		}
+
+		return converted;
+
+	},
+
+	preload: function () {
+
+		for ( var mn in this.materialsInfo ) {
+
+			this.create( mn );
+
+		}
+
+	},
+
+	getIndex: function ( materialName ) {
+
+		return this.nameLookup[ materialName ];
+
+	},
+
+	getAsArray: function () {
+
+		var index = 0;
+
+		for ( var mn in this.materialsInfo ) {
+
+			this.materialsArray[ index ] = this.create( mn );
+			this.nameLookup[ mn ] = index;
+			index ++;
+
+		}
+
+		return this.materialsArray;
+
+	},
+
+	create: function ( materialName ) {
+
+		if ( this.materials[ materialName ] === undefined ) {
+
+			this.createMaterial_( materialName );
+
+		}
+
+		return this.materials[ materialName ];
+
+	},
+
+	createMaterial_: function ( materialName ) {
+
+		// Create material
+
+		var scope = this;
+		var mat = this.materialsInfo[ materialName ];
+		var params = {
+
+			name: materialName,
+			side: this.side
+
+		};
+
+		function resolveURL( baseUrl, url ) {
+
+			if ( typeof url !== 'string' || url === '' )
+				return '';
+
+			// Absolute URL
+			if ( /^https?:\/\//i.test( url ) ) return url;
+
+			return baseUrl + url;
+
+		}
+
+		function setMapForType( mapType, value ) {
+
+			if ( params[ mapType ] ) return; // Keep the first encountered texture
+
+			var texParams = scope.getTextureParams( value, params );
+			var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
+
+			map.repeat.copy( texParams.scale );
+			map.offset.copy( texParams.offset );
+
+			map.wrapS = scope.wrap;
+			map.wrapT = scope.wrap;
+
+			params[ mapType ] = map;
+
+		}
+
+		for ( var prop in mat ) {
+
+			var value = mat[ prop ];
+			var n;
+
+			if ( value === '' ) continue;
+
+			switch ( prop.toLowerCase() ) {
+
+				// Ns is material specular exponent
+
+				case 'kd':
+
+					// Diffuse color (color under white light) using RGB values
+
+					params.color = new THREE.Color().fromArray( value );
+
+					break;
+
+				case 'ks':
+
+					// Specular color (color when light is reflected from shiny surface) using RGB values
+					params.specular = new THREE.Color().fromArray( value );
+
+					break;
+
+				case 'map_kd':
+
+					// Diffuse texture map
+
+					setMapForType( "map", value );
+
+					break;
+
+				case 'map_ks':
+
+					// Specular map
+
+					setMapForType( "specularMap", value );
+
+					break;
+
+				case 'norm':
+
+					setMapForType( "normalMap", value );
+
+					break;
+
+				case 'map_bump':
+				case 'bump':
+
+					// Bump texture map
+
+					setMapForType( "bumpMap", value );
+
+					break;
+
+				case 'map_d':
+
+					// Alpha map
+
+					setMapForType( "alphaMap", value );
+					params.transparent = true;
+
+					break;
+
+				case 'ns':
+
+					// The specular exponent (defines the focus of the specular highlight)
+					// A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
+
+					params.shininess = parseFloat( value );
+
+					break;
+
+				case 'd':
+					n = parseFloat( value );
+
+					if ( n < 1 ) {
+
+						params.opacity = n;
+						params.transparent = true;
+
+					}
+
+					break;
+
+				case 'tr':
+					n = parseFloat( value );
+
+					if ( this.options && this.options.invertTrProperty ) n = 1 - n;
+
+					if ( n > 0 ) {
+
+						params.opacity = 1 - n;
+						params.transparent = true;
+
+					}
+
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
+		return this.materials[ materialName ];
+
+	},
+
+	getTextureParams: function ( value, matParams ) {
+
+		var texParams = {
+
+			scale: new THREE.Vector2( 1, 1 ),
+			offset: new THREE.Vector2( 0, 0 )
+
+		 };
+
+		var items = value.split( /\s+/ );
+		var pos;
+
+		pos = items.indexOf( '-bm' );
+
+		if ( pos >= 0 ) {
+
+			matParams.bumpScale = parseFloat( items[ pos + 1 ] );
+			items.splice( pos, 2 );
+
+		}
+
+		pos = items.indexOf( '-s' );
+
+		if ( pos >= 0 ) {
+
+			texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
+			items.splice( pos, 4 ); // we expect 3 parameters here!
+
+		}
+
+		pos = items.indexOf( '-o' );
+
+		if ( pos >= 0 ) {
+
+			texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
+			items.splice( pos, 4 ); // we expect 3 parameters here!
+
+		}
+
+		texParams.url = items.join( ' ' ).trim();
+		return texParams;
+
+	},
+
+	loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
+
+		var texture;
+		var loader = THREE.Loader.Handlers.get( url );
+		var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager;
+
+		if ( loader === null ) {
+
+			loader = new THREE.TextureLoader( manager );
+
+		}
+
+		if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
+		texture = loader.load( url, onLoad, onProgress, onError );
+
+		if ( mapping !== undefined ) texture.mapping = mapping;
+
+		return texture;
+
+	}
+
+};

+ 793 - 0
js/OBJLoader.js

@@ -0,0 +1,793 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.OBJLoader = ( function () {
+
+	// o object_name | g group_name
+	var object_pattern = /^[og]\s*(.+)?/;
+	// mtllib file_reference
+	var material_library_pattern = /^mtllib /;
+	// usemtl material_name
+	var material_use_pattern = /^usemtl /;
+
+	function ParserState() {
+
+		var state = {
+			objects: [],
+			object: {},
+
+			vertices: [],
+			normals: [],
+			colors: [],
+			uvs: [],
+
+			materialLibraries: [],
+
+			startObject: function ( name, fromDeclaration ) {
+
+				// If the current object (initial from reset) is not from a g/o declaration in the parsed
+				// file. We need to use it for the first parsed g/o to keep things in sync.
+				if ( this.object && this.object.fromDeclaration === false ) {
+
+					this.object.name = name;
+					this.object.fromDeclaration = ( fromDeclaration !== false );
+					return;
+
+				}
+
+				var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
+
+				if ( this.object && typeof this.object._finalize === 'function' ) {
+
+					this.object._finalize( true );
+
+				}
+
+				this.object = {
+					name: name || '',
+					fromDeclaration: ( fromDeclaration !== false ),
+
+					geometry: {
+						vertices: [],
+						normals: [],
+						colors: [],
+						uvs: []
+					},
+					materials: [],
+					smooth: true,
+
+					startMaterial: function ( name, libraries ) {
+
+						var previous = this._finalize( false );
+
+						// New usemtl declaration overwrites an inherited material, except if faces were declared
+						// after the material, then it must be preserved for proper MultiMaterial continuation.
+						if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
+
+							this.materials.splice( previous.index, 1 );
+
+						}
+
+						var material = {
+							index: this.materials.length,
+							name: name || '',
+							mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
+							smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
+							groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
+							groupEnd: - 1,
+							groupCount: - 1,
+							inherited: false,
+
+							clone: function ( index ) {
+
+								var cloned = {
+									index: ( typeof index === 'number' ? index : this.index ),
+									name: this.name,
+									mtllib: this.mtllib,
+									smooth: this.smooth,
+									groupStart: 0,
+									groupEnd: - 1,
+									groupCount: - 1,
+									inherited: false
+								};
+								cloned.clone = this.clone.bind( cloned );
+								return cloned;
+
+							}
+						};
+
+						this.materials.push( material );
+
+						return material;
+
+					},
+
+					currentMaterial: function () {
+
+						if ( this.materials.length > 0 ) {
+
+							return this.materials[ this.materials.length - 1 ];
+
+						}
+
+						return undefined;
+
+					},
+
+					_finalize: function ( end ) {
+
+						var lastMultiMaterial = this.currentMaterial();
+						if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
+
+							lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
+							lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
+							lastMultiMaterial.inherited = false;
+
+						}
+
+						// Ignore objects tail materials if no face declarations followed them before a new o/g started.
+						if ( end && this.materials.length > 1 ) {
+
+							for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) {
+
+								if ( this.materials[ mi ].groupCount <= 0 ) {
+
+									this.materials.splice( mi, 1 );
+
+								}
+
+							}
+
+						}
+
+						// Guarantee at least one empty material, this makes the creation later more straight forward.
+						if ( end && this.materials.length === 0 ) {
+
+							this.materials.push( {
+								name: '',
+								smooth: this.smooth
+							} );
+
+						}
+
+						return lastMultiMaterial;
+
+					}
+				};
+
+				// Inherit previous objects material.
+				// Spec tells us that a declared material must be set to all objects until a new material is declared.
+				// If a usemtl declaration is encountered while this new object is being parsed, it will
+				// overwrite the inherited material. Exception being that there was already face declarations
+				// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
+
+				if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
+
+					var declared = previousMaterial.clone( 0 );
+					declared.inherited = true;
+					this.object.materials.push( declared );
+
+				}
+
+				this.objects.push( this.object );
+
+			},
+
+			finalize: function () {
+
+				if ( this.object && typeof this.object._finalize === 'function' ) {
+
+					this.object._finalize( true );
+
+				}
+
+			},
+
+			parseVertexIndex: function ( value, len ) {
+
+				var index = parseInt( value, 10 );
+				return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
+
+			},
+
+			parseNormalIndex: function ( value, len ) {
+
+				var index = parseInt( value, 10 );
+				return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
+
+			},
+
+			parseUVIndex: function ( value, len ) {
+
+				var index = parseInt( value, 10 );
+				return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
+
+			},
+
+			addVertex: function ( a, b, c ) {
+
+				var src = this.vertices;
+				var dst = this.object.geometry.vertices;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
+
+			},
+
+			addVertexPoint: function ( a ) {
+
+				var src = this.vertices;
+				var dst = this.object.geometry.vertices;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+
+			},
+
+			addVertexLine: function ( a ) {
+
+				var src = this.vertices;
+				var dst = this.object.geometry.vertices;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+
+			},
+
+			addNormal: function ( a, b, c ) {
+
+				var src = this.normals;
+				var dst = this.object.geometry.normals;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
+
+			},
+
+			addColor: function ( a, b, c ) {
+
+				var src = this.colors;
+				var dst = this.object.geometry.colors;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
+
+			},
+
+			addUV: function ( a, b, c ) {
+
+				var src = this.uvs;
+				var dst = this.object.geometry.uvs;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ] );
+
+			},
+
+			addUVLine: function ( a ) {
+
+				var src = this.uvs;
+				var dst = this.object.geometry.uvs;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ] );
+
+			},
+
+			addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
+
+				var vLen = this.vertices.length;
+
+				var ia = this.parseVertexIndex( a, vLen );
+				var ib = this.parseVertexIndex( b, vLen );
+				var ic = this.parseVertexIndex( c, vLen );
+
+				this.addVertex( ia, ib, ic );
+
+				if ( ua !== undefined && ua !== '' ) {
+
+					var uvLen = this.uvs.length;
+					ia = this.parseUVIndex( ua, uvLen );
+					ib = this.parseUVIndex( ub, uvLen );
+					ic = this.parseUVIndex( uc, uvLen );
+					this.addUV( ia, ib, ic );
+
+				}
+
+				if ( na !== undefined && na !== '' ) {
+
+					// Normals are many times the same. If so, skip function call and parseInt.
+					var nLen = this.normals.length;
+					ia = this.parseNormalIndex( na, nLen );
+
+					ib = na === nb ? ia : this.parseNormalIndex( nb, nLen );
+					ic = na === nc ? ia : this.parseNormalIndex( nc, nLen );
+
+					this.addNormal( ia, ib, ic );
+
+				}
+
+				if ( this.colors.length > 0 ) {
+
+					this.addColor( ia, ib, ic );
+
+				}
+
+			},
+
+			addPointGeometry: function ( vertices ) {
+
+				this.object.geometry.type = 'Points';
+
+				var vLen = this.vertices.length;
+
+				for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
+
+					this.addVertexPoint( this.parseVertexIndex( vertices[ vi ], vLen ) );
+
+				}
+
+			},
+
+			addLineGeometry: function ( vertices, uvs ) {
+
+				this.object.geometry.type = 'Line';
+
+				var vLen = this.vertices.length;
+				var uvLen = this.uvs.length;
+
+				for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
+
+					this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
+
+				}
+
+				for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
+
+					this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
+
+				}
+
+			}
+
+		};
+
+		state.startObject( '', false );
+
+		return state;
+
+	}
+
+	//
+
+	function OBJLoader( manager ) {
+
+		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+		this.materials = null;
+
+	}
+
+	OBJLoader.prototype = {
+
+		constructor: OBJLoader,
+
+		load: function ( url, onLoad, onProgress, onError ) {
+
+			var scope = this;
+
+			var loader = new THREE.FileLoader( scope.manager );
+			loader.setPath( this.path );
+			loader.load( url, function ( text ) {
+
+				onLoad( scope.parse( text ) );
+
+			}, onProgress, onError );
+
+		},
+
+		setPath: function ( value ) {
+
+			this.path = value;
+
+			return this;
+
+		},
+
+		setMaterials: function ( materials ) {
+
+			this.materials = materials;
+
+			return this;
+
+		},
+
+		parse: function ( text ) {
+
+			console.time( 'OBJLoader' );
+
+			var state = new ParserState();
+
+			if ( text.indexOf( '\r\n' ) !== - 1 ) {
+
+				// This is faster than String.split with regex that splits on both
+				text = text.replace( /\r\n/g, '\n' );
+
+			}
+
+			if ( text.indexOf( '\\\n' ) !== - 1 ) {
+
+				// join lines separated by a line continuation character (\)
+				text = text.replace( /\\\n/g, '' );
+
+			}
+
+			var lines = text.split( '\n' );
+			var line = '', lineFirstChar = '';
+			var lineLength = 0;
+			var result = [];
+
+			// Faster to just trim left side of the line. Use if available.
+			var trimLeft = ( typeof ''.trimLeft === 'function' );
+
+			for ( var i = 0, l = lines.length; i < l; i ++ ) {
+
+				line = lines[ i ];
+
+				line = trimLeft ? line.trimLeft() : line.trim();
+
+				lineLength = line.length;
+
+				if ( lineLength === 0 ) continue;
+
+				lineFirstChar = line.charAt( 0 );
+
+				// @todo invoke passed in handler if any
+				if ( lineFirstChar === '#' ) continue;
+
+				if ( lineFirstChar === 'v' ) {
+
+					var data = line.split( /\s+/ );
+
+					switch ( data[ 0 ] ) {
+
+						case 'v':
+							state.vertices.push(
+								parseFloat( data[ 1 ] ),
+								parseFloat( data[ 2 ] ),
+								parseFloat( data[ 3 ] )
+							);
+							if ( data.length === 8 ) {
+
+								state.colors.push(
+									parseFloat( data[ 4 ] ),
+									parseFloat( data[ 5 ] ),
+									parseFloat( data[ 6 ] )
+
+								);
+
+							}
+							break;
+						case 'vn':
+							state.normals.push(
+								parseFloat( data[ 1 ] ),
+								parseFloat( data[ 2 ] ),
+								parseFloat( data[ 3 ] )
+							);
+							break;
+						case 'vt':
+							state.uvs.push(
+								parseFloat( data[ 1 ] ),
+								parseFloat( data[ 2 ] )
+							);
+							break;
+
+					}
+
+				} else if ( lineFirstChar === 'f' ) {
+
+					var lineData = line.substr( 1 ).trim();
+					var vertexData = lineData.split( /\s+/ );
+					var faceVertices = [];
+
+					// Parse the face vertex data into an easy to work with format
+
+					for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
+
+						var vertex = vertexData[ j ];
+
+						if ( vertex.length > 0 ) {
+
+							var vertexParts = vertex.split( '/' );
+							faceVertices.push( vertexParts );
+
+						}
+
+					}
+
+					// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
+
+					var v1 = faceVertices[ 0 ];
+
+					for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
+
+						var v2 = faceVertices[ j ];
+						var v3 = faceVertices[ j + 1 ];
+
+						state.addFace(
+							v1[ 0 ], v2[ 0 ], v3[ 0 ],
+							v1[ 1 ], v2[ 1 ], v3[ 1 ],
+							v1[ 2 ], v2[ 2 ], v3[ 2 ]
+						);
+
+					}
+
+				} else if ( lineFirstChar === 'l' ) {
+
+					var lineParts = line.substring( 1 ).trim().split( " " );
+					var lineVertices = [], lineUVs = [];
+
+					if ( line.indexOf( "/" ) === - 1 ) {
+
+						lineVertices = lineParts;
+
+					} else {
+
+						for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
+
+							var parts = lineParts[ li ].split( "/" );
+
+							if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
+							if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
+
+						}
+
+					}
+					state.addLineGeometry( lineVertices, lineUVs );
+
+				} else if ( lineFirstChar === 'p' ) {
+
+					var lineData = line.substr( 1 ).trim();
+					var pointData = lineData.split( " " );
+
+					state.addPointGeometry( pointData );
+
+				} else if ( ( result = object_pattern.exec( line ) ) !== null ) {
+
+					// o object_name
+					// or
+					// g group_name
+
+					// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
+					// var name = result[ 0 ].substr( 1 ).trim();
+					var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
+
+					state.startObject( name );
+
+				} else if ( material_use_pattern.test( line ) ) {
+
+					// material
+
+					state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
+
+				} else if ( material_library_pattern.test( line ) ) {
+
+					// mtl file
+
+					state.materialLibraries.push( line.substring( 7 ).trim() );
+
+				} else if ( lineFirstChar === 's' ) {
+
+					result = line.split( ' ' );
+
+					// smooth shading
+
+					// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
+					// but does not define a usemtl for each face set.
+					// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
+					// This requires some care to not create extra material on each smooth value for "normal" obj files.
+					// where explicit usemtl defines geometry groups.
+					// Example asset: examples/models/obj/cerberus/Cerberus.obj
+
+					/*
+					 * http://paulbourke.net/dataformats/obj/
+					 * or
+					 * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
+					 *
+					 * From chapter "Grouping" Syntax explanation "s group_number":
+					 * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
+					 * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
+					 * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
+					 * than 0."
+					 */
+					if ( result.length > 1 ) {
+
+						var value = result[ 1 ].trim().toLowerCase();
+						state.object.smooth = ( value !== '0' && value !== 'off' );
+
+					} else {
+
+						// ZBrush can produce "s" lines #11707
+						state.object.smooth = true;
+
+					}
+					var material = state.object.currentMaterial();
+					if ( material ) material.smooth = state.object.smooth;
+
+				} else {
+
+					// Handle null terminated files without exception
+					if ( line === '\0' ) continue;
+
+					throw new Error( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
+
+				}
+
+			}
+
+			state.finalize();
+
+			var container = new THREE.Group();
+			container.materialLibraries = [].concat( state.materialLibraries );
+
+			for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
+
+				var object = state.objects[ i ];
+				var geometry = object.geometry;
+				var materials = object.materials;
+				var isLine = ( geometry.type === 'Line' );
+				var isPoints = ( geometry.type === 'Points' );
+				var hasVertexColors = false;
+
+				// Skip o/g line declarations that did not follow with any faces
+				if ( geometry.vertices.length === 0 ) continue;
+
+				var buffergeometry = new THREE.BufferGeometry();
+
+				buffergeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
+
+				if ( geometry.normals.length > 0 ) {
+
+					buffergeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
+
+				} else {
+
+					buffergeometry.computeVertexNormals();
+
+				}
+
+				if ( geometry.colors.length > 0 ) {
+
+					hasVertexColors = true;
+					buffergeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
+
+				}
+
+				if ( geometry.uvs.length > 0 ) {
+
+					buffergeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
+
+				}
+
+				// Create materials
+
+				var createdMaterials = [];
+
+				for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+
+					var sourceMaterial = materials[ mi ];
+					var material = undefined;
+
+					if ( this.materials !== null ) {
+
+						material = this.materials.create( sourceMaterial.name );
+
+						// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
+						if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
+
+							var materialLine = new THREE.LineBasicMaterial();
+							materialLine.copy( material );
+							materialLine.lights = false; // TOFIX
+							material = materialLine;
+
+						} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
+
+							var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
+							materialLine.copy( material );
+							material = materialPoints;
+
+						}
+
+					}
+
+					if ( ! material ) {
+
+						if ( isLine ) {
+
+							material = new THREE.LineBasicMaterial();
+
+						} else if ( isPoints ) {
+
+							material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
+
+						} else {
+
+							material = new THREE.MeshPhongMaterial();
+
+						}
+
+						material.name = sourceMaterial.name;
+
+					}
+
+					material.flatShading = sourceMaterial.smooth ? false : true;
+					material.vertexColors = hasVertexColors ? THREE.VertexColors : THREE.NoColors;
+
+					createdMaterials.push( material );
+
+				}
+
+				// Create mesh
+
+				var mesh;
+
+				if ( createdMaterials.length > 1 ) {
+
+					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+
+						var sourceMaterial = materials[ mi ];
+						buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
+
+					}
+
+					if ( isLine ) {
+
+						mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
+
+					} else if ( isPoints ) {
+
+						mesh = new THREE.Points( buffergeometry, createdMaterials );
+
+					} else {
+
+						mesh = new THREE.Mesh( buffergeometry, createdMaterials );
+
+					}
+
+				} else {
+
+					if ( isLine ) {
+
+						mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
+
+					} else if ( isPoints ) {
+
+						mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
+
+					} else {
+
+						mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
+
+					}
+
+				}
+
+				mesh.name = object.name;
+
+				container.add( mesh );
+
+			}
+
+			console.timeEnd( 'OBJLoader' );
+
+			return container;
+
+		}
+
+	};
+
+	return OBJLoader;
+
+} )();

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1042 - 0
js/OrbitControls.js


+ 38 - 0
js/SceneUtils.js

@@ -0,0 +1,38 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SceneUtils = {
+
+	createMultiMaterialObject: function ( geometry, materials ) {
+
+		var group = new THREE.Group();
+
+		for ( var i = 0, l = materials.length; i < l; i ++ ) {
+
+			group.add( new THREE.Mesh( geometry, materials[ i ] ) );
+
+		}
+
+		return group;
+
+	},
+
+	detach: function ( child, parent, scene ) {
+
+		child.applyMatrix( parent.matrixWorld );
+		parent.remove( child );
+		scene.add( child );
+
+	},
+
+	attach: function ( child, scene, parent ) {
+
+		child.applyMatrix( new THREE.Matrix4().getInverse( parent.matrixWorld ) );
+
+		scene.remove( child );
+		parent.add( child );
+
+	}
+
+};

+ 486 - 0
js/THREE.MeshLine.js

@@ -0,0 +1,486 @@
+;(function() {
+
+"use strict";
+
+var root = this
+
+var has_require = typeof require !== 'undefined'
+
+var THREE = root.THREE || has_require && require('three')
+if( !THREE )
+	throw new Error( 'MeshLine requires three.js' )
+
+function MeshLine() {
+
+	this.positions = [];
+
+	this.previous = [];
+	this.next = [];
+	this.side = [];
+	this.width = [];
+	this.indices_array = [];
+	this.uvs = [];
+	this.counters = [];
+	this.geometry = new THREE.BufferGeometry();
+
+	this.widthCallback = null;
+
+}
+
+MeshLine.prototype.setGeometry = function( g, c ) {
+
+	this.widthCallback = c;
+
+	this.positions = [];
+	this.counters = [];
+
+	if( g instanceof THREE.Geometry ) {
+		for( var j = 0; j < g.vertices.length; j++ ) {
+			var v = g.vertices[ j ];
+			var c = j/g.vertices.length;
+			this.positions.push( v.x, v.y, v.z );
+			this.positions.push( v.x, v.y, v.z );
+			this.counters.push(c);
+			this.counters.push(c);
+		}
+	}
+
+	if( g instanceof THREE.BufferGeometry ) {
+		// read attribute positions ?
+	}
+
+	if( g instanceof Float32Array || g instanceof Array ) {
+		for( var j = 0; j < g.length; j += 3 ) {
+			var c = j/g.length;
+			this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
+			this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
+			this.counters.push(c);
+			this.counters.push(c);
+		}
+	}
+
+	this.process();
+
+}
+
+MeshLine.prototype.compareV3 = function( a, b ) {
+
+	var aa = a * 6;
+	var ab = b * 6;
+	return ( this.positions[ aa ] === this.positions[ ab ] ) && ( this.positions[ aa + 1 ] === this.positions[ ab + 1 ] ) && ( this.positions[ aa + 2 ] === this.positions[ ab + 2 ] );
+
+}
+
+MeshLine.prototype.copyV3 = function( a ) {
+
+	var aa = a * 6;
+	return [ this.positions[ aa ], this.positions[ aa + 1 ], this.positions[ aa + 2 ] ];
+
+}
+
+MeshLine.prototype.process = function() {
+
+	var l = this.positions.length / 6;
+
+	this.previous = [];
+	this.next = [];
+	this.side = [];
+	this.width = [];
+	this.indices_array = [];
+	this.uvs = [];
+
+	for( var j = 0; j < l; j++ ) {
+		this.side.push( 1 );
+		this.side.push( -1 );
+	}
+
+	var w;
+	for( var j = 0; j < l; j++ ) {
+		if( this.widthCallback ) w = this.widthCallback( j / ( l -1 ) );
+		else w = 1;
+		this.width.push( w );
+		this.width.push( w );
+	}
+
+	for( var j = 0; j < l; j++ ) {
+		this.uvs.push( j / ( l - 1 ), 0 );
+		this.uvs.push( j / ( l - 1 ), 1 );
+	}
+
+	var v;
+
+	if( this.compareV3( 0, l - 1 ) ){
+		v = this.copyV3( l - 2 );
+	} else {
+		v = this.copyV3( 0 );
+	}
+	this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+	this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+	for( var j = 0; j < l - 1; j++ ) {
+		v = this.copyV3( j );
+		this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+		this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+	}
+
+	for( var j = 1; j < l; j++ ) {
+		v = this.copyV3( j );
+		this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+		this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+	}
+
+	if( this.compareV3( l - 1, 0 ) ){
+		v = this.copyV3( 1 );
+	} else {
+		v = this.copyV3( l - 1 );
+	}
+	this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+	this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
+
+	for( var j = 0; j < l - 1; j++ ) {
+		var n = j * 2;
+		this.indices_array.push( n, n + 1, n + 2 );
+		this.indices_array.push( n + 2, n + 1, n + 3 );
+	}
+
+	if (!this.attributes) {
+		this.attributes = {
+			position: new THREE.BufferAttribute( new Float32Array( this.positions ), 3 ),
+			previous: new THREE.BufferAttribute( new Float32Array( this.previous ), 3 ),
+			next: new THREE.BufferAttribute( new Float32Array( this.next ), 3 ),
+			side: new THREE.BufferAttribute( new Float32Array( this.side ), 1 ),
+			width: new THREE.BufferAttribute( new Float32Array( this.width ), 1 ),
+			uv: new THREE.BufferAttribute( new Float32Array( this.uvs ), 2 ),
+			index: new THREE.BufferAttribute( new Uint16Array( this.indices_array ), 1 ),
+			counters: new THREE.BufferAttribute( new Float32Array( this.counters ), 1 )
+		}
+	} else {
+		this.attributes.position.copyArray(new Float32Array(this.positions));
+		this.attributes.position.needsUpdate = true;
+		this.attributes.previous.copyArray(new Float32Array(this.previous));
+		this.attributes.previous.needsUpdate = true;
+		this.attributes.next.copyArray(new Float32Array(this.next));
+		this.attributes.next.needsUpdate = true;
+		this.attributes.side.copyArray(new Float32Array(this.side));
+		this.attributes.side.needsUpdate = true;
+		this.attributes.width.copyArray(new Float32Array(this.width));
+		this.attributes.width.needsUpdate = true;
+		this.attributes.uv.copyArray(new Float32Array(this.uvs));
+		this.attributes.uv.needsUpdate = true;
+		this.attributes.index.copyArray(new Uint16Array(this.indices_array));
+		this.attributes.index.needsUpdate = true;
+    }
+
+	this.geometry.addAttribute( 'position', this.attributes.position );
+	this.geometry.addAttribute( 'previous', this.attributes.previous );
+	this.geometry.addAttribute( 'next', this.attributes.next );
+	this.geometry.addAttribute( 'side', this.attributes.side );
+	this.geometry.addAttribute( 'width', this.attributes.width );
+	this.geometry.addAttribute( 'uv', this.attributes.uv );
+	this.geometry.addAttribute( 'counters', this.attributes.counters );
+
+	this.geometry.setIndex( this.attributes.index );
+
+}
+
+function memcpy (src, srcOffset, dst, dstOffset, length) {
+	var i
+
+	src = src.subarray || src.slice ? src : src.buffer
+	dst = dst.subarray || dst.slice ? dst : dst.buffer
+
+	src = srcOffset ? src.subarray ?
+	src.subarray(srcOffset, length && srcOffset + length) :
+	src.slice(srcOffset, length && srcOffset + length) : src
+
+	if (dst.set) {
+		dst.set(src, dstOffset)
+	} else {
+		for (i=0; i<src.length; i++) {
+			dst[i + dstOffset] = src[i]
+		}
+	}
+
+	return dst
+}
+
+/**
+ * Fast method to advance the line by one position.  The oldest position is removed.
+ * @param position
+ */
+MeshLine.prototype.advance = function(position) {
+
+	var positions = this.attributes.position.array;
+	var previous = this.attributes.previous.array;
+	var next = this.attributes.next.array;
+	var l = positions.length;
+
+	// PREVIOUS
+	memcpy( positions, 0, previous, 0, l );
+
+	// POSITIONS
+	memcpy( positions, 6, positions, 0, l - 6 );
+
+	positions[l - 6] = position.x;
+	positions[l - 5] = position.y;
+	positions[l - 4] = position.z;
+	positions[l - 3] = position.x;
+	positions[l - 2] = position.y;
+	positions[l - 1] = position.z;
+
+    // NEXT
+	memcpy( positions, 6, next, 0, l - 6 );
+
+	next[l - 6]  = position.x;
+	next[l - 5]  = position.y;
+	next[l - 4]  = position.z;
+	next[l - 3]  = position.x;
+	next[l - 2]  = position.y;
+	next[l - 1]  = position.z;
+
+	this.attributes.position.needsUpdate = true;
+	this.attributes.previous.needsUpdate = true;
+	this.attributes.next.needsUpdate = true;
+
+};
+
+function MeshLineMaterial( parameters ) {
+
+	var vertexShaderSource = [
+'precision highp float;',
+'',
+'attribute vec3 position;',
+'attribute vec3 previous;',
+'attribute vec3 next;',
+'attribute float side;',
+'attribute float width;',
+'attribute vec2 uv;',
+'attribute float counters;',
+'',
+'uniform mat4 projectionMatrix;',
+'uniform mat4 modelViewMatrix;',
+'uniform vec2 resolution;',
+'uniform float lineWidth;',
+'uniform vec3 color;',
+'uniform float opacity;',
+'uniform float near;',
+'uniform float far;',
+'uniform float sizeAttenuation;',
+'',
+'varying vec2 vUV;',
+'varying vec4 vColor;',
+'varying float vCounters;',
+'',
+'vec2 fix( vec4 i, float aspect ) {',
+'',
+'    vec2 res = i.xy / i.w;',
+'    res.x *= aspect;',
+'	 vCounters = counters;',
+'    return res;',
+'',
+'}',
+'',
+'void main() {',
+'',
+'    float aspect = resolution.x / resolution.y;',
+'	 float pixelWidthRatio = 1. / (resolution.x * projectionMatrix[0][0]);',
+'',
+'    vColor = vec4( color, opacity );',
+'    vUV = uv;',
+'',
+'    mat4 m = projectionMatrix * modelViewMatrix;',
+'    vec4 finalPosition = m * vec4( position, 1.0 );',
+'    vec4 prevPos = m * vec4( previous, 1.0 );',
+'    vec4 nextPos = m * vec4( next, 1.0 );',
+'',
+'    vec2 currentP = fix( finalPosition, aspect );',
+'    vec2 prevP = fix( prevPos, aspect );',
+'    vec2 nextP = fix( nextPos, aspect );',
+'',
+'	 float pixelWidth = finalPosition.w * pixelWidthRatio;',
+'    float w = 1.8 * pixelWidth * lineWidth * width;',
+'',
+'    if( sizeAttenuation == 1. ) {',
+'        w = 1.8 * lineWidth * width;',
+'    }',
+'',
+'    vec2 dir;',
+'    if( nextP == currentP ) dir = normalize( currentP - prevP );',
+'    else if( prevP == currentP ) dir = normalize( nextP - currentP );',
+'    else {',
+'        vec2 dir1 = normalize( currentP - prevP );',
+'        vec2 dir2 = normalize( nextP - currentP );',
+'        dir = normalize( dir1 + dir2 );',
+'',
+'        vec2 perp = vec2( -dir1.y, dir1.x );',
+'        vec2 miter = vec2( -dir.y, dir.x );',
+'        //w = clamp( w / dot( miter, perp ), 0., 4. * lineWidth * width );',
+'',
+'    }',
+'',
+'    //vec2 normal = ( cross( vec3( dir, 0. ), vec3( 0., 0., 1. ) ) ).xy;',
+'    vec2 normal = vec2( -dir.y, dir.x );',
+'    normal.x /= aspect;',
+'    normal *= .5 * w;',
+'',
+'    vec4 offset = vec4( normal * side, 0.0, 1.0 );',
+'    finalPosition.xy += offset.xy;',
+'',
+'    gl_Position = finalPosition;',
+'',
+'}' ];
+
+	var fragmentShaderSource = [
+		'#extension GL_OES_standard_derivatives : enable',
+'precision mediump float;',
+'',
+'uniform sampler2D map;',
+'uniform sampler2D alphaMap;',
+'uniform float useMap;',
+'uniform float useAlphaMap;',
+'uniform float useDash;',
+'uniform float dashArray;',
+'uniform float dashOffset;',
+'uniform float dashRatio;',
+'uniform float visibility;',
+'uniform float alphaTest;',
+'uniform vec2 repeat;',
+'',
+'varying vec2 vUV;',
+'varying vec4 vColor;',
+'varying float vCounters;',
+'',
+'void main() {',
+'',
+'    vec4 c = vColor;',
+'    if( useMap == 1. ) c *= texture2D( map, vUV * repeat );',
+'    if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUV * repeat ).a;',
+'    if( c.a < alphaTest ) discard;',
+'    if( useDash == 1. ){',
+'        c.a *= ceil(mod(vCounters + dashOffset, dashArray) - (dashArray * dashRatio));',
+'    }',
+'    gl_FragColor = c;',
+'    gl_FragColor.a *= step(vCounters, visibility);',
+'}' ];
+
+	function check( v, d ) {
+		if( v === undefined ) return d;
+		return v;
+	}
+
+	THREE.Material.call( this );
+
+	parameters = parameters || {};
+
+	this.lineWidth = check( parameters.lineWidth, 1 );
+	this.map = check( parameters.map, null );
+	this.useMap = check( parameters.useMap, 0 );
+	this.alphaMap = check( parameters.alphaMap, null );
+	this.useAlphaMap = check( parameters.useAlphaMap, 0 );
+	this.color = check( parameters.color, new THREE.Color( 0xffffff ) );
+	this.opacity = check( parameters.opacity, 1 );
+	this.resolution = check( parameters.resolution, new THREE.Vector2( 1, 1 ) );
+	this.sizeAttenuation = check( parameters.sizeAttenuation, 1 );
+	this.near = check( parameters.near, 1 );
+	this.far = check( parameters.far, 1 );
+	this.dashArray = check( parameters.dashArray, 0 );
+	this.dashOffset = check( parameters.dashOffset, 0 );
+	this.dashRatio = check( parameters.dashRatio, 0.5 );
+	this.useDash = ( this.dashArray !== 0 ) ? 1 : 0;
+	this.visibility = check( parameters.visibility, 1 );
+	this.alphaTest = check( parameters.alphaTest, 0 );
+	this.repeat = check( parameters.repeat, new THREE.Vector2( 1, 1 ) );
+
+	var material = new THREE.RawShaderMaterial( {
+		uniforms:{
+			lineWidth: { type: 'f', value: this.lineWidth },
+			map: { type: 't', value: this.map },
+			useMap: { type: 'f', value: this.useMap },
+			alphaMap: { type: 't', value: this.alphaMap },
+			useAlphaMap: { type: 'f', value: this.useAlphaMap },
+			color: { type: 'c', value: this.color },
+			opacity: { type: 'f', value: this.opacity },
+			resolution: { type: 'v2', value: this.resolution },
+			sizeAttenuation: { type: 'f', value: this.sizeAttenuation },
+			near: { type: 'f', value: this.near },
+			far: { type: 'f', value: this.far },
+			dashArray: { type: 'f', value: this.dashArray },
+			dashOffset: { type: 'f', value: this.dashOffset },
+			dashRatio: { type: 'f', value: this.dashRatio },
+			useDash: { type: 'f', value: this.useDash },
+			visibility: {type: 'f', value: this.visibility},
+			alphaTest: {type: 'f', value: this.alphaTest},
+			repeat: { type: 'v2', value: this.repeat }
+		},
+		vertexShader: vertexShaderSource.join( '\r\n' ),
+		fragmentShader: fragmentShaderSource.join( '\r\n' )
+	});
+
+	delete parameters.lineWidth;
+	delete parameters.map;
+	delete parameters.useMap;
+	delete parameters.alphaMap;
+	delete parameters.useAlphaMap;
+	delete parameters.color;
+	delete parameters.opacity;
+	delete parameters.resolution;
+	delete parameters.sizeAttenuation;
+	delete parameters.near;
+	delete parameters.far;
+	delete parameters.dashArray;
+	delete parameters.dashOffset;
+	delete parameters.dashRatio;
+	delete parameters.visibility;
+	delete parameters.alphaTest;
+	delete parameters.repeat;
+
+	material.type = 'MeshLineMaterial';
+
+	material.setValues( parameters );
+
+	return material;
+
+};
+
+MeshLineMaterial.prototype = Object.create( THREE.Material.prototype );
+MeshLineMaterial.prototype.constructor = MeshLineMaterial;
+
+MeshLineMaterial.prototype.copy = function ( source ) {
+
+	THREE.Material.prototype.copy.call( this, source );
+
+	this.lineWidth = source.lineWidth;
+	this.map = source.map;
+	this.useMap = source.useMap;
+	this.alphaMap = source.alphaMap;
+	this.useAlphaMap = source.useAlphaMap;
+	this.color.copy( source.color );
+	this.opacity = source.opacity;
+	this.resolution.copy( source.resolution );
+	this.sizeAttenuation = source.sizeAttenuation;
+	this.near = source.near;
+	this.far = source.far;
+	this.dashArray.copy( source.dashArray );
+	this.dashOffset.copy( source.dashOffset );
+	this.dashRatio.copy( source.dashRatio );
+	this.useDash = source.useDash;
+	this.visibility = source.visibility;
+	this.alphaTest = source.alphaTest;
+	this.repeat.copy( source.repeat );
+
+	return this;
+
+};
+
+if( typeof exports !== 'undefined' ) {
+	if( typeof module !== 'undefined' && module.exports ) {
+		exports = module.exports = { MeshLine: MeshLine, MeshLineMaterial: MeshLineMaterial };
+	}
+	exports.MeshLine = MeshLine;
+	exports.MeshLineMaterial = MeshLineMaterial;
+}
+else {
+	root.MeshLine = MeshLine;
+	root.MeshLineMaterial = MeshLineMaterial;
+}
+
+}).call(this);

+ 627 - 0
js/d3_threeD.js

@@ -0,0 +1,627 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var transformSVGPathExposed;
+
+function d3threeD(exports) {
+
+    const DEGS_TO_RADS = Math.PI / 180,
+        UNIT_SIZE = 1;
+
+    const DIGIT_0 = 48, DIGIT_9 = 57, COMMA = 44, SPACE = 32, PERIOD = 46,
+        MINUS = 45;
+
+
+
+    function transformSVGPath(pathStr) {
+
+        var paths = [];
+        var path = new THREE.Shape();
+
+        var idx = 1, len = pathStr.length, activeCmd,
+            x = 0, y = 0, nx = 0, ny = 0, firstX = null, firstY = null,
+            x1 = 0, x2 = 0, y1 = 0, y2 = 0,
+            rx = 0, ry = 0, xar = 0, laf = 0, sf = 0, cx, cy;
+
+        function eatNum() {
+            var sidx, c, isFloat = false, s;
+            // eat delims
+            while (idx < len) {
+                c = pathStr.charCodeAt(idx);
+                if (c !== COMMA && c !== SPACE)
+                    break;
+                idx++;
+            }
+            if (c === MINUS)
+                sidx = idx++;
+            else
+                sidx = idx;
+            // eat number
+            while (idx < len) {
+                c = pathStr.charCodeAt(idx);
+                if (DIGIT_0 <= c && c <= DIGIT_9) {
+                    idx++;
+                    continue;
+                }
+                else if (c === PERIOD) {
+                    idx++;
+                    isFloat = true;
+                    continue;
+                }
+
+                s = pathStr.substring(sidx, idx);
+                return isFloat ? parseFloat(s) : parseInt(s);
+            }
+
+            s = pathStr.substring(sidx);
+            return isFloat ? parseFloat(s) : parseInt(s);
+        }
+
+        function nextIsNum() {
+            var c;
+            // do permanently eat any delims...
+            while (idx < len) {
+                c = pathStr.charCodeAt(idx);
+                if (c !== COMMA && c !== SPACE)
+                    break;
+                idx++;
+            }
+            c = pathStr.charCodeAt(idx);
+            return (c === MINUS || (DIGIT_0 <= c && c <= DIGIT_9));
+        }
+
+        var canRepeat;
+        var enteredSub = false;
+        var zSeen = false;
+        activeCmd = pathStr[0];
+
+        while (idx <= len) {
+            canRepeat = true;
+            switch (activeCmd) {
+                // moveto commands, become lineto's if repeated
+                case 'M':
+                    enteredSub = false;
+                    x = eatNum();
+                    y = eatNum();
+                    path.moveTo(x, y);
+                    activeCmd = 'L';
+                    break;
+                case 'm':
+                    x += eatNum();
+                    y += eatNum();
+                    path.moveTo(x, y);
+                    activeCmd = 'l';
+                    break;
+                case 'Z':
+                case 'z':
+                    // z is a special case. This ends a segment and starts
+                    // a new path. Since the three.js path is continuous
+                    // we should start a new path here. This also draws a
+                    // line from the current location to the start location.
+                    canRepeat = false;
+                    if (x !== firstX || y !== firstY)
+                        path.lineTo(firstX, firstY);
+
+                    paths.push(path);
+
+                    // reset the elements
+                    firstX = null;
+                    firstY = null;
+
+                    // avoid x,y being set incorrectly
+                    enteredSub = true;
+
+                    path = new THREE.Shape();
+
+                    zSeen = true;
+
+                    break;
+                // - lines!
+                case 'L':
+                case 'H':
+                case 'V':
+                    nx = (activeCmd === 'V') ? x : eatNum();
+                    ny = (activeCmd === 'H') ? y : eatNum();
+                    path.lineTo(nx, ny);
+                    x = nx;
+                    y = ny;
+                    break;
+                case 'l':
+                case 'h':
+                case 'v':
+                    nx = (activeCmd === 'v') ? x : (x + eatNum());
+                    ny = (activeCmd === 'h') ? y : (y + eatNum());
+                    path.lineTo(nx, ny);
+                    x = nx;
+                    y = ny;
+                    break;
+                // - cubic bezier
+                case 'C':
+                    x1 = eatNum(); y1 = eatNum();
+                case 'S':
+                    if (activeCmd === 'S') {
+                        x1 = 2 * x - x2; y1 = 2 * y - y2;
+                    }
+                    x2 = eatNum();
+                    y2 = eatNum();
+                    nx = eatNum();
+                    ny = eatNum();
+                    path.bezierCurveTo(x1, y1, x2, y2, nx, ny);
+                    x = nx; y = ny;
+                    break;
+                case 'c':
+                    x1 = x + eatNum();
+                    y1 = y + eatNum();
+                case 's':
+                    if (activeCmd === 's') {
+                        x1 = 2 * x - x2;
+                        y1 = 2 * y - y2;
+                    }
+                    x2 = x + eatNum();
+                    y2 = y + eatNum();
+                    nx = x + eatNum();
+                    ny = y + eatNum();
+                    path.bezierCurveTo(x1, y1, x2, y2, nx, ny);
+                    x = nx; y = ny;
+                    break;
+                // - quadratic bezier
+                case 'Q':
+                    x1 = eatNum(); y1 = eatNum();
+                case 'T':
+                    if (activeCmd === 'T') {
+                        x1 = 2 * x - x1;
+                        y1 = 2 * y - y1;
+                    }
+                    nx = eatNum();
+                    ny = eatNum();
+                    path.quadraticCurveTo(x1, y1, nx, ny);
+                    x = nx;
+                    y = ny;
+                    break;
+                case 'q':
+                    x1 = x + eatNum();
+                    y1 = y + eatNum();
+                case 't':
+                    if (activeCmd === 't') {
+                        x1 = 2 * x - x1;
+                        y1 = 2 * y - y1;
+                    }
+                    nx = x + eatNum();
+                    ny = y + eatNum();
+                    path.quadraticCurveTo(x1, y1, nx, ny);
+                    x = nx; y = ny;
+                    break;
+                // - elliptical arc
+                case 'A':
+                    rx = eatNum();
+                    ry = eatNum();
+                    xar = eatNum() * DEGS_TO_RADS;
+                    laf = eatNum();
+                    sf = eatNum();
+                    nx = eatNum();
+                    ny = eatNum();
+                    if (rx !== ry) {
+                        console.warn("Forcing elliptical arc to be a circular one :(",
+                            rx, ry);
+                    }
+                    // SVG implementation notes does all the math for us! woo!
+                    // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+                    // step1, using x1 as x1'
+                    x1 = Math.cos(xar) * (x - nx) / 2 + Math.sin(xar) * (y - ny) / 2;
+                    y1 = -Math.sin(xar) * (x - nx) / 2 + Math.cos(xar) * (y - ny) / 2;
+                    // step 2, using x2 as cx'
+                    var norm = Math.sqrt(
+                        (rx*rx * ry*ry - rx*rx * y1*y1 - ry*ry * x1*x1) /
+                            (rx*rx * y1*y1 + ry*ry * x1*x1));
+                    if (laf === sf)
+                        norm = -norm;
+                    x2 = norm * rx * y1 / ry;
+                    y2 = norm * -ry * x1 / rx;
+                    // step 3
+                    cx = Math.cos(xar) * x2 - Math.sin(xar) * y2 + (x + nx) / 2;
+                    cy = Math.sin(xar) * x2 + Math.cos(xar) * y2 + (y + ny) / 2;
+
+                    var u = new THREE.Vector2(1, 0),
+                        v = new THREE.Vector2((x1 - x2) / rx,
+                            (y1 - y2) / ry);
+                    var startAng = Math.acos(u.dot(v) / u.length() / v.length());
+                    if (u.x * v.y - u.y * v.x < 0)
+                        startAng = -startAng;
+
+                    // we can reuse 'v' from start angle as our 'u' for delta angle
+                    u.x = (-x1 - x2) / rx;
+                    u.y = (-y1 - y2) / ry;
+
+                    var deltaAng = Math.acos(v.dot(u) / v.length() / u.length());
+                    // This normalization ends up making our curves fail to triangulate...
+                    if (v.x * u.y - v.y * u.x < 0)
+                        deltaAng = -deltaAng;
+                    if (!sf && deltaAng > 0)
+                        deltaAng -= Math.PI * 2;
+                    if (sf && deltaAng < 0)
+                        deltaAng += Math.PI * 2;
+
+                    path.absarc(cx, cy, rx, startAng, startAng + deltaAng, sf);
+                    x = nx;
+                    y = ny;
+                    break;
+
+                case ' ':
+                    // if it's an empty space, just skip it, and see if we can find a real command
+                    break;
+
+                default:
+                    throw new Error("weird path command: " + activeCmd);
+            }
+            if (firstX === null && !enteredSub) {
+                firstX = x;
+                firstY = y;
+            }
+
+            // just reissue the command
+            if (canRepeat && nextIsNum())
+                continue;
+            activeCmd = pathStr[idx++];
+        }
+
+        if (zSeen) {
+            return paths;
+        } else {
+            paths.push(path);
+            return paths;
+        }
+    }
+
+    transformSVGPathExposed = transformSVGPath;
+
+    function applySVGTransform(obj, tstr) {
+
+
+        var idx = tstr.indexOf('('), len = tstr.length,
+            cmd = tstr.substring(0, idx++);
+        function eatNum() {
+            var sidx, c, isFloat = false, s;
+            // eat delims
+            while (idx < len) {
+                c = tstr.charCodeAt(idx);
+                if (c !== COMMA && c !== SPACE)
+                    break;
+                idx++;
+            }
+            if (c === MINUS)
+                sidx = idx++;
+            else
+                sidx = idx;
+            // eat number
+            while (idx < len) {
+                c = tstr.charCodeAt(idx);
+                if (DIGIT_0 <= c && c <= DIGIT_9) {
+                    idx++;
+                    continue;
+                }
+                else if (c === PERIOD) {
+                    idx++;
+                    isFloat = true;
+                    continue;
+                }
+
+                s = tstr.substring(sidx, idx);
+                return isFloat ? parseFloat(s) : parseInt(s);
+            }
+
+            s = tstr.substring(sidx);
+            return isFloat ? parseFloat(s) : parseInt(s);
+        }
+        switch (cmd) {
+            case 'translate':
+                obj.position.x = Math.floor(eatNum() * UNIT_SIZE);
+                obj.position.y = Math.floor(eatNum() * UNIT_SIZE);
+                break;
+            case 'scale':
+                obj.scale.x = Math.floor(eatNum() * UNIT_SIZE);
+                obj.scale.y = Math.floor(eatNum() * UNIT_SIZE);
+                break;
+            default:
+                console.warn("don't understand transform", tstr);
+                break;
+        }
+    }
+
+    applySVGTransformExposed = applySVGTransform;
+
+    function wrap_setAttribute(name, value) {
+    }
+    function wrap_setAttributeNS(namespace, name, value) {
+    }
+
+
+
+
+
+    var extrudeDefaults = {
+        amount: 20,
+        bevelEnabled: true,
+        material: 0,
+        extrudeMaterial: 0,
+    };
+
+
+
+
+
+    function commonSetAttribute(name, value) {
+        switch (name) {
+            case 'x':
+                this.position.x = Math.floor(value * UNIT_SIZE);
+                break;
+
+            case 'y':
+                this.position.y = Math.floor(value * UNIT_SIZE);
+                break;
+
+            case 'class':
+                this.clazz = value;
+                break;
+
+            case 'stroke':
+            case 'fill':
+                if (typeof(value) !== 'string')
+                    value = value.toString();
+                this.material.color.setHex(parseInt(value.substring(1), 16));
+                break;
+
+            case 'transform':
+                applySVGTransform(this, value);
+                break;
+
+            case 'd':
+                var shape = transformSVGPath(value),
+                    geom = shape.extrude(extrudeDefaults);
+                this.geometry = geom;
+                this.geometry.boundingSphere = {radius: 3 * UNIT_SIZE};
+                this.scale.set(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
+
+                break;
+
+            default:
+                throw new Error("no setter for: " + name);
+        }
+    }
+    function commonSetAttributeNS(namespace, name, value) {
+        this.setAttribute(name, value);
+    }
+
+    function Group(parentThing) {
+        THREE.Object3D.call(this);
+
+        this.d3class = '';
+
+        parentThing.add(this);
+    };
+    Group.prototype = new THREE.Object3D();
+    Group.prototype.constructor = Group;
+    Group.prototype.d3tag = 'g';
+    Group.prototype.setAttribute = commonSetAttribute;
+    Group.prototype.setAttributeNS = commonSetAttributeNS;
+
+    function fabGroup() {
+        return new Group(this);
+    }
+
+    function Mesh(parentThing, tag, geometry, material) {
+        THREE.Mesh.call(this, geometry, material);
+
+        this.d3tag = tag;
+        this.d3class = '';
+
+        parentThing.add(this);
+    }
+    Mesh.prototype = new THREE.Mesh();
+    Mesh.prototype.constructor = Mesh;
+    Mesh.prototype.setAttribute = commonSetAttribute;
+    Mesh.prototype.setAttributeNS = commonSetAttributeNS;
+
+
+    const SPHERE_SEGS = 16, SPHERE_RINGS = 16,
+        DEFAULT_COLOR = 0xcc0000;
+
+    var sharedSphereGeom = null,
+        sharedCubeGeom = null;
+
+    function fabSphere() {
+        if (!sharedSphereGeom)
+            sharedSphereGeom = new THREE.SphereGeometry(
+                UNIT_SIZE / 2, SPHERE_SEGS, SPHERE_RINGS);
+        var material = new THREE.MeshLambertMaterial({
+            color: DEFAULT_COLOR,
+        });
+        return new Mesh(this, 'sphere', sharedSphereGeom, material);
+    }
+
+    function fabCube() {
+        if (!sharedCubeGeom)
+            sharedCubeGeom = new THREE.CubeGeometry(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
+        var material = new THREE.MeshLambertMaterial({
+            color: DEFAULT_COLOR,
+        });
+        return new Mesh(this, 'cube', sharedCubeGeom, material);
+    }
+
+    function fabPath() {
+        // start with a cube that we will replace with the path once it gets created
+        if (!sharedCubeGeom)
+            sharedCubeGeom = new THREE.CubeGeometry(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
+        var material = new THREE.MeshLambertMaterial({
+            color: DEFAULT_COLOR,
+        });
+        return new Mesh(this, 'path', sharedCubeGeom, material);
+    }
+
+    function Scene() {
+        THREE.Scene.call(this);
+        this.renderer = null;
+        this.camera = null;
+        this.controls = null;
+        this._d3_width = null;
+        this._d3_height = null;
+    }
+    Scene.prototype = new THREE.Scene();
+    Scene.prototype.constructor = Scene;
+    Scene.prototype._setBounds = function() {
+        this.renderer.setSize(this._d3_width, this._d3_height);
+        var aspect = this.camera.aspect;
+        this.camera.position.set(
+            this._d3_width * UNIT_SIZE / 2,
+            this._d3_height * UNIT_SIZE / 2,
+            Math.max(this._d3_width * UNIT_SIZE / Math.sqrt(2),
+                this._d3_height * UNIT_SIZE / Math.sqrt(2)));
+        this.controls.target.set(this.camera.position.x, this.camera.position.y, 0);
+        console.log("camera:", this.camera.position.x, this.camera.position.y,
+            this.camera.position.z);
+
+
+
+        //this.camera.position.z = 1000;
+    };
+    Scene.prototype.setAttribute = function(name, value) {
+        switch (name) {
+            case 'width':
+                this._d3_width = value;
+                if (this._d3_height)
+                    this._setBounds();
+                break;
+            case 'height':
+                this._d3_height = value;
+                if (this._d3_width)
+                    this._setBounds();
+                break;
+        }
+    };
+
+
+
+    function fabVis() {
+        var camera, scene, controls, renderer;
+
+        // - scene
+        scene = new Scene();
+        threeJsScene = scene;
+
+        // - camera
+        camera = scene.camera = new THREE.PerspectiveCamera(
+            75,
+            window.innerWidth / window.innerHeight,
+            1, 100000);
+        /*
+         camera = scene.camera = new THREE.OrthographicCamera(
+         window.innerWidth / -2, window.innerWidth / 2,
+         window.innerHeight / 2, window.innerHeight / -2,
+         1, 50000);
+         */
+        scene.add(camera);
+
+        // - controls
+        // from misc_camera_trackball.html example
+        controls = scene.controls = new THREE.TrackballControls(camera);
+        controls.rotateSpeed = 1.0;
+        controls.zoomSpeed = 1.2;
+        controls.panSpeed = 0.8;
+
+        controls.noZoom = false;
+        controls.noPan = false;
+
+        controls.staticMoving = true;
+        controls.dynamicDampingFactor = 0.3;
+
+        controls.keys = [65, 83, 68];
+
+        controls.addEventListener('change', render);
+
+        // - light
+        /*
+         var pointLight = new THREE.PointLight(0xFFFFFF);
+         pointLight.position.set(10, 50, 130);
+         scene.add(pointLight);
+         */
+
+        var spotlight = new THREE.SpotLight(0xffffff);
+        spotlight.position.set(-50000, 50000, 100000);
+        scene.add(spotlight);
+
+        var backlight = new THREE.SpotLight(0x888888);
+        backlight.position.set(50000, -50000, -100000);
+        scene.add(backlight);
+
+        /*
+         var ambientLight = new THREE.AmbientLight(0x888888);
+         scene.add(ambientLight);
+         */
+
+        function helperPlanes(maxBound) {
+            var geom = new THREE.PlaneGeometry(maxBound, maxBound, 4, 4);
+            for (var i = 0; i < 4; i++) {
+                var color, cx, cy;
+                switch (i) {
+                    case 0:
+                        color = 0xff0000;
+                        cx = maxBound / 2;
+                        cy = maxBound / 2;
+                        break;
+                    case 1:
+                        color = 0x00ff00;
+                        cx = maxBound / 2;
+                        cy = -maxBound / 2;
+                        break;
+                    case 2:
+                        color = 0x0000ff;
+                        cx = -maxBound / 2;
+                        cy = -maxBound / 2;
+                        break;
+                    case 3:
+                        color = 0xffff00;
+                        cx = -maxBound / 2;
+                        cy = maxBound / 2;
+                        break;
+                }
+                var material = new THREE.MeshLambertMaterial({ color: color });
+                var mesh = new THREE.Mesh(geom, material);
+                mesh.position.set(cx, cy, -1);
+
+                scene.add(mesh);
+            }
+        }
+        //helperPlanes(UNIT_SIZE * 225);
+
+        // - renderer
+        renderer = scene.renderer = new THREE.WebGLRenderer({
+            // too slow...
+            //antialias: true,
+        });
+        this.appendChild( renderer.domElement );
+
+        // - stats
+        var stats = new Stats();
+        stats.domElement.style.position = 'absolute';
+        stats.domElement.style.top = '0px';
+        stats.domElement.style.zIndex = 100;
+        this.appendChild( stats.domElement );
+
+        function animate() {
+            requestAnimationFrame(animate, renderer.domElement);
+            controls.update();
+        }
+
+        function render() {
+            renderer.render(scene, camera);
+            stats.update();
+        }
+
+        animate();
+
+        return scene;
+    };
+}
+
+var $d3g = {};
+d3threeD($d3g);

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 14 - 0
js/dat.gui.min.js


+ 138 - 0
js/main.js

@@ -0,0 +1,138 @@
+$(function() {
+  init();
+});
+
+function init() {
+  var canvas = document.getElementById("myCanvas");
+  var context = canvas.getContext("2d");
+
+  var w = canvas.width;
+  var h = canvas.height;
+  context.translate(w/2,h/2);
+
+  // 选择线段的roomId和line
+  var _selectLineArr = []
+  var _selectLine = [];
+  var _selectVector = [];
+  var _verticalLine = []
+  var inFaceArr = []
+  var intersectArr =[]
+  var isDrag = false;
+  var isDivide = false;
+  var isFirst = true
+
+  var firstStack = []
+  // f_cube = rawToCube(calculateRawData(rawData),w,h)
+  f_cube = rawToCube(raw_cube,w,h)
+ 
+  draw(context, w, h);
+  $('#divide').click(function (e) {
+    if (isDivide) {
+      isDivide = false
+    $('#divide').removeClass('active')    
+    }else{
+      isDivide = true
+    $('#divide').addClass('active')
+    }
+  })
+  $("#myCanvas").mousedown(function(e) {
+    
+    var current = calculateXY([e.offsetX, e.offsetY],w,h);
+    f_cubeLine = changeData(f_cube)
+    _selectLineArr = selectLine(current, f_cubeLine, w, h)
+    _selectLine = _selectLineArr.length>0?_selectLineArr[0]:[];
+    _selectVector = _selectLine.length !== 0 ? [
+      _selectLine["_3d_point2"]["data"][0] -
+        _selectLine["_3d_point1"]["data"][0],
+      _selectLine["_3d_point2"]["data"][1] -
+        _selectLine["_3d_point1"]["data"][1]
+    ]:[];
+    
+
+    _verticalLine = verticalLine(_selectVector)
+    if (_selectLine.length !== 0) {
+      drawSelectLine(context, _selectLine, w, h);
+      isDrag = true;
+    } else {
+      draw(context, w, h);
+    }
+
+    // var current = [e.offsetX/w, e.offsetY/h];
+   
+  });
+  $("#myCanvas").mouseup(function(e) {
+    isDrag = false;
+    if (_selectLine.length !== 0) {
+      // console.log(intersect(f_cubeLine));
+      if (firstStack.length>0) {
+        f_cube = _getWhichPoint(...firstStack)[1];
+        f_cubeLine = changeData(f_cube)
+        // inFaceArr = cubePointInFace(_selectLineArr[1],f_cube,f_cubeLine)
+        // pushInNewCube(inFaceArr,intersectArr,f_cube)
+      }
+    draw(context, w, h);
+    }
+    // draw(context, w, h);
+  });
+  $("#myCanvas").mousemove(function(e) {
+    
+    if (isDrag) {
+      var current = calculateXY([e.offsetX, e.offsetY],w,h);
+      // f_cubeLine = _getWhichPoint(_selectLine, current,_verticalLine, f_cubeLine, f_cube)[0];
+      // f_cube = _getWhichPoint(_selectLine, current,_verticalLine, f_cubeLine, f_cube)[1];
+      // draw(canvas, w, h);
+      // drawSelectLine(canvas, _selectLine, w, h);
+      // getPointFromLine(f_cubeLine)
+      // var current = [e.offsetX, e.offsetY];
+      if (isFirst) {
+        firstStack = [_selectLine, current,_verticalLine, f_cubeLine, f_cube,w,h]
+        isFirst = false
+        return
+      }
+      f_cube = _getWhichPoint(_selectLine, current,_verticalLine, f_cubeLine, f_cube,w,h)[1];
+      intersectArr = intersect(_selectLineArr[1],f_cubeLine)
+      // console.log('====================================');
+      // console.log(intersectArr);
+      // console.log('====================================');
+      // if (Math.abs(current[0]-lastX)>50&&lastX) {
+      //   return
+      // }
+      if (!pointInSelectFace(_selectLineArr[1],f_cube) || intersectArr.length>0) {
+        return
+      }
+      
+      drawXX(_selectLine, current,_verticalLine, f_cubeLine, f_cube,w,h)
+
+      firstStack = [_selectLine, current,_verticalLine, f_cubeLine, f_cube,w,h]
+      function drawXX(_selectLine, current,_verticalLine, f_cubeLine, f_cube,w,h) {
+        f_cube = _getWhichPoint(_selectLine, current,_verticalLine, f_cubeLine, f_cube,w,h)[1];
+        draw(context, w, h);
+        drawSelectLine(context, _selectLine, w, h);
+      }
+    }
+    
+  });
+}
+
+function draw(context, w, h) {
+    context.clearRect(-w/2, -h/2, w, h);
+    var temp;
+    var cam_pos = []
+    for (let i = 0; i < f_cube.length; i++) {
+      temp = [];
+      for (let j = 0; j < f_cube[i].points.length; j++) {
+        temp.push(f_cube[i].points[j]["data"]);
+      }
+      cam_pos = f_cube[i].campos?f_cube[i].campos:[]
+      circularLine(temp, context, "#ff0000",cam_pos, w, h, false);
+    }
+}
+
+function drawSelectLine(context, selectLine, w, h) {
+    // var context = canvas.getContext("2d");
+    var temps = [];
+    temps.push(selectLine["_3d_point1"]["data"]);
+    temps.push(selectLine["_3d_point2"]["data"]);
+    
+    circularLine(temps, context, "#0000ff",[], w, h, false);
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 5 - 0
js/stats.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 47349 - 0
js/three.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 892 - 0
js/three.min.js


+ 519 - 0
js/threeBSP.js

@@ -0,0 +1,519 @@
+// Generated by CoffeeScript 1.6.3
+(function() {
+    var BACK, COPLANAR, EPSILON, FRONT, SPANNING, returning,
+      __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
+      __slice = [].slice,
+      __hasProp = {}.hasOwnProperty,
+      __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+  
+    EPSILON = 1e-5;
+  
+    COPLANAR = 0;
+  
+    FRONT = 1;
+  
+    BACK = 2;
+  
+    SPANNING = 3;
+  
+    returning = function(value, fn) {
+      fn();
+      return value;
+    };
+  
+    window.ThreeBSP = (function() {
+      function ThreeBSP(treeIsh, matrix) {
+        this.matrix = matrix;
+        this.intersect = __bind(this.intersect, this);
+        this.union = __bind(this.union, this);
+        this.subtract = __bind(this.subtract, this);
+        this.toGeometry = __bind(this.toGeometry, this);
+        this.toMesh = __bind(this.toMesh, this);
+        this.toTree = __bind(this.toTree, this);
+        if (this.matrix == null) {
+          this.matrix = new THREE.Matrix4();
+        }
+        this.tree = this.toTree(treeIsh);
+      }
+  
+      ThreeBSP.prototype.toTree = function(treeIsh) {
+        var face, geometry, i, polygons, _fn, _i, _len, _ref,
+          _this = this;
+        if (treeIsh instanceof ThreeBSP.Node) {
+          return treeIsh;
+        }
+        polygons = [];
+        geometry = treeIsh instanceof THREE.Geometry ? treeIsh : treeIsh instanceof THREE.Mesh ? (treeIsh.updateMatrix(), this.matrix = treeIsh.matrix.clone(), treeIsh.geometry) : void 0;
+        _ref = geometry.faces;
+        _fn = function(face, i) {
+          var faceVertexUvs, idx, polygon, vIndex, vName, vertex, _j, _len1, _ref1, _ref2;
+          faceVertexUvs = (_ref1 = geometry.faceVertexUvs) != null ? _ref1[0][i] : void 0;
+          if (faceVertexUvs == null) {
+            faceVertexUvs = [new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2()];
+          }
+          polygon = new ThreeBSP.Polygon();
+          _ref2 = ['a', 'b', 'c', 'd'];
+          for (vIndex = _j = 0, _len1 = _ref2.length; _j < _len1; vIndex = ++_j) {
+            vName = _ref2[vIndex];
+            if ((idx = face[vName]) != null) {
+              vertex = geometry.vertices[idx];
+              vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], new THREE.Vector2(faceVertexUvs[vIndex].x, faceVertexUvs[vIndex].y));
+              vertex.applyMatrix4(_this.matrix);
+              polygon.vertices.push(vertex);
+            }
+          }
+          return polygons.push(polygon.calculateProperties());
+        };
+        for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
+          face = _ref[i];
+          _fn(face, i);
+        }
+        return new ThreeBSP.Node(polygons);
+      };
+  
+      ThreeBSP.prototype.toMesh = function(material) {
+        var geometry, mesh,
+          _this = this;
+        if (material == null) {
+          material = new THREE.MeshNormalMaterial();
+        }
+        geometry = this.toGeometry();
+        return returning((mesh = new THREE.Mesh(geometry, material)), function() {
+          mesh.position.getPositionFromMatrix(_this.matrix);
+          return mesh.rotation.setFromRotationMatrix(_this.matrix);
+        });
+      };
+  
+      ThreeBSP.prototype.toGeometry = function() {
+        var geometry, matrix,
+          _this = this;
+        matrix = new THREE.Matrix4().getInverse(this.matrix);
+        return returning((geometry = new THREE.Geometry()), function() {
+          var face, idx, polyVerts, polygon, v, vertUvs, verts, _i, _len, _ref, _results;
+          _ref = _this.tree.allPolygons();
+          _results = [];
+          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+            polygon = _ref[_i];
+            polyVerts = (function() {
+              var _j, _len1, _ref1, _results1;
+              _ref1 = polygon.vertices;
+              _results1 = [];
+              for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+                v = _ref1[_j];
+                _results1.push(v.clone().applyMatrix4(matrix));
+              }
+              return _results1;
+            })();
+            _results.push((function() {
+              var _j, _ref1, _results1;
+              _results1 = [];
+              for (idx = _j = 2, _ref1 = polyVerts.length; 2 <= _ref1 ? _j < _ref1 : _j > _ref1; idx = 2 <= _ref1 ? ++_j : --_j) {
+                verts = [polyVerts[0], polyVerts[idx - 1], polyVerts[idx]];
+                vertUvs = (function() {
+                  var _k, _len1, _ref2, _ref3, _results2;
+                  _results2 = [];
+                  for (_k = 0, _len1 = verts.length; _k < _len1; _k++) {
+                    v = verts[_k];
+                    _results2.push(new THREE.Vector2((_ref2 = v.uv) != null ? _ref2.x : void 0, (_ref3 = v.uv) != null ? _ref3.y : void 0));
+                  }
+                  return _results2;
+                })();
+                face = (function(func, args, ctor) {
+                  ctor.prototype = func.prototype;
+                  var child = new ctor, result = func.apply(child, args);
+                  return Object(result) === result ? result : child;
+                })(THREE.Face3, __slice.call((function() {
+                  var _k, _len1, _results2;
+                  _results2 = [];
+                  for (_k = 0, _len1 = verts.length; _k < _len1; _k++) {
+                    v = verts[_k];
+                    _results2.push(geometry.vertices.push(v) - 1);
+                  }
+                  return _results2;
+                })()).concat([polygon.normal.clone()]), function(){});
+                geometry.faces.push(face);
+                _results1.push(geometry.faceVertexUvs[0].push(vertUvs));
+              }
+              return _results1;
+            })());
+          }
+          return _results;
+        });
+      };
+  
+      ThreeBSP.prototype.subtract = function(other) {
+        var them, us, _ref;
+        _ref = [this.tree.clone(), other.tree.clone()], us = _ref[0], them = _ref[1];
+        us.invert().clipTo(them);
+        them.clipTo(us).invert().clipTo(us).invert();
+        return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
+      };
+  
+      ThreeBSP.prototype.union = function(other) {
+        var them, us, _ref;
+        _ref = [this.tree.clone(), other.tree.clone()], us = _ref[0], them = _ref[1];
+        us.clipTo(them);
+        them.clipTo(us).invert().clipTo(us).invert();
+        return new ThreeBSP(us.build(them.allPolygons()), this.matrix);
+      };
+  
+      ThreeBSP.prototype.intersect = function(other) {
+        var them, us, _ref;
+        _ref = [this.tree.clone(), other.tree.clone()], us = _ref[0], them = _ref[1];
+        them.clipTo(us.invert()).invert().clipTo(us.clipTo(them));
+        return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
+      };
+  
+      return ThreeBSP;
+  
+    })();
+  
+    ThreeBSP.Vertex = (function(_super) {
+      __extends(Vertex, _super);
+  
+      function Vertex(x, y, z, normal, uv) {
+        this.normal = normal != null ? normal : new THREE.Vector3();
+        this.uv = uv != null ? uv : new THREE.Vector2();
+        this.interpolate = __bind(this.interpolate, this);
+        this.lerp = __bind(this.lerp, this);
+        Vertex.__super__.constructor.call(this, x, y, z);
+      }
+  
+      Vertex.prototype.clone = function() {
+        return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());
+      };
+  
+      Vertex.prototype.lerp = function(v, alpha) {
+        var _this = this;
+        return returning(Vertex.__super__.lerp.apply(this, arguments), function() {
+          _this.uv.add(v.uv.clone().sub(_this.uv).multiplyScalar(alpha));
+          return _this.normal.lerp(v, alpha);
+        });
+      };
+  
+      Vertex.prototype.interpolate = function() {
+        var args, _ref;
+        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+        return (_ref = this.clone()).lerp.apply(_ref, args);
+      };
+  
+      return Vertex;
+  
+    })(THREE.Vector3);
+  
+    ThreeBSP.Polygon = (function() {
+      function Polygon(vertices, normal, w) {
+        this.vertices = vertices != null ? vertices : [];
+        this.normal = normal;
+        this.w = w;
+        this.subdivide = __bind(this.subdivide, this);
+        this.tessellate = __bind(this.tessellate, this);
+        this.classifySide = __bind(this.classifySide, this);
+        this.classifyVertex = __bind(this.classifyVertex, this);
+        this.invert = __bind(this.invert, this);
+        this.clone = __bind(this.clone, this);
+        this.calculateProperties = __bind(this.calculateProperties, this);
+        if (this.vertices.length) {
+          this.calculateProperties();
+        }
+      }
+  
+      Polygon.prototype.calculateProperties = function() {
+        var _this = this;
+        return returning(this, function() {
+          var a, b, c, _ref;
+          _ref = _this.vertices, a = _ref[0], b = _ref[1], c = _ref[2];
+          _this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
+          return _this.w = _this.normal.clone().dot(a);
+        });
+      };
+  
+      Polygon.prototype.clone = function() {
+        var v;
+        return new ThreeBSP.Polygon((function() {
+          var _i, _len, _ref, _results;
+          _ref = this.vertices;
+          _results = [];
+          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+            v = _ref[_i];
+            _results.push(v.clone());
+          }
+          return _results;
+        }).call(this), this.normal.clone(), this.w);
+      };
+  
+      Polygon.prototype.invert = function() {
+        var _this = this;
+        return returning(this, function() {
+          _this.normal.multiplyScalar(-1);
+          _this.w *= -1;
+          return _this.vertices.reverse();
+        });
+      };
+  
+      Polygon.prototype.classifyVertex = function(vertex) {
+        var side;
+        side = this.normal.dot(vertex) - this.w;
+        switch (false) {
+          case !(side < -EPSILON):
+            return BACK;
+          case !(side > EPSILON):
+            return FRONT;
+          default:
+            return COPLANAR;
+        }
+      };
+  
+      Polygon.prototype.classifySide = function(polygon) {
+        var back, front, tally, v, _i, _len, _ref, _ref1,
+          _this = this;
+        _ref = [0, 0], front = _ref[0], back = _ref[1];
+        tally = function(v) {
+          switch (_this.classifyVertex(v)) {
+            case FRONT:
+              return front += 1;
+            case BACK:
+              return back += 1;
+          }
+        };
+        _ref1 = polygon.vertices;
+        for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+          v = _ref1[_i];
+          tally(v);
+        }
+        if (front > 0 && back === 0) {
+          return FRONT;
+        }
+        if (front === 0 && back > 0) {
+          return BACK;
+        }
+        if ((front === back && back === 0)) {
+          return COPLANAR;
+        }
+        return SPANNING;
+      };
+  
+      Polygon.prototype.tessellate = function(poly) {
+        var b, count, f, i, j, polys, t, ti, tj, v, vi, vj, _i, _len, _ref, _ref1, _ref2,
+          _this = this;
+        _ref = {
+          f: [],
+          b: [],
+          count: poly.vertices.length
+        }, f = _ref.f, b = _ref.b, count = _ref.count;
+        if (this.classifySide(poly) !== SPANNING) {
+          return [poly];
+        }
+        _ref1 = poly.vertices;
+        for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) {
+          vi = _ref1[i];
+          vj = poly.vertices[(j = (i + 1) % count)];
+          _ref2 = (function() {
+            var _j, _len1, _ref2, _results;
+            _ref2 = [vi, vj];
+            _results = [];
+            for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
+              v = _ref2[_j];
+              _results.push(this.classifyVertex(v));
+            }
+            return _results;
+          }).call(this), ti = _ref2[0], tj = _ref2[1];
+          if (ti !== BACK) {
+            f.push(vi);
+          }
+          if (ti !== FRONT) {
+            b.push(vi);
+          }
+          if ((ti | tj) === SPANNING) {
+            t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().sub(vi));
+            v = vi.interpolate(vj, t);
+            f.push(v);
+            b.push(v);
+          }
+        }
+        return returning((polys = []), function() {
+          if (f.length >= 3) {
+            polys.push(new ThreeBSP.Polygon(f));
+          }
+          if (b.length >= 3) {
+            return polys.push(new ThreeBSP.Polygon(b));
+          }
+        });
+      };
+  
+      Polygon.prototype.subdivide = function(polygon, coplanar_front, coplanar_back, front, back) {
+        var poly, side, _i, _len, _ref, _results;
+        _ref = this.tessellate(polygon);
+        _results = [];
+        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+          poly = _ref[_i];
+          side = this.classifySide(poly);
+          switch (side) {
+            case FRONT:
+              _results.push(front.push(poly));
+              break;
+            case BACK:
+              _results.push(back.push(poly));
+              break;
+            case COPLANAR:
+              if (this.normal.dot(poly.normal) > 0) {
+                _results.push(coplanar_front.push(poly));
+              } else {
+                _results.push(coplanar_back.push(poly));
+              }
+              break;
+            default:
+              throw new Error("BUG: Polygon of classification " + side + " in subdivision");
+          }
+        }
+        return _results;
+      };
+  
+      return Polygon;
+  
+    })();
+  
+    ThreeBSP.Node = (function() {
+      Node.prototype.clone = function() {
+        var node,
+          _this = this;
+        return returning((node = new ThreeBSP.Node()), function() {
+          var p, _ref, _ref1, _ref2;
+          node.divider = (_ref = _this.divider) != null ? _ref.clone() : void 0;
+          node.polygons = (function() {
+            var _i, _len, _ref1, _results;
+            _ref1 = this.polygons;
+            _results = [];
+            for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+              p = _ref1[_i];
+              _results.push(p.clone());
+            }
+            return _results;
+          }).call(_this);
+          node.front = (_ref1 = _this.front) != null ? _ref1.clone() : void 0;
+          return node.back = (_ref2 = _this.back) != null ? _ref2.clone() : void 0;
+        });
+      };
+  
+      function Node(polygons) {
+        this.clipTo = __bind(this.clipTo, this);
+        this.clipPolygons = __bind(this.clipPolygons, this);
+        this.invert = __bind(this.invert, this);
+        this.allPolygons = __bind(this.allPolygons, this);
+        this.isConvex = __bind(this.isConvex, this);
+        this.build = __bind(this.build, this);
+        this.clone = __bind(this.clone, this);
+        this.polygons = [];
+        if ((polygons != null) && polygons.length) {
+          this.build(polygons);
+        }
+      }
+  
+      Node.prototype.build = function(polygons) {
+        var _this = this;
+        return returning(this, function() {
+          var poly, polys, side, sides, _i, _len, _results;
+          sides = {
+            front: [],
+            back: []
+          };
+          if (_this.divider == null) {
+            _this.divider = polygons[0].clone();
+          }
+          for (_i = 0, _len = polygons.length; _i < _len; _i++) {
+            poly = polygons[_i];
+            _this.divider.subdivide(poly, _this.polygons, _this.polygons, sides.front, sides.back);
+          }
+          _results = [];
+          for (side in sides) {
+            if (!__hasProp.call(sides, side)) continue;
+            polys = sides[side];
+            if (polys.length) {
+              if (_this[side] == null) {
+                _this[side] = new ThreeBSP.Node();
+              }
+              _results.push(_this[side].build(polys));
+            } else {
+              _results.push(void 0);
+            }
+          }
+          return _results;
+        });
+      };
+  
+      Node.prototype.isConvex = function(polys) {
+        var inner, outer, _i, _j, _len, _len1;
+        for (_i = 0, _len = polys.length; _i < _len; _i++) {
+          inner = polys[_i];
+          for (_j = 0, _len1 = polys.length; _j < _len1; _j++) {
+            outer = polys[_j];
+            if (inner !== outer && outer.classifySide(inner) !== BACK) {
+              return false;
+            }
+          }
+        }
+        return true;
+      };
+  
+      Node.prototype.allPolygons = function() {
+        var _ref, _ref1;
+        return this.polygons.slice().concat(((_ref1 = this.front) != null ? _ref1.allPolygons() : void 0) || []).concat(((_ref = this.back) != null ? _ref.allPolygons() : void 0) || []);
+      };
+  
+      Node.prototype.invert = function() {
+        var _this = this;
+        return returning(this, function() {
+          var flipper, poly, _i, _j, _len, _len1, _ref, _ref1, _ref2;
+          _ref = _this.polygons;
+          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+            poly = _ref[_i];
+            poly.invert();
+          }
+          _ref1 = [_this.divider, _this.front, _this.back];
+          for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+            flipper = _ref1[_j];
+            if (flipper != null) {
+              flipper.invert();
+            }
+          }
+          return _ref2 = [_this.back, _this.front], _this.front = _ref2[0], _this.back = _ref2[1], _ref2;
+        });
+      };
+  
+      Node.prototype.clipPolygons = function(polygons) {
+        var back, front, poly, _i, _len;
+        if (!this.divider) {
+          return polygons.slice();
+        }
+        front = [];
+        back = [];
+        for (_i = 0, _len = polygons.length; _i < _len; _i++) {
+          poly = polygons[_i];
+          this.divider.subdivide(poly, front, back, front, back);
+        }
+        if (this.front) {
+          front = this.front.clipPolygons(front);
+        }
+        if (this.back) {
+          back = this.back.clipPolygons(back);
+        }
+        return front.concat(this.back ? back : []);
+      };
+  
+      Node.prototype.clipTo = function(node) {
+        var _this = this;
+        return returning(this, function() {
+          var _ref, _ref1;
+          _this.polygons = node.clipPolygons(_this.polygons);
+          if ((_ref = _this.front) != null) {
+            _ref.clipTo(node);
+          }
+          return (_ref1 = _this.back) != null ? _ref1.clipTo(node) : void 0;
+        });
+      };
+  
+      return Node;
+  
+    })();
+  
+  }).call(this);

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1289 - 0
js/webgl-utils.js


+ 384 - 0
model/data.js

@@ -0,0 +1,384 @@
+var rawData = {
+  "vertex":[
+   {"x":1.62,"y":0.501}
+  , {"x":1.62,"y":-4.124}
+  , {"x":8.295,"y":-4.124}
+  , {"x":10.67,"y":-10.924}
+  , {"x":-14.63,"y":-10.924}
+  , {"x":-14.63,"y":0.501}
+  , {"x":3.845,"y":7.026}
+  , {"x":1.17,"y":7.026}
+  , {"x":1.17,"y":0.501}
+  , {"x":-12.83,"y":0.501}
+  , {"x":-12.83,"y":11.626}
+  , {"x":3.845,"y":11.626}
+  , {"x":15.67,"y":5.301}
+  , {"x":15.67,"y":2.226}
+  , {"x":11.67,"y":2.226}
+  , {"x":3.845,"y":13.476}
+  , {"x":11.97,"y":13.476}
+  , {"x":11.97,"y":7.576}
+  , {"x":13.02,"y":7.576}
+  , {"x":13.02,"y":5.301}
+  , {"x":7.92,"y":0.076}
+  , {"x":7.92,"y":5.301}
+  , {"x":8.295,"y":-5.874}
+  , {"x":10.67,"y":-5.874}
+  , {"x":8.295,"y":0.076}
+  , {"x":10.67,"y":0.076}
+  , {"x":11.67,"y":0.076}
+  , {"x":3.845,"y":5.301}
+  , {"x":11.67,"y":5.301}
+  ] ,"wall":[
+    {"p1":0,"p2":1}
+  ,	{"p1":1,"p2":2}
+  ,	{"p1":2,"p2":22}
+  ,	{"p1":22,"p2":23}
+  ,	{"p1":23,"p2":3}
+  ,	{"p1":3,"p2":4}
+  ,	{"p1":4,"p2":5}
+  ,	{"p1":5,"p2":0}
+  
+  ,	{"p1":8,"p2":9}
+  ,	{"p1":9,"p2":10}
+  ,	{"p1":10,"p2":11}
+  ,	{"p1":11,"p2":6}
+  ,	{"p1":6,"p2":7}
+  ,	{"p1":7,"p2":8}
+  
+  ,	{"p1":27,"p2":15}
+  ,	{"p1":15,"p2":16}
+  ,	{"p1":16,"p2":17}
+  ,	{"p1":17,"p2":18}
+  ,	{"p1":18,"p2":19}
+  ,	{"p1":19,"p2":27}
+  
+  ,	{"p1":21,"p2":28}
+  ,	{"p1":28,"p2":26}
+  ,	{"p1":26,"p2":20}
+  ,	{"p1":20,"p2":21}
+  
+  ,	{"p1":28,"p2":12}
+  ,	{"p1":12,"p2":13}
+  ,	{"p1":13,"p2":14}
+  ,	{"p1":14,"p2":28}
+  
+  ,	{"p1":24,"p2":25}
+  ,	{"p1":25,"p2":23}
+  ,	{"p1":23,"p2":22}
+  ,	{"p1":22,"p2":24}
+  ] ,
+  "room":[
+    [0, 1, 22, 23, 3, 4, 5]
+  ,	[8, 9, 10, 11, 6, 7]
+  ,	[27, 15, 16, 17, 18, 19]
+  ,	[21, 28, 26, 20]
+  ,	[28, 12, 13, 14]
+  ,	[24, 25, 23, 22]
+  ]
+}
+
+var raw_cube = [
+  // {
+  //   point:[
+  //     {"x":15.67,"y":5.301}
+  //   ,	{"x":15.67,"y":2.226}
+  //   ,	{"x":3.845,"y":2.226}
+  //   ,	{"x":3.845,"y":13.476}
+  //   ,	{"x":11.97,"y":13.476}
+  //   ,	{"x":11.97,"y":7.576}
+  //   ,	{"x":13.02,"y":7.576}
+  //   ,	{"x":13.02,"y":5.301}
+  //   ]
+  // },
+  // {
+  //   point:[
+  //     {"x":3.845,"y":7.026}
+  //   ,	{"x":1.17,"y":7.026}
+  //   ,	{"x":1.17,"y":0.501}
+  //   ,	{"x":-12.83,"y":0.501}
+  //   ,	{"x":-12.83,"y":11.626}
+  //   ,	{"x":3.845,"y":11.626}
+  //   ]
+  // },
+  // {
+  //   point:[
+  // {"x":1.62,"y":0.501}
+  // ,	{"x":1.62,"y":-4.124}
+  // ,	{"x":8.295,"y":-4.124}
+  // ,	{"x":8.295,"y":-10.924}
+  // ,	{"x":-14.63,"y":-10.924}
+  // ,	{"x":-14.63,"y":0.501}
+  //   ]
+  // },
+  
+  {
+    campos:[
+      {
+        x:-0.1,
+        y:-0.25
+      }
+    ],
+    point:[
+      {"x":1.62,"y":0.501}
+      ,	{"x":1.62,"y":-4.124}
+      ,	{"x":8.295,"y":-4.124}
+      ,	{"x":8.295,"y":-5.874}
+      ,	{"x":10.67,"y":-5.874}
+      ,	{"x":10.67,"y":-10.924}
+      ,	{"x":-14.63,"y":-10.924}
+      ,{"x":-14.63,"y":0.501}
+    ]
+  },
+  {
+    campos:[
+      {
+        x:-0.1,
+        y:-0.25
+      }
+    ],
+    point:[
+      {"x":1.17,"y":0.501}
+      , {"x":-12.83,"y":0.501}
+      ,	{"x":-12.83,"y":11.626}
+      ,	{"x":3.845,"y":11.626}
+      , {"x":3.845,"y":7.026}
+      , {"x":1.17,"y":7.026}
+    ]
+  },
+  {
+    campos:[],
+    point:[
+      {"x":3.845,"y":5.301}
+      ,	{"x":3.845,"y":13.476}
+      ,	{"x":11.97,"y":13.476}
+      , {"x":11.97,"y":7.576}
+      , {"x":13.02,"y":7.576}
+      ,	{"x":13.02,"y":5.301}
+    ]
+  },
+  {
+    campos:[],
+    point:[
+      {"x":7.92,"y":5.301}
+      ,{"x":11.67,"y":5.301}
+      ,{"x":11.67,"y":0.076}
+      ,{"x":7.92,"y":0.076}
+    ]
+  },
+  {
+    campos:[],
+    point:[
+      {"x":11.67,"y":5.301}
+      ,	{"x":15.67,"y":5.301}
+      ,	{"x":15.67,"y":2.226}
+      ,	{"x":11.67,"y":2.226}
+    ]
+  },
+  {
+    campos:[],
+    point:[
+      {"x":8.300,"y":0.076}
+      , {"x":10.67,"y":0.076}
+      , {"x":10.67,"y":-5.874}
+      , {"x":8.300,"y":-5.874}
+    ]
+  },
+]
+
+var f_cube = [
+  {
+  roomId:0,
+  campos:[
+  //   {
+  //   x:0.4,
+  //   y:0.35
+  // }
+],
+  points:[
+    {
+      id:0,
+      data:[-0.1, -0.1],
+    },
+    {
+      id:1,
+      data:[0.2, -0.1],
+    },
+    {
+      id:2,
+      data:[0.2, 0.1],
+    },
+    {
+      id:3,
+      data:[0.2, 0.1],
+    },
+    {
+      id:4,
+      data:[0.2, 0.2],
+    },
+    {
+      id:5,
+      data:[-0.1, 0.2],
+    }
+  ]
+}
+,{
+  roomId:1,
+  campos:[
+  //   {
+  //   x:0.55,
+  //   y:0.45
+  // },{
+  //   x:0.58,
+  //   y:0.48
+  // }
+],
+  points:[
+    {
+      id:6,
+      data:[0.2, 0.1],
+    },
+    {
+      id:7,
+      data:[0.5, 0.1],
+    },
+    {
+      id:8,
+      data:[0.5, 0.5],
+    },
+    {
+      id:9,
+      data:[0.2, 0.5],
+    }
+  ]
+}
+// ,{
+//   roomId:2,
+//   campos:[{
+//     x:0.25,
+//     y:0.76
+//   },{
+//     x:0.38,
+//     y:0.58
+//   }],
+//   points:[
+//     {
+//       id:8,
+//       data:[0.2, 0.5],
+//     },
+//     {
+//       id:9,
+//       data:[0.5, 0.5],
+//     },
+//     {
+//       id:10,
+//       data:[0.5, 0.8],
+//     },
+//     {
+//       id:11,
+//       data:[0.2, 0.8],
+//     }
+//   ]
+// }
+]
+
+var f_cubeLine = [
+  {
+    room:0,
+    line:[{
+      '_3d_id':0,
+      '_3d_point1':{
+        'point_id':0,
+        'data':[0.3, 0.3]
+      },
+      '_3d_point2':{
+        'point_id':1,
+        'data':[0.5, 0.3]
+      },
+    },{
+      '_3d_id':1,
+      '_3d_point1':{
+        'point_id':1,
+        'data':[0.5, 0.3]
+      },
+      '_3d_point2':{
+        'point_id':2,
+        'data':[0.5, 0.4]
+      },
+    },{
+      '_3d_id': 2,
+      '_3d_point1':{
+        'point_id':3,
+        'data':[0.5, 0.4]
+      },
+      '_3d_point2':{
+        'point_id':4,
+        'data':[0.5, 0.5]
+      },
+    },{
+      '_3d_id': 3,
+      '_3d_point1':{
+        'point_id':4,
+        'data':[0.5, 0.5]
+      },
+      '_3d_point2':{
+        'point_id':5,
+        'data':[0.3, 0.5]
+      },
+    },{
+      '_3d_id': 4,
+      '_3d_point1':{
+        'point_id':5,
+        'data':[0.3, 0.5]
+      },
+      '_3d_point2':{
+        'point_id':0,
+        'data':[0.3, 0.3]
+      },
+    }]
+  },
+  // {
+  //   room:1,
+  //   line:[{
+  //     '_3d_id':5,
+  //     '_3d_point1':{
+  //       'point_id':2,
+  //       'data':[0.5, 0.4]
+  //     },
+  //     '_3d_point2':{
+  //       'point_id':5,
+  //       'data':[0.6, 0.4]
+  //     },
+  //   },{
+  //     '_3d_id':6,
+  //     '_3d_point1':{
+  //       'point_id':5,
+  //       'data':[0.6, 0.4]
+  //     },
+  //     '_3d_point2':{
+  //       'point_id':6,
+  //       'data':[0.6, 0.5]
+  //     },
+  //   },{
+  //     '_3d_id': 7,
+  //     '_3d_point1':{
+  //       'point_id':6,
+  //       'data':[0.6, 0.5]
+  //     },
+  //     '_3d_point2':{
+  //       'point_id':3,
+  //       'data':[0.5, 0.5]
+  //     },
+  //   },{
+  //     '_3d_id': 2,
+  //     '_3d_point1':{
+  //       'point_id':3,
+  //       'data':[0.5, 0.5]
+  //     },
+  //     '_3d_point2':{
+  //       'point_id':2,
+  //       'data':[0.5, 0.4]
+  //     },
+  //   }]
+  // }
+]

+ 614 - 0
utils/utils.js

@@ -0,0 +1,614 @@
+// 归一化
+function normalize(xyz) {
+  var _mo_ = Math.sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2]);
+  var x = xyz[0] / _mo_;
+  var y = xyz[1] / _mo_;
+  var z = xyz[2] / _mo_;
+
+  return [x, y, z];
+}
+
+// 向量归一化
+function normalizeV(xy) {
+  
+  var _mo_ = Math.sqrt(xy[0] * xy[0] + xy[1] * xy[1]);
+  var x = xy[0] / _mo_;
+  var y = xy[1] / _mo_;
+  // var z = xyz[2] / _mo_;
+
+  return [x, y];
+}
+/**
+ * 画线
+ * @param {*} arr
+ * @param {*} context
+ * @param {*} color
+ * @param {*} w
+ * @param {*} h
+ * @param {*} isClear
+ */
+function circularLine(arr, context, color,cam_pos, w, h, isClear) {
+
+  if (isClear) {
+    context.clearRect(0, 0, w, h);
+    // context.translate(w/2, h/2);
+    
+  }
+  
+  if (cam_pos.length>0) {
+
+    for (let i = 0; i < cam_pos.length; i++) {
+      const element = cam_pos[i];
+      draw_point(context,element,w,h)
+    }
+  }
+
+  context.strokeStyle = color;
+  context.lineWidth = 4;
+
+  context.beginPath();
+  context.moveTo(arr[0][0] * w/2, arr[0][1] * h/2);
+
+  for (let i = 0; i < arr.length; i++) {
+    context.lineTo(arr[i][0] * w/2, arr[i][1] * h/2);
+  }
+  context.closePath();
+  context.stroke();
+}
+
+/**
+ * 选择线
+ * @param {*} current
+ * @param {*} cubeLine
+ * @param {*} w
+ * @param {*} h
+ */
+function selectLine(current, cubeLine, w, h) {
+  for (let i = 0; i < cubeLine.length; i++) {
+    for (let j = 0; j < cubeLine[i].line.length; j++) {
+      if (isInLine(current, cubeLine[i].line[j], w, h)) {
+        // return current,arr[i]
+        
+        return [cubeLine[i].line[j],cubeLine[i]['room']];
+      }
+    }
+  }
+  return [];
+}
+
+function _getWhichPoint(line, current, verticalLine, cubeLine, cube,w,h) {
+  current = [current[0]/w*2,current[1]/h*2]
+  var direction=0;
+  if (verticalLine[0]) {
+    direction = 0
+  }
+  if (verticalLine[1]) {
+    direction = 1
+  }
+  for (let i = 0; i < cube.length; i++) {
+    for (let j = 0; j < cube[i].points.length; j++) {
+      if (
+        cube[i].points[j]["id"] === line["_3d_point1"]["point_id"] ||
+        cube[i].points[j]["id"] === line["_3d_point2"]["point_id"]
+      ) {
+        
+        // cube[i].points[j]['data'][0] = current[0]
+        cube[i].points[j]["data"][direction] = current[direction];
+      }
+    }
+  }
+  // for (let i = 0; i < cubeLine.length; i++) {
+  //   for (let j = 0; j < cubeLine[i].line.length; j++) {
+  //     if (
+  //       cubeLine[i].line[j]["_3d_point1"]["point_id"] ===
+  //         line["_3d_point1"]["point_id"] ||
+  //       cubeLine[i].line[j]["_3d_point1"]["point_id"] ===
+  //         line["_3d_point2"]["point_id"]
+  //     ) {
+  //       cubeLine[i].line[j]["_3d_point1"]["data"][direction] = current[direction];
+  //     }
+  //     if (
+  //       cubeLine[i].line[j]["_3d_point2"]["point_id"] ===
+  //         line["_3d_point1"]["point_id"] ||
+  //       cubeLine[i].line[j]["_3d_point2"]["point_id"] ===
+  //         line["_3d_point2"]["point_id"]
+  //     ) {
+  //       cubeLine[i].line[j]["_3d_point2"]["data"][direction] = current[direction];
+  //     }
+  //   }
+  // }
+
+  return [cubeLine, cube];
+}
+
+/**
+ * 判断是否在线上
+ * @param {*} current
+ * @param {*} arr
+ * @param {*} w
+ * @param {*} h
+ */
+function isInLine(current, arr, w, h) {
+  var accuay = 2;
+
+  var minX = arr["_3d_point1"]["data"][0],
+    minY = arr["_3d_point1"]["data"][1],
+    maxX = arr["_3d_point1"]["data"][0],
+    maxY = arr["_3d_point1"]["data"][1];
+
+  minX = Math.min(minX, arr["_3d_point1"]["data"][0]);
+  maxX = Math.max(maxX, arr["_3d_point1"]["data"][0]);
+  minY = Math.min(minY, arr["_3d_point1"]["data"][1]);
+  maxY = Math.max(maxY, arr["_3d_point1"]["data"][1]);
+
+  minX = Math.min(minX, arr["_3d_point2"]["data"][0]);
+  maxX = Math.max(maxX, arr["_3d_point2"]["data"][0]);
+  minY = Math.min(minY, arr["_3d_point2"]["data"][1]);
+  maxY = Math.max(maxY, arr["_3d_point2"]["data"][1]);
+
+  return (
+    current[0] >= minX * w/2 - accuay &&
+    current[0] <= maxX * w/2 + accuay &&
+    current[1] >= minY * h/2 - accuay &&
+    current[1] <= maxY * h/2 + accuay
+  );
+}
+
+/**
+ * 计算向量的垂直向量
+ * @param {向量} arr
+ */
+function verticalLine(arr) {
+  if (arr.length !== 0) {
+    var x, y;
+
+    y = Math.sqrt(1 / ((arr[1] * arr[1]) / (arr[0] * arr[0]) + 1));
+    x = Math.sqrt(1 - y * y);
+    return [x, y];
+  }
+  return [];
+}
+
+
+function getPointFromLine(line) {
+  var ret = []
+  var temp 
+  for (let i = 0; i < line.length; i++) {
+    temp  = []
+    for (let j = 0; j < line[i].line.length; j++) {
+      temp.push(line[i].line[j]['_3d_point2']['data'])
+    }
+    ret.push(temp)
+  }
+}
+
+/**
+ * 根据顶点数据获取向量数据
+ * @param {顶点数据} cube 
+ */
+function changeData(cube) {
+  let ret = []
+  let item = [] ;
+  for (let i = 0; i < cube.length; i++) {
+    item = []
+    for (let j = 0; j < cube[i].points.length; j++) {
+      if (j+1<cube[i].points.length) {
+        item.push(
+          {
+            '_3d_id':j,
+            '_3d_point1':{
+              'point_id':cube[i].points[j]['id'],
+              'data':cube[i].points[j]['data']
+            },
+            '_3d_point2':{
+              'point_id':cube[i].points[j+1]['id'],
+              'data':cube[i].points[j+1]['data']
+            },
+            '_vector':[cube[i].points[j+1]['data'][0]-cube[i].points[j]['data'][0],cube[i].points[j+1]['data'][1]-cube[i].points[j]['data'][1]]
+          }
+        ) 
+      }
+      if (j+1==cube[i].points.length) {
+        item.push(
+          {
+            '_3d_id':j,
+            '_3d_point1':{
+              'point_id':cube[i].points[j]['id'],
+              'data':cube[i].points[j]['data']
+            },
+            '_3d_point2':{
+              'point_id':cube[i].points[0]['id'],
+              'data':cube[i].points[0]['data']
+            },
+            '_vector':[cube[i].points[0]['data'][0]-cube[i].points[j]['data'][0],cube[i].points[0]['data'][1]-cube[i].points[j]['data'][1]]
+          }
+        ) 
+      }
+      
+    }
+    ret.push({
+      room:i,
+      line:item
+    })
+  }
+  return ret
+}
+
+
+/**
+ * 判断点是否在墙体上
+ * @param {鼠标当前坐标点} current
+ * @param {墙体线段} lineArr
+ * @param {水平缩放倍数} scaleTime
+ * @param {垂直缩放倍数} scaleTimeH
+ */
+function isInFace(current, test, scaleTime, scaleTimeH) {
+  var fixArr = [];
+  var selectYArr = [];
+  var minX = 1,
+      minY = 1,
+      maxX = 0,
+      maxY = 0;
+  for (var i = 0; i < test.length; i++) {
+      for (var j = 0; j < test[i].line.length; j++) {
+          minX = Math.min(minX, test[i].line[j][0]);
+          // minY = Math.min(minY, test[i].line[j][1]);
+          maxX = Math.max(maxX, test[i].line[j][0]);
+          // maxY = Math.max(maxY, test[i].line[j][1]);
+          fixArr.push({
+              id: parseFloat(test[i].line[j][0]).toFixed(1),
+              y: test[i].line[j][1]
+          });
+      }
+  }
+  for (var i = 0; i < fixArr.length; i++) {
+      if (fixArr[i].id == parseFloat(current[0] / scaleTime).toFixed(1)) {
+          selectYArr.push(fixArr[i].y);
+      }
+  }
+  for (var i = 0; i < selectYArr.length; i++) {
+      minY = Math.min(minY, selectYArr[i]);
+      maxY = Math.max(maxY, selectYArr[i]);
+  }
+  return (
+      current[0] > minX * scaleTime &&
+      current[0] < maxX * scaleTime &&
+      current[1] > minY * scaleTimeH &&
+      current[1] < maxY * scaleTimeH
+  );
+}
+
+/**
+ * 计算两线段的交点
+ * @param {线段一端点} a 
+ * @param {线段一端点} b 
+ * @param {线段二端点} c 
+ * @param {线段二端点} d 
+ */
+function segmentsIntr(a, b, c, d){  
+  
+  // 三角形abc 面积的2倍  
+  var area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);  
+
+  // 三角形abd 面积的2倍  
+  var area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x);   
+
+  // 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,本例当作不相交处理);  
+  if ( area_abc*area_abd>=0 ) {  
+      return false;  
+  }  
+
+  // 三角形cda 面积的2倍  
+  var area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x);  
+  // 三角形cdb 面积的2倍  
+  // 注意: 这里有一个小优化.不需要再用公式计算面积,而是通过已知的三个面积加减得出.  
+  var area_cdb = area_cda + area_abc - area_abd ;  
+  if (  area_cda * area_cdb >= 0 ) {  
+      return false;  
+  }  
+
+  //计算交点坐标  
+  var t = area_cda / ( area_abd- area_abc );  
+  var dx= t*(b.x - a.x),  
+      dy= t*(b.y - a.y);  
+  return toDecimal({ x: a.x + dx , y: a.y + dy });  
+
+}  
+
+/**
+ * 判断一个点是否在面上
+ * @param {点} point 
+ * @param {面} vs 
+ */
+function pointInside(point, vs) {
+  // ray-casting algorithm based on
+  // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
+  
+  
+  let temp = []
+  for (let i = 0; i < vs.points.length; i++) {
+    temp.push(vs.points[i]['data'])
+  }
+  var x = point[0], y = point[1];
+  
+  var inside = false;
+  for (var i = 0, j = temp.length - 1; i < temp.length; j = i++) {
+      var xi = temp[i][0], yi = temp[i][1];
+      var xj = temp[j][0], yj = temp[j][1];
+      
+      var intersect = ((yi > y) != (yj > y))
+          && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
+      if (intersect) inside = !inside;
+  }
+  
+  return inside;
+};
+
+function cubePointInFace(selectRoomId,cube,cubeLine) {
+
+  let selectCube
+  let ret = []
+  for (let i = 0; i < cube.length; i++) {
+    if (cube[i]['roomId']==selectRoomId) {
+      selectCube = cube[i]
+      for (let j = 0; j < cube[i].points.length; j++) {
+        pointInside(cube[i].points[j]['data'],f_cube[0])
+      }
+    }
+  }
+  for (let i = 0; i < cube.length; i++) {
+    if (cube[i]['roomId']!==selectRoomId) {
+      for (let j = 0; j < selectCube.points.length; j++) {
+        if (pointInside(selectCube.points[j]['data'],cube[i])) {
+          ret.push(selectCube.points[j]['data'])
+        }
+      }
+    }
+  }
+
+  return ret
+}
+
+/**
+ * 找出相交线的交点
+ * @param {线段数组} cubeLine 
+ */
+function intersect(selectRoomId,cubeLine) {
+ 
+  //获取选中面的每条线段
+  let selectCubeVector = [];
+  let ret = []
+
+  for (let i = 0; i < cubeLine.length; i++) {
+    if (cubeLine[i]['room']===selectRoomId) {
+      for (let j = 0; j < cubeLine[i].line.length; j++) {
+        // segmentsIntr(cubeLine[i].line)
+        // cubeLine[i].line[j]['_vector']
+        selectCubeVector.push([{
+          x:cubeLine[i].line[j]['_3d_point1']['data'][0],
+          y:cubeLine[i].line[j]['_3d_point1']['data'][1],
+        },{
+          x:cubeLine[i].line[j]['_3d_point2']['data'][0],
+          y:cubeLine[i].line[j]['_3d_point2']['data'][1],
+        }])
+      }
+    }
+  }
+  
+  for (let i = 0; i < cubeLine.length; i++) {
+    //未选中的房间
+    if (cubeLine[i]['room']!==selectRoomId) {
+      for (let k = 0; k < selectCubeVector.length; k++) {
+        for (let l = 0; l < cubeLine[i].line.length; l++) {
+          let item = [{
+            x:cubeLine[i].line[l]['_3d_point1']['data'][0],
+            y:cubeLine[i].line[l]['_3d_point1']['data'][1],
+          },{
+            x:cubeLine[i].line[l]['_3d_point2']['data'][0],
+            y:cubeLine[i].line[l]['_3d_point2']['data'][1],
+          }]
+          if ((segmentsIntr(selectCubeVector[k][0],selectCubeVector[k][1],item[0],item[1]))) {
+            let tempItem = segmentsIntr(selectCubeVector[k][0],selectCubeVector[k][1],item[0],item[1])
+            //判断该交点不是面的顶点
+            if ((selectCubeVector[k][0]['x']==tempItem['x'] && selectCubeVector[k][0]['y']==tempItem['y'])
+            || (selectCubeVector[k][1]['x']==tempItem['x'] && selectCubeVector[k][1]['y']==tempItem['y'])
+            || (item[0]['x']==tempItem['x']&&item[0]['y']==tempItem['y'])
+            || (item[1]['x']==tempItem['x']&&item[1]['y']==tempItem['y'])) {
+            }
+            else{
+              ret.push(segmentsIntr(selectCubeVector[k][0],selectCubeVector[k][1],item[0],item[1]))
+            }
+          }
+        }
+      }
+    }
+  }
+  return ret
+}
+
+function pushInNewCube(inFaceArr,intersectArr,cube) {
+  let count = 0;
+  let temp = []
+  let item 
+  for (let i = 0; i < cube.length; i++) {
+    item = []
+    for (let j = 0; j < cube[i].points.length; j++) {
+
+      let ele = cube[i].points[j];
+      item.push(ele['data'])
+    }
+    for (let l = 0; l < intersectArr.length; l++) {
+      let el = intersectArr[l];
+      item.push([el.x,el.y])
+    }
+    for (let k = 0; k < inFaceArr.length; k++) {
+      let element = inFaceArr[k];
+      item.push(element)
+    }
+    temp.push(item)
+  }
+
+  return changeCube(temp)
+}
+
+function changeCube(arr) {
+  let item;
+  let ret = [];
+  let count = 0
+  for (let i = 0; i < arr.length; i++) {
+    const element = arr[i];
+    item = []
+    for (let j = 0; j < arr[i].length; j++) {
+      count++
+      item.push({
+        id:count,
+        data:arr[i][j],
+      })
+    }
+    ret.push({
+      roomId:i,
+      points:item
+    })
+  }
+  return ret
+}
+
+function draw_point(ctx, point,w,h) {
+  ctx.fillStyle="#00cc00";
+  ctx.fillRect(point.x*w-4,point.y*h-4,4,4);
+}
+
+/**
+ * 判断选择的cube相机位置是否在面内
+ * @param {选择的面id} selectFaceId 
+ * @param {*} cube 
+ */
+function pointInSelectFace(selectFaceId,cube) {
+  for (let i = 0; i < cube.length; i++) {
+    const element = cube[i];
+    if (element['roomId'] ===selectFaceId) {
+      for (let j = 0; j < element['campos'].length; j++) {
+        if (!pointInside([element['campos'][j].x,element['campos'][j].y],element)) {
+          return false
+        }
+      }
+    }
+  }
+  return true
+}
+
+/**
+ * 判断交点是否为顶点
+ * @param {交点坐标} intersectArr 
+ * @param {顶点数组} cube 
+ */
+function interectNotInPoint(intersectArr,cube) {
+  
+  if (intersectArr.length>0) {
+    for (let k = 0; k < intersectArr.length; k++) {
+      for (let i = 0; i < cube.length; i++) {
+        let element = cube[i];
+        for (let j = 0; j < element['points'].length; j++) {
+          let ele = element['points'][j]['data'];
+          if (intersectArr[k].x==ele[0]&&intersectArr[k].y==ele[1]) {
+            return true
+          }
+        }
+      }
+    }
+    return false
+  }
+  return false
+  
+}
+
+/**
+ * 
+ * @param {原生数据} raw_cube 
+ */
+function rawToCube(raw_cube,w,h) {
+  let item;
+  let count = 0
+  let ret = [];
+  for (let i = 0; i < raw_cube.length; i++) {
+
+    item = {
+      roomId:i,
+      campos:[
+        {
+          x:-0.1,
+          y:0.25
+        }
+      ],
+      points:[]
+    }
+    for (let j = 0; j < raw_cube[i].point.length; j++) {
+      count++
+      let element = raw_cube[i].point[j];
+      let norV = fixRawData([element['x'],element['y']],w,h)
+      item.points.push({
+        id:count,
+        data:norV
+      })
+    }
+  ret.push(item)
+
+  }
+
+  return ret
+}
+/**
+ * 将鼠标点击起始点移到中心
+ * @param {鼠标点} xy 
+ */
+function calculateXY(xy,w,h) {
+  return [xy[0]-w/2,xy[1]-h/2]
+  
+}
+
+function fixRawData(xy,w,h) {
+  return [xy[0]*50 / w,xy[1]*50 / h]
+}
+
+/**
+ * 四舍五入保留7位小数
+ * @param {数组} arr 
+ */
+function toDecimal(arr) {
+  var xf = parseFloat(arr.x);
+  var yf = parseFloat(arr.y);
+
+  if (isNaN(xf) || isNaN(yf)) {
+      return;
+  }
+  tx = Math.round(xf * 10e6) / 10e6;
+  ty = Math.round(yf * 10e6) / 10e6;
+
+  return {x:tx,y:ty};
+}
+
+
+function calculateRawData(data) {
+  let {vertex,room} = data
+  let item
+  let ret = []
+    for (let i = 0; i < room.length; i++) {
+      let element = room[i];
+      item = {
+        point:[]
+      }
+      for (let j = 0; j < element.length; j++) {
+        let ele = element[j];
+          for (let a = 0; a < vertex.length; a++) {
+            if (a === ele) {
+              item.point.push(vertex[a])
+            }
+          }
+      }
+      ret.push(item)
+
+    }
+  
+    return ret
+
+}