// // DataDisplayLink.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.*; /** DataDisplayLink objects define connections between DataReference objects and Display objects. It extends ReferenceActionLink which is the more general link between ThingReference (extended by DataReference) and Action (extended by Display).<P> */ public class DataDisplayLink extends ReferenceActionLink { /** ShadowType created for data */ private ShadowType shadow; /** cached copy of linked Data, used by prepareData() */ private Data data; /** ConstantMaps specific to this Data */ private Vector ConstantMapVector = new Vector(); /** DataRenderer associated with this Data (may be multiple Data per DataRenderer) */ private DataRenderer renderer; /** Vector of ScalarMaps applying to this Data */ private Vector SelectedMapVector = new Vector(); /** default values for DisplayRealTypes, determined by: 1. this.ConstantMapVector 2. Display.ConstantMapVector 3. DisplayRealType.DefaultValue */ private float[] defaultValues; /** flag per Control to indicate need for transform when Control changes, index by Control.getIndex() */ boolean[] isTransform; /** value of System.currentTimeMillis() when doTransform() started */ public long start_time; /** flag indicating current doTransform() has taken more than 500 ms */ public boolean time_flag; /** * construct a DataDisplayLink linking a DataReference to a Display * @param ref the DataReference to link * @param local_d if d is DisplayImpl, then d; if d is RemoteDisplay, then * its adapted DisplayImpl * @param d the Display * @param constant_maps array of ConstantMaps specific to this Data * @param rend DataRenderer that creates Data depictions * @param jd - unique ID among ReferenceActionLinks attached to Action * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public DataDisplayLink(DataReference ref, DisplayImpl local_d, Display d, ConstantMap[] constant_maps, DataRenderer rend, long jd) throws VisADException, RemoteException { super(ref, local_d, d, jd); renderer = rend; setConstantMaps(constant_maps, true); } /** * Change ConstantMaps[] array specific to this DataDisplayLink * Note this call should occur between * display.disableAction() * and * display.enableAction() * * there are two ways for an application to get a DataDisplayLink: * given a DisplayImpl and a DataReference: * DataDisplayLink link = (DataDisplayLink) display.findReference(ref); * given a DataRenderer (assuming it has only one DataReference): * DataDisplayLink link = renderer.getLinks()[0]; * * @param constant_maps array of ConstantMaps specific to this Data * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void setConstantMaps(ConstantMap[] constant_maps) throws VisADException, RemoteException { setConstantMaps(constant_maps, false); } private void setConstantMaps(ConstantMap[] constant_maps, boolean init) throws VisADException, RemoteException { Enumeration maps; synchronized (ConstantMapVector) { if (!init) { maps = ConstantMapVector.elements(); while(maps.hasMoreElements()) { ConstantMap map = (ConstantMap) maps.nextElement(); map.nullDisplay(); } ConstantMapVector.removeAllElements(); } DisplayImpl local_d = (DisplayImpl) getLocalAction(); Display d = (Display) getAction(); if (constant_maps != null) { for (int i=0; i<constant_maps.length; i++) { maps = ((Vector) ConstantMapVector.clone()).elements(); while(maps.hasMoreElements()) { ScalarMap map = (ScalarMap) maps.nextElement(); if (map.getDisplayScalar().equals(constant_maps[i].getDisplayScalar())) { throw new DisplayException("DataDisplayLink: two ConstantMaps have" + " the same DisplayScalar"); } } if (constant_maps[i].getDisplay() != null && !ConstantMap.getAllowMultipleUseKludge()) { throw new DisplayException(constant_maps[i] + " already has a display\n" + "If this Exception breaks an existing app add a call to:\n" + "ConstantMap.setAllowMultipleUseKludge(true) at the " + "start of your app \n OR you can stop reusing ConstantMaps"); } constant_maps[i].setDisplay(local_d); ConstantMapVector.addElement(constant_maps[i]); local_d.addDisplayScalar(constant_maps[i]); } } if (!init) { getThingReference().incTick(); } } } /** * @return the local DisplayImpl for the linked Display */ public DisplayImpl getDisplay() { return (DisplayImpl) local_action; } /** * @return the DataRenderer that creates Data depictions */ public DataRenderer getRenderer() { return renderer; } /** * @return a clone of Vector of ScalarMaps applying to this Data */ public Vector getSelectedMapVector() { return (Vector) SelectedMapVector.clone(); } /** * add a ScalarMap applying to this Data * @param map ScalarMap to add */ public void addSelectedMapVector(ScalarMap map) { if (renderer == null) return; // 'synchronized' unnecessary // (since prepareData is a single Thread, but ...) synchronized (SelectedMapVector) { if (!SelectedMapVector.contains(map)) { SelectedMapVector.addElement(map); } } } /** * clear Vectors of ScalarMaps applying to this Data and * of ConstantMaps; also clear other instance variables * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void clearMaps() throws RemoteException, VisADException { Enumeration maps; synchronized (ConstantMapVector) { maps = ConstantMapVector.elements(); while(maps.hasMoreElements()) { ConstantMap map = (ConstantMap) maps.nextElement(); map.nullDisplay(); } ConstantMapVector.removeAllElements(); SelectedMapVector.removeAllElements(); shadow = null; data = null; renderer = null; } } /** * Prepare to render data (include feasibility check); * @return false if infeasible * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public boolean prepareData() throws VisADException, RemoteException { if (renderer == null) return false; int[] indices; int[] display_indices; int[] value_indices; int levelOfDifficulty; data = ((DataReference) ref).getData(); if (data == null) { renderer.clearExceptions(); renderer.addException( // new DisplayException("Data is null: DataDisplayLink.prepareData")); new DisplayException("Data is null")); return false; } MathType type = data.getType(); SelectedMapVector.removeAllElements(); // calculate default values for DisplayRealTypes // lowest priority: DisplayRealType.DefaultValue int n = ((DisplayImpl) local_action).getDisplayScalarCount(); defaultValues = new float[n]; GraphicsModeControl mode = ((DisplayImpl) local_action).getGraphicsModeControl(); for (int i=0; i<n; i++) { DisplayRealType dreal = (DisplayRealType) ((DisplayImpl) local_action).getDisplayScalar(i); defaultValues[i] = (float) dreal.getDefaultValue(); if (Display.PointSize.equals(dreal)) { defaultValues[i] = mode.getPointSize(); } else if (Display.LineWidth.equals(dreal)) { defaultValues[i] = mode.getLineWidth(); } else if (Display.LineStyle.equals(dreal)) { defaultValues[i] = mode.getLineStyle(); } else if (Display.PolygonMode.equals(dreal)) { defaultValues[i] = mode.getPolygonMode(); } else if (Display.PolygonOffset.equals(dreal)) { defaultValues[i] = mode.getPolygonOffset(); } else if (Display.PolygonOffsetFactor.equals(dreal)) { defaultValues[i] = mode.getPolygonOffsetFactor(); } else if (Display.ColorMode.equals(dreal)) { defaultValues[i] = mode.getColorMode(); } else if (Display.CurvedSize.equals(dreal)) { defaultValues[i] = mode.getCurvedSize(); } else if (Display.MissingTransparent.equals(dreal)) { defaultValues[i] = (mode.getMissingTransparent()) ? 1 : -1; } else if (Display.TextureEnable.equals(dreal)) { defaultValues[i] = (mode.getTextureEnable()) ? 1 : -1; } else if (Display.AdjustProjectionSeam.equals(dreal)) { defaultValues[i] = (mode.getAdjustProjectionSeam()) ? 1 : -1; } else if (Display.Texture3DMode.equals(dreal)) { defaultValues[i] = mode.getTexture3DMode(); } else if (Display.CacheAppearances.equals(dreal)) { defaultValues[i] = (mode.getCacheAppearances()) ? 1 : -1; } else if (Display.MergeGeometries.equals(dreal)) { defaultValues[i] = (mode.getMergeGeometries()) ? 1 : -1; } else if (Display.PointMode.equals(dreal)) { defaultValues[i] = (mode.getPointMode()) ? 1 : -1; } /* WLH 21 Aug 98 defaultValues[i] = (float) (((DisplayRealType) ((DisplayImpl) local_action).getDisplayScalar(i)).getDefaultValue()); */ } // middle priority: DisplayImpl.ConstantMapVector Vector temp = (Vector) ((DisplayImpl) local_action).getConstantMapVector().clone(); Enumeration maps = temp.elements(); /* WLH 13 July 98 Enumeration maps = ((DisplayImpl) local_action).getConstantMapVector().elements(); */ while(maps.hasMoreElements()) { ConstantMap map = (ConstantMap) maps.nextElement(); defaultValues[map.getDisplayScalarIndex()] = (float) map.getConstant(); } // highest priority: this.ConstantMapVector // WLH 13 July 98 maps =((Vector) ConstantMapVector.clone()).elements(); while(maps.hasMoreElements()) { ConstantMap map = (ConstantMap) maps.nextElement(); // WLH 10 Aug 2001 int index = map.getDisplayScalarIndex(); if (index >= 0) defaultValues[index] = (float) map.getConstant(); // defaultValues[map.getDisplayScalarIndex()] = (float) map.getConstant(); } try { renderer.clearExceptions(); DisplayImpl local_dpy = (DisplayImpl )local_action; shadow = type.buildShadowType(this, null); ShadowType adaptedShadow = shadow.getAdaptedShadowType(); indices = ShadowType.zeroIndices(local_dpy.getScalarCount()); display_indices = ShadowType.zeroIndices( local_dpy.getDisplayScalarCount()); value_indices = ShadowType.zeroIndices(local_dpy.getValueArrayLength()); final int numControls = local_dpy.getNumberOfControls(); isTransform = new boolean[numControls]; for (int i=0; i<numControls; i++) isTransform[i] = false; levelOfDifficulty = shadow.checkIndices(indices, display_indices, value_indices, isTransform, ShadowType.NOTHING_MAPPED); if (levelOfDifficulty == ShadowType.LEGAL) { // every Control isTransform for merely LEGAL // (i.e., the 'dots') rendering for (int i=0; i<numControls; i++) isTransform[i] = true; } renderer.checkDirect(); } catch (BadMappingException e) { data = null; renderer.addException(e); return false; } catch (UnimplementedException e) { data = null; renderer.addException(e); return false; } catch (RemoteException e) { data = null; renderer.addException(e); return false; } // can now render data return true; } /** * @return ShadowType generated from MathType of linked Data */ public ShadowType getShadow() { return shadow; } /** * @return linked Data (note Data is cached until * clearData() is called) * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public Data getData() throws VisADException, RemoteException { if (renderer == null) return null; Data data_copy = data; if (data_copy == null) { data_copy = ((DataReference) ref).getData(); } data = data_copy; return data_copy; } /** * clear cached copy of linked Data */ public void clearData() { data = null; } /** * @return MathType of linked Data * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public MathType getType() throws VisADException, RemoteException { Data d = getData(); return (d == null) ? null : d.getType(); } /** * @return default values for DisplayRealTypes */ public float[] getDefaultValues() { return defaultValues; } /** * @return linked DataReference */ public DataReference getDataReference() { return (DataReference) getThingReference(); } /** * @return Vector of ConstantMaps specific to this Data */ public Vector getConstantMaps() { return ConstantMapVector; } /** * @return Vector of ScalarMaps that apply to this Data */ public Vector getScalarMaps() { return SelectedMapVector; } /** * Indicates whether or not this instance is equal to an object * @param o the object in question. * @return <code>true</code> if and only if this instance equals o. */ public boolean equals(Object o) { if (!(o instanceof DataDisplayLink)) { return false; } DataDisplayLink ddl = (DataDisplayLink )o; if (!getDataReference().equals(ddl.getDataReference())) { return false; } if (!getDisplay().equals(ddl.getDisplay())) { return false; } return true; } }