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