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