package org.freehep.j3d.plot; import java.awt.Font; import javax.media.j3d.*; import javax.vecmath.*; public class AxisBuilder { /** * Constructs an axis * @author Joy Kyriakopulos (joyk@fnal.gov) * @version $Id: AxisBuilder.java 8584 2006-08-10 23:06:37Z duns $ */ AxisBuilder() { // build all the major components of the Axis, keeping the important // parts in member variables so we can modify them later. label = new Text3D(); // The Axis label label.setAlignment(label.ALIGN_CENTER); Point3f pos = new Point3f(scale/2,-labelOffSet,0); label.setPosition(pos); label.setCapability(label.ALLOW_FONT3D_WRITE); label.setCapability(label.ALLOW_STRING_WRITE); axis = new Shape3D(); // The axis and tick marks. axis.setCapability(axis.ALLOW_GEOMETRY_WRITE); // We can create the tick labels yet, since we don't know how many there // will be, but we can make a group to hold them. ticks = new BranchGroup(); ticks.setCapability(ticks.ALLOW_CHILDREN_READ); ticks.setCapability(ticks.ALLOW_CHILDREN_WRITE); ticks.setCapability(ticks.ALLOW_DETACH); // Group the components together mainGroup = new TransformGroup(); mainGroup.setCapability(ticks.ALLOW_CHILDREN_WRITE); mainGroup.setCapability(ticks.ALLOW_CHILDREN_EXTEND); mainGroup.addChild(new Shape3D(label)); mainGroup.addChild(axis); mainGroup.addChild(ticks); // Set up a BulletinBoard behaviour to keep the axis oriented // towards the user. mainGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); mainGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); Billboard bboard = new Billboard( mainGroup ); mainGroup.addChild( bboard ); bboard.setSchedulingBounds( new BoundingSphere(new Point3d(0,0,0),10)); bboard.setAlignmentAxis( 1.0f, 0.0f, 0.0f); } public String getLabel() { return labelText; } public void setLabel(String label) { labelText = label; } public Font3D getLabelFont() { return labelFont; } public void setLabelFont(Font3D font) { labelFont = font; } public Font3D getTickFont() { return tickFont; } public void setTickFont(Font3D font) { tickFont = font; } /** * Tick labels and locations (positions) can be set * by the caller or calculated and set by the * createLabelsNTicks method as a convenience. */ public double[] getTickLocations() { return tickLocations; } public void setTickLocations(double[] ticks) { tickLocations = ticks; } public String[] getTickLabels() { return tickLabels; } public void setTickLabels(String[] labels) { tickLabels = labels; } /** * Call the createLabelsNTicks method if you would like the * axisbuilder to create axis labels and tick positions for you. */ public void createLabelsNTicks(double min, double max) { AxisLabelCalculator axisCalc = new AxisLabelCalculator(); axisCalc.createNewLabels(min, max); // System.out.println("in createLabelsNTicks: min = " + min + ", max = " + max); // axisCalc.printLabels(); tickLabels = axisCalc.getLabels(); tickLocations = axisCalc.getPositions(); } /** * Call this method after setting the required axis properties, to actually * setup/modify the axis appearance. */ public void apply() { /* * Build an axis on the X axis from 0 to 256. Note the reason for the * 256 is to compensate for the giant size of the Text3D characters. * The axis is built in the XY plane. * We will use suitable translation and rotation to position it later. * * Note we currently use Text3D to draw the labels. This is because Text3D * seems somewhat easier to use. A better solution would * probably involve using Text2D, but the com.sun.j3d.utils.geometry.Text2D * class seems pretty brain dead. A better solution may be to get the source * for the Text2D class and rewrite it for our own purposes. */ int tMajor = (tickLocations.length-1)/(tickLabels.length-1); LineArray lines = new LineArray(2*tickLocations.length+2, LineArray.COORDINATES); int coordIdx = 0; // coordinate index into Axis linearray // Actual axis lines.setCoordinate(coordIdx++, new Point3d(0, 0, 0)); lines.setCoordinate(coordIdx++, new Point3d(scale, 0, 0)); // Remove the old tick labels. Note a more efficient implementation // we would keep as many as of the old labels as possible, adding/removing // new tick marks only as the number of tick marks change, and changing the // text only as necessary. For now we just do the easiest thing. // TODO: More efficient implementation. // while (ticks.numChildren() > 0) // ticks.removeChild(0); ticks.detach(); ticks = new BranchGroup(); ticks.setCapability(ticks.ALLOW_CHILDREN_READ); ticks.setCapability(ticks.ALLOW_CHILDREN_WRITE); ticks.setCapability(ticks.ALLOW_DETACH); // Rendering Ticks on Axis for(int i = 0; i < tickLocations.length; i++) { float x = (float) tickLocations[i]*scale; if (i % tMajor == 0) // Major tick mark? { lines.setCoordinate(coordIdx++, new Point3d(x, 0, 0)); lines.setCoordinate(coordIdx++, new Point3d(x, -major, 0)); // Add the tick label int nt = i/tMajor; Point3f pos = new Point3f(x,-tickOffSet,0); Text3D tickLabel = new Text3D(tickFont,tickLabels[nt],pos); tickLabel.setAlignment(tickLabel.ALIGN_CENTER); ticks.addChild(new Shape3D(tickLabel)); } else // Minor tick mark { lines.setCoordinate(coordIdx++, new Point3d(x, 0, 0)); lines.setCoordinate(coordIdx++, new Point3d(x, -minor, 0)); } } // TODO: It would be more efficient to only update the label if something has changed. mainGroup.addChild(ticks); label.setFont3D(labelFont); label.setString(labelText); axis.setGeometry(lines); } /** * Returns the node representing this Axis * Subclasses can override this method to transform this axis * to make it into an X,Y,Z axis. */ public Node getNode() { return mainGroup; } private String labelText; private Font3D labelFont = defaultLabelFont; private double[] tickLocations; private String[] tickLabels; private Font3D tickFont = defaultTickFont; private TransformGroup mainGroup; private Text3D label; private Shape3D axis; private BranchGroup ticks; protected static float scale = 256f; // See comment on apply protected static float major = 0.02f*scale; protected static float minor = 0.01f*scale; protected static float tickOffSet = 0.06f*scale; protected static float labelOffSet = 0.12f*scale; private static final Font3D defaultLabelFont = new Font3D(new Font("DIALOG",Font.BOLD,16),null); private static final Font3D defaultTickFont = new Font3D(new Font("DIALOG",Font.PLAIN,12),null); private static Color3f white = new Color3f(1,1,1); }