package curve;

import curve.*;
import java.awt.*;

//Drawcurve class will link to a number of curves and draw them on a
//graph.
public class DrawCurve extends Canvas {
  //Object properties
  protected ListCurve curves;//Reference to Curve Objects
  protected int numPoints; //number of points in curve plot
  protected double pointStep; //increase in t for each point
  
  protected DoubleRect curveRect; //MinMax values for curve
  protected Rectangle clipBounds; //clipping area bounds
  protected Graphics g; //global variable for my canvas
  protected AnimatePanel animatePanel; //the animation panel
  
  //Variables for animation procedures to share
  protected int animateCtr; //animation counter (for array length)
  protected int animateTextX,animateTextY; //x,y position for the text
  protected String animateS; //String to print
  protected boolean animateOn;
  
  //--------------------------------------------------------------------
  // Start up
  //--------------------------------------------------------------------
  //Constructor defaults to 100 points on curve (actually 101, inc 0.0 and 1.0)
  public DrawCurve() {
    numPoints=100;
    init();
  };

  //Constructor receives number of plotting points
  public DrawCurve(int aNumPoints) {
    if(aNumPoints>0) { //Validate number
      numPoints=aNumPoints;
    } else {
      numPoints=100;
    };
    init();
  };

  //Set up links and background
  private void init() {
    animatePanel=null;
    pointStep=1.0/numPoints;
    setBackground(new Color(0xFFFFFF));
    addMouseListener(new MyMouseAdapter());
    addMouseMotionListener(new MyMouseMotionAdapter());
    curves=new ListCurve();
  };

  //Add a curve to the list
  public void addCurve(Curve newCurve) {
    curves.addCurve(newCurve);
  };

  public int setAnimatePanel(AnimatePanel animatePanel) {
    this.animatePanel=animatePanel;
    return numPoints;
  };
  //--------------------------------------------------------------------
  // Drawing Curve
  //--------------------------------------------------------------------

  //Gets min max values from all the curves
  protected void getMinMax() {
    Curve currentCurve;
    currentCurve=curves.getFirstCurve();
//    System.out.println("GetMinMax:");
    curveRect=currentCurve.getMinMax();
//    System.out.println(currentCurve.getMinMax()+" -> "+curveRect);
    while((currentCurve=curves.getNextCurve(currentCurve))!=null) {
      curveRect.extendTo(currentCurve.getMinMax());
//      System.out.println(currentCurve.getMinMax()+" -> "+curveRect);
    };
  };

  //Get bounds for graph display; 30 pixel boundary (40 at bottom)
  protected void getGraphBounds() {
    //Get min max values of drawing surface
    clipBounds=getBounds();
    //Graphics are relative to top-left of canvas, not window
    clipBounds.x=30;
    clipBounds.y=30;
    clipBounds.width-=60;
    clipBounds.height-=70;
  };

  //Make x&y scales equal
  protected void rescaleXYAxes() {
    if(curveRect.height/curveRect.width<
    ((double)clipBounds.height)/((double)clipBounds.width)) {
      //Increase curveheight
      curveRect.height=curveRect.width*clipBounds.height/clipBounds.width;
    } else {
      //increase width
      curveRect.width=curveRect.height*clipBounds.width/clipBounds.height;
    };
  };

  //Paint routine draws curves from data received from Curve object
  public void paint(Graphics newG) {
    Rectangle graphicsBounds;
    g=getGraphics();//newG; //Set global graphics to newG
    graphicsBounds=getBounds();
    g.setPaintMode();
    if(animateOn) {
      showAnimate(numPoints); //Will tidy everything up
      animateOn=false;
    };
    g.setColor(Color.white);
    g.clearRect(0,0,graphicsBounds.width-1,graphicsBounds.height-1);
    g.setColor(Color.lightGray);
    g.drawRect(0,0,graphicsBounds.width-1,graphicsBounds.height-1);
    if(curves.getFirstCurve()!=null) {
      g.setXORMode(Color.white);
      g.setColor(Color.black);

      setForeground(new Color(0));
      //Get min max values of curve
      getMinMax();

      //if curve is wider than 0 pixels in boths directions
      if(curveRect.width>0 && curveRect.height>0) {
        getGraphBounds(); //Get graph drawing area to clipBounds
        rescaleXYAxes(); //Make axes x,y same scale
        drawAxes(); //Draw axes
        drawCurves();
      };
    };
    //Set text location
    animateTextX=clipBounds.x;
    animateTextY=clipBounds.y+clipBounds.height+37;
  };

