Java/2D Graphics GUI/Curve — различия между версиями

Материал из Java эксперт
Перейти к: навигация, поиск
 
м (1 версия)
 
(нет различий)

Текущая версия на 06:56, 1 июня 2010

A spline factory instance

  
/*
 * This code 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 code 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 program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA  02111-1307, USA.
 */
//package no.geosoft.cc.geometry.spline;

/**
 * A spline factory instance.
 * 
 * @author 
 */   
class CatmullRomSpline extends CubicSpline
{
  /**
   * Construct a Catmull-Rom spline. Package local; Use the SplineFactory
   * to create splines of this type. The control points are used according
   * to the definition of Catmull-Rom splines.
   * 
   * @param controlPoints  Control points of spline (x0,y0,z0,x1,y1,z1,...)
   * @param nParts         Number of parts in generated spline.
   */
  CatmullRomSpline (double controlPoints[], int nParts)
  {
    super (controlPoints, nParts);
  }

  
  protected void initialize (double controlPoints[], int nParts)
  {
    nParts_ = nParts;
    // Endpoints are added twice to force in the generated array
    controlPoints_ = new double[controlPoints.length + 6];
    System.arraycopy (controlPoints, 0, controlPoints_, 3,
                      controlPoints.length);
    
    controlPoints_[0] = controlPoints_[3];
    controlPoints_[1] = controlPoints_[4];
    controlPoints_[2] = controlPoints_[5];
    
    controlPoints_[controlPoints_.length - 3] = controlPoints_[controlPoints_.length - 6];
    controlPoints_[controlPoints_.length - 2] = controlPoints_[controlPoints_.length - 5];
    controlPoints_[controlPoints_.length - 1] = controlPoints_[controlPoints_.length - 4];
  }
  
  
  protected double blend (int i, double t)
  {
    if      (i == -2) return ((-t + 2) * t - 1) * t / 2;
    else if (i == -1) return (((3 * t - 5) * t) * t + 2) / 2;
    else if (i ==  0) return ((-3 * t + 4) * t + 1) * t / 2;
    else              return ((t - 1) * t * t) / 2;
  }
}



Curve with QuadCurve2D

  
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Rectangle2D;
import java.util.Vector;
import javax.swing.JApplet;
import javax.swing.JFrame;
public class QuadCurve extends JApplet {
  DrawingCanvas canvas;
  public static void main(String[] a){
      JFrame f = new JFrame();
      QuadCurve curve = new QuadCurve();
      curve.init();
      f.getContentPane().add(curve);
      f.setDefaultCloseOperation(1);
      f.setSize(650,250);
      f.setVisible(true);
  }
  
  public void init() {
    Container container = getContentPane();
    canvas = new DrawingCanvas();
    container.add(canvas);
  }
  class DrawingCanvas extends Canvas {
    Vector quadCurves;
    QuadCurve2D selectedCurve = null;
    Rectangle2D boundingRec = null;
    public DrawingCanvas() {
      setBackground(Color.white);
      setSize(400, 200); // width and height of canvas
      quadCurves = new Vector();
      quadCurves.addElement(new QuadCurve2D.Float(20, 20, 80, 160, 120,
          20));
      quadCurves.addElement(new QuadCurve2D.Float(120, 100, 160, 40, 200,
          180));
      quadCurves.addElement(new QuadCurve2D.Float(240, 20, 220, 60, 260,
          120));
      quadCurves.addElement(new QuadCurve2D.Float(250, 160, 260, 140,
          280, 180));
      quadCurves.addElement(new QuadCurve2D.Float(300, 180, 340, 40, 380,
          120));
      quadCurves.addElement(new QuadCurve2D.Float(20, 180, 80, 170, 120,
          190));
    }
    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D) g;
      for (int i = 0; i < quadCurves.size(); i++) {
        g2D.draw((QuadCurve2D) quadCurves.elementAt(i));
      }
    }
  }
}



