Mega Code Archive

 
Categories / Java / 3D Graphics
 

Viewer

/*  * %Z%%M% %I% %E% %U%  *   * ************************************************************** "Copyright (c)  * 2001 Sun Microsystems, Inc. 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.  *   * -Redistribution 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 Sun Microsystems, Inc. or the names of contributors may  * be used to endorse or promote products derived from this software without  * specific prior written permission.  *   * This software is provided "AS IS," without a warranty of any kind. ALL  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY  * OF SUCH DAMAGES.  *   * You acknowledge that Software is not designed,licensed or intended for use in  * the design, construction, operation or maintenance of any nuclear facility."  *   * ***************************************************************************  */ import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.text.NumberFormat; import java.util.Enumeration; import java.util.EventListener; import java.util.EventObject; import java.util.Hashtable; import java.util.Vector; import javax.media.j3d.Alpha; 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.ColoringAttributes; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.LineArray; import javax.media.j3d.LineAttributes; import javax.media.j3d.LineStripArray; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Screen3D; import javax.media.j3d.Shape3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TriangleFanArray; import javax.media.j3d.View; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import javax.vecmath.Vector4d; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGEncodeParam; import com.sun.image.codec.jpeg.JPEGImageEncoder; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class ViewProj extends Applet implements Java3DExplorerConstants {   PolygonAttributes solidPa;   PolygonAttributes wirePa;   JSlider dynamicOffsetSlider;   JSlider staticOffsetSlider;   JLabel dynamicSliderValueLabel;   JLabel staticSliderValueLabel;   float dynamicOffset = 1.0f;   float staticOffset = 1.0f;   float frontClipDist = 1.413f;   float backClipDist = 3.309f;   float backClipRatio = backClipDist / frontClipDist;   View view;   ViewingPlatform viewingPlatform;   float innerScale = 0.94f;   TransformGroup innerTG;   Transform3D scale;   Transform3D projTrans = new Transform3D();   int numClipGridPts;   int maxClipGridPts = 180;   Point3f[] clipGridPtsVW = new Point3f[maxClipGridPts];   Point3f[] clipGridPtsProj = new Point3f[maxClipGridPts];   int numCirclePts = 36;   Point3f[] circlePtsVW = new Point3f[numCirclePts];   Point3f[] circlePtsProj = new Point3f[numCirclePts];   Point3f eyePtVW = new Point3f();   float fov;   float sphereRadius = 0.85f;   BranchGroup urScene;   BranchGroup lrScene;   SimpleUniverse urUniverse;   SimpleUniverse lrUniverse;   boolean isApplication;   Canvas3D canvas;   Canvas3D urCanvas;   Canvas3D lrCanvas;   OffScreenCanvas3D offScreenCanvas;   OffScreenCanvas3D urOffScreenCanvas;   OffScreenCanvas3D lrOffScreenCanvas;   String snapImageString = "Snap Main";   String urSnapImageString = "Snap UR";   String lrSnapImageString = "Snap LR";   String outFileBase = "vproj";   int outFileSeq = 0;   float offScreenScale = 1.0f;   String urOutFileBase = "vprojur";   int urOutFileSeq = 0;   float urOffScreenScale = 1.0f;   String lrOutFileBase = "vprojlr";   int lrOutFileSeq = 0;   float lrOffScreenScale = 1.0f;   NumberFormat nf;   Vector4d projPt = new Vector4d();   public BranchGroup createSceneGraph() {     // Create the root of the branch graph     BranchGroup objRoot = new BranchGroup();     // Create the transform group node and initialize it to the     // identity. Enable the TRANSFORM_WRITE capability so that     // our behavior code can modify it at runtime. Add it to the     // root of the subgraph.     TransformGroup objTrans = new TransformGroup();     objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);     objRoot.addChild(objTrans);     // Create a Sphere. We will display this as both wireframe and     // solid to make a hidden line display     // wireframe     Appearance wireApp = new Appearance();     ColoringAttributes ca = new ColoringAttributes(black,         ColoringAttributes.SHADE_FLAT);     wireApp.setColoringAttributes(ca);     wirePa = new PolygonAttributes(PolygonAttributes.POLYGON_LINE,         PolygonAttributes.CULL_BACK, 0.0f);     wireApp.setPolygonAttributes(wirePa);     Sphere outWireSphere = new Sphere(sphereRadius, 0, 10, wireApp);     objTrans.addChild(outWireSphere);     // solid     ColoringAttributes outCa = new ColoringAttributes(red,         ColoringAttributes.SHADE_FLAT);     Appearance outSolid = new Appearance();     outSolid.setColoringAttributes(outCa);     solidPa = new PolygonAttributes(PolygonAttributes.POLYGON_FILL,         PolygonAttributes.CULL_BACK, 0.0f);     solidPa.setPolygonOffsetFactor(dynamicOffset);     solidPa.setPolygonOffset(staticOffset);     solidPa.setCapability(PolygonAttributes.ALLOW_OFFSET_WRITE);     outSolid.setPolygonAttributes(solidPa);     Sphere outSolidSphere = new Sphere(sphereRadius, 0, 10, outSolid);     objTrans.addChild(outSolidSphere);     innerTG = new TransformGroup();     innerTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);     scale = new Transform3D();     updateInnerScale();     objTrans.addChild(innerTG);     // Create a smaller sphere to go inside. This sphere has a different     // tesselation and color     Sphere inWireSphere = new Sphere(sphereRadius, 0, 15, wireApp);     innerTG.addChild(inWireSphere);     // inside solid     ColoringAttributes inCa = new ColoringAttributes(blue,         ColoringAttributes.SHADE_FLAT);     Appearance inSolid = new Appearance();     inSolid.setColoringAttributes(inCa);     inSolid.setPolygonAttributes(solidPa);     Sphere inSolidSphere = new Sphere(sphereRadius, 0, 15, inSolid);     innerTG.addChild(inSolidSphere);     // Create a new Behavior object that will perform the desired     // operation on the specified transform object and add it into     // the scene graph.     AxisAngle4f axisAngle = new AxisAngle4f(0.0f, 0.0f, 1.0f,         -(float) Math.PI / 2.0f);     Transform3D yAxis = new Transform3D();     Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,         80000, 0, 0, 0, 0, 0);     RotationInterpolator rotator = new RotationInterpolator(rotationAlpha,         objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f);     BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),         100.0);     rotator.setSchedulingBounds(bounds);     //objTrans.addChild(rotator);     Background bgWhite = new Background(white);     bgWhite.setApplicationBounds(bounds);     objTrans.addChild(bgWhite);     // Have Java 3D perform optimizations on this scene graph.     objRoot.compile();     return objRoot;   }   void updateInnerScale() {     scale.set(innerScale);     innerTG.setTransform(scale);   }   public BranchGroup createVWorldViewSG() {     // Create the root of the branch graph     BranchGroup objRoot = new BranchGroup();     objRoot.setCapability(BranchGroup.ALLOW_DETACH);     // setup a transform group to hold the scaled scene     TransformGroup objTrans = new TransformGroup();     objRoot.addChild(objTrans);     // get the eye point, field of view and clip distances     float fov = (float) view.getFieldOfView();     // figure out the angle factors to find points along the edges     // of the FOV     // X = fovSpreadX * (Y - eyeVW.y) + eyeVW.x;     float fovSpreadX = (float) Math.tan(fov / 2);     // Z = fovSpreadZ * (X - eyeVW.x) + eyeVW.z;     float fovSpreadZ = 1.0f / fovSpreadX;     //System.out.println("fovSpreadX = " + fovSpreadX);     //System.out.println("fovSpreadZ = " + fovSpreadZ);     Transform3D vpTransform = new Transform3D();     viewingPlatform.getViewPlatformTransform().getTransform(vpTransform);     Vector3f vpTranslation = new Vector3f();     vpTransform.get(vpTranslation);     eyePtVW.set(vpTranslation);     eyePtVW.negate();     // get the eye point in our 2D coord system.     Point3f eyePt = new Point3f(0.0f, eyePtVW.z, 0.1f);     float frontClipDist = (float) view.getFrontClipDistance();     float backClipDist = (float) view.getBackClipDistance();     // set up the clip plane lines     Point3f[] cpPoints = new Point3f[5];     cpPoints[0] = new Point3f(frontClipDist * fovSpreadX, eyePtVW.z         + frontClipDist, 0.1f);     cpPoints[1] = new Point3f(cpPoints[0]);     cpPoints[1].x *= -1;     Point3f backLeft = new Point3f(-backClipDist * fovSpreadX, eyePtVW.z         + backClipDist, 0.1f);     cpPoints[2] = backLeft;     Point3f backRight = new Point3f(backLeft);     backRight.x *= -1;     cpPoints[3] = backRight;     cpPoints[4] = cpPoints[0];     //for (int i = 0; i < 4; i++) {     //    System.out.println("cpPoints[" + i + "] = " + cpPoints[i]);     //}     int[] cpLength = new int[1];     cpLength[0] = 5;     LineStripArray cpLines = new LineStripArray(5, LineArray.COORDINATES,         cpLength);     cpLines.setCoordinates(0, cpPoints);     Appearance cpApp = new Appearance();     ColoringAttributes cpCa = new ColoringAttributes(blue,         ColoringAttributes.SHADE_FLAT);     cpApp.setColoringAttributes(cpCa);     Shape3D cpShape = new Shape3D(cpLines, cpApp);     objTrans.addChild(cpShape);     // get the limits of the space     float minY = eyePt.y;     float maxY = backLeft.y;     float minX = backLeft.x;     float maxX = backRight.x;     // figure out the X and Y extents and offsets     float deltaX = maxX - minX;     float deltaY = maxY - minY;     float offsetX = -(maxX + minX) / 2.0f;     float offsetY = -(maxY + minY) / 2.0f;     float gridSize = Math.max(deltaX, deltaY);     // scale the grid slightly to give a border around the edge     gridSize *= 1.1f;     //System.out.println("offsetX = " + offsetX);     //System.out.println("offsetY = " + offsetY);     // Scale the view to fit -1 to 1     Transform3D trans = new Transform3D();     trans.set(new Vector3f(offsetX, offsetY, 0.0f), 2.0f / gridSize);     objTrans.setTransform(trans);     // figure out a grid step that is a multiple of 10 which keeps the     // number of steps less than 30.     float gridStep = 1.0f;     while ((gridSize / gridStep) > 30.0) {       gridStep *= 10;     }     int gridNumSteps = (int) Math.ceil(gridSize / gridStep) + 1;     // allocate the grid points array, four points for each step (x and y)     // with a couple extra points for the extra grid points added     // below     int gridNumPoints = 4 * (gridNumSteps + 4);     Point3f[] gridPts = new Point3f[gridNumPoints];     for (int i = 0; i < gridNumPoints; i++) {       gridPts[i] = new Point3f();     }     // find the grid limits. Add a step on each side to make sure     // the grid is larger than the view     float gridMinY = gridStepFloor(minY, gridStep) - gridStep;     float gridMaxY = gridStepCeil(maxY, gridStep) + gridStep;     float gridMinX = gridStepFloor(minX, gridStep) - gridStep;     float gridMaxX = gridStepCeil(maxX, gridStep) + gridStep;     //System.out.println("gridMinY = " + gridMinY);     //System.out.println("gridMaxY = " + gridMaxY);     //System.out.println("gridMinX = " + gridMinX);     //System.out.println("gridMaxX = " + gridMaxX);     // set up the background grid     Appearance bgApp = new Appearance();     ColoringAttributes bgCa = new ColoringAttributes();     bgCa.setColor(grey);     LineAttributes bgLa = new LineAttributes();     bgApp.setColoringAttributes(bgCa);     // clear out the clip grid point list     numClipGridPts = 0;     // set up the vertical lines     int numPts = 0;     for (float x = gridMinX; x <= gridMaxX; x += gridStep) {       gridPts[numPts].x = x;       gridPts[numPts].y = gridMinY;       gridPts[numPts].z = -0.2f;       gridPts[numPts + 1].x = x;       gridPts[numPts + 1].y = gridMaxY;       gridPts[numPts + 1].z = -0.2f;       numPts += 2;       // try to add a line to the clipped grid       // find the intersection of the clipped line with the FOV sides       // this is a distance relative to the eye       float clipZ = fovSpreadZ * Math.abs(x - eyePtVW.x);       if (clipZ < frontClipDist) { // clip to front clip plane         clipZ = frontClipDist;       }       if (clipZ < backClipDist) { // clip to back clip plane         // line is not clipped         clipGridPtsVW[numClipGridPts].x = x;         clipGridPtsVW[numClipGridPts].y = clipZ + eyePtVW.z;         clipGridPtsVW[numClipGridPts].z = -0.1f;         clipGridPtsVW[numClipGridPts + 1].x = x;         clipGridPtsVW[numClipGridPts + 1].y = backClipDist + eyePtVW.z;         clipGridPtsVW[numClipGridPts + 1].z = -0.1f;         numClipGridPts += 2;       }     }     LineArray vertLa = new LineArray(numPts, LineArray.COORDINATES);     vertLa.setCoordinates(0, gridPts, 0, numPts);     Shape3D vertShape = new Shape3D(vertLa, bgApp);     objTrans.addChild(vertShape);     // set up the horizontal lines     numPts = 0;     for (float y = gridMinY; y <= gridMaxY; y += gridStep) {       gridPts[numPts].x = gridMinX;       gridPts[numPts].y = y;       gridPts[numPts++].z = -0.2f;       gridPts[numPts].x = gridMaxX;       gridPts[numPts].y = y;       gridPts[numPts++].z = -0.2f;       // try to add a line to the clipped grid       // find the intersection of the clipped line with the FOV sides       // this is a distance relative to the eye       float clipDist = (y - eyePtVW.z);       if ((clipDist > frontClipDist) && (clipDist < backClipDist)) {         float clipX = fovSpreadX * clipDist;         clipGridPtsVW[numClipGridPts].x = -clipX;         clipGridPtsVW[numClipGridPts].y = y;         clipGridPtsVW[numClipGridPts].z = -0.1f;         clipGridPtsVW[numClipGridPts + 1].x = clipX;         clipGridPtsVW[numClipGridPts + 1].y = y;         clipGridPtsVW[numClipGridPts + 1].z = -0.1f;         numClipGridPts += 2;       }     }     LineArray horizLa = new LineArray(numPts, LineArray.COORDINATES);     horizLa.setCoordinates(0, gridPts, 0, numPts);     Shape3D horizShape = new Shape3D(horizLa, bgApp);     objTrans.addChild(horizShape);     // draw the clipped grid.     if (numClipGridPts > 0) {       LineArray clipLa = new LineArray(numClipGridPts,           LineArray.COORDINATES);       clipLa.setCoordinates(0, clipGridPtsVW, 0, numClipGridPts);       Appearance clipGridApp = new Appearance();       ColoringAttributes clipCa = new ColoringAttributes(black,           ColoringAttributes.SHADE_FLAT);       clipGridApp.setColoringAttributes(clipCa);       LineAttributes clipGridLa = new LineAttributes();       Shape3D clipShape = new Shape3D(clipLa, clipGridApp);       objTrans.addChild(clipShape);     }     // set up the coordinate system     Appearance coordSysApp = new Appearance();     LineAttributes coordSysLa = new LineAttributes();     coordSysLa.setLineWidth(3.0f);     coordSysApp.setLineAttributes(coordSysLa);     ColoringAttributes coordSysCa = new ColoringAttributes(grey,         ColoringAttributes.SHADE_FLAT);     coordSysApp.setColoringAttributes(coordSysCa);     Point3f[] coordSysPts = new Point3f[4];     coordSysPts[0] = new Point3f(gridMinX, 0, -0.5f);     coordSysPts[1] = new Point3f(gridMaxX, 0, -0.5f);     coordSysPts[2] = new Point3f(0, gridMinY, -0.5f);     coordSysPts[3] = new Point3f(0, gridMaxY, -0.5f);     LineArray coordSysLines = new LineArray(4, LineArray.COORDINATES);     coordSysLines.setCoordinates(0, coordSysPts);     Shape3D coordSysShape = new Shape3D(coordSysLines, coordSysApp);     objTrans.addChild(coordSysShape);     // set up the circle     Appearance circleApp = new Appearance();     ColoringAttributes circleCa = new ColoringAttributes();     circleCa.setColor(red);     circleApp.setColoringAttributes(circleCa);     PolygonAttributes pa = new PolygonAttributes();     pa.setCullFace(PolygonAttributes.CULL_NONE);     circleApp.setPolygonAttributes(pa);     int step = 360 / (numCirclePts - 1);     for (int deg = 0; deg < 360; deg += step) {       double angle = Math.toRadians(deg);       circlePtsVW[deg / 10].x = sphereRadius * (float) Math.sin(angle);       circlePtsVW[deg / 10].y = sphereRadius * (float) Math.cos(angle);       circlePtsVW[deg / 10].z = -0.3f;     }     circlePtsVW[numCirclePts - 1].set(circlePtsVW[0]);     int[] lineStripLength = new int[1];     lineStripLength[0] = numCirclePts;     //LineStripArray circleLineStrip = new LineStripArray(numCirclePts,     //        LineArray.COORDINATES, lineStripLength);     TriangleFanArray circleLineStrip = new TriangleFanArray(numCirclePts,         LineArray.COORDINATES, lineStripLength);     circleLineStrip.setCoordinates(0, circlePtsVW);     Shape3D circleShape = new Shape3D(circleLineStrip, circleApp);     objTrans.addChild(circleShape);     return objRoot;   }   // return the closest multiple of step less than value   float gridStepFloor(float value, float step) {     return (float) (step * (Math.floor(value / step)));   }   // return the closest multiple of step greater than value   float gridStepCeil(float value, float step) {     return (float) (step * (Math.ceil(value / step)));   }   public BranchGroup createProjViewSG() {     // Create the root of the branch graph     BranchGroup objRoot = new BranchGroup();     objRoot.setCapability(BranchGroup.ALLOW_DETACH);     // setup a transform group to hold the scaled scene     TransformGroup objTrans = new TransformGroup();     Transform3D scale = new Transform3D();     scale.set(0.9);     objTrans.setTransform(scale);     objRoot.addChild(objTrans);     // create the clip limits line     Point3f[] cpPoints = new Point3f[5];     cpPoints[0] = new Point3f(-1, -1, 0.1f);     cpPoints[1] = new Point3f(1, -1, 0.1f);     cpPoints[2] = new Point3f(1, 1, 0.1f);     cpPoints[3] = new Point3f(-1, 1, 0.1f);     cpPoints[4] = cpPoints[0];     int[] cpLength = new int[1];     cpLength[0] = 5;     LineStripArray cpLines = new LineStripArray(5, LineArray.COORDINATES,         cpLength);     cpLines.setCoordinates(0, cpPoints);     Appearance cpApp = new Appearance();     ColoringAttributes cpCa = new ColoringAttributes(blue,         ColoringAttributes.SHADE_FLAT);     cpApp.setColoringAttributes(cpCa);     LineAttributes cpLa = new LineAttributes();     Shape3D cpShape = new Shape3D(cpLines, cpApp);     objTrans.addChild(cpShape);     // transform and render the clip grid points     updateProjTrans();     if (numClipGridPts > 0) {       // transform the clipGridPts       for (int i = 0; i < numClipGridPts; i++) {         projectPoint(clipGridPtsVW[i], clipGridPtsProj[i]);       }       LineArray clipLn = new LineArray(numClipGridPts,           LineArray.COORDINATES);       clipLn.setCoordinates(0, clipGridPtsProj, 0, numClipGridPts);       Appearance clipGridApp = new Appearance();       ColoringAttributes clipCa = new ColoringAttributes(black,           ColoringAttributes.SHADE_FLAT);       clipGridApp.setColoringAttributes(clipCa);       LineAttributes clipLa = new LineAttributes();       Shape3D clipShape = new Shape3D(clipLn, clipGridApp);       objTrans.addChild(clipShape);     }     // set up the circle     Appearance circleApp = new Appearance();     ColoringAttributes circleCa = new ColoringAttributes();     circleCa.setColor(red);     circleApp.setColoringAttributes(circleCa);     PolygonAttributes pa = new PolygonAttributes();     pa.setCullFace(PolygonAttributes.CULL_NONE);     circleApp.setPolygonAttributes(pa);     // transform the circlePts     for (int i = 0; i < numCirclePts; i++) {       projectPoint(circlePtsVW[i], circlePtsProj[i]);     }     int[] lineStripLength = new int[1];     lineStripLength[0] = numCirclePts;     //LineStripArray circleLineStrip = new LineStripArray(numCirclePts,     //        LineArray.COORDINATES, lineStripLength);     TriangleFanArray circleLineStrip = new TriangleFanArray(numCirclePts,         LineArray.COORDINATES, lineStripLength);     circleLineStrip.setCoordinates(0, circlePtsProj);     Shape3D circleShape = new Shape3D(circleLineStrip, circleApp);     objTrans.addChild(circleShape);     return objRoot;   }   void projectPoint(Point3f ptVW, Point3f ptProj) {     // handle the VW having y and z switched     // TODO: fix viewpoint for views     projPt.x = ptVW.x;     projPt.y = ptVW.z;     projPt.z = -ptVW.y;     projPt.w = 1.0f;     projPt.z += eyePtVW.z; // TODO: move to projTrans     //System.out.println("projPtVW = (" +     //  projPt.x + ", " +     //  projPt.y + ", " +     //  projPt.z + ")");     projTrans.transform(projPt);     projPt.x /= projPt.w;     projPt.y /= projPt.w;     projPt.z /= projPt.w;     //System.out.println("projPt = (" +     //  projPt.x + ", " +     //  projPt.y + ", " +     //  projPt.z + ")");     ptProj.x = (float) projPt.x;     ptProj.y = (float) projPt.z;     ptProj.z = (float) projPt.y;   }   /**    * Calculates the projection transform specified by the field of view and    * clip distances specified by the view.    */   public void updateProjTrans() {     int projType = view.getProjectionPolicy();     if (projType == View.PARALLEL_PROJECTION) {       //System.out.println("PARALLEL_PROJECTION");       projTrans.setIdentity();       return;     }     //System.out.println("PERSPECTIVE_PROJECTION");     // figure out the perspective transform from the view     double fov = view.getFieldOfView();     // n = near clip     double n = frontClipDist;     // f = far clip     double f = backClipDist;     //System.out.println("n = " + nf.format(n) + " f = " + nf.format(f));     // Create a matrix using coefficents derived from the OpenGL     // glFrustum() man page. This assumes the eye point is a 0,0,0,     // the front clip plane is a z = -n, the back clip plane is at     // z = -f and that the front clip plane intersects the FOV so that     // -1 <= X,Y <= 1 at the front plane (the last assumption may not     // be true, so we'll scale later).     Matrix4d matrix = new Matrix4d();     matrix.m00 = n;     matrix.m11 = n;     matrix.m22 = -(f + n) / (f - n);     matrix.m23 = -2 * f * n / (f - n);     matrix.m32 = -1;     //System.out.println("matrix = " + matrix);     // This is the distance where the FOV maps to a -1 to 1 area in X and Y     double d = 1 / Math.tan(fov / 2);     //System.out.println("n = " + nf.format(n) + " f = " + nf.format(f) +     //  " d = " + nf.format(d));     // this is a scaling ratio to make the OpenGL glFrustum() matrix     // elements work with with the J3D matrix. It compensates for the     // front clip plane not being at the FOV distance (the OpenGL     // matrix expects n == d).     double scale = n / d;     //System.out.println("scale = " + nf.format(scale));     // scale the elements of the matrix     //matrix.m00 *= 1.0/scale;     //matrix.m11 *= 1.0/scale;     matrix.m22 *= scale;     matrix.m23 *= scale;     matrix.m32 *= scale;     // set the Transform3D     projTrans.set(matrix);     //System.out.println("projTrans = " + projTrans);   }   /* TODO: use a behavior post to avoid the flicker when these change */   void updateViewWindows() {     BranchGroup newUlScene = createVWorldViewSG();     urScene.detach();     urUniverse.addBranchGraph(newUlScene);     urScene = newUlScene;     BranchGroup newLlScene = createProjViewSG();     lrScene.detach();     lrUniverse.addBranchGraph(newLlScene);     lrScene = newLlScene;   }   public ViewProj() {     this(true);   }   public ViewProj(boolean isApplication) {     this.isApplication = isApplication;   }   public void init() {     setLayout(new BorderLayout());     nf = NumberFormat.getInstance();     nf.setMaximumFractionDigits(3);     GraphicsConfiguration config = SimpleUniverse         .getPreferredConfiguration();     JPanel canvasPanel = new JPanel();     GridBagLayout gridbag = new GridBagLayout();     canvasPanel.setLayout(gridbag);     canvas = new Canvas3D(config);     canvas.setSize(400, 400);     GridBagConstraints constraints = new GridBagConstraints();     constraints.gridx = 0;     constraints.gridy = 0;     constraints.gridwidth = 2;     constraints.gridheight = 2;     constraints.insets = new Insets(5, 5, 5, 5);     constraints.fill = GridBagConstraints.BOTH;     gridbag.setConstraints(canvas, constraints);     canvasPanel.add(canvas);     constraints.fill = GridBagConstraints.REMAINDER;     constraints.gridwidth = 1;     constraints.gridheight = 1;     constraints.gridx = 2;     constraints.gridy = 0;     urCanvas = new Canvas3D(config);     urCanvas.setSize(200, 200);     gridbag.setConstraints(urCanvas, constraints);     canvasPanel.add(urCanvas);     constraints.gridx = 2;     constraints.gridy = 1;     lrCanvas = new Canvas3D(config);     lrCanvas.setSize(200, 200);     gridbag.setConstraints(lrCanvas, constraints);     canvasPanel.add(lrCanvas);     add(canvasPanel, BorderLayout.NORTH);     SimpleUniverse u = new SimpleUniverse(canvas);     urUniverse = new SimpleUniverse(urCanvas);     lrUniverse = new SimpleUniverse(lrCanvas);     if (isApplication) {       offScreenCanvas = new OffScreenCanvas3D(config, true);       // set the size of the off-screen canvas based on a scale       // of the on-screen size       Screen3D sOn = canvas.getScreen3D();       Screen3D sOff = offScreenCanvas.getScreen3D();       Dimension dim = sOn.getSize();       dim.width *= offScreenScale;       dim.height *= offScreenScale;       sOff.setSize(dim);       sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()           * offScreenScale);       sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()           * offScreenScale);       // attach the offscreen canvas to the view       u.getViewer().getView().addCanvas3D(offScreenCanvas);       urOffScreenCanvas = new OffScreenCanvas3D(config, true);       // set the size of the off-screen canvas based on a scale       // of the on-screen size       sOn = urCanvas.getScreen3D();       sOff = urOffScreenCanvas.getScreen3D();       dim = sOn.getSize();       dim.width *= urOffScreenScale;       dim.height *= urOffScreenScale;       sOff.setSize(dim);       sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()           * urOffScreenScale);       sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()           * urOffScreenScale);       // attach the offscreen canvas to the view       urUniverse.getViewer().getView().addCanvas3D(urOffScreenCanvas);       lrOffScreenCanvas = new OffScreenCanvas3D(config, true);       // set the size of the off-screen canvas based on a scale       // of the on-screen size       sOn = lrCanvas.getScreen3D();       sOff = lrOffScreenCanvas.getScreen3D();       dim = sOn.getSize();       dim.width *= lrOffScreenScale;       dim.height *= lrOffScreenScale;       sOff.setSize(dim);       sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()           * lrOffScreenScale);       sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()           * lrOffScreenScale);       // attach the offscreen canvas to the view       lrUniverse.getViewer().getView().addCanvas3D(lrOffScreenCanvas);     }     // Create a simple scene and attach it to the virtual universe     BranchGroup scene = createSceneGraph();     // This will move the ViewPlatform back a bit so the     // objects in the scene can be viewed.     viewingPlatform = u.getViewingPlatform();     viewingPlatform.setNominalViewingTransform();     view = u.getViewer().getView();     view.setFrontClipPolicy(View.VIRTUAL_EYE);     view.setBackClipPolicy(View.VIRTUAL_EYE);     view.setFrontClipDistance(frontClipDist);     view.setBackClipDistance(backClipDist);     u.addBranchGraph(scene);     // init the clipGridPts arrays     for (int i = 0; i < maxClipGridPts; i++) {       clipGridPtsVW[i] = new Point3f();       clipGridPtsProj[i] = new Point3f();     }     // init the circlePts arrays     for (int i = 0; i < numCirclePts; i++) {       circlePtsVW[i] = new Point3f();       circlePtsProj[i] = new Point3f();     }     // setup the ur canvas     urScene = createVWorldViewSG();     // This will move the ViewPlatform back a bit so the     // objects in the scene can be viewed.     urUniverse.getViewingPlatform().setNominalViewingTransform();     View urView = urUniverse.getViewer().getView();     urView.setProjectionPolicy(View.PARALLEL_PROJECTION);     urUniverse.addBranchGraph(urScene);     // set up the background on a separate BG so that it can stay there     // when we replace the scene SG     Background urBgWhite = new Background(white);     urBgWhite.setApplicationBounds(infiniteBounds);     BranchGroup urBackBG = new BranchGroup();     urBackBG.addChild(urBgWhite);     urUniverse.addBranchGraph(urBackBG);     // setup the lr canvas     lrScene = createProjViewSG();     // This will move the ViewPlatform back a bit so the     // objects in the scene can be viewed.     lrUniverse.getViewingPlatform().setNominalViewingTransform();     View lrView = lrUniverse.getViewer().getView();     lrView.setProjectionPolicy(View.PARALLEL_PROJECTION);     lrUniverse.addBranchGraph(lrScene);     // set up the background on a separate BG so that it can stay there     // when we replace the scene SG     Background lrBgWhite = new Background(white);     lrBgWhite.setApplicationBounds(infiniteBounds);     BranchGroup lrBackBG = new BranchGroup();     lrBackBG.addChild(lrBgWhite);     lrUniverse.addBranchGraph(lrBackBG);     // set up the sliders     JPanel guiPanel = new JPanel();     guiPanel.setLayout(new GridLayout(0, 2));     FloatLabelJSlider dynamicSlider = new FloatLabelJSlider(         "Dynamic Offset", 0.1f, 0.0f, 2.0f, dynamicOffset);     dynamicSlider.addFloatListener(new FloatListener() {       public void floatChanged(FloatEvent e) {         dynamicOffset = e.getValue();         solidPa.setPolygonOffsetFactor(dynamicOffset);       }     });     guiPanel.add(dynamicSlider);     LogFloatLabelJSlider staticSlider = new LogFloatLabelJSlider(         "Static Offset", 0.1f, 10000.0f, staticOffset);     staticSlider.addFloatListener(new FloatListener() {       public void floatChanged(FloatEvent e) {         staticOffset = e.getValue();         solidPa.setPolygonOffset(staticOffset);       }     });     guiPanel.add(staticSlider);     // These are declared final here so they can be changed by the     // listener routines below.     LogFloatLabelJSlider frontClipSlider = new LogFloatLabelJSlider(         "Front Clip Distance", 0.001f, 10.0f, frontClipDist);     final LogFloatLabelJSlider backClipSlider = new LogFloatLabelJSlider(         "Back Clip Distance", 1.0f, 10000.0f, backClipDist);     final LogFloatLabelJSlider backClipRatioSlider = new LogFloatLabelJSlider(         "Back Clip Ratio", 1.0f, 10000.0f, backClipRatio);     frontClipSlider.addFloatListener(new FloatListener() {       public void floatChanged(FloatEvent e) {         frontClipDist = e.getValue();         view.setFrontClipDistance(frontClipDist);         backClipRatio = backClipDist / frontClipDist;         backClipRatioSlider.setValue(backClipRatio);         updateViewWindows();       }     });     guiPanel.add(frontClipSlider);     backClipSlider.addFloatListener(new FloatListener() {       public void floatChanged(FloatEvent e) {         backClipDist = e.getValue();         backClipRatio = backClipDist / frontClipDist;         backClipRatioSlider.setValue(backClipRatio);         view.setBackClipDistance(backClipDist);         updateViewWindows();       }     });     guiPanel.add(backClipSlider);     backClipRatioSlider.addFloatListener(new FloatListener() {       public void floatChanged(FloatEvent e) {         backClipRatio = e.getValue();         backClipDist = backClipRatio * frontClipDist;         backClipSlider.setValue(backClipDist);         updateViewWindows();       }     });     guiPanel.add(backClipRatioSlider);     FloatLabelJSlider innerSphereSlider = new FloatLabelJSlider(         "Inner Sphere Scale", 0.001f, 0.90f, 1.0f, innerScale);     innerSphereSlider.addFloatListener(new FloatListener() {       public void floatChanged(FloatEvent e) {         innerScale = e.getValue();         updateInnerScale();       }     });     guiPanel.add(innerSphereSlider);     JButton mainSnap = new JButton(snapImageString);     mainSnap.addActionListener(new ActionListener() {       public void actionPerformed(ActionEvent e) {         Point loc = canvas.getLocationOnScreen();         offScreenCanvas.setOffScreenLocation(loc);         Dimension dim = canvas.getSize();         dim.width *= offScreenScale;         dim.height *= offScreenScale;         nf.setMinimumIntegerDigits(3);         offScreenCanvas.snapImageFile(outFileBase             + nf.format(outFileSeq++), dim.width, dim.height);         nf.setMinimumIntegerDigits(0);       }     });     guiPanel.add(mainSnap);     JButton urSnap = new JButton(urSnapImageString);     urSnap.addActionListener(new ActionListener() {       public void actionPerformed(ActionEvent e) {         System.out.println("Snap UR");         Point loc = urCanvas.getLocationOnScreen();         urOffScreenCanvas.setOffScreenLocation(loc);         Dimension dim = urCanvas.getSize();         dim.width *= urOffScreenScale;         dim.height *= urOffScreenScale;         nf.setMinimumIntegerDigits(3);         urOffScreenCanvas.snapImageFile(urOutFileBase             + nf.format(urOutFileSeq++), dim.width, dim.height);         nf.setMinimumIntegerDigits(0);       }     });     guiPanel.add(urSnap);     JButton lrSnap = new JButton(lrSnapImageString);     lrSnap.addActionListener(new ActionListener() {       public void actionPerformed(ActionEvent e) {         System.out.println("Snap LR");         Point loc = lrCanvas.getLocationOnScreen();         lrOffScreenCanvas.setOffScreenLocation(loc);         Dimension dim = lrCanvas.getSize();         dim.width *= lrOffScreenScale;         dim.height *= lrOffScreenScale;         nf.setMinimumIntegerDigits(3);         lrOffScreenCanvas.snapImageFile(lrOutFileBase             + nf.format(lrOutFileSeq++), dim.width, dim.height);         nf.setMinimumIntegerDigits(0);       }     });     guiPanel.add(lrSnap);     add(guiPanel, BorderLayout.SOUTH);   }   //   // The following allows ViewProj to be run as an application   // as well as an applet   //   public static void main(String[] args) {     new MainFrame(new ViewProj(true), 700, 600);   } } interface Java3DExplorerConstants {   // colors   static Color3f black = new Color3f(0.0f, 0.0f, 0.0f);   static Color3f red = new Color3f(1.0f, 0.0f, 0.0f);   static Color3f green = new Color3f(0.0f, 1.0f, 0.0f);   static Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);   static Color3f skyBlue = new Color3f(0.6f, 0.7f, 0.9f);   static Color3f cyan = new Color3f(0.0f, 1.0f, 1.0f);   static Color3f magenta = new Color3f(1.0f, 0.0f, 1.0f);   static Color3f yellow = new Color3f(1.0f, 1.0f, 0.0f);   static Color3f brightWhite = new Color3f(1.0f, 1.5f, 1.5f);   static Color3f white = new Color3f(1.0f, 1.0f, 1.0f);   static Color3f darkGrey = new Color3f(0.15f, 0.15f, 0.15f);   static Color3f medGrey = new Color3f(0.3f, 0.3f, 0.3f);   static Color3f grey = new Color3f(0.5f, 0.5f, 0.5f);   static Color3f lightGrey = new Color3f(0.75f, 0.75f, 0.75f);   // infinite bounding region, used to make env nodes active everywhere   BoundingSphere infiniteBounds = new BoundingSphere(new Point3d(),       Double.MAX_VALUE);   // common values   static final String nicestString = "NICEST";   static final String fastestString = "FASTEST";   static final String antiAliasString = "Anti-Aliasing";   static final String noneString = "NONE";   // light type constants   static int LIGHT_AMBIENT = 1;   static int LIGHT_DIRECTIONAL = 2;   static int LIGHT_POSITIONAL = 3;   static int LIGHT_SPOT = 4;   // screen capture constants   static final int USE_COLOR = 1;   static final int USE_BLACK_AND_WHITE = 2;   // number formatter   NumberFormat nf = NumberFormat.getInstance(); } class OffScreenCanvas3D extends Canvas3D {   OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration,       boolean offScreen) {     super(graphicsConfiguration, offScreen);   }   private BufferedImage doRender(int width, int height) {     BufferedImage bImage = new BufferedImage(width, height,         BufferedImage.TYPE_INT_RGB);     ImageComponent2D buffer = new ImageComponent2D(         ImageComponent.FORMAT_RGB, bImage);     //buffer.setYUp(true);     setOffScreenBuffer(buffer);     renderOffScreenBuffer();     waitForOffScreenRendering();     bImage = getOffScreenBuffer().getImage();     return bImage;   }   void snapImageFile(String filename, int width, int height) {     BufferedImage bImage = doRender(width, height);     /*      * JAI: RenderedImage fImage = JAI.create("format", bImage,      * DataBuffer.TYPE_BYTE); JAI.create("filestore", fImage, filename +      * ".tif", "tiff", null);      */     /* No JAI: */     try {       FileOutputStream fos = new FileOutputStream(filename + ".jpg");       BufferedOutputStream bos = new BufferedOutputStream(fos);       JPEGImageEncoder jie = JPEGCodec.createJPEGEncoder(bos);       JPEGEncodeParam param = jie.getDefaultJPEGEncodeParam(bImage);       param.setQuality(1.0f, true);       jie.setJPEGEncodeParam(param);       jie.encode(bImage);       bos.flush();       fos.close();     } catch (Exception e) {       System.out.println(e);     }   } } class FloatLabelJSlider extends JPanel implements ChangeListener,     Java3DExplorerConstants {   JSlider slider;   JLabel valueLabel;   Vector listeners = new Vector();   float min, max, resolution, current, scale;   int minInt, maxInt, curInt;;   int intDigits, fractDigits;   float minResolution = 0.001f;   // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital   // 0.5   FloatLabelJSlider(String name) {     this(name, 0.1f, 0.0f, 1.0f, 0.5f);   }   FloatLabelJSlider(String name, float resolution, float min, float max,       float current) {     this.resolution = resolution;     this.min = min;     this.max = max;     this.current = current;     if (resolution < minResolution) {       resolution = minResolution;     }     // round scale to nearest integer fraction. i.e. 0.3 => 1/3 = 0.33     scale = (float) Math.round(1.0f / resolution);     resolution = 1.0f / scale;     // get the integer versions of max, min, current     minInt = Math.round(min * scale);     maxInt = Math.round(max * scale);     curInt = Math.round(current * scale);     // sliders use integers, so scale our floating point value by "scale"     // to make each slider "notch" be "resolution". We will scale the     // value down by "scale" when we get the event.     slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);     slider.addChangeListener(this);     valueLabel = new JLabel(" ");     // set the initial value label     setLabelString();     // add min and max labels to the slider     Hashtable labelTable = new Hashtable();     labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));     labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));     slider.setLabelTable(labelTable);     slider.setPaintLabels(true);     /* layout to align left */     setLayout(new BorderLayout());     Box box = new Box(BoxLayout.X_AXIS);     add(box, BorderLayout.WEST);     box.add(new JLabel(name));     box.add(slider);     box.add(valueLabel);   }   public void setMinorTickSpacing(float spacing) {     int intSpacing = Math.round(spacing * scale);     slider.setMinorTickSpacing(intSpacing);   }   public void setMajorTickSpacing(float spacing) {     int intSpacing = Math.round(spacing * scale);     slider.setMajorTickSpacing(intSpacing);   }   public void setPaintTicks(boolean paint) {     slider.setPaintTicks(paint);   }   public void addFloatListener(FloatListener listener) {     listeners.add(listener);   }   public void removeFloatListener(FloatListener listener) {     listeners.remove(listener);   }   public void stateChanged(ChangeEvent e) {     JSlider source = (JSlider) e.getSource();     // get the event type, set the corresponding value.     // Sliders use integers, handle floating point values by scaling the     // values by "scale" to allow settings at "resolution" intervals.     // Divide by "scale" to get back to the real value.     curInt = source.getValue();     current = curInt / scale;     valueChanged();   }   public void setValue(float newValue) {     boolean changed = (newValue != current);     current = newValue;     if (changed) {       valueChanged();     }   }   private void valueChanged() {     // update the label     setLabelString();     // notify the listeners     FloatEvent event = new FloatEvent(this, current);     for (Enumeration e = listeners.elements(); e.hasMoreElements();) {       FloatListener listener = (FloatListener) e.nextElement();       listener.floatChanged(event);     }   }   void setLabelString() {     // Need to muck around to try to make sure that the width of the label     // is wide enough for the largest value. Pad the string     // be large enough to hold the largest value.     int pad = 5; // fudge to make up for variable width fonts     float maxVal = Math.max(Math.abs(min), Math.abs(max));     intDigits = Math.round((float) (Math.log(maxVal) / Math.log(10))) + pad;     if (min < 0) {       intDigits++; // add one for the '-'     }     // fractDigits is num digits of resolution for fraction. Use base 10 log     // of scale, rounded up, + 2.     fractDigits = (int) Math.ceil((Math.log(scale) / Math.log(10)));     nf.setMinimumFractionDigits(fractDigits);     nf.setMaximumFractionDigits(fractDigits);     String value = nf.format(current);     while (value.length() < (intDigits + fractDigits)) {       value = value + "  ";     }     valueLabel.setText(value);   } } class FloatEvent extends EventObject {   float value;   FloatEvent(Object source, float newValue) {     super(source);     value = newValue;   }   float getValue() {     return value;   } } interface FloatListener extends EventListener {   void floatChanged(FloatEvent e); } class LogFloatLabelJSlider extends JPanel implements ChangeListener,     Java3DExplorerConstants {   JSlider slider;   JLabel valueLabel;   Vector listeners = new Vector();   float min, max, resolution, current, scale;   double minLog, maxLog, curLog;   int minInt, maxInt, curInt;;   int intDigits, fractDigits;   NumberFormat nf = NumberFormat.getInstance();   float minResolution = 0.001f;   double logBase = Math.log(10);   // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital   // 0.5   LogFloatLabelJSlider(String name) {     this(name, 0.1f, 100.0f, 10.0f);   }   LogFloatLabelJSlider(String name, float min, float max, float current) {     this.resolution = resolution;     this.min = min;     this.max = max;     this.current = current;     if (resolution < minResolution) {       resolution = minResolution;     }     minLog = log10(min);     maxLog = log10(max);     curLog = log10(current);     // resolution is 100 steps from min to max     scale = 100.0f;     resolution = 1.0f / scale;     // get the integer versions of max, min, current     minInt = (int) Math.round(minLog * scale);     maxInt = (int) Math.round(maxLog * scale);     curInt = (int) Math.round(curLog * scale);     slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);     slider.addChangeListener(this);     valueLabel = new JLabel(" ");     // Need to muck around to make sure that the width of the label     // is wide enough for the largest value. Pad the initial string     // be large enough to hold the largest value.     int pad = 5; // fudge to make up for variable width fonts     intDigits = (int) Math.ceil(maxLog) + pad;     if (min < 0) {       intDigits++; // add one for the '-'     }     if (minLog < 0) {       fractDigits = (int) Math.ceil(-minLog);     } else {       fractDigits = 0;     }     nf.setMinimumFractionDigits(fractDigits);     nf.setMaximumFractionDigits(fractDigits);     String value = nf.format(current);     while (value.length() < (intDigits + fractDigits)) {       value = value + " ";     }     valueLabel.setText(value);     // add min and max labels to the slider     Hashtable labelTable = new Hashtable();     labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));     labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));     slider.setLabelTable(labelTable);     slider.setPaintLabels(true);     // layout to align left     setLayout(new BorderLayout());     Box box = new Box(BoxLayout.X_AXIS);     add(box, BorderLayout.WEST);     box.add(new JLabel(name));     box.add(slider);     box.add(valueLabel);   }   public void setMinorTickSpacing(float spacing) {     int intSpacing = Math.round(spacing * scale);     slider.setMinorTickSpacing(intSpacing);   }   public void setMajorTickSpacing(float spacing) {     int intSpacing = Math.round(spacing * scale);     slider.setMajorTickSpacing(intSpacing);   }   public void setPaintTicks(boolean paint) {     slider.setPaintTicks(paint);   }   public void addFloatListener(FloatListener listener) {     listeners.add(listener);   }   public void removeFloatListener(FloatListener listener) {     listeners.remove(listener);   }   public void stateChanged(ChangeEvent e) {     JSlider source = (JSlider) e.getSource();     curInt = source.getValue();     curLog = curInt / scale;     current = (float) exp10(curLog);     valueChanged();   }   public void setValue(float newValue) {     boolean changed = (newValue != current);     current = newValue;     if (changed) {       valueChanged();     }   }   private void valueChanged() {     String value = nf.format(current);     valueLabel.setText(value);     // notify the listeners     FloatEvent event = new FloatEvent(this, current);     for (Enumeration e = listeners.elements(); e.hasMoreElements();) {       FloatListener listener = (FloatListener) e.nextElement();       listener.floatChanged(event);     }   }   double log10(double value) {     return Math.log(value) / logBase;   }   double exp10(double value) {     return Math.exp(value * logBase);   } }