  //Draws the curve
  protected void drawCurve(Curve curve) {
    Coordinate point;
    double param; //Parameter, between 0 and 1, to pass to Curve
    int ctr;
    int x[],y[];

    x=new int[numPoints+1];
    y=new int[numPoints+1];
    if(curve!=null) {
      g.setColor(curve.color);
      param=0;
      for(ctr=0;ctr<=numPoints;ctr++) {
        point=curve.getValue(param);
        x[ctr]=curveToClipX(point.x);
        y[ctr]=curveToClipY(point.y);
        param+=pointStep;
      };
      g.drawPolyline(x,y,numPoints+1);
    };
    curves.setCurveXY(curve,x,y);
  };

  protected void drawCurves() {
    Curve currentCurve;
    //Draw Curves
    g=getGraphics();
    g.setXORMode(Color.white);

    //Hide animation if that's on; hide constructs too
    if(animateOn) {
      showAnimate(numPoints);
      animateOn=false;
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {};
    };
    currentCurve=curves.getFirstCurve();
    if(animatePanel!=null) animatePanel.reset();
    currentCurve=curves.getFirstCurve();
    if(currentCurve!=null)
    do {
      g.setColor(currentCurve.color);
      drawCurve(currentCurve);
      //Draw construction
      currentCurve.drawConstructs();
    } while((currentCurve=curves.getNextCurve(currentCurve))!=null);
  };

  protected void drawCurvesOnly() {
    Curve currentCurve;
    //Draw Curves only
    currentCurve=curves.getFirstCurve();
    if(currentCurve!=null)
    do {
      g.setColor(currentCurve.color);
      drawCurve(currentCurve);
    } while((currentCurve=curves.getNextCurve(currentCurve))!=null);
  };

  //Draws the two axes
  protected void drawAxes() {
    int xAxis,yAxis;
    double axisCount;
    int textX,textY;
    String s;
    AxisCalculator axis;
    Font font,largerFont;
    FontMetrics fontMetrics,largerFontMetrics;

    //Create two fonts, one larger than the other
    font=new Font("SansSerif",Font.PLAIN,10);
    largerFont=new Font(font.getName(),font.getStyle(),font.getSize()+4);
    fontMetrics=g.getFontMetrics(font);
    largerFontMetrics=g.getFontMetrics(largerFont);

    //Set font
    g.setFont(font);
    g.setColor(Color.black);
    g.setPaintMode();

    //Find where x axis should lie
    xAxis=curveToClipY(0);
    if(xAxis<clipBounds.y) xAxis=clipBounds.y; //too low
    if(xAxis>clipBounds.y+clipBounds.height-1) //too high
    xAxis=clipBounds.y+clipBounds.height-1;
    g.drawLine(clipBounds.x,xAxis,clipBounds.x+clipBounds.width-1,xAxis);

    //Find where y axis should lie
    yAxis=curveToClipX(0);
    if(yAxis<clipBounds.x) yAxis=clipBounds.x; //to the left
    if(yAxis>clipBounds.x+clipBounds.width-1) //to the right
    yAxis=clipBounds.y+clipBounds.width-1;
    g.drawLine(yAxis,clipBounds.y,yAxis,clipBounds.y+clipBounds.height-1);


    //Set up for x axis labels
    axis=new AxisCalculator(curveRect.width/5);
    //Select first count label
    axisCount=Math.ceil(0.49+curveRect.x/(axis.labelSpacing*pow(10,axis.labelPower)))*
    axis.labelSpacing*pow(10,axis.labelPower);
    //loop
    do {
      addXAxisLabel(xAxis,axisCount,axis.labelPower);
      axisCount+=axis.labelSpacing*pow(10,axis.labelPower);
    } while(axisCount+axis.labelSpacing*pow(10,axis.labelPower)/2<
    curveRect.x+curveRect.width);

    //Add end labels
    addXAxisLabel(xAxis,curveRect.x,axis.labelPower);
    addXAxisLabel(xAxis,curveRect.x+curveRect.width,axis.labelPower);

    //Add axis main label
    makeXLabel(xAxis,font,largerFont,axis);

    g.setFont(font);
    //Set up for y axis labels
    axis=new AxisCalculator(curveRect.height/5);
    //Select first count label
    axisCount=Math.ceil(0.49+curveRect.y/(axis.labelSpacing*pow(10,axis.labelPower)))*
    axis.labelSpacing*pow(10,axis.labelPower);
    //loop
    do {
      addYAxisLabel(yAxis,axisCount,axis.labelPower);
      axisCount+=axis.labelSpacing*pow(10,axis.labelPower);
    } while(axisCount+axis.labelSpacing*pow(10,axis.labelPower)/2<
    curveRect.y+curveRect.height);

    //Add end labels
    addYAxisLabel(yAxis,curveRect.y,axis.labelPower);
    addYAxisLabel(yAxis,curveRect.y+curveRect.height,axis.labelPower);

    //Add axis main label
    makeYLabel(yAxis,font,largerFont,axis);
    g.setXORMode(Color.white);
  };

