├── LICENSE
├── README.md
├── demo
├── assets
│ ├── LeePerrySmith.obj
│ ├── stroke.png
│ ├── worldHigh.svg
│ └── worldLow.svg
├── birds.html
├── css
│ └── main.css
├── graph.html
├── index.html
├── js
│ ├── Bird.js
│ ├── ImprovedNoise.js
│ ├── Maf.js
│ ├── OBJLoader.js
│ ├── OrbitControls.js
│ ├── THREE.ConstantSpline.js
│ ├── dat.gui.min.js
│ ├── main-graph.js
│ ├── main-shape.js
│ ├── main-spinner.js
│ ├── main-svg.js
│ ├── main.js
│ ├── pathseg.js
│ └── three.min.js
├── shape.html
├── spinner.html
└── svg.html
├── package.json
├── screenshots
├── birds.jpg
├── demo.jpg
├── graph.jpg
├── shape.jpg
├── spinner.jpg
└── svg.jpg
└── src
└── THREE.MeshLine.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Jaume Sanchez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MeshLine
2 | Mesh replacement for ```THREE.Line```
3 |
4 | Instead of using GL_LINE, it uses a strip of triangles billboarded. Some examples:
5 |
6 | [](http://spite.github.io/THREE.MeshLine/demo/index.html)
7 | [](http://spite.github.io/THREE.MeshLine/demo/graph.html)
8 | [](http://spite.github.io/THREE.MeshLine/demo/spinner.html)
9 | [](http://spite.github.io/THREE.MeshLine/demo/svg.html)
10 | [](http://spite.github.io/THREE.MeshLine/demo/shape.html)
11 | [](http://spite.github.io/THREE.MeshLine/demo/birds.html)
12 |
13 | * [Demo](http://spite.github.io/THREE.MeshLine/demo/index.html): play with the different settings of materials
14 | * [Graph](http://spite.github.io/THREE.MeshLine/demo/graph.html): example of using ```MeshLine``` to plot graphs
15 | * [Spinner](http://spite.github.io/THREE.MeshLine/demo/spinner.html): example of dynamic ```MeshLine``` with texture
16 | * [SVG](http://spite.github.io/THREE.MeshLine/demo/svg.html): example of ```MeshLine``` rendering SVG Paths
17 | * [Shape](http://spite.github.io/THREE.MeshLine/demo/shape.html): example of ```MeshLine``` created from a mesh
18 | * [Birds](http://spite.github.io/THREE.MeshLine/demo/birds.html): example of ```MeshLine.advance()``` by @caramelcode (Jared Sprague) and @mwcz (Michael Clayton)
19 |
20 | ### How to use ####
21 |
22 | * Include script
23 | * Create an array of 3D coordinates
24 | * Create a MeshLine and assign the points
25 | * Create a MeshLineMaterial
26 | * Use MeshLine and MeshLineMaterial to create a THREE.Mesh
27 |
28 | #### Include the script ####
29 |
30 | Include script after THREE is included
31 | ```js
32 |
33 | ```
34 | or use npm to install it
35 | ```
36 | npm i three.meshline
37 | ```
38 | and include it in your code (don't forget to require three.js)
39 | ```js
40 | const THREE = require('three');
41 | const MeshLine = require('three.meshline').MeshLine;
42 | const MeshLineMaterial = require('three.meshline').MeshLineMaterial;
43 | const MeshLineRaycast = require('three.meshline').MeshLineRaycast;
44 | ```
45 | or
46 | ```js
47 | import * as THREE from 'three';
48 | import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline';
49 | ```
50 |
51 | ##### Create an array of 3D coordinates #####
52 |
53 | First, create the list of numbers that will define the 3D points for the line.
54 |
55 | ```js
56 | const points = [];
57 | for (let j = 0; j < Math.PI; j += (2 * Math.PI) / 100) {
58 | points.push(Math.cos(j), Math.sin(j), 0);
59 | }
60 | ```
61 |
62 | ```MeshLine``` also accepts a ```BufferGeometry``` looking up the vertices in it.
63 |
64 | ```js
65 | const points = [];
66 | for (let j = 0; j < Math.PI; j += 2 * Math.PI / 100) {
67 | points.push(new THREE.Vector3(Math.cos(j), Math.sin(j), 0));
68 | }
69 | const geometry = new THREE.BufferGeometry().setFromPoints(points);
70 | const line = new MeshLine();
71 | line.setGeometry(geometry);
72 | ```
73 |
74 | ##### Create a MeshLine and assign the points #####
75 |
76 | Once you have that, you can create a new `MeshLine`, and call `.setPoints()` passing the list of points.
77 |
78 | ```js
79 | const line = new MeshLine();
80 | line.setPoints(points);
81 | ```
82 |
83 | Note: `.setPoints` accepts a second parameter, which is a function to define the width in each point along the line. By default that value is 1, making the line width 1 \* lineWidth in the material.
84 |
85 | ```js
86 | // p is a decimal percentage of the number of points
87 | // ie. point 200 of 250 points, p = 0.8
88 | line.setPoints(geometry, p => 2); // makes width 2 * lineWidth
89 | line.setPoints(geometry, p => 1 - p); // makes width taper
90 | line.setPoints(geometry, p => 2 + Math.sin(50 * p)); // makes width sinusoidal
91 | ```
92 |
93 | ##### Create a MeshLineMaterial #####
94 |
95 | A ```MeshLine``` needs a ```MeshLineMaterial```:
96 |
97 | ```js
98 | const material = new MeshLineMaterial(OPTIONS);
99 | ```
100 |
101 | By default it's a white material of width 1 unit.
102 |
103 | ```MeshLineMaterial``` has several attributes to control the appereance of the ```MeshLine```:
104 |
105 | * ```map``` - a ```THREE.Texture``` to paint along the line (requires ```useMap``` set to true)
106 | * ```useMap``` - tells the material to use ```map``` (0 - solid color, 1 use texture)
107 | * ```alphaMap``` - a ```THREE.Texture``` to use as alpha along the line (requires ```useAlphaMap``` set to true)
108 | * ```useAlphaMap``` - tells the material to use ```alphaMap``` (0 - no alpha, 1 modulate alpha)
109 | * ```repeat``` - THREE.Vector2 to define the texture tiling (applies to map and alphaMap - MIGHT CHANGE IN THE FUTURE)
110 | * ```color``` - ```THREE.Color``` to paint the line width, or tint the texture with
111 | * ```opacity``` - alpha value from 0 to 1 (requires ```transparent``` set to ```true```)
112 | * ```alphaTest``` - cutoff value from 0 to 1
113 | * ```dashArray``` - the length and space between dashes. (0 - no dash)
114 | * ```dashOffset``` - defines the location where the dash will begin. Ideal to animate the line.
115 | * ```dashRatio``` - defines the ratio between that is visible or not (0 - more visible, 1 - more invisible).
116 | * ```resolution``` - ```THREE.Vector2``` specifying the canvas size (REQUIRED)
117 | * ```sizeAttenuation``` - makes the line width constant regardless distance (1 unit is 1px on screen) (0 - attenuate, 1 - don't attenuate)
118 | * ```lineWidth``` - float defining width (if ```sizeAttenuation``` is true, it's world units; else is screen pixels)
119 |
120 | If you're rendering transparent lines or using a texture with alpha map, you should set ```depthTest``` to ```false```, ```transparent``` to ```true``` and ```blending``` to an appropriate blending mode, or use ```alphaTest```.
121 |
122 | ##### Use MeshLine and MeshLineMaterial to create a THREE.Mesh #####
123 |
124 | Finally, we create a mesh and add it to the scene:
125 |
126 | ```js
127 | const mesh = new THREE.Mesh(line, material);
128 | scene.add(mesh);
129 | ```
130 |
131 | You can optionally add raycast support with the following.
132 |
133 | ```js
134 | mesh.raycast = MeshLineRaycast;
135 | ```
136 |
137 | ### Declarative use ###
138 |
139 | THREE.meshline can be used declaritively. This is how it would look like in [react-three-fiber](https://github.com/drcmda/react-three-fiber). You can try it live [here](https://codesandbox.io/s/react-three-fiber-three.meshline-example-vl221).
140 |
141 |
142 |
143 |
144 |
145 |
146 | ```jsx
147 | import { extend, Canvas } from 'react-three-fiber'
148 | import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline'
149 |
150 | extend({ MeshLine, MeshLineMaterial })
151 |
152 | function Line({ points, width, color }) {
153 | return (
154 |
168 | )
169 | }
170 | ```
171 |
172 | Dynamic line widths can be set along each point using the `widthCallback` prop.
173 | ```jsx
174 | pointWidth * Math.random()} />
175 | ```
176 |
177 | ### TODO ###
178 |
179 | * Better miters
180 | * Proper sizes
181 |
182 | ### Support ###
183 |
184 | Tested successfully on
185 |
186 | * Chrome OSX, Windows, Android
187 | * Firefox OSX, Windows, Anroid
188 | * Safari OSX, iOS
189 | * Internet Explorer 11 (SVG and Shape demo won't work because they use Promises)
190 | * Opera OSX, Windows
191 |
192 | ### References ###
193 |
194 | * [Drawing lines is hard](http://mattdesl.svbtle.com/drawing-lines-is-hard)
195 | * [WebGL rendering of solid trails](http://codeflow.org/entries/2012/aug/05/webgl-rendering-of-solid-trails/)
196 | * [Drawing Antialiased Lines with OpenGL](https://www.mapbox.com/blog/drawing-antialiased-lines/)
197 |
198 | #### License ####
199 |
200 | MIT licensed
201 |
202 | Copyright (C) 2015-2016 Jaume Sanchez Elias, http://www.clicktorelease.com
203 |
--------------------------------------------------------------------------------
/demo/assets/stroke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/THREE.MeshLine/b49ae600898a88bdb3486a3674d5887ca1b61ff6/demo/assets/stroke.png
--------------------------------------------------------------------------------
/demo/birds.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | THREE.MeshLine - THREE.MeshLine advance() Demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
THREE.MeshLine - advance() Demo
16 |
Efficient and fast line updating. Move your mouse around.
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/demo/js/Bird.js:
--------------------------------------------------------------------------------
1 | var Bird = function () {
2 |
3 | var scope = this;
4 |
5 | THREE.Geometry.call( this );
6 |
7 | v( 5, 0, 0 );
8 | v( - 5, - 2, 1 );
9 | v( - 5, 0, 0 );
10 | v( - 5, - 2, - 1 );
11 |
12 | v( 0, 2, - 6 );
13 | v( 0, 2, 6 );
14 | v( 2, 0, 0 );
15 | v( - 3, 0, 0 );
16 |
17 | f3( 0, 2, 1 );
18 | // f3( 0, 3, 2 );
19 |
20 | f3( 4, 7, 6 );
21 | f3( 5, 6, 7 );
22 |
23 | this.computeFaceNormals();
24 |
25 | function v( x, y, z ) {
26 |
27 | scope.vertices.push( new THREE.Vector3( x, y, z ) );
28 |
29 | }
30 |
31 | function f3( a, b, c ) {
32 |
33 | scope.faces.push( new THREE.Face3( a, b, c ) );
34 |
35 | }
36 |
37 | }
38 |
39 | Bird.prototype = Object.create( THREE.Geometry.prototype );
40 | Bird.prototype.constructor = Bird;
41 |
--------------------------------------------------------------------------------
/demo/js/ImprovedNoise.js:
--------------------------------------------------------------------------------
1 | // http://mrl.nyu.edu/~perlin/noise/
2 |
3 | var ImprovedNoise = function () {
4 |
5 | var p = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,
6 | 23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,
7 | 174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,
8 | 133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,
9 | 89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,
10 | 202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,
11 | 248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,
12 | 178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,
13 | 14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,
14 | 93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180];
15 |
16 | for (var i=0; i < 256 ; i++) {
17 |
18 | p[256+i] = p[i];
19 |
20 | }
21 |
22 | function fade(t) {
23 |
24 | return t * t * t * (t * (t * 6 - 15) + 10);
25 |
26 | }
27 |
28 | function lerp(t, a, b) {
29 |
30 | return a + t * (b - a);
31 |
32 | }
33 |
34 | function grad(hash, x, y, z) {
35 |
36 | var h = hash & 15;
37 | var u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
38 | return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
39 |
40 | }
41 |
42 | return {
43 |
44 | noise: function (x, y, z) {
45 |
46 | var floorX = Math.floor(x), floorY = Math.floor(y), floorZ = Math.floor(z);
47 |
48 | var X = floorX & 255, Y = floorY & 255, Z = floorZ & 255;
49 |
50 | x -= floorX;
51 | y -= floorY;
52 | z -= floorZ;
53 |
54 | var xMinus1 = x -1, yMinus1 = y - 1, zMinus1 = z - 1;
55 |
56 | var u = fade(x), v = fade(y), w = fade(z);
57 |
58 | var A = p[X]+Y, AA = p[A]+Z, AB = p[A+1]+Z, B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z;
59 |
60 | return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z),
61 | grad(p[BA], xMinus1, y, z)),
62 | lerp(u, grad(p[AB], x, yMinus1, z),
63 | grad(p[BB], xMinus1, yMinus1, z))),
64 | lerp(v, lerp(u, grad(p[AA+1], x, y, zMinus1),
65 | grad(p[BA+1], xMinus1, y, z-1)),
66 | lerp(u, grad(p[AB+1], x, yMinus1, zMinus1),
67 | grad(p[BB+1], xMinus1, yMinus1, zMinus1))));
68 |
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/demo/js/Maf.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Module code from underscore.js
4 |
5 | // Establish the root object, `window` (`self`) in the browser, `global`
6 | // on the server, or `this` in some virtual machines. We use `self`
7 | // instead of `window` for `WebWorker` support.
8 | var root = typeof self == 'object' && self.self === self && self ||
9 | typeof global == 'object' && global.global === global && global ||
10 | this;
11 |
12 | var Maf = function(obj) {
13 | if (obj instanceof Maf ) return obj;
14 | if (!(this instanceof Maf )) return new Maf(obj);
15 | this._wrapped = obj;
16 | };
17 |
18 | // Export the Underscore object for **Node.js**, with
19 | // backwards-compatibility for their old module API. If we're in
20 | // the browser, add `Maf` as a global object.
21 | // (`nodeType` is checked to ensure that `module`
22 | // and `exports` are not HTML elements.)
23 | if (typeof exports != 'undefined' && !exports.nodeType) {
24 | if (typeof module != 'undefined' && !module.nodeType && module.exports) {
25 | exports = module.exports = Maf;
26 | }
27 | exports.Maf = Maf;
28 | } else {
29 | root.Maf = Maf;
30 | }
31 |
32 | // Current version.
33 | Maf.VERSION = '1.0.0';
34 |
35 | Maf.PI = Math.PI;
36 |
37 | // https://www.opengl.org/sdk/docs/man/html/clamp.xhtml
38 |
39 | Maf.clamp = function( v, minVal, maxVal ) {
40 | return Math.min( maxVal, Math.max( minVal, v ) );
41 | };
42 |
43 | // https://www.opengl.org/sdk/docs/man/html/step.xhtml
44 |
45 | Maf.step = function( edge, v ) {
46 | return ( v < edge ) ? 0 : 1;
47 | }
48 |
49 | // https://www.opengl.org/sdk/docs/man/html/smoothstep.xhtml
50 |
51 | Maf.smoothStep = function ( edge0, edge1, v ) {
52 | var t = Maf.clamp( ( v - edge0 ) / ( edge1 - edge0 ), 0.0, 1.0 );
53 | return t * t * ( 3.0 - 2.0 * t );
54 | };
55 |
56 | // http://docs.unity3d.com/ScriptReference/Mathf.html
57 | // http://www.shaderific.com/glsl-functions/
58 | // https://www.opengl.org/sdk/docs/man4/html/
59 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ff471376(v=vs.85).aspx
60 | // http://moutjs.com/docs/v0.11/math.html#map
61 | // https://code.google.com/p/kuda/source/browse/public/js/hemi/utils/mathUtils.js?r=8d581c02651077c4ac3f5fc4725323210b6b13cc
62 |
63 | // Converts from degrees to radians.
64 | Maf.deg2Rad = function( degrees ) {
65 | return degrees * Math.PI / 180;
66 | };
67 |
68 | Maf.toRadians = Maf.deg2Rad;
69 |
70 | // Converts from radians to degrees.
71 | Maf.rad2Deg = function(radians) {
72 | return radians * 180 / Math.PI;
73 | };
74 |
75 | Maf.toDegrees = Maf.rad2Deg;
76 |
77 | Maf.clamp01 = function( v ) {
78 | return Maf.clamp( v, 0, 1 );
79 | };
80 |
81 | // https://www.opengl.org/sdk/docs/man/html/mix.xhtml
82 |
83 | Maf.mix = function( x, y, a ) {
84 | if( a <= 0 ) return x;
85 | if( a >= 1 ) return y;
86 | return x + a * (y - x)
87 | };
88 |
89 | Maf.lerp = Maf.mix;
90 |
91 | Maf.inverseMix = function( a, b, v ) {
92 | return ( v - a ) / ( b - a );
93 | };
94 |
95 | Maf.inverseLerp = Maf.inverseMix;
96 |
97 | Maf.mixUnclamped = function( x, y, a ) {
98 | if( a <= 0 ) return x;
99 | if( a >= 1 ) return y;
100 | return x + a * (y - x)
101 | };
102 |
103 | Maf.lerpUnclamped = Maf.mixUnclamped;
104 |
105 | // https://www.opengl.org/sdk/docs/man/html/fract.xhtml
106 |
107 | Maf.fract = function( v ) {
108 | return v - Math.floor( v );
109 | };
110 |
111 | Maf.frac = Maf.fract;
112 |
113 | // http://stackoverflow.com/questions/4965301/finding-if-a-number-is-a-power-of-2
114 |
115 | Maf.isPowerOfTwo = function( v ) {
116 | return ( ( ( v - 1) & v ) == 0 );
117 | };
118 |
119 | // https://bocoup.com/weblog/find-the-closest-power-of-2-with-javascript
120 |
121 | Maf.closestPowerOfTwo = function( v ) {
122 | return Math.pow( 2, Math.round( Math.log( v ) / Math.log( 2 ) ) );
123 | };
124 |
125 | Maf.nextPowerOfTwo = function( v ) {
126 | return Math.pow( 2, Math.ceil( Math.log( v ) / Math.log( 2 ) ) );
127 | }
128 |
129 | // http://stackoverflow.com/questions/1878907/the-smallest-difference-between-2-angles
130 |
131 | //function mod(a, n) { return a - Math.floor(a/n) * n; }
132 | Maf.mod = function(a, n) { return (a % n + n) % n; }
133 |
134 | Maf.deltaAngle = function( a, b ) {
135 | var d = Maf.mod( b - a, 360 );
136 | if( d > 180 ) d = Math.abs( d - 360 );
137 | return d;
138 | };
139 |
140 | Maf.deltaAngleDeg = Maf.deltaAngle;
141 |
142 | Maf.deltaAngleRad = function( a, b ) {
143 | return Maf.toRadians( Maf.deltaAngle( Maf.toDegrees( a ), Maf.toDegrees( b ) ) );
144 | };
145 |
146 | Maf.lerpAngle = function( a, b, t ) {
147 | var angle = Maf.deltaAngle( a, b );
148 | return Maf.mod( a + Maf.lerp( 0, angle, t ), 360 );
149 | };
150 |
151 | Maf.lerpAngleDeg = Maf.lerpAngle;
152 |
153 | Maf.lerpAngleRad = function( a, b, t ) {
154 | return Maf.toRadians( Maf.lerpAngleDeg( Maf.toDegrees( a ), Maf.toDegrees( b ), t ) );
155 | };
156 |
157 | // http://gamedev.stackexchange.com/questions/74324/gamma-space-and-linear-space-with-shader
158 |
159 | Maf.gammaToLinearSpace = function( v ) {
160 | return Math.pow( v, 2.2 );
161 | };
162 |
163 | Maf.linearToGammaSpace = function( v ) {
164 | return Math.pow( v, 1 / 2.2 );
165 | };
166 |
167 | Maf.map = function( from1, to1, from2, to2, v ) {
168 | return from2 + ( v - from1 ) * ( to2 - from2 ) / ( to1 - from1 );
169 | }
170 |
171 | Maf.scale = Maf.map;
172 |
173 | // http://www.iquilezles.org/www/articles/functions/functions.htm
174 |
175 | Maf.almostIdentity = function( x, m, n ) {
176 |
177 | if( x > m ) return x;
178 |
179 | var a = 2 * n - m;
180 | var b = 2 * m - 3 * n;
181 | var t = x / m;
182 |
183 | return ( a * t + b) * t * t + n;
184 | }
185 |
186 | Maf.impulse = function( k, x ) {
187 | var h = k * x;
188 | return h * Math.exp( 1 - h );
189 | };
190 |
191 | Maf.cubicPulse = function( c, w, x ) {
192 | x = Math.abs( x - c );
193 | if( x > w ) return 0;
194 | x /= w;
195 | return 1 - x * x * ( 3 - 2 * x );
196 | }
197 |
198 | Maf.expStep = function( x, k, n ) {
199 | return Math.exp( -k * Math.pow( x, n ) );
200 | }
201 |
202 | Maf.parabola = function( x, k ) {
203 | return Math.pow( 4 * x * ( 1 - x ), k );
204 | }
205 |
206 | Maf.powerCurve = function( x, a, b ) {
207 | var k = Math.pow( a + b, a + b ) / ( Math.pow( a, a ) * Math.pow( b, b ) );
208 | return k * Math.pow( x, a ) * Math.pow( 1 - x, b );
209 | }
210 |
211 | // http://iquilezles.org/www/articles/smin/smin.htm ?
212 |
213 | Maf.latLonToCartesian = function( lat, lon ) {
214 |
215 | lon += 180;
216 | lat = Maf.clamp( lat, -85, 85 );
217 | var phi = Maf.toRadians( 90 - lat );
218 | var theta = Maf.toRadians( 180 - lon );
219 | var x = Math.sin( phi ) * Math.cos( theta );
220 | var y = Math.cos( phi );
221 | var z = Math.sin( phi ) * Math.sin( theta );
222 |
223 | return { x: x, y: y, z: z }
224 |
225 | }
226 |
227 | Maf.cartesianToLatLon = function( x, y, z ) {
228 | var n = Math.sqrt( x * x + y * y + z * z );
229 | return{ lat: Math.asin( z / n ), lon: Math.atan2( y, x ) };
230 | }
231 |
232 | Maf.randomInRange = function( min, max ) {
233 | return min + Math.random() * ( max - min );
234 | }
235 |
236 | Maf.norm = function( v, minVal, maxVal ) {
237 | return ( v - minVal ) / ( maxVal - minVal );
238 | }
239 |
240 | Maf.hash = function( n ) {
241 | return Maf.fract( (1.0 + Math.cos(n)) * 415.92653);
242 | }
243 |
244 | Maf.noise2d = function( x, y ) {
245 | var xhash = Maf.hash( x * 37.0 );
246 | var yhash = Maf.hash( y * 57.0 );
247 | return Maf.fract( xhash + yhash );
248 | }
249 |
250 | // http://iquilezles.org/www/articles/smin/smin.htm
251 |
252 | Maf.smoothMin = function( a, b, k ) {
253 | var res = Math.exp( -k*a ) + Math.exp( -k*b );
254 | return - Math.log( res )/k;
255 | }
256 |
257 | Maf.smoothMax = function( a, b, k ){
258 | return Math.log( Math.exp(a) + Math.exp(b) )/k;
259 | }
260 |
261 | Maf.almost = function( a, b ) {
262 | return ( Math.abs( a - b ) < .0001 );
263 | }
264 |
265 | }());
--------------------------------------------------------------------------------
/demo/js/OBJLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.OBJLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | this.materials = null;
10 |
11 | this.regexp = {
12 | // v float float float
13 | vertex_pattern : /^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,
14 | // vn float float float
15 | normal_pattern : /^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,
16 | // vt float float
17 | uv_pattern : /^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,
18 | // f vertex vertex vertex
19 | face_vertex : /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/,
20 | // f vertex/uv vertex/uv vertex/uv
21 | face_vertex_uv : /^f\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+))?/,
22 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal
23 | face_vertex_uv_normal : /^f\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+)\/(-?\d+))?/,
24 | // f vertex//normal vertex//normal vertex//normal
25 | face_vertex_normal : /^f\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)(?:\s+(-?\d+)\/\/(-?\d+))?/,
26 | // o object_name | g group_name
27 | object_pattern : /^[og]\s*(.+)?/,
28 | // s boolean
29 | smoothing_pattern : /^s\s+(\d+|on|off)/,
30 | // mtllib file_reference
31 | material_library_pattern : /^mtllib /,
32 | // usemtl material_name
33 | material_use_pattern : /^usemtl /
34 | };
35 |
36 | };
37 |
38 | THREE.OBJLoader.prototype = {
39 |
40 | constructor: THREE.OBJLoader,
41 |
42 | load: function ( url, onLoad, onProgress, onError ) {
43 |
44 | var scope = this;
45 |
46 | var loader = new THREE.FileLoader( scope.manager );
47 | loader.setPath( this.path );
48 | loader.load( url, function ( text ) {
49 |
50 | onLoad( scope.parse( text ) );
51 |
52 | }, onProgress, onError );
53 |
54 | },
55 |
56 | setPath: function ( value ) {
57 |
58 | this.path = value;
59 |
60 | },
61 |
62 | setMaterials: function ( materials ) {
63 |
64 | this.materials = materials;
65 |
66 | },
67 |
68 | _createParserState : function () {
69 |
70 | var state = {
71 | objects : [],
72 | object : {},
73 |
74 | vertices : [],
75 | normals : [],
76 | uvs : [],
77 |
78 | materialLibraries : [],
79 |
80 | startObject: function ( name, fromDeclaration ) {
81 |
82 | // If the current object (initial from reset) is not from a g/o declaration in the parsed
83 | // file. We need to use it for the first parsed g/o to keep things in sync.
84 | if ( this.object && this.object.fromDeclaration === false ) {
85 |
86 | this.object.name = name;
87 | this.object.fromDeclaration = ( fromDeclaration !== false );
88 | return;
89 |
90 | }
91 |
92 | var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
93 |
94 | if ( this.object && typeof this.object._finalize === 'function' ) {
95 |
96 | this.object._finalize( true );
97 |
98 | }
99 |
100 | this.object = {
101 | name : name || '',
102 | fromDeclaration : ( fromDeclaration !== false ),
103 |
104 | geometry : {
105 | vertices : [],
106 | normals : [],
107 | uvs : []
108 | },
109 | materials : [],
110 | smooth : true,
111 |
112 | startMaterial : function( name, libraries ) {
113 |
114 | var previous = this._finalize( false );
115 |
116 | // New usemtl declaration overwrites an inherited material, except if faces were declared
117 | // after the material, then it must be preserved for proper MultiMaterial continuation.
118 | if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
119 |
120 | this.materials.splice( previous.index, 1 );
121 |
122 | }
123 |
124 | var material = {
125 | index : this.materials.length,
126 | name : name || '',
127 | mtllib : ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
128 | smooth : ( previous !== undefined ? previous.smooth : this.smooth ),
129 | groupStart : ( previous !== undefined ? previous.groupEnd : 0 ),
130 | groupEnd : -1,
131 | groupCount : -1,
132 | inherited : false,
133 |
134 | clone : function( index ) {
135 | var cloned = {
136 | index : ( typeof index === 'number' ? index : this.index ),
137 | name : this.name,
138 | mtllib : this.mtllib,
139 | smooth : this.smooth,
140 | groupStart : 0,
141 | groupEnd : -1,
142 | groupCount : -1,
143 | inherited : false
144 | };
145 | cloned.clone = this.clone.bind(cloned);
146 | return cloned;
147 | }
148 | };
149 |
150 | this.materials.push( material );
151 |
152 | return material;
153 |
154 | },
155 |
156 | currentMaterial : function() {
157 |
158 | if ( this.materials.length > 0 ) {
159 | return this.materials[ this.materials.length - 1 ];
160 | }
161 |
162 | return undefined;
163 |
164 | },
165 |
166 | _finalize : function( end ) {
167 |
168 | var lastMultiMaterial = this.currentMaterial();
169 | if ( lastMultiMaterial && lastMultiMaterial.groupEnd === -1 ) {
170 |
171 | lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
172 | lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
173 | lastMultiMaterial.inherited = false;
174 |
175 | }
176 |
177 | // Ignore objects tail materials if no face declarations followed them before a new o/g started.
178 | if ( end && this.materials.length > 1 ) {
179 |
180 | for ( var mi = this.materials.length - 1; mi >= 0; mi-- ) {
181 | if ( this.materials[mi].groupCount <= 0 ) {
182 | this.materials.splice( mi, 1 );
183 | }
184 | }
185 |
186 | }
187 |
188 | // Guarantee at least one empty material, this makes the creation later more straight forward.
189 | if ( end && this.materials.length === 0 ) {
190 |
191 | this.materials.push({
192 | name : '',
193 | smooth : this.smooth
194 | });
195 |
196 | }
197 |
198 | return lastMultiMaterial;
199 |
200 | }
201 | };
202 |
203 | // Inherit previous objects material.
204 | // Spec tells us that a declared material must be set to all objects until a new material is declared.
205 | // If a usemtl declaration is encountered while this new object is being parsed, it will
206 | // overwrite the inherited material. Exception being that there was already face declarations
207 | // to the inherited material, then it will be preserved for proper MultiMaterial continuation.
208 |
209 | if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === "function" ) {
210 |
211 | var declared = previousMaterial.clone( 0 );
212 | declared.inherited = true;
213 | this.object.materials.push( declared );
214 |
215 | }
216 |
217 | this.objects.push( this.object );
218 |
219 | },
220 |
221 | finalize : function() {
222 |
223 | if ( this.object && typeof this.object._finalize === 'function' ) {
224 |
225 | this.object._finalize( true );
226 |
227 | }
228 |
229 | },
230 |
231 | parseVertexIndex: function ( value, len ) {
232 |
233 | var index = parseInt( value, 10 );
234 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
235 |
236 | },
237 |
238 | parseNormalIndex: function ( value, len ) {
239 |
240 | var index = parseInt( value, 10 );
241 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
242 |
243 | },
244 |
245 | parseUVIndex: function ( value, len ) {
246 |
247 | var index = parseInt( value, 10 );
248 | return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
249 |
250 | },
251 |
252 | addVertex: function ( a, b, c ) {
253 |
254 | var src = this.vertices;
255 | var dst = this.object.geometry.vertices;
256 |
257 | dst.push( src[ a + 0 ] );
258 | dst.push( src[ a + 1 ] );
259 | dst.push( src[ a + 2 ] );
260 | dst.push( src[ b + 0 ] );
261 | dst.push( src[ b + 1 ] );
262 | dst.push( src[ b + 2 ] );
263 | dst.push( src[ c + 0 ] );
264 | dst.push( src[ c + 1 ] );
265 | dst.push( src[ c + 2 ] );
266 |
267 | },
268 |
269 | addVertexLine: function ( a ) {
270 |
271 | var src = this.vertices;
272 | var dst = this.object.geometry.vertices;
273 |
274 | dst.push( src[ a + 0 ] );
275 | dst.push( src[ a + 1 ] );
276 | dst.push( src[ a + 2 ] );
277 |
278 | },
279 |
280 | addNormal : function ( a, b, c ) {
281 |
282 | var src = this.normals;
283 | var dst = this.object.geometry.normals;
284 |
285 | dst.push( src[ a + 0 ] );
286 | dst.push( src[ a + 1 ] );
287 | dst.push( src[ a + 2 ] );
288 | dst.push( src[ b + 0 ] );
289 | dst.push( src[ b + 1 ] );
290 | dst.push( src[ b + 2 ] );
291 | dst.push( src[ c + 0 ] );
292 | dst.push( src[ c + 1 ] );
293 | dst.push( src[ c + 2 ] );
294 |
295 | },
296 |
297 | addUV: function ( a, b, c ) {
298 |
299 | var src = this.uvs;
300 | var dst = this.object.geometry.uvs;
301 |
302 | dst.push( src[ a + 0 ] );
303 | dst.push( src[ a + 1 ] );
304 | dst.push( src[ b + 0 ] );
305 | dst.push( src[ b + 1 ] );
306 | dst.push( src[ c + 0 ] );
307 | dst.push( src[ c + 1 ] );
308 |
309 | },
310 |
311 | addUVLine: function ( a ) {
312 |
313 | var src = this.uvs;
314 | var dst = this.object.geometry.uvs;
315 |
316 | dst.push( src[ a + 0 ] );
317 | dst.push( src[ a + 1 ] );
318 |
319 | },
320 |
321 | addFace: function ( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) {
322 |
323 | var vLen = this.vertices.length;
324 |
325 | var ia = this.parseVertexIndex( a, vLen );
326 | var ib = this.parseVertexIndex( b, vLen );
327 | var ic = this.parseVertexIndex( c, vLen );
328 | var id;
329 |
330 | if ( d === undefined ) {
331 |
332 | this.addVertex( ia, ib, ic );
333 |
334 | } else {
335 |
336 | id = this.parseVertexIndex( d, vLen );
337 |
338 | this.addVertex( ia, ib, id );
339 | this.addVertex( ib, ic, id );
340 |
341 | }
342 |
343 | if ( ua !== undefined ) {
344 |
345 | var uvLen = this.uvs.length;
346 |
347 | ia = this.parseUVIndex( ua, uvLen );
348 | ib = this.parseUVIndex( ub, uvLen );
349 | ic = this.parseUVIndex( uc, uvLen );
350 |
351 | if ( d === undefined ) {
352 |
353 | this.addUV( ia, ib, ic );
354 |
355 | } else {
356 |
357 | id = this.parseUVIndex( ud, uvLen );
358 |
359 | this.addUV( ia, ib, id );
360 | this.addUV( ib, ic, id );
361 |
362 | }
363 |
364 | }
365 |
366 | if ( na !== undefined ) {
367 |
368 | // Normals are many times the same. If so, skip function call and parseInt.
369 | var nLen = this.normals.length;
370 | ia = this.parseNormalIndex( na, nLen );
371 |
372 | ib = na === nb ? ia : this.parseNormalIndex( nb, nLen );
373 | ic = na === nc ? ia : this.parseNormalIndex( nc, nLen );
374 |
375 | if ( d === undefined ) {
376 |
377 | this.addNormal( ia, ib, ic );
378 |
379 | } else {
380 |
381 | id = this.parseNormalIndex( nd, nLen );
382 |
383 | this.addNormal( ia, ib, id );
384 | this.addNormal( ib, ic, id );
385 |
386 | }
387 |
388 | }
389 |
390 | },
391 |
392 | addLineGeometry: function ( vertices, uvs ) {
393 |
394 | this.object.geometry.type = 'Line';
395 |
396 | var vLen = this.vertices.length;
397 | var uvLen = this.uvs.length;
398 |
399 | for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
400 |
401 | this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
402 |
403 | }
404 |
405 | for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
406 |
407 | this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
408 |
409 | }
410 |
411 | }
412 |
413 | };
414 |
415 | state.startObject( '', false );
416 |
417 | return state;
418 |
419 | },
420 |
421 | parse: function ( text ) {
422 |
423 | console.time( 'OBJLoader' );
424 |
425 | var state = this._createParserState();
426 |
427 | if ( text.indexOf( '\r\n' ) !== - 1 ) {
428 |
429 | // This is faster than String.split with regex that splits on both
430 | text = text.replace( /\r\n/g, '\n' );
431 |
432 | }
433 |
434 | if ( text.indexOf( '\\\n' ) !== - 1) {
435 |
436 | // join lines separated by a line continuation character (\)
437 | text = text.replace( /\\\n/g, '' );
438 |
439 | }
440 |
441 | var lines = text.split( '\n' );
442 | var line = '', lineFirstChar = '', lineSecondChar = '';
443 | var lineLength = 0;
444 | var result = [];
445 |
446 | // Faster to just trim left side of the line. Use if available.
447 | var trimLeft = ( typeof ''.trimLeft === 'function' );
448 |
449 | for ( var i = 0, l = lines.length; i < l; i ++ ) {
450 |
451 | line = lines[ i ];
452 |
453 | line = trimLeft ? line.trimLeft() : line.trim();
454 |
455 | lineLength = line.length;
456 |
457 | if ( lineLength === 0 ) continue;
458 |
459 | lineFirstChar = line.charAt( 0 );
460 |
461 | // @todo invoke passed in handler if any
462 | if ( lineFirstChar === '#' ) continue;
463 |
464 | if ( lineFirstChar === 'v' ) {
465 |
466 | lineSecondChar = line.charAt( 1 );
467 |
468 | if ( lineSecondChar === ' ' && ( result = this.regexp.vertex_pattern.exec( line ) ) !== null ) {
469 |
470 | // 0 1 2 3
471 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
472 |
473 | state.vertices.push(
474 | parseFloat( result[ 1 ] ),
475 | parseFloat( result[ 2 ] ),
476 | parseFloat( result[ 3 ] )
477 | );
478 |
479 | } else if ( lineSecondChar === 'n' && ( result = this.regexp.normal_pattern.exec( line ) ) !== null ) {
480 |
481 | // 0 1 2 3
482 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
483 |
484 | state.normals.push(
485 | parseFloat( result[ 1 ] ),
486 | parseFloat( result[ 2 ] ),
487 | parseFloat( result[ 3 ] )
488 | );
489 |
490 | } else if ( lineSecondChar === 't' && ( result = this.regexp.uv_pattern.exec( line ) ) !== null ) {
491 |
492 | // 0 1 2
493 | // ["vt 0.1 0.2", "0.1", "0.2"]
494 |
495 | state.uvs.push(
496 | parseFloat( result[ 1 ] ),
497 | parseFloat( result[ 2 ] )
498 | );
499 |
500 | } else {
501 |
502 | throw new Error( "Unexpected vertex/normal/uv line: '" + line + "'" );
503 |
504 | }
505 |
506 | } else if ( lineFirstChar === "f" ) {
507 |
508 | if ( ( result = this.regexp.face_vertex_uv_normal.exec( line ) ) !== null ) {
509 |
510 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal
511 | // 0 1 2 3 4 5 6 7 8 9 10 11 12
512 | // ["f 1/1/1 2/2/2 3/3/3", "1", "1", "1", "2", "2", "2", "3", "3", "3", undefined, undefined, undefined]
513 |
514 | state.addFace(
515 | result[ 1 ], result[ 4 ], result[ 7 ], result[ 10 ],
516 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
517 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
518 | );
519 |
520 | } else if ( ( result = this.regexp.face_vertex_uv.exec( line ) ) !== null ) {
521 |
522 | // f vertex/uv vertex/uv vertex/uv
523 | // 0 1 2 3 4 5 6 7 8
524 | // ["f 1/1 2/2 3/3", "1", "1", "2", "2", "3", "3", undefined, undefined]
525 |
526 | state.addFace(
527 | result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
528 | result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
529 | );
530 |
531 | } else if ( ( result = this.regexp.face_vertex_normal.exec( line ) ) !== null ) {
532 |
533 | // f vertex//normal vertex//normal vertex//normal
534 | // 0 1 2 3 4 5 6 7 8
535 | // ["f 1//1 2//2 3//3", "1", "1", "2", "2", "3", "3", undefined, undefined]
536 |
537 | state.addFace(
538 | result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
539 | undefined, undefined, undefined, undefined,
540 | result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
541 | );
542 |
543 | } else if ( ( result = this.regexp.face_vertex.exec( line ) ) !== null ) {
544 |
545 | // f vertex vertex vertex
546 | // 0 1 2 3 4
547 | // ["f 1 2 3", "1", "2", "3", undefined]
548 |
549 | state.addFace(
550 | result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
551 | );
552 |
553 | } else {
554 |
555 | throw new Error( "Unexpected face line: '" + line + "'" );
556 |
557 | }
558 |
559 | } else if ( lineFirstChar === "l" ) {
560 |
561 | var lineParts = line.substring( 1 ).trim().split( " " );
562 | var lineVertices = [], lineUVs = [];
563 |
564 | if ( line.indexOf( "/" ) === - 1 ) {
565 |
566 | lineVertices = lineParts;
567 |
568 | } else {
569 |
570 | for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
571 |
572 | var parts = lineParts[ li ].split( "/" );
573 |
574 | if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
575 | if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
576 |
577 | }
578 |
579 | }
580 | state.addLineGeometry( lineVertices, lineUVs );
581 |
582 | } else if ( ( result = this.regexp.object_pattern.exec( line ) ) !== null ) {
583 |
584 | // o object_name
585 | // or
586 | // g group_name
587 |
588 | // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
589 | // var name = result[ 0 ].substr( 1 ).trim();
590 | var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
591 |
592 | state.startObject( name );
593 |
594 | } else if ( this.regexp.material_use_pattern.test( line ) ) {
595 |
596 | // material
597 |
598 | state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
599 |
600 | } else if ( this.regexp.material_library_pattern.test( line ) ) {
601 |
602 | // mtl file
603 |
604 | state.materialLibraries.push( line.substring( 7 ).trim() );
605 |
606 | } else if ( ( result = this.regexp.smoothing_pattern.exec( line ) ) !== null ) {
607 |
608 | // smooth shading
609 |
610 | // @todo Handle files that have varying smooth values for a set of faces inside one geometry,
611 | // but does not define a usemtl for each face set.
612 | // This should be detected and a dummy material created (later MultiMaterial and geometry groups).
613 | // This requires some care to not create extra material on each smooth value for "normal" obj files.
614 | // where explicit usemtl defines geometry groups.
615 | // Example asset: examples/models/obj/cerberus/Cerberus.obj
616 |
617 | var value = result[ 1 ].trim().toLowerCase();
618 | state.object.smooth = ( value === '1' || value === 'on' );
619 |
620 | var material = state.object.currentMaterial();
621 | if ( material ) {
622 |
623 | material.smooth = state.object.smooth;
624 |
625 | }
626 |
627 | } else {
628 |
629 | // Handle null terminated files without exception
630 | if ( line === '\0' ) continue;
631 |
632 | throw new Error( "Unexpected line: '" + line + "'" );
633 |
634 | }
635 |
636 | }
637 |
638 | state.finalize();
639 |
640 | var container = new THREE.Group();
641 | container.materialLibraries = [].concat( state.materialLibraries );
642 |
643 | for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
644 |
645 | var object = state.objects[ i ];
646 | var geometry = object.geometry;
647 | var materials = object.materials;
648 | var isLine = ( geometry.type === 'Line' );
649 |
650 | // Skip o/g line declarations that did not follow with any faces
651 | if ( geometry.vertices.length === 0 ) continue;
652 |
653 | var buffergeometry = new THREE.BufferGeometry();
654 |
655 | buffergeometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) );
656 |
657 | if ( geometry.normals.length > 0 ) {
658 |
659 | buffergeometry.setAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) );
660 |
661 | } else {
662 |
663 | buffergeometry.computeVertexNormals();
664 |
665 | }
666 |
667 | if ( geometry.uvs.length > 0 ) {
668 |
669 | buffergeometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) );
670 |
671 | }
672 |
673 | // Create materials
674 |
675 | var createdMaterials = [];
676 |
677 | for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
678 |
679 | var sourceMaterial = materials[mi];
680 | var material = undefined;
681 |
682 | if ( this.materials !== null ) {
683 |
684 | material = this.materials.create( sourceMaterial.name );
685 |
686 | // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
687 | if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
688 |
689 | var materialLine = new THREE.LineBasicMaterial();
690 | materialLine.copy( material );
691 | material = materialLine;
692 |
693 | }
694 |
695 | }
696 |
697 | if ( ! material ) {
698 |
699 | material = ( ! isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
700 | material.name = sourceMaterial.name;
701 |
702 | }
703 |
704 | material.flatShading = sourceMaterial.smooth ? THREE.SmoothShading : THREE.FlatShading;
705 |
706 | createdMaterials.push(material);
707 |
708 | }
709 |
710 | // Create mesh
711 |
712 | var mesh;
713 |
714 | if ( createdMaterials.length > 1 ) {
715 |
716 | for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
717 |
718 | var sourceMaterial = materials[mi];
719 | buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
720 |
721 | }
722 |
723 | var multiMaterial = new THREE.MultiMaterial( createdMaterials );
724 | mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, multiMaterial ) : new THREE.LineSegments( buffergeometry, multiMaterial ) );
725 |
726 | } else {
727 |
728 | mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ) : new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ) );
729 | }
730 |
731 | mesh.name = object.name;
732 |
733 | container.add( mesh );
734 |
735 | }
736 |
737 | console.timeEnd( 'OBJLoader' );
738 |
739 | return container;
740 |
741 | }
742 |
743 | };
744 |
--------------------------------------------------------------------------------
/demo/js/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 | /*global THREE, console */
9 |
10 | ( function () {
11 |
12 | function OrbitConstraint ( object ) {
13 |
14 | this.object = object;
15 |
16 | // "target" sets the location of focus, where the object orbits around
17 | // and where it pans with respect to.
18 | this.target = new THREE.Vector3();
19 |
20 | // Limits to how far you can dolly in and out ( PerspectiveCamera only )
21 | this.minDistance = 0;
22 | this.maxDistance = Infinity;
23 |
24 | // Limits to how far you can zoom in and out ( OrthographicCamera only )
25 | this.minZoom = 0;
26 | this.maxZoom = Infinity;
27 |
28 | // How far you can orbit vertically, upper and lower limits.
29 | // Range is 0 to Math.PI radians.
30 | this.minPolarAngle = 0; // radians
31 | this.maxPolarAngle = Math.PI; // radians
32 |
33 | // How far you can orbit horizontally, upper and lower limits.
34 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
35 | this.minAzimuthAngle = - Infinity; // radians
36 | this.maxAzimuthAngle = Infinity; // radians
37 |
38 | // Set to true to enable damping (inertia)
39 | // If damping is enabled, you must call controls.update() in your animation loop
40 | this.enableDamping = false;
41 | this.dampingFactor = 0.25;
42 |
43 | ////////////
44 | // internals
45 |
46 | var scope = this;
47 |
48 | var EPS = 0.000001;
49 |
50 | // Current position in spherical coordinate system.
51 | var theta;
52 | var phi;
53 |
54 | // Pending changes
55 | var phiDelta = 0;
56 | var thetaDelta = 0;
57 | var scale = 1;
58 | var panOffset = new THREE.Vector3();
59 | var zoomChanged = false;
60 |
61 | // API
62 |
63 | this.getPolarAngle = function () {
64 |
65 | return phi;
66 |
67 | };
68 |
69 | this.getAzimuthalAngle = function () {
70 |
71 | return theta;
72 |
73 | };
74 |
75 | this.rotateLeft = function ( angle ) {
76 |
77 | thetaDelta -= angle;
78 |
79 | };
80 |
81 | this.rotateUp = function ( angle ) {
82 |
83 | phiDelta -= angle;
84 |
85 | };
86 |
87 | // pass in distance in world space to move left
88 | this.panLeft = function() {
89 |
90 | var v = new THREE.Vector3();
91 |
92 | return function panLeft ( distance ) {
93 |
94 | var te = this.object.matrix.elements;
95 |
96 | // get X column of matrix
97 | v.set( te[ 0 ], te[ 1 ], te[ 2 ] );
98 | v.multiplyScalar( - distance );
99 |
100 | panOffset.add( v );
101 |
102 | };
103 |
104 | }();
105 |
106 | // pass in distance in world space to move up
107 | this.panUp = function() {
108 |
109 | var v = new THREE.Vector3();
110 |
111 | return function panUp ( distance ) {
112 |
113 | var te = this.object.matrix.elements;
114 |
115 | // get Y column of matrix
116 | v.set( te[ 4 ], te[ 5 ], te[ 6 ] );
117 | v.multiplyScalar( distance );
118 |
119 | panOffset.add( v );
120 |
121 | };
122 |
123 | }();
124 |
125 | // pass in x,y of change desired in pixel space,
126 | // right and down are positive
127 | this.pan = function ( deltaX, deltaY, screenWidth, screenHeight ) {
128 |
129 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
130 |
131 | // perspective
132 | var position = scope.object.position;
133 | var offset = position.clone().sub( scope.target );
134 | var targetDistance = offset.length();
135 |
136 | // half of the fov is center to top of screen
137 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
138 |
139 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
140 | scope.panLeft( 2 * deltaX * targetDistance / screenHeight );
141 | scope.panUp( 2 * deltaY * targetDistance / screenHeight );
142 |
143 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
144 |
145 | // orthographic
146 | scope.panLeft( deltaX * ( scope.object.right - scope.object.left ) / screenWidth );
147 | scope.panUp( deltaY * ( scope.object.top - scope.object.bottom ) / screenHeight );
148 |
149 | } else {
150 |
151 | // camera neither orthographic or perspective
152 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
153 |
154 | }
155 |
156 | };
157 |
158 | this.dollyIn = function ( dollyScale ) {
159 |
160 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
161 |
162 | scale /= dollyScale;
163 |
164 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
165 |
166 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) );
167 | scope.object.updateProjectionMatrix();
168 | zoomChanged = true;
169 |
170 | } else {
171 |
172 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
173 |
174 | }
175 |
176 | };
177 |
178 | this.dollyOut = function ( dollyScale ) {
179 |
180 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
181 |
182 | scale *= dollyScale;
183 |
184 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
185 |
186 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) );
187 | scope.object.updateProjectionMatrix();
188 | zoomChanged = true;
189 |
190 | } else {
191 |
192 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
193 |
194 | }
195 |
196 | };
197 |
198 | this.update = function() {
199 |
200 | var offset = new THREE.Vector3();
201 |
202 | // so camera.up is the orbit axis
203 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
204 | var quatInverse = quat.clone().inverse();
205 |
206 | var lastPosition = new THREE.Vector3();
207 | var lastQuaternion = new THREE.Quaternion();
208 |
209 | return function () {
210 |
211 | var position = this.object.position;
212 |
213 | offset.copy( position ).sub( this.target );
214 |
215 | // rotate offset to "y-axis-is-up" space
216 | offset.applyQuaternion( quat );
217 |
218 | // angle from z-axis around y-axis
219 |
220 | theta = Math.atan2( offset.x, offset.z );
221 |
222 | // angle from y-axis
223 |
224 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
225 |
226 | theta += thetaDelta;
227 | phi += phiDelta;
228 |
229 | // restrict theta to be between desired limits
230 | theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) );
231 |
232 | // restrict phi to be between desired limits
233 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
234 |
235 | // restrict phi to be betwee EPS and PI-EPS
236 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
237 |
238 | var radius = offset.length() * scale;
239 |
240 | // restrict radius to be between desired limits
241 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
242 |
243 | // move target to panned location
244 | this.target.add( panOffset );
245 |
246 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
247 | offset.y = radius * Math.cos( phi );
248 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
249 |
250 | // rotate offset back to "camera-up-vector-is-up" space
251 | offset.applyQuaternion( quatInverse );
252 |
253 | position.copy( this.target ).add( offset );
254 |
255 | this.object.lookAt( this.target );
256 |
257 | if ( this.enableDamping === true ) {
258 |
259 | thetaDelta *= ( 1 - this.dampingFactor );
260 | phiDelta *= ( 1 - this.dampingFactor );
261 |
262 | } else {
263 |
264 | thetaDelta = 0;
265 | phiDelta = 0;
266 |
267 | }
268 |
269 | scale = 1;
270 | panOffset.set( 0, 0, 0 );
271 |
272 | // update condition is:
273 | // min(camera displacement, camera rotation in radians)^2 > EPS
274 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
275 |
276 | if ( zoomChanged ||
277 | lastPosition.distanceToSquared( this.object.position ) > EPS ||
278 | 8 * ( 1 - lastQuaternion.dot( this.object.quaternion ) ) > EPS ) {
279 |
280 | lastPosition.copy( this.object.position );
281 | lastQuaternion.copy( this.object.quaternion );
282 | zoomChanged = false;
283 |
284 | return true;
285 |
286 | }
287 |
288 | return false;
289 |
290 | };
291 |
292 | }();
293 |
294 | };
295 |
296 |
297 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains
298 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
299 | // supported.
300 | //
301 | // Orbit - left mouse / touch: one finger move
302 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
303 | // Pan - right mouse, or arrow keys / touch: three finter swipe
304 |
305 | THREE.OrbitControls = function ( object, domElement ) {
306 |
307 | var constraint = new OrbitConstraint( object );
308 |
309 | this.domElement = ( domElement !== undefined ) ? domElement : document;
310 |
311 | // API
312 |
313 | Object.defineProperty( this, 'constraint', {
314 |
315 | get: function() {
316 |
317 | return constraint;
318 |
319 | }
320 |
321 | } );
322 |
323 | this.getPolarAngle = function () {
324 |
325 | return constraint.getPolarAngle();
326 |
327 | };
328 |
329 | this.getAzimuthalAngle = function () {
330 |
331 | return constraint.getAzimuthalAngle();
332 |
333 | };
334 |
335 | // Set to false to disable this control
336 | this.enabled = true;
337 |
338 | // center is old, deprecated; use "target" instead
339 | this.center = this.target;
340 |
341 | // This option actually enables dollying in and out; left as "zoom" for
342 | // backwards compatibility.
343 | // Set to false to disable zooming
344 | this.enableZoom = true;
345 | this.zoomSpeed = 1.0;
346 |
347 | // Set to false to disable rotating
348 | this.enableRotate = true;
349 | this.rotateSpeed = 1.0;
350 |
351 | // Set to false to disable panning
352 | this.enablePan = true;
353 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
354 |
355 | // Set to true to automatically rotate around the target
356 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
357 | this.autoRotate = false;
358 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
359 |
360 | // Set to false to disable use of the keys
361 | this.enableKeys = true;
362 |
363 | // The four arrow keys
364 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
365 |
366 | // Mouse buttons
367 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
368 |
369 | ////////////
370 | // internals
371 |
372 | var scope = this;
373 |
374 | var rotateStart = new THREE.Vector2();
375 | var rotateEnd = new THREE.Vector2();
376 | var rotateDelta = new THREE.Vector2();
377 |
378 | var panStart = new THREE.Vector2();
379 | var panEnd = new THREE.Vector2();
380 | var panDelta = new THREE.Vector2();
381 |
382 | var dollyStart = new THREE.Vector2();
383 | var dollyEnd = new THREE.Vector2();
384 | var dollyDelta = new THREE.Vector2();
385 |
386 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
387 |
388 | var state = STATE.NONE;
389 |
390 | // for reset
391 |
392 | this.target0 = this.target.clone();
393 | this.position0 = this.object.position.clone();
394 | this.zoom0 = this.object.zoom;
395 |
396 | // events
397 |
398 | var changeEvent = { type: 'change' };
399 | var startEvent = { type: 'start' };
400 | var endEvent = { type: 'end' };
401 |
402 | // pass in x,y of change desired in pixel space,
403 | // right and down are positive
404 | function pan( deltaX, deltaY ) {
405 |
406 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
407 |
408 | constraint.pan( deltaX, deltaY, element.clientWidth, element.clientHeight );
409 |
410 | }
411 |
412 | this.update = function () {
413 |
414 | if ( this.autoRotate && state === STATE.NONE ) {
415 |
416 | constraint.rotateLeft( getAutoRotationAngle() );
417 |
418 | }
419 |
420 | if ( constraint.update() === true ) {
421 |
422 | this.dispatchEvent( changeEvent );
423 |
424 | }
425 |
426 | };
427 |
428 | this.reset = function () {
429 |
430 | state = STATE.NONE;
431 |
432 | this.target.copy( this.target0 );
433 | this.object.position.copy( this.position0 );
434 | this.object.zoom = this.zoom0;
435 |
436 | this.object.updateProjectionMatrix();
437 | this.dispatchEvent( changeEvent );
438 |
439 | this.update();
440 |
441 | };
442 |
443 | function getAutoRotationAngle() {
444 |
445 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
446 |
447 | }
448 |
449 | function getZoomScale() {
450 |
451 | return Math.pow( 0.95, scope.zoomSpeed );
452 |
453 | }
454 |
455 | function onMouseDown( event ) {
456 |
457 | if ( scope.enabled === false ) return;
458 |
459 | event.preventDefault();
460 |
461 | if ( event.button === scope.mouseButtons.ORBIT ) {
462 |
463 | if ( scope.enableRotate === false ) return;
464 |
465 | state = STATE.ROTATE;
466 |
467 | rotateStart.set( event.clientX, event.clientY );
468 |
469 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
470 |
471 | if ( scope.enableZoom === false ) return;
472 |
473 | state = STATE.DOLLY;
474 |
475 | dollyStart.set( event.clientX, event.clientY );
476 |
477 | } else if ( event.button === scope.mouseButtons.PAN ) {
478 |
479 | if ( scope.enablePan === false ) return;
480 |
481 | state = STATE.PAN;
482 |
483 | panStart.set( event.clientX, event.clientY );
484 |
485 | }
486 |
487 | if ( state !== STATE.NONE ) {
488 |
489 | document.addEventListener( 'mousemove', onMouseMove, false );
490 | document.addEventListener( 'mouseup', onMouseUp, false );
491 | scope.dispatchEvent( startEvent );
492 |
493 | }
494 |
495 | }
496 |
497 | function onMouseMove( event ) {
498 |
499 | if ( scope.enabled === false ) return;
500 |
501 | event.preventDefault();
502 |
503 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
504 |
505 | if ( state === STATE.ROTATE ) {
506 |
507 | if ( scope.enableRotate === false ) return;
508 |
509 | rotateEnd.set( event.clientX, event.clientY );
510 | rotateDelta.subVectors( rotateEnd, rotateStart );
511 |
512 | // rotating across whole screen goes 360 degrees around
513 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
514 |
515 | // rotating up and down along whole screen attempts to go 360, but limited to 180
516 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
517 |
518 | rotateStart.copy( rotateEnd );
519 |
520 | } else if ( state === STATE.DOLLY ) {
521 |
522 | if ( scope.enableZoom === false ) return;
523 |
524 | dollyEnd.set( event.clientX, event.clientY );
525 | dollyDelta.subVectors( dollyEnd, dollyStart );
526 |
527 | if ( dollyDelta.y > 0 ) {
528 |
529 | constraint.dollyIn( getZoomScale() );
530 |
531 | } else if ( dollyDelta.y < 0 ) {
532 |
533 | constraint.dollyOut( getZoomScale() );
534 |
535 | }
536 |
537 | dollyStart.copy( dollyEnd );
538 |
539 | } else if ( state === STATE.PAN ) {
540 |
541 | if ( scope.enablePan === false ) return;
542 |
543 | panEnd.set( event.clientX, event.clientY );
544 | panDelta.subVectors( panEnd, panStart );
545 |
546 | pan( panDelta.x, panDelta.y );
547 |
548 | panStart.copy( panEnd );
549 |
550 | }
551 |
552 | if ( state !== STATE.NONE ) scope.update();
553 |
554 | }
555 |
556 | function onMouseUp( /* event */ ) {
557 |
558 | if ( scope.enabled === false ) return;
559 |
560 | document.removeEventListener( 'mousemove', onMouseMove, false );
561 | document.removeEventListener( 'mouseup', onMouseUp, false );
562 | scope.dispatchEvent( endEvent );
563 | state = STATE.NONE;
564 |
565 | }
566 |
567 | function onMouseWheel( event ) {
568 |
569 | if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
570 |
571 | event.preventDefault();
572 | event.stopPropagation();
573 |
574 | var delta = 0;
575 |
576 | if ( event.wheelDelta !== undefined ) {
577 |
578 | // WebKit / Opera / Explorer 9
579 |
580 | delta = event.wheelDelta;
581 |
582 | } else if ( event.detail !== undefined ) {
583 |
584 | // Firefox
585 |
586 | delta = - event.detail;
587 |
588 | }
589 |
590 | if ( delta > 0 ) {
591 |
592 | constraint.dollyOut( getZoomScale() );
593 |
594 | } else if ( delta < 0 ) {
595 |
596 | constraint.dollyIn( getZoomScale() );
597 |
598 | }
599 |
600 | scope.update();
601 | scope.dispatchEvent( startEvent );
602 | scope.dispatchEvent( endEvent );
603 |
604 | }
605 |
606 | function onKeyDown( event ) {
607 |
608 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
609 |
610 | switch ( event.keyCode ) {
611 |
612 | case scope.keys.UP:
613 | pan( 0, scope.keyPanSpeed );
614 | scope.update();
615 | break;
616 |
617 | case scope.keys.BOTTOM:
618 | pan( 0, - scope.keyPanSpeed );
619 | scope.update();
620 | break;
621 |
622 | case scope.keys.LEFT:
623 | pan( scope.keyPanSpeed, 0 );
624 | scope.update();
625 | break;
626 |
627 | case scope.keys.RIGHT:
628 | pan( - scope.keyPanSpeed, 0 );
629 | scope.update();
630 | break;
631 |
632 | }
633 |
634 | }
635 |
636 | function touchstart( event ) {
637 |
638 | if ( scope.enabled === false ) return;
639 |
640 | switch ( event.touches.length ) {
641 |
642 | case 1: // one-fingered touch: rotate
643 |
644 | if ( scope.enableRotate === false ) return;
645 |
646 | state = STATE.TOUCH_ROTATE;
647 |
648 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
649 | break;
650 |
651 | case 2: // two-fingered touch: dolly
652 |
653 | if ( scope.enableZoom === false ) return;
654 |
655 | state = STATE.TOUCH_DOLLY;
656 |
657 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
658 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
659 | var distance = Math.sqrt( dx * dx + dy * dy );
660 | dollyStart.set( 0, distance );
661 | break;
662 |
663 | case 3: // three-fingered touch: pan
664 |
665 | if ( scope.enablePan === false ) return;
666 |
667 | state = STATE.TOUCH_PAN;
668 |
669 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
670 | break;
671 |
672 | default:
673 |
674 | state = STATE.NONE;
675 |
676 | }
677 |
678 | if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent );
679 |
680 | }
681 |
682 | function touchmove( event ) {
683 |
684 | if ( scope.enabled === false ) return;
685 |
686 | event.preventDefault();
687 | event.stopPropagation();
688 |
689 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
690 |
691 | switch ( event.touches.length ) {
692 |
693 | case 1: // one-fingered touch: rotate
694 |
695 | if ( scope.enableRotate === false ) return;
696 | if ( state !== STATE.TOUCH_ROTATE ) return;
697 |
698 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
699 | rotateDelta.subVectors( rotateEnd, rotateStart );
700 |
701 | // rotating across whole screen goes 360 degrees around
702 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
703 | // rotating up and down along whole screen attempts to go 360, but limited to 180
704 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
705 |
706 | rotateStart.copy( rotateEnd );
707 |
708 | scope.update();
709 | break;
710 |
711 | case 2: // two-fingered touch: dolly
712 |
713 | if ( scope.enableZoom === false ) return;
714 | if ( state !== STATE.TOUCH_DOLLY ) return;
715 |
716 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
717 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
718 | var distance = Math.sqrt( dx * dx + dy * dy );
719 |
720 | dollyEnd.set( 0, distance );
721 | dollyDelta.subVectors( dollyEnd, dollyStart );
722 |
723 | if ( dollyDelta.y > 0 ) {
724 |
725 | constraint.dollyOut( getZoomScale() );
726 |
727 | } else if ( dollyDelta.y < 0 ) {
728 |
729 | constraint.dollyIn( getZoomScale() );
730 |
731 | }
732 |
733 | dollyStart.copy( dollyEnd );
734 |
735 | scope.update();
736 | break;
737 |
738 | case 3: // three-fingered touch: pan
739 |
740 | if ( scope.enablePan === false ) return;
741 | if ( state !== STATE.TOUCH_PAN ) return;
742 |
743 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
744 | panDelta.subVectors( panEnd, panStart );
745 |
746 | pan( panDelta.x, panDelta.y );
747 |
748 | panStart.copy( panEnd );
749 |
750 | scope.update();
751 | break;
752 |
753 | default:
754 |
755 | state = STATE.NONE;
756 |
757 | }
758 |
759 | }
760 |
761 | function touchend( /* event */ ) {
762 |
763 | if ( scope.enabled === false ) return;
764 |
765 | scope.dispatchEvent( endEvent );
766 | state = STATE.NONE;
767 |
768 | }
769 |
770 | function contextmenu( event ) {
771 |
772 | event.preventDefault();
773 |
774 | }
775 |
776 | this.dispose = function() {
777 |
778 | this.domElement.removeEventListener( 'contextmenu', contextmenu, false );
779 | this.domElement.removeEventListener( 'mousedown', onMouseDown, false );
780 | this.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
781 | this.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
782 |
783 | this.domElement.removeEventListener( 'touchstart', touchstart, false );
784 | this.domElement.removeEventListener( 'touchend', touchend, false );
785 | this.domElement.removeEventListener( 'touchmove', touchmove, false );
786 |
787 | document.removeEventListener( 'mousemove', onMouseMove, false );
788 | document.removeEventListener( 'mouseup', onMouseUp, false );
789 |
790 | window.removeEventListener( 'keydown', onKeyDown, false );
791 |
792 | }
793 |
794 | this.domElement.addEventListener( 'contextmenu', contextmenu, false );
795 |
796 | this.domElement.addEventListener( 'mousedown', onMouseDown, false );
797 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
798 | this.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
799 |
800 | this.domElement.addEventListener( 'touchstart', touchstart, false );
801 | this.domElement.addEventListener( 'touchend', touchend, false );
802 | this.domElement.addEventListener( 'touchmove', touchmove, false );
803 |
804 | window.addEventListener( 'keydown', onKeyDown, false );
805 |
806 | // force an update at start
807 | this.update();
808 |
809 | };
810 |
811 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
812 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
813 |
814 | Object.defineProperties( THREE.OrbitControls.prototype, {
815 |
816 | object: {
817 |
818 | get: function () {
819 |
820 | return this.constraint.object;
821 |
822 | }
823 |
824 | },
825 |
826 | target: {
827 |
828 | get: function () {
829 |
830 | return this.constraint.target;
831 |
832 | },
833 |
834 | set: function ( value ) {
835 |
836 | console.warn( 'THREE.OrbitControls: target is now immutable. Use target.set() instead.' );
837 | this.constraint.target.copy( value );
838 |
839 | }
840 |
841 | },
842 |
843 | minDistance : {
844 |
845 | get: function () {
846 |
847 | return this.constraint.minDistance;
848 |
849 | },
850 |
851 | set: function ( value ) {
852 |
853 | this.constraint.minDistance = value;
854 |
855 | }
856 |
857 | },
858 |
859 | maxDistance : {
860 |
861 | get: function () {
862 |
863 | return this.constraint.maxDistance;
864 |
865 | },
866 |
867 | set: function ( value ) {
868 |
869 | this.constraint.maxDistance = value;
870 |
871 | }
872 |
873 | },
874 |
875 | minZoom : {
876 |
877 | get: function () {
878 |
879 | return this.constraint.minZoom;
880 |
881 | },
882 |
883 | set: function ( value ) {
884 |
885 | this.constraint.minZoom = value;
886 |
887 | }
888 |
889 | },
890 |
891 | maxZoom : {
892 |
893 | get: function () {
894 |
895 | return this.constraint.maxZoom;
896 |
897 | },
898 |
899 | set: function ( value ) {
900 |
901 | this.constraint.maxZoom = value;
902 |
903 | }
904 |
905 | },
906 |
907 | minPolarAngle : {
908 |
909 | get: function () {
910 |
911 | return this.constraint.minPolarAngle;
912 |
913 | },
914 |
915 | set: function ( value ) {
916 |
917 | this.constraint.minPolarAngle = value;
918 |
919 | }
920 |
921 | },
922 |
923 | maxPolarAngle : {
924 |
925 | get: function () {
926 |
927 | return this.constraint.maxPolarAngle;
928 |
929 | },
930 |
931 | set: function ( value ) {
932 |
933 | this.constraint.maxPolarAngle = value;
934 |
935 | }
936 |
937 | },
938 |
939 | minAzimuthAngle : {
940 |
941 | get: function () {
942 |
943 | return this.constraint.minAzimuthAngle;
944 |
945 | },
946 |
947 | set: function ( value ) {
948 |
949 | this.constraint.minAzimuthAngle = value;
950 |
951 | }
952 |
953 | },
954 |
955 | maxAzimuthAngle : {
956 |
957 | get: function () {
958 |
959 | return this.constraint.maxAzimuthAngle;
960 |
961 | },
962 |
963 | set: function ( value ) {
964 |
965 | this.constraint.maxAzimuthAngle = value;
966 |
967 | }
968 |
969 | },
970 |
971 | enableDamping : {
972 |
973 | get: function () {
974 |
975 | return this.constraint.enableDamping;
976 |
977 | },
978 |
979 | set: function ( value ) {
980 |
981 | this.constraint.enableDamping = value;
982 |
983 | }
984 |
985 | },
986 |
987 | dampingFactor : {
988 |
989 | get: function () {
990 |
991 | return this.constraint.dampingFactor;
992 |
993 | },
994 |
995 | set: function ( value ) {
996 |
997 | this.constraint.dampingFactor = value;
998 |
999 | }
1000 |
1001 | },
1002 |
1003 | // backward compatibility
1004 |
1005 | noZoom: {
1006 |
1007 | get: function () {
1008 |
1009 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1010 | return ! this.enableZoom;
1011 |
1012 | },
1013 |
1014 | set: function ( value ) {
1015 |
1016 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1017 | this.enableZoom = ! value;
1018 |
1019 | }
1020 |
1021 | },
1022 |
1023 | noRotate: {
1024 |
1025 | get: function () {
1026 |
1027 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1028 | return ! this.enableRotate;
1029 |
1030 | },
1031 |
1032 | set: function ( value ) {
1033 |
1034 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1035 | this.enableRotate = ! value;
1036 |
1037 | }
1038 |
1039 | },
1040 |
1041 | noPan: {
1042 |
1043 | get: function () {
1044 |
1045 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1046 | return ! this.enablePan;
1047 |
1048 | },
1049 |
1050 | set: function ( value ) {
1051 |
1052 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1053 | this.enablePan = ! value;
1054 |
1055 | }
1056 |
1057 | },
1058 |
1059 | noKeys: {
1060 |
1061 | get: function () {
1062 |
1063 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1064 | return ! this.enableKeys;
1065 |
1066 | },
1067 |
1068 | set: function ( value ) {
1069 |
1070 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1071 | this.enableKeys = ! value;
1072 |
1073 | }
1074 |
1075 | },
1076 |
1077 | staticMoving : {
1078 |
1079 | get: function () {
1080 |
1081 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1082 | return ! this.constraint.enableDamping;
1083 |
1084 | },
1085 |
1086 | set: function ( value ) {
1087 |
1088 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1089 | this.constraint.enableDamping = ! value;
1090 |
1091 | }
1092 |
1093 | },
1094 |
1095 | dynamicDampingFactor : {
1096 |
1097 | get: function () {
1098 |
1099 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1100 | return this.constraint.dampingFactor;
1101 |
1102 | },
1103 |
1104 | set: function ( value ) {
1105 |
1106 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1107 | this.constraint.dampingFactor = value;
1108 |
1109 | }
1110 |
1111 | }
1112 |
1113 | } );
1114 |
1115 | }() );
--------------------------------------------------------------------------------
/demo/js/THREE.ConstantSpline.js:
--------------------------------------------------------------------------------
1 | THREE.ConstantSpline = function() {
2 |
3 | this.p0 = new THREE.Vector3();
4 | this.p1 = new THREE.Vector3();
5 | this.p2 = new THREE.Vector3();
6 | this.p3 = new THREE.Vector3();
7 |
8 | this.tmp = new THREE.Vector3();
9 | this.res = new THREE.Vector3();
10 | this.o = new THREE.Vector3();
11 |
12 | this.points = [];
13 | this.lPoints = [];
14 | this.steps = [];
15 |
16 | this.inc = .01;
17 | this.d = 0;
18 |
19 | this.distancesNeedUpdate = false;
20 |
21 | };
22 |
23 | THREE.ConstantSpline.prototype.calculate = function() {
24 |
25 | this.d = 0;
26 | this.points = [];
27 |
28 | this.o.copy( this.p0 );
29 |
30 | for( var j = 0; j <= 1; j += this.inc ) {
31 |
32 | var i = ( 1 - j );
33 | var ii = i * i;
34 | var iii = ii * i;
35 | var jj = j * j;
36 | var jjj = jj * j;
37 |
38 | this.res.set( 0, 0, 0 );
39 |
40 | this.tmp.copy( this.p0 );
41 | this.tmp.multiplyScalar( iii );
42 | this.res.add( this.tmp );
43 |
44 | this.tmp.copy( this.p1 );
45 | this.tmp.multiplyScalar( 3 * j * ii );
46 | this.res.add( this.tmp );
47 |
48 | this.tmp.copy( this.p2 );
49 | this.tmp.multiplyScalar( 3 * jj * i );
50 | this.res.add( this.tmp );
51 |
52 | this.tmp.copy( this.p3 );
53 | this.tmp.multiplyScalar( jjj );
54 | this.res.add( this.tmp );
55 |
56 | this.points.push( this.res.clone() );
57 |
58 | }
59 |
60 | this.points.push( this.p3.clone() );
61 |
62 | this.distancesNeedUpdate = true;
63 |
64 | };
65 |
66 | THREE.ConstantSpline.prototype.calculateDistances = function() {
67 |
68 | this.steps = [];
69 | this.d = 0;
70 |
71 | var from, to, td = 0;
72 |
73 | for( var j = 0; j < this.points.length - 1; j++ ) {
74 |
75 | this.points[ j ].distance = td;
76 | this.points[ j ].ac = this.d;
77 |
78 | from = this.points[ j ],
79 | to = this.points[ j + 1 ],
80 | td = to.distanceTo( from );
81 |
82 | this.d += td;
83 |
84 | }
85 |
86 | this.points[ this.points.length - 1 ].distance = 0;
87 | this.points[ this.points.length - 1 ].ac = this.d;
88 |
89 | }
90 |
91 | THREE.ConstantSpline.prototype.reticulate = function( settings ) {
92 |
93 | if( this.distancesNeedUpdate ) {
94 | this.calculateDistances();
95 | this.distancesNeedUpdate = false;
96 | }
97 |
98 | this.lPoints = [];
99 |
100 | var l = [];
101 |
102 | var steps, distancePerStep;
103 |
104 | if( settings.steps) {
105 | steps = settings.steps;
106 | distancePerStep = this.d / steps;
107 | }
108 |
109 | if( settings.distancePerStep ) {
110 | distancePerStep = settings.distancePerStep;
111 | steps = this.d / distancePerStep;
112 | }
113 |
114 | var d = 0,
115 | p = 0;
116 |
117 | this.lPoints = [];
118 |
119 | var current = new THREE.Vector3();
120 | current.copy( this.points[ 0 ].clone() );
121 | this.lPoints.push( current.clone() );
122 |
123 | function splitSegment( a, b, l ) {
124 |
125 | var t = b.clone();
126 | var d = 0;
127 | t.sub( a );
128 | var rd = t.length();
129 | t.normalize();
130 | t.multiplyScalar( distancePerStep );
131 | var s = Math.floor( rd / distancePerStep );
132 | for( var j = 0; j < s; j++ ) {
133 | a.add( t );
134 | l.push( a.clone() );
135 | d += distancePerStep;
136 | }
137 | return d;
138 | }
139 |
140 | for( var j = 0; j < this.points.length; j++ ) {
141 |
142 | if( this.points[ j ].ac - d > distancePerStep ) {
143 |
144 | d += splitSegment( current, this.points[ j ], this.lPoints );
145 |
146 | }
147 |
148 | }
149 | this.lPoints.push( this.points[ this.points.length - 1 ].clone() );
150 |
151 |
152 | };
--------------------------------------------------------------------------------
/demo/js/dat.gui.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * dat-gui JavaScript Controller Library
3 | * http://code.google.com/p/dat-gui
4 | *
5 | * Copyright 2011 Data Arts Team, Google Creative Lab
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | */
13 | var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(f,a){a=a||document;var d=a.createElement("link");d.type="text/css";d.rel="stylesheet";d.href=f;a.getElementsByTagName("head")[0].appendChild(d)},inject:function(f,a){a=a||document;var d=document.createElement("style");d.type="text/css";d.innerHTML=f;a.getElementsByTagName("head")[0].appendChild(d)}}}();
14 | dat.utils.common=function(){var f=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(a[c])||(d[c]=a[c])},this);return d},defaults:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(d[c])&&(d[c]=a[c])},this);return d},compose:function(){var d=a.call(arguments);return function(){for(var e=a.call(arguments),c=d.length-1;0<=c;c--)e=[d[c].apply(this,e)];return e[0]}},
15 | each:function(a,e,c){if(a)if(f&&a.forEach&&a.forEach===f)a.forEach(e,c);else if(a.length===a.length+0)for(var b=0,p=a.length;b
this.__max&&(a=this.__max);void 0!==this.__step&&0!=a%this.__step&&(a=Math.round(a/this.__step)*this.__step);return e.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__impliedStep=this.__step=a;this.__precision=d(a);return this}});return e}(dat.controllers.Controller,dat.utils.common);
29 | dat.controllers.NumberControllerBox=function(f,a,d){var e=function(c,b,f){function q(){var a=parseFloat(n.__input.value);d.isNaN(a)||n.setValue(a)}function l(a){var b=u-a.clientY;n.setValue(n.getValue()+b*n.__impliedStep);u=a.clientY}function r(){a.unbind(window,"mousemove",l);a.unbind(window,"mouseup",r)}this.__truncationSuspended=!1;e.superclass.call(this,c,b,f);var n=this,u;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",q);a.bind(this.__input,
30 | "blur",function(){q();n.__onFinishChange&&n.__onFinishChange.call(n,n.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",l);a.bind(window,"mouseup",r);u=b.clientY});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&(n.__truncationSuspended=!0,this.blur(),n.__truncationSuspended=!1)});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype,f.prototype,{updateDisplay:function(){var a=this.__input,b;if(this.__truncationSuspended)b=
31 | this.getValue();else{b=this.getValue();var d=Math.pow(10,this.__precision);b=Math.round(b*d)/d}a.value=b;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common);
32 | dat.controllers.NumberControllerSlider=function(f,a,d,e,c){function b(a,b,c,e,d){return e+(a-b)/(c-b)*(d-e)}var p=function(c,e,d,f,u){function A(c){c.preventDefault();var e=a.getOffset(k.__background),d=a.getWidth(k.__background);k.setValue(b(c.clientX,e.left,e.left+d,k.__min,k.__max));return!1}function g(){a.unbind(window,"mousemove",A);a.unbind(window,"mouseup",g);k.__onFinishChange&&k.__onFinishChange.call(k,k.getValue())}p.superclass.call(this,c,e,{min:d,max:f,step:u});var k=this;this.__background=
33 | document.createElement("div");this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",A);a.bind(window,"mouseup",g);A(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};p.superclass=f;p.useDefaultStyles=function(){d.inject(c)};e.extend(p.prototype,f.prototype,{updateDisplay:function(){var a=
34 | (this.getValue()-this.__min)/(this.__max-this.__min);this.__foreground.style.width=100*a+"%";return p.superclass.prototype.updateDisplay.call(this)}});return p}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}");
35 | dat.controllers.FunctionController=function(f,a,d){var e=function(c,b,d){e.superclass.call(this,c,b);var f=this;this.__button=document.createElement("div");this.__button.innerHTML=void 0===d?"Fire":d;a.bind(this.__button,"click",function(a){a.preventDefault();f.fire();return!1});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};e.superclass=f;d.extend(e.prototype,f.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.getValue().call(this.object);
36 | this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
37 | dat.controllers.BooleanController=function(f,a,d){var e=function(c,b){e.superclass.call(this,c,b);var d=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){d.setValue(!d.__prev)},!1);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};e.superclass=f;d.extend(e.prototype,f.prototype,{setValue:function(a){a=e.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&
38 | this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){!0===this.getValue()?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
39 | dat.color.toString=function(f){return function(a){if(1==a.a||f.isUndefined(a.a)){for(a=a.hex.toString(16);6>a.length;)a="0"+a;return"#"+a}return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common);
40 | dat.color.interpret=function(f,a){var d,e,c=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:f},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:f},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
41 | return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:f},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:f}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!=
42 | a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&&
43 | a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){e=!1;
44 | var b=1\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n