//
// ContourControl.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.rmi.RemoteException;
import java.util.StringTokenizer;
import visad.browser.Convert;
import visad.util.Util;
/**
* ContourControl is the VisAD class for controlling IsoContour display scalars.
*/
public class ContourControl extends Control {
private static final long serialVersionUID = 1L;
// number below is from GUI control, it is approximately
// how many times a contour line will (attempt to) be labeled
public static final int LABEL_FREQ_LO = 1;
public static final int LABEL_FREQ_MED = 5;
public static final int LABEL_FREQ_HI = 9;
// default and (somewhat arbitrary) values for labeling every Nth line
public static final int EVERY_NTH_MIN = 0;
public static final int EVERY_NTH_DEFAULT = 2;
public static final int EVERY_NTH_MAX = 100;
private boolean mainContours;
// for 3-D mainContours
private float surfaceValue;
// for 2-D mainContours
// these are the 'old' descriptors for 2-D contour lines
private float contourInterval;
private float lowLimit;
private float hiLimit;
private float base;
private boolean labels;
private int labelFreq = LABEL_FREQ_LO;
private int everyNth = EVERY_NTH_DEFAULT;
private boolean public_set = false; // application called setLevels()
//
// these are the 'new' descriptors for 2-D contour lines
// includes lowLimit, hiLimit and base from the 'old' descriptors
// true if contourInterval is valid
private boolean arithmeticProgression = true;
// contour line levels
private float[] levels = null;
private int lineStyle = GraphicsModeControl.SOLID_STYLE;
private boolean dash = false;
private boolean horizontalContourSlice;
private boolean verticalContourSlice;
private float horizontalSliceLow;
private float horizontalSliceHi;
private float horizontalSliceStep;
private float verticalSliceLow;
private float verticalSliceHi;
private float verticalSliceStep;
boolean contourFill;
private static double init_scale = Double.NaN;
private static double default_init_scale = 0.40;
private boolean autoSizeLabels = true; // BMF 2009-03-05 change default to true
private boolean alignLabels = true;
private double labelSizeFactor = 1;
private transient ZoomDoneListener zoom;
private ProjectionControl pcntrl;
private ControlListener projListener;
private double ratio = 1.20;
// label color defaults to white
private byte[] labelColor = null;
private boolean colorSet = false;
private Object labelFont = null;
//private Object labelFont = new java.awt.Font("Dialog", java.awt.Font.PLAIN, 14);
/**
* Construct a new ContourControl for the display
* @param d Display to associate with this
*/
public ContourControl(DisplayImpl d) {
super(d);
mainContours = true;
labels = false;
surfaceValue = Float.NaN;
contourInterval = Float.NaN;
lowLimit = Float.NaN;
hiLimit = Float.NaN;
base = Float.NaN;
horizontalContourSlice = false;
verticalContourSlice = false;
horizontalSliceLow = Float.NaN;
horizontalSliceHi = Float.NaN;
horizontalSliceStep = Float.NaN;
verticalSliceLow = Float.NaN;
verticalSliceHi = Float.NaN;
verticalSliceStep = Float.NaN;
contourFill = false;
pcntrl = d.getProjectionControl();
double[] matrix = pcntrl.getMatrix();
double[] rot = new double[3];
double[] trans = new double[3];
double[] scale = new double[1];
MouseBehavior mouse = d.getMouseBehavior();
double projScale;
if (mouse != null) {
mouse.instance_unmake_matrix(rot, scale, trans, matrix);
if (init_scale != init_scale) init_scale = scale[0];
projScale = scale[0];
}
else {
init_scale = default_init_scale;
projScale = init_scale;
}
zoom = new ZoomDoneListener(this, pcntrl, mouse, projScale);
d.addDisplayListener(zoom);
}
/**
* set parameters for IsoContour depictions, if not already set
* @param bvalues must be dimensioned boolean[2], where
* bvalues[0] enable contours
* bvalues[1] enable labels (if applicable)
* @param fvalues must be dimensioned float[5], where
* fvalues[0] level for iso-surface
* fvalues[1] interval for iso-lines
* fvalues[2] low limit for iso-lines
* fvalues[3] high limit for iso-lines
* fvalues[4] base for iso-lines
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
void setMainContours(boolean[] bvalues, float[] fvalues)
throws VisADException, RemoteException {
setMainContours(bvalues, fvalues, false, false);
}
/**
* set parameters for IsoContour depictions, if not already set
* @param bvalues must be dimensioned boolean[2], where
* bvalues[0] enable contours
* bvalues[1] enable labels (if applicable)
* @param fvalues must be dimensioned float[5], where
* fvalues[0] level for iso-surface
* fvalues[1] interval for iso-lines
* fvalues[2] low limit for iso-lines
* fvalues[3] high limit for iso-lines
* fvalues[4] base for iso-lines
* @param noChange true to not trigger re-transform (false for
* auto-scale)
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
void setMainContours(boolean[] bvalues, float[] fvalues, boolean noChange)
throws VisADException, RemoteException {
setMainContours(bvalues, fvalues, noChange, false);
}
/**
* set parameters for IsoContour depictions
* @param bvalues must be dimensioned boolean[2], where
* bvalues[0] enable contours
* bvalues[1] enable labels (if applicable)
* @param fvalues must be dimensioned float[5], where
* fvalues[0] level for iso-surface
* fvalues[1] interval for iso-lines
* fvalues[2] low limit for iso-lines
* fvalues[3] high limit for iso-lines
* fvalues[4] base for iso-lines
* @param noChange true to not trigger re-transform (false for
* auto-scale)
* @param override true to set float values even if already set
* (i.e., not NaNs)
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
void setMainContours(boolean[] bvalues, float[] fvalues, boolean noChange,
boolean override)
throws VisADException, RemoteException {
if (fvalues == null || fvalues.length != 5 ||
bvalues == null || bvalues.length != 2) {
throw new DisplayException("ContourControl.setMainContours: " +
"bad array length");
}
boolean setLevels = false;
float[] levs = null;
boolean[] dashes = null;
float myBase = 0;
synchronized(this) {
mainContours = bvalues[0];
labels = bvalues[1];
// WLH 13 Sept 2000
if (override) {
surfaceValue = fvalues[0];
contourInterval = fvalues[1];
lowLimit = fvalues[2];
hiLimit = fvalues[3];
base = fvalues[4];
}
else {
if (surfaceValue != surfaceValue) surfaceValue = fvalues[0];
if (contourInterval != contourInterval) contourInterval = fvalues[1];
if (lowLimit != lowLimit) lowLimit = fvalues[2];
if (hiLimit != hiLimit) hiLimit = fvalues[3];
if (base != base) base = fvalues[4];
}
// adapt to 'new' descriptors
if (arithmeticProgression) {
if (contourInterval == contourInterval && base == base &&
lowLimit == lowLimit && hiLimit == hiLimit) {
dashes = new boolean[] {false};
levs =
Contour2D.intervalToLevels(contourInterval, lowLimit, hiLimit, base, dashes);
myBase = base;
setLevels = true;
}
else {
dash = false;
levels = null;
}
}
}
/**
* The following methods are "alien" because they are outside the control of
* this class. If they were invoked in a synchronized block, then deadlock
* could occur if one of the methods waits on a thread that it creates that
* calls back into this class and tries to obtain a lock (it's happened
* before). See Item 49 (Avoid Excessive Synchronization) in Joshua Bloch's
* "Effective Java" for more information.
*/
if (setLevels)
setLevels(levs, myBase, dashes[0], false);
changeControl(!noChange);
}
/**
* Set level for iso-surfaces
* @param value value of the iso-surface to display
* @throws VisADException VisAD error
* @throws RemoteException Java RMI failure.
*/
public void setSurfaceValue(float value)
throws VisADException, RemoteException {
setSurfaceValue(value, false);
}
/**
* Set level for iso-surfaces
* @param value value of the iso-surface to display
* @param setLevels true if this should be used for contour levels
* @throws VisADException VisAD error
* @throws RemoteException Java RMI failure.
*/
public void setSurfaceValue(float value, boolean setLevels)
throws VisADException, RemoteException {
boolean change;
synchronized(this) {
change = !Util.isApproximatelyEqual(surfaceValue, value);
surfaceValue = value;
if (setLevels) levels = new float[] {value};
}
if (change) {
/**
* The following method is "alien" because it is outside the control of
* this class. If it were invoked in a synchronized block, then deadlock
* could occur if the method waits on a thread that it creates that
* calls back into this class and tries to obtain a lock (it's happened
* before). See Item 49 (Avoid Excessive Synchronization) in Joshua
* Bloch's "Effective Java" for more information.
*/
changeControl(true);
}
}
/**
* Sets the parameters for contour iso-lines. This method invokes the
* {@link ControlListener#controlChanged(ControlEvent)} method of all
* registered listeners;
*
* @param interval The contour interval. Must be non-zero. If
* negative, then contour lines below the base will
* be dashed. Must not be NaN.
* @param low The minimum contour value. No contour line less
* than this value will be drawn. Must not be NaN.
* @param hi The maximum contour value. No contour line
* greater than this value will be drawn. Must not
* be NaN.
* @param ba The base contour value. The contour lines will
* be integer multiples of the interval away from
* this value. Must not be NaN.
* @throws VisADException The interval is zero or too small.
* @throws RemoteException Java RMI failure.
*/
public void setContourInterval(float interval, float low,
float hi, float ba)
throws VisADException, RemoteException {
public_set = true;
float[] levs;
float myBase;
boolean[] dashes = {false};
boolean change;
synchronized(this) {
change = (contourInterval != interval) || (base != ba) ||
!Util.isApproximatelyEqual(lowLimit, low) ||
!Util.isApproximatelyEqual(hiLimit, hi);
contourInterval = interval;
lowLimit = low;
hiLimit = hi;
myBase = base = ba;
// adapt to 'new' descriptors
levs =
Contour2D.intervalToLevels(contourInterval, lowLimit, hiLimit, base, dashes);
arithmeticProgression = true;
}
/**
* The following method is "alien" because it is outside the control of this
* class. If it were invoked in a synchronized block, then deadlock could
* occur if the method waits on a thread that it creates that calls back
* into this class and tries to obtain a lock (it's happened before). See
* Item 49 (Avoid Excessive Synchronization) in Joshua Bloch's "Effective
* Java" for more information.
*/
setLevels(levs, myBase, dashes[0], change);
}
/**
* Set low and high iso-line levels
* @param low The minimum contour value. No contour line less
* than this value will be drawn. Must not be NaN.
* @param hi The maximum contour value. No contour line
* greater than this value will be drawn. Must not
* be NaN.
* @throws VisADException VisAD error
* @throws RemoteException Java RMI failure.
*/
public void setContourLimits(float low, float hi)
throws VisADException, RemoteException {
public_set = true;
boolean change;
boolean setLevels;
float[] levs = null;
float myBase = 0;
boolean[] dashes = null;
synchronized(this) {
change = !Util.isApproximatelyEqual(lowLimit, low) ||
!Util.isApproximatelyEqual(hiLimit, hi);
lowLimit = low;
hiLimit = hi;
// adapt to 'new' descriptors
if (arithmeticProgression) {
setLevels = true;
dashes = new boolean[] {false};
levs =
Contour2D.intervalToLevels(contourInterval, lowLimit, hiLimit, base, dashes);
myBase = base;
}
else {
setLevels = false;
int n = 0;
for (int i=0; i<levels.length; i++) {
if (lowLimit < levels[i] && levels[i] < hiLimit) n++;
}
if (n != levels.length) {
levs = new float[n];
int k = 0;
for (int i=0; i<levels.length; i++) {
if (lowLimit < levels[i] && levels[i] < hiLimit) levs[k++] = levels[i];
}
levels = levs;
}
else {
change = false;
}
}
}
/**
* The following methods are "alien" because they are outside the control of
* this class. If they were invoked in a synchronized block, then deadlock
* could occur if one of the methods waits on a thread that it creates that
* calls back into this class and tries to obtain a lock (it's happened
* before). See Item 49 (Avoid Excessive Synchronization) in Joshua Bloch's
* "Effective Java" for more information.
*/
if (setLevels)
setLevels(levs, myBase, dashes[0], false);
if (change) changeControl(true);
}
/**
* @return boolean indicating whether levels have
* been set by other than auto-scale
*/
public boolean getPublicSet() {
return public_set;
}
/**
* Set arbitrary levels for 2-D contour lines;
* levels below base are dashed if dash == true
* @param levels An array of contour values to display.
* @param base The base contour value for dashing. Levels
* below base are dashed if dash is true
* @param dash flag for making dashed contours below the
* base contour value.
* @throws VisADException VisAD error
* @throws RemoteException Java RMI failure.
*/
public void setLevels(float[] levels, float base, boolean dash)
throws VisADException, RemoteException {
public_set = true;
setLevels(levels, base, dash, true);
}
private void setLevels(float[] levs, float ba, boolean da,
boolean by_user)
throws VisADException, RemoteException {
if (levs == null) return;
float[] newLevels = new float[levs.length];
float min = Float.MAX_VALUE;
float max = -Float.MAX_VALUE;
for (int i=0; i<levs.length; i++) {
if (levs[i] < min) min = levs[i];
if (levs[i] > max) max = levs[i];
newLevels[i] = levs[i];
}
synchronized(this) {
levels = newLevels;
dash = da;
base = ba;
if (by_user) {
lowLimit = min - Math.abs(.01f * min); // DRM 25-APR-2001
hiLimit = max + Math.abs(.01f * max); // DRM 25-APR-2001
}
}
if (by_user) {
/**
* The following method is "alien" because it is outside the control of
* this class. If it were invoked in a synchronized block, then deadlock
* could occur if the method waits on a thread that it creates that
* calls back into this class and tries to obtain a lock (it's happened
* before). See Item 49 (Avoid Excessive Synchronization) in Joshua
* Bloch's "Effective Java" for more information.
*/
changeControl(true);
}
}
/** get 'new' descriptors for 2-D contour lines
* @param lowhibase must be dimensioned float[3], where
* lowhibase[0] used to return low limit
* lowhibase[1] used to return high limit
* lowhibase[2] used to return base
* @param dashes must be dimensioned boolean[1], where
* dashed[0] used to return dash enable
* @return float[] array of levels for contour curves
*/
public synchronized float[] getLevels(float[] lowhibase, boolean[] dashes) {
float[] levs = null;
if (levels != null) {
levs = new float[levels.length];
System.arraycopy(levels, 0, levs, 0, levels.length);
}
lowhibase[0] = lowLimit;
lowhibase[1] = hiLimit;
lowhibase[2] = base;
dashes[0] = dash;
return levs;
}
/**
* set label enable
* @param on new value for label enable
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
public void enableLabels(boolean on)
throws VisADException, RemoteException {
boolean change;
synchronized(this) {
change = (labels != on);
labels = on;
}
/**
* The following method is "alien" because it is outside the control of this
* class. If it were invoked in a synchronized block, then deadlock could
* occur if the method waits on a thread that it creates that calls back
* into this class and tries to obtain a lock (it's happened before). See
* Item 49 (Avoid Excessive Synchronization) in Joshua Bloch's "Effective
* Java" for more information.
*/
if (change) changeControl(true);
}
/**
* set contour enable
* @param on new value for contour enable
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
public void enableContours(boolean on)
throws VisADException, RemoteException {
boolean change;
synchronized(this) {
change = (mainContours != on);
mainContours = on;
}
/**
* The following method is "alien" because it is outside the control of this
* class. If it were invoked in a synchronized block, then deadlock could
* occur if the method waits on a thread that it creates that calls back
* into this class and tries to obtain a lock (it's happened before). See
* Item 49 (Avoid Excessive Synchronization) in Joshua Bloch's "Effective
* Java" for more information.
*/
if (change) changeControl(true);
}
/**
* get parameters for IsoContour depictions
* @param bvalues must be dimensioned boolean[2], where
* bvalues[0] used to return contour enable
* bvalues[1] used to return label enable
* @param fvalues must be dimensioned float[5], where
* fvalues[0] used to return level for iso-surface
* fvalues[1] used to return interval for iso-lines
* fvalues[2] used to return low limit for iso-lines
* fvalues[3] used to return high limit for iso-lines
* fvalues[4] used to return base for iso-lines
* @throws VisADException a VisAD error occurred
*/
public void getMainContours(boolean[] bvalues, float[] fvalues)
throws VisADException {
if (fvalues == null || fvalues.length != 5 ||
bvalues == null || bvalues.length != 2) {
throw new DisplayException("ContourControl.getMainContours: " +
"bad array length");
}
synchronized(this) {
bvalues[0] = mainContours;
bvalues[1] = labels;
fvalues[0] = surfaceValue;
fvalues[1] = contourInterval;
fvalues[2] = lowLimit;
fvalues[3] = hiLimit;
fvalues[4] = base;
}
}
public void setContourFill(boolean flag)
throws VisADException, RemoteException {
if (flag) {
if (!colorSet)
labelColor = new byte[]{(byte)255, (byte)255, (byte)255, (byte)255};
} else {
if (!colorSet)
labelColor = null;
}
synchronized(this) {
contourFill = flag;
}
/**
* The following method is "alien" because it is outside the control of this
* class. If it were invoked in a synchronized block, then deadlock could
* occur if the method waits on a thread that it creates that calls back
* into this class and tries to obtain a lock (it's happened before). See
* Item 49 (Avoid Excessive Synchronization) in Joshua Bloch's "Effective
* Java" for more information.
*/
changeControl(true);
}
/**
* @return contourFill enable
*/
public synchronized boolean contourFilled() {
return contourFill;
}
/**
* @return initial scale for label auto-size
*/
public static double getInitScale() {
return init_scale;
}
/**
* set enable for label auto-size
* @param flag new value for label auto-size enable
*/
public void setAutoScaleLabels(boolean flag) {
synchronized(this) {
autoSizeLabels = flag;
}
}
/**
* @return label auto-size enable
*/
public boolean getAutoSizeLabels() {
return autoSizeLabels;
}
/**
* Set the contour label alignment policy
* @param flag true: labels follow the contour line, false: labels are horizontal, default: true
* @throws VisADException
* @throws RemoteException
*/
public void setAlignLabels(boolean flag) throws RemoteException, VisADException {
synchronized(this) {
alignLabels = flag;
}
changeControl(true);
}
public boolean getAlignLabels() {
return alignLabels;
}
//BMF 2006-10-04
/**
* Sets the color for label.
* @param color RGB color array, if null label takes contour color
* @throws VisADException
* @throws RemoteException
*/
public void setLabelColor(byte[] color) throws RemoteException, VisADException {
setLabelColor(color, true);
}
//BMF 2006-10-04
/**
* Sets the label color.
* @param color RGB color array, if null label takes contour color
* @param change If false, no {@link visad.ControlEvent} is fired.
* @throws RemoteException
* @throws VisADException
*/
public void setLabelColor(byte[] color, boolean change) throws RemoteException, VisADException {
labelColor = color;
colorSet = true;
changeControl(change);
}
//BMF 2006-10-04
/**
* Gets the label color.
* @return label color as RGB array, null if not set
*/
public byte[] getLabelColor() {
return labelColor;
}
/**
* Set the contour label Font
* @param font can be java.awt.Font or visad.util.HersheyFont. The former are rendered as
* filled polygons which can exhibit co-planar artifacts, the latter as lines
* which will always be visible, front or back, on co-planar surfaces. Can be
* <code>null</code>, the default, which results in the Times Roman HersheyFont.
* @throws VisADException
* @throws RemoteException
*/
public void setLabelFont(Object font) throws RemoteException, VisADException {
synchronized(this) {
labelFont = font;
}
changeControl(true);
}
/**
* Get contour label font
* @return label font
*/
public Object getLabelFont() {
return labelFont;
}
/**
* set label frequency
* @param freq how many labels to attempt per line
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
public void setLabelFreq(int freq)
throws VisADException, RemoteException {
synchronized (this) {
// bounds check the setter value, if out of range set to default
if ((labelFreq < LABEL_FREQ_LO) || (labelFreq > LABEL_FREQ_HI)) {
labelFreq = LABEL_FREQ_LO;
} else {
labelFreq = freq;
}
}
changeControl(true);
}
/**
* @return label frequency (number per line)
*/
public int getLabelFreq() {
return labelFreq;
}
/**
* @return the everyNth
*/
public int getEveryNth() {
return everyNth;
}
/**
* Set how often we label lines
* @param lineCount how many lines to skip before next labeled line
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
public void setEveryNth(int lineCount) throws VisADException, RemoteException {
synchronized (this) {
// bounds check the setter value, if out of range set to bounds
if (lineCount < 1) {
everyNth = EVERY_NTH_DEFAULT;
} else if (lineCount > EVERY_NTH_MAX) {
everyNth = EVERY_NTH_MAX;
} else {
everyNth = lineCount;
}
}
changeControl(true);
}
/**
* set size for label auto-size
* @param factor new size for label auto-size
* @throws VisADException a VisAD error occurred
* @throws RemoteException an RMI error occurred
*/
public void setLabelSize(double factor)
throws VisADException, RemoteException {
synchronized (this) {
labelSizeFactor = factor;
}
changeControl(true);
}
/**
* @return size for label auto-size
*/
public double getLabelSize() {
return labelSizeFactor;
}
/**
* Get the line style for lines that are styled.
* @return One of the line style constants from <code>GraphicsModeControl</code>.
*/
public int getDashedStyle() {
return lineStyle;
}
/**
* Set the line style to apply to dashed lines.
* @param style One of the line style constants from <code>GraphicsModeControl</code>.
* @throws RemoteException
* @throws VisADException
*/
public void setDashedStyle(int style)
throws RemoteException, VisADException {
lineStyle = style;
changeControl(true);
}
/**
* @return String representation of this ContourControl
*/
public synchronized String getSaveString() {
return mainContours + " " + labels + " " + surfaceValue + " " +
contourInterval + " " + lowLimit + " " + hiLimit + " " + base;
}
/**
* reconstruct this ContourControl using the specified save string
* @param save - String representation for reconstruction
* @throws VisADException if a VisAD error occurs
* @throws RemoteException if an RMI error occurs
*/
public void setSaveString(String save)
throws VisADException, RemoteException
{
if (save == null) throw new VisADException("Invalid save string");
StringTokenizer st = new StringTokenizer(save);
if (st.countTokens() < 7) throw new VisADException("Invalid save string");
boolean[] b = new boolean[2];
float[] f = new float[5];
for (int i=0; i<2; i++) b[i] = Convert.getBoolean(st.nextToken());
for (int i=0; i<5; i++) f[i] = Convert.getFloat(st.nextToken());
setMainContours(b, f, false, true);
}
/**
* remove previous projListener from pcntrl, and save cl as
* new projListener
* @param cl new ControlListener for projListener
* @param pcntrl ProjectionControl
*/
public void addProjectionControlListener(ControlListener cl,
ProjectionControl pcntrl)
{
pcntrl.removeControlListener(projListener);
projListener = cl;
}
/**
* copy the state of a remote control to this control
* @param rmt remote Control whose state is copied
* @throws VisADException if a VisAD error occurs
*/
public void syncControl(Control rmt)
throws VisADException
{
if (rmt == null) {
throw new VisADException("Cannot synchronize " + getClass().getName() +
" with null Control object");
}
if (!(rmt instanceof ContourControl)) {
throw new VisADException("Cannot synchronize " + getClass().getName() +
" with " + rmt.getClass().getName());
}
ContourControl cc = (ContourControl )rmt;
boolean changed = false;
synchronized(this) {
synchronized(cc) {
if (mainContours != cc.mainContours) {
changed = true;
mainContours = cc.mainContours;
}
if (!Util.isApproximatelyEqual(surfaceValue, cc.surfaceValue)) {
changed = true;
surfaceValue = cc.surfaceValue;
}
if (!Util.isApproximatelyEqual(contourInterval, cc.contourInterval)) {
changed = true;
contourInterval = cc.contourInterval;
}
if (!Util.isApproximatelyEqual(lowLimit, cc.lowLimit)) {
changed = true;
lowLimit = cc.lowLimit;
}
if (!Util.isApproximatelyEqual(hiLimit, cc.hiLimit)) {
changed = true;
hiLimit = cc.hiLimit;
}
if (!Util.isApproximatelyEqual(base, cc.base)) {
changed = true;
base = cc.base;
}
if (labels != cc.labels) {
changed = true;
labels = cc.labels;
}
if (arithmeticProgression != cc.arithmeticProgression) {
changed = true;
arithmeticProgression = cc.arithmeticProgression;
}
if (cc.levels == null) {
if (levels != null) {
changed = true;
levels = null;
}
} else {
// make sure array lengths match
if (levels == null || levels.length != cc.levels.length) {
changed = true;
levels = new float[cc.levels.length];
for (int i = 0; i < levels.length; i++) {
levels[i] = 0;
}
}
// copy remote values
for (int i = 0; i < levels.length; i++) {
if (!Util.isApproximatelyEqual(levels[i], cc.levels[i])) {
changed = true;
levels[i] = cc.levels[i];
}
}
}
if (dash != cc.dash) {
changed = true;
dash = cc.dash;
}
if (horizontalContourSlice != cc.horizontalContourSlice) {
changed = true;
horizontalContourSlice = cc.horizontalContourSlice;
}
if (verticalContourSlice != cc.verticalContourSlice) {
changed = true;
verticalContourSlice = cc.verticalContourSlice;
}
if (!Util.isApproximatelyEqual(horizontalSliceLow,
cc.horizontalSliceLow))
{
changed = true;
horizontalSliceLow = cc.horizontalSliceLow;
}
if (!Util.isApproximatelyEqual(horizontalSliceHi, cc.horizontalSliceHi)) {
changed = true;
horizontalSliceHi = cc.horizontalSliceHi;
}
if (!Util.isApproximatelyEqual(horizontalSliceStep,
cc.horizontalSliceStep))
{
changed = true;
horizontalSliceStep = cc.horizontalSliceStep;
}
if (!Util.isApproximatelyEqual(verticalSliceLow, cc.verticalSliceLow)) {
changed = true;
verticalSliceLow = cc.verticalSliceLow;
}
if (!Util.isApproximatelyEqual(verticalSliceHi, cc.verticalSliceHi)) {
changed = true;
verticalSliceHi = cc.verticalSliceHi;
}
if (!Util.isApproximatelyEqual(verticalSliceStep, cc.verticalSliceStep)) {
changed = true;
verticalSliceStep = cc.verticalSliceStep;
}
if (contourFill != cc.contourFill) {
changed = true;
contourFill = cc.contourFill;
}
if (autoSizeLabels != cc.autoSizeLabels) {
changed = true;
autoSizeLabels = cc.autoSizeLabels;
}
if (labelSizeFactor != cc.labelSizeFactor) {
changed = true;
labelSizeFactor = cc.labelSizeFactor;
}
if (alignLabels != cc.alignLabels) {
changed = true;
alignLabels = cc.alignLabels;
}
if (labelFont != cc.labelFont) {
changed = true;
labelFont = cc.labelFont;
}
}
}
if (changed) {
try {
/**
* The following method is "alien" because it is outside the control of
* this class. If it were invoked in a synchronized block, then deadlock
* could occur if the method waits on a thread that it creates that
* calls back into this class and tries to obtain a lock (it's happened
* before). See Item 49 (Avoid Excessive Synchronization) in Joshua
* Bloch's "Effective Java" for more information.
*/
changeControl(true);
} catch (RemoteException re) {
throw new VisADException("Could not indicate that control" +
" changed: " + re.getMessage());
}
}
}
/**
* Indicates whether or not this instance equals an Object
* @param o an Object
* @return true if and only if this instance is equal to o
*/
public boolean equals(Object o)
{
if (!super.equals(o)) {
return false;
}
ContourControl cc = (ContourControl )o;
synchronized(this) {
synchronized(cc) {
if (mainContours != cc.mainContours) {
return false;
}
if (!Util.isApproximatelyEqual(surfaceValue, cc.surfaceValue)) {
return false;
}
if (!Util.isApproximatelyEqual(contourInterval, cc.contourInterval)) {
return false;
}
if (!Util.isApproximatelyEqual(lowLimit, cc.lowLimit)) {
return false;
}
if (!Util.isApproximatelyEqual(hiLimit, cc.hiLimit)) {
return false;
}
if (!Util.isApproximatelyEqual(base, cc.base)) {
return false;
}
if (labels != cc.labels) {
return false;
}
if (arithmeticProgression != cc.arithmeticProgression) {
return false;
}
if (levels == null) {
if (cc.levels != null) {
return false;
}
} else {
// make sure array lengths match
if (cc.levels == null || levels.length != cc.levels.length) {
return false;
}
// copy remote values
for (int i = 0; i < levels.length; i++) {
if (!Util.isApproximatelyEqual(levels[i], cc.levels[i])) {
return false;
}
}
}
if (dash != cc.dash) {
return false;
}
if (horizontalContourSlice != cc.horizontalContourSlice) {
return false;
}
if (verticalContourSlice != cc.verticalContourSlice) {
return false;
}
if (!Util.isApproximatelyEqual(horizontalSliceLow,
cc.horizontalSliceLow))
{
return false;
}
if (!Util.isApproximatelyEqual(horizontalSliceHi, cc.horizontalSliceHi)) {
return false;
}
if (!Util.isApproximatelyEqual(horizontalSliceStep,
cc.horizontalSliceStep))
{
return false;
}
if (!Util.isApproximatelyEqual(verticalSliceLow, cc.verticalSliceLow)) {
return false;
}
if (!Util.isApproximatelyEqual(verticalSliceHi, cc.verticalSliceHi)) {
return false;
}
if (!Util.isApproximatelyEqual(verticalSliceStep, cc.verticalSliceStep)) {
return false;
}
if (contourFill != cc.contourFill) {
return false;
}
if (autoSizeLabels != cc.autoSizeLabels) {
return false;
}
if (alignLabels != cc.alignLabels) {
return false;
}
if (labelFont != cc.labelFont) {
return false;
}
if (!Util.isApproximatelyEqual(labelSizeFactor, cc.labelSizeFactor)) {
return false;
}
}
}
return true;
}
/**
* @return a copy of this ContourControl
*/
public synchronized Object clone()
{
ContourControl cc = (ContourControl )super.clone();
if (levels != null) {
cc.levels = (float[] )levels.clone();
}
cc.lineStyle = lineStyle;
return cc;
}
//BMF 2006-10-04 added contourFill condition
/**
* If zoom scale has changed sufficiently, re-transform in
* order to recompute labels.
*
* No action is taken if {@link #setContourFill(boolean)} has been set.
*
* @throws VisADException if a VisAD error occurs
* @throws RemoteException if an RMI error occurs
*/
public void reLabel() throws VisADException, RemoteException {
if (zoom == null || contourFill) return;
zoom.reLabel(ratio);
}
class ZoomDoneListener implements DisplayListener
{
ContourControl c_cntrl;
ProjectionControl p_cntrl;
MouseBehavior mouse;
double last_scale;
ZoomDoneListener(ContourControl c_cntrl, ProjectionControl p_cntrl,
MouseBehavior mouse, double scale) {
this.c_cntrl = c_cntrl;
this.p_cntrl = p_cntrl;
this.mouse = mouse;
last_scale = scale;
}
public void displayChanged(DisplayEvent de)
throws VisADException, RemoteException
{
if (de.getId() == DisplayEvent.MOUSE_RELEASED_LEFT ||
de.getId() == DisplayEvent.MOUSE_RELEASED_RIGHT) {
reLabel(ratio);
}
}
public void reLabel(double ratio)
throws VisADException, RemoteException {
if (!c_cntrl.contourFilled() && autoSizeLabels) {
double[] matrix = p_cntrl.getMatrix();
double[] rot = new double[3];
double[] trans = new double[3];
double[] scale = new double[1];
mouse.instance_unmake_matrix(rot, scale, trans, matrix);
if (scale[0]/last_scale > ratio ||
scale[0]/last_scale < 1/ratio) { //- re-label
if (labels) c_cntrl.changeControl(true);
last_scale = scale[0];
}
}
}
}
/**
* End this control (called by ScalarMap.nullDisplay()). Override
* to remove zoom control listener.
*/
public void nullControl() {
if (projListener != null) {
pcntrl.removeControlListener(projListener);
}
getDisplay().removeDisplayListener(zoom);
zoom = null;
super.nullControl();
}
}