Draw curve with mouse

  
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Line2D;
import java.awt.geom.QuadCurve2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class CubicCurveMouse extends JFrame {
  DrawingCanvas canvas;
  JLabel label = new JLabel("Mouse Location (x, y):  "); 
  JLabel coords = new JLabel("");
  public CubicCurveMouse() {
    super();
    Container container = getContentPane();
    JPanel panel = new JPanel();
    panel.setLayout(new GridLayout(1, 2));
    
    panel.add(label);
    panel.add(label);
    panel.add(coords);
    container.add(panel, BorderLayout.SOUTH);
    canvas = new DrawingCanvas();
    container.add(canvas);
    addWindowListener(new WindowEventHandler());
        setSize(300,300);
    setVisible(true);
  }
  class WindowEventHandler extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
      System.exit(0);
    }
  }
  public static void main(String arg[]) {
    new CubicCurveMouse();
  }
  class DrawingCanvas extends Canvas {
    float x1, y1, xc1cur, yc1cur, xc1new, yc1new, xc2cur, yc2cur, xc2new,
        yc2new, x4cur, y4cur, x4new, y4new;
    int pressNo = 0;
    int dragFlag1 = -1;
    int dragFlag2 = -1;
    boolean clearFlag = false;
    float dashes[] = { 5f, 5f };
    BasicStroke stroke;
    public DrawingCanvas() {
      setBackground(Color.white);
      addMouseListener(new MyMouseListener());
      addMouseMotionListener(new MyMouseListener());
      setSize(400, 400);
      stroke = new BasicStroke(1f, BasicStroke.CAP_BUTT,
          BasicStroke.JOIN_BEVEL, 10f, dashes, 0f);
    }
    public void update(Graphics g) {
      paint(g);
    }
    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D) g;
      if (pressNo == 1) {
        g2D.setXORMode(getBackground());
        g2D.setColor(Color.black);
        g2D.setStroke(stroke);
        // Erase the currently existing line
        g2D.draw(new Line2D.Float(x1, y1, x4cur, y4cur));
        // Draw the new line
        g2D.draw(new Line2D.Float(x1, y1, x4new, y4new));
        // Update the currently existing coordinate values
        x4cur = x4new;
        y4cur = y4new;
      }else if (pressNo == 2) {
        g2D.setXORMode(getBackground());
        g2D.setColor(Color.black);
        g2D.setStroke(stroke);
        if (dragFlag1 != -1) {
          g2D.draw(new QuadCurve2D.Float(x1, y1, xc1cur, yc1cur,
              x4new, y4new));
        }
        dragFlag1++; // Reset the drag-flag
        g2D.draw(new QuadCurve2D.Float(x1, y1, xc1new, yc1new, x4new,
            y4new));
        xc1cur = xc1new;
        yc1cur = yc1new;
      }else if (pressNo == 3) {
        g2D.setXORMode(getBackground());
        g2D.setColor(Color.black);
        if (dragFlag2 != -1) {
          g2D.draw(new CubicCurve2D.Float(x1, y1, xc1new, yc1new,
              xc2cur, yc2cur, x4new, y4new));
        }
        dragFlag2++; // Reset the drag flag
        g2D.draw(new CubicCurve2D.Float(x1, y1, xc1new, yc1new, xc2new,
            yc2new, x4new, y4new));
        xc2cur = xc2new;
        yc2cur = yc2new;
      }
      if (clearFlag) {
        g2D.setXORMode(getBackground());
        g2D.setColor(Color.black);
        g2D.setStroke(stroke);
        g2D.draw(new Line2D.Float(x1, y1, x4new, y4new));
        g2D.draw(new QuadCurve2D.Float(x1, y1, xc1new, yc1new, x4new,
            y4new));
        clearFlag = false;
      }
    }
    class MyMouseListener extends MouseAdapter implements MouseMotionListener {
      public void mousePressed(MouseEvent e) {
        if (pressNo == 0) { 
          pressNo++;
          x1 = x4cur = e.getX();
          y1 = y4cur = e.getY();
        } else if (pressNo == 1) {
          pressNo++;
          xc1cur = e.getX();
          yc1cur = e.getY();
        } else if (pressNo == 2) {
          pressNo++;
          xc2cur = e.getX();
          yc2cur = e.getY();
        }
      }
      public void mouseReleased(MouseEvent e) {
        if (pressNo == 1) {
          x4new = e.getX();
          y4new = e.getY();
          canvas.repaint();
        } else if (pressNo == 2) {
          xc1new = e.getX();
          yc1new = e.getY();
          canvas.repaint();
        } else if (pressNo == 3) {
          xc2new = e.getX();
          yc2new = e.getY();
          canvas.repaint();
          pressNo = 0;
          dragFlag1 = -1;
          dragFlag2 = -1;
          clearFlag = true;
        }
      }
      public void mouseDragged(MouseEvent e) {
        if (pressNo == 1) {
          x4new = e.getX();
          y4new = e.getY();
          String string = "(" + Integer.toString(e.getX()) + ", "
              + Integer.toString(e.getY()) + ")";
          coords.setText(string);
          canvas.repaint();
        } else if (pressNo == 2) {
          xc1new = e.getX();
          yc1new = e.getY();
          String string = "(" + Integer.toString(e.getX()) + ", "
              + Integer.toString(e.getY()) + ")";
          coords.setText(string);
          canvas.repaint();
        } else if (pressNo == 3) {
          xc2new = e.getX();
          yc2new = e.getY();
          String string = "(" + Integer.toString(e.getX()) + ", "
              + Integer.toString(e.getY()) + ")";
          coords.setText(string);
          canvas.repaint();
        }
      }
      public void mouseMoved(MouseEvent e) {
        String string = "(" + Integer.toString(e.getX()) + ", "
            + Integer.toString(e.getY()) + ")";
        coords.setText(string);
      }
    }
  }
}



