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