// // RubberBandBoxRendererJ3D.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.bom; import visad.*; import visad.java3d.*; import java.awt.event.*; import javax.swing.*; import java.util.Vector; import java.util.Enumeration; import java.rmi.*; import javax.media.j3d.*; /** RubberBandBoxRendererJ3D is the VisAD class for direct manipulation of rubber band boxes */ public class RubberBandBoxRendererJ3D extends DirectManipulationRendererJ3D { private RealType x = null; private RealType y = null; private RealTupleType xy = null; private int mouseModifiersMask = 0; private int mouseModifiersValue = 0; private BranchGroup branch = null; private BranchGroup group = null; /** this DirectManipulationRenderer is quite different - it does not render its data, but only place values into its DataReference on right mouse button release; it uses xarg and yarg to determine spatial ScalarMaps */ public RubberBandBoxRendererJ3D (RealType xarg, RealType yarg) { this(xarg, yarg, 0, 0); } /** xarg and yarg determine spatial ScalarMaps; mmm and mmv determine whehter SHIFT or CTRL keys are required - this is needed since this is a greedy DirectManipulationRenderer that will grab any right mouse click (that intersects its 2-D sub-manifold) */ public RubberBandBoxRendererJ3D (RealType xarg, RealType yarg, int mmm, int mmv) { super(); x = xarg; y = yarg; mouseModifiersMask = mmm; mouseModifiersValue = mmv; } /** don't render - just return BranchGroup for scene graph to render rectangle into */ public synchronized BranchGroup doTransform() throws VisADException, RemoteException { branch = new BranchGroup(); branch.setCapability(BranchGroup.ALLOW_DETACH); branch.setCapability(Group.ALLOW_CHILDREN_READ); branch.setCapability(Group.ALLOW_CHILDREN_WRITE); branch.setCapability(Group.ALLOW_CHILDREN_EXTEND); // check type and maps for valid direct manipulation if (!getIsDirectManipulation()) { throw new BadDirectManipulationException(getWhyNotDirect() + ": DirectManipulationRendererJ3D.doTransform"); } setBranch(branch); return branch; } /** for use in drag_direct */ private transient DataDisplayLink link = null; private transient DataReference ref = null; private transient ScalarMap xmap = null; private transient ScalarMap ymap = null; float[] default_values; /** arrays of length one for inverseScaleValues */ private float[] f = new float[1]; private float[] d = new float[1]; private float[] value = new float[2]; /** information calculated by checkDirect */ /** explanation for invalid use of DirectManipulationRenderer */ private String whyNotDirect = null; /** dimension of direct manipulation (always 2 for RubberBandBoxRendererJ3D) */ private int directManifoldDimension = 2; /** spatial DisplayTupleType other than DisplaySpatialCartesianTuple */ private DisplayTupleType tuple; private CoordinateSystem tuplecs; private int xindex = -1; private int yindex = -1; private int otherindex = -1; private float othervalue; private byte red, green, blue; // default colors private float[][] first_x; private float[][] last_x; private float[][] clast_x; private float cum_lon; /** possible values for whyNotDirect */ private final static String xandyNotMatch = "x and y spatial domains don't match"; private final static String xandyNotSpatial = "x and y must be mapped to spatial"; private boolean stop = false; public void checkDirect() throws VisADException, RemoteException { setIsDirectManipulation(false); DisplayImpl display = getDisplay(); DataDisplayLink[] Links = getLinks(); if (Links == null || Links.length == 0) { link = null; return; } link = Links[0]; ref = link.getDataReference(); default_values = link.getDefaultValues(); xmap = null; ymap = null; Vector scalar_map_vector = display.getMapVector(); Enumeration en = scalar_map_vector.elements(); while (en.hasMoreElements()) { ScalarMap map = (ScalarMap) en.nextElement(); ScalarType real = map.getScalar(); if (real.equals(x)) { DisplayRealType dreal = map.getDisplayScalar(); DisplayTupleType t = dreal.getTuple(); if (t != null && (t.equals(Display.DisplaySpatialCartesianTuple) || (t.getCoordinateSystem() != null && t.getCoordinateSystem().getReference().equals( Display.DisplaySpatialCartesianTuple)))) { xmap = map; xindex = dreal.getTupleIndex(); if (tuple == null) { tuple = t; } else if (!t.equals(tuple)) { whyNotDirect = xandyNotMatch; return; } } } if (real.equals(y)) { DisplayRealType dreal = map.getDisplayScalar(); DisplayTupleType t = dreal.getTuple(); if (t != null && (t.equals(Display.DisplaySpatialCartesianTuple) || (t.getCoordinateSystem() != null && t.getCoordinateSystem().getReference().equals( Display.DisplaySpatialCartesianTuple)))) { ymap = map; yindex = dreal.getTupleIndex(); if (tuple == null) { tuple = t; } else if (!t.equals(tuple)) { whyNotDirect = xandyNotMatch; return; } } } } if (xmap == null || ymap == null) { whyNotDirect = xandyNotSpatial; return; } xy = new RealTupleType(x, y); // get default value for other component of tuple otherindex = 3 - (xindex + yindex); DisplayRealType dreal = (DisplayRealType) tuple.getComponent(otherindex); int index = getDisplay().getDisplayScalarIndex(dreal); othervalue = (index > 0) ? default_values[index] : (float) dreal.getDefaultValue(); // get default colors index = getDisplay().getDisplayScalarIndex(Display.Red); float v = (index > 0) ? default_values[index] : (float) Display.Red.getDefaultValue(); red = ShadowType.floatToByte(v); index = getDisplay().getDisplayScalarIndex(Display.Green); v = (index > 0) ? default_values[index] : (float) Display.Green.getDefaultValue(); green = ShadowType.floatToByte(v); index = getDisplay().getDisplayScalarIndex(Display.Blue); v = (index > 0) ? default_values[index] : (float) Display.Blue.getDefaultValue(); blue = ShadowType.floatToByte(v); if (Display.DisplaySpatialCartesianTuple.equals(tuple)) { tuple = null; tuplecs = null; } else { tuplecs = tuple.getCoordinateSystem(); } directManifoldDimension = 2; setIsDirectManipulation(true); } private int getDirectManifoldDimension() { return directManifoldDimension; } public String getWhyNotDirect() { return whyNotDirect; } public void addPoint(float[] x) throws VisADException { // may need to do this for performance } // methods customized from DataRenderer: public CoordinateSystem getDisplayCoordinateSystem() { return tuplecs; } /** set spatialValues from ShadowType.doTransform */ public synchronized void setSpatialValues(float[][] spatial_values) { // do nothing } /** check if ray intersects sub-manifold */ public synchronized float checkClose(double[] origin, double[] direction) { int mouseModifiers = getLastMouseModifiers(); if ((mouseModifiers & mouseModifiersMask) != mouseModifiersValue) { return Float.MAX_VALUE; } try { float r = findRayManifoldIntersection(true, origin, direction, tuple, otherindex, othervalue); if (r == r) { return 0.0f; } else { return Float.MAX_VALUE; } } catch (VisADException ex) { return Float.MAX_VALUE; } } /** mouse button released, ending direct manipulation */ public synchronized void release_direct() { // set data in ref if (group != null) group.detach(); group = null; try { float[][] samples = new float[2][2]; f[0] = first_x[xindex][0]; d = xmap.inverseScaleValues(f); samples[0][0] = (float) d[0]; f[0] = first_x[yindex][0]; d = ymap.inverseScaleValues(f); samples[1][0] = (float) d[0]; f[0] = last_x[xindex][0]; d = xmap.inverseScaleValues(f); samples[0][1] = (float) d[0]; f[0] = last_x[yindex][0]; d = ymap.inverseScaleValues(f); samples[1][1] = (float) d[0]; Gridded2DSet set = new Gridded2DSet(xy, samples, 2); ref.setData(set); link.clearData(); } // end try catch (VisADException e) { // do nothing System.out.println("release_direct " + e); e.printStackTrace(); } catch (RemoteException e) { // do nothing System.out.println("release_direct " + e); e.printStackTrace(); } } public void stop_direct() { stop = true; } private static final int EDGE = 20; private static final float EPS = 0.005f; public synchronized void drag_direct(VisADRay ray, boolean first, int mouseModifiers) { if (ref == null) return; if (first) { stop = false; } else { if (stop) return; } double[] origin = ray.position; double[] direction = ray.vector; try { float r = findRayManifoldIntersection(true, origin, direction, tuple, otherindex, othervalue); if (r != r) { if (group != null) group.detach(); return; } float[][] xx = {{(float) (origin[0] + r * direction[0])}, {(float) (origin[1] + r * direction[1])}, {(float) (origin[2] + r * direction[2])}}; if (tuple != null) xx = tuplecs.fromReference(xx); if (first) { first_x = xx; cum_lon = 0.0f; } else if (Display.DisplaySpatialSphericalTuple.equals(tuple)) { float diff = xx[1][0] - clast_x[1][0]; if (diff > 180.0f) diff -= 360.0f; else if (diff < -180.0f) diff += 360.0f; cum_lon += diff; if (cum_lon > 360.0f) cum_lon -= 360.0f; else if (cum_lon < -360.0f) cum_lon += 360.0f; } clast_x = xx; Vector vect = new Vector(); f[0] = xx[xindex][0]; d = xmap.inverseScaleValues(f); // WLH 31 Aug 2000 Real rr = new Real(x, d[0]); Unit overrideUnit = xmap.getOverrideUnit(); Unit rtunit = x.getDefaultUnit(); // units not part of Time string if (overrideUnit != null && !overrideUnit.equals(rtunit) && !RealType.Time.equals(x)) { double dval = overrideUnit.toThis((double) d[0], rtunit); rr = new Real(x, dval, overrideUnit); } String valueString = rr.toValueString(); vect.addElement(x.getName() + " = " + valueString); f[0] = xx[yindex][0]; d = ymap.inverseScaleValues(f); // WLH 31 Aug 2000 rr = new Real(y, d[0]); overrideUnit = ymap.getOverrideUnit(); rtunit = y.getDefaultUnit(); // units not part of Time string if (overrideUnit != null && !overrideUnit.equals(rtunit) && !RealType.Time.equals(y)) { double dval = overrideUnit.toThis((double) d[0], rtunit); rr = new Real(y, dval, overrideUnit); } valueString = rr.toValueString(); valueString = new Real(y, d[0]).toValueString(); vect.addElement(y.getName() + " = " + valueString); getDisplayRenderer().setCursorStringVector(vect); float[][] xxp = {{xx[0][0]}, {xx[1][0]}, {xx[2][0]}}; xxp[otherindex][0] += EPS; if (tuplecs != null) xxp = tuplecs.toReference(xxp); float[][] xxm = {{xx[0][0]}, {xx[1][0]}, {xx[2][0]}}; xxm[otherindex][0] -= EPS; if (tuplecs != null) xxm = tuplecs.toReference(xxm); double dot = (xxp[0][0] - xxm[0][0]) * direction[0] + (xxp[1][0] - xxm[1][0]) * direction[1] + (xxp[2][0] - xxm[2][0]) * direction[2]; float abs = (float) Math.sqrt((xxp[0][0] - xxm[0][0]) * (xxp[0][0] - xxm[0][0]) + (xxp[1][0] - xxm[1][0]) * (xxp[1][0] - xxm[1][0]) + (xxp[2][0] - xxm[2][0]) * (xxp[2][0] - xxm[2][0])); float other_offset = EPS * (2.0f * EPS / abs); if (dot >= 0.0) other_offset = -other_offset; last_x = new float[][] {{clast_x[0][0]}, {clast_x[1][0]}, {clast_x[2][0]}}; if (Display.DisplaySpatialSphericalTuple.equals(tuple) && otherindex != 1) { if (last_x[1][0] < first_x[1][0] && cum_lon > 0.0f) { last_x[1][0] += 360.0; } else if (last_x[1][0] > first_x[1][0] && cum_lon < 0.0f) { last_x[1][0] -= 360.0; } } int npoints = 4 * EDGE + 1; float[][] c = new float[3][npoints]; for (int i=0; i<EDGE; i++) { float a = ((float) i) / EDGE; float b = 1.0f - a; c[xindex][i] = b * first_x[xindex][0] + a * last_x[xindex][0]; c[yindex][i] = first_x[yindex][0]; c[otherindex][i] = first_x[otherindex][0] + other_offset; c[xindex][EDGE + i] = last_x[xindex][0]; c[yindex][EDGE + i] = b * first_x[yindex][0] + a * last_x[yindex][0]; c[otherindex][EDGE + i] = first_x[otherindex][0] + other_offset; c[xindex][2 * EDGE + i] = b * last_x[xindex][0] + a * first_x[xindex][0]; c[yindex][2 * EDGE + i] = last_x[yindex][0]; c[otherindex][2 * EDGE + i] = first_x[otherindex][0] + other_offset; c[xindex][3 * EDGE + i] = first_x[xindex][0]; c[yindex][3 * EDGE + i] = b * last_x[yindex][0] + a * first_x[yindex][0]; c[otherindex][3 * EDGE + i] = first_x[otherindex][0] + other_offset; } c[0][npoints - 1] = c[0][0]; c[1][npoints - 1] = c[1][0]; c[2][npoints - 1] = c[2][0]; if (tuple != null) c = tuplecs.toReference(c); float[] coordinates = new float[3 * npoints]; for (int i=0; i<npoints; i++) { int i3 = 3 * i; coordinates[i3] = c[0][i]; coordinates[i3 + 1] = c[1][i]; coordinates[i3 + 2] = c[2][i]; } VisADLineStripArray array = new VisADLineStripArray(); array.vertexCount = npoints; array.stripVertexCounts = new int[1]; array.stripVertexCounts[0] = npoints; array.coordinates = coordinates; byte[] colors = new byte[3 * npoints]; for (int i=0; i<npoints; i++) { int i3 = 3 * i; colors[i3] = red; colors[i3 + 1] = green; colors[i3 + 2] = blue; } array.colors = colors; array = (VisADLineStripArray) array.adjustSeam(this); DisplayImplJ3D display = (DisplayImplJ3D) getDisplay(); GeometryArray geometry = display.makeGeometry(array); DataDisplayLink[] Links = getLinks(); if (Links == null || Links.length == 0) { return; } DataDisplayLink link = Links[0]; float[] default_values = link.getDefaultValues(); GraphicsModeControl mode = (GraphicsModeControl) display.getGraphicsModeControl().clone(); float pointSize = default_values[display.getDisplayScalarIndex(Display.PointSize)]; float lineWidth = default_values[display.getDisplayScalarIndex(Display.LineWidth)]; mode.setPointSize(pointSize, true); mode.setLineWidth(lineWidth, true); Appearance appearance = ShadowTypeJ3D.staticMakeAppearance(mode, null, null, geometry, false); if (group != null) group.detach(); group = null; Shape3D shape = new Shape3D(geometry, appearance); group = new BranchGroup(); group.setCapability(Group.ALLOW_CHILDREN_READ); group.setCapability(BranchGroup.ALLOW_DETACH); group.addChild(shape); if (branch != null) branch.addChild(group); } // end try catch (VisADException e) { // do nothing System.out.println("drag_direct " + e); e.printStackTrace(); } } public Object clone() { return new RubberBandBoxRendererJ3D(x, y, mouseModifiersMask, mouseModifiersValue); } private static final int N = 64; /** test RubberBandBoxRendererJ3D */ public static void main(String args[]) throws VisADException, RemoteException { RealType x = RealType.getRealType("x"); RealType y = RealType.getRealType("y"); RealTupleType xy = new RealTupleType(x, y); RealType c = RealType.getRealType("c"); FunctionType ft = new FunctionType(xy, c); // construct Java3D display and mappings DisplayImpl display = new DisplayImplJ3D("display1"); if (args.length == 0 || args[0].equals("z")) { display.addMap(new ScalarMap(x, Display.XAxis)); display.addMap(new ScalarMap(y, Display.YAxis)); } else if (args[0].equals("x")) { display.addMap(new ScalarMap(x, Display.YAxis)); display.addMap(new ScalarMap(y, Display.ZAxis)); } else if (args[0].equals("y")) { display.addMap(new ScalarMap(x, Display.XAxis)); display.addMap(new ScalarMap(y, Display.ZAxis)); } else if (args[0].equals("radius")) { display.addMap(new ScalarMap(x, Display.Longitude)); display.addMap(new ScalarMap(y, Display.Latitude)); } else if (args[0].equals("lat")) { display.addMap(new ScalarMap(x, Display.Longitude)); display.addMap(new ScalarMap(y, Display.Radius)); } else if (args[0].equals("lon")) { display.addMap(new ScalarMap(x, Display.Latitude)); display.addMap(new ScalarMap(y, Display.Radius)); } else { display.addMap(new ScalarMap(x, Display.Longitude)); display.addMap(new ScalarMap(y, Display.Latitude)); } display.addMap(new ScalarMap(c, Display.RGB)); Integer2DSet fset = new Integer2DSet(xy, N, N); FlatField field = new FlatField(ft, fset); float[][] values = new float[1][N * N]; int k = 0; for (int i=0; i<N; i++) { for (int j=0; j<N; j++) { values[0][k++] = (i - N / 2) * (j - N / 2); } } field.setSamples(values); DataReferenceImpl field_ref = new DataReferenceImpl("field"); field_ref.setData(field); display.addReference(field_ref); Gridded2DSet dummy_set = new Gridded2DSet(xy, null, 1); final DataReferenceImpl ref = new DataReferenceImpl("set"); ref.setData(dummy_set); int m = (args.length > 1) ? InputEvent.CTRL_MASK : 0; display.addReferences(new RubberBandBoxRendererJ3D(x, y, m, m), ref); CellImpl cell = new CellImpl() { public void doAction() throws VisADException, RemoteException { Set set = (Set) ref.getData(); float[][] samples = set.getSamples(); if (samples != null) { System.out.println("box (" + samples[0][0] + ", " + samples[1][0] + ") to (" + samples[0][1] + ", " + samples[1][1] + ")"); } } }; cell.addReference(ref); // create JFrame (i.e., a window) for display and slider JFrame frame = new JFrame("test RubberBandBoxRendererJ3D"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} }); // create JPanel in JFrame JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.setAlignmentY(JPanel.TOP_ALIGNMENT); panel.setAlignmentX(JPanel.LEFT_ALIGNMENT); frame.getContentPane().add(panel); // add display to JPanel panel.add(display.getComponent()); // set size of JFrame and make it visible frame.setSize(500, 500); frame.setVisible(true); } }