Interpolates given points by a bezier curve

   
/*
 * Copyright (c) 2005 David Benson
 *  
 * See LICENSE file in distribution for licensing details of this source file
 */
import java.awt.Point;
import java.awt.geom.Point2D;
/**
 * Interpolates given points by a bezier curve. The first
 * and the last two points are interpolated by a quadratic
 * bezier curve; the other points by a cubic bezier curve.
 * 
 * Let p a list of given points and b the calculated bezier points,
 * then one get the whole curve by:
 * 
 * sharedPath.moveTo(p[0])
 * sharedPath.quadTo(b[0].x, b[0].getY(), p[1].x, p[1].getY());
 * 
 * for(int i = 2; i < p.length - 1; i++ ) {
 *    Point b0 = b[2*i-3];
 *    Point b1 = b[2*i-2];
 *    sharedPath.curveTo(b0.x, b0.getY(), b1.x, b1.getY(), p[i].x, p[i].getY());
 * }
 * 
 * sharedPath.quadTo(b[b.length-1].x, b[b.length-1].getY(), p[n - 1].x, p[n - 1].getY());
 * 
 * @author krueger
 */
public class Bezier {
  private static final float AP = 0.5f;
  private Point2D[] bPoints;
  /**
   * Creates a new Bezier curve.
   * @param points
   */
  public Bezier(Point2D[] points) {
    int n = points.length;
    if (n < 3) {
      // Cannot create bezier with less than 3 points
      return;
    }
    bPoints = new Point[2 * (n - 2)];
    double paX, paY;
    double pbX = points[0].getX();
    double pbY = points[0].getY();
    double pcX = points[1].getX();
    double pcY = points[1].getY();
    for (int i = 0; i < n - 2; i++) {
      paX = pbX;
      paY = pbY;
      pbX = pcX;
      pbY = pcY;
      pcX = points[i + 2].getX();
      pcY = points[i + 2].getY();
      double abX = pbX - paX;
      double abY = pbY - paY;
      double acX = pcX - paX;
      double acY = pcY - paY;
      double lac = Math.sqrt(acX * acX + acY * acY);
      acX = acX /lac;
      acY = acY /lac;
      double proj = abX * acX + abY * acY;
      proj = proj < 0 ? -proj : proj;
      double apX = proj * acX;
      double apY = proj * acY;
      double p1X = pbX - AP * apX;
      double p1Y = pbY - AP * apY;
      bPoints[2 * i] = new Point((int) p1X, (int) p1Y);
      acX = -acX;
      acY = -acY;
      double cbX = pbX - pcX;
      double cbY = pbY - pcY;
      proj = cbX * acX + cbY * acY;
      proj = proj < 0 ? -proj : proj;
      apX = proj * acX;
      apY = proj * acY;
      double p2X = pbX - AP * apX;
      double p2Y = pbY - AP * apY;
      bPoints[2 * i + 1] = new Point((int) p2X, (int) p2Y);
    }
  }
  /**
   * Returns the calculated bezier points.
   * @return the calculated bezier points
   */
  public Point2D[] getPoints() {
    return bPoints;
  }
  /**
   * Returns the number of bezier points.
   * @return number of bezier points
   */
  public int getPointCount() {
    return bPoints.length;
  }
  /**
   * Returns the bezier points at position i.
   * @param i
   * @return the bezier point at position i
   */
  public Point2D getPoint(int i) {
    return bPoints[i];
  }
}



