Package simtools.shapes

Source Code of simtools.shapes.CurveShape$CurvePoint

/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info:  http://jsynoptic.sourceforge.net/index.html
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*
* (C) Copyright 1999-2003, by :
*     Corporate:
*         Astrium SAS
*         EADS CRC
*     Individual:
*         Claude Cazenave
*     Nicolas Brodu
*
*
* $Id: CurveShape.java,v 1.39 2008/11/27 11:23:29 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/

package simtools.shapes;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.NumberFormat;

import simtools.data.DataException;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DataSourceListener;
import simtools.data.DataSourcePool;
import simtools.data.NoSuchIndex;
import simtools.data.UnsupportedOperation;
import simtools.shapes.AbstractShape.AbstractShapePropertiesNames;
import simtools.ui.PlotInformationDialog;
import simtools.util.ListenerManager;

public class CurveShape implements Shape, Cloneable, java.io.Serializable, DataSourceListener {

    protected static final long serialVersionUID = 3969204429218131993L;

    protected static final int MAXSTEPS = 4000;

    protected transient DataSource xSource;
    protected transient DataSource ySource;

    protected long min;
    protected long max;

    protected transient double orgx;
    protected transient double orgy;
    protected transient double maxx;
    protected transient double maxy;
    protected transient double scalex;
    protected transient double scaley;

    protected transient boolean useCache;
    protected transient double[] xCache;
    protected transient double[] yCache;
    protected transient double[] ySourceCache;


    protected transient ListenerManager listeners = new ListenerManager();

    protected boolean drawNewOnly;
    protected transient long iteratorMinIndex;
    protected transient long iteratorMaxIndex;
    protected transient boolean xSourceChanged, ySourceChanged;
    protected transient long xSourceNewStartIndex, xSourceNewLastIndex, ySourceNewStartIndex, ySourceNewLastIndex;
    protected transient long lastDrawnIndex;

    protected transient boolean logx, logy;
    /**The value of ln(10), used to compute LOG10_VALUE*/
    public static final double LOG10_VALUE = Math.log(10);
    /** Smallest arbitrarily-close-to-zero value allowed. */
    public static final double LOG_MIN = 1e-1;

    public static final int SLOP_LENGTH = 25;

    // Statistics on current range
    /** Display the specified number of digits in the fraction portion of all curve statistics */
    public static int STATISTIC_ACCURACY = 10;

    /** Display the curve X minimum value in the curve statistic */
    public static boolean SHOW_X_MIN = false;

    /** Display the curve X maximum value in the curve statistic */
    public static boolean SHOW_X_MAX = false;

    /** Display the curve Y minimum value in the curve statistic */
    public static boolean SHOW_Y_MIN = true;

    /** Display the curve Y maximum value in the curve statistic */
    static public boolean SHOW_Y_MAX = true;

    /** Display the curve number of points  in the curve statistic */
    public static boolean SHOW_POINT_NUMBER = true;

    /** Display the curve mean in the curve statistic */
    public static boolean SHOW_MEAN = false;

    /** Display the curve deviation in the curve statistic */
    public static boolean SHOW_DEVIATION = false;

    /** Display the curve integral in the curve statistic */
    public static boolean SHOW_INTEGRAL = false;

    protected transient long localRangeNbPoints;
    protected transient CurvePoint localRangeMaxPoint;
    protected transient CurvePoint localRangeMinPoint;
    protected transient long localMinIndex;
    protected transient long localMaxIndex;

    /** show curve points by drawing a circle on each point position     */
    protected transient boolean showPoints;

    /** Display the curve values using vertical lines   */
    protected transient boolean drawBars;


    /** Local mean:  */
    protected transient double localRangeMean;

    /** Local integral:  */
    protected transient double localRangeIntegral;

    /** Local standard deviation:  */
    protected transient double  localRangeDeviation;


    // Curve point the mouse aims at
    protected transient CurvePoint currentPoint;

    // Curve point defined as reference
    protected transient CurvePoint referencePoint;


    public CurveShape(){
        xSource=null;
        ySource=null;
        min=-1;
        max=-1;
        iteratorMinIndex = -1;
        iteratorMaxIndex = -1;
        xSourceChanged = ySourceChanged = false;
        xSourceNewStartIndex = xSourceNewLastIndex = ySourceNewStartIndex = ySourceNewLastIndex = -1;
        drawNewOnly = false;

        showPoints = false;
        drawBars = false;

        useCache=false;
        xCache=new double[2*MAXSTEPS];
        yCache=new double[2*MAXSTEPS];
        ySourceCache = new double[2*MAXSTEPS];

        localRangeNbPoints = 0;
        localRangeMaxPoint = new CurvePoint();
        localRangeMinPoint = new CurvePoint();
        currentPoint = new CurvePoint();
        referencePoint = null;
    }

    public CurveShape(DataSource xSource, DataSource ySource) {
        this();
        setData(xSource, ySource);
    }



    public void setShowPoints(boolean showPoints) {
        this.showPoints = showPoints;

        if (showPoints && drawBars){
            drawBars = false;
        }
    }

    public void setDrawBars(boolean drawBars) {
        this.drawBars = drawBars;

        if (showPoints && drawBars){
            showPoints = false;
        }
    }

    public void setData(DataSource xSource, DataSource ySource){
        if (this.xSource!=null){
            this.xSource.removeListener(this);
        }
        if (this.ySource!=null){
            this.ySource.removeListener(this);
        }
        this.xSource = xSource;
        this.ySource = ySource;

        this.xSource.addListener(this);
        this.ySource.addListener(this);

        useCache=false;
    }

    public DataSource getXSource() throws DataException{
        if (xSource==null)
            throw new DataException();
        return xSource;
    }

    public DataSource getYSource() throws DataException{
        if (ySource==null)
            throw new DataException();
        return ySource;
    }

    public void setSlice(long min, long max){
        if(min!=min || max!=max){
            useCache=false;
        }
        this.min = min;
        this.max = max;

        if (xSource != null){
            xSource.setSlice(min, max);
        }
        if (ySource != null){
            ySource.setSlice(min, max);
        }
    }

    // Shape Interface
    public boolean contains(double x, double y){
        return getBounds2D().contains(x,y);
    }

    public boolean contains(double x, double y, double w, double h){
        return getBounds2D().contains(x,y,w,h);
    }

    public boolean contains(Point2D p){
        return getBounds2D().contains(p);
    }

    public boolean contains(Rectangle2D r){
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
    }

    public boolean intersects(double x, double y, double w, double h){
        return getBounds2D().intersects(x,y,w,h);
    }

    public boolean intersects(Rectangle2D r){
        return getBounds2D().intersects(r);
    }

