001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2016
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
033package edu.wisc.ssec.mcidasv.data.hydra;
034
035import java.rmi.RemoteException;
036import java.util.Enumeration;
037import java.util.Vector;
038
039import visad.CommonUnit;
040import visad.Data;
041import visad.DataDisplayLink;
042import visad.DataReference;
043import visad.Display;
044import visad.DisplayImpl;
045import visad.DisplayRealType;
046import visad.DisplayTupleType;
047import visad.Gridded1DSet;
048import visad.MathType;
049import visad.Real;
050import visad.RealTuple;
051import visad.RealTupleType;
052import visad.RealType;
053import visad.ScalarMap;
054import visad.ShadowRealType;
055import visad.ShadowType;
056import visad.Unit;
057import visad.VisADException;
058import 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
068public 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/*
188System.out.println("origin = " + o_x + " " + o_y + " " + o_z);
189System.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/*
209System.out.println("spatialValues["+i+"] = " + spatialValues[0][i] + " " +
210spatialValues[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}