Move the curve control point and redraw the curve

  
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;
public class MainClass {
  public static void main(String[] args){
    JFrame frame = new JFrame();
    frame.getContentPane().add(new CurveApplet());
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(200, 200);
    frame.setVisible(true);
  }
}
class CurveApplet extends JPanel {
  public CurveApplet() {
    super(new BorderLayout());
    pane = new CurvePane();
    add(pane,"Center");
    MouseHandler handler = new MouseHandler();
    pane.addMouseListener(handler);
    pane.addMouseMotionListener(handler);
  }
  class CurvePane extends JComponent {
    public CurvePane() {
      quadCurve = new QuadCurve2D.Double( 
          startQ.x, startQ.y, 
          control.x, control.y,
          endQ.x, endQ.y);
      cubicCurve = new CubicCurve2D.Double(
          startC.x, startC.y, 
          controlStart.x, controlStart.y,
          controlEnd.x, controlEnd.y,
          endC.x, endC.y);
    }
    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D) g;
      quadCurve.ctrlx = ctrlQuad.getCenter().x;
      quadCurve.ctrly = ctrlQuad.getCenter().y;
      cubicCurve.ctrlx1 = ctrlCubic1.getCenter().x;
      cubicCurve.ctrly1 = ctrlCubic1.getCenter().y;
      cubicCurve.ctrlx2 = ctrlCubic2.getCenter().x;
      cubicCurve.ctrly2 = ctrlCubic2.getCenter().y;
      g2D.setPaint(Color.BLUE);
      g2D.draw(quadCurve);
      g2D.draw(cubicCurve);
      g2D.setPaint(Color.RED);
      ctrlQuad.draw(g2D);
      ctrlCubic1.draw(g2D);
      ctrlCubic2.draw(g2D);
      Line2D.Double tangent = new Line2D.Double(startQ, ctrlQuad.getCenter());
      g2D.draw(tangent);
      tangent = new Line2D.Double(endQ, ctrlQuad.getCenter());
      g2D.draw(tangent);
      tangent = new Line2D.Double(startC, ctrlCubic1.getCenter());
      g2D.draw(tangent);
      tangent = new Line2D.Double(endC, ctrlCubic2.getCenter());
      g2D.draw(tangent);
    }
  }
 
  Point2D.Double startQ = new Point2D.Double(50, 75);
  Point2D.Double endQ = new Point2D.Double(150, 75);
  Point2D.Double control = new Point2D.Double(80, 25); 
  Point2D.Double startC = new Point2D.Double(50, 150); 
  Point2D.Double endC = new Point2D.Double(150, 150); 
  Point2D.Double controlStart = new Point2D.Double(80, 100);
  Point2D.Double controlEnd = new Point2D.Double(160, 100); 
  Marker ctrlQuad = new Marker(control);
  Marker ctrlCubic1 = new Marker(controlStart);
  Marker ctrlCubic2 = new Marker(controlEnd);
  QuadCurve2D.Double quadCurve; 
  CubicCurve2D.Double cubicCurve; 
  CurvePane pane = new CurvePane();
  class Marker {
    public Marker(Point2D.Double control) {
      center = control; 
      circle = new Ellipse2D.Double(control.x - radius, control.y - radius, 2.0 * radius,
          2.0 * radius);
    }
    public void draw(Graphics2D g2D) {
      g2D.draw(circle);
    }
    Point2D.Double getCenter() {
      return center;
    }
    public boolean contains(double x, double y) {
      return circle.contains(x, y);
    }
    public void setLocation(double x, double y) {
      center.x = x; 
      center.y = y; 
      circle.x = x - radius; 
      circle.y = y - radius; 
    }
    Ellipse2D.Double circle; 
    Point2D.Double center; 
    static final double radius = 3;
  }
  class MouseHandler extends MouseInputAdapter {
    public void mousePressed(MouseEvent e) {
      if (ctrlQuad.contains(e.getX(), e.getY()))
        selected = ctrlQuad;
      else if (ctrlCubic1.contains(e.getX(), e.getY()))
        selected = ctrlCubic1;
      else if (ctrlCubic2.contains(e.getX(), e.getY()))
        selected = ctrlCubic2;
    }
    public void mouseReleased(MouseEvent e) {
      selected = null;
    }
    public void mouseDragged(MouseEvent e) {
      if (selected != null) {
        selected.setLocation(e.getX(), e.getY());
        pane.repaint(); 
      }
    }
    Marker selected = null;
  }
}



