├── LICENSE
├── README.md
├── index.html
├── js
├── Detector.js
├── OrbitControls.js
├── Simulation.js
├── THREE.FBOHelper.js
├── THREE.GeometryUtils.js
├── dat.gui.min.js
├── isMobile.min.js
├── main-boxels.js
├── three.js
└── three.min.js
├── snapshot.jpg
├── spotlight.jpg
└── thumb.jpg
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
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 | # The Polygon Shredder
2 | The polygon shredder that takes many cubes and turns them into confetti
3 |
4 | [See the site here](https://www.clicktorelease.com/code/polygon-shredder/)
5 |
6 | 
7 |
8 | A WebGL experiment **write up coming soon!**
9 |
10 | Curl Noise GLSL function from [Isaac Cohen's gist](https://github.com/cabbibo/glsl-curl-noise)
11 |
12 | # The MIT License (MIT)
13 | **Copyright © 2016 Jaume Sanchez Elias**
14 |
15 | * * *
16 |
17 | Permission is hereby granted, free of charge, to any person obtaining a copy of
18 | this software and associated documentation files (the “Software”), to deal in
19 | the Software without restriction, including without limitation the rights to
20 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
21 | the Software, and to permit persons to whom the Software is furnished to do so,
22 | subject to the following conditions:
23 |
24 | The above copyright notice and this permission notice shall be included in all
25 | copies or substantial portions of the Software.
26 |
27 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
29 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
30 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
31 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | The Polygon Shredder
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
72 |
73 |
74 |
75 |
76 |
Polygon shredder
77 |
The polygon shredder that takes many cubes and turns them into confetti
78 |
79 |
80 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
Jaume Sanchez · clicktorelease · Twitter · GitHub
92 |
93 |
Loading...
94 |
95 |
96 |
97 | - Move mouse around, the particles will be generated following the mouse
98 | - Press space to pause, space again to resume
99 | - Click and drag to rotate the camera, use scroll to zoom in and out
100 | - Play with the controls and let's see what you can find!
101 |
102 |
103 | - Factor: speed at which the particles move
104 | - Evolution: the variation over time of the particles flow
105 | - Rotation: speed at which the particle field auto-rotates
106 | - Radius: radius of a sphere that repels particles
107 | - Pulsate: if enabled, the sphere pulsates, pushing in and out
108 | - ScaleX/Y/Z: set the scale of each axis of the particles
109 | - Scale: change the overall scale of the particles
110 |
111 |
112 |
Too many? Too litte?
Try
113 |
Close this message
114 |
115 |
116 |
117 |
132 |
133 |
134 |
I'm sorry, no fallback for browsers with no working WebGL.
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
163 |
164 |
482 |
483 |
600 |
601 |
699 |
700 |
719 |
720 |
742 |
743 |
744 |
745 |
--------------------------------------------------------------------------------
/js/Detector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | * @author mr.doob / http://mrdoob.com/
4 | */
5 |
6 | Detector = {
7 |
8 | canvas: !! window.CanvasRenderingContext2D,
9 | webgl: ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(),
10 | workers: !! window.Worker,
11 | fileapi: window.File && window.FileReader && window.FileList && window.Blob,
12 |
13 | getWebGLErrorMessage: function () {
14 |
15 | var element = document.createElement( 'div' );
16 | element.id = 'webgl-error-message';
17 | element.style.fontFamily = 'monospace';
18 | element.style.fontSize = '13px';
19 | element.style.fontWeight = 'normal';
20 | element.style.textAlign = 'center';
21 | element.style.background = '#fff';
22 | element.style.color = '#000';
23 | element.style.padding = '1.5em';
24 | element.style.width = '400px';
25 | element.style.margin = '5em auto 0';
26 |
27 | if ( ! this.webgl ) {
28 |
29 | element.innerHTML = window.WebGLRenderingContext ? [
30 | 'Your graphics card does not seem to support WebGL.
',
31 | 'Find out how to get it here.'
32 | ].join( '\n' ) : [
33 | 'Your browser does not seem to support WebGL.
',
34 | 'Find out how to get it here.'
35 | ].join( '\n' );
36 |
37 | }
38 |
39 | return element;
40 |
41 | },
42 |
43 | addGetWebGLMessage: function ( parameters ) {
44 |
45 | var parent, id, element;
46 |
47 | parameters = parameters || {};
48 |
49 | parent = parameters.parent !== undefined ? parameters.parent : document.body;
50 | id = parameters.id !== undefined ? parameters.id : 'oldie';
51 |
52 | element = Detector.getWebGLErrorMessage();
53 | element.id = id;
54 |
55 | parent.appendChild( element );
56 |
57 | }
58 |
59 | };
--------------------------------------------------------------------------------
/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 | }() );
--------------------------------------------------------------------------------
/js/Simulation.js:
--------------------------------------------------------------------------------
1 | function Simulation( renderer, width, height ) {
2 |
3 | this.width = width;
4 | this.height = height;
5 | this.renderer = renderer;
6 | this.targetPos = 0;
7 |
8 | this.data = new Float32Array( this.width * this.height * 4 );
9 |
10 | // var geometry = new THREE.IcosahedronGeometry( 2, 3 );
11 | // var geometry = new THREE.TorusKnotGeometry( 2, .6, 100, 16 );
12 | // var geometry = new THREE.BoxGeometry( 1,1,1 );
13 |
14 | // var m = new THREE.Mesh( geometry, new THREE.MeshNormalMaterial () );
15 | // scene.add( m );
16 |
17 | // var points = THREE.GeometryUtils.randomPointsInGeometry( geometry, this.width * this.height );
18 |
19 | /*
20 | var r = 2;
21 | for( var i = 0, l = this.width * this.height; i < l; i ++ ) {
22 |
23 | this.data[ i * 4 ] = ( .5 - Math.random() ) * r;
24 | this.data[ i * 4 + 1 ] = ( .5 - Math.random() ) * r;
25 | this.data[ i * 4 + 2 ] = ( .5 - Math.random() ) * r;
26 | this.data[ i * 4 + 3 ] = Math.random() * 100; // frames life
27 |
28 | }*/
29 |
30 | var r = 1;
31 | for( var i = 0, l = this.width * this.height; i < l; i ++ ) {
32 |
33 | var phi = Math.random() * 2 * Math.PI;
34 | var costheta = Math.random() * 2 -1;
35 | var theta = Math.acos( costheta );
36 | r = .85 + .15 * Math.random();
37 |
38 | this.data[ i * 4 ] = r * Math.sin( theta) * Math.cos( phi );
39 | this.data[ i * 4 + 1 ] = r * Math.sin( theta) * Math.sin( phi );
40 | this.data[ i * 4 + 2 ] = r * Math.cos( theta );
41 | this.data[ i * 4 + 3 ] = Math.random() * 100; // frames life
42 |
43 | }
44 |
45 | /* points.forEach( function( p, i ) {
46 |
47 | this.data[ i * 4 ] = p.x;
48 | this.data[ i * 4 + 1 ] = p.y;
49 | this.data[ i * 4 + 2 ] = p.z;
50 | this.data[ i * 4 + 3 ] = Math.random() * 100; // frames life
51 |
52 | }.bind( this ) );*/
53 |
54 |
55 | var floatType = isMobile.apple.device ? THREE.HalfFloatType : THREE.FloatType;
56 |
57 | this.texture = new THREE.DataTexture( this.data, this.width, this.height, THREE.RGBAFormat, THREE.FloatType );
58 | this.texture.minFilter = THREE.NearestFilter;
59 | this.texture.magFilter = THREE.NearestFilter;
60 | this.texture.needsUpdate = true;
61 |
62 | this.rtTexturePos = new THREE.WebGLRenderTarget( this.width, this.height, {
63 | wrapS: THREE.ClampToEdgeWrapping,
64 | wrapT: THREE.ClampToEdgeWrapping,
65 | minFilter: THREE.NearestFilter,
66 | magFilter: THREE.NearestFilter,
67 | format: THREE.RGBAFormat,
68 | type: floatType,
69 | stencilBuffer: false,
70 | depthBuffer: false,
71 | generateMipmaps: false
72 | });
73 |
74 | this.targets = [ this.rtTexturePos, this.rtTexturePos.clone() ];
75 |
76 | this.simulationShader = new THREE.ShaderMaterial({
77 |
78 | uniforms: {
79 | active: { type: 'f', value: 1 },
80 | width: { type: "f", value: this.width },
81 | height: { type: "f", value: this.height },
82 | oPositions: { type: "t", value: this.texture },
83 | tPositions: { type: "t", value: null },
84 | timer: { type: "f", value: 0 },
85 | delta: { type: "f", value: 0 },
86 | speed: { type: "f", value: .5 },
87 | reset: { type: 'f', value: 0 },
88 | offset: { type: 'v3', value: new THREE.Vector3( 0, 0, 0 ) },
89 | genScale: { type: 'f', value: 1 },
90 | factor: { type: 'f', value: .5 },
91 | evolution: { type: 'f', value: .5 },
92 | inverseModelViewMatrix: { type: 'm4', value: new THREE.Matrix4() },
93 | radius: { type: 'f', value: 2 }
94 | },
95 |
96 | vertexShader: document.getElementById('texture_vertex_simulation_shader').textContent,
97 | fragmentShader: document.getElementById('texture_fragment_simulation_shader').textContent,
98 | side: THREE.DoubleSide
99 |
100 | });
101 |
102 | this.simulationShader.uniforms.tPositions.value = this.texture;
103 |
104 | this.rtScene = new THREE.Scene();
105 | this.rtCamera = new THREE.OrthographicCamera( -this.width / 2, this.width / 2, -this.height / 2, this.height / 2, -500, 1000 );
106 | this.rtQuad = new THREE.Mesh(
107 | new THREE.PlaneBufferGeometry( this.width, this.height ),
108 | this.simulationShader
109 | );
110 | this.rtScene.add( this.rtQuad );
111 |
112 | this.renderer.render( this.rtScene, this.rtCamera, this.rtTexturePos );
113 |
114 | this.plane = new THREE.Mesh( new THREE.PlaneGeometry( 64, 64 ), new THREE.MeshBasicMaterial( { map: this.rtTexturePos, side: THREE.DoubleSide } ) );
115 | //scene.add( this.plane );
116 |
117 | }
118 |
119 | Simulation.prototype.render = function( time, delta ) {
120 |
121 | this.simulationShader.uniforms.timer.value = time;
122 | this.simulationShader.uniforms.delta.value = delta;
123 |
124 | this.simulationShader.uniforms.tPositions.value = this.targets[ this.targetPos ];
125 | this.targetPos = 1 - this.targetPos;
126 | this.renderer.render( this.rtScene, this.rtCamera, this.targets[ this.targetPos ] );
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/js/THREE.FBOHelper.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 |
3 | "use strict";
4 |
5 | var root = this
6 |
7 | var has_require = typeof require !== 'undefined'
8 |
9 | var THREE = root.THREE || has_require && require('three')
10 | if( !THREE )
11 | throw new Error( 'FBOHelper requires three.js' )
12 |
13 | "use strict";
14 |
15 | var layerCSS = `
16 | #fboh-fbos-list{
17 | all: unset;
18 | position: fixed;
19 | left: 0;
20 | top: 0;
21 | z-index: 1000000;
22 | width: 150px;
23 | }
24 | #fboh-fbos-list, #fboh-fbos-list *, #fboh-hotspot, #fboh-label, #fboh-info{
25 | box-sizing: border-box;
26 | font-family: 'Roboto Mono', 'courier new', courier, monospace;
27 | font-size: 11px;
28 | line-height: 1.4em;
29 | }
30 | #fboh-fbos-list li{
31 | cursor: pointer;
32 | color: white;
33 | width: 100%;
34 | padding: 4px 0;
35 | border-top: 1px solid #888;
36 | border-bottom: 1px solid black;
37 | background-color: #444;
38 | text-align: center;
39 | text-shadow: 0 -1px black;
40 | }
41 | #fboh-fbos-list li:hover{
42 | background-color: rgba( 158, 253, 56, .5 );
43 | }
44 | #fboh-fbos-list li.active{
45 | background-color: rgba( 158, 253, 56, .5 );
46 | color: white;
47 | text-shadow: 0 1px black;
48 | }
49 | #fboh-hotspot{
50 | position: absolute;
51 | left: 0;
52 | top: 0;
53 | background-color: rgba( 158, 253, 56,.5);
54 | pointer-events: none;
55 | }
56 | #fboh-label{
57 | position: absolute;
58 | left: 0;
59 | bottom: 0;
60 | transform-origin: bottom left;
61 | pointer-events: none;
62 | }
63 | #fboh-info{
64 | display: none;
65 | position: absolute;
66 | left: 160px;
67 | top: 10px;
68 | pointer-events: none;
69 | }
70 | .fboh-card{
71 | display: block;
72 | white-space: nowrap;
73 | color: black;
74 | padding: 10px;
75 | background-color: white;
76 | border: 1px solid black;
77 | }
78 | `;
79 |
80 | let formats = {}
81 | formats[ THREE.AlphaFormat ] = 'THREE.AlphaFormat';
82 | formats[ THREE.RGBFormat ] = 'THREE.RGBFormat';
83 | formats[ THREE.RGBAFormat ] = 'THREE.RGBAFormat';
84 | formats[ THREE.LuminanceFormat ] = 'THREE.LuminanceFormat';
85 | formats[ THREE.LuminanceAlphaFormat ] = 'THREE.LuminanceAlphaFormat';
86 | //formats[ THREE.RGBEFormat ] = 'THREE.RGBEFormat';
87 |
88 | let types = {}
89 | types[ THREE.UnsignedByteType ] = 'THREE.UnsignedByteType';
90 | types[ THREE.ByteType ] = 'THREE.ByteType';
91 | types[ THREE.ShortType ] = 'THREE.ShortType';
92 | types[ THREE.UnsignedShortType ] = 'THREE.UnsignedShortType';
93 | types[ THREE.IntType ] = 'THREE.IntType';
94 | types[ THREE.UnsignedIntType ] = 'THREE.UnsignedIntType';
95 | types[ THREE.FloatType ] = 'THREE.FloatType';
96 | types[ THREE.HalfFloatType ] = 'THREE.HalfFloatType';
97 | types[ THREE.UnsignedShort4444Type ] = 'THREE.UnsignedShort4444Type';
98 | types[ THREE.UnsignedShort5551Type ] = 'THREE.UnsignedShort5551Type';
99 | types[ THREE.UnsignedShort565Type ] = 'THREE.UnsignedShort565Type';
100 |
101 | class FBOHelper {
102 |
103 | constructor( renderer ) {
104 |
105 | this.renderer = renderer;
106 | this.autoUpdate = false;
107 | this.fbos = []
108 | this.list = document.createElement( 'ul' );
109 | this.list.setAttribute( 'id', 'fboh-fbos-list' );
110 | document.body.appendChild( this.list );
111 |
112 | this.scene = new THREE.Scene();
113 | this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, .000001, 1000 );
114 |
115 | this.raycaster = new THREE.Raycaster();
116 | this.mouse = new THREE.Vector2();
117 |
118 | this.grid = document.createElement( 'div' );
119 | this.grid.setAttribute( 'style', 'position: fixed; left: 50%; top: 50%; border: 1px solid #000000; transform: translate3d(-50%, -50%, 0 ); box-shadow: 0 0 50px black; display: none' );
120 | this.grid.setAttribute( 'id', 'bfoh-grid' );
121 | document.body.appendChild( this.grid );
122 |
123 | this.hotspot = document.createElement( 'div' );
124 | this.hotspot.setAttribute( 'id', 'fboh-hotspot' );
125 | this.grid.appendChild( this.hotspot );
126 |
127 | this.label = document.createElement( 'div' );
128 | this.label.setAttribute( 'id', 'fboh-label' );
129 | this.label.className = 'fboh-card';
130 | this.hotspot.appendChild( this.label );
131 |
132 | this.info = document.createElement( 'div' );
133 | this.info.setAttribute( 'id', 'fboh-info' );
134 | this.info.className = 'fboh-card';
135 | document.body.appendChild( this.info );
136 |
137 | this.currentObj = null;
138 | this.currentU = 0;
139 | this.currentV = 0;
140 |
141 | this.fboMap = new Map();
142 |
143 | this.offsetX = 0;
144 | this.offsetY = 0;
145 |
146 | this.grid.appendChild( this.hotspot );
147 |
148 | const head = window.document.head || window.document.getElementsByTagName('head')[0];
149 | const style = window.document.createElement('style');
150 |
151 | style.type = 'text/css';
152 | if (style.styleSheet){
153 | style.styleSheet.cssText = layerCSS;
154 | } else {
155 | style.appendChild(document.createTextNode(layerCSS));
156 | }
157 |
158 | head.appendChild(style);
159 |
160 | const ss = document.createElement( 'link' );
161 | ss.type = 'text/css';
162 | ss.rel = 'stylesheet';
163 | ss.href = 'https://fonts.googleapis.com/css?family=Roboto+Mono';
164 |
165 | head.appendChild( ss );
166 |
167 | this.grid.addEventListener( 'wheel', e => {
168 |
169 | var direction = ( e.deltaY < 0 ) ? 1 : -1;
170 |
171 | this.camera.zoom += direction / 50;
172 | this.camera.updateProjectionMatrix();
173 | this.grid.style.transform = `translate3d(-50%, -50%, 0 ) scale(${this.camera.zoom},${this.camera.zoom}) translate3d(${this.offsetX}px,${this.offsetY}px,0) `;
174 | this.label.style.transform = `scale(${1/this.camera.zoom},${1/this.camera.zoom})`;
175 | this.hotspot.style.transform = `scale(${1/this.camera.zoom},${1/this.camera.zoom})`;
176 | this.hotspot.style.borderWidth = `${1/this.camera.zoom}px`;
177 | this.readPixel( this.currentObj, this.currentU, this.currentV );
178 |
179 | } );
180 |
181 | let dragging = false;
182 | let mouseStart = { x: 0, y: 0 };
183 | let offsetStart = { x: 0, y: 0 };
184 |
185 | this.grid.addEventListener( 'mousedown', e => {
186 |
187 | dragging = true;
188 | mouseStart.x = e.clientX;
189 | mouseStart.y = e.clientY;
190 | offsetStart.x = this.offsetX;
191 | offsetStart.y = this.offsetY;
192 |
193 | } );
194 |
195 | this.grid.addEventListener( 'mouseup', e => {
196 |
197 | dragging = false;
198 |
199 | } );
200 |
201 | this.grid.addEventListener( 'mouseout', e => {
202 |
203 | this.label.style.display = 'none';
204 | dragging = false;
205 |
206 | } );
207 |
208 | this.grid.addEventListener( 'mouseover', e => {
209 |
210 | this.label.style.display = 'block';
211 |
212 | } );
213 |
214 | this.grid.addEventListener( 'mousemove', e => {
215 |
216 | if( dragging ) {
217 |
218 | this.offsetX = offsetStart.x + ( e.clientX - mouseStart.x ) / this.camera.zoom;
219 | this.offsetY = offsetStart.y + ( e.clientY - mouseStart.y ) / this.camera.zoom;
220 | this.camera.position.x = -this.offsetX;
221 | this.camera.position.y = this.offsetY;
222 |
223 | this.grid.style.transform = `translate3d(-50%, -50%, 0 ) scale(${this.camera.zoom},${this.camera.zoom}) translate3d(${this.offsetX}px,${this.offsetY}px,0)`;
224 |
225 | } else {
226 |
227 | this.mouse.x = ( e.clientX / renderer.domElement.clientWidth ) * 2 - 1;
228 | this.mouse.y = - ( e.clientY / renderer.domElement.clientHeight ) * 2 + 1;
229 | this.raycaster.setFromCamera( this.mouse, this.camera );
230 |
231 | const intersects = this.raycaster.intersectObject( this.currentObj.quad, true );
232 |
233 | if ( intersects.length > 0 ) {
234 |
235 | this.readPixel( this.fboMap.get( intersects[ 0 ].object ), intersects[ 0 ].uv.x, intersects[ 0 ].uv.y );
236 | this.label.style.display = 'block';
237 |
238 | } else {
239 |
240 | this.label.style.display = 'none';
241 |
242 | }
243 |
244 | }
245 |
246 | } );
247 |
248 | window.addEventListener( 'keydown', e => {
249 | if( e.keyCode === 27 ) {
250 | this.hide();
251 | }
252 | } );
253 |
254 | this.grid.addEventListener( 'keydown', e => {
255 | if( e.keyCode === 27 ) {
256 | this.hide();
257 | }
258 | } );
259 |
260 | }
261 |
262 | hide() {
263 |
264 | this.hideAll();
265 | this.info.style.display = 'none';
266 | this.grid.style.display = 'none';
267 | this.currentObj = null;
268 |
269 | }
270 |
271 | attach( fbo, name, formatter ) {
272 |
273 | var li = document.createElement( 'li' );
274 |
275 | li.textContent = name;
276 |
277 | if( fbo.image ) {
278 | fbo.width = fbo.image.width;
279 | fbo.height = fbo.image.height;
280 | }
281 |
282 | const width = 600;
283 | const height = fbo.height * width / fbo.width;
284 |
285 | const material = new THREE.MeshBasicMaterial( { map: fbo, side: THREE.DoubleSide } );
286 | const quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 1, 1 ), material );
287 | if( !fbo.flipY ) quad.rotation.x = Math.PI;
288 | quad.visible = false;
289 | quad.width = width;
290 | quad.height = height;
291 | quad.scale.set( width, height, 1. );
292 | this.scene.add( quad );
293 |
294 | var fboData = {
295 | width: width,
296 | height: height,
297 | name: name,
298 | fbo: fbo,
299 | flipY: fbo.flipY,
300 | li: li,
301 | visible: false,
302 | quad: quad,
303 | material: material,
304 | formatter: formatter
305 | };
306 | this.fbos.push( fboData );
307 | this.fboMap.set( quad, fboData );
308 |
309 | li.addEventListener( 'click', e => {
310 | quad.visible = !quad.visible;
311 | if( quad.visible ) {
312 | this.hideAll();
313 | quad.visible = true;
314 | li.classList.add( 'active' );
315 | this.info.style.display = 'block';
316 | this.grid.style.display = 'block';
317 | this.grid.style.width = ( fboData.width + 2 ) + 'px';
318 | this.grid.style.height = ( fboData.height + 2 ) + 'px';
319 | this.currentObj = fboData;
320 | this.info.innerHTML = `Width: ${fbo.width} Height: ${fbo.height}
Format: ${formats[fbo.texture?fbo.texture.format:fbo.format]} Type: ${types[fbo.texture?fbo.texture.type:fbo.type]}`;
321 | } else {
322 | this.info.style.display = 'none';
323 | li.classList.remove( 'active' );
324 | this.grid.style.display = 'none';
325 | this.currentObj = null;
326 | }
327 | } );
328 |
329 | this.buildList();
330 |
331 | }
332 |
333 | detach( f ) {
334 |
335 | var p = 0;
336 | for( var fbo of this.fbos ) {
337 | if( fbo.fbo === f ) {
338 | this.fbos.splice( p, 1 )
339 | }
340 | p++;
341 | }
342 |
343 | this.buildList();
344 |
345 | }
346 |
347 | refreshFBO( f ) {
348 |
349 | for( var fbo of this.fbos ) {
350 | if( fbo.fbo === f ) {
351 | const width = 600;
352 | const height = f.height * width / f.width;
353 | fbo.width = width;
354 | fbo.height = height;
355 | fbo.quad.width = width;
356 | fbo.quad.height = height;
357 | fbo.quad.scale.set( width, height, 1. );
358 | }
359 | }
360 |
361 | }
362 |
363 | hideAll() {
364 |
365 | this.fbos.forEach( fbo => {
366 | fbo.quad.visible = false;
367 | fbo.li.classList.remove( 'active' );
368 | } );
369 |
370 | }
371 |
372 | buildList() {
373 |
374 | while( this.list.firstChild ) this.list.removeChild( this.list.firstChild );
375 |
376 | for( var fbo of this.fbos ) {
377 | this.list.appendChild( fbo.li );
378 | }
379 |
380 | }
381 |
382 | setSize( w, h ) {
383 |
384 | this.camera.left = w / - 2;
385 | this.camera.right = w / 2;
386 | this.camera.top = h / 2;
387 | this.camera.bottom = h / - 2;
388 |
389 | this.camera.updateProjectionMatrix();
390 |
391 | }
392 |
393 | readPixel( obj, u, v ) {
394 |
395 | this.currentU = u;
396 | this.currentV = v;
397 |
398 | if( this.currentObj === null ) return;
399 |
400 | const fbo = obj.fbo;
401 |
402 | const x = ~~( fbo.width * u );
403 | const y = ~~( fbo.height * v );
404 |
405 | let types = {}
406 | types[ THREE.UnsignedByteType ] = Uint8Array;
407 | types[ THREE.ByteType ] = Int8Array;
408 | types[ THREE.ShortType ] = Int16Array;
409 | types[ THREE.UnsignedShortType ] = Uint16Array;
410 | types[ THREE.IntType ] = Int32Array;
411 | types[ THREE.UnsignedIntType ] = Uint32Array;
412 | types[ THREE.FloatType ] = Float32Array;
413 | types[ THREE.HalfFloatType ] = null;
414 | types[ THREE.UnsignedShort4444Type ] = Uint16Array;
415 | types[ THREE.UnsignedShort5551Type ] = Uint16Array;
416 | types[ THREE.UnsignedShort565Type ] = Uint16Array;
417 |
418 | var type = types[ fbo.texture ? fbo.texture.type : fbo.type ];
419 | if( type === null ) {
420 | console.warning( fbo.texture ? fbo.texture.type : fbo.type + ' not supported' );
421 | return;
422 | }
423 |
424 | const pixelBuffer = new ( type )( 4 );
425 |
426 | this.renderer.readRenderTargetPixels( fbo, x, y, 1, 1, pixelBuffer );
427 | const posTxt = `X : ${x} Y: ${y} u: ${u} v: ${v}`;
428 | const dataTxt = obj.formatter ?
429 | obj.formatter( {
430 | x: x,
431 | y: y,
432 | u: u,
433 | v: v,
434 | r: pixelBuffer[ 0 ],
435 | g: pixelBuffer[ 1 ],
436 | b: pixelBuffer[ 2 ],
437 | a: pixelBuffer[ 3 ]
438 | } )
439 | :
440 | `R: ${pixelBuffer[ 0 ]} G: ${pixelBuffer[ 1 ]} B: ${pixelBuffer[ 2 ]} A: ${pixelBuffer[ 3 ]}`;
441 | this.label.innerHTML = `${posTxt}
${dataTxt}`;
442 |
443 | const ox = ~~( u * fbo.width ) * obj.quad.width / fbo.width;
444 | const oy = ~~( obj.flipY ? ( 1 - v ) * fbo.height : v * fbo.height ) * obj.quad.height / fbo.height;
445 | this.hotspot.style.width = `${obj.quad.width / fbo.width}px`;
446 | this.hotspot.style.height = `${obj.quad.height / fbo.height}px`;
447 | this.hotspot.style.transform = `translate3d(${ox}px,${oy}px,0)`;
448 | this.label.style.bottom = ( obj.quad.height / fbo.height ) + 'px';
449 |
450 | }
451 |
452 |
453 | show( state ) {
454 |
455 | this.list.style.display = state ? 'block' : 'none';
456 |
457 | }
458 |
459 | update() {
460 |
461 | this.renderer.autoClear = false;
462 | this.renderer.render( this.scene, this.camera );
463 | this.renderer.autoClear = true;
464 | if( this.autoUpdate ) this.readPixel( this.currentObj, this.currentU, this.currentV );
465 |
466 | }
467 |
468 | }
469 |
470 | if( typeof exports !== 'undefined' ) {
471 | if( typeof module !== 'undefined' && module.exports ) {
472 | exports = module.exports = FBOHelper
473 | }
474 | exports.FBOHelper = FBOHelper
475 | }
476 | else {
477 | root.FBOHelper = FBOHelper
478 | }
479 |
480 | }).call(this);
481 |
482 |
--------------------------------------------------------------------------------
/js/THREE.GeometryUtils.js:
--------------------------------------------------------------------------------
1 | THREE.GeometryUtils.randomPointsInGeometry = function ( geometry, n ) {
2 |
3 | var face, i,
4 | faces = geometry.faces,
5 | vertices = geometry.vertices,
6 | il = faces.length,
7 | totalArea = 0,
8 | cumulativeAreas = [],
9 | vA, vB, vC;
10 |
11 | // precompute face areas
12 |
13 | for ( i = 0; i < il; i ++ ) {
14 |
15 | face = faces[ i ];
16 |
17 | vA = vertices[ face.a ];
18 | vB = vertices[ face.b ];
19 | vC = vertices[ face.c ];
20 |
21 | face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC );
22 |
23 | totalArea += face._area;
24 |
25 | cumulativeAreas[ i ] = totalArea;
26 |
27 | }
28 |
29 | // binary search cumulative areas array
30 |
31 | function binarySearchIndices( value ) {
32 |
33 | function binarySearch( start, end ) {
34 |
35 | // return closest larger index
36 | // if exact number is not found
37 |
38 | if ( end < start )
39 | return start;
40 |
41 | var mid = start + Math.floor( ( end - start ) / 2 );
42 |
43 | if ( cumulativeAreas[ mid ] > value ) {
44 |
45 | return binarySearch( start, mid - 1 );
46 |
47 | } else if ( cumulativeAreas[ mid ] < value ) {
48 |
49 | return binarySearch( mid + 1, end );
50 |
51 | } else {
52 |
53 | return mid;
54 |
55 | }
56 |
57 | }
58 |
59 | var result = binarySearch( 0, cumulativeAreas.length - 1 );
60 | return result;
61 |
62 | }
63 |
64 | // pick random face weighted by face area
65 |
66 | var r, index,
67 | result = [];
68 |
69 | var stats = {};
70 |
71 | for ( i = 0; i < n; i ++ ) {
72 |
73 | r = THREE.Math.random16() * totalArea;
74 |
75 | index = binarySearchIndices( r );
76 |
77 | result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry );
78 |
79 | if ( ! stats[ index ] ) {
80 |
81 | stats[ index ] = 1;
82 |
83 | } else {
84 |
85 | stats[ index ] += 1;
86 |
87 | }
88 |
89 | }
90 |
91 | return result;
92 |
93 | }
94 |
95 | THREE.GeometryUtils.triangleArea = function () {
96 |
97 | var vector1 = new THREE.Vector3();
98 | var vector2 = new THREE.Vector3();
99 |
100 | return function ( vectorA, vectorB, vectorC ) {
101 |
102 | vector1.subVectors( vectorB, vectorA );
103 | vector2.subVectors( vectorC, vectorA );
104 | vector1.cross( vector2 );
105 |
106 | return 0.5 * vector1.length();
107 |
108 | };
109 |
110 | }()
111 |
112 | THREE.GeometryUtils.randomPointInTriangle = function () {
113 |
114 | var vector = new THREE.Vector3();
115 |
116 | return function ( vectorA, vectorB, vectorC ) {
117 |
118 | var point = new THREE.Vector3();
119 |
120 | var a = THREE.Math.random16();
121 | var b = THREE.Math.random16();
122 |
123 | if ( ( a + b ) > 1 ) {
124 |
125 | a = 1 - a;
126 | b = 1 - b;
127 |
128 | }
129 |
130 | var c = 1 - a - b;
131 |
132 | point.copy( vectorA );
133 | point.multiplyScalar( a );
134 |
135 | vector.copy( vectorB );
136 | vector.multiplyScalar( b );
137 |
138 | point.add( vector );
139 |
140 | vector.copy( vectorC );
141 | vector.multiplyScalar( c );
142 |
143 | point.add( vector );
144 |
145 | return point;
146 |
147 | };
148 |
149 | }()
150 |
151 | THREE.GeometryUtils.randomPointInFace = function ( face, geometry ) {
152 |
153 | var vA, vB, vC;
154 |
155 | vA = geometry.vertices[ face.a ];
156 | vB = geometry.vertices[ face.b ];
157 | vC = geometry.vertices[ face.c ];
158 |
159 | return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC );
160 |
161 | }
--------------------------------------------------------------------------------
/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(e,a){var a=a||document,c=a.createElement("link");c.type="text/css";c.rel="stylesheet";c.href=e;a.getElementsByTagName("head")[0].appendChild(c)},inject:function(e,a){var a=a||document,c=document.createElement("style");c.type="text/css";c.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(c)}}}();
14 | dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(c[f]=a[f])},this);return c},defaults:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(c[f])&&(c[f]=a[f])},this);return c},compose:function(){var c=a.call(arguments);return function(){for(var d=a.call(arguments),f=c.length-1;f>=0;f--)d=[c[f].apply(this,d)];return d[0]}},
15 | each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var b=0,n=a.length;b-1?d.length-d.indexOf(".")-1:0};c.superclass=e;a.extend(c.prototype,e.prototype,{setValue:function(a){if(this.__min!==void 0&&athis.__max)a=this.__max;this.__step!==void 0&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step);return c.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.__step=a;return this}});return c}(dat.controllers.Controller,dat.utils.common);
29 | dat.controllers.NumberControllerBox=function(e,a,c){var d=function(f,b,e){function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedStep);o=a.clientY}function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}this.__truncationSuspended=false;d.superclass.call(this,f,b,e);var l=this,o;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",h);
30 | a.bind(this.__input,"blur",function(){h();l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",j);a.bind(window,"mouseup",m);o=b.clientY});a.bind(this.__input,"keydown",function(a){if(a.keyCode===13)l.__truncationSuspended=true,this.blur(),l.__truncationSuspended=false});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input,
31 | b;if(this.__truncationSuspended)b=this.getValue();else{b=this.getValue();var c=Math.pow(10,this.__precision);b=Math.round(b*c)/c}a.value=b;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common);
32 | dat.controllers.NumberControllerSlider=function(e,a,c,d,f){var b=function(d,c,f,e,l){function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.getWidth(g.__background);g.setValue(g.__min+(g.__max-g.__min)*((b.clientX-d.left)/(d.left+c-d.left)));return false}function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y);g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}b.superclass.call(this,d,c,{min:f,max:e,step:l});var g=this;this.__background=document.createElement("div");
33 | this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",o);a.bind(window,"mouseup",y);o(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};b.superclass=e;b.useDefaultStyles=function(){c.inject(f)};d.extend(b.prototype,e.prototype,{updateDisplay:function(){this.__foreground.style.width=
34 | (this.getValue()-this.__min)/(this.__max-this.__min)*100+"%";return b.superclass.prototype.updateDisplay.call(this)}});return b}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,".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(e,a,c){var d=function(c,b,e){d.superclass.call(this,c,b);var h=this;this.__button=document.createElement("div");this.__button.innerHTML=e===void 0?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();h.fire();return false});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;c.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this,
36 | this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
37 | dat.controllers.BooleanController=function(e,a,c){var d=function(c,b){d.superclass.call(this,c,b);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},false);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&
38 | this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){this.getValue()===true?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=true):this.__checkbox.checked=false;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
39 | dat.color.toString=function(e){return function(a){if(a.a==1||e.isUndefined(a.a)){for(a=a.hex.toString(16);a.length<6;)a="0"+a;return"#"+a}else return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common);
40 | dat.color.interpret=function(e,a){var c,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
41 | return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{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 a.length!=
42 | 3?false:{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 a.length!=4?false:{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}:false},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}:false},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}:false},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}:false},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d=
44 | false;var b=arguments.length>1?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(b))return a.each(e.conversions,function(e,f){c=e.read(b);if(d===false&&c!==false)return d=c,c.conversionName=f,c.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common);
45 | dat.GUI=dat.gui.GUI=function(e,a,c,d,f,b,n,h,j,m,l,o,y,g,i){function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no property "'+r+'"');c.color?b=new l(b,r):(b=[b,r].concat(c.factoryArgs),b=d.apply(a,b));if(c.before instanceof f)c.before=c.before.__li;t(a,b);g.addClass(b.domElement,"c");r=document.createElement("span");g.addClass(r,"property-name");r.innerHTML=b.property;var e=document.createElement("div");e.appendChild(r);e.appendChild(b.domElement);c=s(a,e,c.before);g.addClass(c,k.CLASS_CONTROLLER_ROW);
46 | g.addClass(c,typeof b.getValue());p(a,c,b);a.__controllers.push(b);return b}function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b);d?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(arguments.length>1)return c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[i.toArray(arguments)]});if(i.isArray(b)||i.isObject(b))return c.remove(),q(a,c.object,c.property,
47 | {before:c.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){c.__li.firstElementChild.firstElementChild.innerHTML=a;return c},listen:function(){c.__gui.listen(c);return c},remove:function(){c.__gui.remove(c);return c}});if(c instanceof j){var e=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});i.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],H=e[a];c[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);b.apply(c,a);return H.apply(e,a)}});
48 | g.addClass(d,"has-slider");c.domElement.insertBefore(e.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var f=function(b){return i.isNumber(c.__min)&&i.isNumber(c.__max)?(c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=i.compose(f,c.min);c.max=i.compose(f,c.max)}else if(c instanceof b)g.bind(d,"click",function(){g.fakeEvent(c.__checkbox,"click")}),g.bind(c.__checkbox,"click",function(a){a.stopPropagation()});
49 | else if(c instanceof n)g.bind(d,"click",function(){g.fakeEvent(c.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(c.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(c.__button,"hover")});else if(c instanceof l)g.addClass(d,"color"),c.updateDisplay=i.compose(function(a){d.style.borderLeftColor=c.__color.toString();return a},c.updateDisplay),c.updateDisplay();c.setValue=i.compose(function(b){a.getRoot().__preset_select&&c.isModified()&&B(a.getRoot(),true);return b},c.setValue)}
50 | function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(d!=-1){var e=c.__rememberedObjectIndecesToControllers[d];e===void 0&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c[w])c=c[w];else return;if(c[d]&&c[d][b.property]!==void 0)d=c[d][b.property],b.initialValue=d,b.setValue(d)}}}function I(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement,
51 | "has-save");a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML=" ";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var m=a.__preset_select=document.createElement("select");
52 | a.load&&a.load.remembered?i.each(a.load.remembered,function(b,c){C(a,c,c==a.preset)}):C(a,w,false);g.bind(m,"change",function(){for(var b=0;b0){a.preset=this.preset;if(!a.remembered)a.remembered={};a.remembered[this.preset]=z(this)}a.folders={};i.each(this.__folders,function(b,
69 | c){a.folders[c]=b.getSaveObject()});return a},save:function(){if(!this.load.remembered)this.load.remembered={};this.load.remembered[this.preset]=z(this);B(this,false)},saveAs:function(a){if(!this.load.remembered)this.load.remembered={},this.load.remembered[w]=z(this,true);this.load.remembered[a]=z(this);this.preset=a;C(this,a,true)},revert:function(a){i.each(this.__controllers,function(b){this.getRoot().load.remembered?t(a||this.getRoot(),b):b.setValue(b.initialValue)},this);i.each(this.__folders,
70 | function(a){a.revert(a)});a||B(this.getRoot(),false)},listen:function(a){var b=this.__listening.length==0;this.__listening.push(a);b&&E(this.__listening)}});return k}(dat.utils.css,'\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
\n \n
\n\n
',
71 | ".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear;border:0;position:absolute;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-x:hidden}.dg.a.has-save ul{margin-top:27px}.dg.a.has-save ul.closed{margin-top:0}.dg.a .save-row{position:fixed;top:0;z-index:1002}.dg li{-webkit-transition:height 0.1s ease-out;-o-transition:height 0.1s ease-out;-moz-transition:height 0.1s ease-out;transition:height 0.1s ease-out}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;overflow:hidden;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li > *{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:9px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2fa1d6}.dg .cr.number input[type=text]{color:#2fa1d6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2fa1d6}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n",
72 | dat.controllers.factory=function(e,a,c,d,f,b,n){return function(h,j,m,l){var o=h[j];if(n.isArray(m)||n.isObject(m))return new e(h,j,m);if(n.isNumber(o))return n.isNumber(m)&&n.isNumber(l)?new c(h,j,m,l):new a(h,j,{min:m,max:l});if(n.isString(o))return new d(h,j);if(n.isFunction(o))return new f(h,j,"");if(n.isBoolean(o))return new b(h,j)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,c){var d=
73 | function(c,b){function e(){h.setValue(h.__input.value)}d.superclass.call(this,c,b);var h=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())});a.bind(this.__input,"keydown",function(a){a.keyCode===13&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,
74 | e.prototype,{updateDisplay:function(){if(!a.isActive(this.__input))this.__input.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController,
75 | dat.controllers.ColorController=function(e,a,c,d,f){function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function n(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";
76 | a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(e,l){function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window,
77 | "mouseup",j)}function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),d=a.getOffset(p.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c,b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=
78 | 1:b<0&&(b=0);e>1?e=1:e<0&&(e=0);p.__color.v=b;p.__color.s=e;p.setValue(p.__color.toOriginal());return false}function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.getOffset(p.__hue_field),b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=1:b<0&&(b=0);p.__color.h=b*360;p.setValue(p.__color.toOriginal());return false}h.superclass.call(this,e,l);this.__color=new c(this.getValue());this.__temp=new c(0);var p=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,
79 | false);this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=
80 | document.createElement("input");this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){a.keyCode===13&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(){a.addClass(this,"drag").bind(window,"mouseup",function(){a.removeClass(p.__selector,"drag")})});var t=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});
81 | f.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<0.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(t.style,
82 | {width:"100%",height:"100%",background:"none"});b(t,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});n(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",o);a.bind(this.__field_knob,"mousedown",o);a.bind(this.__hue_field,"mousedown",
83 | function(b){s(b);a.bind(window,"mousemove",s);a.bind(window,"mouseup",i)});this.__saturation_field.appendChild(t);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};h.superclass=e;f.extend(h.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue());
84 | if(a!==false){var e=false;f.each(c.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=true,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var h=this.__color.v<0.5||this.__color.s>0.5?255:0,j=255-h;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+
85 | "rgb("+h+","+h+","+h+")"});this.__hue_knob.style.marginTop=(1-this.__color.h/360)*100+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+h+","+h+","+h+")",textShadow:this.__input_textShadow+"rgba("+j+","+j+","+j+",.7)"})}});var j=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,c,d){function f(a,
86 | b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")return this.__state[b];n(this,b,c);return this.__state[b]},set:function(a){if(this.__state.space!=="RGB")n(this,b,c),this.__state.space="RGB";this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="HSV")return this.__state[b];h(this);return this.__state[b]},set:function(a){if(this.__state.space!=="HSV")h(this),this.__state.space="HSV";this.__state[b]=a}})}function n(b,c,e){if(b.__state.space===
87 | "HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a||
88 | 1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex=
89 | a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0};
90 | a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255<