Source Code of Lorentz Java 3D Application

Lorentz 3D Application Screenshot

This is the simplified source code for a Java 3D program to render the solution of the Lorentz equations, with full mouse control. It is basically the same as the Lorentz applet with two differences: a simple Euler solver is used (see below) and there are no controls for the parameters. It is included here primarily as an example of how to write a Java3D program. A screenshot of the application running on Microsoft Windows® XP is shown to the right.

You can download the full source code (12kB). As usual with Java programs, the code needs to be placed in a directory hierarchy, in this case com/fourdelta/lorentz/LorentzApp.java before compiling and running. To build the source code you will need a Java Development Kit (tested with JDK 1.6 update 11) with the Java 3D libraries installed (tested with Java3D 1.5.2).

Mathematics

As discussed the Lorentz equations are a family of simple differential equations which exhibit chaotic behaviour in the face of attempts to "solve" them using numerical methods. This program uses a simple Euler solver, which uses a linear extrapolation of the derivatives of the equations to produce the next set of values. The webstart application uses a more accurate 4th order Runge-Kutta method from our library of scientific code, but this algorithm is not reproduced here. The solver is located at lines 296-305 in the source code below.

The physical constants A, B and C of the Lorentz system are set in lines 69-71. Note that the choice of constants interacts with the solver. The simple Euler solver demonstrates chaotic behaviour with the constants given, but if you implement a more accurate solver it may be necessary to change these constants to get chaotic results!

Compilation and Running

C:\Temp\LorentzApp> mkdir com\fourdelta\lorentz
C:\Temp\LorentzApp> move LorentzApp.java com\fourdelta\lorentz\
C:\Temp\LorentzApp> set JAVA_HOME="C:\Program Files\Java\jdk1.6.0_11"
C:\Temp\LorentzApp> set PATH="%JAVA_HOME%\bin";%PATH%
C:\Temp\LorentzApp> java -version
java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode, sharing)
C:\Temp\LorentzApp> javac -d . com\fourdelta\lorentz\LorentzApp.java
C:\Temp\LorentzApp> java -cp . com.fourdelta.lorentz.LorentzApp

