planets.js Example File | Qt Canvas 3D (deprecated) 5.12.11

- Qt 5.12
- Qt Canvas 3D (deprecated)
- Planets Example
- planets.js Example File
- Qt 5.12.11 Reference Documentation

# planets.js Example File

threejs/planets/planets.js

     /****************************************************************************
     **
     ** Copyright (C) 2016 The Qt Company Ltd.
     ** Contact: https://www.qt.io/licensing/
     **
     ** This file is part of the QtCanvas3D module of the Qt Toolkit.
     **
     ** $QT_BEGIN_LICENSE:BSD$
     ** Commercial License Usage
     ** Licensees holding valid commercial Qt licenses may use this file in
     ** accordance with the commercial license agreement provided with the
     ** Software or, alternatively, in accordance with the terms contained in
     ** a written agreement between you and The Qt Company. For licensing terms
     ** and conditions see https://www.qt.io/terms-conditions. For further
     ** information use the contact form at https://www.qt.io/contact-us.
     **
     ** BSD License Usage
     ** Alternatively, you may use this file under the terms of the BSD license
     ** as follows:
     **
     ** "Redistribution and use in source and binary forms, with or without
     ** modification, are permitted provided that the following conditions are
     ** met:
     **   * Redistributions of source code must retain the above copyright
     **     notice, this list of conditions and the following disclaimer.
     **   * Redistributions in binary form must reproduce the above copyright
     **     notice, this list of conditions and the following disclaimer in
     **     the documentation and/or other materials provided with the
     **     distribution.
     **   * Neither the name of The Qt Company Ltd nor the names of its
     **     contributors may be used to endorse or promote products derived
     **     from this software without specific prior written permission.
     **
     **
     ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
     **
     ** $QT_END_LICENSE$
     **
     ****************************************************************************/

     Qt.include("three.js")
     Qt.include("threex.planets.js")

     var SUN = 0;
     var MERCURY = 1;
     var VENUS = 2;
     var EARTH = 3;
     var MARS = 4;
     var JUPITER = 5;
     var SATURN = 6;
     var URANUS = 7;
     var NEPTUNE = 8;
     var NUM_SELECTABLE_PLANETS = 9;
     var MOON = 9;
     var SOLAR_SYSTEM = 100;

     var camera, scene, renderer;
     var planetCanvas, mouse, raycaster;

     var daysPerFrame;
     var daysPerFrameScale;
     var planetScale;
     var cameraDistance;

     var objects = []; // Planet objects
     var hitObjects = []; // Planet hit detection objects
     var planets = []; // Planet data info

     var commonGeometry;
     var hitGeometry;
     var solarDistance = 2600000;
     var saturnOuterRadius = 120.700;
     var uranusOuterRadius = 40;

     var qmlView;

     var oldFocusedPlanetPosition;
     var oldCameraPosition;
     var defaultCameraPosition;

     var y = 2000;
     var m = 1;
     var D = 1;
     // Time scale formula based on http://www.stjarnhimlen.se/comp/ppcomp.html
     var startD = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9 + D - 730530;
     var oldTimeD = startD;
     var currTimeD = startD;

     var auScale = 149597.870700; // AU in thousands of kilometers

     var focusedScaling = false;
     var focusedMinimumScale = 20;
     var actualScale;

     function initializeGL(canvas, eventSource, mainView) {

         planetCanvas = canvas;
         qmlView = mainView;

         camera = new THREE.PerspectiveCamera(45, canvas.width / canvas.height, 2500000, 20000000);
         defaultCameraPosition = new THREE.Vector3(solarDistance, solarDistance, solarDistance);
         camera.position.set(defaultCameraPosition.x, defaultCameraPosition.y, defaultCameraPosition.z);

         scene = new THREE.Scene();

         var starSphere = THREEx.Planets.createStarfield(8500000);
         scene.add(starSphere);

         var light = new THREE.PointLight(0x777777, 2);
         light.position.set(0, 0, 0);
         scene.add(light);

         scene.add(new THREE.AmbientLight(0x111111));

         loadPlanetData();
         createPlanets();
         setScale(1200);

         camera.lookAt(objects[0].position); // look at the Sun

         raycaster = new THREE.Raycaster();
         mouse = new THREE.Vector2();

         renderer = new THREE.Canvas3DRenderer(
                     { canvas: canvas, antialias: true, devicePixelRatio: canvas.devicePixelRatio });

         renderer.setPixelRatio(canvas.devicePixelRatio);
         renderer.setSize(canvas.width, canvas.height);
         eventSource.mouseDown.connect(onDocumentMouseDown);
     }

     function loadPlanetData() {

         // Planet Data
         // radius - planet radius in millions of meters
         // tilt - planet axis angle
         // N1 N2 - longitude of the ascending node
         // i1 i2 - inclination to the ecliptic (plane of the Earth's orbit)
         // w1 w2 - argument of perihelion
         // a1 a2 - semi-major axis, or mean distance from Sun
         // e1 e2 - eccentricity (0=circle, 0-1=ellipse, 1=parabola)
         // M1 M2 - mean anomaly (0 at perihelion; increases uniformly with time)
         // period - sidereal rotation period
         // centerOfOrbit - the planet in the center of the orbit
         // (orbital elements based on http://www.stjarnhimlen.se/comp/ppcomp.html)

         var sun = { radius: 694.439, tilt: 63.87, period: 25.05 };
         planets.push(sun);
         var mercury = {
             radius: 2.433722, tilt: 0.04, N1: 48.3313, N2: 0.0000324587,
             i1: 7.0047, i2: 0.0000000500, w1: 29.1241, w2: 0.0000101444,
             a1: 0.387098, a2: 0, e1: 0.205635, e2: 0.000000000559,
             M1: 168.6562, M2: 4.0923344368, period: 58.646,
             centerOfOrbit: SUN
         };
         planets.push(mercury);
         var venus = {
             radius: 6.046079, tilt: 177.36, N1: 76.6799, N2: 0.0000246590,
             i1: 3.3946, i2: 0.0000000275, w1: 54.8910, w2: 0.0000138374,
             a1: 0.723330, a2: 0, e1: 0.006773, e2: -0.000000001302,
             M1: 48.0052, M2: 1.6021302244, period: 243.0185,
             centerOfOrbit: SUN
         };
         planets.push(venus);
         var earth = {
             radius: 6.371, tilt: 25.44, N1: 174.873, N2: 0,
             i1: 0.00005, i2: 0, w1: 102.94719, w2: 0,
             a1: 1, a2: 0, e1: 0.01671022, e2: 0,
             M1: 357.529, M2: 0.985608, period: 0.997,
             centerOfOrbit: SUN
         };
         planets.push(earth);
         var mars = {
             radius: 3.389372, tilt: 25.19, N1: 49.5574, N2: 0.0000211081,
             i1: 1.8497, i2: -0.0000000178, w1: 286.5016, w2: 0.0000292961,
             a1: 1.523688, a2: 0, e1: 0.093405, e2: 0.000000002516,
             M1: 18.6021, M2: 0.5240207766, period: 1.025957,
             centerOfOrbit: SUN
         };
         planets.push(mars);
         var jupiter = {
             radius: 71.41254, tilt: 3.13, N1: 100.4542, N2: 0.0000276854,
             i1: 1.3030, i2: -0.0000001557, w1: 273.8777, w2: 0.0000164505,
             a1: 5.20256, a2: 0, e1: 0.048498, e2: 0.000000004469,
             M1: 19.8950, M2: 0.0830853001, period: 0.4135,
             centerOfOrbit: SUN
         };
         planets.push(jupiter);
         var saturn = {
             radius: 60.19958, tilt: 26.73, N1: 113.6634, N2: 0.0000238980,
             i1: 2.4886, i2: -0.0000001081, w1: 339.3939, w2: 0.0000297661,
             a1: 9.55475, a2: 0, e1: 0.055546, e2: -0.000000009499,
             M1: 316.9670, M2: 0.0334442282, period: 0.4395,
             centerOfOrbit: SUN
         };
         planets.push(saturn);
         var uranus = {
             radius: 25.5286, tilt: 97.77, N1: 74.0005, N2: 0.000013978,
             i1: 0.7733, i2: 0.000000019, w1: 96.6612, w2: 0.000030565,
             a1: 19.18171, a2: -0.0000000155, e1: 0.047318, e2: 0.00000000745,
             M1: 142.5905, M2: 0.011725806, period: 0.71833,
             centerOfOrbit: SUN
         };
         planets.push(uranus);
         var neptune = {
             radius: 24.73859, tilt: 28.32, N1: 131.7806, N2: 0.000030173,
             i1: 1.7700, i2: -0.000000255, w1: 272.8461, w2: 0.000006027,
             a1: 30.05826, a2: 0.00000003313, e1: 0.008606, e2: 0.00000000215,
             M1: 260.2471, M2: 0.005995147, period: 0.6713,
             centerOfOrbit: SUN
         };
         planets.push(neptune);
         var moon = {
             radius: 1.5424, tilt: 28.32, N1: 125.1228, N2: -0.0529538083,
             i1: 5.1454, i2: 0, w1: 318.0634, w2: 0.1643573223,
             a1: 0.273, a2: 0, e1: 0.054900, e2: 0,
             M1: 115.3654, M2: 13.0649929509, period: 27.321582,
             centerOfOrbit: EARTH
         };
         planets.push(moon);

     }

     function createPlanets() {

         objects = [];

         commonGeometry = new THREE.BufferGeometry().fromGeometry(new THREE.SphereGeometry(1, 64, 64));
         hitGeometry = new THREE.BufferGeometry().fromGeometry(new THREE.SphereGeometry(1, 8, 8));

         var ringSegments = 70;
         var mesh, innerRadius, outerRadius, ring;

         for (var i = 0; i < planets.length; i ++) {
             switch (i) {
             case SUN:
                 mesh = createSun(planets[i]["radius"]);
                 mesh.position.set(0, 0, 0);
                 break;
             case MERCURY:
                 mesh = createPlanet(planets[i]["radius"], 0.005, 'images/mercurymap.jpg',
                                     'images/mercurybump.jpg');
                 break;
             case VENUS:
                 mesh = createPlanet(planets[i]["radius"], 0.005, 'images/venusmap.jpg',
                                     'images/venusbump.jpg');
                 break;
             case EARTH:
                 mesh = createPlanet(planets[i]["radius"], 0.05, 'images/earthmap1k.jpg',
                                     'images/earthbump1k.jpg', 'images/earthspec1k.jpg');
                 createEarthCloud(mesh);
                 break;
             case MARS:
                 mesh = createPlanet(planets[i]["radius"], 0.05, 'images/marsmap1k.jpg',
                                     'images/marsbump1k.jpg');
                 break;
             case JUPITER:
                 mesh = createPlanet(planets[i]["radius"], 0.02, 'images/jupitermap.jpg',
                                     'images/jupitermap.jpg');
                 break;
             case SATURN:
                 mesh = createPlanet(planets[i]["radius"], 0.05, 'images/saturnmap.jpg',
                                     'images/saturnmap.jpg');
                 innerRadius = (planets[i]["radius"] + 6.630) / planets[i]["radius"];
                 outerRadius = (planets[i]["radius"] + saturnOuterRadius) / planets[i]["radius"];
                 ring = createRing(innerRadius, outerRadius, ringSegments,
                                       'qrc:images/saturnringcolortrans.png');
                 ring.receiveShadow = true;
                 ring.castShadow = true;
                 mesh.add(ring);
                 break;
             case URANUS:
                 mesh = createPlanet(planets[i]["radius"], 0.05, 'images/uranusmap.jpg',
                                     'images/uranusmap.jpg');
                 innerRadius = (planets[i]["radius"] + 2) / planets[i]["radius"];
                 outerRadius = (planets[i]["radius"] + uranusOuterRadius) / planets[i]["radius"];
                 ring = createRing(innerRadius, outerRadius, ringSegments,
                                       'qrc:images/uranusringcolortrans.png');
                 ring.receiveShadow = true;
                 ring.castShadow = true;
                 mesh.add(ring);
                 break;
             case NEPTUNE:
                 mesh = createPlanet(planets[i]["radius"], 0.05, 'images/neptunemap.jpg',
                                     'images/neptunemap.jpg');
                 break;
             case MOON:
                 mesh = createPlanet(planets[i]["radius"], 0.05, 'images/moonmap1k.jpg',
                                     'images/moonbump1k.jpg');
                 break;
             }

             objects.push(mesh);
             scene.add(mesh);

             // Create separate meshes for click detection
             var hitMesh = new THREE.Mesh(hitGeometry);
             hitMesh.visible = false;
             hitObjects.push(hitMesh);
             scene.add(hitMesh);
         }

     }

     function createSun(radius) {

         var textureLoader = new THREE.TextureLoader();
         var texture = textureLoader.load('images/sunmap.jpg');
         var material = new THREE.MeshBasicMaterial({ map: texture });
         var mesh = new THREE.Mesh(commonGeometry, material);
         mesh.scale.set(radius, radius, radius);

         mesh.receiveShadow = false;
         mesh.castShadow = false;

         return mesh;
     }

     function createPlanet(radius, bumpMapScale, mapTexture, bumpTexture, specularTexture) {

         var textureLoader = new THREE.TextureLoader();
         var material = new THREE.MeshPhongMaterial({
                                                        map: textureLoader.load(mapTexture),
                                                        bumpMap: textureLoader.load(bumpTexture),
                                                        bumpScale: bumpMapScale
                                                    });

         if (specularTexture) {
             material.specularMap = textureLoader.load(specularTexture);
             material.specular = new THREE.Color('grey');
             material.shininess = 50.0;
         } else {
             material.shininess = 1.0;
         }

         var mesh = new THREE.Mesh(commonGeometry, material);
         mesh.scale.set(radius, radius, radius);

         return mesh;

     }

     function createEarthCloud(earthMesh) {

         var textureLoader = new THREE.TextureLoader();
         var material = new THREE.MeshPhongMaterial({
                                                        map: textureLoader.load('qrc:images/earthcloudmapcolortrans.png'),
                                                        side: THREE.BackSide,
                                                        transparent: true,
                                                        opacity: 0.8
                                                    });
         var mesh = new THREE.Mesh(commonGeometry, material);

         var material2 = new THREE.MeshPhongMaterial({
                                                        map: textureLoader.load('qrc:images/earthcloudmapcolortrans.png'),
                                                        side: THREE.FrontSide,
                                                        transparent: true,
                                                        opacity: 0.8
                                                    });
         var mesh2 = new THREE.Mesh(commonGeometry, material2);

         mesh.scale.set(1.02, 1.02, 1.02);
         earthMesh.add(mesh);
         mesh2.scale.set(1.02, 1.02, 1.02);
         earthMesh.add(mesh2);
     }

     function createRing(radius, width, height, texture) {

         var textureLoader = new THREE.TextureLoader();
         var geometry = new THREE.BufferGeometry().fromGeometry(
                     new THREEx.Planets._RingGeometry(radius, width, height));

         var material = new THREE.MeshPhongMaterial({
                                                        map: textureLoader.load(texture),
                                                        side: THREE.DoubleSide,
                                                        transparent: true,
                                                        opacity: 0.8
                                                    });
         material.map.minFilter = THREE.NearestFilter;
         var mesh = new THREE.Mesh(geometry, material);
         mesh.lookAt(new THREE.Vector3(0, 90, 0));

         return mesh;

     }

     function createStarfield(radius) {

         var textureLoader = new THREE.TextureLoader();
         var texture = textureLoader.load('images/galaxy_starfield.png')
         var material = new THREE.MeshBasicMaterial({
                                                        map: texture,
                                                        side: THREE.BackSide
                                                    })
         var geometry = new THREE.BufferGeometry().fromGeometry(new THREE.SphereGeometry(radius, 32, 32));
         var mesh = new THREE.Mesh(geometry, material)

         return mesh

     }

     function onResizeGL(canvas) {

         if (camera === undefined) return;

         camera.aspect = canvas.width / canvas.height;
         camera.updateProjectionMatrix();
         renderer.setPixelRatio(canvas.devicePixelRatio);
         renderer.setSize(canvas.width, canvas.height);

     }

     function onSpeedChanged(value) {

         daysPerFrameScale = value;

     }

     function setScale(value, focused) {

         // Save actual scale in focus mode
         if (!focused)
             actualScale = value;

         // Limit minimum scaling in focus mode to avoid jitter caused by rounding errors
         if (value <= focusedMinimumScale && (focusedScaling || focused)) {
             planetScale = focusedMinimumScale;
         } else {
             planetScale = actualScale;
         }

         for (var i = 0; i < objects.length; i++) {
             var object = objects[i];
             // first reset scale
             var radius = planets[i]["radius"];
             object.scale.set(radius, radius, radius);
             if (i === SUN) {
                 object.scale.multiplyScalar(planetScale / 100);
             } else {
                 object.scale.multiplyScalar(planetScale);
             }
             hitObjects[i].scale.set(object.scale.x, object.scale.y, object.scale.z);
         }

     }

     function prepareFocusedPlanetAnimation() {

         oldCameraPosition = camera.position.clone();

         var planet = SUN;
         if (qmlView.oldPlanet !== SOLAR_SYSTEM)
             planet = qmlView.oldPlanet;
         oldFocusedPlanetPosition = objects[planet].position.clone();
         qmlView.oldPlanet = qmlView.focusedPlanet;

         if (qmlView.focusedPlanet !== SOLAR_SYSTEM && actualScale <= focusedMinimumScale) {
             // Limit minimum scaling in focus mode to avoid jitter caused by rounding errors
             planetScale = focusedMinimumScale;
             setScale(focusedMinimumScale, true);
             focusedScaling = true;
         } else if (focusedScaling === true) {
             // Restore normal scaling
             focusedScaling = false;
             setScale(actualScale);
         }

         calculateLookAtOffset();
         calculateCameraOffset();

     }

     function setCameraDistance(distance) {

         cameraDistance = distance;

     }

     function calculateLookAtOffset() {

         var offset = oldFocusedPlanetPosition.clone();

         var planet = 0;
         if (qmlView.focusedPlanet !== SOLAR_SYSTEM)
             planet = qmlView.oldPlanet;

         var focusedPlanetPosition = objects[planet].position.clone();
         offset.sub(focusedPlanetPosition);

         qmlView.xLookAtOffset = offset.x;
         qmlView.yLookAtOffset = offset.y;
         qmlView.zLookAtOffset = offset.z;

     }

     function calculateCameraOffset() {

         var offset = oldCameraPosition.clone();

         var planet = 0;
         if (qmlView.focusedPlanet !== SOLAR_SYSTEM)
             planet = qmlView.focusedPlanet;

         var newCameraPosition = getNewCameraPosition(getOuterRadius(planet));

         if (qmlView.focusedPlanet !== SUN)
             offset.sub(newCameraPosition);

         if (qmlView.focusedPlanet === SUN && qmlView.oldPlanet === SOLAR_SYSTEM) {
             qmlView.xCameraOffset = Math.abs(offset.x);
             qmlView.yCameraOffset = Math.abs(offset.y);
             qmlView.zCameraOffset = Math.abs(offset.z);
         } else { // from a planet to another
             qmlView.xCameraOffset = offset.x;
             qmlView.yCameraOffset = offset.y;
             qmlView.zCameraOffset = offset.z;
         }

     }

     function getNewCameraPosition( radius ) {

         var position;
         if (qmlView.focusedPlanet === SOLAR_SYSTEM) {
             position = defaultCameraPosition.clone();
             position.multiplyScalar(cameraDistance);
         } else if (qmlView.focusedPlanet === SUN) {
             position = new THREE.Vector3(radius * planetScale * 2,
                                          radius * planetScale * 2,
                                          radius * planetScale * 2);
             position.multiplyScalar(cameraDistance);

         } else {
             var vec1 = objects[qmlView.focusedPlanet].position.clone();
             var vec2 = new THREE.Vector3(0, 1, 0);
             vec1.normalize();
             vec2.cross(vec1);
             vec2.multiplyScalar(radius * planetScale * cameraDistance * 4);
             vec2.add(objects[qmlView.focusedPlanet].position);
             vec1.set(0, radius * planetScale, 0);
             vec2.add(vec1);
             position = vec2;
         }
         return position;
     }

     function onDocumentMouseDown(x, y) {

         // Mouse selection for planets and Solar system, not for the Moon.
         // Intersection tests are done against a set of cruder hit objects instead of
         // actual planet meshes, as checking a lot of faces can be slow.
         mouse.set((x / planetCanvas.width) * 2 - 1, - (y / planetCanvas.height ) * 2 + 1);

         raycaster.setFromCamera(mouse, camera);

         var intersects = [];
         var i = 0;
         var objectCount = hitObjects.length - 1; // -1 excludes the moon, which is the last object
         while (i < objectCount) {
             // Update hitObject position
             var objectPos = objects[i].position;
             var hitObject = hitObjects[i];
             hitObject.position.set(objectPos.x, objectPos.y, objectPos.z);
             hitObject.updateMatrixWorld();

             hitObject.raycast( raycaster, intersects );

             i++;
         }
         intersects.sort( raycaster.ascSort );

         var selectedPlanet;

         if (intersects.length > 0) {
             var intersect = intersects[0];

             i = 0;
             while (i < objectCount) {
                 if (intersect.object === hitObjects[i]) {
                     selectedPlanet = i;
                     break;
                 }
                 i++;
             }
             if (selectedPlanet < NUM_SELECTABLE_PLANETS) {
                 qmlView.focusedPlanet = selectedPlanet;
                 // Limit minimum scaling in focus mode to avoid jitter caused by rounding errors
                 if (actualScale <= focusedMinimumScale) {
                     planetScale = focusedMinimumScale;
                     setScale(focusedMinimumScale, true);
                 }
                 focusedScaling = true;
             }
         } else {
             qmlView.focusedPlanet = SOLAR_SYSTEM;
             // Restore normal scaling
             if (focusedScaling === true) {
                 focusedScaling = false;
                 setScale(actualScale);
             }
         }

     }

     function paintGL(canvas) {

         if (qmlView.focusedPlanet === SOLAR_SYSTEM)
             daysPerFrame = daysPerFrameScale * 10;
         else
             daysPerFrame = daysPerFrameScale * planets[qmlView.focusedPlanet]["period"] / 100;

         // Advance the time in days
         oldTimeD = currTimeD;
         currTimeD = currTimeD + daysPerFrame;
         var deltaTimeD = currTimeD - oldTimeD;

         // Position the planets orbiting the sun
         for (var i = 1; i < objects.length; i ++) {
             var object = objects[i];
             var planet = planets[i];

             // Bumpmaps of mercury, venus, jupiter and moon need special handling
             if (i == MERCURY || i == VENUS || i == JUPITER || i == MOON)
                 object.material.bumpScale = 0.03 * planetScale;
             else
                 object.material.bumpScale = 0.3 * planetScale;

             // Calculate the planet orbital elements from the current time in days
             var N =  (planet["N1"] + planet["N2"] * currTimeD) * Math.PI / 180;
             var iPlanet = (planet["i1"] + planet["i2"] * currTimeD) * Math.PI / 180;
             var w =  (planet["w1"] + planet["w2"] * currTimeD) * Math.PI / 180;
             var a = planet["a1"] + planet["a2"] * currTimeD;
             var e = planet["e1"] + planet["e2"] * currTimeD;
             var M = (planet["M1"] + planet["M2"] * currTimeD) * Math.PI / 180;
             var E = M + e * Math.sin(M) * (1.0 + e * Math.cos(M));

             var xv = a * (Math.cos(E) - e);
             var yv = a * (Math.sqrt(1.0 - e * e) * Math.sin(E));
             var v = Math.atan2(yv, xv);

             // Calculate the distance (radius)
             var r = Math.sqrt(xv * xv + yv * yv);

             // From http://www.davidcolarusso.com/astro/
             // Modified to compensate for the right handed coordinate system of OpenGL
             var xh = r * (Math.cos(N) * Math.cos(v + w)
                           - Math.sin(N) * Math.sin(v + w) * Math.cos(iPlanet));
             var zh = -r * (Math.sin(N) * Math.cos(v + w)
                            + Math.cos(N) * Math.sin(v + w) * Math.cos(iPlanet));
             var yh = r * (Math.sin(w + v) * Math.sin(iPlanet));

             // Apply the position offset from the center of orbit to the bodies
             var centerOfOrbit = objects[planet["centerOfOrbit"]];
             object.position.set(centerOfOrbit.position.x + xh * auScale,
                                 centerOfOrbit.position.y + yh * auScale,
                                 centerOfOrbit.position.z + zh * auScale);

             // Calculate and apply the appropriate axis tilt to the bodies
             // and rotate them around the axis
             var radians = planet["tilt"] * Math.PI / 180; // tilt in radians
             object.rotation.order = 'ZXY';
             object.rotation.x = 0;
             object.rotation.y += (deltaTimeD / planet["period"]) * 2 * Math.PI;
             object.rotation.z = radians;
         }

         // rotate the Sun
         var sun = objects[SUN];
         sun.rotation.order = 'ZXY';
         sun.rotation.x = 0;
         sun.rotation.y += (deltaTimeD / planets[SUN]["period"]) * 2 * Math.PI;
         sun.rotation.z = planets[SUN]["tilt"] * Math.PI / 180; // tilt in radians

         // calculate the outer radius of the focused item
         var outerRadius = getOuterRadius(qmlView.focusedPlanet);

         // get the appropriate near plane position for the camera and animate it with QML animations
         qmlView.cameraNear = outerRadius;

         camera.near = qmlView.cameraNear;
         camera.updateProjectionMatrix();

         // Calculate and set camera position
         var cameraPosition = getNewCameraPosition(outerRadius);
         var cameraOffset = new THREE.Vector3(qmlView.xCameraOffset,
                                              qmlView.yCameraOffset,
                                              qmlView.zCameraOffset);
         cameraPosition.add(cameraOffset);
         camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);

         // Calculate and set camera look-at point
         var lookAtPlanet = SUN;
         if (qmlView.focusedPlanet !== SOLAR_SYSTEM)
             lookAtPlanet = qmlView.focusedPlanet;
         var cameraLookAt = objects[lookAtPlanet].position.clone();
         var lookAtOffset = new THREE.Vector3(qmlView.xLookAtOffset,
                                              qmlView.yLookAtOffset,
                                              qmlView.zLookAtOffset);
         cameraLookAt.add(lookAtOffset);
         camera.lookAt(cameraLookAt);

         // Render the scene
         renderer.render(scene, camera);

     }

     function getOuterRadius( planet ) {

         var outerRadius = solarDistance;
         if (planet !== SOLAR_SYSTEM) {
             outerRadius = planets[planet]["radius"];
             if (planet === SATURN) {
                 outerRadius =+ saturnOuterRadius;
             } else if (planet === URANUS) {
                 outerRadius =+ uranusOuterRadius;
             } else if (planet === SUN) {
                 outerRadius = planets[planet]["radius"] / 100;
             }
         }

         return outerRadius;
     }