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