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    }