Spline 2D

  
/*
 * @(#)Spline2D.java
 * 
 * Copyright (c) 2003 Martin Krueger
 * Copyright (c) 2005 David Benson
 *  
 */
import java.awt.geom.Point2D;
import java.util.Arrays;
/**
 * Interpolates points given in the 2D plane. The resulting spline
 * is a function s: R -> R^2 with parameter t in [0,1].
 * 
 * @author krueger
 */
public class Spline2D {
  /** 
   *  Array representing the relative proportion of the total distance
   *  of each point in the line ( i.e. first point is 0.0, end point is
   *  1.0, a point halfway on line is 0.5 ).
   */
  private double[] t;
  private Spline splineX;
  private Spline splineY;
  /**
   * Total length tracing the points on the spline
   */
  private double length;
  
  /**
   * Creates a new Spline2D.
   * @param points
   */
  public Spline2D(Point2D[] points) {
    double[] x = new double[points.length];
    double[] y = new double[points.length];
    
    for(int i = 0; i< points.length; i++) {
      x[i] = points[i].getX();
      y[i] = points[i].getY(); 
    }
    
    init(x, y);
  }
  /**
   * Creates a new Spline2D.
   * @param x
   * @param y
   */
  public Spline2D(double[] x, double[] y) {
    init(x, y);
  }
  private void init(double[] x, double[] y) {
    if (x.length != y.length) {
      throw new IllegalArgumentException("Arrays must have the same length.");
    }
    
    if (x.length < 2) {
      throw new IllegalArgumentException("Spline edges must have at least two points.");
    }
    t = new double[x.length];
    t[0] = 0.0; // start point is always 0.0
    
    // Calculate the partial proportions of each section between each set
    // of points and the total length of sum of all sections
    for (int i = 1; i < t.length; i++) {
      double lx = x[i] - x[i-1];
      double ly = y[i] - y[i-1];
      // If either diff is zero there is no point performing the square root
      if ( 0.0 == lx ) {
        t[i] = Math.abs(ly);
      } else if ( 0.0 == ly ) {
        t[i] = Math.abs(lx);
      } else {
        t[i] = Math.sqrt(lx*lx+ly*ly);
      }
      
      length += t[i];
      t[i] += t[i-1];
    }
    
    for(int i = 1; i< (t.length)-1; i++) {
      t[i] = t[i] / length;
    }
    
    t[(t.length)-1] = 1.0; // end point is always 1.0
    
    splineX = new Spline(t, x);
    splineY = new Spline(t, y);
  }
  /**
   * @param t 0 <= t <= 1
   */
  public double[] getPoint(double t) {
    double[] result = new double[2];
    result[0] = splineX.getValue(t);
    result[1] = splineY.getValue(t);
    return result;
  }
  
  /**
   * Used to check the correctness of this spline
   */
  public boolean checkValues() {
    return (splineX.checkValues() && splineY.checkValues());
  }
  public double getDx(double t) {
    return splineX.getDx(t);
  }
  
  public double getDy(double t) {
    return splineY.getDx(t);
  }
  
  public Spline getSplineX() {
    return splineX;
  }
  
  public Spline getSplineY() {
    return splineY;
  }
  
  public double getLength() {
    return length;
  }
}
/* This code is PUBLIC DOMAIN */