    public Rectangle getBounds(){
        Rectangle r=new Rectangle();
        r.setRect(getBounds2D());
        return r;
    }

    public Rectangle2D getBounds2D(){
        if((ySource==null)||(xSource==null)){
            return new Rectangle2D.Double(0.,0.,0.,0.);
        }
        else{
            try {
                return new Rectangle2D.Double(xSource.getDoubleMin(), ySource.getDoubleMin(),
                        xSource.getDoubleMax()-xSource.getDoubleMin(),
                        ySource.getDoubleMax()-ySource.getDoubleMin());
            } catch (UnsupportedOperation uop) {
            } catch (DataException e) {
            }
            return new Rectangle2D.Double(-Double.MAX_VALUE/2, -Double.MAX_VALUE/2, Double.MAX_VALUE, Double.MAX_VALUE);
        }
    }



    /**
     * @param g2 - the current graphics
     * @param ox
     * @param oy
     * @param sx
     * @param sy
     */
    public void drawReferencePoint(Graphics2D g2, double ox, double oy, double sx, double sy){
        if (referencePoint != null) {
            Color oldColor = g2.getColor();
            Stroke oldStroke = g2.getStroke();

            // Get current point coordinates in the graphical frame
            int posX, posY;
            if (logx) {
                if(referencePoint.x < LOG_MIN){
                    referencePoint.x= LOG_MIN;
                }
                posX = (int)Math.round(((Math.log(referencePoint.x)/LOG10_VALUE - ox) * sx));
            } else {
                posX = (int)Math.round(((referencePoint.x - ox) * sx));
            }
            if (logy) {
                if(referencePoint.y < LOG_MIN){
                    referencePoint.y= LOG_MIN;
                }
                posY = (int)Math.round( ((oy - Math.log(referencePoint.y)/LOG10_VALUE) * sy));
            } else {
                posY= (int)Math.round(((oy - referencePoint.y) * sy));
            }

            // draw current point
            g2.setStroke(new BasicStroke());
            g2.setColor(new Color(0,100,0));    //dark green
            g2.fillOval(posX - 6, posY - 6, 12, 12);

            g2.setColor(Color.WHITE);
            g2.fillOval(posX - 5, posY - 5, 10, 10);

            g2.setColor(new Color(0,100,0));    //dark green
            g2.fillOval(posX - 3, posY - 3, 6, 6);

            // Restore old preferences
            g2.setColor(oldColor);
            g2.setStroke(oldStroke);
        }
    }

    /**
     * Draws the computed magnetized point into the graphic
     * @param g2 the current graphics
     */
    public void drawMagnetizedPoint(Graphics2D g2, double ox, double oy, double sx, double sy){
        Color oldColor = g2.getColor();
        Stroke oldStroke = g2.getStroke();

        // Get current point coordinates in the graphical frame
        int posX, posY;
        if (logx) {
            if(currentPoint.x < LOG_MIN){
                currentPoint.x= LOG_MIN;
            }
            posX = (int)Math.round(((Math.log(currentPoint.x)/LOG10_VALUE - ox) * sx));
        } else {
            posX = (int)Math.round(((currentPoint.x - ox) * sx));
        }
        if (logy) {
            if(currentPoint.y < LOG_MIN){
                currentPoint.y= LOG_MIN;
            }
            posY = (int)Math.round( ((oy - Math.log(currentPoint.y)/LOG10_VALUE) * sy));
        } else {
            posY= (int)Math.round(((oy - currentPoint.y) * sy));
        }

        // draw current point
        g2.setColor(Color.RED);
        g2.setStroke(new BasicStroke());
        g2.fillOval(posX - 5, posY - 5, 10, 10);


        // Display slope
        double slopeX =  currentPoint.slop_x * sx;
        double slopeY =  currentPoint.slop_y * sy;

        // Normalize slope vector
        double lenth =  Math.sqrt(slopeX*slopeX + slopeY*slopeY);
        slopeX = (slopeX/lenth) * SLOP_LENGTH;
        slopeY = (slopeY/lenth) * SLOP_LENGTH;

        int slopeEndposX = (int)Math.round(posX  + slopeX );
        int slopeEndposY = (int)Math.round(posY -slopeY);

        g2.setStroke(new BasicStroke(2));
        g2.draw(new Line2D.Double(posX, posY, slopeEndposX, slopeEndposY));

        // Draw arrow
        double  theta = Math.atan2((slopeEndposY-posY),(slopeEndposX-posX));

        AffineTransform affineTransform = new AffineTransform();
        affineTransform.setToTranslation(slopeEndposX, slopeEndposY);
        affineTransform.rotate(theta);
        Polygon first = new Polygon(new int[] {-5,0,-5},new int[] {2,0,-2},3);
        Shape firstArrow = affineTransform.createTransformedShape(first);
        g2.fill(firstArrow);
        g2.draw(firstArrow);

        // Restore old preferences
        g2.setColor(oldColor);
        g2.setStroke(oldStroke);
    }
    /**
     * Method <b>setLogsProperties</b>
     * <br><b>Summary:</b><br>
     * Use this method to set logarithmic modes on curve axis.
     * Parameters:
     * @param _logx     True to use logarithmic mode on x axis. False otherwise.
     * @param _logy     True to use logarithmic mode on y axis. False otherwise.
     *
     */
    public void setLogsProperties(boolean _logx, boolean _logy){
        logx=_logx;
        logy=_logy;
    }

