001    /*
002     * $Id: GrabLineRendererJ3D.java,v 1.7 2012/02/19 17:35:41 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
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    
035    package edu.wisc.ssec.mcidasv.data.hydra;
036    
037    import visad.*;
038    
039    import javax.media.j3d.*;
040    import javax.vecmath.*;
041    
042    import java.util.*;
043    import 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    
053    public 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    /*
178    System.out.println("origin = " + o_x + " " + o_y + " " + o_z);
179    System.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    /*
199    System.out.println("spatialValues["+i+"] = " + spatialValues[0][i] + " " +
200    spatialValues[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    }