00001 package edu.stanford.hci.r3.util.geometry;
00002
00003 import java.awt.Shape;
00004 import java.awt.geom.*;
00005 import java.util.ArrayList;
00006 import java.util.List;
00007
00020 public class CatmullRomSpline {
00021
00022 private List<Vector2D> backwardTangents = new ArrayList<Vector2D>();
00023
00024 private List<Point2D> controlPoints = new ArrayList<Point2D>();
00025
00026 private List<Vector2D> forwardTangents = new ArrayList<Vector2D>();
00027
00028 private GeneralPath linePath = new GeneralPath();
00029
00030 private GeneralPath path = new GeneralPath();
00031
00032 private List<Point2D> pathPoints = new ArrayList<Point2D>();
00033
00034
00035 private double tension = .3;
00036
00037 public List<Vector2D> getBackwardTangents() {
00038 return backwardTangents;
00039 }
00040
00041 public List<Point2D> getControlPoints() {
00042 return controlPoints;
00043 }
00044
00048 public List<Vector2D> getForwardTangents() {
00049 return forwardTangents;
00050 }
00051
00055 public Shape getLineShape() {
00056 return linePath;
00057 }
00058
00062 public List<Point2D> getPathPoints() {
00063 return pathPoints;
00064 }
00065
00069 public Shape getShape() {
00070 return path;
00071 }
00072
00078 private void makeConnectedLines() {
00079 for (int i = 0; i < pathPoints.size() - 1; i++) {
00080 Point2D p0 = pathPoints.get(i);
00081 Point2D p1 = pathPoints.get(i + 1);
00082
00083 linePath.append(new Line2D.Double(p0, p1), false);
00084
00085 p0 = p1;
00086 }
00087 }
00088
00092 private void makePiecewiseBezier() {
00093 path.reset();
00094
00095
00096
00097
00098
00099
00100 Point2D p1;
00101 Point2D c1;
00102 Point2D c2;
00103 Point2D p2;
00104 int j = 0;
00105 for (int i = 0; i < pathPoints.size() - 1; i++) {
00106 p1 = pathPoints.get(i);
00107 c1 = controlPoints.get(j++);
00108 c2 = controlPoints.get(j++);
00109 p2 = pathPoints.get(i + 1);
00110
00111 path.append(
00112 new CubicCurve2D.Double(
00113 p1.getX(), p1.getY(),
00114 c1.getX(), c1.getY(),
00115 c2.getX(), c2.getY(),
00116 p2.getX(), p2.getY()),
00117 false);
00118 }
00119 }
00120
00125 public void setPoints(double[] x, double[] y) {
00126 List<Point2D> points = new ArrayList<Point2D>();
00127 for (int i = 0; i < x.length; i++) {
00128 points.add(new Point2D.Double(x[i], y[i]));
00129 }
00130 setPoints(points);
00131 }
00132
00139 public void setPoints(List<Point2D> points) {
00140 path.reset();
00141 pathPoints.clear();
00142
00143 pathPoints.addAll(points);
00144
00145
00146 if (points.size() == 1) {
00147 final Point2D point = points.get(0);
00148 path.append(new Line2D.Double(point, point), false);
00149 return;
00150 }
00151
00152
00153 if (points.size() == 2) {
00154 final Point2D point1 = points.get(0);
00155 final Point2D point2 = points.get(1);
00156 path.append(new Line2D.Double(point1, point2), false);
00157 return;
00158 }
00159
00160
00161
00162
00163
00164 Point2D pSecondToLast = points.get(points.size() - 2);
00165 Point2D pLast = points.get(points.size() - 1);
00166 points.add(Vector2D.add(pLast, Vector2D.subtract(pLast, pSecondToLast)));
00167
00168
00169 Point2D p0 = Vector2D.add(points.get(0), Vector2D.subtract(points.get(0), points.get(1)));
00170
00171 Point2D p1 = points.get(0);
00172
00173 Point2D p2;
00174
00175
00176 for (int i = 0; i < points.size() - 1; i++) {
00177 p2 = points.get(i + 1);
00178
00179
00180
00181
00182 final Vector2D bkwd = Vector2D.getScaled(tension * Vector2D.subtract(p0, p1).magnitude(),
00183 Vector2D.subtract(p0, p2));
00184 backwardTangents.add(bkwd);
00185
00186
00187 controlPoints.add(Vector2D.add(p1, bkwd));
00188
00189
00190
00191
00192 final Vector2D fwd = Vector2D.getScaled(tension * Vector2D.subtract(p2, p1).magnitude(), Vector2D
00193 .subtract(p2, p0));
00194 forwardTangents.add(fwd);
00195
00196
00197 controlPoints.add(Vector2D.add(p1, fwd));
00198
00199
00200 p0 = p1;
00201 p1 = p2;
00202 }
00203
00204
00205 controlPoints.remove(0);
00206 controlPoints.remove(controlPoints.size() - 1);
00207
00208
00209
00210
00211 makeConnectedLines();
00212
00213
00214 makePiecewiseBezier();
00215 }
00216 }