    /**
     * Draws it
     * @param g2 the current graphics
     */
    public void draw(Graphics2D g2, double ox, double oy, double mx, double my, double sx, double sy, int height) {
        if(orgx!=ox || orgy!=oy || maxx!=mx || maxy!=my || scalex!=sx || scaley!=sy){
            useCache=false;
        }
        orgx=ox;
        orgy=oy;
        maxx=mx;
        maxy=my;
        scalex=sx;
        scaley=sy;

        if (drawBars){
            PathIterator p=getPathIterator(null);
            double[] v=new double[2];
            double currentValue=Double.NEGATIVE_INFINITY;
           
            if (p instanceof CurveIterator){
                CurveIterator cu = (CurveIterator)p;

                while(!cu.isDone()){
                    cu.currentSegment(v);
                    if (cu.currentSegmentIsValid){
                        int xpos = (int)Math.round(v[0]);
                        g2.drawLine(xpos, 0, xpos, -height);

                        // display a label on the bar
                        double value = cu.currentYSourceValue;
                        if (value!=currentValue){
                            currentValue=value;
                            String ySourceValue =  new Double(currentValue).toString();
                            LabelShape label = new LabelShape(ySourceValue,xpos, -height,  LabelShape.RIGHTUP, true);
                            label.draw(g2);
                        }
                    }
                    cu.next();
                }
            }

        }else{
            g2.draw(this)
            if(showPoints){
                Color oldColor = g2.getColor();
                float[] hsbs = new float[3];
                Color.RGBtoHSB(oldColor.getRed(), oldColor.getGreen(), oldColor.getBlue(), hsbs);
                float bb=(float)((hsbs[2]+0.5)%1.0);
                Color newColor = Color.getHSBColor(hsbs[0], hsbs[1], bb);
                g2.setColor(newColor);

                Stroke oldStroke = g2.getStroke();
                g2.setStroke(new BasicStroke());

                PathIterator p=getPathIterator(null);
                if (p instanceof CurveIterator){
                    CurveIterator cu = (CurveIterator)p;
                    double[] v=new double[2];
                    while(!cu.isDone()){
                        cu.currentSegment(v);
                       
                        if (cu.currentSegmentIsValid){
                            g2.drawOval((int)Math.round(v[0]-2.),(int)Math.round(v[1]-2.), 4, 4);
                        }
                        cu.next();
                    }
                }
             
                g2.setColor(oldColor);
                g2.setStroke(oldStroke);
            }
        }
    }

    public PathIterator getPathIterator(AffineTransform at){
        if((ySource==null)||(xSource==null)){
            return new NoDrawIterator();
        }
        return new CurveIterator(at,orgx,orgy,maxx, maxy, scalex,scaley);
    }

    public PathIterator getPathIterator(AffineTransform at, double flatness){
        if((ySource==null)||(xSource==null)){
            return new NoDrawIterator();
        }
        return new CurveIterator(at,orgx,orgy,maxx, maxy, scalex,scaley);
    }

    // Shape Interface end

    public class NoDrawIterator implements PathIterator {

        public int currentSegment(double[] coords){
            coords[0] = 0.0;
            coords[1] = 0.0;
            return SEG_MOVETO;
        }

        public int currentSegment(float[] coords){
            coords[0] = 0.0f;
            coords[1] = 0.0f;
            return SEG_MOVETO;
        }

        public int getWindingRule(){
            return WIND_NON_ZERO;
        }

        public boolean isDone() {
            return true;
        }

        public void next(){
        }
    }

    protected AffineTransform _aff;
    public class CurveIterator implements PathIterator {
        protected long _index;
        protected long _maxi;
        protected long _mini;
        protected long _indexStep;
        protected double _ox;
        protected  double _oy;
        protected  double _sx;
        protected double _sy;
        protected double _mx;
        protected double _my;
       
        protected double[] currentValue;
        protected double currentYSourceValue;
       
        /** If current segment is invalid: move to next segment */
        protected boolean currentSegmentIsValid;

        /** currentValue is computed */
        protected boolean currentValueIsComputed;

        protected boolean odd;
        protected double optimizedValue;

        protected boolean firstPoint;
        protected int iCache;
        protected boolean _useCache;

