001 /*
002 * Bezier.java
003 *
004
005 * The Salamander Project - 2D and 3D graphics libraries in Java
006 * Copyright (C) 2004 Mark McKay
007 *
008 * This library is free software; you can redistribute it and/or
009 * modify it under the terms of the GNU Lesser General Public
010 * License as published by the Free Software Foundation; either
011 * version 2.1 of the License, or (at your option) any later version.
012 *
013 * This library is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016 * Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public
019 * License along with this library; if not, write to the Free Software
020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021 *
022 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
023 * projects can be found at http://www.kitfox.com
024 *
025 * Created on January 14, 2005, 4:08 AM
026 */
027
028 package com.kitfox.svg.animation;
029
030 import java.awt.geom.*;
031
032 /**
033 * http://mathworld.wolfram.com/BezierCurve.html
034 * @author kitfox
035 */
036 public class Bezier
037 {
038 double length;
039 double[] coord;
040
041 public Bezier(double sx, double sy, double[] coords, int numCoords)
042 {
043 setCoords(sx, sy, coords, numCoords);
044 }
045
046 public void setCoords(double sx, double sy, double[] coords, int numCoords)
047 {
048 coord = new double[numCoords * 2 + 2];
049 coord[0] = sx;
050 coord[1] = sy;
051 for (int i = 0; i < numCoords; i++)
052 {
053 coord[i * 2 + 2] = coords[i * 2];
054 coord[i * 2 + 3] = coords[i * 2 + 1];
055 }
056
057 calcLength();
058 }
059
060 /**
061 * Retuns aproximation of the length of the bezier
062 */
063 public double getLength()
064 {
065 return length;
066 }
067
068 private void calcLength()
069 {
070 length = 0;
071 for (int i = 2; i < coord.length; i += 2)
072 {
073 length += lineLength(coord[i - 2], coord[i - 1], coord[i], coord[i + 1]);
074 }
075 }
076
077 private double lineLength(double x1, double y1, double x2, double y2)
078 {
079 double dx = x2 - x1, dy = y2 - y1;
080 return Math.sqrt(dx * dx + dy * dy);
081 }
082
083 public Point2D.Double getFinalPoint(Point2D.Double point)
084 {
085 point.x = coord[coord.length - 2];
086 point.y = coord[coord.length - 1];
087 return point;
088 }
089
090 public Point2D.Double eval(double param, Point2D.Double point)
091 {
092 point.x = 0;
093 point.y = 0;
094 int numKnots = coord.length / 2;
095
096 for (int i = 0; i < numKnots; i++)
097 {
098 double scale = bernstein(numKnots - 1, i, param);
099 point.x += coord[i * 2] * scale;
100 point.y += coord[i * 2 + 1] * scale;
101 }
102
103 return point;
104 }
105
106 /**
107 * Calculates the bernstein polynomial for evaluating parametric bezier
108 * @param numKnots - one less than number of knots in this curve hull
109 * @param knotNo - knot we are evaluating Bernstein for
110 * @param param - Parametric value we are evaluating at
111 */
112 private double bernstein(int numKnots, int knotNo, double param)
113 {
114 double iParam = 1 - param;
115 //Faster evaluation for easy cases:
116 switch (numKnots)
117 {
118 case 0:
119 return 1;
120 case 1:
121 {
122 switch (knotNo)
123 {
124 case 0:
125 return iParam;
126 case 1:
127 return param;
128 }
129 break;
130 }
131 case 2:
132 {
133 switch (knotNo)
134 {
135 case 0:
136 return iParam * iParam;
137 case 1:
138 return 2 * iParam * param;
139 case 2:
140 return param * param;
141 }
142 break;
143 }
144 case 3:
145 {
146 switch (knotNo)
147 {
148 case 0:
149 return iParam * iParam * iParam;
150 case 1:
151 return 3 * iParam * iParam * param;
152 case 2:
153 return 3 * iParam * param * param;
154 case 3:
155 return param * param * param;
156 }
157 break;
158 }
159 }
160
161 //If this bezier has more than four points, calculate bernstein the hard way
162 double retVal = 1;
163 for (int i = 0; i < knotNo; i++)
164 {
165 retVal *= param;
166 }
167 for (int i = 0; i < numKnots - knotNo; i++)
168 {
169 retVal *= iParam;
170 }
171 retVal *= choose(numKnots, knotNo);
172
173 return retVal;
174 }
175
176
177
178 private int choose(int num, int denom)
179 {
180 int denom2 = num - denom;
181 if (denom < denom2)
182 {
183 int tmp = denom;
184 denom = denom2;
185 denom2 = tmp;
186 }
187
188 int prod = 1;
189 for (int i = num; i > denom; i--)
190 {
191 prod *= num;
192 }
193
194 for (int i = 2; i <= denom2; i++)
195 {
196 prod /= i;
197 }
198
199 return prod;
200 }
201 }