  protected void makeXLabel(int xAxis,Font font,Font largerFont,
  AxisCalculator axis) {
    String s;
    int textX,textY;
    FontMetrics fontMetrics,largerFontMetrics;
    //Get two fontmetrics, one larger than the other
    fontMetrics=g.getFontMetrics(font);
    largerFontMetrics=g.getFontMetrics(largerFont);

    g.setPaintMode();
    //Make Power and x label
    textX=clipBounds.x+clipBounds.width;
    textY=xAxis+2*fontMetrics.getAscent();
    textY+=largerFontMetrics.getAscent();
    if(axis.labelPower==0) {
      //Draw "X" only
      s="X";
      textX-=largerFontMetrics.stringWidth(s);
      g.setFont(largerFont);
      g.drawString(s,textX,textY);
    } else {
      g.setFont(font);

      s=new Long(axis.labelPower).toString();
      textX-=fontMetrics.stringWidth(s);
      //Draw power number
      g.drawString(s,textX,textY-fontMetrics.getAscent());

      //Draw rest of string
      s="X, *10";
      textX-=largerFontMetrics.stringWidth(s);
      g.setFont(largerFont);
      g.drawString(s,textX,textY);
    };
    g.setXORMode(Color.white);
  };

  protected void makeYLabel(int yAxis,Font font,Font largerFont,
  AxisCalculator axis) {
    String s;
    int textX,textY;
    FontMetrics fontMetrics,largerFontMetrics;
    //Get two fontmetrics, one larger than the other
    fontMetrics=g.getFontMetrics(font);
    largerFontMetrics=g.getFontMetrics(largerFont);

    g.setPaintMode();
    //Make Power and y label
    textX=yAxis;
    textY=2*fontMetrics.getAscent();
    if(axis.labelPower==0) {
      //Draw "Y" only
      s="Y";
      g.setFont(largerFont);
      g.drawString(s,textX,textY);
    } else {
      //Draw start of string
      s="Y, *10";
      //Change textX if neccessary
      if(clipBounds.x+clipBounds.width-textX<largerFontMetrics.stringWidth(s)+
      fontMetrics.stringWidth(new Long(axis.labelPower).toString()))
      textX=clipBounds.x+clipBounds.width-largerFontMetrics.stringWidth(s)+
      fontMetrics.stringWidth(new Long(axis.labelPower).toString());

      g.setFont(largerFont);
      g.drawString(s,textX,textY);

      textX+=largerFontMetrics.stringWidth(s);
      s=new Long(axis.labelPower).toString();
      //Draw power number
      g.setFont(font);
      g.drawString(s,textX,textY-fontMetrics.getAscent());
    };
    g.setXORMode(Color.white);
  };

  //Adds a label to x Axis at x
  protected void addXAxisLabel(int xAxis,double x,int power) {
    String s;
    int xScreen;
    FontMetrics fontMetrics;

    fontMetrics=g.getFontMetrics();

    g.setPaintMode();
    //Draw grid line
    xScreen=clipBounds.x+(int)((x-curveRect.x)/curveRect.width*clipBounds.width);
    if(x<-0.00001 || x>0.00001) { //ie if x isn't 0
      g.setColor(Color.lightGray);
      g.drawLine(xScreen,clipBounds.y,xScreen,clipBounds.y+clipBounds.height);
      g.setColor(Color.black);
    };

    //Draw dash on axis
    g.drawLine(xScreen,xAxis,xScreen,xAxis+5);

    //Calculate text output
    s=new PrintableDouble(x/pow(10,power)).asStringSF(2);

    //Draw text
    g.drawString(s,xScreen-fontMetrics.stringWidth(s)/2,
      xAxis+7+fontMetrics.getAscent());
    g.setXORMode(Color.white);
  };