        public CurveIterator(AffineTransform aff, double ox, double oy,
                double mx, double my,
                double sx, double sy) {
            _aff=aff;
            _ox=ox;
            _oy=oy;
            _mx=mx;
            _my=my;
            _sx=sx;
            _sy=sy;
            odd=true;
            iCache=0;
            if (iteratorMinIndex != -1) _mini = _index = iteratorMinIndex;
            else try {
                long minx = xSource.getStartIndex();
                long miny = ySource.getStartIndex();
                // use greater index as min => no out of bounds
                _mini=_index = (minx > miny) ? minx : miny;
                if (min > _mini) _mini = _index = min;


            } catch (UnsupportedOperation uop1) {
                _mini = 0;
                _index = 0;
            }
            if (iteratorMaxIndex != -1) _maxi = iteratorMaxIndex;
            else try {
                long maxx = xSource.getLastIndex();
                long maxy = ySource.getLastIndex();
                // use smaller index as max => no out of bounds
                _maxi = (maxx < maxy) ? maxx : maxy;
                if ((max>=0) && (max < _maxi)) _maxi = max;
            } catch (UnsupportedOperation uop2) {
                _maxi = -1;
            }

            if(xSource.sortedOrder()!=0 && _maxi>_mini ){
                // optimize in case of zoom
                boolean computeStep = (_maxi-_mini)>MAXSTEPS;
                try {
                    double ds=xSource.getDoubleValue(_mini);
                    double dl=xSource.getDoubleValue(_maxi);
                    double ln2 = Math.log(2);

                    if ((ds<_ox) && computeStep){
                        long s = (_maxi-_mini) / ( MAXSTEPS<100? 1 :  MAXSTEPS/100);
                        long min = _mini;
                        long max =_maxi;
                        long pivot;
                        long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                        for(int i =0;i<nbIteration;i++){
                            pivot = (max-min/2 + min;
                            double rr = xSource.getDoubleValue(pivot);
                            if (rr<=_ox){
                                min = pivot;
                            }else{
                                max = pivot;
                            }
                        }
                        if(min>_mini && min<_maxi && xSource.getDoubleValue(min)<=_ox){
                            _index=_mini=min;
                        }
                    }
                    if ((dl>_mx) && computeStep) {
                        long s = (_maxi-_mini) / ( MAXSTEPS<100? 1 :  MAXSTEPS/100);
                        long min = _mini;
                        long max =_maxi;
                        long pivot;
                        long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                        for(int i =0;i<nbIteration;i++){
                            pivot = (max-min/2 + min;
                            double rr = xSource.getDoubleValue(pivot);
                            if (rr<=_mx){
                                min = pivot;
                            }else{
                                max = pivot;
                            }
                        }
                        if(max>_mini && max<_maxi && xSource.getDoubleValue(max)>=_mx){
                            _maxi=max;
                        }
                    }

                } catch (DataException e) {
                }
            }

            _indexStep=1;
            if(_maxi!=-1 && _mini!=-1){
                if((_maxi-_mini)>MAXSTEPS){
                    // we need to keep at least two points
                    // one for the min and one for the max
                    // benefits of this optimisation are
                    // taken into account if we suppress
                    // at least half of the points
                    // thus the step is 2*2=4
                    _indexStep=(_maxi-_mini)/MAXSTEPS;
                    if(_indexStep>=4){
                        //System.out.println("Optim indexStep = "+_indexStep);
                    }
                    else{
                        // not enough benefits, go back
                        // to nominal computation
                        _indexStep=1;
                    }
                }
            }
            currentValue = new double[2];
            currentValueIsComputed = false;
            currentSegmentIsValid = false;
            firstPoint = true;
        }


        /**
         * Tries to compute the current values.
         * <ul>
         <li>Gets the data sources values for the current index, if they exist.
         *  <li>Check if data values are valid.
         *  <li>Apply coordinate conversions and affine transforms.
         */
        protected void computeValues() {
            try {
                updateCurrentValueValue(xSource.getDoubleValue(_index), true, ySource.getDoubleValue(_index), true);

                if (_aff!=null) {
                    _aff.transform(currentValue,0,currentValue,0,1);
                }
                currentSegmentIsValid = true;
            } catch (NoSuchIndex nsi) {
            } catch (DataException de) {
                de.printStackTrace();
                throw new java.util.NoSuchElementException(de.toString());
            }
        }

        /**
         * Method <b>updateCurrentValueValue</b>
         * <br><b>Summary:</b><br>
         * Use this method to update the current Value.
         * Using the log property, and scale if needed.
         *
         */
        protected void updateCurrentValueValue(double x, boolean updateX, double y, boolean updateY){
            if (updateX) {
                if (logx) {
                    if(x < LOG_MIN){
                        x= LOG_MIN;
                    }
                    currentValue[0] = (Math.log(x)/LOG10_VALUE - _ox) * _sx;
                } else {
                    currentValue[0] = (x - _ox) * _sx;
                }
            }
            if (updateY) {
                currentYSourceValue =  y;
                if (logy) {
                    if(y < LOG_MIN){
                        y= LOG_MIN;
                    }
                    currentValue[1] = (_oy - Math.log(y)/LOG10_VALUE) * _sy;
                } else {
                    currentValue[1] = (_oy - y) * _sy;
                }
            }
        }

        // Common to the 2 others
        public int currentSegment(){

            // check user specified bounds
            if ((_maxi!=-1) && (_index>_maxi)) {
                firstPoint = true; // restart from the next valid point
                return SEG_MOVETO;
            }

            // try to get values for current index
            if (!currentValueIsComputed){
                computeValues();
            }
            if (!currentSegmentIsValid) {
                firstPoint = true; // restart from the next valid point
                return SEG_MOVETO;
            }

            // Detects NaN. Spec says x!=x is true if and only if x is NaN
            if ((currentValue[0]!=currentValue[0]) || (currentValue[1]!=currentValue[1])) {
                firstPoint = true; // restart from the next valid point
                return SEG_MOVETO; // Move to nowhere...
            }
            if (firstPoint) {
                firstPoint = false;
                return SEG_MOVETO;
            }
            return SEG_LINETO;
        }



        // PathIterator Interface
        public int currentSegment(double[] coords){
            int ret = currentSegment();
            coords[0] = currentValue[0];
            coords[1] = currentValue[1];
            return ret;
        }

        public int currentSegment(float[] coords){
            int ret = currentSegment();
            coords[0] = (float)currentValue[0];
            coords[1] = (float)currentValue[1];
            return ret;
        }

        public int getWindingRule(){
            return WIND_NON_ZERO;
        }

        public boolean isDone() {
            boolean res;
            // if user specified a range, use it
            if (_maxi != -1) {
                res = _index > _maxi;

            } else {
                // otherwise, try to get new values and see what happens
                if (!currentValueIsComputed) {
                    computeValues();
                }
                res = !currentValueIsComputed; // On success, they exist => not done yet
            }
            return res;
        }

        public void next(){

            currentSegmentIsValid = false;

            if(_indexStep>1) {
                iCache++;
                if(useCache){
                    currentValue[0]=xCache[iCache];
                    currentValue[1]=yCache[iCache];

                    if (_aff!=null) _aff.transform(currentValue,0,currentValue,0,1);

                    _index+=_indexStep;

                    currentSegmentIsValid = true;

                } else {
                    long k=_index+_indexStep;
                    odd=!odd;


                    if(k<=_maxi){ // if _indexStep > 1 _maxi>0
                        if(odd){
                            try {
                                double min=ySource.getDoubleValue(_index);
                                double max=min;
                                double v;
                                for(long i=_index+1;i<k;i++){
                                    // assume x is sorted_xSource.getDoubleValue(i);
                                    v=ySource.getDoubleValue(i);
                                    if(v<min) min=v;
                                    if(v>max) max=v;
                                }
                                if(ySource.sortedOrder()>=0){
                                    v=min;
                                    optimizedValue=max;
                                }
                                else{
                                    v=max;
                                    optimizedValue=min;
                                }

                                updateCurrentValueValue(xSource.getDoubleValue(_index), true, v, true);
                                xCache[iCache]=currentValue[0];
                                yCache[iCache]=currentValue[1];
                                ySourceCache[iCache]=currentYSourceValue;
                                if (_aff!=null) _aff.transform(currentValue,0,currentValue,0,1);

                                currentSegmentIsValid = true;

                            } catch (NoSuchIndex nsi) { 
                            } catch (DataException de) {
                                de.printStackTrace();
                                throw new java.util.NoSuchElementException(de.toString());
                            }
                        }
                        else{
                            try {
                                updateCurrentValueValue(xSource.getDoubleValue(_index), true, optimizedValue, true);
                                xCache[iCache]=currentValue[0];
                                yCache[iCache]=currentValue[1];
                                ySourceCache[iCache]=currentYSourceValue;
                                if (_aff!=null) _aff.transform(currentValue,0,currentValue,0,1);

                                currentSegmentIsValid = true;

                            } catch (DataException e) {
                                e.printStackTrace();
                                throw new java.util.NoSuchElementException(e.toString());
                            }

                        }

                    } else {
                        // end is reached
                        try { // get last value
                            currentValue[0] = (xSource.getDoubleValue(_maxi)-_ox)*_sx;
                            currentValue[1] = (_oy-ySource.getDoubleValue(_maxi))*_sy;
                            xCache[iCache]=currentValue[0];
                            yCache[iCache]=currentValue[1];
                            ySourceCache[iCache]=currentYSourceValue;
                            useCache=true;

                            currentSegmentIsValid = true;

                        } catch (DataException e) {
                            e.printStackTrace();
                            throw new java.util.NoSuchElementException(e.toString());
                        }
                    }
                    _index=k;
                }
                currentValueIsComputed = true;
            }
            else {
                _index++;
                currentValueIsComputed = false;
            }
        }
        // PathIterator Interface end
    }

    //  Take care of serialisation. Special handling for datasources

    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
        out.defaultWriteObject();
        DataSourcePool.global.writeDataSource(out, xSource);
        DataSourcePool.global.writeDataSource(out, ySource);
    }

    private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
        in.defaultReadObject();
        localRangeMaxPoint = new CurvePoint();
        localRangeMinPoint = new CurvePoint();
        currentPoint = new CurvePoint();