/**
 * Interpolates given values by B-Splines.
 * 
 * @author krueger
 */
 class Spline {
  private double[] xx;
  private double[] yy;
  private double[] a;
  private double[] b;
  private double[] c;
  private double[] d;
  /** tracks the last index found since that is mostly commonly the next one used */
  private int storageIndex = 0;
  /**
   * Creates a new Spline.
   * @param xx
   * @param yy
   */
  public Spline(double[] xx, double[] yy) {
    setValues(xx, yy);
  }
  /**
   * Set values for this Spline.
   * @param xx
   * @param yy
   */
  public void setValues(double[] xx, double[] yy) {
    this.xx = xx;
    this.yy = yy;
    if (xx.length > 1) {
      calculateCoefficients();
    }
  }
  /**
   * Returns an interpolated value.
   * @param x
   * @return the interpolated value
   */
  public double getValue(double x) {
    if (xx.length == 0) {
      return Double.NaN;
    }
    if (xx.length == 1) {
      if (xx[0] == x) {
        return yy[0];
      } else {
        return Double.NaN;
      }
    }
    int index = Arrays.binarySearch(xx, x);
    if (index > 0) {
      return yy[index];
    }
    index = - (index + 1) - 1;
    //TODO linear interpolation or extrapolation
    if (index < 0) {
      return yy[0];
    }
    return a[index]
      + b[index] * (x - xx[index])
      + c[index] * Math.pow(x - xx[index], 2)
      + d[index] * Math.pow(x - xx[index], 3);
  }
  /**
   * Returns an interpolated value. To be used when a long sequence of values
   * are required in order, but ensure checkValues() is called beforehand to
   * ensure the boundary checks from getValue() are made
   * @param x
   * @return the interpolated value
   */
  public double getFastValue(double x) {
    // Fast check to see if previous index is still valid
    if (storageIndex > -1 && storageIndex < xx.length-1 && x > xx[storageIndex] && x < xx[storageIndex + 1]) {
    } else {
      int index = Arrays.binarySearch(xx, x);
      if (index > 0) {
        return yy[index];
      }
      index = - (index + 1) - 1;
      storageIndex = index;
    }
  
    //TODO linear interpolation or extrapolation
    if (storageIndex < 0) {
      return yy[0];
    }
    double value = x - xx[storageIndex];
    return a[storageIndex]
          + b[storageIndex] * value
          + c[storageIndex] * (value * value)
          + d[storageIndex] * (value * value * value);
  }
  /**
   * Used to check the correctness of this spline
   */
  public boolean checkValues() {
    if (xx.length < 2) {
      return false;
    } else {
      return true;
    }
  }
  /**
   * Returns the first derivation at x.
   * @param x
   * @return the first derivation at x
   */
  public double getDx(double x) {
    if (xx.length == 0 || xx.length == 1) {
      return 0;
    }
    int index = Arrays.binarySearch(xx, x);
    if (index < 0) {
      index = - (index + 1) - 1;
    }
    return b[index]
      + 2 * c[index] * (x - xx[index])
      + 3 * d[index] * Math.pow(x - xx[index], 2);
  }
  /**
   * Calculates the Spline coefficients.
   */
  private void calculateCoefficients() {
    int N = yy.length;
    a = new double[N];
    b = new double[N];
    c = new double[N];
    d = new double[N];
    if (N == 2) {
      a[0] = yy[0];
      b[0] = yy[1] - yy[0];
      return;
    }
    double[] h = new double[N - 1];
    for (int i = 0; i < N - 1; i++) {
      a[i] = yy[i];
      h[i] = xx[i + 1] - xx[i];
      // h[i] is used for division later, avoid a NaN
      if (h[i] == 0.0) {
        h[i] = 0.01;
      }
    }
    a[N - 1] = yy[N - 1];
    double[][] A = new double[N - 2][N - 2];
    double[] y = new double[N - 2];
    for (int i = 0; i < N - 2; i++) {
      y[i] =
        3
          * ((yy[i + 2] - yy[i + 1]) / h[i
            + 1]
            - (yy[i + 1] - yy[i]) / h[i]);
      A[i][i] = 2 * (h[i] + h[i + 1]);
      if (i > 0) {
        A[i][i - 1] = h[i];
      }
      if (i < N - 3) {
        A[i][i + 1] = h[i + 1];
      }
    }
    solve(A, y);
    for (int i = 0; i < N - 2; i++) {
      c[i + 1] = y[i];
      b[i] = (a[i + 1] - a[i]) / h[i] - (2 * c[i] + c[i + 1]) / 3 * h[i];
      d[i] = (c[i + 1] - c[i]) / (3 * h[i]);
    }
    b[N - 2] =
      (a[N - 1] - a[N - 2]) / h[N
        - 2]
        - (2 * c[N - 2] + c[N - 1]) / 3 * h[N
        - 2];
    d[N - 2] = (c[N - 1] - c[N - 2]) / (3 * h[N - 2]);
  }
  /**
   * Solves Ax=b and stores the solution in b.
   */
  public void solve(double[][] A, double[] b) {
    int n = b.length;
    for (int i = 1; i < n; i++) {
      A[i][i - 1] = A[i][i - 1] / A[i - 1][i - 1];
      A[i][i] = A[i][i] - A[i - 1][i] * A[i][i - 1];
      b[i] = b[i] - A[i][i - 1] * b[i - 1];
    }
    b[n - 1] = b[n - 1] / A[n - 1][n - 1];
    for (int i = b.length - 2; i >= 0; i--) {
      b[i] = (b[i] - A[i][i + 1] * b[i + 1]) / A[i][i];
    }
  }
}