//
// DataRenderer.java
//

/*
VisAD system for interactive analysis and visualization of numerical
data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

package visad;

import java.util.*;
import java.rmi.*;

/**
   DataRenderer is the VisAD abstract super-class for graphics rendering
   algorithms.  These transform Data objects into 3-D (or 2-D)
   depictions in a Display window.

DataRenderer is not Serializable and should not be copied between JVMs.

*/ public abstract class DataRenderer extends Object implements Cloneable { /** DisplayImpl this is associated with */ private DisplayImpl display = null; /** used to insert output into scene graph */ private DisplayRenderer displayRenderer = null; /** links to Data to be renderer by this */ private transient DataDisplayLink[] Links = null; /** flag from DataDisplayLink.prepareData() */ private boolean[] feasible; // it's a miracle if this is correct private boolean[] is_null; // WLH 7 May 2001 /** flag to indicate that DataDisplayLink.prepareData() was invoked */ private boolean[] changed; private boolean any_changed; private boolean all_feasible; private boolean any_transform_control; /** a Vector of BadMappingException and UnimplementedException Strings generated during the last invocation of doAction() */ private Vector exceptionVector = new Vector(); /** flag indicating whether to suppress Exceptions generated during doAction() */ private boolean suppress_exceptions = false; /** flag for visibility of Data depictions */ protected boolean enabled = true; /** place to hold ProjectionControl Listeners created by this renderer */ private ArrayList projCntrlListeners = new ArrayList(); /** Depth buffer offset and factor for this DataRenderer */ private boolean hasPolygonOffset = false; private float polygonOffset = 0f; private float polygonOffsetFactor = 0f; /** * construct a DataRenderer */ public DataRenderer() { Links = null; display = null; } /** * clear Vector of Exceptions generated during doAction() */ public void clearExceptions() { exceptionVector.removeAllElements(); } /** * set a flag indicating whether to suppress Exceptions * generated during doAction() */ public void suppressExceptions(boolean suppress) { suppress_exceptions = suppress; } /** * add a BadMappingException or UnimplementedException to * Vector of Exceptions generated during doAction() * @param error Exception to add */ public void addException(Exception error) { if (display == null) return; exceptionVector.addElement(error); // System.out.println(error.getMessage()); } /** * there is no need to over-ride this method, but it may be invoked * by DisplayRenderer; gets a clone of exceptionVector to avoid * concurrent access by Display thread * @return a Vector of Strings from the BadMappingExceptions * and UnimplementedExceptions generated during the last invocation * of this DataRenderer's doAction method; */ public Vector getExceptionVector() { return (suppress_exceptions ? new Vector() : (Vector) exceptionVector.clone()); } /** * @return flag indicating whether depiction generation is feasible * for all linked Data */ public boolean get_all_feasible() { return all_feasible; } /** * @return flag indicating whether any linked Data have changed * since last invocation of prepareAction() */ public boolean get_any_changed() { return any_changed; } /** * @return flag indicating whether any Controls associated with * ScalarMaps applying to any linked Data have changed and require * re-transform */ public boolean get_any_transform_control() { return any_transform_control; } /** * set flag indicating whether depiction generation is feasible * for all linked Data * @param b value to set in flag */ public void set_all_feasible(boolean b) { all_feasible = b; } /** * set DataDisplayLinks for linked Data, and set associated DisplayImpl * @param links array of DataDisplayLinks to set * @param d associated DisplayImpl to set * @throws VisADException a VisAD error occurred */ public abstract void setLinks(DataDisplayLink[] links, DisplayImpl d) throws VisADException; /** * Sets the visibility of the data being rendered by this instance. * * @param on Whether or not to render the data. */ public void toggle(boolean on) { enabled = on; } /** * Returns the visibility of the data being rendered by this instance. * * @return Whether or not the data is being rendered. */ public boolean getEnabled() { return enabled; } /** * Returns the list of ProjectionControl listeners created by this. * Provides a way to remove listeners from the Control that may only * be needed per doTransform. * * @return */ public ArrayList getProjectionControlListeners() { return projCntrlListeners; } /** * set DataDisplayLinks for linked Data, including constructing * arrays of booleans associated with DataDisplayLinks; called by * setLinks(DataDisplayLink[], DisplayImpl) * @param links array of DataDisplayLinks to set */ public synchronized void setLinks(DataDisplayLink[] links) { if (display == null) return; if (links == null || links.length == 0) return; Links = links; feasible = new boolean[Links.length]; is_null = new boolean[Links.length]; changed = new boolean[Links.length]; for (int i=0; i " + map.getDisplayScalar().getName()); } */ } } // System.out.println("getBadScale return " + badScale); return badScale; } /** * clear any scene graph created by this DataRenderer, and * clear all instance variables */ public void clearScene() { // test for display == null in methods // remove any ProjectionControl listeners this renderer may have added // to the display. ProjectionControl pCntrl = display.getProjectionControl(); Iterator iter = projCntrlListeners.iterator(); while (iter.hasNext()) { pCntrl.removeControlListener(iter.next()); } display = null; displayRenderer = null; Links = null; exceptionVector.removeAllElements(); projCntrlListeners.clear(); // clear flow rendering and direct manipulation variables shadow_data_out = null; data_out = null; data_units_out = null; shadow_data_in = null; data_in = null; data_units_in = null; data_coord_in = null; sdo_maps = null; sdi_maps = null; rvts = new RealVectorType[] {null, null}; display_coordinate_system = null; spatial_tuple = null; lat_map = null; lon_map = null; link = null; ref = null; type = null; shadow = null; directMap = new ScalarMap[] {null, null, null}; tuple = null; } /** * clear all information associated with AnimationControls * and ValueControls created by this DataRenderer */ public void clearAVControls() { if (display == null) return; Enumeration controls = display.getControls(AVControl.class).elements(); while (controls.hasMoreElements()) { ((AVControl )controls.nextElement()).clearSwitches(this); } // a convenient place to throw this in ProjectionControl control = display.getProjectionControl(); control.clearSwitches(this); // a convenient place to throw this in lat_index = -1; lon_index = -1; } /** * factory method for constructing a subclass of ShadowType appropriate * for the graphics API, that also adapts ShadowFunctionType; * ShadowType trees are constructed that 'shadow' the MathType trees of * Data to be depicted, via recursive calls to buildShadowType() methods * of MathType sub-classes, to DataRenderer.makeShadow*Type() methods, * to Shadow*Type constructors, then back to buildShadowType() methods; * the recursive call chain is initiated by DataDisplayLink.prepareData() * calls to buildShadowType() methods of MathType sub-classes; * @param type FunctionType that returned ShadowType will shadow * @param link DataDisplayLink linking Data to be depicted * @param parent parent in ShadowType tree structure * @return constructed ShadowType * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public abstract ShadowType makeShadowFunctionType( FunctionType type, DataDisplayLink link, ShadowType parent) throws VisADException, RemoteException; /** * factory for constructing a subclass of ShadowType appropriate * for the graphics API, that also adapts ShadowRealTupleType; * ShadowType trees are constructed that 'shadow' the MathType trees of * Data to be depicted, via recursive calls to buildShadowType() methods * of MathType sub-classes, to DataRenderer.makeShadow*Type() methods, * to Shadow*Type constructors, then back to buildShadowType() methods; * the recursive call chain is initiated by DataDisplayLink.prepareData() * calls to buildShadowType() methods of MathType sub-classes; * @param type FunctionType that returned ShadowType will shadow * @param link DataDisplayLink linking Data to be depicted * @param parent parent in ShadowType tree structure * @return constructed ShadowType * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public abstract ShadowType makeShadowRealTupleType( RealTupleType type, DataDisplayLink link, ShadowType parent) throws VisADException, RemoteException; /** * factory for constructing a subclass of ShadowType appropriate * for the graphics API, that also adapts ShadowRealType; * ShadowType trees are constructed that 'shadow' the MathType trees of * Data to be depicted, via recursive calls to buildShadowType() methods * of MathType sub-classes, to DataRenderer.makeShadow*Type() methods, * to Shadow*Type constructors, then back to buildShadowType() methods; * the recursive call chain is initiated by DataDisplayLink.prepareData() * calls to buildShadowType() methods of MathType sub-classes; * @param type FunctionType that returned ShadowType will shadow * @param link DataDisplayLink linking Data to be depicted * @param parent parent in ShadowType tree structure * @return constructed ShadowType * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public abstract ShadowType makeShadowRealType( RealType type, DataDisplayLink link, ShadowType parent) throws VisADException, RemoteException; /** * factory for constructing a subclass of ShadowType appropriate * for the graphics API, that also adapts ShadowSetType; * ShadowType trees are constructed that 'shadow' the MathType trees of * Data to be depicted, via recursive calls to buildShadowType() methods * of MathType sub-classes, to DataRenderer.makeShadow*Type() methods, * to Shadow*Type constructors, then back to buildShadowType() methods; * the recursive call chain is initiated by DataDisplayLink.prepareData() * calls to buildShadowType() methods of MathType sub-classes; * @param type FunctionType that returned ShadowType will shadow * @param link DataDisplayLink linking Data to be depicted * @param parent parent in ShadowType tree structure * @return constructed ShadowType * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public abstract ShadowType makeShadowSetType( SetType type, DataDisplayLink link, ShadowType parent) throws VisADException, RemoteException; /** * factory for constructing a subclass of ShadowType appropriate * for the graphics API, that also adapts ShadowTextType; * ShadowType trees are constructed that 'shadow' the MathType trees of * Data to be depicted, via recursive calls to buildShadowType() methods * of MathType sub-classes, to DataRenderer.makeShadow*Type() methods, * to Shadow*Type constructors, then back to buildShadowType() methods; * the recursive call chain is initiated by DataDisplayLink.prepareData() * calls to buildShadowType() methods of MathType sub-classes; * @param type FunctionType that returned ShadowType will shadow * @param link DataDisplayLink linking Data to be depicted * @param parent parent in ShadowType tree structure * @return constructed ShadowType * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public abstract ShadowType makeShadowTextType( TextType type, DataDisplayLink link, ShadowType parent) throws VisADException, RemoteException; /** * factory for constructing a subclass of ShadowType appropriate * for the graphics API, that also adapts ShadowTupleType; * ShadowType trees are constructed that 'shadow' the MathType trees of * Data to be depicted, via recursive calls to buildShadowType() methods * of MathType sub-classes, to DataRenderer.makeShadow*Type() methods, * to Shadow*Type constructors, then back to buildShadowType() methods; * the recursive call chain is initiated by DataDisplayLink.prepareData() * calls to buildShadowType() methods of MathType sub-classes; * @param type FunctionType that returned ShadowType will shadow * @param link DataDisplayLink linking Data to be depicted * @param parent parent in ShadowType tree structure * @return constructed ShadowType * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public abstract ShadowType makeShadowTupleType( TupleType type, DataDisplayLink link, ShadowType parent) throws VisADException, RemoteException; /** * DataRenderer-specific decision about which Controls require * re-transform; may be over-ridden by DataRenderer sub-classes; * this decision may use some values computed by link.prepareData() * @param control Control being judged whether it needs re-transform * @param link DataDisplayLink possibly involved in decision * @return flag indicating whether re-transform is needed */ public boolean isTransformControl(Control control, DataDisplayLink link) { if (display == null) return false; if (control instanceof ProjectionControl || control instanceof ToggleControl) { return false; } /* WLH 1 Nov 97 - temporary hack - RangeControl changes always require Transform ValueControl and AnimationControl never do if (control instanceof AnimationControl || control instanceof ValueControl || control instanceof RangeControl) { return link.isTransform[control.getIndex()]; */ if (control instanceof AnimationControl || control instanceof ValueControl) { return false; } return true; } /** * used by ShadowFunctionOrSetType for transform time-out hack * @return single DataDisplayLink (over-ridden by sub-classes) */ public DataDisplayLink getLink() { return null; } /** * @return flag indicating whether texture mapping is legal * for this DataRenderer */ public boolean isLegalTextureMap() { return true; } /* ********************** */ /* flow rendering stuff */ /* ********************** */ // value array (display_values) indices // ((ScalarMap) MapVector.elementAt(valueToMap[index])) // can get these indices through shadow_data_out or shadow_data_in /** true if lat and lon in data_in & shadow_data_in is allSpatial or if lat and lon in data_in & lat_lon_in_by_coord */ boolean lat_lon_in = false; /** true if lat_lon_in and shadow_data_out is allSpatial, i.e., map from lat, lon to display is through data CoordinateSystem */ boolean lat_lon_in_by_coord = false; /** true if lat and lon in data_out & shadow_data_out is allSpatial */ boolean lat_lon_out = false; /** true if lat_lon_out and shadow_data_in is allSpatial, i.e., map from lat, lon to display is inverse via data CoordinateSystem */ boolean lat_lon_out_by_coord = false; /** earth dimension, either 2, 3 or -1 (for none) */ int lat_lon_dimension = -1; /** Shadow of reference RealTupleType in Data */ ShadowRealTupleType shadow_data_out = null; /** reference RealTupleType in Data */ RealTupleType data_out = null; /** Units of reference RealTupleType in Data */ Unit[] data_units_out = null; // CoordinateSystem data_coord_out is always null /** Shadow of RealTupleType with reference in Data */ ShadowRealTupleType shadow_data_in = null; /** RealTupleType with reference in Data */ RealTupleType data_in = null; /** Units of RealTupleType with reference in Data */ Unit[] data_units_in = null; /** CoordinateSystems relating data_in to data_out, usually just one, but may be one per point */ CoordinateSystem[] data_coord_in = null; /** spatial ScalarMaps for allSpatial shadow_data_out */ ScalarMap[] sdo_maps = null; /** spatial ScalarMaps for allSpatial shadow_data_in */ ScalarMap[] sdi_maps = null; /** indices in DisplayTupleType of DisplayRealTypes in sdo_maps */ int[] sdo_spatial_index = null; /** indices in DisplayTupleType of DisplayRealTypes in sdi_maps */ int[] sdi_spatial_index = null; /** index of RealType.Latitude if lat_lon_in then index in data_in if lat_lon_out then index in data_out if lat_lon_spatial then values index */ int lat_index = -1; /** index of RealType.Longitude if lat_lon_in then index in data_in if lat_lon_out then index in data_out if lat_lon_spatial then values indices */ int lon_index = -1; /** other index if lat & lon in a RealTupleType of length 3, otherwise -1 */ int other_index = -1; /** true if other_index Units convertable to meter */ boolean other_meters = false; /** RealVectorTypes (extends RealTupleType) mapped to flow1 and flow2, to be tested for being instances of EarthVectorType; values will be either data_in, data_out or null */ RealVectorType[] rvts = {null, null}; /** * @param index 0 or 1 for flow1 and flow2 * @return RealVectorType (extends RealTupleType) mapped to flow1 or * flow2 (values will be either data_in, data_out or null) */ public RealVectorType getRealVectorTypes(int index) { if (index == 0 || index == 1) return rvts[index]; else return null; } /** * @return indices of RealType.Latitude and RealType.Longitude * in data_in, data_out, or just spatial value array */ public int[] getLatLonIndices() { return new int[] {lat_index, lon_index}; } /** * @param indices indices of RealType.Latitude and RealType.Longitude * in data_in, data_out, or just spatial value array */ public void setLatLonIndices(int[] indices) { lat_index = indices[0]; lon_index = indices[1]; } /** * @return earth dimension, either 2, 3 or -1 (for none) */ public int getEarthDimension() { return lat_lon_dimension; } /** * @return Units of earth coordinates used in Data */ public Unit[] getEarthUnits() { if (display == null) return null; Unit[] units = null; if (lat_lon_in) { units = data_units_in; } else if (lat_lon_out) { units = data_units_out; } else if (lat_lon_spatial) { units = new Unit[] {RealType.Latitude.getDefaultUnit(), RealType.Longitude.getDefaultUnit()}; } else { units = null; } int lat = lat_index; int lon = lon_index; int other = other_index; if (units == null) { return null; } else if (units.length == 2) { return new Unit[] {lat >= 0 ? units[lat] : null, lon >= 0 ? units[lon] : null}; } else if (units.length == 3) { return new Unit[] {lat >= 0 ? units[lat] : null, lon >= 0 ? units[lon] : null, other >= 0 ? units[other] : null}; } else { return null; } } /** * @return maximum of display ranges of latitude and longitude */ public float getLatLonRange() { if (display == null) return 1.0f; double[] rlat = null; double[] rlon = null; int lat = lat_index; int lon = lon_index; if ((lat_lon_out && !lat_lon_out_by_coord) || (lat_lon_in && lat_lon_in_by_coord)) { rlat = lat >= 0 ? sdo_maps[lat].getRange() : new double[] {Double.NaN, Double.NaN}; rlon = lon >= 0 ? sdo_maps[lon].getRange() : new double[] {Double.NaN, Double.NaN}; } else if ((lat_lon_in && !lat_lon_in_by_coord) || (lat_lon_out && lat_lon_out_by_coord)) { rlat = lat >= 0 ? sdi_maps[lat].getRange() : new double[] {Double.NaN, Double.NaN}; rlon = lon >= 0 ? sdi_maps[lon].getRange() : new double[] {Double.NaN, Double.NaN}; } else if (lat_lon_spatial) { rlat = lat_map.getRange(); rlon = lon_map.getRange(); } else { return 1.0f; } double dlat = Math.abs(rlat[1] - rlat[0]); double dlon = Math.abs(rlon[1] - rlon[0]); if (dlat != dlat) dlat = 1.0f; if (dlon != dlon) dlon = 1.0f; return (dlat > dlon) ? (float) dlat : (float) dlon; } /** * convert (lat, lon) or (lat, lon, other) values to display (x, y, z) * @param locs (lat, lon) or (lat, lon, other) coordinates * @param vert vertical flow component (if non-null, used to * adjust non-lat/lon spatial_locs * @return display (x, y, z) coordinates * @throws VisADException a VisAD error occurred */ public float[][] earthToSpatial(float[][] locs, float[] vert) throws VisADException { if (display == null) return null; return earthToSpatial(locs, vert, null); } /** * convert (lat, lon) or (lat, lon, other) values to display (x, y, z) * @param locs (lat, lon) or (lat, lon, other) coordinates * @param vert vertical flow component (if non-null, used to * adjust non-lat/lon spatial_locs * @param base_spatial_locs saved spatial_locs argument from * spatialToEarth() call used to fill in any null members * of return array * @return display (x, y, z) coordinates * @throws VisADException a VisAD error occurred */ public float[][] earthToSpatial(float[][] locs, float[] vert, float[][] base_spatial_locs) throws VisADException { if (display == null) return null; int lat = lat_index; int lon = lon_index; int other = other_index; if (lat_index < 0 || lon_index < 0) return null; int size = locs[0].length; if (locs.length < lat_lon_dimension) { // extend locs to lat_lon_dimension with zero fill float[][] temp = locs; locs = new float[lat_lon_dimension][]; for (int i=0; i lat_lon_dimension) { // truncate locs to lat_lon_dimension float[][] temp = locs; locs = new float[lat_lon_dimension][]; for (int i=0; i -1) { for (int j=0; j -1 && spatial_locs[vert_index] != null) { for (int j=0; j 0 && spatial_locs[0] != null && spatial_locs[0].length > 0) { // DRM 14 Apr 2003 - could do transform in place //spatial_locs = display_coordinate_system.toReference(spatial_locs); spatial_locs = display_coordinate_system.toReference(Set.copyFloats(spatial_locs)); } } return spatial_locs; } /** * convert display (x, y, z) to (lat, lon) or (lat, lon, other) values * @param spatial_locs display (x, y, z) coordinates * @return (lat, lon) or (lat, lon, other) coordinates * @throws VisADException a VisAD error occurred */ public float[][] spatialToEarth(float[][] spatial_locs) throws VisADException { if (display == null) return null; float[][] base_spatial_locs = new float[3][]; return spatialToEarth(spatial_locs, base_spatial_locs); } /** * convert display (x, y, z) to (lat, lon) or (lat, lon, other) values * @param spatial_locs display (x, y, z) coordinates * @param base_spatial_locs float[3][] array used to return member * arrays of spatial_locs argument * @return (lat, lon) or (lat, lon, other) coordinates * @throws VisADException a VisAD error occurred */ public float[][] spatialToEarth(float[][] spatial_locs, float[][] base_spatial_locs) throws VisADException { if (display == null) return null; int lat = lat_index; int lon = lon_index; int other = other_index; if (lat_index < 0 || lon_index < 0) return null; if (spatial_locs.length != 3) return null; int size = 0; for (int i=0; i<3; i++) { if (spatial_locs[i] != null && spatial_locs[i].length > size) { size = spatial_locs[i].length; } } if (size == 0) return null; // fill any empty spatial DisplayRealTypes with default values for (int i=0; i<3; i++) { if (spatial_locs[i] == null) { spatial_locs[i] = new float[size]; // defaults for Cartesian spatial DisplayRealTypes = 0.0f for (int j=0; j -1) rvts[k] = (RealVectorType) d_o; } if (d_i != null && d_i instanceof RealVectorType) { ScalarMap[] maps = new ScalarMap[3]; int k = getFlowMaps(s_d_i, maps); if (k > -1) rvts[k] = (RealVectorType) d_i; } int lat_index_local = -1; int lon_index_local = -1; int other_index_local = -1; int n = 0; int m = 0; if (d_i != null) { n = d_i.getDimension(); for (int i=0; i -1 && lon_index_local > -1 && (n == 2 || n == 3)) { if (s_d_i != null && s_d_i.getAllSpatial() && !s_d_i.getSpatialReference()) { lat_lon_in_by_coord = false; sdi_spatial_index = new int[s_d_i.getDimension()]; sdi_maps = getSpatialMaps(s_d_i, sdi_spatial_index); if (sdi_maps == null) { throw new DisplayException("sdi_maps null A"); } } else if (s_d_o != null && s_d_o.getAllSpatial() && !s_d_o.getSpatialReference()) { lat_lon_in_by_coord = true; sdo_spatial_index = new int[s_d_o.getDimension()]; sdo_maps = getSpatialMaps(s_d_o, sdo_spatial_index); if (sdo_maps == null) { throw new DisplayException("sdo_maps null A"); } } else { // do not update lat_index & lon_index return; } lat_lon_in = true; lat_lon_out = false; lat_lon_out_by_coord = false; lat_lon_spatial = false; lat_lon_dimension = n; if (n == 3) { other_index_local = 3 - (lat_index_local + lon_index_local); if (Unit.canConvert(d_u_i[other_index_local], CommonUnit.meter)) { other_meters = true; } } } else { // if( !(lat & lon in di, di dimension = 2 or 3) ) lat_index_local = -1; lon_index_local = -1; other_index_local = -1; if (d_o != null) { m = d_o.getDimension(); for (int i=0; i -1 && lon_index > -1) return; lat_index = -1; lon_index = -1; other_index = -1; int valueArrayLength = display.getValueArrayLength(); int[] valueToScalar = display.getValueToScalar(); int[] valueToMap = display.getValueToMap(); Vector MapVector = display.getMapVector(); for (int i=0; i -1 && lon_index > -1) { lat_lon_spatial = true; lat_lon_dimension = 2; lat_lon_out = false; lat_lon_in_by_coord = false; lat_lon_in = false; } else { lat_lon_spatial = false; lat_index = -1; lon_index = -1; } } /* *************************** */ /* direct manipulation stuff */ /* *************************** */ private float[][] spatialValues = null; /** if Function, last domain index and range values */ private int lastIndex = -1; private double[] lastD = null; private float[] lastX = new float[6]; /** index into spatialValues found by checkClose */ private int closeIndex = -1; /** pick error offset, communicated from checkClose() to drag_direct() */ private float offsetx = 0.0f, offsety = 0.0f, offsetz = 0.0f; /** count down to decay offset to 0.0 */ private int offset_count = 0; /** initial offset_count */ private static final int OFFSET_COUNT_INIT = 30; /** for use in drag_direct */ private transient DataDisplayLink link = null; // private transient ShadowTypeJ3D type = null; private transient DataReference ref = null; private transient MathType type = null; private transient ShadowType shadow = null; /** point on direct manifold line or plane */ private float point_x, point_y, point_z; /** normalized direction of line or perpendicular to plane */ private float line_x, line_y, line_z; /** arrays of length one for inverseScaleValues */ private float[] f = new float[1]; private float[] d = new float[1]; private float[][] value = new float[1][1]; /** information calculated by checkDirect */ /** explanation for invalid use of DirectManipulationRenderer */ private String whyNotDirect = null; /** mapping from spatial axes to tuple component */ private int[] axisToComponent = {-1, -1, -1}; /** mapping from spatial axes to ScalarMaps */ private ScalarMap[] directMap = {null, null, null}; /** spatial axis for Function domain */ private int domainAxis = -1; /** dimension of direct manipulation (including any Function domain) */ private int directManifoldDimension = 0; /** spatial DisplayTupleType other than DisplaySpatialCartesianTuple */ private DisplayTupleType tuple = null; /** possible values for whyNotDirect */ private final static String notRealFunction = "FunctionType must be Real"; private final static String notSimpleField = "not simple field"; private final static String notSimpleTuple = "not simple tuple"; private final static String multipleMapping = "RealType with multiple mappings"; private final static String multipleSpatialMapping = "RealType with multiple spatial mappings"; private final static String nonSpatial = "no spatial mapping"; private final static String viaReference = "spatial mapping through Reference"; private final static String domainDimension = "domain dimension must be 1"; private final static String domainNotSpatial = "domain must be mapped to spatial"; private final static String rangeType = "range must be RealType or RealTupleType"; private final static String rangeNotSpatial = "range must be mapped to spatial"; private final static String domainSet = "domain Set must be Gridded1DSet"; private final static String tooFewSpatial = "Function without spatial domain"; private final static String functionTooFew = "Function directManifoldDimension < 2"; private final static String badCoordSysManifoldDim = "bad directManifoldDimension with spatial CoordinateSystem"; private final static String lostConnection = "lost connection to Data server"; private boolean stop = false; private int LastMouseModifiers = 0; /** * determine if direct manipulation is feasible for the Data * objects rendered by this, and for the ScalarMaps linked to * the associated DisplayImpl; * "returns" its result by calls to setIsDirectManipulation() * called by checkDirect() method of DirectManipulationRendererJ2D * and DirectManipulationRendererJ3D, basically just to share * code between those two classes * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public synchronized void realCheckDirect() throws VisADException, RemoteException { if (display == null) return; setIsDirectManipulation(false); DataDisplayLink[] Links = getLinks(); if (Links == null || Links.length == 0) { link = null; return; } link = Links[0]; ref = link.getDataReference(); shadow = link.getShadow().getAdaptedShadowType(); type = link.getType(); tuple = null; if (type instanceof FunctionType) { ShadowRealTupleType domain = ((ShadowFunctionType) shadow).getDomain(); ShadowType range = ((ShadowFunctionType) shadow).getRange(); tuple = domain.getDisplaySpatialTuple(); // there is some redundancy among these conditions if (!((FunctionType) type).getReal()) { whyNotDirect = notRealFunction; return; } else if (shadow.getLevelOfDifficulty() != ShadowType.SIMPLE_FIELD) { whyNotDirect = notSimpleField; return; } else if (shadow.getMultipleSpatialDisplayScalar()) { whyNotDirect = multipleSpatialMapping; return; } else if (domain.getDimension() != 1) { whyNotDirect = domainDimension; return; } else if(!(Display.DisplaySpatialCartesianTuple.equals(tuple) || (tuple != null && tuple.getCoordinateSystem().getReference().equals( Display.DisplaySpatialCartesianTuple)) )) { whyNotDirect = domainNotSpatial; return; } else if (domain.getSpatialReference()) { whyNotDirect = viaReference; return; } DisplayTupleType rtuple = null; if (range instanceof ShadowRealTupleType) { rtuple = ((ShadowRealTupleType) range).getDisplaySpatialTuple(); } else if (range instanceof ShadowRealType) { rtuple = ((ShadowRealType) range).getDisplaySpatialTuple(); } else { whyNotDirect = rangeType; return; } if (!tuple.equals(rtuple)) { whyNotDirect = rangeNotSpatial; return; } else if (range instanceof ShadowRealTupleType && ((ShadowRealTupleType) range).getSpatialReference()) { whyNotDirect = viaReference; return; } else { Data data; try { data = link.getData(); } catch (RemoteException re) { if (visad.collab.CollabUtil.isDisconnectException(re)) { getDisplay().connectionFailed(this, link); removeLink(link); link = null; whyNotDirect = lostConnection; return; } throw re; } if (!(data instanceof Field) || !(((Field) data).getDomainSet() instanceof Gridded1DSet)) { whyNotDirect = domainSet; return; } } if (Display.DisplaySpatialCartesianTuple.equals(tuple)) { tuple = null; } domainAxis = -1; for (int i=0; i<3; i++) { axisToComponent[i] = -1; directMap[i] = null; } directManifoldDimension = setDirectMap((ShadowRealType) domain.getComponent(0), -1, true); if (range instanceof ShadowRealType) { directManifoldDimension += setDirectMap((ShadowRealType) range, 0, false); } else if (range instanceof ShadowRealTupleType) { ShadowRealTupleType r = (ShadowRealTupleType) range; for (int i=0; i 0) offset_count--; } if (offset_count > 0) { float mult = ((float) offset_count) / ((float) OFFSET_COUNT_INIT); o_x += mult * offsetx; o_y += mult * offsety; o_z += mult * offsetz; } } if (first) { point_x = spatialValues[0][closeIndex]; point_y = spatialValues[1][closeIndex]; point_z = spatialValues[2][closeIndex]; int lineAxis = -1; if (getDirectManifoldDimension() == 3) { // coord sys ok line_x = d_x; line_y = d_y; line_z = d_z; } else { if (getDirectManifoldDimension() == 2) { if (displayRenderer.getMode2D()) { // coord sys ok lineAxis = 2; } else { for (int i=0; i<3; i++) { if (getAxisToComponent(i) < 0 && getDomainAxis() != i) { lineAxis = i; } } } } else if (getDirectManifoldDimension() == 1) { for (int i=0; i<3; i++) { if (getAxisToComponent(i) >= 0) { lineAxis = i; } } } line_x = (lineAxis == 0) ? 1.0f : 0.0f; line_y = (lineAxis == 1) ? 1.0f : 0.0f; line_z = (lineAxis == 2) ? 1.0f : 0.0f; } } // end if (first) float[] x = new float[3]; // x marks the spot if (getDirectManifoldDimension() == 1) { // find closest point on line to ray // logic from vis5d/cursor.c // line o_, d_ to line point_, line_ float ld = d_x * line_x + d_y * line_y + d_z * line_z; float od = o_x * d_x + o_y * d_y + o_z * d_z; float pd = point_x * d_x + point_y * d_y + point_z * d_z; float ol = o_x * line_x + o_y * line_y + o_z * line_z; float pl = point_x * line_x + point_y * line_y + point_z * line_z; if (ld * ld == 1.0f) return; float t = ((pl - ol) - (ld * (pd - od))) / (ld * ld - 1.0f); // x is closest point x[0] = point_x + t * line_x; x[1] = point_y + t * line_y; x[2] = point_z + t * line_z; } else { // getDirectManifoldDimension() = 2 or 3 // intersect ray with plane float dot = (point_x - o_x) * line_x + (point_y - o_y) * line_y + (point_z - o_z) * line_z; float dot2 = d_x * line_x + d_y * line_y + d_z * line_z; if (dot2 == 0.0) return; dot = dot / dot2; // x is intersection x[0] = o_x + dot * d_x; x[1] = o_y + dot * d_y; x[2] = o_z + dot * d_z; } // //jeffmc: new call so a derived class can constrain the position of the drag point // constrainDragPoint(x); // // TO_DO // might estimate errors from pixel resolution on screen // try { float[] xx = {x[0], x[1], x[2]}; if (tuple != null) { float[][] cursor = {{x[0]}, {x[1]}, {x[2]}}; float[][] new_cursor = tuple.getCoordinateSystem().fromReference(cursor); x[0] = new_cursor[0][0]; x[1] = new_cursor[1][0]; x[2] = new_cursor[2][0]; } Data newData = null; Data data; try { data = link.getData(); } catch (RemoteException re) { if (visad.collab.CollabUtil.isDisconnectException(re)) { getDisplay().connectionFailed(this, link); removeLink(link); link = null; return; } throw re; } if (type instanceof RealType) { addPoint(xx); for (int i=0; i<3; i++) { if (getAxisToComponent(i) >= 0) { f[0] = x[i]; d = getDirectMap(i).inverseScaleValues(f); // RealType rtype = (RealType) data.getType(); RealType rtype = (RealType) type; newData = new Real(rtype, (double) d[0], rtype.getDefaultUnit(), null); // create location string Vector vect = new Vector(); Real r = new Real(rtype, d[0]); Unit overrideUnit = getDirectMap(i).getOverrideUnit(); Unit rtunit = rtype.getDefaultUnit(); // units not part of Time string if (overrideUnit != null && !overrideUnit.equals(rtunit) && (!Unit.canConvert(rtunit, CommonUnit.secondsSinceTheEpoch) || rtunit.getAbsoluteUnit().equals(rtunit))) { double dval = overrideUnit.toThis((double) d[0], rtunit); r = new Real(rtype, dval, overrideUnit); } String valueString = r.toValueString(); vect.addElement(rtype.getName() + " = " + valueString); getDisplayRenderer().setCursorStringVector(vect); break; } } ref.setData(newData); link.clearData(); } else if (type instanceof RealTupleType) { addPoint(xx); int n = ((RealTuple) data).getDimension(); Real[] reals = new Real[n]; Vector vect = new Vector(); for (int i=0; i<3; i++) { int j = getAxisToComponent(i); if (j >= 0) { f[0] = x[i]; d = getDirectMap(i).inverseScaleValues(f); Real c = (Real) ((RealTuple) data).getComponent(j); RealType rtype = (RealType) c.getType(); reals[j] = new Real(rtype, (double) d[0], rtype.getDefaultUnit(), null); // create location string Real r = new Real(rtype, d[0]); Unit overrideUnit = getDirectMap(i).getOverrideUnit(); Unit rtunit = rtype.getDefaultUnit(); // units not part of Time string if (overrideUnit != null && !overrideUnit.equals(rtunit) && (!Unit.canConvert(rtunit, CommonUnit.secondsSinceTheEpoch) || rtunit.getAbsoluteUnit().equals(rtunit))) { double dval = overrideUnit.toThis((double) d[0], rtunit); r = new Real(rtype, dval, overrideUnit); } String valueString = r.toValueString(); vect.addElement(rtype.getName() + " = " + valueString); } } getDisplayRenderer().setCursorStringVector(vect); for (int j=0; j= thisIndex) ? 1 : -1; for (int i=0; i 0 && ((g < 0.0f && lastg >= 0.0f) || (g >= 0.0f && lastg < 0.0f))) { float r = (float) (i - (Math.abs(g) / (Math.abs(lastg) + Math.abs(g)))); ray_pos = (r - HALF_GUESSES) * RAY_POS_INC; break; } lastg = g; // otherwise look for closest to zero double d = Math.abs(othervalue - guesses[otherindex][i]); if (d < distance) { distance = d; ray_pos = (i - HALF_GUESSES) * RAY_POS_INC; } } // end for (int i=0; i

Ads help maintain this website.