        listeners=new ListenerManager();
        xCache=new double[2*MAXSTEPS];
        yCache=new double[2*MAXSTEPS];
        ySourceCache=new double[2*MAXSTEPS];
        useCache=false;
        iteratorMinIndex=-1;
        iteratorMaxIndex=-1;
        xSource = DataSourcePool.global.readDataSource(in);
        ySource = DataSourcePool.global.readDataSource(in);
        if(xSource==null || ySource==null){
            return;
        }
        xSource.addListener(this);
        ySource.addListener(this);
        if (min!=-1) {
            try {
                long ximin = xSource.computeStartIndex();
                long yimin = ySource.computeStartIndex();
                min = (ximin>yimin)? ximin : yimin;
            }
            catch (UnsupportedOperation uo1) {
                min = -1;
            }
        }
        if (max!=-1) {
            try {
                long ximax = xSource.computeLastIndex();
                long yimax = ySource.computeLastIndex();
                max = (ximax<yimax)? ximax : yimax;
            }
            catch (UnsupportedOperation uo1) {
                max = -1;
            }
        }
        if ((min!=-1) && (max!=-1)) {
            xSource.setSlice(min, max);
            ySource.setSlice(min, max);
        }
    }

    /* (non-Javadoc)
     * @see java.lang.Object#clone()
     */
    public Object clone() throws CloneNotSupportedException {
        CurveShape cs = (CurveShape)super.clone();
        // object cloning this curve should re-register on cline
        cs.listeners = new ListenerManager();

        cs.localRangeMaxPoint = new CurvePoint();
        cs.localRangeMinPoint = new CurvePoint();
        cs.currentPoint = new CurvePoint();
        cs.referencePoint = null;

        cs.useCache = false;
        cs.xCache = new double[2*MAXSTEPS];
        cs.yCache = new double[2*MAXSTEPS];
        cs.ySourceCache = new double[2*MAXSTEPS];

        if (cs.xSource != null){
            cs.xSource.addListener(cs);
        }
        if (cs.ySource != null){
            cs.ySource.addListener(cs);
        }
        return cs;
    }

    /* (non-Javadoc)
     * @see simtools.data.DataSourceListener#DataSourceValueChanged(simtools.data.DataSource, long, long)
     */
    public void DataSourceValueChanged(DataSource ds,long minIndex,long maxIndex) {
        // Filter out of range changes
        if ((min!=-1) && (min>maxIndex)) return;
        if ((max!=-1) && (max<minIndex)) return;
        notifyChange();
    }


    /* (non-Javadoc)
     * @see simtools.data.DataSourceListener#DataSourceIndexRangeChanged(simtools.data.DataSource, long, long)
     */
    public void DataSourceIndexRangeChanged(DataSource ds, long startIndex, long lastIndex) {

        if ( (ds.equals(xSource)) || (ds.equals(ySource))) {
            if (ds.equals(xSource)) {
                xSourceChanged = true;
                xSourceNewStartIndex = startIndex;
                xSourceNewLastIndex = lastIndex;
            }
            if (ds.equals(ySource)) {
                ySourceChanged = true;
                ySourceNewStartIndex = startIndex;
                ySourceNewLastIndex = lastIndex;
            }

            // Wait for both sources to change before propagating change info
            if (xSourceChanged && ySourceChanged) {
                long min = Math.max(xSourceNewStartIndex, ySourceNewStartIndex);
                long max = Math.min(xSourceNewLastIndex, ySourceNewLastIndex);

                if ((this.min==min) && (this.max==max)) return; // no change

                if (min!=-1) {
                    this.min = min;
                }
                if (max!=-1) {
                    this.max = max;
                }
                if (drawNewOnly) {
                    iteratorMinIndex = lastDrawnIndex;
                    iteratorMaxIndex = max;
                }
                notifyChange();
            }
        }
    }

    /* (non-Javadoc)
     * @see simtools.data.DataSourceListener#DataSourceInfoChanged(simtools.data.DataSource, simtools.data.DataInfo)
     */
    public void DataSourceInfoChanged(DataSource ds, DataInfo newInfo) {
        // We don't care about that
    }

    /* (non-Javadoc)
     * @see simtools.data.DataSourceListener#DataSourceValueRangeChanged(simtools.data.DataSource, java.lang.Object, java.lang.Object)
     */
    public void DataSourceValueRangeChanged(DataSource ds) {
        notifyChange(); // Our bounds may have changed
    }

    /* (non-Javadoc)
     * @see simtools.data.DataSourceListener#DataSourceOrderChanged(simtools.data.DataSource, int)
     */
    public void DataSourceOrderChanged(DataSource ds, int newOrder) {
        notifyChange(); // May destroy user's optimizations
    }

    /* (non-Javadoc)
     * @see simtools.data.DataSourceListener#DataSourceReplaced(simtools.data.DataSource, simtools.data.DataSource)
     */
    public void DataSourceReplaced(DataSource oldData, DataSource newData) {
        if (xSource==oldData){

            if(xSource!=null){
                xSource.removeListener(this);
            }

            xSource=newData;

            if(xSource!=null){
                xSource.addListener(this);
            }
            synchronized(listeners) {
                int n = listeners.size(); // only one call outside loop
                for (int i=0; i<n; ++i) {
                    CurveShapeListener csl = (CurveShapeListener)listeners.get(i);
                    if (csl!=null) csl.shapeDataChanged(this, oldData,newData,true);
                }
            }
        }
        if (ySource==oldData){

            if(ySource!=null) {
                ySource.removeListener(this);
            }

            ySource=newData;

            if(ySource!=null){
                ySource.addListener(this);
            }
            synchronized(listeners) {
                int n = listeners.size(); // only one call outside loop
                for (int i=0; i<n; ++i) {
                    CurveShapeListener csl = (CurveShapeListener)listeners.get(i);
                    if (csl!=null) csl.shapeDataChanged(this, oldData,newData,true);
                }
            }
        }
    }


    /**
     * Give an opportunity to reload shape attribute when the shape is being restored
     * By default do nothing. This method shall be overloaded by sub classes.
     */
    public void processShapeRestoring(){
        if (xSource != null){
            xSource.addListener(this);
        }
        if (ySource != null){
            ySource.addListener(this);
        }
    }

    /**
     * Give an opportunity to unload shape attribute when the shape is being deleted
     * By default do nothing. This method shall be overloaded by sub classes.
     */
    public void processShapeRemoving(){
        if (xSource != null){
            xSource.removeListener(this);
        }
        if (ySource != null){
            ySource.removeListener(this);
        }
    }



    // Listeners related functions

