Source Code of Lorentz Java 3D Application
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> 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
- /*****************************************************************************
- * Java3D demonstration program to render the Lorentz "butterfly".
- *
- * Copyright (c) 2009 Four Delta Consulting Ltd.
- * All rights reserved.
- *
- * 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 Four Delta Consulting 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 FOUR DELTA CONSULTING "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 FOUR DELTA CONSULTING 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.
- ****************************************************************************/
- package com.fourdelta.lorentz;
- import java.awt.BorderLayout;
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.GraphicsConfiguration;
- import javax.media.j3d.AmbientLight;
- import javax.media.j3d.Appearance;
- import javax.media.j3d.Background;
- import javax.media.j3d.BoundingSphere;
- import javax.media.j3d.BranchGroup;
- import javax.media.j3d.Canvas3D;
- import javax.media.j3d.GeometryArray;
- import javax.media.j3d.LineArray;
- import javax.media.j3d.LineAttributes;
- import javax.media.j3d.Shape3D;
- import javax.media.j3d.Transform3D;
- import javax.media.j3d.TransformGroup;
- import javax.swing.JFrame;
- import javax.swing.WindowConstants;
- import javax.vecmath.Color3f;
- import javax.vecmath.Point3d;
- import javax.vecmath.Vector3d;
- import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
- import com.sun.j3d.utils.universe.SimpleUniverse;
- import com.sun.j3d.utils.universe.ViewingPlatform;
- /**
- * Java3D demonstration program to render the Lorentz "butterfly".
- */
- public class LorentzApp extends JFrame {
- private static final long serialVersionUID = 2839740866350009392L;
- // Lorentz Equation constants
- private static final double A = 12.0;
- private static final double B = 20.0;
- private static final double C = 3.666;
- // rendering attributes
- private static final float LINE_WIDTH = 1.0f;
- private static final int ITER = 2000;
- private static final boolean ANTIALIAS = true;
- private LorentzApp() {
- super("Lorentz 3D");
- // set up the frame
- setLayout(new BorderLayout());
- setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
- setPreferredSize(new Dimension(640, 480));
- // create a 3D canvas
- final GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
- final Canvas3D canvas = new Canvas3D(config);
- // add the canvas to the frame
- add(canvas, BorderLayout.CENTER);
- // create the Java 3D universe
- final SimpleUniverse universe = createUniverse(canvas);
- // create the Java 3D scene & attach it to the universe
- universe.addBranchGraph(createScene());
- // display the frame
- pack();
- setVisible(true);
- }
- ///////////////////////////////////////////////////////////////////////////////////
- // Application entry point
- public static void main(final String[] args) {
- new LorentzApp();
- }
- ///////////////////////////////////////////////////////////////////////////////////
- // Java3D universe creation
- private SimpleUniverse createUniverse(final Canvas3D canvas) {
- final SimpleUniverse universe = new SimpleUniverse(canvas);
- // add mouse behaviour to the ViewingPlatform
- ViewingPlatform viewPlatform = universe.getViewingPlatform();
- // move the ViewPlatform back a bit
- viewPlatform.setNominalViewingTransform();
- // add orbit behaviour to the ViewingPlatform
- OrbitBehavior orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL);
- BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
- orbit.setSchedulingBounds(bounds);
- viewPlatform.setViewPlatformBehavior(orbit);
- return universe;
- }
- ///////////////////////////////////////////////////////////////////////////////////
- // Java3D scene creation
- private BranchGroup createScene()
- {
- // create the root of the branch graph
- final BranchGroup root = new BranchGroup();
- // create a bounds for the background and lights
- final BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
- // create some colors
- final Color3f backColor = new Color3f(Color.BLACK);
- final Color3f ambientColor = new Color3f(Color.WHITE);
- // set up the background
- final Background background = new Background(backColor);
- background.setApplicationBounds(bounds);
- root.addChild(background);
- // set up the ambient light
- final AmbientLight ambientLight = new AmbientLight(ambientColor);
- ambientLight.setInfluencingBounds(bounds);
- root.addChild(ambientLight);
- // create a global identity TransformGroup for the scene
- final TransformGroup masterTrans = new TransformGroup();
- root.addChild(masterTrans);
- // scale and translate the rendered object
- final TransformGroup objectTrans = new TransformGroup();
- final Transform3D transform = new Transform3D();
- transform.setScale(0.03);
- transform.setTranslation(new Vector3d(0.0, 0.0, -0.5)); // translate to origin
- objectTrans.setTransform(transform);
- // create the equations
- final Shape3D equationObject = new Shape3D();
- equationObject.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
- equationObject.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
- equationObject.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
- equationObject.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
- equationObject.setGeometry(calculateGeometry());
- equationObject.setAppearance(createAppearance());
- // add the rendered object to its transform
- objectTrans.addChild(equationObject);
- // add the object transform group to the master transform
- masterTrans.addChild(objectTrans);
- // compile the scene graph.
- root.compile();
- return root;
- }
- ///////////////////////////////////////////////////////////////////////////////////
- // Java3D object creation
- /*
- * Calculate the geometry by iterating the solution of the equation set.
- *
- * @return a LineArray of the geometry.
- */
- private LineArray calculateGeometry()
- {
- final double step = 0.01f;
- final float[] points = new float[ITER * 6];
- final float[] colors = new float[ITER * 6];
- // initial conditions
- double[] current = { 0.0, 1.0, 0.0 };
- double[] derivs = derivatives(current);
- double time = step;
- final double period = ITER / 100.0;
- for (int index = 0; index < ITER; ++index)
- {
- // stepwise integrate the equations to get the next values
- final double[] next = integrate(current, derivs, time, step);
- // create a rainbow color sweep in RGB
- final double red = ((1.0 + Math.sin(index * Math.PI / period)) / 2.0);
- final double green = ((1.0 + Math.sin(index * Math.PI / period + 0.6667 * Math.PI)) / 2.0);
- final double blue = ((1.0 + Math.sin(index * Math.PI / period + 1.3333 * Math.PI)) / 2.0);
- // set the points for the line segment
- points[index * 6 + 0] = (float)current[0];
- points[index * 6 + 1] = (float)current[1];
- points[index * 6 + 2] = (float)current[2];
- points[index * 6 + 3] = (float)next[0];
- points[index * 6 + 4] = (float)next[1];
- points[index * 6 + 5] = (float)next[2];
- // set the start & end colours for the line segment
- colors[index * 6 + 0] = (float)red;
- colors[index * 6 + 1] = (float)green;
- colors[index * 6 + 2] = (float)blue;
- colors[index * 6 + 3] = (float)red;
- colors[index * 6 + 4] = (float)green;
- colors[index * 6 + 5] = (float)blue;
- current = next;
- time += step;
- }
- // build the line array representing the normals
- final LineArray line = new LineArray(ITER * 2, GeometryArray.COORDINATES | GeometryArray.COLOR_3);
- line.setCoordinates(0, points);
- line.setColors(0, colors);
- return line;
- }
- /**
- * Create the appearance for the rendered object.
- *
- * @return an Appearance.
- */
- private Appearance createAppearance() {
- final Appearance appearance = new Appearance();
- LineAttributes attrs = new LineAttributes(LINE_WIDTH, LineAttributes.PATTERN_SOLID, true);
- attrs.setLineAntialiasingEnable(ANTIALIAS);
- appearance.setLineAttributes(attrs);
- return appearance;
- }
- ///////////////////////////////////////////////////////////////////////////////////
- // Lorentz Equations
- /**
- * Get the derivatives of the Lorentz equation system w.r.t. time.
- *
- * @param current the current values of the equation system.
- * @return the derivatives.
- */
- private double[] derivatives(double[] current) {
- final double x = current[0];
- final double y = current[1];
- final double z = current[2];
- return new double[] {
- A * (y - x), // dx / dt
- x * (B - z) - y, // dy / dt
- x * y - C * z // dz / dt
- };
- }
- ///////////////////////////////////////////////////////////////////////////////////
- // Solver
- /**
- * Given values for the variables current[1..n] and their derivatives derivs[1..n] known at time t,
- * advance the solution over an interval step and return the incremented variables.
- *
- * @param current current values at starting time
- * @param derivs derivatives at starting time
- * @param time current time value
- * @param step step size
- * @return resulting values at time + step
- */
- private double[] integrate(double[] current, double[] derivs, double time, double step) {
- // this is a simple Euler (straight line approximation)
- final int n = current.length;
- final double[] d = derivatives(current);
- final double[] result = new double[n];
- for (int index = 0; index < n; ++index) {
- result[index] = current[index] + d[index] * step;
- }
- return result;
- }
- }
24 Great King Street, Edinburgh, EH3 6QN
+44 (0)7870 552992