  //Adds label to y axis at y
  protected void addYAxisLabel(int yAxis,double y,int power) {
    String s;
    int yScreen;
    int textX;
    FontMetrics fontMetrics;

    g.setPaintMode();
    fontMetrics=g.getFontMetrics();
    //Draw grid line
    yScreen=clipBounds.y+clipBounds.height-1-
    (int)((y-curveRect.y)/curveRect.height*clipBounds.height);
    if(y<-0.00001 || y>0.00001) { //ie if y isn't 0
      g.setColor(Color.lightGray);
      g.drawLine(clipBounds.x,yScreen,clipBounds.x+clipBounds.width,yScreen);
      g.setColor(Color.black);
    };

    //Draw dash on axis
    g.drawLine(yAxis-5,yScreen,yAxis,yScreen);

    //Calculate text output
    s=new PrintableDouble(y/pow(10,power)).asStringSF(2);

    //Draw text output
    textX=yAxis-10-fontMetrics.stringWidth(s);
    if(textX<0) textX=yAxis+5;
    g.drawString(s,textX,yScreen+fontMetrics.getAscent()/3);
    g.setXORMode(Color.white);
  };

  //Draws a line between two points on current global graphics context
  //Called by axis drawer and by some curve objects construction
  public void drawLine(Curve curve,Coordinate oldCo,Coordinate newCo) {
    g.setColor(new Color(curve.color.getRed()/2,
      curve.color.getGreen()/2,curve.color.getBlue()/2));
    g.drawLine(curveToClipX(oldCo.x),curveToClipY(oldCo.y),
      curveToClipX(newCo.x),curveToClipY(newCo.y));
  };

  //Draws a line between two points on current global graphics context
  //in a different colour
  public void drawLine(Curve curve,Coordinate oldCo,
    Coordinate newCo,Color color) {
    g.setColor(color);
    g.drawLine(curveToClipX(oldCo.x),curveToClipY(oldCo.y),
    curveToClipX(newCo.x),curveToClipY(newCo.y));
  };

  private void drawMarker(Coordinate co) {
    int x,y; //Position of marker in screen coordinates
    x=curveToClipX(co.x);
    y=curveToClipY(co.y);
    g.fillOval(x-3,y-3,6,6);
  }
  
  //Draws a marker at a point on current global graphics context
  public void drawMarker(Curve curve,Coordinate co) {
    g.setColor(curve.color);
    drawMarker(co);
  };

  //Draws a marker in a different colour
  public void drawMarker(Curve curve,Coordinate co,Color color) {
    g.setColor(color);
    drawMarker(co);
  };

  private void drawLargerMarker(Coordinate co,int biggerBy) {
    int x,y; //Position of marker in screen coordinates
    x=curveToClipX(co.x);
    y=curveToClipY(co.y);
    g.fillOval(x-3-biggerBy,y-3-biggerBy,6+2*biggerBy,6+2*biggerBy);
  }
  
  //Draws a larger marker at a point on current global graphics context
  public void drawLargerMarker(Curve curve,Coordinate co,int biggerBy) {
    g.setColor(curve.color);
    drawLargerMarker(co,biggerBy);
  };

  //Draws a larger marker in a different colour
  public void drawLargerMarker(Curve curve,Coordinate co,
    int biggerBy,Color color) {
    g.setColor(color);
    drawLargerMarker(co,biggerBy);
  };

  private void drawMarkerP0(Coordinate co) {
    int x,y; //Position of marker in screen coordinates
    FontMetrics fontMetrics;
    Font font;
    String s;
    int polyX[],polyY[];

    x=curveToClipX(co.x);
    y=curveToClipY(co.y);
    g.fillOval(x-3,y-3,6,6);
    
    polyX=new int[10];polyY=new int[10];
    //Draw "P"
    polyX[0]=x-4;polyX[1]=x-4;polyX[2]=x-1;polyX[3]=x;
    polyX[4]=x;polyX[5]=x-1;polyX[6]=x-4;
    polyY[0]=y+12;polyY[1]=y+6;polyY[2]=y+6;polyY[3]=y+7;
    polyY[4]=y+8;polyY[5]=y+9;polyY[6]=y+9;
    g.drawPolyline(polyX,polyY,7);
    //Draw subscript "0"
    polyX[0]=x+2;polyX[1]=x+3;polyX[2]=x+3;polyX[3]=x+2;
    polyX[4]=x+1;polyX[5]=x+1;polyX[6]=x+2;
    polyY[0]=y+10;polyY[1]=y+11;polyY[2]=y+13;polyY[3]=y+14;
    polyY[4]=y+13;polyY[5]=y+11;polyY[6]=y+10;
    g.drawPolyline(polyX,polyY,7);
  };
  
