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