    public void addListener(CurveShapeListener csl) {
        listeners.add(csl);
    }

    public void removeListener(CurveShapeListener csl) {
        listeners.remove(csl);
    }

    protected void notifyChange() {
        synchronized(listeners) {
            int n = listeners.size(); // only one call outside loop
            for (int i=0; i<n; ++i) {
                CurveShapeListener csl = (CurveShapeListener)listeners.get(i);
                if (csl!=null) csl.shapeChanged(this);
            }
        }
        useCache=false;
    }

    /**
     * @return boolean
     */
    public boolean isDrawNewOnly() {
        return drawNewOnly;
    }

    /**
     * Sets the drawNewOnly.
     * @param drawNewOnly The drawNewOnly to set
     */
    public boolean tryDrawNewOnly() { 
        drawNewOnly = (xSource!= null) &&  (xSource.sortedOrder() != 0);
        return drawNewOnly;
    }

    // Should be synchronized because of the static, or allocate each time
    // For now, keep static to avoid allocation and don't synchronize...
    static private double [] drawOnlyCurrentValue = new double[4];
    public Rectangle2D getDrawNewOnlyArea() {

        if ( (xSource== null) || (xSource.sortedOrder()==0) || (iteratorMinIndex==-1) || (iteratorMinIndex==-1))
            return getBounds();

        // use iterator min/max index
        try {
            drawOnlyCurrentValue[0] (xSource.getDoubleValue(iteratorMinIndex)-orgx)*scalex;
            drawOnlyCurrentValue[1] = (orgy-ySource.getDoubleValue(iteratorMinIndex))*scaley;
            drawOnlyCurrentValue[2] (xSource.getDoubleValue(iteratorMaxIndex)-orgx)*scalex;
            drawOnlyCurrentValue[3] = (orgy-ySource.getDoubleValue(iteratorMaxIndex))*scaley;
        } catch (DataException e) {
            return null;
        }
        // Affine transform on both points at the same time
        if (_aff!=null) _aff.transform(drawOnlyCurrentValue,0,drawOnlyCurrentValue,0,2);

        double x = Math.min(drawOnlyCurrentValue[0],drawOnlyCurrentValue[2]);
        double y = Math.min(drawOnlyCurrentValue[1],drawOnlyCurrentValue[3]);
        double w = Math.max(drawOnlyCurrentValue[0],drawOnlyCurrentValue[2]) - x;
        double h = Math.max(drawOnlyCurrentValue[1],drawOnlyCurrentValue[3]) - y;
        return new Rectangle2D.Double(x,y,w,h);
    }

