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    }