├── images
└── maps
│ ├── iomap.jpg
│ ├── marsmap.jpg
│ ├── moonmap.jpg
│ ├── rheamap.jpg
│ ├── sunmap.jpg
│ ├── sunmap2.jpg
│ ├── arielmap.jpg
│ ├── ceresmap.jpg
│ ├── charonmap.jpg
│ ├── deimosmap.jpg
│ ├── dionemap.jpg
│ ├── earthbump.jpg
│ ├── earthmap.jpg
│ ├── earthspec.jpg
│ ├── europamap.jpg
│ ├── marsbump.jpg
│ ├── mimasmap.jpg
│ ├── moonbump.jpg
│ ├── oberonmap.jpg
│ ├── phobosmap.jpg
│ ├── phoebemap.jpg
│ ├── plutomap.jpg
│ ├── saturnmap.jpg
│ ├── tethysmap.jpg
│ ├── titanmap.jpg
│ ├── tritonmap.jpg
│ ├── uranusmap.jpg
│ ├── venusmap.jpg
│ ├── venusmap2.jpg
│ ├── vestabump.jpg
│ ├── vestamap.jpg
│ ├── callistomap.jpg
│ ├── earthclouds.png
│ ├── enceladusmap.jpg
│ ├── ganymedemap.jpg
│ ├── hyperionmap.jpg
│ ├── iapetusmap.jpg
│ ├── jupitermap.jpg
│ ├── jupiterrings.png
│ ├── marsclouds.png
│ ├── mercurybump.jpg
│ ├── mercurymap.jpg
│ ├── mirandamap.jpg
│ ├── neptunemap.jpg
│ ├── neptunerings.png
│ ├── phobosbump.jpg
│ ├── proteusmap.jpg
│ ├── saturnrings.png
│ ├── solarcorona.jpg
│ ├── titanclouds.jpg
│ ├── titaniamap.jpg
│ ├── tycho-skymap.jpg
│ ├── umbrielmap.jpg
│ ├── uranusrings.png
│ ├── earthmapclouds.jpg
│ ├── earthclouds-fair.png
│ ├── earthclouds-heavy.png
│ └── galaxy_starfield.png
├── package.require.js
├── Makefile
├── threex.atmospheredatgui.js
├── threex.atmospherematerial.js
├── examples
├── planets.html
├── earthmoon.html
├── atmospherematerial.html
├── basic.html
├── earth.html
├── select.html
├── planetary-systems.html
└── vendor
│ └── three.js
│ └── examples
│ └── js
│ ├── controls
│ └── OrbitControls.js
│ └── libs
│ └── dat.gui.min.js
├── lib
├── three.css.js
├── planetary.js
├── threex.planets.js
└── OrbitControls.js
├── README.md
└── threex.planets.js
/images/maps/iomap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/iomap.jpg
--------------------------------------------------------------------------------
/images/maps/marsmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/marsmap.jpg
--------------------------------------------------------------------------------
/images/maps/moonmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/moonmap.jpg
--------------------------------------------------------------------------------
/images/maps/rheamap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/rheamap.jpg
--------------------------------------------------------------------------------
/images/maps/sunmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/sunmap.jpg
--------------------------------------------------------------------------------
/images/maps/sunmap2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/sunmap2.jpg
--------------------------------------------------------------------------------
/images/maps/arielmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/arielmap.jpg
--------------------------------------------------------------------------------
/images/maps/ceresmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/ceresmap.jpg
--------------------------------------------------------------------------------
/images/maps/charonmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/charonmap.jpg
--------------------------------------------------------------------------------
/images/maps/deimosmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/deimosmap.jpg
--------------------------------------------------------------------------------
/images/maps/dionemap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/dionemap.jpg
--------------------------------------------------------------------------------
/images/maps/earthbump.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/earthbump.jpg
--------------------------------------------------------------------------------
/images/maps/earthmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/earthmap.jpg
--------------------------------------------------------------------------------
/images/maps/earthspec.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/earthspec.jpg
--------------------------------------------------------------------------------
/images/maps/europamap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/europamap.jpg
--------------------------------------------------------------------------------
/images/maps/marsbump.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/marsbump.jpg
--------------------------------------------------------------------------------
/images/maps/mimasmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/mimasmap.jpg
--------------------------------------------------------------------------------
/images/maps/moonbump.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/moonbump.jpg
--------------------------------------------------------------------------------
/images/maps/oberonmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/oberonmap.jpg
--------------------------------------------------------------------------------
/images/maps/phobosmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/phobosmap.jpg
--------------------------------------------------------------------------------
/images/maps/phoebemap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/phoebemap.jpg
--------------------------------------------------------------------------------
/images/maps/plutomap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/plutomap.jpg
--------------------------------------------------------------------------------
/images/maps/saturnmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/saturnmap.jpg
--------------------------------------------------------------------------------
/images/maps/tethysmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/tethysmap.jpg
--------------------------------------------------------------------------------
/images/maps/titanmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/titanmap.jpg
--------------------------------------------------------------------------------
/images/maps/tritonmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/tritonmap.jpg
--------------------------------------------------------------------------------
/images/maps/uranusmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/uranusmap.jpg
--------------------------------------------------------------------------------
/images/maps/venusmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/venusmap.jpg
--------------------------------------------------------------------------------
/images/maps/venusmap2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/venusmap2.jpg
--------------------------------------------------------------------------------
/images/maps/vestabump.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/vestabump.jpg
--------------------------------------------------------------------------------
/images/maps/vestamap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/vestamap.jpg
--------------------------------------------------------------------------------
/images/maps/callistomap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/callistomap.jpg
--------------------------------------------------------------------------------
/images/maps/earthclouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/earthclouds.png
--------------------------------------------------------------------------------
/images/maps/enceladusmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/enceladusmap.jpg
--------------------------------------------------------------------------------
/images/maps/ganymedemap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/ganymedemap.jpg
--------------------------------------------------------------------------------
/images/maps/hyperionmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/hyperionmap.jpg
--------------------------------------------------------------------------------
/images/maps/iapetusmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/iapetusmap.jpg
--------------------------------------------------------------------------------
/images/maps/jupitermap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/jupitermap.jpg
--------------------------------------------------------------------------------
/images/maps/jupiterrings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/jupiterrings.png
--------------------------------------------------------------------------------
/images/maps/marsclouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/marsclouds.png
--------------------------------------------------------------------------------
/images/maps/mercurybump.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/mercurybump.jpg
--------------------------------------------------------------------------------
/images/maps/mercurymap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/mercurymap.jpg
--------------------------------------------------------------------------------
/images/maps/mirandamap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/mirandamap.jpg
--------------------------------------------------------------------------------
/images/maps/neptunemap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/neptunemap.jpg
--------------------------------------------------------------------------------
/images/maps/neptunerings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/neptunerings.png
--------------------------------------------------------------------------------
/images/maps/phobosbump.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/phobosbump.jpg
--------------------------------------------------------------------------------
/images/maps/proteusmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/proteusmap.jpg
--------------------------------------------------------------------------------
/images/maps/saturnrings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/saturnrings.png
--------------------------------------------------------------------------------
/images/maps/solarcorona.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/solarcorona.jpg
--------------------------------------------------------------------------------
/images/maps/titanclouds.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/titanclouds.jpg
--------------------------------------------------------------------------------
/images/maps/titaniamap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/titaniamap.jpg
--------------------------------------------------------------------------------
/images/maps/tycho-skymap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/tycho-skymap.jpg
--------------------------------------------------------------------------------
/images/maps/umbrielmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/umbrielmap.jpg
--------------------------------------------------------------------------------
/images/maps/uranusrings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/uranusrings.png
--------------------------------------------------------------------------------
/images/maps/earthmapclouds.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/earthmapclouds.jpg
--------------------------------------------------------------------------------
/images/maps/earthclouds-fair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/earthclouds-fair.png
--------------------------------------------------------------------------------
/images/maps/earthclouds-heavy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/earthclouds-heavy.png
--------------------------------------------------------------------------------
/images/maps/galaxy_starfield.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ofrohn/threex.planets/HEAD/images/maps/galaxy_starfield.png
--------------------------------------------------------------------------------
/package.require.js:
--------------------------------------------------------------------------------
1 | define( [ 'module'
2 | , './threex.planets',
3 | , './threex.atmospherematerial'
4 | , './threex.atmospheredatgui'
5 | ], function(module){
6 | // set baseUrl for this plugin
7 | THREEx.Planets.baseURL = module.uri+'/../';
8 | });
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # makefile to automatize simple operations
2 |
3 | server:
4 | python -m SimpleHTTPServer
5 |
6 | deploy:
7 | # assume there is something to commit
8 | # use "git diff --exit-code HEAD" to know if there is something to commit
9 | # so two lines: one if no commit, one if something to commit
10 | git commit -a -m "New deploy" && git push -f origin HEAD:gh-pages && git reset HEAD~
11 |
12 | install:
13 | git submodule update --init
--------------------------------------------------------------------------------
/threex.atmospheredatgui.js:
--------------------------------------------------------------------------------
1 | /**
2 | * vendor.js framework definition
3 | * @type {Object}
4 | */
5 | var THREEx = THREEx || {};
6 |
7 | /**
8 | * add a THREEx.AtmosphereMaterial to Dat.DUI
9 | *
10 | * @param {THREE.ShaderMaterial} material the material to handle
11 | * @param {dat.GUI+} datGui the dat.GUI to which we need to add
12 | */
13 | THREEx.addAtmosphereMaterial2DatGui = function(material, datGui){
14 | datGui = datGui || new dat.GUI()
15 | var uniforms = material.uniforms
16 | // options
17 | var options = {
18 | coeficient : uniforms['coeficient'].value,
19 | power : uniforms['power'].value,
20 | glowColor : '#'+uniforms.glowColor.value.getHexString(),
21 | presetFront : function(){
22 | options.coeficient = 1
23 | options.power = 2
24 | onChange()
25 | },
26 | presetBack : function(){
27 | options.coeficient = 0.5
28 | options.power = 4.0
29 | onChange()
30 | },
31 | }
32 | var onChange = function(){
33 | uniforms['coeficient'].value = options.coeficient
34 | uniforms['power'].value = options.power
35 | uniforms.glowColor.value.set( options.glowColor );
36 | }
37 | onChange()
38 |
39 | // config datGui
40 | datGui.add( options, 'coeficient' , 0.0 , 2)
41 | .listen().onChange( onChange )
42 | datGui.add( options, 'power' , 0.0 , 30)
43 | .listen().onChange( onChange )
44 | datGui.addColor( options, 'glowColor' )
45 | .listen().onChange( onChange )
46 | datGui.add( options, 'presetFront' )
47 | datGui.add( options, 'presetBack' )
48 | }
--------------------------------------------------------------------------------
/threex.atmospherematerial.js:
--------------------------------------------------------------------------------
1 | var THREEx = THREEx || {}
2 |
3 | /**
4 | * from http://stemkoski.blogspot.fr/2013/07/shaders-in-threejs-glow-and-halo.html
5 | * @return {[type]} [description]
6 | */
7 | THREEx.createAtmosphereMaterial = function(){
8 | var vertexShader = [
9 | 'varying vec3 vVertexWorldPosition;',
10 | 'varying vec3 vVertexNormal;',
11 |
12 | 'void main(){',
13 | ' vVertexNormal = normalize(normalMatrix * normal);',
14 |
15 | ' vVertexWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;',
16 |
17 | ' // set gl_Position',
18 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
19 | '}',
20 |
21 | ].join('\n')
22 | var fragmentShader = [
23 | 'uniform vec3 glowColor;',
24 | 'uniform float coeficient;',
25 | 'uniform float power;',
26 |
27 | 'varying vec3 vVertexNormal;',
28 | 'varying vec3 vVertexWorldPosition;',
29 |
30 | 'void main(){',
31 | ' vec3 worldCameraToVertex= vVertexWorldPosition - cameraPosition;',
32 | ' vec3 viewCameraToVertex = (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;',
33 | ' viewCameraToVertex = normalize(viewCameraToVertex);',
34 | ' float intensity = pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);',
35 | ' gl_FragColor = vec4(glowColor, intensity);',
36 | '}',
37 | ].join('\n')
38 |
39 | // create custom material from the shader code above
40 | // that is within specially labeled script tags
41 | var material = new THREE.ShaderMaterial({
42 | uniforms: {
43 | coeficient : {
44 | type : "f",
45 | value : 1.0
46 | },
47 | power : {
48 | type : "f",
49 | value : 2
50 | },
51 | glowColor : {
52 | type : "c",
53 | value : new THREE.Color('pink')
54 | },
55 | },
56 | vertexShader : vertexShader,
57 | fragmentShader : fragmentShader,
58 | //blending : THREE.AdditiveBlending,
59 | transparent : true,
60 | depthWrite : false,
61 | });
62 | return material
63 | }
--------------------------------------------------------------------------------
/examples/planets.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
100 |
--------------------------------------------------------------------------------
/examples/earthmoon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
122 |
--------------------------------------------------------------------------------
/lib/three.css.js:
--------------------------------------------------------------------------------
1 | /************************************************************************/
2 | /* Initialized some variables for CSS, and also it computes the initial
3 |
4 | position for the CSS cube based on the Three Cube */
5 | /************************************************************************/
6 | function initCSS3D() {
7 | screenWhalf = screenWidth / 2;
8 | screenHhalf = screenHeight / 2;
9 |
10 | divCSSWorld = document.getElementById('css-world');
11 | divCSSCamera = document.getElementById('css-camera');
12 | divCube = document.getElementById('shape');
13 |
14 | fovValue = 0.5 / Math.tan(camera.fov * Math.PI / 360) * screenHeight;
15 |
16 | setDivPosition(divCube, glCube);
17 | }
18 |
19 | /************************************************************************/
20 | /* Applies CSS3 styles to the css-world div */
21 | /************************************************************************/
22 | function setCSSWorld() {
23 | divCSSWorld.style.WebkitPerspective = fovValue + "px";
24 | divCSSWorld.style.WebkitPerspectiveOrigin = "50% 50%";
25 | divCSSWorld.style.MozPerspective = fovValue + "px";
26 | divCSSWorld.style.MozPerspectiveOrigin = "50% 50%";
27 | }
28 |
29 | /************************************************************************/
30 | /* Applies CSS3 styles to css-camera div */
31 | /************************************************************************/
32 | function setCSSCamera(camera, fovValue) {
33 | var cameraStyle = getCSS3D_cameraStyle(camera, fovValue);
34 | divCSSCamera.style.WebkitTransform = cameraStyle;
35 | divCSSCamera.style.MozTransform = cameraStyle;
36 | }
37 |
38 | /************************************************************************/
39 | /* Return the CSS3D transformations from the Three camera */
40 | /************************************************************************/
41 | function getCSS3D_cameraStyle(camera, fov) {
42 | var cssStyle = "";
43 | cssStyle += "translate3d(0,0," + epsilon(fov) + "px) ";
44 | cssStyle += toCSSMatrix(camera.matrixWorldInverse, true);
45 | cssStyle += " translate3d(" + screenWhalf + "px," + screenHhalf + "px, 0)";
46 | return cssStyle;
47 | }
48 |
49 | /************************************************************************/
50 | /* Fixes the difference between WebGL coordinates to CSS coordinates */
51 | /************************************************************************/
52 | function toCSSMatrix(threeMat4, b) {
53 | var a = threeMat4, f;
54 | if (b) {
55 | f = [
56 | a.elements[0], -a.elements[1], a.elements[2], a.elements[3],
57 | a.elements[4], -a.elements[5], a.elements[6], a.elements[7],
58 | a.elements[8], -a.elements[9], a.elements[10], a.elements[11],
59 | a.elements[12], -a.elements[13], a.elements[14], a.elements[15]
60 | ];
61 | } else {
62 | f = [
63 | a.elements[0], a.elements[1], a.elements[2], a.elements[3],
64 | a.elements[4], a.elements[5], a.elements[6], a.elements[7],
65 | a.elements[8], a.elements[9], a.elements[10], a.elements[11],
66 | a.elements[12], a.elements[13], a.elements[14], a.elements[15]
67 | ];
68 | }
69 | for (var e in f) {
70 | f[e] = epsilon(f[e]);
71 | }
72 | return "matrix3d(" + f.join(",") + ")";
73 | }
74 |
75 | /************************************************************************/
76 | /* Computes CSS3D transformations based on a Three Object */
77 | /************************************************************************/
78 | function setDivPosition(cssObject, glObject) {
79 | var offset = 400; //value to offset the cube
80 | glObject.updateMatrix();
81 | cssObject.style.position = "absolute";
82 | //Webkit:
83 | cssObject.style.WebkitTransformOrigin = "50% 50%";
84 | cssObject.style.WebkitTransform = CSStransform(200 + offset, 200, glObject.matrix);
85 | //Mozilla:
86 | cssObject.style.MozTransformOrigin = "50% 50%";
87 | cssObject.style.MozTransform = CSStransform(200 + offset, 200, glObject.matrix);
88 | }
89 |
90 | /************************************************************************/
91 | /* Helper function to convert to CSS3D transformations */
92 | /************************************************************************/
93 | function CSStransform(width, height, matrix) {
94 | var scale = 1.0;
95 | return [toCSSMatrix(matrix, false),
96 | "scale3d(" + scale + ", -" + scale + ", " + scale + ")",
97 | "translate3d(" + epsilon(-0.5 * width) + "px," + epsilon(-0.5 * height) + "px,0)"].join(" ");
98 | }
99 |
100 | /************************************************************************/
101 | /* Rounding error */
102 | /************************************************************************/
103 | function epsilon(a) {
104 | if (Math.abs(a) < 0.000001) {
105 | return 0
106 | }
107 | return a;
108 | }
109 |
--------------------------------------------------------------------------------
/examples/atmospherematerial.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
124 |
--------------------------------------------------------------------------------
/examples/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
131 |
--------------------------------------------------------------------------------
/examples/earth.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
144 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | threex.planets.js
2 | =================
3 |
4 | It is a three.js extension to display planets
5 | based on the data from
6 | * [planetpixelemporium](http://planetpixelemporium.com/planets.html) (Sun*, Earth DEM, Uranus)
7 | * [USGS Astrogeology](http://astrogeology.usgs.gov/solar-system) (Mercury DEM, Moon*, Mars, Jupiter Moons, Uranus Moons)
8 | * [NASA/JHUAPL/Carnegie Institution of Washington](http://messenger.jhuapl.edu/Explore/Images.html#global-mosaics) (Mercury Map*)
9 | * [NASA/JHUAPL/SWRI](http://pluto.jhuapl.edu/Multimedia/Science-Photos/image.php?gallery_id=2&image_id=252) (Pluto*, Charon)
10 | * [NASA/JPL-Caltech/Space Science Institute](http://www.jpl.nasa.gov/spaceimages/searchwp.php?category=saturn) (Saturn Rings*, Titan, Triton*)
11 | * [NASA/JPL-Caltech/Space Science Institute/LPI](http://www.lpi.usra.edu/icy_moons/) (Saturn Icy Moons*)
12 | * [NASA/JPL-Caltech](http://voyager.jpl.nasa.gov/gallery/uranus.html) (Uranus Rings*)
13 | * [NASA/JPL-Caltech](http://maps.jpl.nasa.gov/) (Stars)
14 | * [NASA Visible Earth](http://visibleearth.nasa.gov/view_cat.php?categoryID=1484) (Earth, Earth Clouds*)
15 | * [ESO](http://www.eso.org/public/usa/images/eso0127a/) (Solar Corona*)
16 | * [Björn Jónsson](http://bjj.mmedia.is/data/planetary_maps.html) (Venus*, Jupiter, Neptune)
17 | * [Solar System Scope](http://www.solarsystemscope.com/nexus/textures/planet_textures/) (Saturn, Titan Clouds)
18 | * [Philip Stooke](http://solarviews.com/cap/index/maps-cylindrical1.html) (Deimos, Proteus)
19 | \* Hand adapted by ofrohn, also Jupiter & Neptune ring maps
20 |
21 | Here is some demos to show off
22 |
23 | * [planetary systems](http://ofrohn.github.io/threex-planets-demo/planetary-systems.html)
24 | and its
25 | [source](https://github.com/ofrohn/threex.planets/blob/master/examples/planetary-systems.html). Simulation of planets with their moons.
26 | * [all the planets](http://ofrohn.github.io/threex-planets-demo/planets.html)
27 | and its
28 | [source](https://github.com/ofrohn/threex.planets/blob/master/examples/planets.html). All the implemented objects dancing in a circle.
29 | * [earth and moon](http://ofrohn.github.io/threex-planets-demo/eartmoon.html)
30 | and its
31 | [source](https://github.com/ofrohn/threex.planets/blob/master/examples/earthmoon.html). The Earth spinning and the Moon orbiting it.
32 | _Older examples_
33 | * [earth demo](http://jeromeetienne.github.io/threex.planets/examples/earth.html)
34 | and check its
35 | [source](https://github.com/jeromeetienne/threex.planets/blob/master/examples/earth.html).
36 | It displays a nice earth with cloud and even the moon.
37 | * [select demo](http://jeromeetienne.github.io/threex.planets/examples/select.html)
38 | and check its
39 | [source](https://github.com/jeromeetienne/threex.planets/blob/master/examples/select.html).
40 | Display all the planets available
41 | * [atmospherematerial demo](http://jeromeetienne.github.io/threex.planets/examples/atmospherematerial.html)
42 | and check its
43 | [source](https://github.com/jeromeetienne/threex.planets/blob/master/examples/atmospherematerial.html). a simple demo to show ```THREEx.createAtmosphereMaterial()```
44 | * [basic demo](http://jeromeetienne.github.io/threex.planets/examples/basic.html)
45 | and check its
46 | [source](https://github.com/jeromeetienne/threex.planets/blob/master/examples/basic.html).
47 | Good for educational purpose
48 |
49 | ## How To install it
50 |
51 | You can install it manually or with
52 | [bower](http://bower.io/).
53 | for the manual version, first include ```threex.planets.js``` with the usual
54 |
55 | ```html
56 |
57 | ```
58 |
59 | or with
60 | [bower](http://bower.io/)
61 | you type the following to install the package.
62 |
63 | ```bash
64 | bower install -s threex.planets=https://github.com/jeromeetienne/threex.planets/archive/master.zip
65 | ```
66 |
67 | then you add that in your html
68 |
69 | ```html
70 |
71 | ```
72 |
73 |
74 | ## Usage
75 |
76 | to create uranus with its ring
77 |
78 | ```javascript
79 | var mesh = THREEx.Planets.createUranus()
80 | scene.add(mesh)
81 | var mesh = THREEx.Planets.createUranusRing()
82 | scene.add(mesh)
83 | ```
84 |
85 | to create the earth plus the clouds moving around
86 |
87 | ```javascript
88 | var mesh = THREEx.Planets.createEarth()
89 | scene.add(mesh)
90 | var mesh = THREEx.Planets.createEarthCloud()
91 | scene.add(mesh)
92 | updateFcts.push(function(delta, now){
93 | mesh.rotation.y += 1/8 * delta;
94 | })
95 | ```
96 |
97 | new simpler form:
98 |
99 | ```javascript
100 | var mesh = THREEx.Planets.create("Uranus"); // ring included
101 | scene.add(mesh);
102 |
103 | var mesh = THREEx.Planets.create("Earth"); // clouds included
104 | scene.add(mesh);
105 | updateFcts.push(function(delta, now){
106 | mesh.traverse(function(child) {
107 | if (child.name.search("cloud") !== -1) child.rotation.y += 1/8 * delta;
108 | });
109 | });
110 |
111 | var mesh = THREEx.Planets.create("Earth", true); // no clouds
112 | ```
113 |
114 | ## API
115 |
116 | Here is the list of all the functions.
117 | They all return a ```THREE.Object3d```.
118 | You can tune it to fit your need
119 |
120 | * ```THREEx.Planets.create(body, skipextras)``` return the mesh of any supported body
121 | body: Sun|Mercury|Venus|Earth|Moon|Mars|Vesta|Ceres|Jupiter|Saturn|Uranus|Neptune|Pluto
122 | skipextras: No ring, cloud, corona or other extra elements
123 | * ```THREEx.Planets.createRings(body)``` return the ring mesh of any supported body
124 | body: Jupiter|Saturn|Uranus|Neptune
125 | * ```THREEx.Planets.createClouds(body)``` return the cloud mesh of any supported body
126 | body: Earth|Mars
127 |
128 | * ```THREEx.Planets.createSun()``` return the mesh of the Sun
129 | * ```THREEx.Planets.createMercury()``` return the mesh of Mercury
130 | * ```THREEx.Planets.createVenus()``` return the mesh of Venus
131 | * ```THREEx.Planets.createMoon()``` return the mesh of the Moon
132 | * ```THREEx.Planets.createEarth()``` return the mesh of the Earth
133 | * ```THREEx.Planets.createEarthCloud()``` return the mesh of the Earth Cloud
134 | * ```THREEx.Planets.createMars()``` return the mesh of Mars
135 | * ```THREEx.Planets.createJupiter()``` return the mesh of Jupiter
136 | * ```THREEx.Planets.createSaturn()``` return the mesh of Saturn
137 | * ```THREEx.Planets.createSaturnRing()``` return the mesh of Saturn's ring
138 | * ```THREEx.Planets.createUranus()``` return the mesh of Uranus
139 | * ```THREEx.Planets.createUranusRing()``` return the mesh of Uranus's ring
140 | * ```THREEx.Planets.createNeptune()``` return the mesh of Neptune
141 | * ```THREEx.Planets.createPluto()``` return the mesh of Pluto
142 | * ```THREEx.Planets.createStarfield()``` return the mesh of a starfield environmental sphere
143 |
144 |
--------------------------------------------------------------------------------
/examples/select.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
189 |
--------------------------------------------------------------------------------
/examples/planetary-systems.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Planetary Systems
5 |
6 |
7 |
8 |
9 |
10 |
16 |
296 |
--------------------------------------------------------------------------------
/lib/planetary.js:
--------------------------------------------------------------------------------
1 | var deg2rad = Math.PI / 180,
2 | τ = 2 * Math.PI,
3 | halfπ = Math.PI / 2,
4 | ε = 23.43928
5 | sinε = Math.sin(ε),
6 | cosε = Math.sin(ε);
7 |
8 | function $(id) { return document.getElementById(id); }
9 | function px(n) { return n + "px"; }
10 | function Round(x, dg) { return(Math.round(Math.pow(10,dg)*x)/Math.pow(10,dg)); }
11 | function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
12 |
13 | function has(o, key) { return o !== null && hasOwnProperty.call(o, key); }
14 | function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); }
15 | function isArray(o) { return Object.prototype.toString.call(o) === "[object Array]"; }
16 | function isObject(o) { var type = typeof o; return type === 'function' || type === 'object' && !!o; }
17 | function isFunction(o) { return typeof o == 'function' || false; }
18 | /*
19 | function dist(p1, p2){
20 | var θ1 = p1.θ * deg2rad, ϕ1 = p1.ϕ * deg2rad,
21 | θ2 = p2.θ * deg2rad, ϕ2 = p2.ϕ * deg2rad;
22 |
23 | return Math.sqrt(p1.r*p1.r + p2.r*p2.r - 2*p1.r*p2.r * (Math.sin(θ1) * Math.sin(θ2) * Math.cos(ϕ1-ϕ2) + Math.cos(θ1) * Math.cos(θ2)));
24 | }
25 | */
26 |
27 | function dateParse(s) {
28 | if (!s) return;
29 | var t = s.split(".");
30 | if (t.length < 1) return;
31 | t = t[0].split("-");
32 | t[0] = t[0].replace(/\D/g, "");
33 | if (!t[0]) return;
34 | t[1] = t[1]?t[1].replace(/\D/g, ""):"1";
35 | t[2] = t[2]?t[2].replace(/\D/g, ""):"1";
36 |
37 | return new Date(t[0], t[1]-1, t[2]);
38 | }
39 |
40 | function dateAdd(dt, val, type) {
41 | var t, ldt = dt.valueOf();
42 | if (!val) return new Date(ldt);
43 | t = type || "d";
44 | switch (t) {
45 | case 'y': case 'yr': ldt += 31556926080*val; break;
46 | case 'm': case 'mo': ldt += 2629800000*val; break;
47 | case 'd': case 'dy': ldt += 86400000*val; break;
48 | case 'h': case 'hr': ldt += 3600000*val; break;
49 | case 'n': case 'mn': ldt += 60000*val; break;
50 | case 's': case 'sec': ldt += 1000*val; break;
51 | case 'ms': ldt += val; break;
52 | }
53 | return new Date(ldt);
54 | }
55 |
56 |
57 | function dateDiff(dt1, dt2, type) {
58 | if (!dt2 || !dt1) return;
59 | var diff = dt2.valueOf() - dt1.valueOf(),
60 | tp = type || "d";
61 | switch (tp) {
62 | case 'y': case 'yr': diff /= 31556926080; break;
63 | case 'm': case 'mo': diff /= 2629800000; break;
64 | case 'd': case 'dy': diff /= 86400000; break;
65 | case 'h': case 'hr': diff /= 3600000; break;
66 | case 'n': case 'mn': diff /= 60000; break;
67 | case 's': case 'sec': diff /= 1000; break;
68 | case 'ms': break;
69 | }
70 | if (type) return Math.floor(diff);
71 | return diff;
72 | }
73 |
74 |
75 | var Trig = {
76 | sinh: function (val) { return (Math.pow(Math.E, val)-Math.pow(Math.E, -val))/2; },
77 | cosh: function (val) { return (Math.pow(Math.E, val)+Math.pow(Math.E, -val))/2; },
78 | tanh: function (val) { return 2.0 / (1.0 + Math.exp(-2.0 * val)) - 1.0; },
79 | asinh: function (val) { return Math.log(val + Math.sqrt(val * val + 1)); },
80 | acosh: function (val) { return Math.log(val + Math.sqrt(val * val - 1)); },
81 | normalize0: function(val) { return ((val + Math.PI*3) % (Math.PI*2)) - Math.PI; },
82 | normalize: function(val) { return ((val + Math.PI*2) % (Math.PI*2)); },
83 | cartesian: function(p) {
84 | var ϕ = p[0], θ = halfπ - p[1], r = p[2];
85 | return {"x": r * Math.sin(θ) * Math.cos(ϕ), "y": r * Math.sin(θ) * Math.sin(ϕ), "z": r * Math.cos(θ)};
86 | },
87 | spherical: function(p) {
88 | var r = Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z),
89 | θ = Math.atan(p.y / p.x),
90 | ϕ = Math.acos(p.z / r);
91 | return [θ / deg2rad, ϕ / deg2rad, r];
92 | }
93 | };
94 |
95 | function dist(p1, p2) {
96 | var ϕ1 = p1[0],
97 | θ1 = p1[1],
98 | ϕ2 = p2[0],
99 | θ2 = p2[1],
100 | dθ = θ2 - θ1,
101 | dϕ = ϕ2 - ϕ1;
102 |
103 | //return Math.acos( Math.sin(θ1) * Math.sin(θ2) + Math.cos(θ1) * Math.cos(θ2) * Math.cos(dϕ) );
104 | var a = Math.sin(dθ / 2) * Math.sin(dθ / 2) + Math.cos(θ1) * Math.cos(θ2) * Math.sin(dϕ / 2) * Math.sin(dϕ / 2);
105 | var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
106 | return c;
107 | }
108 |
109 | var gmdat = {
110 | "sol": 0.0002959122082855911025, // AU^3/d^2
111 | "mer": 164468599544771, //km^3/d^2
112 | "ven": 2425056445892137,
113 | "ter": 2975536307796296,
114 | "lun": 36599199229256,
115 | "mar": 319711652803400,
116 | "cer": 467549107200,
117 | "ves": 129071530155,
118 | "jup": 945905743547733000,
119 | "sat": 283225255921345000,
120 | "ura": 43256076555832200,
121 | "nep": 51034453325494200,
122 | "plu": 7327611364884,
123 | "eri": 8271175680000
124 | }
125 |
126 |
127 | var transform = function(item, date, parentBody) {
128 | var dt, i, key, dat = {}, elms = ["a","e","i","w","M","L","W","N","n","ref","lecl","becl","Tilt"];
129 | /*
130 | ep = epoch (dt)
131 | N = longitude of the ascending node (deg) Ω
132 | i = inclination to the refrence plane, default:ecliptic (deg)
133 | w = argument of periapsis (deg) ω
134 | a = semi-major axis, or mean distance from parent body (AU,km)
135 | e = eccentricity (0=circle, 0-1=ellipse, 1=parabola, >1=hyperbola ) (-)
136 | M = mean anomaly (0 at periapsis; increases uniformly with time) (deg)
137 | n = mean daily motion = 360/P (deg/day)
138 |
139 | W = N + w = longitude of periapsis ϖ
140 | L = M + W = mean longitude
141 | q = a*(1-e) = periapsis distance
142 | Q = a*(1+e) = apoapsis distance
143 | P = 2π * sqrt(a^3/GM) = orbital period (years)
144 | T = Epoch_of_M - (M(deg)/360_deg) / P = time of periapsis
145 | v = true anomaly (angle between position and periapsis) ν
146 | E = eccentric anomaly
147 |
148 | Mandatory: a, e, i, N, w|W, M|L, dM|n
149 | */
150 |
151 | if (parentBody) gm = gmdat[parentBody];
152 | else gm = gmdat["sol"];
153 |
154 | if (date) {
155 | if (date instanceof Date) { dt = date; }
156 | else { dt = dateParse(date); }
157 | }
158 | if (!dt) { dt = new Date(); }
159 | dat.jd = JD(dt);
160 |
161 | dt = dateParse(item.ep);
162 | dat.jd0 = JD(dt);
163 | dat.d = dat.jd - dat.jd0;
164 | dat.cy = dat.d / 36525;
165 |
166 | for (i=0; i 1.0 ? E*E : -E*E,
196 | term = e * anom2 * E / 6.0,
197 | rval = (1.0 - e) * E - term,
198 | n = 4;
199 |
200 | while(Math.abs(term) > 1e-15) {
201 | term *= anom2 / (n * (n + 1));
202 | rval -= term;
203 | n += 2;
204 | }
205 | return(rval);
206 | }
207 |
208 | function kepler(dat) {
209 | var curr, err, trial, tmod,
210 | e = dat.e, M = dat.M,
211 | thresh = 1e-8,
212 | offset = 0.0,
213 | delta_curr = 1.9,
214 | is_negative = false,
215 | n_iter = 0;
216 |
217 | if (!M) return(0.0);
218 |
219 | if (e < 1.0) {
220 | if (M < -Math.PI || M > Math.PI) {
221 | tmod = Trig.normalize0(M);
222 | offset = M - tmod;
223 | M = tmod;
224 | }
225 |
226 | if (e < 0.9) {
227 | curr = Math.atan2(Math.sin(M), Math.cos(M) - e);
228 | do {
229 | err = (curr - e * Math.sin(curr) - M) / (1.0 - e * Math.cos(curr));
230 | curr -= err;
231 | } while (Math.abs(err) > thresh);
232 | return curr; // + offset;
233 | }
234 | }
235 |
236 | if ( M < 0.0) {
237 | M = -M;
238 | is_negative = true;
239 | }
240 |
241 | curr = M;
242 | thresh = thresh * Math.abs(1.0 - e);
243 | /* Due to roundoff error, there's no way we can hope to */
244 | /* get below a certain minimum threshhold anyway: */
245 | if ( thresh < 1e-15) { thresh = 1e-15; }
246 | if ( (e > 0.8 && M < Math.PI / 3.0) || e > 1.0) { /* up to 60 degrees */
247 | trial = M / Math.abs( 1.0 - e);
248 |
249 | if (trial * trial > 6.0 * Math.abs(1.0 - e)) { /* cubic term is dominant */
250 | if (M < Math.PI) {
251 | trial = Math.pow(6.0 * M, 1/3);
252 | } else { /* hyperbolic w/ 5th & higher-order terms predominant */
253 | trial = Trig.asinh( M / e);
254 | }
255 | }
256 | curr = trial;
257 | }
258 | if (e > 1.0 && M > 4.0) { /* hyperbolic, large-mean-anomaly case */
259 | curr = Math.log(M);
260 | }
261 | if (e < 1.0) {
262 | while(Math.abs(delta_curr) > thresh) {
263 | if ( n_iter++ > 8) {
264 | err = near_parabolic(curr, e) - M;
265 | } else {
266 | err = curr - e * Math.sin(curr) - M;
267 | }
268 | delta_curr = -err / (1.0 - e * Math.cos(curr));
269 | curr += delta_curr;
270 | }
271 | } else {
272 | while (Math.abs(delta_curr) > thresh) {
273 | if (n_iter++ > 7) {
274 | err = -near_parabolic(curr, e) - M;
275 | } else {
276 | err = e * Trig.sinh(curr) - curr - M;
277 | }
278 | delta_curr = -err / (e * Trig.cosh(curr) - 1.0);
279 | curr += delta_curr;
280 | }
281 | }
282 | return( is_negative ? offset - curr : offset + curr);
283 | }
284 |
285 | function trueAnomaly(dat) {
286 | var v, r, x, y, r0, g, t;
287 |
288 | if (dat.e === 1.0) { /* parabolic */
289 | t = dat.jd0 - dat.T;
290 | g = dat.w0 * t * 0.5;
291 |
292 | y = Math.pow(g + Math.sqrt(g * g + 1.0), 1/3);
293 | dat.v = 2.0 * Math.atan(y - 1.0 / y);
294 | } else { /* got the mean anomaly; compute eccentric, then true */
295 | dat.E = kepler(dat);
296 | if (dat.e > 1.0) { /* hyperbolic case */
297 | x = (dat.e - Trig.cosh(dat.E));
298 | y = Trig.sinh(dat.E);
299 | } else { /* elliptical case */
300 | x = (Math.cos(dat.E) - dat.e);
301 | y = Math.sin(dat.E);
302 | }
303 | y *= Math.sqrt(Math.abs(1.0 - dat.e * dat.e));
304 | dat.v = Math.atan2(y, x);
305 | }
306 |
307 | r0 = dat.q * (1.0 + dat.e);
308 | dat.r = r0 / (1.0 + dat.e * Math.cos(dat.v));
309 | }
310 |
311 | function derive(dat) {
312 | if (!dat.hasOwnProperty("w")) {
313 | dat.w = dat.W - dat.N;
314 | }
315 | if (!dat.hasOwnProperty("M")) {
316 | dat.M = dat.L - dat.W;
317 | }
318 | if (dat.e < 1.0) { dat.M = Trig.normalize0(dat.M); }
319 | //dat.P = Math.pow(Math.abs(dat.a), 1.5);
320 | dat.P = τ * Math.sqrt(Math.pow(dat.a, 3) / gm) / 365.25;
321 | dat.T = dat.jd0 - (dat.M / halfπ) / dat.P;
322 |
323 | if (dat.e !== 1.0) { /* for non-parabolic orbits: */
324 | dat.q = dat.a * (1.0 - dat.e);
325 | dat.t0 = dat.a * Math.sqrt(Math.abs(dat.a) / gm);
326 | } else {
327 | dat.w0 = (3.0 / Math.sqrt(2)) / (dat.q * Math.sqrt(dat.q / gm));
328 | dat.a = 0.0;
329 | dat.t0 = 0.0;
330 | }
331 | dat.am = Math.sqrt(gm * dat.q * (1.0 + dat.e));
332 | }
333 |
334 | function transpose(dat) {
335 | if (!dat.ref || dat.ref === "ecl") {
336 | dat.tx = dat.x;
337 | dat.ty = dat.y;
338 | dat.tz = dat.z;
339 | return;
340 | }
341 | var a0 = dat.lecl,// - Math.PI/2,
342 | a1 = Math.PI/2 - dat.becl,
343 | angles = [0, a1, -a0];
344 | euler(dat, angles);
345 | var tp = Trig.cartesian([dat.tl, dat.tb, dat.r]);
346 | dat.tx = tp.x;
347 | dat.ty = tp.y;
348 | dat.tz = tp.z;
349 | }
350 |
351 | function equatorial(dat) {
352 | dat.xeq = dat.x;
353 | dat.yeq = cosε * dat.y - sinε * dat.z;
354 | dat.zeq = sinε * dat.y + cosε * dat.z;
355 | dat.ra = Math.atan2(dat.yeq, dat.xeq);
356 | dat.dec = Math.atan2(dat.zeq, Math.sqrt(dat.xeq*dat.xeq + dat.yeq*dat.yeq));
357 | }
358 |
359 | function cartesian(dat) {
360 | var x, y, z, u = dat.v + dat.w;
361 | x = dat.r * (Math.cos(dat.N) * Math.cos(u) - Math.sin(dat.N) * Math.sin(u) * Math.cos(dat.i));
362 | y = dat.r * (Math.sin(dat.N) * Math.cos(u) + Math.cos(dat.N) * Math.sin(u) * Math.cos(dat.i));
363 | z = dat.r * (Math.sin(u) * Math.sin(dat.i));
364 | dat.x = x;
365 | dat.y = y;
366 | dat.z = z;
367 | return {x:x, y:y, z:z};
368 | }
369 |
370 | function spherical(dat) {
371 | var lon, lat;
372 | lon = Math.atan2(dat.y, dat.x);
373 | lat = Math.atan2(dat.z, Math.sqrt(dat.x*dat.x + dat.y*dat.y));
374 | dat.l = Trig.normalize(lon);
375 | dat.b = lat;
376 | return {l:lon, b:lat};
377 | }
378 |
379 | function JD(dt) {
380 | var yr = dt.getUTCFullYear(),
381 | mo = dt.getUTCMonth() + 1,
382 | dy = dt.getUTCDate(),
383 | frac = (dt.getUTCHours() - 12 + dt.getUTCMinutes()/60.0 + dt.getUTCSeconds()/3600.0) / 24,
384 | IYMIN = -4799; /* Earliest year allowed (4800BC) */
385 |
386 | if (yr < IYMIN) return -1;
387 | var a = Math.floor((14 - mo) / 12),
388 | y = yr + 4800 - a,
389 | m = mo + (12 * a) - 3;
390 | var jdn = dy + Math.floor((153 * m + 2)/5) + (365 * y) + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
391 | return jdn + frac;
392 | }
393 |
394 | //Transform coordinates according to euler angles
395 | function euler(dat, angles) {
396 | var x, y, z, β, γ, λ, φ, dψ, ψ, θ,
397 | ε = 1.0e-5;
398 |
399 | if (!angles) return [dat.l, dat.b];
400 |
401 | λ = dat.l; // longitude 0..2 Pi
402 | if (λ < 0) λ += τ;
403 | φ = dat.b; // latitude -Pi/2..Pi/2
404 |
405 | λ -= angles[0]; // celestial longitude of the native pole
406 | β = angles[1]; // inclination between the poles (colatitude)
407 | γ = angles[2]; // native longitude of the celestial pole
408 |
409 | x = Math.sin(φ) * Math.sin(β) - Math.cos(φ) * Math.cos(β) * Math.cos(λ);
410 | if (Math.abs(x) < ε) {
411 | x = -Math.cos(φ + β) + Math.cos(φ) * Math.cos(β) * (1 - Math.cos(λ));
412 | }
413 | y = -Math.cos(φ) * Math.sin(λ);
414 |
415 | if (x !== 0 || y !== 0) {
416 | dψ = Math.atan2(y, x);
417 | } else {
418 | dψ = λ - Math.PI;
419 | }
420 | ψ = (γ + dψ);
421 | if (ψ < 0) ψ += τ;
422 |
423 | if (λ % Math.PI === 0) {
424 | θ = φ + Math.cos(λ) * β;
425 | if (θ > halfπ) θ = Math.PI - θ;
426 | if (θ < -halfπ) θ = -Math.PI - θ;
427 | } else {
428 | z = Math.sin(φ) * Math.cos(β) + Math.cos(φ) * Math.sin(β) * Math.cos(λ);
429 | if (Math.abs(z) > 0.99) {
430 | θ = Math.abs(Math.acos(Math.sqrt(x*x+y*y)));
431 | if (z < 0) θ *= -1;
432 | } else {
433 | θ = Math.asin(z);
434 | }
435 | }
436 |
437 | dat.tl = ψ;
438 | dat.tb = θ;
439 | return [ψ, θ];
440 | }
441 |
--------------------------------------------------------------------------------
/threex.planets.js:
--------------------------------------------------------------------------------
1 | var THREEx = THREEx || {};
2 |
3 | THREEx.Planets = {};
4 |
5 | THREEx.Planets.baseURL = "../images/maps/";
6 | // Proportional scaling of planetary spheres
7 | THREEx.Planets.scale = 1.0;
8 |
9 | // maps from http://planetpixelemporium.com/ and others (see readme)
10 |
11 | THREEx.Planets.params = {
12 | // texture map, bump map, cloud map, arbitrary radius, axis tilt in degrees, rotation period in days,
13 | // rotation: axis pole long/lat in eclipic coordinate in degrees, angular velocity in degrees/day
14 | // ring: texture map, outer radius rel. to planet, opacity
15 | "sol": {map: "sunmap.jpg", radius: 1.2, tilt: 7.25, rot: 1.0438, rotation:[286.13, 63.87, 14.1844],
16 | corona: {map: "solarcorona.jpg", radius:5.1} },
17 | "mer": {map: "mercurymap.jpg", bump:"mercurybump.jpg", radius: 0.3, tilt: 0, rot: 58.646, rotation:[318.2274, 82.9623, 6.1385]},
18 | "ven": {map: "venusmap2.jpg", radius: 0.4, tilt: 177.3, rot: -4.05, rotation:[30.1871, 88.761, -1.481]},
19 | "ter": {map: "earthmap.jpg", bump:"earthbump.jpg", clouds:"earthclouds.png", radius: 0.4, tilt: 23.45, rot: 0.9973, rotation:[90, 66.5607, 360.9856]},
20 | "lun": {map: "moonmap.jpg", bump:"moonbump.jpg", radius: 0.25, tilt: 1.54, rot: 27.3217, rotation:[264.6051, 89.9784, 13.1763]},
21 | "mar": {map: "marsmap.jpg", bump:"marsbump.jpg", clouds:"marsclouds.png", radius: 0.35, tilt: 25.19, rot: 1.026, rotation:[352.9076, 63.2821, 350.8919] },
22 | "phob": {map: "phobosmap.jpg", bump: "phobosbump.jpg", radius: 0.05, tilt: 0.009, rot: 0.3189, rotation:[352.9259, 63.2928, 1128.8445]},
23 | "deim": {map: "deimosmap.jpg", radius: 0.04, tilt: 0.889, rot: 1.2624, rotation:[352.801, 64.1656, 285.1618]},
24 | "vest": {map: "vestamap.jpg", bump: "vestabump.jpg", radius: 0.1, tilt: 29.0, rot: 0.223, rotation:[330.8257, 57.7239, 1617.3329]},
25 | "cer": {map: "ceresmap.jpg", radius: 0.16, tilt: 4.0, rot: 0.378, rotation:[331.5058, 77.8769, 952.1532]},
26 | "jup": {map: "jupitermap.jpg", radius: 1.2, tilt: 3.12, rot: 0.414, rotation:[247.8167, 87.7835, 870.5360],
27 | ring: {map: "jupiterrings.png", radius: 2.7, opacity: 0.5} },
28 | "io": {map: "iomap.jpg", radius: 0.25, tilt: 0.0, rot: 1.7691, rotation:[247.7063, 87.7869, 203.4889]},
29 | "euro": {map: "europamap.jpg", radius: 0.25, tilt: 0.016, rot: 3.5512, rotation:[247.93, 87.8008, 101.3747]},
30 | "gany": {map: "ganymedemap.jpg", radius: 0.3, tilt: 0.068, rot: 7.1546, rotation:[248.6707, 87.8748, 50.3176]},
31 | "call": {map: "callistomap.jpg", radius: 0.3, tilt: 0.356, rot: 16.689, rotation:[252.4849, 88.191, 21.5710]},
32 | "sat": {map: "saturnmap.jpg", radius: 1.2, tilt: 26.73, rot: 0.444, rotation:[79.5275, 61.9478, 810.7939],
33 | ring: {map: "saturnrings.png", radius: 2.6, opacity: 1.0} },
34 | "mima": {map: "mimasmap.jpg", radius: 0.1, tilt: 0.002, rot: 0.942, rotation:[79.5174, 61.9296, 381.9945]},
35 | "ence": {map: "enceladusmap.jpg", radius: 0.1, tilt: 0.002, rot: 1.37, rotation:[79.5174, 61.9296, 262.7318]},
36 | "teth": {map: "tethysmap.jpg", radius: 0.15, tilt: 0.001, rot: 1.888, rotation:[79.5174, 61.9296, 190.6979]},
37 | "dion": {map: "dionemap.jpg", radius: 0.15, tilt: 0.005, rot: 2.737, rotation:[79.5174, 61.9296, 131.5349]},
38 | "rhea": {map: "rheamap.jpg", radius: 0.2, tilt: 0.036, rot: 4.518, rotation:[79.507, 61.9729, 79.690]},
39 | "tita": {map: "titanclouds.jpg", radius: 0.35, tilt: 0.629, rot: 15.95, rotation:[79.1738, 61.946, 22.577]},
40 | "hype": {map: "hyperionmap.jpg", radius: 0.08, tilt: 0.564, rot: 21.28, rotation:[79.3687, 61.9625, 16.917]},
41 | "iape": {map: "iapetusmap.jpg", radius: 0.15, tilt: 15.21, rot: 79.33, rotation:[49.608, 72.7238, 4.5379]},
42 | "phoe": {map: "phoebemap.jpg", radius: 0.08, tilt: 26.723, rot: 0.4, rotation:[60.8478, 64.3305, 931.639]},
43 | "ura": {map: "uranusmap.jpg", radius: 1.0, tilt: 97.86, rot: 0.718, rotation:[77.6467, -7.7218, 501.1601],
44 | ring: {map: "uranusrings.png", radius: 2.0, opacity: 0.5} },
45 | "arie": {map: "arielmap.jpg", radius: 0.15, tilt: 0.0, rot: -2.520, rotation:[77.7555, -7.8066, -142.8357]},
46 | "umbr": {map: "umbrielmap.jpg", radius: 0.15, tilt: 0.0, rot: -4.144, rotation:[77.7555, -7.8066, -86.8689]},
47 | "titan": {map: "titaniamap.jpg", radius: 0.2, tilt: 0.0, rot: -8.706, rotation:[77.7555, -7.8066, -41.3514]},
48 | "ober": {map: "oberonmap.jpg", radius: 0.2, tilt: 0.0, rot: -13.46, rotation:[77.7555, -7.8066, -26.7394]},
49 | "mira": {map: "mirandamap.jpg", radius: 0.1, tilt: 0.0, rot: -1.413, rotation:[77.7538, -7.8265, -254.6906]},
50 | "nep": {map: "neptunemap.jpg", radius: 1.0, tilt: 29.56, rot: 0.671, rotation:[319.2351, 61.9736, 536.3128],
51 | ring: {map: "neptunerings.png", radius: 2.5, opacity: 0.8} },
52 | "trit": {map: "tritonmap.jpg", radius: 0.2, tilt: 0.01, rot: -5.877, rotation:[317.3413, 59.8764, -61.2573]},
53 | "prot": {map: "proteusmap.jpg", radius: 0.1, tilt: 0.974, rot: 1.122, rotation:[318.631, 61.4993, 320.7654]},
54 | "plu": {map: "plutomap.jpg", radius: 0.2, tilt: 122.53, rot: 6.387, rotation:[133.6817, -10.9977, 56.3625]},
55 | "cha": {map: "charonmap.jpg", radius: 0.1, tilt: 0.0, rot: 6.387, rotation:[133.6817, -10.9977, 56.3625]}
56 | };
57 |
58 |
59 | // Friendly names
60 | var substitutes = {
61 | "Sun": "sol",
62 | "Mercury": "mer",
63 | "Venus": "ven",
64 | "Earth": "ter",
65 | "Moon": "lun",
66 | "Mars": "mar",
67 | "Phobos": "phob",
68 | "Deimos": "deim",
69 | "Vesta": "vest",
70 | "Ceres": "cer",
71 | "Jupiter": "jup",
72 | "Io": "io",
73 | "Europa": "euro",
74 | "Ganymede": "gany",
75 | "Callisto": "call",
76 | "Saturn": "sat",
77 | "Mimas": "mima",
78 | "Enceladus": "ence",
79 | "Tethys": "teth",
80 | "Dione": "dion",
81 | "Rhea": "rhea",
82 | "Titan": "tita",
83 | "Iapetus": "iape",
84 | "Phoebe": "phoe",
85 | "Uranus": "ura",
86 | "Ariel": "arie",
87 | "Umbriel": "umbr",
88 | "Titania": "titan",
89 | "Oberon": "ober",
90 | "Miranda": "mira",
91 | "Neptune": "nep",
92 | "Triton": "trit",
93 | "Pluto": "plu",
94 | "Charon": "cha",
95 | };
96 |
97 | THREEx.Planets.createSun = function() { return THREEx.Planets.create("sol"); };
98 | THREEx.Planets.createMercury = function() { return THREEx.Planets.create("mer"); };
99 | THREEx.Planets.createVenus = function() { return THREEx.Planets.create("ven"); };
100 | THREEx.Planets.createEarth = function() { return THREEx.Planets.create("ter", true); };
101 | THREEx.Planets.createMoon = function() { return THREEx.Planets.create("lun"); };
102 | THREEx.Planets.createMars = function() { return THREEx.Planets.create("mar"); };
103 | THREEx.Planets.createJupiter = function() { return THREEx.Planets.create("jup"); };
104 | THREEx.Planets.createJupiterRing = function() { return THREEx.Planets.createRing("jup"); };
105 | THREEx.Planets.createSaturn = function() { return THREEx.Planets.create("sat", true); };
106 | THREEx.Planets.createSaturnRing = function() { return THREEx.Planets.createRing("sat"); };
107 | THREEx.Planets.createUranus = function() { return THREEx.Planets.create("ura", true); };
108 | THREEx.Planets.createUranusRing = function() { return THREEx.Planets.createRing("ura"); };
109 | THREEx.Planets.createNeptune = function() { return THREEx.Planets.create("nepe"); };
110 | THREEx.Planets.createNeptuneRing = function() { return THREEx.Planets.createRing("nep"); };
111 | THREEx.Planets.createPluto = function() { return THREEx.Planets.create("plu"); };
112 |
113 | THREEx.Planets.createStarfield = function() {
114 | var loader = new THREE.TextureLoader();
115 | var texture = loader.load(THREEx.Planets.baseURL + "tycho-skymap.jpg");
116 | var material = new THREE.MeshBasicMaterial({
117 | map : texture,
118 | side : THREE.BackSide
119 | })
120 | var geometry = new THREE.SphereGeometry(100000, 32, 32)
121 | var mesh = new THREE.Mesh(geometry, material)
122 | return mesh
123 | }
124 |
125 | // Create body, skipextras true -> don't create cloud, ring etc.
126 | THREEx.Planets.create = function(body, skipextras) {
127 | if (!THREEx.Planets.params.hasOwnProperty(body)) {
128 | if (substitutes.hasOwnProperty(body)) body = substitutes[body];
129 | else {
130 | console.log("Object not found: " + body);
131 | return null;
132 | }
133 | }
134 | var p = THREEx.Planets.params[body], arg = {};
135 | var loader = new THREE.TextureLoader();
136 |
137 | var geometry = new THREE.SphereGeometry(p.radius * THREEx.Planets.scale, 32, 32);
138 |
139 | arg.map = loader.load(THREEx.Planets.baseURL + p.map);
140 |
141 | if (p.hasOwnProperty("bump")) {
142 | arg.bumpMap = loader.load(THREEx.Planets.baseURL + p.bump);
143 | arg.bumpScale = 0.001;
144 | }
145 | if (p.hasOwnProperty("spec")) {
146 | arg.specularMap = loader.load(THREEx.Planets.baseURL + p.spec);
147 | }
148 |
149 | if (body === "sol") { //ommmmmmm
150 | var material = new THREE.MeshBasicMaterial(arg);
151 | } else {
152 | var material = new THREE.MeshPhongMaterial(arg);
153 | arg.specular = new THREE.Color( 0x333333 );
154 | arg.shininess = 0.1;
155 | }
156 | var mesh = new THREE.Mesh(geometry, material);
157 |
158 | if (!skipextras && p.hasOwnProperty("ring")) {
159 | mesh.receiveShadow = true;
160 | mesh.castShadow = true;
161 | var ring = THREEx.Planets.createRings(body);
162 | ring.receiveShadow = true;
163 | ring.castShadow = true;
164 | mesh.add(ring);
165 | };
166 |
167 | if (!skipextras && p.hasOwnProperty("clouds")) {
168 | mesh.add(THREEx.Planets.createClouds(body));
169 | }
170 |
171 | if (!skipextras && body === "sol") {
172 | mesh.add(THREEx.Planets.createCorona());
173 | }
174 | mesh.rotateY(THREE.Math.degToRad(p.rotation[0]-180));
175 | mesh.rotateZ(THREE.Math.degToRad(90-p.rotation[1]));
176 | return mesh;
177 | }
178 |
179 |
180 | // Planetary rings
181 | THREEx.Planets.createRings = function(body) {
182 | if (!THREEx.Planets.params.hasOwnProperty(body)) {
183 | if (substitutes.hasOwnProperty(body)) body = substitutes[body];
184 | else {
185 | console.log("Object not found: " + body);
186 | return null;
187 | }
188 | }
189 | if (!THREEx.Planets.params[body].hasOwnProperty("ring")) {
190 | console.log("Rings not found: " + body);
191 | return null;
192 | }
193 | var p = THREEx.Planets.params[body], map = THREEx.Planets.baseURL + p.ring.map,
194 | loader = new THREE.TextureLoader();
195 |
196 | var geometry = new THREEx.Planets.RingGeometry(p.radius * THREEx.Planets.scale * 1.05, p.ring.radius * THREEx.Planets.scale, 64, 64);
197 | var material = new THREE.MeshPhongMaterial({
198 | map: loader.load(map),
199 | side: THREE.DoubleSide,
200 | transparent: true,
201 | opacity: p.ring.opacity
202 | });
203 | var mesh = new THREE.Mesh(geometry, material);
204 | mesh.lookAt(new THREE.Vector3(0, 1, 0));
205 | mesh.name = body + "rings";
206 | return mesh;
207 | }
208 |
209 | // Cloud layer from transparent png, see http://blog.thematicmapping.org/2013/09/creating-webgl-earth-with-threejs.html
210 | THREEx.Planets.createClouds = function(body) {
211 | if (!THREEx.Planets.params.hasOwnProperty(body)) {
212 | if (substitutes.hasOwnProperty(body)) body = substitutes[body];
213 | else {
214 | console.log("Object not found: " + body);
215 | return null;
216 | }
217 | }
218 | if (!THREEx.Planets.params[body].hasOwnProperty("clouds")) {
219 | console.log("Clouds not found: " + body);
220 | return null;
221 | }
222 | var p = THREEx.Planets.params[body], map = THREEx.Planets.baseURL + p.clouds,
223 | loader = new THREE.TextureLoader();
224 |
225 | var mesh = new THREE.Mesh(
226 | new THREE.SphereGeometry(p.radius * THREEx.Planets.scale * 1.01, 32, 32),
227 | new THREE.MeshPhongMaterial({
228 | map: loader.load(map),
229 | transparent: true
230 | })
231 | );
232 | mesh.name = body + "clouds";
233 | return mesh;
234 | }
235 |
236 | // Solar corona, based on Lee Stemkoski's https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Simple-Glow.html
237 | THREEx.Planets.createCorona = function() {
238 | var p = THREEx.Planets.params.sol, map = THREEx.Planets.baseURL + p.corona.map;
239 |
240 | var material = new THREE.SpriteMaterial({
241 | map: new THREE.TextureLoader().load(map),
242 | color: 0xffff33,
243 | transparent: false,
244 | blending: THREE.AdditiveBlending
245 | });
246 | var mesh = new THREE.Sprite(material);
247 | mesh.scale.multiplyScalar(p.corona.radius * THREEx.Planets.scale);
248 | mesh.name = "solcorona";
249 | return mesh;
250 | };
251 |
252 |
253 |
254 | /**
255 | * change the original from three.js because i needed different UV
256 | *
257 | * @author Kaleb Murphy
258 | * @author Olaf Frohn
259 | */
260 | THREEx.Planets.RingGeometry = function (innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) {
261 |
262 | THREE.Geometry.call( this );
263 |
264 | this.type = 'RingGeometry';
265 |
266 | this.parameters = {
267 | innerRadius: innerRadius,
268 | outerRadius: outerRadius,
269 | thetaSegments: thetaSegments,
270 | phiSegments: phiSegments,
271 | thetaStart: thetaStart,
272 | thetaLength: thetaLength
273 | };
274 |
275 | innerRadius = innerRadius || 0;
276 | outerRadius = outerRadius || 50;
277 |
278 | thetaStart = thetaStart !== undefined ? thetaStart : 0;
279 | thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
280 |
281 | thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
282 | phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
283 |
284 | var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
285 |
286 | for ( i = 0; i < phiSegments + 1; i ++ ) {
287 |
288 | // concentric circles inside ring
289 |
290 | for ( o = 0; o < thetaSegments + 1; o ++ ) {
291 |
292 | // number of segments per circle
293 |
294 | var vertex = new THREE.Vector3();
295 | var segment = thetaStart + o / thetaSegments * thetaLength;
296 | vertex.x = radius * Math.cos( segment );
297 | vertex.y = radius * Math.sin( segment );
298 |
299 | this.vertices.push( vertex );
300 | //uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );
301 | uvs.push( new THREE.Vector2( i/(thetaSegments-1), o/ (phiSegments-1) ) );
302 | }
303 |
304 | radius += radiusStep;
305 |
306 | }
307 |
308 | var n = new THREE.Vector3( 0, 0, 1 );
309 |
310 | for ( i = 0; i < phiSegments; i ++ ) {
311 |
312 | // concentric circles inside ring
313 |
314 | var thetaSegment = i * ( thetaSegments + 1 );
315 |
316 | for ( o = 0; o < thetaSegments ; o ++ ) {
317 |
318 | // number of segments per circle
319 |
320 | var segment = o + thetaSegment;
321 |
322 | var v1 = segment;
323 | var v2 = segment + thetaSegments + 1;
324 | var v3 = segment + thetaSegments + 2;
325 |
326 | this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
327 | this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );
328 |
329 | v1 = segment;
330 | v2 = segment + thetaSegments + 2;
331 | v3 = segment + 1;
332 |
333 | this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
334 | this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );
335 |
336 | }
337 |
338 | }
339 |
340 | this.computeFaceNormals();
341 |
342 | this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
343 |
344 | };
345 |
346 | THREEx.Planets.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
347 | THREEx.Planets.RingGeometry.prototype.constructor = THREEx.Planets.RingGeometry;
348 |
--------------------------------------------------------------------------------
/lib/threex.planets.js:
--------------------------------------------------------------------------------
1 | var THREEx = THREEx || {};
2 |
3 | THREEx.Planets = {};
4 |
5 | THREEx.Planets.baseURL = "../images/maps/";
6 | // Proportional scaling of planetary spheres
7 | THREEx.Planets.scale = 1.0;
8 |
9 | // maps from http://planetpixelemporium.com/ and others (see readme)
10 |
11 | THREEx.Planets.params = {
12 | // texture map, bump map, cloud map, arbitrary radius, axis tilt in degrees, rotation period in days,
13 | // rotation: axis pole long/lat in eclipic coordinate in degrees, angular velocity in degrees/day
14 | // ring: texture map, outer radius rel. to planet, opacity
15 | "sol": {map: "sunmap.jpg", radius: 1.2, tilt: 7.25, rot: 1.0438, rotation:[286.13, 63.87, 14.1844],
16 | corona: {map: "solarcorona.jpg", radius:5.1} },
17 | "mer": {map: "mercurymap.jpg", bump:"mercurybump.jpg", radius: 0.3, tilt: 0, rot: 58.646, rotation:[318.2274, 82.9623, 6.1385]},
18 | "ven": {map: "venusmap2.jpg", radius: 0.4, tilt: 177.3, rot: -4.05, rotation:[30.1871, 88.761, -1.481]},
19 | "ter": {map: "earthmap.jpg", bump:"earthbump.jpg", clouds:"earthclouds.png", radius: 0.4, tilt: 23.45, rot: 0.9973, rotation:[90, 66.5607, 360.9856]},
20 | "lun": {map: "moonmap.jpg", bump:"moonbump.jpg", radius: 0.25, tilt: 1.54, rot: 27.3217, rotation:[264.6051, 89.9784, 13.1763]},
21 | "mar": {map: "marsmap.jpg", bump:"marsbump.jpg", clouds:"marsclouds.png", radius: 0.35, tilt: 25.19, rot: 1.026, rotation:[352.9076, 63.2821, 350.8919] },
22 | "phob": {map: "phobosmap.jpg", bump: "phobosbump.jpg", radius: 0.05, tilt: 0.009, rot: 0.3189, rotation:[352.9259, 63.2928, 1128.8445]},
23 | "deim": {map: "deimosmap.jpg", radius: 0.04, tilt: 0.889, rot: 1.2624, rotation:[352.801, 64.1656, 285.1618]},
24 | "vest": {map: "vestamap.jpg", bump: "vestabump.jpg", radius: 0.1, tilt: 29.0, rot: 0.223, rotation:[330.8257, 57.7239, 1617.3329]},
25 | "cer": {map: "ceresmap.jpg", radius: 0.16, tilt: 4.0, rot: 0.378, rotation:[331.5058, 77.8769, 952.1532]},
26 | "jup": {map: "jupitermap.jpg", radius: 1.2, tilt: 3.12, rot: 0.414, rotation:[247.8167, 87.7835, 870.5360],
27 | ring: {map: "jupiterrings.png", radius: 2.7, opacity: 0.5} },
28 | "io": {map: "iomap.jpg", radius: 0.25, tilt: 0.0, rot: 1.7691, rotation:[247.7063, 87.7869, 203.4889]},
29 | "euro": {map: "europamap.jpg", radius: 0.25, tilt: 0.016, rot: 3.5512, rotation:[247.93, 87.8008, 101.3747]},
30 | "gany": {map: "ganymedemap.jpg", radius: 0.3, tilt: 0.068, rot: 7.1546, rotation:[248.6707, 87.8748, 50.3176]},
31 | "call": {map: "callistomap.jpg", radius: 0.3, tilt: 0.356, rot: 16.689, rotation:[252.4849, 88.191, 21.5710]},
32 | "sat": {map: "saturnmap.jpg", radius: 1.2, tilt: 26.73, rot: 0.444, rotation:[79.5275, 61.9478, 810.7939],
33 | ring: {map: "saturnrings.png", radius: 2.6, opacity: 1.0} },
34 | "mima": {map: "mimasmap.jpg", radius: 0.1, tilt: 0.002, rot: 0.942, rotation:[79.5174, 61.9296, 381.9945]},
35 | "ence": {map: "enceladusmap.jpg", radius: 0.1, tilt: 0.002, rot: 1.37, rotation:[79.5174, 61.9296, 262.7318]},
36 | "teth": {map: "tethysmap.jpg", radius: 0.15, tilt: 0.001, rot: 1.888, rotation:[79.5174, 61.9296, 190.6979]},
37 | "dion": {map: "dionemap.jpg", radius: 0.15, tilt: 0.005, rot: 2.737, rotation:[79.5174, 61.9296, 131.5349]},
38 | "rhea": {map: "rheamap.jpg", radius: 0.2, tilt: 0.036, rot: 4.518, rotation:[79.507, 61.9729, 79.690]},
39 | "tita": {map: "titanclouds.jpg", radius: 0.35, tilt: 0.629, rot: 15.95, rotation:[79.1738, 61.946, 22.577]},
40 | "hype": {map: "hyperionmap.jpg", radius: 0.08, tilt: 0.564, rot: 21.28, rotation:[79.3687, 61.9625, 16.917]},
41 | "iape": {map: "iapetusmap.jpg", radius: 0.15, tilt: 15.21, rot: 79.33, rotation:[49.608, 72.7238, 4.5379]},
42 | "phoe": {map: "phoebemap.jpg", radius: 0.08, tilt: 26.723, rot: 0.4, rotation:[60.8478, 64.3305, 931.639]},
43 | "ura": {map: "uranusmap.jpg", radius: 1.0, tilt: 97.86, rot: 0.718, rotation:[77.6467, -7.7218, 501.1601],
44 | ring: {map: "uranusrings.png", radius: 2.0, opacity: 0.5} },
45 | "arie": {map: "arielmap.jpg", radius: 0.15, tilt: 0.0, rot: -2.520, rotation:[77.7555, -7.8066, -142.8357]},
46 | "umbr": {map: "umbrielmap.jpg", radius: 0.15, tilt: 0.0, rot: -4.144, rotation:[77.7555, -7.8066, -86.8689]},
47 | "titan": {map: "titaniamap.jpg", radius: 0.2, tilt: 0.0, rot: -8.706, rotation:[77.7555, -7.8066, -41.3514]},
48 | "ober": {map: "oberonmap.jpg", radius: 0.2, tilt: 0.0, rot: -13.46, rotation:[77.7555, -7.8066, -26.7394]},
49 | "mira": {map: "mirandamap.jpg", radius: 0.1, tilt: 0.0, rot: -1.413, rotation:[77.7538, -7.8265, -254.6906]},
50 | "nep": {map: "neptunemap.jpg", radius: 1.0, tilt: 29.56, rot: 0.671, rotation:[319.2351, 61.9736, 536.3128],
51 | ring: {map: "neptunerings.png", radius: 2.5, opacity: 0.8} },
52 | "trit": {map: "tritonmap.jpg", radius: 0.2, tilt: 0.01, rot: -5.877, rotation:[317.3413, 59.8764, -61.2573]},
53 | "prot": {map: "proteusmap.jpg", radius: 0.1, tilt: 0.974, rot: 1.122, rotation:[318.631, 61.4993, 320.7654]},
54 | "plu": {map: "plutomap.jpg", radius: 0.2, tilt: 122.53, rot: 6.387, rotation:[133.6817, -10.9977, 56.3625]},
55 | "cha": {map: "charonmap.jpg", radius: 0.1, tilt: 0.0, rot: 6.387, rotation:[133.6817, -10.9977, 56.3625]}
56 | };
57 |
58 |
59 | // Friendly names
60 | var substitutes = {
61 | "Sun": "sol",
62 | "Mercury": "mer",
63 | "Venus": "ven",
64 | "Earth": "ter",
65 | "Moon": "lun",
66 | "Mars": "mar",
67 | "Phobos": "phob",
68 | "Deimos": "deim",
69 | "Vesta": "vest",
70 | "Ceres": "cer",
71 | "Jupiter": "jup",
72 | "Io": "io",
73 | "Europa": "euro",
74 | "Ganymede": "gany",
75 | "Callisto": "call",
76 | "Saturn": "sat",
77 | "Mimas": "mima",
78 | "Enceladus": "ence",
79 | "Tethys": "teth",
80 | "Dione": "dion",
81 | "Rhea": "rhea",
82 | "Titan": "tita",
83 | "Iapetus": "iape",
84 | "Phoebe": "phoe",
85 | "Uranus": "ura",
86 | "Ariel": "arie",
87 | "Umbriel": "umbr",
88 | "Titania": "titan",
89 | "Oberon": "ober",
90 | "Miranda": "mira",
91 | "Neptune": "nep",
92 | "Triton": "trit",
93 | "Pluto": "plu",
94 | "Charon": "cha",
95 | };
96 |
97 | THREEx.Planets.createSun = function() { return THREEx.Planets.create("sol"); };
98 | THREEx.Planets.createMercury = function() { return THREEx.Planets.create("mer"); };
99 | THREEx.Planets.createVenus = function() { return THREEx.Planets.create("ven"); };
100 | THREEx.Planets.createEarth = function() { return THREEx.Planets.create("ter", true); };
101 | THREEx.Planets.createMoon = function() { return THREEx.Planets.create("lun"); };
102 | THREEx.Planets.createMars = function() { return THREEx.Planets.create("mar"); };
103 | THREEx.Planets.createJupiter = function() { return THREEx.Planets.create("jup"); };
104 | THREEx.Planets.createJupiterRing = function() { return THREEx.Planets.createRing("jup"); };
105 | THREEx.Planets.createSaturn = function() { return THREEx.Planets.create("sat", true); };
106 | THREEx.Planets.createSaturnRing = function() { return THREEx.Planets.createRing("sat"); };
107 | THREEx.Planets.createUranus = function() { return THREEx.Planets.create("ura", true); };
108 | THREEx.Planets.createUranusRing = function() { return THREEx.Planets.createRing("ura"); };
109 | THREEx.Planets.createNeptune = function() { return THREEx.Planets.create("nepe"); };
110 | THREEx.Planets.createNeptuneRing = function() { return THREEx.Planets.createRing("nep"); };
111 | THREEx.Planets.createPluto = function() { return THREEx.Planets.create("plu"); };
112 |
113 | THREEx.Planets.createStarfield = function() {
114 | var loader = new THREE.TextureLoader();
115 | var texture = loader.load(THREEx.Planets.baseURL + "tycho-skymap.jpg");
116 | var material = new THREE.MeshBasicMaterial({
117 | map : texture,
118 | side : THREE.BackSide
119 | })
120 | var geometry = new THREE.SphereGeometry(100000, 32, 32)
121 | var mesh = new THREE.Mesh(geometry, material)
122 | return mesh
123 | }
124 |
125 | // Create body, skipextras true -> don't create cloud, ring etc.
126 | THREEx.Planets.create = function(body, skipextras) {
127 | if (!THREEx.Planets.params.hasOwnProperty(body)) {
128 | if (substitutes.hasOwnProperty(body)) body = substitutes[body];
129 | else {
130 | console.log("Object not found: " + body);
131 | return null;
132 | }
133 | }
134 | var p = THREEx.Planets.params[body], arg = {};
135 | var loader = new THREE.TextureLoader();
136 |
137 | var geometry = new THREE.SphereGeometry(p.radius * THREEx.Planets.scale, 32, 32);
138 |
139 | arg.map = loader.load(THREEx.Planets.baseURL + p.map);
140 |
141 | if (p.hasOwnProperty("bump")) {
142 | arg.bumpMap = loader.load(THREEx.Planets.baseURL + p.bump);
143 | arg.bumpScale = 0.001;
144 | }
145 | if (p.hasOwnProperty("spec")) {
146 | arg.specularMap = loader.load(THREEx.Planets.baseURL + p.spec);
147 | }
148 |
149 | if (body === "sol") { //ommmmmmm
150 | var material = new THREE.MeshBasicMaterial(arg);
151 | } else {
152 | var material = new THREE.MeshPhongMaterial(arg);
153 | arg.specular = new THREE.Color( 0x333333 );
154 | arg.shininess = 0.1;
155 | }
156 | var mesh = new THREE.Mesh(geometry, material);
157 |
158 | if (!skipextras && p.hasOwnProperty("ring")) {
159 | mesh.receiveShadow = true;
160 | mesh.castShadow = true;
161 | var ring = THREEx.Planets.createRings(body);
162 | ring.receiveShadow = true;
163 | ring.castShadow = true;
164 | mesh.add(ring);
165 | };
166 |
167 | if (!skipextras && p.hasOwnProperty("clouds")) {
168 | mesh.add(THREEx.Planets.createClouds(body));
169 | }
170 |
171 | if (!skipextras && body === "sol") {
172 | mesh.add(THREEx.Planets.createCorona());
173 | }
174 | mesh.rotateY(THREE.Math.degToRad(p.rotation[0]-180));
175 | mesh.rotateZ(THREE.Math.degToRad(90-p.rotation[1]));
176 | return mesh;
177 | }
178 |
179 |
180 | // Planetary rings
181 | THREEx.Planets.createRings = function(body) {
182 | if (!THREEx.Planets.params.hasOwnProperty(body)) {
183 | if (substitutes.hasOwnProperty(body)) body = substitutes[body];
184 | else {
185 | console.log("Object not found: " + body);
186 | return null;
187 | }
188 | }
189 | if (!THREEx.Planets.params[body].hasOwnProperty("ring")) {
190 | console.log("Rings not found: " + body);
191 | return null;
192 | }
193 | var p = THREEx.Planets.params[body], map = THREEx.Planets.baseURL + p.ring.map,
194 | loader = new THREE.TextureLoader();
195 |
196 | var geometry = new THREEx.Planets.RingGeometry(p.radius * THREEx.Planets.scale * 1.05, p.ring.radius * THREEx.Planets.scale, 64, 64);
197 | var material = new THREE.MeshPhongMaterial({
198 | map: loader.load(map),
199 | side: THREE.DoubleSide,
200 | transparent: true,
201 | opacity: p.ring.opacity
202 | });
203 | var mesh = new THREE.Mesh(geometry, material);
204 | mesh.lookAt(new THREE.Vector3(0, 1, 0));
205 | mesh.name = body + "rings";
206 | return mesh;
207 | }
208 |
209 | // Cloud layer from transparent png, see http://blog.thematicmapping.org/2013/09/creating-webgl-earth-with-threejs.html
210 | THREEx.Planets.createClouds = function(body) {
211 | if (!THREEx.Planets.params.hasOwnProperty(body)) {
212 | if (substitutes.hasOwnProperty(body)) body = substitutes[body];
213 | else {
214 | console.log("Object not found: " + body);
215 | return null;
216 | }
217 | }
218 | if (!THREEx.Planets.params[body].hasOwnProperty("clouds")) {
219 | console.log("Clouds not found: " + body);
220 | return null;
221 | }
222 | var p = THREEx.Planets.params[body], map = THREEx.Planets.baseURL + p.clouds,
223 | loader = new THREE.TextureLoader();
224 |
225 | var mesh = new THREE.Mesh(
226 | new THREE.SphereGeometry(p.radius * THREEx.Planets.scale * 1.01, 32, 32),
227 | new THREE.MeshPhongMaterial({
228 | map: loader.load(map),
229 | transparent: true
230 | })
231 | );
232 | mesh.name = body + "clouds";
233 | return mesh;
234 | }
235 |
236 | // Solar corona, based on Lee Stemkoski's https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Simple-Glow.html
237 | THREEx.Planets.createCorona = function() {
238 | var p = THREEx.Planets.params.sol, map = THREEx.Planets.baseURL + p.corona.map;
239 |
240 | var material = new THREE.SpriteMaterial({
241 | map: new THREE.TextureLoader().load(map),
242 | color: 0xffff33,
243 | transparent: false,
244 | blending: THREE.AdditiveBlending
245 | });
246 | var mesh = new THREE.Sprite(material);
247 | mesh.scale.multiplyScalar(p.corona.radius * THREEx.Planets.scale);
248 | mesh.name = "solcorona";
249 | return mesh;
250 | };
251 |
252 |
253 |
254 | /**
255 | * change the original from three.js because i needed different UV
256 | *
257 | * @author Kaleb Murphy
258 | * @author Olaf Frohn
259 | */
260 | THREEx.Planets.RingGeometry = function (innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) {
261 |
262 | THREE.Geometry.call( this );
263 |
264 | this.type = 'RingGeometry';
265 |
266 | this.parameters = {
267 | innerRadius: innerRadius,
268 | outerRadius: outerRadius,
269 | thetaSegments: thetaSegments,
270 | phiSegments: phiSegments,
271 | thetaStart: thetaStart,
272 | thetaLength: thetaLength
273 | };
274 |
275 | innerRadius = innerRadius || 0;
276 | outerRadius = outerRadius || 50;
277 |
278 | thetaStart = thetaStart !== undefined ? thetaStart : 0;
279 | thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
280 |
281 | thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
282 | phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
283 |
284 | var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
285 |
286 | for ( i = 0; i < phiSegments + 1; i ++ ) {
287 |
288 | // concentric circles inside ring
289 |
290 | for ( o = 0; o < thetaSegments + 1; o ++ ) {
291 |
292 | // number of segments per circle
293 |
294 | var vertex = new THREE.Vector3();
295 | var segment = thetaStart + o / thetaSegments * thetaLength;
296 | vertex.x = radius * Math.cos( segment );
297 | vertex.y = radius * Math.sin( segment );
298 |
299 | this.vertices.push( vertex );
300 | //uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );
301 | uvs.push( new THREE.Vector2( i/(thetaSegments-1), o/ (phiSegments-1) ) );
302 | }
303 |
304 | radius += radiusStep;
305 |
306 | }
307 |
308 | var n = new THREE.Vector3( 0, 0, 1 );
309 |
310 | for ( i = 0; i < phiSegments; i ++ ) {
311 |
312 | // concentric circles inside ring
313 |
314 | var thetaSegment = i * ( thetaSegments + 1 );
315 |
316 | for ( o = 0; o < thetaSegments ; o ++ ) {
317 |
318 | // number of segments per circle
319 |
320 | var segment = o + thetaSegment;
321 |
322 | var v1 = segment;
323 | var v2 = segment + thetaSegments + 1;
324 | var v3 = segment + thetaSegments + 2;
325 |
326 | this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
327 | this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );
328 |
329 | v1 = segment;
330 | v2 = segment + thetaSegments + 2;
331 | v3 = segment + 1;
332 |
333 | this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
334 | this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );
335 |
336 | }
337 |
338 | }
339 |
340 | this.computeFaceNormals();
341 |
342 | this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
343 |
344 | };
345 |
346 | THREEx.Planets.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
347 | THREEx.Planets.RingGeometry.prototype.constructor = THREEx.Planets.RingGeometry;
348 |
--------------------------------------------------------------------------------
/lib/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 |
9 | // This set of controls performs orbiting, dollying (zooming), and panning.
10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
11 | //
12 | // Orbit - left mouse / touch: one finger move
13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
14 | // Pan - right mouse, or arrow keys / touch: three finter swipe
15 |
16 | THREE.OrbitControls = function ( object, domElement ) {
17 |
18 | this.object = object;
19 |
20 | this.domElement = ( domElement !== undefined ) ? domElement : document;
21 |
22 | // Set to false to disable this control
23 | this.enabled = true;
24 |
25 | // "target" sets the location of focus, where the object orbits around
26 | this.target = new THREE.Vector3();
27 |
28 | // How far you can dolly in and out ( PerspectiveCamera only )
29 | this.minDistance = 0;
30 | this.maxDistance = Infinity;
31 |
32 | // How far you can zoom in and out ( OrthographicCamera only )
33 | this.minZoom = 0;
34 | this.maxZoom = Infinity;
35 |
36 | // How far you can orbit vertically, upper and lower limits.
37 | // Range is 0 to Math.PI radians.
38 | this.minPolarAngle = 0; // radians
39 | this.maxPolarAngle = Math.PI; // radians
40 |
41 | // How far you can orbit horizontally, upper and lower limits.
42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
43 | this.minAzimuthAngle = - Infinity; // radians
44 | this.maxAzimuthAngle = Infinity; // radians
45 |
46 | // Set to true to enable damping (inertia)
47 | // If damping is enabled, you must call controls.update() in your animation loop
48 | this.enableDamping = false;
49 | this.dampingFactor = 0.25;
50 |
51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
52 | // Set to false to disable zooming
53 | this.enableZoom = true;
54 | this.zoomSpeed = 1.0;
55 |
56 | // Set to false to disable rotating
57 | this.enableRotate = true;
58 | this.rotateSpeed = 1.0;
59 |
60 | // Set to false to disable panning
61 | this.enablePan = true;
62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
63 |
64 | // Set to true to automatically rotate around the target
65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
66 | this.autoRotate = false;
67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
68 |
69 | // Set to false to disable use of the keys
70 | this.enableKeys = true;
71 |
72 | // The four arrow keys
73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
74 |
75 | // Mouse buttons
76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
77 |
78 | // for reset
79 | this.target0 = this.target.clone();
80 | this.position0 = this.object.position.clone();
81 | this.zoom0 = this.object.zoom;
82 |
83 | //
84 | // public methods
85 | //
86 |
87 | this.getPolarAngle = function () {
88 |
89 | return phi;
90 |
91 | };
92 |
93 | this.getAzimuthalAngle = function () {
94 |
95 | return theta;
96 |
97 | };
98 |
99 | this.reset = function () {
100 |
101 | scope.target.copy( scope.target0 );
102 | scope.object.position.copy( scope.position0 );
103 | scope.object.zoom = scope.zoom0;
104 |
105 | scope.object.updateProjectionMatrix();
106 | scope.dispatchEvent( changeEvent );
107 |
108 | scope.update();
109 |
110 | state = STATE.NONE;
111 |
112 | };
113 |
114 | // this method is exposed, but perhaps it would be better if we can make it private...
115 | this.update = function() {
116 |
117 | var offset = new THREE.Vector3();
118 |
119 | // so camera.up is the orbit axis
120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
121 | var quatInverse = quat.clone().inverse();
122 |
123 | var lastPosition = new THREE.Vector3();
124 | var lastQuaternion = new THREE.Quaternion();
125 |
126 | return function () {
127 |
128 | var position = scope.object.position;
129 |
130 | offset.copy( position ).sub( scope.target );
131 |
132 | // rotate offset to "y-axis-is-up" space
133 | offset.applyQuaternion( quat );
134 |
135 | // angle from z-axis around y-axis
136 |
137 | theta = Math.atan2( offset.x, offset.z );
138 |
139 | // angle from y-axis
140 |
141 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
142 |
143 | if ( scope.autoRotate && state === STATE.NONE ) {
144 |
145 | rotateLeft( getAutoRotationAngle() );
146 |
147 | }
148 |
149 | theta += thetaDelta;
150 | phi += phiDelta;
151 |
152 | // restrict theta to be between desired limits
153 | theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, theta ) );
154 |
155 | // restrict phi to be between desired limits
156 | phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, phi ) );
157 |
158 | // restrict phi to be betwee EPS and PI-EPS
159 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
160 |
161 | var radius = offset.length() * scale;
162 |
163 | // restrict radius to be between desired limits
164 | radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, radius ) );
165 |
166 | // move target to panned location
167 | scope.target.add( panOffset );
168 |
169 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
170 | offset.y = radius * Math.cos( phi );
171 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
172 |
173 | // rotate offset back to "camera-up-vector-is-up" space
174 | offset.applyQuaternion( quatInverse );
175 |
176 | position.copy( scope.target ).add( offset );
177 |
178 | scope.object.lookAt( scope.target );
179 |
180 | if ( scope.enableDamping === true ) {
181 |
182 | thetaDelta *= ( 1 - scope.dampingFactor );
183 | phiDelta *= ( 1 - scope.dampingFactor );
184 |
185 | } else {
186 |
187 | thetaDelta = 0;
188 | phiDelta = 0;
189 |
190 | }
191 |
192 | scale = 1;
193 | panOffset.set( 0, 0, 0 );
194 |
195 | // update condition is:
196 | // min(camera displacement, camera rotation in radians)^2 > EPS
197 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
198 |
199 | if ( zoomChanged ||
200 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
201 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
202 |
203 | scope.dispatchEvent( changeEvent );
204 |
205 | lastPosition.copy( scope.object.position );
206 | lastQuaternion.copy( scope.object.quaternion );
207 | zoomChanged = false;
208 |
209 | return true;
210 |
211 | }
212 |
213 | return false;
214 |
215 | };
216 |
217 | }();
218 |
219 | this.dispose = function() {
220 |
221 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
222 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
223 | scope.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
224 | scope.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
225 |
226 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
227 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
228 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
229 |
230 | document.removeEventListener( 'mousemove', onMouseMove, false );
231 | document.removeEventListener( 'mouseup', onMouseUp, false );
232 | document.removeEventListener( 'mouseout', onMouseUp, false );
233 |
234 | window.removeEventListener( 'keydown', onKeyDown, false );
235 |
236 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
237 |
238 | };
239 |
240 | //
241 | // internals
242 | //
243 |
244 | var scope = this;
245 |
246 | var changeEvent = { type: 'change' };
247 | var startEvent = { type: 'start' };
248 | var endEvent = { type: 'end' };
249 |
250 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
251 |
252 | var state = STATE.NONE;
253 |
254 | var EPS = 0.000001;
255 |
256 | // current position in spherical coordinates
257 | var theta;
258 | var phi;
259 |
260 | var phiDelta = 0;
261 | var thetaDelta = 0;
262 | var scale = 1;
263 | var panOffset = new THREE.Vector3();
264 | var zoomChanged = false;
265 |
266 | var rotateStart = new THREE.Vector2();
267 | var rotateEnd = new THREE.Vector2();
268 | var rotateDelta = new THREE.Vector2();
269 |
270 | var panStart = new THREE.Vector2();
271 | var panEnd = new THREE.Vector2();
272 | var panDelta = new THREE.Vector2();
273 |
274 | var dollyStart = new THREE.Vector2();
275 | var dollyEnd = new THREE.Vector2();
276 | var dollyDelta = new THREE.Vector2();
277 |
278 | function getAutoRotationAngle() {
279 |
280 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
281 |
282 | }
283 |
284 | function getZoomScale() {
285 |
286 | return Math.pow( 0.95, scope.zoomSpeed );
287 |
288 | }
289 |
290 | function rotateLeft( angle ) {
291 |
292 | thetaDelta -= angle;
293 |
294 | }
295 |
296 | function rotateUp( angle ) {
297 |
298 | phiDelta -= angle;
299 |
300 | }
301 |
302 | var panLeft = function() {
303 |
304 | var v = new THREE.Vector3();
305 |
306 | return function panLeft( distance, objectMatrix ) {
307 |
308 | var te = objectMatrix.elements;
309 |
310 | // get X column of objectMatrix
311 | v.set( te[ 0 ], te[ 1 ], te[ 2 ] );
312 |
313 | v.multiplyScalar( - distance );
314 |
315 | panOffset.add( v );
316 |
317 | };
318 |
319 | }();
320 |
321 | var panUp = function() {
322 |
323 | var v = new THREE.Vector3();
324 |
325 | return function panUp( distance, objectMatrix ) {
326 |
327 | var te = objectMatrix.elements;
328 |
329 | // get Y column of objectMatrix
330 | v.set( te[ 4 ], te[ 5 ], te[ 6 ] );
331 |
332 | v.multiplyScalar( distance );
333 |
334 | panOffset.add( v );
335 |
336 | };
337 |
338 | }();
339 |
340 | // deltaX and deltaY are in pixels; right and down are positive
341 | var pan = function() {
342 |
343 | var offset = new THREE.Vector3();
344 |
345 | return function( deltaX, deltaY ) {
346 |
347 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
348 |
349 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
350 |
351 | // perspective
352 | var position = scope.object.position;
353 | offset.copy( position ).sub( scope.target );
354 | var targetDistance = offset.length();
355 |
356 | // half of the fov is center to top of screen
357 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
358 |
359 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
360 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
361 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
362 |
363 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
364 |
365 | // orthographic
366 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / element.clientWidth, scope.object.matrix );
367 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / element.clientHeight, scope.object.matrix );
368 |
369 | } else {
370 |
371 | // camera neither orthographic nor perspective
372 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
373 | scope.enablePan = false;
374 |
375 | }
376 |
377 | };
378 |
379 | }();
380 |
381 | function dollyIn( dollyScale ) {
382 |
383 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
384 |
385 | scale /= dollyScale;
386 |
387 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
388 |
389 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
390 | scope.object.updateProjectionMatrix();
391 | zoomChanged = true;
392 |
393 | } else {
394 |
395 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
396 | scope.enableZoom = false;
397 |
398 | }
399 |
400 | }
401 |
402 | function dollyOut( dollyScale ) {
403 |
404 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
405 |
406 | scale *= dollyScale;
407 |
408 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
409 |
410 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
411 | scope.object.updateProjectionMatrix();
412 | zoomChanged = true;
413 |
414 | } else {
415 |
416 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
417 | scope.enableZoom = false;
418 |
419 | }
420 |
421 | }
422 |
423 | //
424 | // event callbacks - update the object state
425 | //
426 |
427 | function handleMouseDownRotate( event ) {
428 |
429 | //console.log( 'handleMouseDownRotate' );
430 |
431 | rotateStart.set( event.clientX, event.clientY );
432 |
433 | }
434 |
435 | function handleMouseDownDolly( event ) {
436 |
437 | //console.log( 'handleMouseDownDolly' );
438 |
439 | dollyStart.set( event.clientX, event.clientY );
440 |
441 | }
442 |
443 | function handleMouseDownPan( event ) {
444 |
445 | //console.log( 'handleMouseDownPan' );
446 |
447 | panStart.set( event.clientX, event.clientY );
448 |
449 | }
450 |
451 | function handleMouseMoveRotate( event ) {
452 |
453 | //console.log( 'handleMouseMoveRotate' );
454 |
455 | rotateEnd.set( event.clientX, event.clientY );
456 | rotateDelta.subVectors( rotateEnd, rotateStart );
457 |
458 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
459 |
460 | // rotating across whole screen goes 360 degrees around
461 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
462 |
463 | // rotating up and down along whole screen attempts to go 360, but limited to 180
464 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
465 |
466 | rotateStart.copy( rotateEnd );
467 |
468 | scope.update();
469 |
470 | }
471 |
472 | function handleMouseMoveDolly( event ) {
473 |
474 | //console.log( 'handleMouseMoveDolly' );
475 |
476 | dollyEnd.set( event.clientX, event.clientY );
477 |
478 | dollyDelta.subVectors( dollyEnd, dollyStart );
479 |
480 | if ( dollyDelta.y > 0 ) {
481 |
482 | dollyIn( getZoomScale() );
483 |
484 | } else if ( dollyDelta.y < 0 ) {
485 |
486 | dollyOut( getZoomScale() );
487 |
488 | }
489 |
490 | dollyStart.copy( dollyEnd );
491 |
492 | scope.update();
493 |
494 | }
495 |
496 | function handleMouseMovePan( event ) {
497 |
498 | //console.log( 'handleMouseMovePan' );
499 |
500 | panEnd.set( event.clientX, event.clientY );
501 |
502 | panDelta.subVectors( panEnd, panStart );
503 |
504 | pan( panDelta.x, panDelta.y );
505 |
506 | panStart.copy( panEnd );
507 |
508 | scope.update();
509 |
510 | }
511 |
512 | function handleMouseUp( event ) {
513 |
514 | //console.log( 'handleMouseUp' );
515 |
516 | }
517 |
518 | function handleMouseWheel( event ) {
519 |
520 | //console.log( 'handleMouseWheel' );
521 |
522 | var delta = 0;
523 |
524 | if ( event.wheelDelta !== undefined ) {
525 |
526 | // WebKit / Opera / Explorer 9
527 |
528 | delta = event.wheelDelta;
529 |
530 | } else if ( event.detail !== undefined ) {
531 |
532 | // Firefox
533 |
534 | delta = - event.detail;
535 |
536 | }
537 |
538 | if ( delta > 0 ) {
539 |
540 | dollyOut( getZoomScale() );
541 |
542 | } else if ( delta < 0 ) {
543 |
544 | dollyIn( getZoomScale() );
545 |
546 | }
547 |
548 | scope.update();
549 |
550 | }
551 |
552 | function handleKeyDown( event ) {
553 |
554 | //console.log( 'handleKeyDown' );
555 |
556 | switch ( event.keyCode ) {
557 |
558 | case scope.keys.UP:
559 | pan( 0, scope.keyPanSpeed );
560 | scope.update();
561 | break;
562 |
563 | case scope.keys.BOTTOM:
564 | pan( 0, - scope.keyPanSpeed );
565 | scope.update();
566 | break;
567 |
568 | case scope.keys.LEFT:
569 | pan( scope.keyPanSpeed, 0 );
570 | scope.update();
571 | break;
572 |
573 | case scope.keys.RIGHT:
574 | pan( - scope.keyPanSpeed, 0 );
575 | scope.update();
576 | break;
577 |
578 | }
579 |
580 | }
581 |
582 | function handleTouchStartRotate( event ) {
583 |
584 | //console.log( 'handleTouchStartRotate' );
585 |
586 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
587 |
588 | }
589 |
590 | function handleTouchStartDolly( event ) {
591 |
592 | //console.log( 'handleTouchStartDolly' );
593 |
594 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
595 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
596 |
597 | var distance = Math.sqrt( dx * dx + dy * dy );
598 |
599 | dollyStart.set( 0, distance );
600 |
601 | }
602 |
603 | function handleTouchStartPan( event ) {
604 |
605 | //console.log( 'handleTouchStartPan' );
606 |
607 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
608 |
609 | }
610 |
611 | function handleTouchMoveRotate( event ) {
612 |
613 | //console.log( 'handleTouchMoveRotate' );
614 |
615 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
616 | rotateDelta.subVectors( rotateEnd, rotateStart );
617 |
618 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
619 |
620 | // rotating across whole screen goes 360 degrees around
621 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
622 |
623 | // rotating up and down along whole screen attempts to go 360, but limited to 180
624 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
625 |
626 | rotateStart.copy( rotateEnd );
627 |
628 | scope.update();
629 |
630 | }
631 |
632 | function handleTouchMoveDolly( event ) {
633 |
634 | //console.log( 'handleTouchMoveDolly' );
635 |
636 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
637 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
638 |
639 | var distance = Math.sqrt( dx * dx + dy * dy );
640 |
641 | dollyEnd.set( 0, distance );
642 |
643 | dollyDelta.subVectors( dollyEnd, dollyStart );
644 |
645 | if ( dollyDelta.y > 0 ) {
646 |
647 | dollyOut( getZoomScale() );
648 |
649 | } else if ( dollyDelta.y < 0 ) {
650 |
651 | dollyIn( getZoomScale() );
652 |
653 | }
654 |
655 | dollyStart.copy( dollyEnd );
656 |
657 | scope.update();
658 |
659 | }
660 |
661 | function handleTouchMovePan( event ) {
662 |
663 | //console.log( 'handleTouchMovePan' );
664 |
665 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
666 |
667 | panDelta.subVectors( panEnd, panStart );
668 |
669 | pan( panDelta.x, panDelta.y );
670 |
671 | panStart.copy( panEnd );
672 |
673 | scope.update();
674 |
675 | }
676 |
677 | function handleTouchEnd( event ) {
678 |
679 | //console.log( 'handleTouchEnd' );
680 |
681 | }
682 |
683 | //
684 | // event handlers - FSM: listen for events and reset state
685 | //
686 |
687 | function onMouseDown( event ) {
688 |
689 | if ( scope.enabled === false ) return;
690 |
691 | event.preventDefault();
692 |
693 | if ( event.button === scope.mouseButtons.ORBIT ) {
694 |
695 | if ( scope.enableRotate === false ) return;
696 |
697 | handleMouseDownRotate( event );
698 |
699 | state = STATE.ROTATE;
700 |
701 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
702 |
703 | if ( scope.enableZoom === false ) return;
704 |
705 | handleMouseDownDolly( event );
706 |
707 | state = STATE.DOLLY;
708 |
709 | } else if ( event.button === scope.mouseButtons.PAN ) {
710 |
711 | if ( scope.enablePan === false ) return;
712 |
713 | handleMouseDownPan( event );
714 |
715 | state = STATE.PAN;
716 |
717 | }
718 |
719 | if ( state !== STATE.NONE ) {
720 |
721 | document.addEventListener( 'mousemove', onMouseMove, false );
722 | document.addEventListener( 'mouseup', onMouseUp, false );
723 | document.addEventListener( 'mouseout', onMouseUp, false );
724 |
725 | scope.dispatchEvent( startEvent );
726 |
727 | }
728 |
729 | }
730 |
731 | function onMouseMove( event ) {
732 |
733 | if ( scope.enabled === false ) return;
734 |
735 | event.preventDefault();
736 |
737 | if ( state === STATE.ROTATE ) {
738 |
739 | if ( scope.enableRotate === false ) return;
740 |
741 | handleMouseMoveRotate( event );
742 |
743 | } else if ( state === STATE.DOLLY ) {
744 |
745 | if ( scope.enableZoom === false ) return;
746 |
747 | handleMouseMoveDolly( event );
748 |
749 | } else if ( state === STATE.PAN ) {
750 |
751 | if ( scope.enablePan === false ) return;
752 |
753 | handleMouseMovePan( event );
754 |
755 | }
756 |
757 | }
758 |
759 | function onMouseUp( event ) {
760 |
761 | if ( scope.enabled === false ) return;
762 |
763 | handleMouseUp( event );
764 |
765 | document.removeEventListener( 'mousemove', onMouseMove, false );
766 | document.removeEventListener( 'mouseup', onMouseUp, false );
767 | document.removeEventListener( 'mouseout', onMouseUp, false );
768 |
769 | scope.dispatchEvent( endEvent );
770 |
771 | state = STATE.NONE;
772 |
773 | }
774 |
775 | function onMouseWheel( event ) {
776 |
777 | if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
778 |
779 | event.preventDefault();
780 | event.stopPropagation();
781 |
782 | handleMouseWheel( event );
783 |
784 | scope.dispatchEvent( startEvent ); // not sure why these are here...
785 | scope.dispatchEvent( endEvent );
786 |
787 | }
788 |
789 | function onKeyDown( event ) {
790 |
791 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
792 |
793 | handleKeyDown( event );
794 |
795 | }
796 |
797 | function onTouchStart( event ) {
798 |
799 | if ( scope.enabled === false ) return;
800 |
801 | switch ( event.touches.length ) {
802 |
803 | case 1: // one-fingered touch: rotate
804 |
805 | if ( scope.enableRotate === false ) return;
806 |
807 | handleTouchStartRotate( event );
808 |
809 | state = STATE.TOUCH_ROTATE;
810 |
811 | break;
812 |
813 | case 2: // two-fingered touch: dolly
814 |
815 | if ( scope.enableZoom === false ) return;
816 |
817 | handleTouchStartDolly( event );
818 |
819 | state = STATE.TOUCH_DOLLY;
820 |
821 | break;
822 |
823 | case 3: // three-fingered touch: pan
824 |
825 | if ( scope.enablePan === false ) return;
826 |
827 | handleTouchStartPan( event );
828 |
829 | state = STATE.TOUCH_PAN;
830 |
831 | break;
832 |
833 | default:
834 |
835 | state = STATE.NONE;
836 |
837 | }
838 |
839 | if ( state !== STATE.NONE ) {
840 |
841 | scope.dispatchEvent( startEvent );
842 |
843 | }
844 |
845 | }
846 |
847 | function onTouchMove( event ) {
848 |
849 | if ( scope.enabled === false ) return;
850 |
851 | event.preventDefault();
852 | event.stopPropagation();
853 |
854 | switch ( event.touches.length ) {
855 |
856 | case 1: // one-fingered touch: rotate
857 |
858 | if ( scope.enableRotate === false ) return;
859 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
860 |
861 | handleTouchMoveRotate( event );
862 |
863 | break;
864 |
865 | case 2: // two-fingered touch: dolly
866 |
867 | if ( scope.enableZoom === false ) return;
868 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
869 |
870 | handleTouchMoveDolly( event );
871 |
872 | break;
873 |
874 | case 3: // three-fingered touch: pan
875 |
876 | if ( scope.enablePan === false ) return;
877 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
878 |
879 | handleTouchMovePan( event );
880 |
881 | break;
882 |
883 | default:
884 |
885 | state = STATE.NONE;
886 |
887 | }
888 |
889 | }
890 |
891 | function onTouchEnd( event ) {
892 |
893 | if ( scope.enabled === false ) return;
894 |
895 | handleTouchEnd( event );
896 |
897 | scope.dispatchEvent( endEvent );
898 |
899 | state = STATE.NONE;
900 |
901 | }
902 |
903 | function onContextMenu( event ) {
904 |
905 | event.preventDefault();
906 |
907 | }
908 |
909 | //
910 |
911 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
912 |
913 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
914 | scope.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
915 | scope.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
916 |
917 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
918 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
919 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
920 |
921 | window.addEventListener( 'keydown', onKeyDown, false );
922 |
923 | // force an update at start
924 |
925 | this.update();
926 |
927 | };
928 |
929 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
930 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
931 |
932 | Object.defineProperties( THREE.OrbitControls.prototype, {
933 |
934 | center: {
935 |
936 | get: function () {
937 |
938 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
939 | return this.target;
940 |
941 | }
942 |
943 | },
944 |
945 | // backward compatibility
946 |
947 | noZoom: {
948 |
949 | get: function () {
950 |
951 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
952 | return ! this.enableZoom;
953 |
954 | },
955 |
956 | set: function ( value ) {
957 |
958 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
959 | this.enableZoom = ! value;
960 |
961 | }
962 |
963 | },
964 |
965 | noRotate: {
966 |
967 | get: function () {
968 |
969 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
970 | return ! this.enableRotate;
971 |
972 | },
973 |
974 | set: function ( value ) {
975 |
976 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
977 | this.enableRotate = ! value;
978 |
979 | }
980 |
981 | },
982 |
983 | noPan: {
984 |
985 | get: function () {
986 |
987 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
988 | return ! this.enablePan;
989 |
990 | },
991 |
992 | set: function ( value ) {
993 |
994 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
995 | this.enablePan = ! value;
996 |
997 | }
998 |
999 | },
1000 |
1001 | noKeys: {
1002 |
1003 | get: function () {
1004 |
1005 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1006 | return ! this.enableKeys;
1007 |
1008 | },
1009 |
1010 | set: function ( value ) {
1011 |
1012 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1013 | this.enableKeys = ! value;
1014 |
1015 | }
1016 |
1017 | },
1018 |
1019 | staticMoving : {
1020 |
1021 | get: function () {
1022 |
1023 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1024 | return ! this.constraint.enableDamping;
1025 |
1026 | },
1027 |
1028 | set: function ( value ) {
1029 |
1030 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1031 | this.constraint.enableDamping = ! value;
1032 |
1033 | }
1034 |
1035 | },
1036 |
1037 | dynamicDampingFactor : {
1038 |
1039 | get: function () {
1040 |
1041 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1042 | return this.constraint.dampingFactor;
1043 |
1044 | },
1045 |
1046 | set: function ( value ) {
1047 |
1048 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1049 | this.constraint.dampingFactor = value;
1050 |
1051 | }
1052 |
1053 | }
1054 |
1055 | } );
1056 |
--------------------------------------------------------------------------------
/examples/vendor/three.js/examples/js/controls/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 |
9 | // This set of controls performs orbiting, dollying (zooming), and panning.
10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
11 | //
12 | // Orbit - left mouse / touch: one finger move
13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
14 | // Pan - right mouse, or arrow keys / touch: three finter swipe
15 |
16 | THREE.OrbitControls = function ( object, domElement ) {
17 |
18 | this.object = object;
19 |
20 | this.domElement = ( domElement !== undefined ) ? domElement : document;
21 |
22 | // Set to false to disable this control
23 | this.enabled = true;
24 |
25 | // "target" sets the location of focus, where the object orbits around
26 | this.target = new THREE.Vector3();
27 |
28 | // How far you can dolly in and out ( PerspectiveCamera only )
29 | this.minDistance = 0;
30 | this.maxDistance = Infinity;
31 |
32 | // How far you can zoom in and out ( OrthographicCamera only )
33 | this.minZoom = 0;
34 | this.maxZoom = Infinity;
35 |
36 | // How far you can orbit vertically, upper and lower limits.
37 | // Range is 0 to Math.PI radians.
38 | this.minPolarAngle = 0; // radians
39 | this.maxPolarAngle = Math.PI; // radians
40 |
41 | // How far you can orbit horizontally, upper and lower limits.
42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
43 | this.minAzimuthAngle = - Infinity; // radians
44 | this.maxAzimuthAngle = Infinity; // radians
45 |
46 | // Set to true to enable damping (inertia)
47 | // If damping is enabled, you must call controls.update() in your animation loop
48 | this.enableDamping = false;
49 | this.dampingFactor = 0.25;
50 |
51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
52 | // Set to false to disable zooming
53 | this.enableZoom = true;
54 | this.zoomSpeed = 1.0;
55 |
56 | // Set to false to disable rotating
57 | this.enableRotate = true;
58 | this.rotateSpeed = 1.0;
59 |
60 | // Set to false to disable panning
61 | this.enablePan = true;
62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
63 |
64 | // Set to true to automatically rotate around the target
65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
66 | this.autoRotate = false;
67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
68 |
69 | // Set to false to disable use of the keys
70 | this.enableKeys = true;
71 |
72 | // The four arrow keys
73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
74 |
75 | // Mouse buttons
76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
77 |
78 | // for reset
79 | this.target0 = this.target.clone();
80 | this.position0 = this.object.position.clone();
81 | this.zoom0 = this.object.zoom;
82 |
83 | //
84 | // public methods
85 | //
86 |
87 | this.getPolarAngle = function () {
88 |
89 | return phi;
90 |
91 | };
92 |
93 | this.getAzimuthalAngle = function () {
94 |
95 | return theta;
96 |
97 | };
98 |
99 | this.reset = function () {
100 |
101 | scope.target.copy( scope.target0 );
102 | scope.object.position.copy( scope.position0 );
103 | scope.object.zoom = scope.zoom0;
104 |
105 | scope.object.updateProjectionMatrix();
106 | scope.dispatchEvent( changeEvent );
107 |
108 | scope.update();
109 |
110 | state = STATE.NONE;
111 |
112 | };
113 |
114 | // this method is exposed, but perhaps it would be better if we can make it private...
115 | this.update = function() {
116 |
117 | var offset = new THREE.Vector3();
118 |
119 | // so camera.up is the orbit axis
120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
121 | var quatInverse = quat.clone().inverse();
122 |
123 | var lastPosition = new THREE.Vector3();
124 | var lastQuaternion = new THREE.Quaternion();
125 |
126 | return function () {
127 |
128 | var position = scope.object.position;
129 |
130 | offset.copy( position ).sub( scope.target );
131 |
132 | // rotate offset to "y-axis-is-up" space
133 | offset.applyQuaternion( quat );
134 |
135 | // angle from z-axis around y-axis
136 |
137 | theta = Math.atan2( offset.x, offset.z );
138 |
139 | // angle from y-axis
140 |
141 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
142 |
143 | if ( scope.autoRotate && state === STATE.NONE ) {
144 |
145 | rotateLeft( getAutoRotationAngle() );
146 |
147 | }
148 |
149 | theta += thetaDelta;
150 | phi += phiDelta;
151 |
152 | // restrict theta to be between desired limits
153 | theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, theta ) );
154 |
155 | // restrict phi to be between desired limits
156 | phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, phi ) );
157 |
158 | // restrict phi to be betwee EPS and PI-EPS
159 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
160 |
161 | var radius = offset.length() * scale;
162 |
163 | // restrict radius to be between desired limits
164 | radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, radius ) );
165 |
166 | // move target to panned location
167 | scope.target.add( panOffset );
168 |
169 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
170 | offset.y = radius * Math.cos( phi );
171 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
172 |
173 | // rotate offset back to "camera-up-vector-is-up" space
174 | offset.applyQuaternion( quatInverse );
175 |
176 | position.copy( scope.target ).add( offset );
177 |
178 | scope.object.lookAt( scope.target );
179 |
180 | if ( scope.enableDamping === true ) {
181 |
182 | thetaDelta *= ( 1 - scope.dampingFactor );
183 | phiDelta *= ( 1 - scope.dampingFactor );
184 |
185 | } else {
186 |
187 | thetaDelta = 0;
188 | phiDelta = 0;
189 |
190 | }
191 |
192 | scale = 1;
193 | panOffset.set( 0, 0, 0 );
194 |
195 | // update condition is:
196 | // min(camera displacement, camera rotation in radians)^2 > EPS
197 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
198 |
199 | if ( zoomChanged ||
200 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
201 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
202 |
203 | scope.dispatchEvent( changeEvent );
204 |
205 | lastPosition.copy( scope.object.position );
206 | lastQuaternion.copy( scope.object.quaternion );
207 | zoomChanged = false;
208 |
209 | return true;
210 |
211 | }
212 |
213 | return false;
214 |
215 | };
216 |
217 | }();
218 |
219 | this.dispose = function() {
220 |
221 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
222 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
223 | scope.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
224 | scope.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
225 |
226 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
227 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
228 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
229 |
230 | document.removeEventListener( 'mousemove', onMouseMove, false );
231 | document.removeEventListener( 'mouseup', onMouseUp, false );
232 | document.removeEventListener( 'mouseout', onMouseUp, false );
233 |
234 | window.removeEventListener( 'keydown', onKeyDown, false );
235 |
236 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
237 |
238 | };
239 |
240 | //
241 | // internals
242 | //
243 |
244 | var scope = this;
245 |
246 | var changeEvent = { type: 'change' };
247 | var startEvent = { type: 'start' };
248 | var endEvent = { type: 'end' };
249 |
250 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
251 |
252 | var state = STATE.NONE;
253 |
254 | var EPS = 0.000001;
255 |
256 | // current position in spherical coordinates
257 | var theta;
258 | var phi;
259 |
260 | var phiDelta = 0;
261 | var thetaDelta = 0;
262 | var scale = 1;
263 | var panOffset = new THREE.Vector3();
264 | var zoomChanged = false;
265 |
266 | var rotateStart = new THREE.Vector2();
267 | var rotateEnd = new THREE.Vector2();
268 | var rotateDelta = new THREE.Vector2();
269 |
270 | var panStart = new THREE.Vector2();
271 | var panEnd = new THREE.Vector2();
272 | var panDelta = new THREE.Vector2();
273 |
274 | var dollyStart = new THREE.Vector2();
275 | var dollyEnd = new THREE.Vector2();
276 | var dollyDelta = new THREE.Vector2();
277 |
278 | function getAutoRotationAngle() {
279 |
280 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
281 |
282 | }
283 |
284 | function getZoomScale() {
285 |
286 | return Math.pow( 0.95, scope.zoomSpeed );
287 |
288 | }
289 |
290 | function rotateLeft( angle ) {
291 |
292 | thetaDelta -= angle;
293 |
294 | }
295 |
296 | function rotateUp( angle ) {
297 |
298 | phiDelta -= angle;
299 |
300 | }
301 |
302 | var panLeft = function() {
303 |
304 | var v = new THREE.Vector3();
305 |
306 | return function panLeft( distance, objectMatrix ) {
307 |
308 | var te = objectMatrix.elements;
309 |
310 | // get X column of objectMatrix
311 | v.set( te[ 0 ], te[ 1 ], te[ 2 ] );
312 |
313 | v.multiplyScalar( - distance );
314 |
315 | panOffset.add( v );
316 |
317 | };
318 |
319 | }();
320 |
321 | var panUp = function() {
322 |
323 | var v = new THREE.Vector3();
324 |
325 | return function panUp( distance, objectMatrix ) {
326 |
327 | var te = objectMatrix.elements;
328 |
329 | // get Y column of objectMatrix
330 | v.set( te[ 4 ], te[ 5 ], te[ 6 ] );
331 |
332 | v.multiplyScalar( distance );
333 |
334 | panOffset.add( v );
335 |
336 | };
337 |
338 | }();
339 |
340 | // deltaX and deltaY are in pixels; right and down are positive
341 | var pan = function() {
342 |
343 | var offset = new THREE.Vector3();
344 |
345 | return function( deltaX, deltaY ) {
346 |
347 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
348 |
349 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
350 |
351 | // perspective
352 | var position = scope.object.position;
353 | offset.copy( position ).sub( scope.target );
354 | var targetDistance = offset.length();
355 |
356 | // half of the fov is center to top of screen
357 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
358 |
359 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
360 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
361 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
362 |
363 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
364 |
365 | // orthographic
366 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / element.clientWidth, scope.object.matrix );
367 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / element.clientHeight, scope.object.matrix );
368 |
369 | } else {
370 |
371 | // camera neither orthographic nor perspective
372 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
373 | scope.enablePan = false;
374 |
375 | }
376 |
377 | };
378 |
379 | }();
380 |
381 | function dollyIn( dollyScale ) {
382 |
383 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
384 |
385 | scale /= dollyScale;
386 |
387 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
388 |
389 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
390 | scope.object.updateProjectionMatrix();
391 | zoomChanged = true;
392 |
393 | } else {
394 |
395 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
396 | scope.enableZoom = false;
397 |
398 | }
399 |
400 | }
401 |
402 | function dollyOut( dollyScale ) {
403 |
404 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
405 |
406 | scale *= dollyScale;
407 |
408 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
409 |
410 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
411 | scope.object.updateProjectionMatrix();
412 | zoomChanged = true;
413 |
414 | } else {
415 |
416 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
417 | scope.enableZoom = false;
418 |
419 | }
420 |
421 | }
422 |
423 | //
424 | // event callbacks - update the object state
425 | //
426 |
427 | function handleMouseDownRotate( event ) {
428 |
429 | //console.log( 'handleMouseDownRotate' );
430 |
431 | rotateStart.set( event.clientX, event.clientY );
432 |
433 | }
434 |
435 | function handleMouseDownDolly( event ) {
436 |
437 | //console.log( 'handleMouseDownDolly' );
438 |
439 | dollyStart.set( event.clientX, event.clientY );
440 |
441 | }
442 |
443 | function handleMouseDownPan( event ) {
444 |
445 | //console.log( 'handleMouseDownPan' );
446 |
447 | panStart.set( event.clientX, event.clientY );
448 |
449 | }
450 |
451 | function handleMouseMoveRotate( event ) {
452 |
453 | //console.log( 'handleMouseMoveRotate' );
454 |
455 | rotateEnd.set( event.clientX, event.clientY );
456 | rotateDelta.subVectors( rotateEnd, rotateStart );
457 |
458 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
459 |
460 | // rotating across whole screen goes 360 degrees around
461 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
462 |
463 | // rotating up and down along whole screen attempts to go 360, but limited to 180
464 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
465 |
466 | rotateStart.copy( rotateEnd );
467 |
468 | scope.update();
469 |
470 | }
471 |
472 | function handleMouseMoveDolly( event ) {
473 |
474 | //console.log( 'handleMouseMoveDolly' );
475 |
476 | dollyEnd.set( event.clientX, event.clientY );
477 |
478 | dollyDelta.subVectors( dollyEnd, dollyStart );
479 |
480 | if ( dollyDelta.y > 0 ) {
481 |
482 | dollyIn( getZoomScale() );
483 |
484 | } else if ( dollyDelta.y < 0 ) {
485 |
486 | dollyOut( getZoomScale() );
487 |
488 | }
489 |
490 | dollyStart.copy( dollyEnd );
491 |
492 | scope.update();
493 |
494 | }
495 |
496 | function handleMouseMovePan( event ) {
497 |
498 | //console.log( 'handleMouseMovePan' );
499 |
500 | panEnd.set( event.clientX, event.clientY );
501 |
502 | panDelta.subVectors( panEnd, panStart );
503 |
504 | pan( panDelta.x, panDelta.y );
505 |
506 | panStart.copy( panEnd );
507 |
508 | scope.update();
509 |
510 | }
511 |
512 | function handleMouseUp( event ) {
513 |
514 | //console.log( 'handleMouseUp' );
515 |
516 | }
517 |
518 | function handleMouseWheel( event ) {
519 |
520 | //console.log( 'handleMouseWheel' );
521 |
522 | var delta = 0;
523 |
524 | if ( event.wheelDelta !== undefined ) {
525 |
526 | // WebKit / Opera / Explorer 9
527 |
528 | delta = event.wheelDelta;
529 |
530 | } else if ( event.detail !== undefined ) {
531 |
532 | // Firefox
533 |
534 | delta = - event.detail;
535 |
536 | }
537 |
538 | if ( delta > 0 ) {
539 |
540 | dollyOut( getZoomScale() );
541 |
542 | } else if ( delta < 0 ) {
543 |
544 | dollyIn( getZoomScale() );
545 |
546 | }
547 |
548 | scope.update();
549 |
550 | }
551 |
552 | function handleKeyDown( event ) {
553 |
554 | //console.log( 'handleKeyDown' );
555 |
556 | switch ( event.keyCode ) {
557 |
558 | case scope.keys.UP:
559 | pan( 0, scope.keyPanSpeed );
560 | scope.update();
561 | break;
562 |
563 | case scope.keys.BOTTOM:
564 | pan( 0, - scope.keyPanSpeed );
565 | scope.update();
566 | break;
567 |
568 | case scope.keys.LEFT:
569 | pan( scope.keyPanSpeed, 0 );
570 | scope.update();
571 | break;
572 |
573 | case scope.keys.RIGHT:
574 | pan( - scope.keyPanSpeed, 0 );
575 | scope.update();
576 | break;
577 |
578 | }
579 |
580 | }
581 |
582 | function handleTouchStartRotate( event ) {
583 |
584 | //console.log( 'handleTouchStartRotate' );
585 |
586 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
587 |
588 | }
589 |
590 | function handleTouchStartDolly( event ) {
591 |
592 | //console.log( 'handleTouchStartDolly' );
593 |
594 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
595 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
596 |
597 | var distance = Math.sqrt( dx * dx + dy * dy );
598 |
599 | dollyStart.set( 0, distance );
600 |
601 | }
602 |
603 | function handleTouchStartPan( event ) {
604 |
605 | //console.log( 'handleTouchStartPan' );
606 |
607 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
608 |
609 | }
610 |
611 | function handleTouchMoveRotate( event ) {
612 |
613 | //console.log( 'handleTouchMoveRotate' );
614 |
615 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
616 | rotateDelta.subVectors( rotateEnd, rotateStart );
617 |
618 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
619 |
620 | // rotating across whole screen goes 360 degrees around
621 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
622 |
623 | // rotating up and down along whole screen attempts to go 360, but limited to 180
624 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
625 |
626 | rotateStart.copy( rotateEnd );
627 |
628 | scope.update();
629 |
630 | }
631 |
632 | function handleTouchMoveDolly( event ) {
633 |
634 | //console.log( 'handleTouchMoveDolly' );
635 |
636 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
637 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
638 |
639 | var distance = Math.sqrt( dx * dx + dy * dy );
640 |
641 | dollyEnd.set( 0, distance );
642 |
643 | dollyDelta.subVectors( dollyEnd, dollyStart );
644 |
645 | if ( dollyDelta.y > 0 ) {
646 |
647 | dollyOut( getZoomScale() );
648 |
649 | } else if ( dollyDelta.y < 0 ) {
650 |
651 | dollyIn( getZoomScale() );
652 |
653 | }
654 |
655 | dollyStart.copy( dollyEnd );
656 |
657 | scope.update();
658 |
659 | }
660 |
661 | function handleTouchMovePan( event ) {
662 |
663 | //console.log( 'handleTouchMovePan' );
664 |
665 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
666 |
667 | panDelta.subVectors( panEnd, panStart );
668 |
669 | pan( panDelta.x, panDelta.y );
670 |
671 | panStart.copy( panEnd );
672 |
673 | scope.update();
674 |
675 | }
676 |
677 | function handleTouchEnd( event ) {
678 |
679 | //console.log( 'handleTouchEnd' );
680 |
681 | }
682 |
683 | //
684 | // event handlers - FSM: listen for events and reset state
685 | //
686 |
687 | function onMouseDown( event ) {
688 |
689 | if ( scope.enabled === false ) return;
690 |
691 | event.preventDefault();
692 |
693 | if ( event.button === scope.mouseButtons.ORBIT ) {
694 |
695 | if ( scope.enableRotate === false ) return;
696 |
697 | handleMouseDownRotate( event );
698 |
699 | state = STATE.ROTATE;
700 |
701 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
702 |
703 | if ( scope.enableZoom === false ) return;
704 |
705 | handleMouseDownDolly( event );
706 |
707 | state = STATE.DOLLY;
708 |
709 | } else if ( event.button === scope.mouseButtons.PAN ) {
710 |
711 | if ( scope.enablePan === false ) return;
712 |
713 | handleMouseDownPan( event );
714 |
715 | state = STATE.PAN;
716 |
717 | }
718 |
719 | if ( state !== STATE.NONE ) {
720 |
721 | document.addEventListener( 'mousemove', onMouseMove, false );
722 | document.addEventListener( 'mouseup', onMouseUp, false );
723 | document.addEventListener( 'mouseout', onMouseUp, false );
724 |
725 | scope.dispatchEvent( startEvent );
726 |
727 | }
728 |
729 | }
730 |
731 | function onMouseMove( event ) {
732 |
733 | if ( scope.enabled === false ) return;
734 |
735 | event.preventDefault();
736 |
737 | if ( state === STATE.ROTATE ) {
738 |
739 | if ( scope.enableRotate === false ) return;
740 |
741 | handleMouseMoveRotate( event );
742 |
743 | } else if ( state === STATE.DOLLY ) {
744 |
745 | if ( scope.enableZoom === false ) return;
746 |
747 | handleMouseMoveDolly( event );
748 |
749 | } else if ( state === STATE.PAN ) {
750 |
751 | if ( scope.enablePan === false ) return;
752 |
753 | handleMouseMovePan( event );
754 |
755 | }
756 |
757 | }
758 |
759 | function onMouseUp( event ) {
760 |
761 | if ( scope.enabled === false ) return;
762 |
763 | handleMouseUp( event );
764 |
765 | document.removeEventListener( 'mousemove', onMouseMove, false );
766 | document.removeEventListener( 'mouseup', onMouseUp, false );
767 | document.removeEventListener( 'mouseout', onMouseUp, false );
768 |
769 | scope.dispatchEvent( endEvent );
770 |
771 | state = STATE.NONE;
772 |
773 | }
774 |
775 | function onMouseWheel( event ) {
776 |
777 | if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
778 |
779 | event.preventDefault();
780 | event.stopPropagation();
781 |
782 | handleMouseWheel( event );
783 |
784 | scope.dispatchEvent( startEvent ); // not sure why these are here...
785 | scope.dispatchEvent( endEvent );
786 |
787 | }
788 |
789 | function onKeyDown( event ) {
790 |
791 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
792 |
793 | handleKeyDown( event );
794 |
795 | }
796 |
797 | function onTouchStart( event ) {
798 |
799 | if ( scope.enabled === false ) return;
800 |
801 | switch ( event.touches.length ) {
802 |
803 | case 1: // one-fingered touch: rotate
804 |
805 | if ( scope.enableRotate === false ) return;
806 |
807 | handleTouchStartRotate( event );
808 |
809 | state = STATE.TOUCH_ROTATE;
810 |
811 | break;
812 |
813 | case 2: // two-fingered touch: dolly
814 |
815 | if ( scope.enableZoom === false ) return;
816 |
817 | handleTouchStartDolly( event );
818 |
819 | state = STATE.TOUCH_DOLLY;
820 |
821 | break;
822 |
823 | case 3: // three-fingered touch: pan
824 |
825 | if ( scope.enablePan === false ) return;
826 |
827 | handleTouchStartPan( event );
828 |
829 | state = STATE.TOUCH_PAN;
830 |
831 | break;
832 |
833 | default:
834 |
835 | state = STATE.NONE;
836 |
837 | }
838 |
839 | if ( state !== STATE.NONE ) {
840 |
841 | scope.dispatchEvent( startEvent );
842 |
843 | }
844 |
845 | }
846 |
847 | function onTouchMove( event ) {
848 |
849 | if ( scope.enabled === false ) return;
850 |
851 | event.preventDefault();
852 | event.stopPropagation();
853 |
854 | switch ( event.touches.length ) {
855 |
856 | case 1: // one-fingered touch: rotate
857 |
858 | if ( scope.enableRotate === false ) return;
859 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
860 |
861 | handleTouchMoveRotate( event );
862 |
863 | break;
864 |
865 | case 2: // two-fingered touch: dolly
866 |
867 | if ( scope.enableZoom === false ) return;
868 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
869 |
870 | handleTouchMoveDolly( event );
871 |
872 | break;
873 |
874 | case 3: // three-fingered touch: pan
875 |
876 | if ( scope.enablePan === false ) return;
877 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
878 |
879 | handleTouchMovePan( event );
880 |
881 | break;
882 |
883 | default:
884 |
885 | state = STATE.NONE;
886 |
887 | }
888 |
889 | }
890 |
891 | function onTouchEnd( event ) {
892 |
893 | if ( scope.enabled === false ) return;
894 |
895 | handleTouchEnd( event );
896 |
897 | scope.dispatchEvent( endEvent );
898 |
899 | state = STATE.NONE;
900 |
901 | }
902 |
903 | function onContextMenu( event ) {
904 |
905 | event.preventDefault();
906 |
907 | }
908 |
909 | //
910 |
911 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
912 |
913 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
914 | scope.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
915 | scope.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
916 |
917 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
918 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
919 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
920 |
921 | window.addEventListener( 'keydown', onKeyDown, false );
922 |
923 | // force an update at start
924 |
925 | this.update();
926 |
927 | };
928 |
929 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
930 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
931 |
932 | Object.defineProperties( THREE.OrbitControls.prototype, {
933 |
934 | center: {
935 |
936 | get: function () {
937 |
938 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
939 | return this.target;
940 |
941 | }
942 |
943 | },
944 |
945 | // backward compatibility
946 |
947 | noZoom: {
948 |
949 | get: function () {
950 |
951 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
952 | return ! this.enableZoom;
953 |
954 | },
955 |
956 | set: function ( value ) {
957 |
958 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
959 | this.enableZoom = ! value;
960 |
961 | }
962 |
963 | },
964 |
965 | noRotate: {
966 |
967 | get: function () {
968 |
969 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
970 | return ! this.enableRotate;
971 |
972 | },
973 |
974 | set: function ( value ) {
975 |
976 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
977 | this.enableRotate = ! value;
978 |
979 | }
980 |
981 | },
982 |
983 | noPan: {
984 |
985 | get: function () {
986 |
987 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
988 | return ! this.enablePan;
989 |
990 | },
991 |
992 | set: function ( value ) {
993 |
994 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
995 | this.enablePan = ! value;
996 |
997 | }
998 |
999 | },
1000 |
1001 | noKeys: {
1002 |
1003 | get: function () {
1004 |
1005 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1006 | return ! this.enableKeys;
1007 |
1008 | },
1009 |
1010 | set: function ( value ) {
1011 |
1012 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1013 | this.enableKeys = ! value;
1014 |
1015 | }
1016 |
1017 | },
1018 |
1019 | staticMoving : {
1020 |
1021 | get: function () {
1022 |
1023 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1024 | return ! this.constraint.enableDamping;
1025 |
1026 | },
1027 |
1028 | set: function ( value ) {
1029 |
1030 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1031 | this.constraint.enableDamping = ! value;
1032 |
1033 | }
1034 |
1035 | },
1036 |
1037 | dynamicDampingFactor : {
1038 |
1039 | get: function () {
1040 |
1041 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1042 | return this.constraint.dampingFactor;
1043 |
1044 | },
1045 |
1046 | set: function ( value ) {
1047 |
1048 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1049 | this.constraint.dampingFactor = value;
1050 |
1051 | }
1052 |
1053 | }
1054 |
1055 | } );
1056 |
--------------------------------------------------------------------------------
/examples/vendor/three.js/examples/js/libs/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<