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