    protected void computeLocalRangeIndex(double xmin, double xmax) throws DataException{
        if ( xSource==null) {
            throw new DataException();
        }

        // Get curve min and curve max indexes related to current zoom
        double ln2 = Math.log(2);
        localMinIndex =  xSource.getStartIndex();
        localMaxIndex=  xSource.getLastIndex();

        double ds=xSource.getDoubleValue(localMinIndex);  
        double dl=xSource.getDoubleValue(localMaxIndex);

        boolean twoTimes = (localMaxIndex-localMinIndex)>MAXSTEPS;    // get max and min indexes in 2 times

        if (xSource.sortedOrder()==0)
            throw new DataException();

        if(localMaxIndex>localMinIndex){
            // Fisrt compute...
            if (  (( xSource.sortedOrder()==-1 && (ds>xmin)) || ( xSource.sortedOrder()==1 && (ds<xmin))) && twoTimes){
                long mini = localMinIndex;
                long maxi = localMaxIndex;
                long pivot;
                long s = (maxi-mini) / ( MAXSTEPS<100? 1 :  MAXSTEPS/100);

                long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                for(int i =0;i<nbIteration;i++){
                    pivot = (maxi-mini/2 + mini;
                    double rr = xSource.getDoubleValue(pivot);
                    if (rr<=xmin){
                        mini = pivot;
                    }else{
                        maxi = pivot;
                    }
                }
                localMinIndex=mini;
            }
            if (  ((xSource.sortedOrder()==-1 && (dl<xmax)) || (xSource.sortedOrder()==1 && (dl>xmax))) && twoTimes) {
                long mini = localMinIndex;
                long maxi = localMaxIndex;
                long pivot;
                long s = (maxi-mini) / ( MAXSTEPS<100? 1 :  MAXSTEPS/100);
                long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                for(int i =0;i<nbIteration;i++){
                    pivot = (maxi-mini/2 + mini;
                    double rr = xSource.getDoubleValue(pivot);
                    if (rr<=xmax){
                        mini = pivot;
                    }else{
                        maxi = pivot;
                    }
                }
                localMaxIndex=maxi;
            }
        }
        // Second compute (more accurate)  
        ds=xSource.getDoubleValue(localMinIndex)
        dl=xSource.getDoubleValue(localMaxIndex);
        if(localMaxIndex>localMinIndex){
            if  (( xSource.sortedOrder()==-1 && (ds>xmin)) || ( xSource.sortedOrder()==1 && (ds<xmin))){
                long mini = localMinIndex;
                long maxi = (!twoTimes || (localMinIndex + ( MAXSTEPS<100? 1 :  MAXSTEPS/100) >localMaxIndex))? localMaxIndex : localMinIndex + ( MAXSTEPS<100? 1 :  MAXSTEPS/100);
                long pivot;
                long s = (maxi-mini);
                long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                for(int i =0;i<nbIteration;i++){
                    pivot = (maxi-mini/2 + mini;
                    double rr = xSource.getDoubleValue(pivot);
                    if (rr<=xmin){
                        mini = pivot;
                    }else{
                        maxi = pivot;
                    }
                }
                if (xSource.getDoubleValue(mini) == xmin)
                    localMinIndex=mini;
                else
                    localMinIndex=mini+1;
            }
            if ((xSource.sortedOrder()==-1 && (dl<xmax)) || (xSource.sortedOrder()==1 && (dl>xmax))) {
                long mini = (!twoTimes || (localMinIndex + ( MAXSTEPS<100? 1 :  MAXSTEPS/100) >localMaxIndex))? localMinIndex : localMaxIndex - ( MAXSTEPS<100? 1 :  MAXSTEPS/100);
                long maxi = localMaxIndex;
                long pivot;
                long s = (maxi-mini);
                long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                for(int i =0;i<nbIteration;i++){
                    pivot = (maxi-mini/2 + mini;
                    double rr = xSource.getDoubleValue(pivot);
                    if (rr<=xmax){
                        mini = pivot;
                    }else{
                        maxi = pivot;
                    }
                }
                if (xSource.getDoubleValue(maxi) == xmax)
                    localMaxIndex=maxi;
                else
                    localMaxIndex=maxi-1;
            }
        }
        if (localMaxIndex<localMinIndex){
            localMaxIndex = localMinIndex;
        }

    }

    public void computeYLocalRange(double xmin, double xmax) throws DataException{
        if ( (xSource==null) || (ySource==null)) {
            throw new DataException();
        }
        computeLocalRangeIndex(xmin, xmax);

        //  maxAfter is true if the point found for max position is after xmax
        boolean maxAfter  = xSource.getDoubleValue(localMaxIndex) > xmax;

        // minAfter is true if the point found for min position is before xmax
        boolean minAfter = xSource.getDoubleValue(localMinIndex) > xmax;

        // maxBefore is true if the point found for max position is before xmin
        boolean maxBefore  = xSource.getDoubleValue(localMaxIndex) < xmin;

        // minBefore is true if the point found for min position is before xmin
        boolean minBefore = xSource.getDoubleValue(localMinIndex) < xmin;


        localRangeNbPoints =0;


        if ( (minBefore && maxBefore) || (maxAfter && minAfter)){
            // No point
            localRangeNbPoints=0;
            localRangeMaxPoint.x = 0;
            localRangeMaxPoint.y = 0;
            localRangeMinPoint.x = 0;
            localRangeMinPoint.y = 0;

        } else {

            // Get max and min curve values, between curveMinIndex and curveMaxIndex
            localRangeMinPoint.x = Double.MAX_VALUE;
            localRangeMaxPoint.x = -  Double.MAX_VALUE;

            localRangeMinPoint.y =  Double.MAX_VALUE;
            localRangeMaxPoint.y = -  Double.MAX_VALUE;

            for(long index=localMinIndex; index<=localMaxIndex; index++){
                localRangeNbPoints++;

                double yvalue = ySource.getDoubleValue(index);
                double xvalue = xSource.getDoubleValue(index);

                if (yvalue<localRangeMinPoint.y) {
                    localRangeMinPoint.y = yvalue;
                    localRangeMinPoint.x = xvalue;

                }
                if (yvalue>localRangeMaxPoint.y) {
                    localRangeMaxPoint.y = yvalue;
                    localRangeMaxPoint.x = xvalue;
                }
            }
        }
    }

    /**
     * Method computeCurveStatisticsAndIntegral
     * Compute min, max, mean, and integral for a function y=f(x) in a specified interval [xmin, xmax]
     * Data source attached to X values <b>must be sorted</b>, otherwise no compute is performed.
     * @param xmin  fisrt bound of interval in which compute is performed
     * @param xmax  last bound of interval in which compute is performed
     * @throws DataException
     */
    public void computeStatistics(double xmin, double xmax) throws DataException{
        if ( (xSource==null) || (ySource==null)) {
            throw new DataException();
        }
        computeLocalRangeIndex(xmin, xmax);

        //  maxAfter is true if the point found for max position is after xmax
        boolean maxAfter  = xSource.getDoubleValue(localMaxIndex) > xmax;

        // minAfter is true if the point found for min position is before xmax
        boolean minAfter = xSource.getDoubleValue(localMinIndex) > xmax;

        // maxBefore is true if the point found for max position is before xmin
        boolean maxBefore  = xSource.getDoubleValue(localMaxIndex) < xmin;

        // minBefore is true if the point found for min position is before xmin
        boolean minBefore = xSource.getDoubleValue(localMinIndex) < xmin;

        localRangeNbPoints = 0;

        if ( (minBefore && maxBefore) || (maxAfter && minAfter)){
            // No point
            localRangeNbPoints=0;
            localRangeMaxPoint.x = 0;
            localRangeMaxPoint.y = 0;
            localRangeMinPoint.x = 0;
            localRangeMinPoint.y = 0;
            localRangeMean = 0;
            localRangeIntegral=0;
            localRangeDeviation = 0;

        } else {
            localRangeMinPoint.x = Double.MAX_VALUE;
            localRangeMaxPoint.x = -  Double.MAX_VALUE;

            localRangeMinPoint.y =  Double.MAX_VALUE;
            localRangeMaxPoint.y = -  Double.MAX_VALUE;

            localRangeMean = 0;
            localRangeIntegral=0;
            localRangeDeviation=0;

            if ((localMaxIndex - localMinIndex)>=2)
                localRangeIntegral=  (xSource.getDoubleValue(localMinIndex+1) - xSource.getDoubleValue(localMinIndex)) * (ySource.getDoubleValue(localMinIndex+1) - ySource.getDoubleValue(localMinIndex));

            // Compute mean, max, min, integral, deviation

            for(long index=localMinIndex; index<=localMaxIndex; index++){
                localRangeNbPoints++;

                double yvalue = ySource.getDoubleValue(index);
                double xvalue = xSource.getDoubleValue(index);

                if (yvalue < localRangeMinPoint.y) {
                    localRangeMinPoint.y = yvalue;
                    localRangeMinPoint.x = xvalue;

                }
                if (yvalue > localRangeMaxPoint.y) {
                    localRangeMaxPoint.y = yvalue;
                    localRangeMaxPoint.x = xvalue;
                }

                localRangeMean += yvalue;

                if (index!=localMaxIndex){
                    //TODO if index + 1 is NOT valid ?
                            localRangeIntegral+=  (xSource.getDoubleValue(index+1) - xvalue) * ((ySource.getDoubleValue(index+1) + yvalue)/2);
                }
            }
            localRangeMean /= localRangeNbPoints;


            // once mean has been computed, standard deviation can be computed also
            // Compute mean, max, min, integral, deviation
            for(long index=localMinIndex; index<=localMaxIndex; index++){
                double yvalue = ySource.getDoubleValue(index);
                localRangeDeviation += Math.pow( (yvalue - localRangeMean), 2);
            }

            localRangeDeviation /= localRangeNbPoints;
            localRangeDeviation = Math.sqrt(localRangeDeviation);
        }
    }


    public void setReferencePoint(CurvePoint curvePoint) {
        if (curvePoint != null) {
            referencePoint = new CurvePoint(curvePoint);

        } else {
            referencePoint = null;
        }
    }


    /**
     * Get curve point the closest to X mouse position
     * Set this point as current point.
     * x data source values must sorted in descending or ascending order.
     * y data source indexes must fit with x indexes
     * If x and y data sources are not compliant with these contraints, an exception is thrown 
     *
     * - Compute magnetized point slope with next point (or previous point if it is the last point)
     */
    public void setCurrentPoint(double pos_x) throws DataException{

        if ( (xSource==null) || (ySource==null))
            throw new DataException();

        double ln2 = Math.log(2);
        long magnetizedPointMin =  xSource.getStartIndex();
        long magnetizedPointMax =  xSource.getLastIndex();

        boolean twoTimes = (magnetizedPointMax-magnetizedPointMin)>MAXSTEPS;  // get max and min indexes in 2 times

        if (xSource.sortedOrder()==0)
            throw new DataException();

        if(magnetizedPointMax>magnetizedPointMin){   
            // Fisrt compute...
            if (twoTimes){
                long mini = magnetizedPointMin;
                long maxi = magnetizedPointMax;
                long pivot;
                long s = (maxi-mini) / ( MAXSTEPS<100? 1 :  MAXSTEPS/100);

                long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                for(int i =0;i<nbIteration;i++){
                    pivot = (maxi-mini/2 + mini;
                    double rr = xSource.getDoubleValue(pivot);
                    if (rr<=pos_x){
                        mini = pivot;
                    }else{
                        maxi = pivot;
                    }
                }
                magnetizedPointMin = mini;
            }

            // Second compute (more accurate) 
            if(magnetizedPointMax>magnetizedPointMin){
                long mini = magnetizedPointMin;
                long maxi = (!twoTimes ||(magnetizedPointMin + ( MAXSTEPS<100? 1 :  MAXSTEPS/100) >magnetizedPointMax)) ? magnetizedPointMax : magnetizedPointMin + ( MAXSTEPS<100? 1 :  MAXSTEPS/100);
                long pivot;
                long s = (maxi-mini);
                long nbIteration = (int)Math.ceil((Math.log(s) /ln2));
                for(int i =0;i<nbIteration;i++){
                    pivot = (maxi-mini/2 + mini;
                    double rr = xSource.getDoubleValue(pivot);
                    if (rr<=pos_x){
                        mini = pivot;
                    }else{
                        maxi = pivot;
                    }
                }
                magnetizedPointMin=mini;
            }
        }

        // Look for nearest point between 2 points surrounding pos_x position
        long magnetizedPoint = (pos_x- xSource.getDoubleValue(magnetizedPointMin) < xSource.getDoubleValue(magnetizedPointMin+1) - pos_x )? magnetizedPointMin : magnetizedPointMin+1;
        // Set _magnetized point x and y:
        currentPoint.x =  xSource.getDoubleValue(magnetizedPoint);
        currentPoint.y = ySource.getDoubleValue(magnetizedPoint)

        // Compute slope
        if (xSource.getLastIndex()-xSource.getStartIndex()>=2){
            if (xSource.getLastIndex()!=magnetizedPoint){
                currentPoint.slop_y = ySource.getDoubleValue(magnetizedPoint+1) - ySource.getDoubleValue(magnetizedPoint);
                currentPoint.slop_x = xSource.getDoubleValue(magnetizedPoint+1) - xSource.getDoubleValue(magnetizedPoint);
            }else{
                currentPoint.slop_y =  -(ySource.getDoubleValue(magnetizedPoint) - ySource.getDoubleValue(magnetizedPoint-1));
                currentPoint.slop_x = - (xSource.getDoubleValue(magnetizedPoint) - xSource.getDoubleValue(magnetizedPoint-1));
            }
        }
    }


    public CurvePoint getLocalRangeMaxPoint(){
        return new CurvePoint(localRangeMaxPoint);
    }

    public CurvePoint getLocalRangeMinPoint(){
        return new CurvePoint(localRangeMinPoint);
    }

    public long getLocalNbPoints() {
        return localRangeNbPoints;
    }

    public double getLocalRangeMean() {
        return localRangeMean;
    }
    public CurvePoint getCurrentPoint() {
        return  new CurvePoint(currentPoint);
    }

    public CurvePoint getReferencePoint() {
        if (referencePoint != null) {
            return  new CurvePoint(referencePoint);
        }
        return null;
    }

    public double getLocalIntegralValue() {
        return localRangeIntegral;
    }

    public double getLocalDeviationValue() {
        return localRangeDeviation;
    }


    /**
     * @return a list of information about the curve.
     *
     */

    public String getCurveInformation(double xmin, double xmax){
        try{
            computeStatistics(xmin, xmax);
            NumberFormat nf = NumberFormat.getInstance();
            nf.setMaximumFractionDigits(STATISTIC_ACCURACY);   

            String curveStat ="";

            if (SHOW_X_MIN){
                curveStat += "X min = " + nf.format(localRangeMinPoint.x+ "; ";
            }
            if (SHOW_X_MAX){
                curveStat += "X max = " + nf.format(localRangeMaxPoint.x+ "; ";
            }
            if (SHOW_Y_MIN){
                curveStat += "Y min = " + nf.format(localRangeMinPoint.y+ "; ";
            }
            if (SHOW_Y_MAX){
                curveStat += "Y max = " + nf.format(localRangeMaxPoint.y+ "; ";
            }
            if (SHOW_POINT_NUMBER){
                curveStat += "Nb pts = " + getLocalNbPoints()  + "; ";
            }
            if (SHOW_MEAN){
                curveStat += "Mean = " + nf.format(getLocalRangeMean()) + "; ";
            }
            if (SHOW_DEVIATION){
                curveStat += "Deviation = " + nf.format(getLocalDeviationValue()) + "; ";
            }
            if (SHOW_INTEGRAL){
                curveStat += "Integral = " + nf.format(getLocalIntegralValue())  + "; ";
            }

            return curveStat;

        }catch (DataException e){
            return ("\t" + PlotInformationDialog.resources.getString("noStatisticsAvailable"));
        }
    }


    public static class CurveShapePropertiesNames extends AbstractShapePropertiesNames{
        private static transient String[] props = new String[] { "PLOT_CURVE_LIST" };

        public CurveShapePropertiesNames(){
            for (int i=0;i<props.length;i++){
                propertyNames.add(props[i]);
            }
        }
    }


    /**
     * A curve point representing a location in (x, y) coordinate space, specified in double precision.
     * @author zxpletran007
     *
     */
    public static class CurvePoint {
        public double x;
        public double y;
        public double slop_x;
        public double slop_y;

        public CurvePoint(CurvePoint ref){
            this(ref.x,ref.y,ref.slop_x,ref.slop_y);
        }

        public CurvePoint(){
            this(0,0,0,0);
        }

        public CurvePoint(double x, double y) {
            this(x,y,0,0);
        }

        public CurvePoint(double x, double y, double slop_x, double slop_y) {
            this.x = x;
            this.y = y;
            this.slop_x = slop_x;
            this.slop_y = slop_y;
        }  
    }



}
TOP

Related Classes of simtools.shapes.CurveShape$CurvePoint

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
cript','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');