001    /*
002     * $Id: MyRubberBandBoxRendererJ3D.java,v 1.10 2012/03/27 21:58:01 rink Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    
031    //
032    // MyRubberBandBoxRendererJ3D.java
033    //
034    
035    /*
036    VisAD system for interactive analysis and visualization of numerical
037    data.  Copyright (C) 1996 - 2002 Bill Hibbard, Curtis Rueden, Tom
038    Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
039    Tommy Jasmin.
040    
041    This library is free software; you can redistribute it and/or
042    modify it under the terms of the GNU Library General Public
043    License as published by the Free Software Foundation; either
044    version 2 of the License, or (at your option) any later version.
045    
046    This library is distributed in the hope that it will be useful,
047    but WITHOUT ANY WARRANTY; without even the implied warranty of
048    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
049    Library General Public License for more details.
050    
051    You should have received a copy of the GNU Library General Public
052    License along with this library; if not, write to the Free
053    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
054    MA 02111-1307, USA
055    */
056    
057    package edu.wisc.ssec.mcidasv.data.hydra;
058    
059    import java.awt.event.InputEvent;
060    import java.awt.event.WindowAdapter;
061    import java.awt.event.WindowEvent;
062    import java.rmi.RemoteException;
063    import java.util.Enumeration;
064    import java.util.Vector;
065    
066    import javax.media.j3d.Appearance;
067    import javax.media.j3d.BranchGroup;
068    import javax.media.j3d.GeometryArray;
069    import javax.media.j3d.Group;
070    import javax.media.j3d.Shape3D;
071    import javax.swing.BoxLayout;
072    import javax.swing.JFrame;
073    import javax.swing.JPanel;
074    
075    import visad.BadDirectManipulationException;
076    import visad.CellImpl;
077    import visad.CoordinateSystem;
078    import visad.DataDisplayLink;
079    import visad.DataReference;
080    import visad.DataReferenceImpl;
081    import visad.Display;
082    import visad.DisplayImpl;
083    import visad.DisplayRealType;
084    import visad.DisplayTupleType;
085    import visad.FlatField;
086    import visad.FunctionType;
087    import visad.GraphicsModeControl;
088    import visad.Gridded2DSet;
089    import visad.Gridded3DSet;
090    import visad.Integer2DSet;
091    import visad.Real;
092    import visad.RealTupleType;
093    import visad.RealType;
094    import visad.ScalarMap;
095    import visad.ScalarType;
096    import visad.Set;
097    import visad.ShadowType;
098    import visad.Unit;
099    import visad.VisADException;
100    import visad.VisADLineStripArray;
101    import visad.VisADRay;
102    import visad.java3d.DirectManipulationRendererJ3D;
103    import visad.java3d.DisplayImplJ3D;
104    import visad.java3d.ShadowTypeJ3D;
105    
106    /**
107       RubberBandBoxRendererJ3D is the VisAD class for direct
108       manipulation of rubber band boxes
109    */
110    public class MyRubberBandBoxRendererJ3D extends DirectManipulationRendererJ3D {
111    
112      private RealType x = null;
113      private RealType y = null;
114      private RealTupleType xy = null;
115    
116      private int mouseModifiersMask  = 0;
117      private int mouseModifiersValue = 0;
118    
119      private BranchGroup branch = null;
120      private BranchGroup group  = null;
121      //- TDR
122      private boolean       keep_last_box   = false;
123      private BranchGroup   last_group      = null;
124      private GeometryArray last_geometry   = null;
125      private Appearance    last_appearance = null;
126      public Gridded3DSet   last_box        = null;
127    
128      public  boolean enabled = true;
129      public  boolean active  = true;
130    
131      /** this DirectManipulationRenderer is quite different - it does not
132          render its data, but only place values into its DataReference
133          on right mouse button release;
134          it uses xarg and yarg to determine spatial ScalarMaps */
135      public MyRubberBandBoxRendererJ3D (RealType xarg, RealType yarg) {
136        this(xarg, yarg, 0, 0);
137      }
138    
139      /** xarg and yarg determine spatial ScalarMaps;
140          mmm and mmv determine whehter SHIFT or CTRL keys are required -
141          this is needed since this is a greedy DirectManipulationRenderer
142          that will grab any right mouse click (that intersects its 2-D
143          sub-manifold) */
144      public MyRubberBandBoxRendererJ3D (RealType xarg, RealType yarg, int mmm, int mmv) {
145        super();
146        x = xarg;
147        y = yarg;
148        mouseModifiersMask = mmm;
149        mouseModifiersValue = mmv;
150      }
151    
152      /** don't render - just return BranchGroup for scene graph to
153          render rectangle into */
154      public synchronized BranchGroup doTransform()
155             throws VisADException, RemoteException {
156        branch = new BranchGroup();
157        branch.setCapability(BranchGroup.ALLOW_DETACH);
158        branch.setCapability(Group.ALLOW_CHILDREN_READ);
159        branch.setCapability(Group.ALLOW_CHILDREN_WRITE);
160        branch.setCapability(Group.ALLOW_CHILDREN_EXTEND);
161    
162        // check type and maps for valid direct manipulation
163        if (!getIsDirectManipulation()) {
164          throw new BadDirectManipulationException(getWhyNotDirect() +
165            ": DirectManipulationRendererJ3D.doTransform");
166        }
167        setBranch(branch);
168    
169        if (keep_last_box) { //-TDR
170          if (last_group != null) last_group.detach();
171          branch.addChild(last_group);
172        }
173    
174        return branch;
175      }
176    
177      /** for use in drag_direct */
178      private transient DataDisplayLink link = null;
179      private transient DataReference ref = null;
180    
181      private transient ScalarMap xmap = null;
182      private transient ScalarMap ymap = null;
183    
184      float[] default_values;
185    
186      /** arrays of length one for inverseScaleValues */
187      private float[] f = new float[1];
188      private float[] d = new float[1];
189      private float[] value = new float[2];
190    
191      /** information calculated by checkDirect */
192      /** explanation for invalid use of DirectManipulationRenderer */
193      private String whyNotDirect = null;
194      /** dimension of direct manipulation
195          (always 2 for RubberBandBoxRendererJ3D) */
196      private int directManifoldDimension = 2;
197      /** spatial DisplayTupleType other than
198          DisplaySpatialCartesianTuple */
199      private DisplayTupleType tuple;
200      private CoordinateSystem tuplecs;
201    
202      private int xindex = -1;
203      private int yindex = -1;
204      private int otherindex = -1;
205      private float othervalue;
206    
207      private byte red, green, blue; // default colors
208    
209      private float[][] first_x;
210      private float[][] last_x;
211      private float[][] clast_x;
212      private float cum_lon;
213    
214      /** possible values for whyNotDirect */
215      private final static String xandyNotMatch =
216        "x and y spatial domains don't match";
217      private final static String xandyNotSpatial =
218        "x and y must be mapped to spatial";
219    
220    
221      private boolean stop = false;
222    
223      public void checkDirect() throws VisADException, RemoteException {
224        setIsDirectManipulation(false);
225    
226        DisplayImpl display = getDisplay();
227    
228        DataDisplayLink[] Links = getLinks();
229        if (Links == null || Links.length == 0) {
230          link = null;
231          return;
232        }
233        link = Links[0];
234    
235        ref = link.getDataReference();
236        default_values = link.getDefaultValues();
237    
238        xmap = null;
239        ymap = null;
240        Vector scalar_map_vector = display.getMapVector();
241        Enumeration smaps = scalar_map_vector.elements();
242        while (smaps.hasMoreElements()) {
243          ScalarMap map = (ScalarMap) smaps.nextElement();
244          ScalarType real = map.getScalar();
245          if (real.equals(x)) {
246            DisplayRealType dreal = map.getDisplayScalar();
247            DisplayTupleType t = dreal.getTuple();
248            if (t != null &&
249                (t.equals(Display.DisplaySpatialCartesianTuple) ||
250                 (t.getCoordinateSystem() != null &&
251                  t.getCoordinateSystem().getReference().equals(
252                  Display.DisplaySpatialCartesianTuple)))) {
253              xmap = map;
254              xindex = dreal.getTupleIndex();
255              if (tuple == null) {
256                tuple = t;
257              }
258              else if (!t.equals(tuple)) {
259                whyNotDirect = xandyNotMatch;
260                return;
261              }
262            }
263          }
264          if (real.equals(y)) {
265            DisplayRealType dreal = map.getDisplayScalar();
266            DisplayTupleType t = dreal.getTuple();
267            if (t != null &&
268                (t.equals(Display.DisplaySpatialCartesianTuple) ||
269                 (t.getCoordinateSystem() != null &&
270                  t.getCoordinateSystem().getReference().equals(
271                  Display.DisplaySpatialCartesianTuple)))) {
272              ymap = map;
273              yindex = dreal.getTupleIndex();
274              if (tuple == null) {
275                tuple = t;
276              }
277              else if (!t.equals(tuple)) {
278                whyNotDirect = xandyNotMatch;
279                return;
280              }
281            }
282          }
283        }
284    
285        if (xmap == null || ymap == null) {
286          whyNotDirect = xandyNotSpatial;
287          return;
288        }
289    
290        xy = new RealTupleType(x, y);
291    
292        // get default value for other component of tuple
293        otherindex = 3 - (xindex + yindex);
294        DisplayRealType dreal = (DisplayRealType) tuple.getComponent(otherindex);
295        int index = getDisplay().getDisplayScalarIndex(dreal);
296        othervalue = (index > 0) ? default_values[index] :
297                                   (float) dreal.getDefaultValue();
298    
299        // get default colors
300        index = getDisplay().getDisplayScalarIndex(Display.Red);
301        float v = (index > 0) ? default_values[index] :
302                               (float) Display.Red.getDefaultValue();
303        red = ShadowType.floatToByte(v);
304        index = getDisplay().getDisplayScalarIndex(Display.Green);
305        v = (index > 0) ? default_values[index] :
306                          (float) Display.Green.getDefaultValue();
307        green = ShadowType.floatToByte(v);
308        index = getDisplay().getDisplayScalarIndex(Display.Blue);
309        v = (index > 0) ? default_values[index] :
310                          (float) Display.Blue.getDefaultValue();
311        blue = ShadowType.floatToByte(v);
312    
313        if (Display.DisplaySpatialCartesianTuple.equals(tuple)) {
314          tuple = null;
315          tuplecs = null;
316        }
317        else {
318          tuplecs = tuple.getCoordinateSystem();
319        }
320    
321        directManifoldDimension = 2;
322        setIsDirectManipulation(true);
323      }
324    
325      private int getDirectManifoldDimension() {
326        return directManifoldDimension;
327      }
328    
329      public String getWhyNotDirect() {
330        return whyNotDirect;
331      }
332    
333      public void addPoint(float[] x) throws VisADException {
334        // may need to do this for performance
335      }
336    
337    // methods customized from DataRenderer:
338    
339      public CoordinateSystem getDisplayCoordinateSystem() {
340        return tuplecs;
341      }
342    
343      /** set spatialValues from ShadowType.doTransform */
344      public synchronized void setSpatialValues(float[][] spatial_values) {
345        // do nothing
346      }
347    
348      /** check if ray intersects sub-manifold */
349      public synchronized float checkClose(double[] origin, double[] direction) {
350        if (!enabled) return Float.MAX_VALUE;
351        if (!active) {
352          return Float.MAX_VALUE;
353        }
354        int mouseModifiers = getLastMouseModifiers();
355        if ((mouseModifiers & mouseModifiersMask) != mouseModifiersValue) {
356          return Float.MAX_VALUE;
357        }
358    
359        try {
360          float r = findRayManifoldIntersection(true, origin, direction, tuple,
361                                                otherindex, othervalue);
362          if (r == r) {
363            return 0.0f;
364          }
365          else {
366            return Float.MAX_VALUE;
367          }
368        }
369        catch (VisADException ex) {
370          return Float.MAX_VALUE;
371        }
372      }
373    
374      /** mouse button released, ending direct manipulation */
375      public synchronized void release_direct() {
376        // set data in ref
377        if (!enabled) return;
378        if (group != null) group.detach();
379        group = null;
380        try {
381          float[][] samples = new float[2][2];
382          f[0] = first_x[xindex][0];
383          d = xmap.inverseScaleValues(f);
384          d[0] = f[0];
385          samples[0][0] = (float) d[0];
386          f[0] = first_x[yindex][0];
387          d = ymap.inverseScaleValues(f);
388          d[0] = f[0];
389          samples[1][0] = (float) d[0];
390          f[0] = last_x[xindex][0];
391          d = xmap.inverseScaleValues(f);
392          d[0] = f[0];
393          samples[0][1] = (float) d[0];
394          f[0] = last_x[yindex][0];
395          d = ymap.inverseScaleValues(f);
396          d[0] = f[0];
397          samples[1][1] = (float) d[0];
398          Gridded2DSet set = new Gridded2DSet(xy, samples, 2);
399          ref.setData(set);
400          link.clearData();
401        } // end try
402        catch (VisADException e) {
403          // do nothing
404          System.out.println("release_direct " + e);
405          e.printStackTrace();
406        }
407        catch (RemoteException e) {
408          // do nothing
409          System.out.println("release_direct " + e);
410          e.printStackTrace();
411        }
412      }
413    
414      public void stop_direct() {
415        stop = true;
416      }
417    
418      private static final int EDGE = 20;
419    
420      private static final float EPS = 0.005f;
421    
422      public synchronized void drag_direct(VisADRay ray, boolean first,
423                                           int mouseModifiers) {
424        if (ref == null) return;
425        if (enabled == false) return;
426    
427        if (first) {
428          stop = false;
429        }
430        else {
431          if (stop) return;
432        }
433    
434        double[] origin = ray.position;
435        double[] direction = ray.vector;
436    
437        try {
438          float r = findRayManifoldIntersection(true, origin, direction, tuple,
439                                                otherindex, othervalue);
440          if (r != r) {
441            if (group != null) group.detach();
442            return;
443          }
444          float[][] xx = {{(float) (origin[0] + r * direction[0])},
445                          {(float) (origin[1] + r * direction[1])},
446                          {(float) (origin[2] + r * direction[2])}};
447          if (tuple != null) xx = tuplecs.fromReference(xx);
448    
449          if (first) {
450            first_x = xx;
451            cum_lon = 0.0f;
452          }
453          else if (Display.DisplaySpatialSphericalTuple.equals(tuple)) {
454            float diff = xx[1][0] - clast_x[1][0];
455            if (diff > 180.0f) diff -= 360.0f;
456            else if (diff < -180.0f) diff += 360.0f;
457            cum_lon += diff;
458            if (cum_lon > 360.0f) cum_lon -= 360.0f;
459            else if (cum_lon < -360.0f) cum_lon += 360.0f;
460          }
461          clast_x = xx;
462    
463          Vector vect = new Vector();
464          f[0] = xx[xindex][0];
465          d = xmap.inverseScaleValues(f);
466    
467          // WLH 31 Aug 2000
468          Real rr = new Real(x, d[0]);
469          Unit overrideUnit = xmap.getOverrideUnit();
470          Unit rtunit = x.getDefaultUnit();
471          // units not part of Time string
472          if (overrideUnit != null && !overrideUnit.equals(rtunit) &&
473              !RealType.Time.equals(x)) {
474            double dval =  overrideUnit.toThis((double) d[0], rtunit);
475            rr = new Real(x, dval, overrideUnit);
476          }   
477          String valueString = rr.toValueString();
478    
479          vect.addElement(x.getName() + " = " + valueString);
480          f[0] = xx[yindex][0];
481          d = ymap.inverseScaleValues(f);
482    
483          // WLH 31 Aug 2000
484          rr = new Real(y, d[0]);
485          overrideUnit = ymap.getOverrideUnit();
486          rtunit = y.getDefaultUnit();
487          // units not part of Time string
488          if (overrideUnit != null && !overrideUnit.equals(rtunit) &&
489              !RealType.Time.equals(y)) {
490            double dval =  overrideUnit.toThis((double) d[0], rtunit);
491            rr = new Real(y, dval, overrideUnit);
492          }
493          valueString = rr.toValueString();
494    
495          valueString = new Real(y, d[0]).toValueString();
496          vect.addElement(y.getName() + " = " + valueString);
497          getDisplayRenderer().setCursorStringVector(vect);
498    
499          float[][] xxp = {{xx[0][0]}, {xx[1][0]}, {xx[2][0]}};
500          xxp[otherindex][0] += EPS;
501          if (tuplecs != null) xxp = tuplecs.toReference(xxp);
502          float[][] xxm = {{xx[0][0]}, {xx[1][0]}, {xx[2][0]}};
503          xxm[otherindex][0] -= EPS;
504          if (tuplecs != null) xxm = tuplecs.toReference(xxm);
505          double dot = (xxp[0][0] - xxm[0][0]) * direction[0] +
506                       (xxp[1][0] - xxm[1][0]) * direction[1] +
507                       (xxp[2][0] - xxm[2][0]) * direction[2];
508          float abs = (float)
509            Math.sqrt((xxp[0][0] - xxm[0][0]) * (xxp[0][0] - xxm[0][0]) +
510                     (xxp[1][0] - xxm[1][0]) * (xxp[1][0] - xxm[1][0]) +
511                     (xxp[2][0] - xxm[2][0]) * (xxp[2][0] - xxm[2][0]));
512          float other_offset = EPS * (2.0f * EPS / abs);
513          if (dot >= 0.0) other_offset = -other_offset;
514    
515          last_x =
516            new float[][] {{clast_x[0][0]}, {clast_x[1][0]}, {clast_x[2][0]}};
517          if (Display.DisplaySpatialSphericalTuple.equals(tuple) &&
518              otherindex != 1) {
519            if (last_x[1][0] < first_x[1][0] && cum_lon > 0.0f) {
520              last_x[1][0] += 360.0;
521            }
522            else if (last_x[1][0] > first_x[1][0] && cum_lon < 0.0f) {
523              last_x[1][0] -= 360.0;
524            }
525          }
526    
527          int npoints = 4 * EDGE + 1;
528          float[][] c = new float[3][npoints];
529          for (int i=0; i<EDGE; i++) {
530            float a = ((float) i) / EDGE;
531            float b = 1.0f - a;
532            c[xindex][i] = b * first_x[xindex][0] + a * last_x[xindex][0];
533            c[yindex][i] = first_x[yindex][0];
534            c[otherindex][i] = first_x[otherindex][0] + other_offset;
535            c[xindex][EDGE + i] = last_x[xindex][0];
536            c[yindex][EDGE + i] = b * first_x[yindex][0] + a * last_x[yindex][0];
537            c[otherindex][EDGE + i] = first_x[otherindex][0] + other_offset;
538            c[xindex][2 * EDGE + i] = b * last_x[xindex][0] + a * first_x[xindex][0];
539            c[yindex][2 * EDGE + i] = last_x[yindex][0];
540            c[otherindex][2 * EDGE + i] = first_x[otherindex][0] + other_offset;
541            c[xindex][3 * EDGE + i] = first_x[xindex][0];
542            c[yindex][3 * EDGE + i] = b * last_x[yindex][0] + a * first_x[yindex][0];
543            c[otherindex][3 * EDGE + i] = first_x[otherindex][0] + other_offset;
544          }
545          c[0][npoints - 1] = c[0][0];
546          c[1][npoints - 1] = c[1][0];
547          c[2][npoints - 1] = c[2][0];
548          if (tuple != null) c = tuplecs.toReference(c);
549          float[] coordinates = new float[3 * npoints];
550          last_box = new Gridded3DSet(RealTupleType.SpatialCartesian3DTuple, c, npoints);
551          for (int i=0; i<npoints; i++) {
552            int i3 = 3 * i;
553            coordinates[i3] = c[0][i];
554            coordinates[i3 + 1] = c[1][i];
555            coordinates[i3 + 2] = c[2][i];
556          }
557          VisADLineStripArray array = new VisADLineStripArray();
558          array.vertexCount = npoints;
559          array.stripVertexCounts = new int[1];
560          array.stripVertexCounts[0] = npoints;
561          array.coordinates = coordinates;
562          byte[] colors = new byte[3 * npoints];
563          for (int i=0; i<npoints; i++) {
564            int i3 = 3 * i;
565            colors[i3] = red;
566            colors[i3 + 1] = green;
567            colors[i3 + 2] = blue;
568          }
569          array.colors = colors;
570          array = (VisADLineStripArray) array.adjustSeam(this);
571    
572          DisplayImplJ3D display = (DisplayImplJ3D) getDisplay();
573          GeometryArray geometry = display.makeGeometry(array);
574      
575          DataDisplayLink[] Links = getLinks();
576          if (Links == null || Links.length == 0) {
577            return;
578          }
579          DataDisplayLink link = Links[0];
580    
581          float[] default_values = link.getDefaultValues();
582          GraphicsModeControl mode = (GraphicsModeControl)
583            display.getGraphicsModeControl().clone();
584          float pointSize =
585            default_values[display.getDisplayScalarIndex(Display.PointSize)];
586          float lineWidth =
587            default_values[display.getDisplayScalarIndex(Display.LineWidth)];
588          mode.setPointSize(pointSize, true);
589          mode.setLineWidth(lineWidth, true);
590          Appearance appearance =
591            ShadowTypeJ3D.staticMakeAppearance(mode, null, null, geometry, false);
592    
593          if (group != null) group.detach();
594          group = null;
595    
596          Shape3D shape = new Shape3D(geometry, appearance);
597          group = new BranchGroup();
598          group.setCapability(Group.ALLOW_CHILDREN_READ);
599          group.setCapability(BranchGroup.ALLOW_DETACH);
600          group.addChild(shape);
601    
602          //-- TDR
603          if (keep_last_box) {
604            last_group      = group;
605            last_geometry   = geometry;
606            last_appearance = appearance;
607          }
608    
609          if (branch != null) branch.addChild(group);
610        } // end try
611        catch (VisADException e) {
612          // do nothing
613          e.printStackTrace();
614        }
615      }
616    
617      public Object clone() {
618        return new MyRubberBandBoxRendererJ3D(x, y, mouseModifiersMask,
619                                            mouseModifiersValue);
620      }
621    
622    
623      //---------------------------------------------------------
624      public void setKeepLastBoxOn(boolean keep) {
625        //- default is false
626        keep_last_box = keep;
627      }
628    
629      public void removeLastBox() {
630        if (last_group != null) {
631          last_group.detach();
632        }
633      }
634    
635      public BranchGroup getLastBox() {
636        Shape3D shape     = new Shape3D(last_geometry, last_appearance);
637        BranchGroup group = new BranchGroup();
638        group.setCapability(Group.ALLOW_CHILDREN_READ);
639        group.setCapability(BranchGroup.ALLOW_DETACH);
640        group.addChild(shape);
641        return group;
642      }
643    
644      public void setLastBox(BranchGroup box_bg) {
645        if (last_group != null) {
646          last_group.detach();
647        }
648        last_group = box_bg;
649        branch.addChild(box_bg);
650      }
651    
652      public void setLastBox(MyRubberBandBoxRendererJ3D rbbr) {
653        BranchGroup box_bg = rbbr.getLastBox();
654        if (last_group != null) {
655          last_group.detach();
656        }
657        last_group = box_bg;
658        branch.addChild(box_bg);
659      }
660      //-----------------------------------------------------------
661    
662    }
663