  //Draws a P0 marker at a point on current global graphics context
  public void drawMarkerP0(Curve curve,Coordinate co) {
    g.setColor(curve.color);
    drawMarkerP0(co);  
  };

  //Draws a P0 marker in a dofferent colour
  public void drawMarkerP0(Curve curve,Coordinate co,Color color) {    
    g.setColor(color);
    drawMarkerP0(co);  
  };  

  //Draw an arrow from co in direction dir
  public void drawArrow(Curve curve,Coordinate co,Coordinate dir) {
    double angle,headAngle;
    int polygon[][],xCentre,yCentre;

    g.setColor(curve.color);
    //Prepare to draw arrowhead
    angle=Math.asin(dir.y);
    if(dir.x<0) {
      if(angle>0) angle=Math.PI-angle;
      else angle=-Math.PI-angle;
    };
    polygon=new int[2][3];
    headAngle=Math.PI/4;
    
    xCentre=curveToClipX(co.x);
    yCentre=curveToClipY(co.y);
    polygon[0][0]=(int)(xCentre+20*Math.cos(angle));
    polygon[1][0]=(int)(yCentre-20*Math.sin(angle));
    polygon[0][1]=(int)(polygon[0][0]-5*Math.cos(angle+headAngle));
    polygon[1][1]=(int)(polygon[1][0]+5*Math.sin(angle+headAngle));
    polygon[0][2]=(int)(polygon[0][0]-5*Math.cos(angle-headAngle));
    polygon[1][2]=(int)(polygon[1][0]+5*Math.sin(angle-headAngle));
    
    g.drawLine(xCentre,yCentre,(int)(xCentre+15*Math.cos(angle)),
      (int)(yCentre-15*Math.sin(angle)));
    g.fillPolygon(polygon[0],polygon[1],3);
  };
  
  //Return where arrowhead is in curve coordinates
  public Coordinate getArrowCoordinate(Coordinate co,Coordinate dir) {
    return new Coordinate(clipToCurveX(curveToClipX(co.x)+(int)(17*dir.x)),
      clipToCurveY(curveToClipY(co.y)-(int)(17*dir.y)));
  };
    
  
  //--------------------------------------------------------------------
  // Animating Curve
  //--------------------------------------------------------------------
  //Draws a frame of the animated curve
  protected void drawAnimateCurve(Curve curve,int x[],int y[]) {
    g.setColor(curve.color);
    g.drawPolyline(x,y,animateCtr+1);
    curve.drawAnimationConstructs(
      (double)animateCtr*(1.0/numPoints));
  };

  //Draws a frame of the animated curves
  protected void drawAnimateCurves() {
    Curve currentCurve;
    g=getGraphics();
    g.setXORMode(Color.white);
    currentCurve=curves.getFirstCurve();
    //For each curve, draw current line (can erase or draw)
    do
      drawAnimateCurve(currentCurve,curves.getCurveX(currentCurve),
        curves.getCurveY(currentCurve));
    while((currentCurve=curves.getNextCurve(currentCurve))!=null);
  };

  //Cleans the animated curves; really only for B-Spline and derivatives
  protected void cleanAnimate() {
    Curve currentCurve;
    g=getGraphics();
    g.setXORMode(Color.white);
    if(animateCtr!=numPoints) {
      currentCurve=curves.getFirstCurve();
      //For each curve, draw current line (can erase or draw)
      do
        currentCurve.cleanAnimationConstructs(
          (double)animateCtr*(1.0/numPoints));
      while((currentCurve=curves.getNextCurve(currentCurve))!=null);
    };
  };