Full Source Code

 

  1. /*****************************************************************************  
  2.  * Java3D demonstration program to render the Lorentz "butterfly".  
  3.  *   
  4.  * Copyright (c) 2009 Four Delta Consulting Ltd.  
  5.  * All rights reserved.  
  6.  *  
  7.  * Redistribution and use in source and binary forms, with or without  
  8.  * modification, are permitted provided that the following conditions are met:  
  9.  *   
  10.  *     * Redistributions of source code must retain the above copyright  
  11.  *       notice, this list of conditions and the following disclaimer.  
  12.  *         
  13.  *     * Redistributions in binary form must reproduce the above copyright  
  14.  *       notice, this list of conditions and the following disclaimer in the  
  15.  *       documentation and/or other materials provided with the distribution.  
  16.  *         
  17.  *     * Neither the name of Four Delta Consulting nor the names of its   
  18.  *       contributors may be used to endorse or promote products  
  19.  *       derived from this software without specific prior written permission.  
  20.  *  
  21.  * THIS SOFTWARE IS PROVIDED BY FOUR DELTA CONSULTING "AS IS" AND ANY  
  22.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  
  23.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  
  24.  * DISCLAIMED. IN NO EVENT SHALL FOUR DELTA CONSULTING BE LIABLE FOR ANY  
  25.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  
  26.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  
  27.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  
  28.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  
  29.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
  30.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
  31.  ****************************************************************************/  
  32.   
  33. package com.fourdelta.lorentz;   
  34.   
  35. import java.awt.BorderLayout;   
  36. import java.awt.Color;   
  37. import java.awt.Dimension;   
  38. import java.awt.GraphicsConfiguration;   
  39.   
  40. import javax.media.j3d.AmbientLight;   
  41. import javax.media.j3d.Appearance;   
  42. import javax.media.j3d.Background;   
  43. import javax.media.j3d.BoundingSphere;   
  44. import javax.media.j3d.BranchGroup;   
  45. import javax.media.j3d.Canvas3D;   
  46. import javax.media.j3d.GeometryArray;   
  47. import javax.media.j3d.LineArray;   
  48. import javax.media.j3d.LineAttributes;   
  49. import javax.media.j3d.Shape3D;   
  50. import javax.media.j3d.Transform3D;   
  51. import javax.media.j3d.TransformGroup;   
  52. import javax.swing.JFrame;   
  53. import javax.swing.WindowConstants;   
  54. import javax.vecmath.Color3f;   
  55. import javax.vecmath.Point3d;   
  56. import javax.vecmath.Vector3d;   
  57.   
  58. import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;   
  59. import com.sun.j3d.utils.universe.SimpleUniverse;   
  60. import com.sun.j3d.utils.universe.ViewingPlatform;   
  61.   
  62. /**  
  63.  * Java3D demonstration program to render the Lorentz "butterfly".  
  64.  */  
  65. public class LorentzApp extends JFrame {   
  66.     private static final long serialVersionUID = 2839740866350009392L;   
  67.   
  68.     // Lorentz Equation constants  
  69.     private static final double     A = 12.0;   
  70.     private static final double     B = 20.0;   
  71.     private static final double     C = 3.666;   
  72.        
  73.     // rendering attributes  
  74.     private static final float      LINE_WIDTH = 1.0f;   
  75.     private static final int        ITER = 2000;   
  76.     private static final boolean    ANTIALIAS = true;   
  77.        
  78.     private LorentzApp() {   
  79.         super("Lorentz 3D");   
  80.            
  81.         // set up the frame  
  82.         setLayout(new BorderLayout());   
  83.         setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);   
  84.         setPreferredSize(new Dimension(640480));   
  85.            
  86.         // create a 3D canvas  
  87.         final GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();   
  88.         final Canvas3D canvas = new Canvas3D(config);   
  89.   
  90.         // add the canvas to the frame   
  91.         add(canvas, BorderLayout.CENTER);   
  92.   
  93.         // create the Java 3D universe  
  94.         final SimpleUniverse universe = createUniverse(canvas);   
  95.   
  96.         // create the Java 3D scene & attach it to the universe    
  97.         universe.addBranchGraph(createScene());   
  98.   
  99.         // display the frame  
  100.         pack();   
  101.         setVisible(true);   
  102.     }   
  103.   
  104.     ///////////////////////////////////////////////////////////////////////////////////  
  105.     // Application entry point  
  106.        
  107.     public static void main(final String[] args) {   
  108.         new LorentzApp();   
  109.     }   
  110.   
  111.     ///////////////////////////////////////////////////////////////////////////////////  
  112.     // Java3D universe creation  
  113.   
  114.     private SimpleUniverse createUniverse(final Canvas3D canvas) {   
  115.         final SimpleUniverse universe = new SimpleUniverse(canvas);   
  116.   
  117.         // add mouse behaviour to the ViewingPlatform  
  118.         ViewingPlatform viewPlatform = universe.getViewingPlatform();   
  119.   
  120.         // move the ViewPlatform back a bit  
  121.         viewPlatform.setNominalViewingTransform();   
  122.   
  123.         // add orbit behaviour to the ViewingPlatform  
  124.         OrbitBehavior orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL);   
  125.         BoundingSphere bounds = new BoundingSphere(new Point3d(0.00.00.0), 100.0);   
  126.         orbit.setSchedulingBounds(bounds);   
  127.         viewPlatform.setViewPlatformBehavior(orbit);   
  128.   
  129.         return universe;   
  130.     }   
  131.        
  132.   
  133.     ///////////////////////////////////////////////////////////////////////////////////  
  134.     // Java3D scene creation  
  135.        
  136.     private BranchGroup createScene()   
  137.     {   
  138.         // create the root of the branch graph  
  139.         final BranchGroup root = new BranchGroup();   
  140.   
  141.         // create a bounds for the background and lights  
  142.         final BoundingSphere bounds = new BoundingSphere(new Point3d(0.00.00.0), 100.0);   
  143.   
  144.         // create some colors  
  145.         final Color3f backColor = new Color3f(Color.BLACK);   
  146.         final Color3f ambientColor = new Color3f(Color.WHITE);   
  147.   
  148.         // set up the background  
  149.         final Background background = new Background(backColor);   
  150.         background.setApplicationBounds(bounds);   
  151.         root.addChild(background);   
  152.   
  153.         // set up the ambient light  
  154.         final AmbientLight ambientLight = new AmbientLight(ambientColor);   
  155.         ambientLight.setInfluencingBounds(bounds);   
  156.         root.addChild(ambientLight);   
  157.   
  158.         // create a global identity TransformGroup for the scene  
  159.         final TransformGroup masterTrans = new TransformGroup();   
  160.         root.addChild(masterTrans);   
  161.            
  162.         // scale and translate the rendered object   
  163.         final TransformGroup objectTrans = new TransformGroup();   
  164.         final Transform3D transform = new Transform3D();   
  165.         transform.setScale(0.03);   
  166.         transform.setTranslation(new Vector3d(0.00.0, -0.5)); // translate to origin  
  167.         objectTrans.setTransform(transform);   
  168.            
  169.         // create the equations  
  170.         final Shape3D equationObject = new Shape3D();   
  171.         equationObject.setCapability(Shape3D.ALLOW_APPEARANCE_READ);   
  172.         equationObject.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);   
  173.         equationObject.setCapability(Shape3D.ALLOW_GEOMETRY_READ);   
  174.         equationObject.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);   
  175.         equationObject.setGeometry(calculateGeometry());   
  176.         equationObject.setAppearance(createAppearance());   
  177.   
  178.         // add the rendered object to its transform  
  179.         objectTrans.addChild(equationObject);   
  180.            
  181.         // add the object transform group to the master transform  
  182.         masterTrans.addChild(objectTrans);   
  183.            
  184.         // compile the scene graph.  
  185.         root.compile();   
  186.   
  187.         return root;   
  188.     }   
  189.        
  190.     ///////////////////////////////////////////////////////////////////////////////////  
  191.     // Java3D object creation  
  192.        
  193.     /*  
  194.      * Calculate the geometry by iterating the solution of the equation set.  
  195.      *   
  196.      * @return a LineArray of the geometry.   
  197.      */  
  198.     private LineArray calculateGeometry()   
  199.     {   
  200.         final double step = 0.01f;   
  201.   
  202.         final float[] points = new float[ITER * 6];   
  203.         final float[] colors = new float[ITER * 6];   
  204.   
  205.         // initial conditions  
  206.         double[] current = { 0.01.00.0 };   
  207.         double[] derivs = derivatives(current);   
  208.         double time = step;   
  209.            
  210.         final double period = ITER / 100.0;   
  211.         for (int index = 0; index < ITER; ++index)   
  212.         {   
  213.             // stepwise integrate the equations to get the next values   
  214.             final double[] next = integrate(current, derivs, time, step);   
  215.                
  216.             // create a rainbow color sweep in RGB  
  217.             final double red = ((1.0 + Math.sin(index * Math.PI / period)) / 2.0);   
  218.             final double green = ((1.0 + Math.sin(index * Math.PI / period + 0.6667 * Math.PI)) / 2.0);   
  219.             final double blue = ((1.0 + Math.sin(index * Math.PI / period + 1.3333 * Math.PI)) / 2.0);   
  220.   
  221.             // set the points for the line segment  
  222.             points[index * 6 + 0] = (float)current[0];   
  223.             points[index * 6 + 1] = (float)current[1];   
  224.             points[index * 6 + 2] = (float)current[2];   
  225.             points[index * 6 + 3] = (float)next[0];   
  226.             points[index * 6 + 4] = (float)next[1];   
  227.             points[index * 6 + 5] = (float)next[2];   
  228.   
  229.             // set the start & end colours for the line segment  
  230.             colors[index * 6 + 0] = (float)red;   
  231.             colors[index * 6 + 1] = (float)green;   
  232.             colors[index * 6 + 2] = (float)blue;   
  233.             colors[index * 6 + 3] = (float)red;   
  234.             colors[index * 6 + 4] = (float)green;   
  235.             colors[index * 6 + 5] = (float)blue;   
  236.   
  237.             current = next;   
  238.             time += step;   
  239.         }   
  240.            
  241.         // build the line array representing the normals  
  242.         final LineArray line = new LineArray(ITER * 2, GeometryArray.COORDINATES | GeometryArray.COLOR_3);   
  243.         line.setCoordinates(0, points);   
  244.         line.setColors(0, colors);   
  245.            
  246.         return line;   
  247.     }   
  248.        
  249.     /**  
  250.      * Create the appearance for the rendered object.  
  251.      *   
  252.      * @return an Appearance.  
  253.      */  
  254.     private Appearance createAppearance() {   
  255.         final Appearance appearance = new Appearance();   
  256.         LineAttributes attrs = new LineAttributes(LINE_WIDTH, LineAttributes.PATTERN_SOLID, true);   
  257.         attrs.setLineAntialiasingEnable(ANTIALIAS);   
  258.         appearance.setLineAttributes(attrs);   
  259.         return appearance;   
  260.     }   
  261.        
  262.     ///////////////////////////////////////////////////////////////////////////////////  
  263.     // Lorentz Equations  
  264.   
  265.     /**  
  266.      * Get the derivatives of the Lorentz equation system w.r.t. time.  
  267.      *   
  268.      * @param current the current values of the equation system.  
  269.      * @return the derivatives.  
  270.      */  
  271.     private double[] derivatives(double[] current) {   
  272.         final double x = current[0];   
  273.         final double y = current[1];   
  274.         final double z = current[2];   
  275.            
  276.         return new double[] {    
  277.             A * (y - x),       // dx / dt  
  278.             x * (B - z) - y,   // dy / dt  
  279.             x * y - C * z      // dz / dt  
  280.         };   
  281.     }   
  282.   
  283.     ///////////////////////////////////////////////////////////////////////////////////  
  284.     // Solver  
  285.   
  286.     /**  
  287.      * Given values for the variables current[1..n] and their derivatives derivs[1..n] known at time t,  
  288.      * advance the solution over an interval step and return the incremented variables.  
  289.      *   
  290.      * @param current current values at starting time  
  291.      * @param derivs derivatives at starting time  
  292.      * @param time current time value  
  293.      * @param step step size  
  294.      * @return resulting values at time + step  
  295.      */  
  296.     private double[] integrate(double[] current, double[] derivs, double time, double step) {   
  297.         // this is a simple Euler (straight line approximation)  
  298.         final int n = current.length;   
  299.         final double[] d = derivatives(current);   
  300.         final double[] result = new double[n];   
  301.         for (int index = 0; index < n; ++index) {   
  302.             result[index] = current[index] + d[index] * step;    
  303.         }   
  304.         return result;   
  305.     }   
  306. }