//
// 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= 0) {
f[0] = x[i];
d = getDirectMap(i).inverseScaleValues(f);
// create location string
rtype = (RealType) getDirectMap(i).getScalar();
r = new Real(rtype, d[0]);
overrideUnit = getDirectMap(i).getOverrideUnit();
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);
}
valueString = r.toValueString();
vect.addElement(rtype.getName() + " = " + valueString);
thisD[j] = d[0];
directComponent[j] = true;
}
}
getDisplayRenderer().setCursorStringVector(vect);
if (lastIndex < 0) {
lastIndex = thisIndex;
lastD = new double[n];
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.