  //Hides the parameter writing; animateS must be same as previous call to
  //showWriting
  protected void hideWriting() {
    g.setPaintMode();
    g.setColor(Color.white);
    g.drawString(animateS,animateTextX,animateTextY);
    g.setXORMode(Color.white);
    g.setColor(Color.black);
  };

  //Shows the current parameter writing
  protected void showWriting() {    
    Coordinate animatePoint;
    g.setPaintMode();
    g.setColor(Color.black);
    animateS="t="+new PrintableDouble(
      (double)animateCtr*(1.0/numPoints)).asStringSF(3);
    //Point is only shown if exactly one curve exists
    if(curves.getNumberOfItems()==1) {
      animatePoint=curves.getFirstCurve().getValue(
        (double)animateCtr*(1.0/numPoints));
      animateS+=", P="+animatePoint.asString(3);
    };
    g.drawString(animateS,animateTextX,animateTextY);
    g.setXORMode(Color.white);
  };

  public synchronized void showAnimate(int newFrameNumber) {
    g=getGraphics();
    g.setXORMode(Color.white);
    //hide whatever's already there
    if(animateOn) {
      drawAnimateCurves();
      hideWriting();
    } else {
      drawCurvesOnly();//Hide curve
      animateOn=true;
    };
      
    //Show new image
    if(newFrameNumber==numPoints) {
      drawCurvesOnly();
      cleanAnimate();//Still cleaning up
      
      animateOn=false;
      animateCtr=newFrameNumber;
    } else {
      animateCtr=newFrameNumber;
      drawAnimateCurves();
      showWriting();
    };
  };
  
  //--------------------------------------------------------------------
  // Utility functions
  //--------------------------------------------------------------------
  protected double pow(int a,int b) {
    if (b<0) return pow(a,b+1)/((double)a);
    if (b==0) return 1.0;
    else return ((double)a) * pow(a,b-1);
  };

  protected int curveToClipX(double x) {
    return clipBounds.x+
      (int)((x-curveRect.x)/curveRect.width*clipBounds.width);
  };

  protected int curveToClipY(double y) {
    return clipBounds.y+clipBounds.height-1-
      (int)((y-curveRect.y)/curveRect.height*clipBounds.height);
  };

  protected double clipToCurveX(int x) {
    return (x-clipBounds.x)*curveRect.width/clipBounds.width
    +curveRect.x;
  };

  protected double clipToCurveY(int y) {
    return (clipBounds.y+clipBounds.height-1-y)*
    curveRect.height/clipBounds.height+curveRect.y;
  };
  //--------------------------------------------------------------------
  // Mouse interaction
  //--------------------------------------------------------------------
  protected int markerNumber; //Number of marker grabbed
  protected Curve movingCurve; //Current curve being moved by mouse

  class MyMouseMotionAdapter extends java.awt.event.MouseMotionAdapter {
    public void mouseDragged(java.awt.event.MouseEvent e) {
      double xDouble,yDouble;
      if(markerNumber!=-1) {
        g=e.getComponent().getGraphics();
        g.setXORMode(Color.white);
        drawCurves();
        xDouble=clipToCurveX(e.getX());
        yDouble=clipToCurveY(e.getY());
        movingCurve.setMarker(markerNumber,xDouble,yDouble);
        drawCurves();//Changing this curve may change others
      };
    };
  };

  class MyMouseAdapter extends java.awt.event.MouseAdapter {
    public void mousePressed(java.awt.event.MouseEvent e) {
      double xDouble,yDouble;
      Curve currentCurve;
      xDouble=clipToCurveX(e.getX());
      yDouble=clipToCurveY(e.getY());
      currentCurve=curves.getFirstCurve();
      markerNumber=currentCurve.getMarker(xDouble,yDouble);
      if(markerNumber!=-1) movingCurve=currentCurve;
      while((currentCurve=curves.getNextCurve(currentCurve))!=null
        && markerNumber==-1) {
        markerNumber=currentCurve.getMarker(xDouble,yDouble);
        if(markerNumber!=-1) movingCurve=currentCurve;
      };
    };

    public void mouseReleased(java.awt.event.MouseEvent e) {
      if(markerNumber!=-1) {
        double xDouble,yDouble;
        xDouble=clipToCurveX(e.getX());
        yDouble=clipToCurveY(e.getY());
        movingCurve.setMarker(markerNumber,xDouble,yDouble);
        repaint();
        markerNumber=-1;
      };
    };
  };
};