/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Icy 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.math; import icy.common.CollapsibleEvent; import icy.common.EventHierarchicalChecker; import icy.common.UpdateEventHandler; import icy.common.listener.ChangeListener; import icy.file.xml.XMLPersistent; import icy.type.TypeUtil; import icy.type.collection.array.ArrayUtil; import icy.util.XMLUtil; import javax.swing.event.EventListenerList; import org.w3c.dom.Node; /** * @author stephane */ public class Scaler implements ChangeListener, XMLPersistent { private enum ScalerRange { SR_ABSIN, SR_IN, SR_OUT }; private static final String ID_ABSLEFTIN = "absleftin"; private static final String ID_ABSRIGHTIN = "absrightin"; private static final String ID_LEFTIN = "leftin"; private static final String ID_RIGHTIN = "rightin"; private static final String ID_LEFTOUT = "leftout"; private static final String ID_RIGHTOUT = "rightout"; private static final String ID_INTEGERDATA = "integerdata"; private static final String ID_CANCROSS = "cancross"; private double absLeftIn; private double absRightIn; private double leftIn; private double rightIn; private double leftOut; private double rightOut; private double scaler; private double unscaler; private boolean integerData; private boolean canCross; private boolean crossed; public double scaleLK[]; private final EventListenerList listeners; /** * internal updater */ private final UpdateEventHandler updater; public static int indexOf(Scaler[] scalers, Scaler scaler) { for (int i = 0; i < scalers.length; i++) if (scalers[i].equals(scaler)) return i; return -1; } public static boolean contains(Scaler[] scalers, Scaler scaler) { return (indexOf(scalers, scaler) != -1); } /** * */ public Scaler(double leftIn, double rightIn, double leftOut, double rightOut, boolean integerData) { this(leftIn, rightIn, leftIn, rightIn, leftOut, rightOut, integerData, false); } /** * */ public Scaler(double leftIn, double rightIn, double leftOut, double rightOut, boolean integerData, boolean canCross) { this(leftIn, rightIn, leftIn, rightIn, leftOut, rightOut, integerData, canCross); } /** * */ public Scaler(double absLeftIn, double absRightIn, double leftIn, double rightIn, double leftOut, double rightOut, boolean integerData, boolean canCross) { super(); this.absLeftIn = absLeftIn; this.absRightIn = absRightIn; this.leftIn = leftIn; this.rightIn = rightIn; this.leftOut = leftOut; this.rightOut = rightOut; this.integerData = integerData; this.canCross = canCross; crossed = absLeftIn > absRightIn; if (crossed && !canCross) throw new IllegalArgumentException("Can't create scaler : left > right and canCross = false"); listeners = new EventListenerList(); updater = new UpdateEventHandler(this, false); // update scaler updateScaler(false); } /** * Refresh the scale lookup table */ private void updateLookup() { scaleLK = null; if (integerData) { final boolean rangeOk; if (crossed) rangeOk = (absLeftIn <= 65535) && (absRightIn >= 0); else rangeOk = (absLeftIn >= 0) && (absRightIn <= 65535); // use lookup table only for integer scalar value in [0..65535] range if (rangeOk) { final int len; if (crossed) len = (int) absRightIn; else len = (int) absLeftIn; scaleLK = new double[len]; // refresh lookup table data for (int i = 0; i < len; i++) scaleLK[i] = scale(i); } } } /** * Refresh the scaler value */ private void updateScaler(boolean notify) { final double deltaIn = rightIn - leftIn; final double deltaOut = rightOut - leftOut; // delta null if ((deltaIn == 0) || (deltaOut == 0)) scaler = 1; else { scaler = deltaOut / deltaIn; unscaler = deltaIn / deltaOut; } // refresh lookup table updateLookup(); // notify scaler changed if (notify) changed(); } private void checkBounds() { double l = leftIn; double r = rightIn; if (crossed) { // check absolute range first if (l > absLeftIn) { l = absLeftIn; if (r > l) r = l - Float.MIN_VALUE; } if (r < absRightIn) { r = absRightIn; if (l < r) l = r + Float.MIN_VALUE; } } else { // check absolute range first if (l < absLeftIn) { l = absLeftIn; if (r < l) r = l + Float.MIN_VALUE; } if (r > absRightIn) { r = absRightIn; if (l > r) l = r - Float.MIN_VALUE; } } // set left and right for input value leftIn = l; rightIn = r; } /** * Sets the left and right value of specified range. * * @param left * the new left value to set * @param right * the new right value to set * @param range * range to modify * @param leftPrio * priority to left border (to resolve conflict) */ private void setLeftRight(double left, double right, ScalerRange range, boolean leftPrio) { double l = left; double r = right; if ((!canCross) && (l > r)) { if (leftPrio) r = l + Float.MIN_VALUE; else l = r - Float.MIN_VALUE; } switch (range) { case SR_ABSIN: // nothing to do if ((absLeftIn == l) && (absRightIn == r)) return; // update crossed information crossed = l > r; // set absolute left and right for input value absLeftIn = l; absRightIn = r; // adjust current left and right for input value if they are out bounds checkBounds(); break; case SR_IN: // nothing to do if ((leftIn == l) && (rightIn == r)) return; // update crossed information updated only on absolute // crossed = l > r; // set left and right for input value leftIn = l; rightIn = r; // adjust current left and right for input value if they are out bounds checkBounds(); break; case SR_OUT: // nothing to do if ((leftOut == l) && (rightOut == r)) return; // set left and right for output value leftOut = l; rightOut = r; break; } // update scaler updateScaler(true); } /** * Scale the value * * @param value * value to scale * @return scaled output value */ public double scale(double value) { if (crossed) { if (value >= leftIn) return leftOut; else if (value <= rightIn) return rightOut; else return ((value - leftIn) * scaler) + leftOut; } if (value <= leftIn) return leftOut; else if (value >= rightIn) return rightOut; else return ((value - leftIn) * scaler) + leftOut; } /** * Scale the value * * @param value * value to scale * @return scaled output value */ public double unscale(double value) { if (crossed) { if (value >= leftOut) return leftIn; else if (value <= rightOut) return rightIn; else return ((value - leftOut) * unscaler) + leftIn; } if (value <= leftOut) return leftIn; else if (value >= rightOut) return rightIn; else return ((value - leftOut) * unscaler) + leftIn; } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of byte (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of int (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(Object src, int srcOffset, int[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); switch (ArrayUtil.getDataType(src)) { case BYTE: scale((byte[]) src, srcOffset, dest, destOffset, len, signed); break; case SHORT: scale((short[]) src, srcOffset, dest, destOffset, len, signed); break; case INT: scale((int[]) src, srcOffset, dest, destOffset, len, signed); break; case LONG: scale((long[]) src, srcOffset, dest, destOffset, len, signed); break; case FLOAT: scale((float[]) src, srcOffset, dest, destOffset, len); break; case DOUBLE: scale((double[]) src, srcOffset, dest, destOffset, len); break; } } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of byte (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of int (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(byte[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of short (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of int (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(short[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of int (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of int (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(int[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of long (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of int (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(long[] src, int srcOffset, int[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale array * * @param src * array of float (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of int (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute */ public void scale(float[] src, int srcOffset, int[] dest, int destOffset, int len) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(src[srcOffset + i]); } /** * Scale array * * @param src * array of double (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of int (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute */ public void scale(double[] src, int srcOffset, int[] dest, int destOffset, int len) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); for (int i = 0; i < len; i++) dest[destOffset + i] = (int) scale(src[srcOffset + i]); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of byte (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of double (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(byte[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of short (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of double (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(short[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of int (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of double (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(int[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of long (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of double (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute * @param signed * signed/unsigned src data flag */ public void scale(long[] src, int srcOffset, double[] dest, int destOffset, int len, boolean signed) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); if (signed) { // signed for (int i = 0; i < len; i++) dest[destOffset + i] = scale(src[srcOffset + i]); } else { // unsigned for (int i = 0; i < len; i++) dest[destOffset + i] = scale(TypeUtil.unsign(src[srcOffset + i])); } } /** * Scale array * * @param src * array of float (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of double (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute */ public void scale(float[] src, int srcOffset, double[] dest, int destOffset, int len) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); for (int i = 0; i < len; i++) dest[destOffset + i] = scale(src[srcOffset + i]); } /** * Scale array * * @param src * array of double (unscaled values) * @param srcOffset * offset for src buffer * @param dest * result as array of double (scaled values) * @param destOffset * offset for dest buffer * @param len * length to compute */ public void scale(double[] src, int srcOffset, double[] dest, int destOffset, int len) { if ((src == null) || (dest == null)) throw new IllegalArgumentException("Parameters 'src' and 'dest' should not be null !"); for (int i = 0; i < len; i++) dest[destOffset + i] = scale(src[srcOffset + i]); } /** * Scale array * * @param data * array of float value to scale * @param offset * offset for buffer * @param len * length to compute */ public void scale(float[] data, int offset, int len) { if (data == null) throw new IllegalArgumentException("Parameters 'data' should not be null !"); for (int i = 0; i < len; i++) data[offset + i] = (float) scale(data[i]); } /** * Scale array * * @param data * array of double value to scale * @param offset * offset for buffer * @param len * length to compute */ public void scale(double[] data, int offset, int len) { if (data == null) throw new IllegalArgumentException("Parameters 'data' should not be null !"); for (int i = 0; i < len; i++) data[offset + i] = scale(data[i]); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of byte (unscaled values) * @param dest * result as array of int (scaled values) * @param signed * signed/unsigned src data flag */ public void scale(Object src, int[] dest, boolean signed) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of byte (unscaled values) * @param dest * result as array of int (scaled values) * @param signed * signed/unsigned src data flag */ public void scale(byte[] src, int[] dest, boolean signed) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of short (unscaled values) * @param dest * result as array of int (scaled values) * @param signed * signed/unsigned src data flag */ public void scale(short[] src, int[] dest, boolean signed) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of int (unscaled values) * @param dest * result as array of int (scaled values) * @param signed * signed/unsigned src data flag */ public void scale(int[] src, int[] dest, boolean signed) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed); } /** * Scale array * * @param src * array of float (unscaled values) * @param dest * result as array of int (scaled values) */ public void scale(float[] src, int[] dest) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src)); } /** * Scale array * * @param src * array of double (unscaled values) * @param dest * result as array of int (scaled values) */ public void scale(double[] src, int[] dest) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src)); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of byte (unscaled values) * @param dest * result as array of double (scaled values) * @param signed * signed/unsigned src data flag */ public void scale(byte[] src, double[] dest, boolean signed) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of short (unscaled values) * @param dest * result as array of double (scaled values) * @param signed * signed/unsigned src data flag */ public void scale(short[] src, double[] dest, boolean signed) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed); } /** * Scale each value in the "src" array and return result in "dest" array * * @param src * array of int (unscaled values) * @param dest * result as array of double (scaled values) * @param signed * signed/unsigned src data flag */ public void scale(int[] src, double[] dest, boolean signed) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src), signed); } /** * Scale array * * @param src * array of float (unscaled values) * @param dest * result as array of double (scaled values) */ public void scale(float[] src, double[] dest) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src)); } /** * Scale array * * @param src * array of double (unscaled values) * @param dest * result as array of double (scaled values) */ public void scale(double[] src, double[] dest) { scale(src, 0, dest, 0, ArrayUtil.getTotalLength(src)); } /** * Scale array * * @param data * array of float value to scale */ public void scale(float[] data) { scale(data, 0, data.length); } /** * Scale array * * @param data * array of double value to scale */ public void scale(double[] data) { scale(data, 0, data.length); } /** * Return the scaler value * * @return the scaler value */ public double getScaler() { return scaler; } /** * @return the integerData */ public boolean isIntegerData() { return integerData; } /** * @return the crossed flag */ public boolean isCrossed() { return crossed; } /** * Return true if scaler doesn't change value (input = output) */ public boolean isNull() { return (leftIn == leftOut) && (rightIn == rightOut); } /** * @return the canCross */ public boolean getCanCross() { return canCross; } /** * @param canCross * the canCross to set */ public void setCanCross(boolean canCross) { if (this.canCross != canCross) { // crossed and we can't anymore... if (!canCross && crossed) { final double ali = absLeftIn; final double ari = absRightIn; final double li = leftIn; final double ri = rightIn; // uncross setLeftRight(ari, ali, ScalerRange.SR_ABSIN, true); setLeftRight(ri, li, ScalerRange.SR_IN, true); } this.canCross = canCross; } } /** * @return the absLeftIn */ public double getAbsLeftIn() { return absLeftIn; } /** * @param absLeftIn * the absLeftIn to set */ public void setAbsLeftIn(double absLeftIn) { setLeftRight(absLeftIn, absRightIn, ScalerRange.SR_ABSIN, true); } /** * @return the absRightIn */ public double getAbsRightIn() { return absRightIn; } /** * @param absRightIn * the absRightIn to set */ public void setAbsRightIn(double absRightIn) { setLeftRight(absLeftIn, absRightIn, ScalerRange.SR_ABSIN, false); } /** * @return the leftIn */ public double getLeftIn() { return leftIn; } /** * @param leftIn * the leftIn to set */ public void setLeftIn(double leftIn) { setLeftRight(leftIn, rightIn, ScalerRange.SR_IN, true); } /** * @return the rightIn */ public double getRightIn() { return rightIn; } /** * @param rightIn * the rightIn to set */ public void setRightIn(double rightIn) { setLeftRight(leftIn, rightIn, ScalerRange.SR_IN, false); } /** * @return the leftOut */ public double getLeftOut() { return leftOut; } /** * @param leftOut * the leftOut to set */ public void setLeftOut(double leftOut) { setLeftRight(leftOut, rightOut, ScalerRange.SR_OUT, true); } /** * @return the rightOut */ public double getRightOut() { return rightOut; } /** * @param rightOut * the rightOut to set */ public void setRightOut(double rightOut) { setLeftRight(leftOut, rightOut, ScalerRange.SR_OUT, false); } /** * @param left * the leftAbsIn to set * @param right * the rightAbsIn to set */ public void setAbsLeftRightIn(double left, double right) { setLeftRight(left, right, ScalerRange.SR_ABSIN, false); } /** * @param left * the leftIn to set * @param right * the rightIn to set */ public void setLeftRightIn(double left, double right) { setLeftRight(left, right, ScalerRange.SR_IN, false); } /** * @param left * the leftOut to set * @param right * the rightOut to set */ public void setLeftRightOut(double left, double right) { setLeftRight(left, right, ScalerRange.SR_OUT, false); } /** * fire event */ public void fireEvent(ScalerEvent e) { for (ScalerListener listener : listeners.getListeners(ScalerListener.class)) listener.scalerChanged(e); } /** * Add a listener * * @param listener */ public void addListener(ScalerListener listener) { listeners.add(ScalerListener.class, listener); } /** * Remove a listener * * @param listener */ public void removeListener(ScalerListener listener) { listeners.remove(ScalerListener.class, listener); } @Override public void onChanged(CollapsibleEvent compare) { final ScalerEvent event = (ScalerEvent) compare; // notify listener we have changed fireEvent(event); } /** * process on change */ private void changed() { // handle changed via updater object updater.changed(new ScalerEvent(this)); } public void beginUpdate() { updater.beginUpdate(); } public void endUpdate() { updater.endUpdate(); } public boolean isUpdating() { return updater.isUpdating(); } @Override public boolean loadFromXML(Node node) { if (node == null) return false; beginUpdate(); try { double l, r; setCanCross(XMLUtil.getElementBooleanValue(node, ID_CANCROSS, false)); integerData = XMLUtil.getElementBooleanValue(node, ID_INTEGERDATA, false); l = XMLUtil.getElementDoubleValue(node, ID_ABSLEFTIN, 0d); r = XMLUtil.getElementDoubleValue(node, ID_ABSRIGHTIN, 0d); setLeftRight(l, r, ScalerRange.SR_ABSIN, true); l = XMLUtil.getElementDoubleValue(node, ID_LEFTIN, 0d); r = XMLUtil.getElementDoubleValue(node, ID_RIGHTIN, 0d); setLeftRight(l, r, ScalerRange.SR_IN, true); l = XMLUtil.getElementDoubleValue(node, ID_LEFTOUT, 0d); r = XMLUtil.getElementDoubleValue(node, ID_RIGHTOUT, 0d); setLeftRight(l, r, ScalerRange.SR_OUT, true); } finally { endUpdate(); } return true; } @Override public boolean saveToXML(Node node) { if (node == null) return false; XMLUtil.setElementBooleanValue(node, ID_CANCROSS, getCanCross()); XMLUtil.setElementBooleanValue(node, ID_INTEGERDATA, isIntegerData()); XMLUtil.setElementDoubleValue(node, ID_ABSLEFTIN, getAbsLeftIn()); XMLUtil.setElementDoubleValue(node, ID_ABSRIGHTIN, getAbsRightIn()); XMLUtil.setElementDoubleValue(node, ID_LEFTIN, getLeftIn()); XMLUtil.setElementDoubleValue(node, ID_RIGHTIN, getRightIn()); XMLUtil.setElementDoubleValue(node, ID_LEFTOUT, getLeftOut()); XMLUtil.setElementDoubleValue(node, ID_RIGHTOUT, getRightOut()); return true; } }