001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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 https://www.gnu.org/licenses/. 027 */ 028 029// 030// GrabLineRendererJ3D.java 031// 032 033package edu.wisc.ssec.mcidasv.data.hydra; 034 035import java.rmi.RemoteException; 036import java.util.Enumeration; 037import java.util.Vector; 038 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041import visad.CommonUnit; 042import visad.Data; 043import visad.DataDisplayLink; 044import visad.DataReference; 045import visad.Display; 046import visad.DisplayImpl; 047import visad.DisplayRealType; 048import visad.DisplayTupleType; 049import visad.Gridded1DSet; 050import visad.MathType; 051import visad.Real; 052import visad.RealTuple; 053import visad.RealTupleType; 054import visad.RealType; 055import visad.ScalarMap; 056import visad.ShadowRealType; 057import visad.ShadowType; 058import visad.Unit; 059import visad.VisADException; 060import visad.VisADRay; 061 062/** 063 Grab and drag lines parallel to a coordinate axis. For simple 064 2D graphs, not yet generalized for 3D displays. For a 065 vertical line, map Real to Display.XAxis, and assign 066 ConstantMap for Display.YAxis. Vice-a-versa for a horizontal 067 line. 068 */ 069 070public class GrabLineRendererJ3D extends visad.java3d.DirectManipulationRendererJ3D { 071 072 private static final Logger logger = 073 LoggerFactory.getLogger(GrabLineRendererJ3D.class); 074 075 private float[][] spatialValues = null; 076 077 private int closeIndex = -1; 078 079 private float offsetx = 0.0f, offsety = 0.0f, offsetz = 0.0f; 080 private int offset_count = 0; 081 private static final int OFFSET_COUNT_INIT = 30; 082 083 private transient DataDisplayLink link = null; 084 private transient DataReference ref = null; 085 private transient MathType type = null; 086 private transient ShadowType shadow = null; 087 088 private float point_x, point_y, point_z; 089 private float line_x, line_y, line_z; 090 091 private float[] f = new float[1]; 092 private float[] d = new float[1]; 093 094 private String notRealType = "not RealType"; 095 private String whyNotDirect = null; 096 097 private boolean pickCrawlToCursor = true; 098 099 private int[] axisToComponent = {-1, -1, -1}; 100 private ScalarMap[] directMap = {null, null, null}; 101 102 private DisplayImpl display = null; 103 private DisplayTupleType tuple = null; 104 private boolean stop = false; 105 106 private Gridded1DSet domainSet = null; 107 private int last_idx = -1; 108 private float[][] samples = null; 109 110 private int mouseModifiersMask = 0; 111 private int mouseModifiersValue = 0; 112 113 public GrabLineRendererJ3D() { 114 this(null); 115 } 116 117 public GrabLineRendererJ3D(Gridded1DSet domainSet) { 118 super(); 119 this.domainSet = domainSet; 120 try { 121 if (domainSet != null) samples = domainSet.getSamples(); 122 } 123 catch (Exception e) { 124 System.out.println(e.getMessage()); 125 } 126 } 127 128 public String getWhyNotDirect() { 129 return whyNotDirect; 130 } 131 132 public synchronized void setSpatialValues(float[][] spatial_values) { 133 spatialValues = spatial_values; 134 } 135 136 public void checkDirect() throws VisADException, RemoteException { 137 setIsDirectManipulation(false); 138 139 display = getDisplay(); 140 link = getLinks()[0]; 141 ref = link.getDataReference(); 142 shadow = link.getShadow().getAdaptedShadowType(); 143 type = link.getType(); 144 if (!(type instanceof RealType)) { 145 whyNotDirect = notRealType; 146 return; 147 } 148 149 tuple = ((ShadowRealType) shadow).getDisplaySpatialTuple(); 150 151 //-ShadowRealType[] components = shadow.getRealComponents(); 152 ShadowRealType[] components = {(ShadowRealType)shadow}; 153 154 for (int i=0; i<components.length; i++) { 155 Enumeration maps = components[i].getSelectedMapVector().elements(); 156 while (maps.hasMoreElements()) { 157 ScalarMap map = (ScalarMap) maps.nextElement(); 158 DisplayRealType dreal = map.getDisplayScalar(); 159 DisplayTupleType tuple = dreal.getTuple(); 160 if (tuple != null && 161 (tuple.equals(Display.DisplaySpatialCartesianTuple) || 162 (tuple.getCoordinateSystem() != null && 163 tuple.getCoordinateSystem().getReference().equals( 164 Display.DisplaySpatialCartesianTuple)))) { 165 int index = dreal.getTupleIndex(); 166 axisToComponent[index] = i; 167 directMap[index] = map; 168 } 169 } // end while (maps.hasMoreElements()) 170 } 171 172 setIsDirectManipulation(true); 173 } 174 175 176 public synchronized float checkClose(double[] origin, double[] direction) 177 { 178 int mouseModifiers = getLastMouseModifiers(); 179 if ((mouseModifiers & mouseModifiersMask) != mouseModifiersValue) { 180 return Float.MAX_VALUE; 181 } 182 183 float distance = Float.MAX_VALUE; 184 if (display == null) return distance; 185 if (spatialValues == null) return distance; 186 float o_x = (float) origin[0]; 187 float o_y = (float) origin[1]; 188 float o_z = (float) origin[2]; 189 float d_x = (float) direction[0]; 190 float d_y = (float) direction[1]; 191 float d_z = (float) direction[2]; 192/* 193System.out.println("origin = " + o_x + " " + o_y + " " + o_z); 194System.out.println("direction = " + d_x + " " + d_y + " " + d_z); 195*/ 196 197 for (int i=0; i<spatialValues[0].length; i++) { 198 float x = spatialValues[0][i] - o_x; 199 float y = spatialValues[1][i] - o_y; 200 float z = spatialValues[2][i] - o_z; 201 float dot = x * d_x + y * d_y + z * d_z; 202 x = x - dot * d_x; 203 y = y - dot * d_y; 204 z = z - dot * d_z; 205 float d = (float) Math.sqrt(x * x + y * y + z * z); 206 if (d < distance) { 207 distance = d; 208 closeIndex = i; 209 offsetx = x; 210 offsety = y; 211 offsetz = z; 212 } 213/* 214System.out.println("spatialValues["+i+"] = " + spatialValues[0][i] + " " + 215spatialValues[1][i] + " " + spatialValues[2][i] + " d = " + d); 216*/ 217 } 218 219 float dist1D = Float.MAX_VALUE; 220 if (axisToComponent[0] != -1) dist1D = offsetx; 221 if (axisToComponent[1] != -1) dist1D = offsety; 222 if (axisToComponent[2] != -1) dist1D = offsetz; 223 return Math.abs(dist1D); 224 } 225 226 public synchronized void drag_direct(VisADRay ray, boolean first, 227 int mouseModifiers) { 228 if (display == null) return; 229 230 // disable printing of the cursor info string 231 getDisplayRenderer().setCursorStringOn(false); 232 233 // System.out.println("drag_direct " + first + " " + type); 234 if (spatialValues == null || ref == null || shadow == null || 235 link == null) return; 236 237 if (first) { 238 stop = false; 239 } 240 else { 241 if (stop) return; 242 } 243 244 float o_x = (float) ray.position[0]; 245 float o_y = (float) ray.position[1]; 246 float o_z = (float) ray.position[2]; 247 float d_x = (float) ray.vector[0]; 248 float d_y = (float) ray.vector[1]; 249 float d_z = (float) ray.vector[2]; 250 251 if (pickCrawlToCursor) { 252 if (first) { 253 offset_count = OFFSET_COUNT_INIT; 254 } 255 else { 256 if (offset_count > 0) offset_count--; 257 } 258 if (offset_count > 0) { 259 float mult = ((float) offset_count) / ((float) OFFSET_COUNT_INIT); 260 o_x += mult * offsetx; 261 o_y += mult * offsety; 262 o_z += mult * offsetz; 263 } 264 } 265 266 if (first) { 267 point_x = spatialValues[0][closeIndex]; 268 point_y = spatialValues[1][closeIndex]; 269 point_z = spatialValues[2][closeIndex]; 270 int lineAxis = -1; 271 for (int i=0; i<3; i++) { 272 if (getAxisToComponent(i) >= 0) { 273 lineAxis = i; 274 } 275 } 276 line_x = (lineAxis == 0) ? 1.0f : 0.0f; 277 line_y = (lineAxis == 1) ? 1.0f : 0.0f; 278 line_z = (lineAxis == 2) ? 1.0f : 0.0f; 279 } 280 float[] x = new float[3]; 281 282 // find closest point on line to ray 283 // logic from vis5d/cursor.c 284 // line o_, d_ to line point_, line_ 285 float ld = d_x * line_x + d_y * line_y + d_z * line_z; 286 float od = o_x * d_x + o_y * d_y + o_z * d_z; 287 float pd = point_x * d_x + point_y * d_y + point_z * d_z; 288 float ol = o_x * line_x + o_y * line_y + o_z * line_z; 289 float pl = point_x * line_x + point_y * line_y + point_z * line_z; 290 if (ld * ld == 1.0f) return; 291 float t = ((pl - ol) - (ld * (pd - od))) / (ld * ld - 1.0f); 292 // x is closest point 293 x[0] = point_x + t * line_x; 294 x[1] = point_y + t * line_y; 295 x[2] = point_z + t * line_z; 296 297 try { 298 float[] xx = {x[0], x[1], x[2]}; 299 if (tuple != null) { 300 /*- TDR ?? 301 float[][] cursor = {{x[0]}, {x[1]}, {x[2]}}; 302 float[][] new_cursor = 303 tuple.getCoordinateSystem().fromReference(cursor); 304 x[0] = new_cursor[0][0]; 305 x[1] = new_cursor[1][0]; 306 x[2] = new_cursor[2][0]; 307 */ 308 } 309 Data newData = null; 310 Data data; 311 try { 312 data = link.getData(); 313 } catch (RemoteException re) { 314 if (visad.collab.CollabUtil.isDisconnectException(re)) { 315 getDisplay().connectionFailed(this, link); 316 removeLink(link); 317 link = null; 318 return; 319 } 320 throw re; 321 } 322 int ii = -1; 323 RealType rtype = null; 324 if (type instanceof RealType) { 325 if (domainSet == null) addPoint(xx); 326 for (int i=0; i<3; i++) { 327 if (getAxisToComponent(i) >= 0) { 328 ii = i; 329 f[0] = x[i]; 330 d = getDirectMap(i).inverseScaleValues(f); 331 // RealType rtype = (RealType) data.getType(); 332 rtype = (RealType) type; 333 newData = new Real(rtype, (double) d[0], rtype.getDefaultUnit(), null); 334 break; 335 } 336 } 337 if (domainSet != null) { 338 int[] idx = domainSet.valueToIndex(new float[][] {d}); 339 if (idx[0] != last_idx && idx[0] >= 0) { 340 newData = new Real(rtype, (double)samples[0][idx[0]], rtype.getDefaultUnit(), null); 341 342 // create location string 343 Vector<String> vect = new Vector<String>(); 344 //-Real r = new Real(rtype, d[0]); 345 Real r = new Real(rtype, samples[0][idx[0]]); 346 Unit overrideUnit = getDirectMap(ii).getOverrideUnit(); 347 Unit rtunit = rtype.getDefaultUnit(); 348 // units not part of Time string 349 if (overrideUnit != null && !overrideUnit.equals(rtunit) && 350 (!Unit.canConvert(rtunit, CommonUnit.secondsSinceTheEpoch) || 351 rtunit.getAbsoluteUnit().equals(rtunit))) { 352 double dval = overrideUnit.toThis((double) d[0], rtunit); 353 r = new Real(rtype, dval, overrideUnit); 354 } 355 String valueString = r.toValueString(); 356 vect.addElement(rtype.getName() + " = " + valueString); 357 getDisplayRenderer().setCursorStringVector(vect); 358 359 ref.setData(newData); 360 link.clearData(); 361 last_idx = idx[0]; 362 } 363 } 364 else { 365 ref.setData(newData); 366 link.clearData(); 367 } 368 } 369 else if (type instanceof RealTupleType) { 370 addPoint(xx); 371 int n = ((RealTuple) data).getDimension(); 372 Real[] reals = new Real[n]; 373 Vector<String> vect = new Vector<String>(); 374 for (int i=0; i<3; i++) { 375 int j = getAxisToComponent(i); 376 if (j >= 0) { 377 f[0] = x[i]; 378 d = getDirectMap(i).inverseScaleValues(f); 379 Real c = (Real) ((RealTuple) data).getComponent(j); 380 rtype = (RealType) c.getType(); 381 reals[j] = new Real(rtype, (double) d[0], rtype.getDefaultUnit(), null); 382 } 383 } 384 getDisplayRenderer().setCursorStringVector(vect); 385 for (int j=0; j<n; j++) { 386 if (reals[j] == null) { 387 reals[j] = (Real) ((RealTuple) data).getComponent(j); 388 } 389 } 390 newData = new RealTuple((RealTupleType) type, reals, 391 ((RealTuple) data).getCoordinateSystem()); 392 //ref.setData(newData); 393 //link.clearData(); 394 395 if (domainSet != null) { 396 int[] idx = domainSet.valueToIndex(new float[][] {d}); 397 if (idx[0] != last_idx && idx[0] >= 0) { 398 newData = new Real(rtype, (double)samples[0][idx[0]], rtype.getDefaultUnit(), null); 399 400 // create location string 401 vect = new Vector<String>(); 402 //-Real r = new Real(rtype, d[0]); 403 Real r = new Real(rtype, samples[0][idx[0]]); 404 Unit overrideUnit = getDirectMap(ii).getOverrideUnit(); 405 Unit rtunit = rtype.getDefaultUnit(); 406 // units not part of Time string 407 if (overrideUnit != null && !overrideUnit.equals(rtunit) && 408 (!Unit.canConvert(rtunit, CommonUnit.secondsSinceTheEpoch) || 409 rtunit.getAbsoluteUnit().equals(rtunit))) { 410 double dval = overrideUnit.toThis((double) d[0], rtunit); 411 r = new Real(rtype, dval, overrideUnit); 412 } 413 String valueString = r.toValueString(); 414 vect.addElement(rtype.getName() + " = " + valueString); 415 getDisplayRenderer().setCursorStringVector(vect); 416 417 ref.setData(newData); 418 link.clearData(); 419 last_idx = idx[0]; 420 } 421 } 422 else { 423 ref.setData(newData); 424 link.clearData(); 425 } 426 427 } 428 429 } catch (VisADException | RemoteException e) { 430 logger.error("Problem in drag_direct", e); 431 } 432 } 433 434 private int getAxisToComponent(int i) { 435 return axisToComponent[i]; 436 } 437 438 private ScalarMap getDirectMap(int i) { 439 return directMap[i]; 440 } 441 442 public void stop_direct() { 443 stop = true; 444 } 445}