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