001/*
002 * @(#)ArcIterator.java 1.17 05/11/17
003 *
004 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
005 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
006 */
007
008package armyc2.c2sd.graphics2d;
009
010//import java.util.*;
011
012/**
013 * A utility class to iterate over the path segments of an arc
014 * through the PathIterator interface.
015 *
016 * @version 10 Feb 1997
017 * @author      Jim Graham
018 */
019public class ArcIterator /* implements PathIterator */{
020    double x, y, w, h, angStRad, increment, cv;
021    AffineTransform affine;
022    int index;
023    int arcSegs;
024    int lineSegs;
025
026    ArcIterator(Arc2D a, AffineTransform at) {
027        this.w = a.getWidth() / 2;
028        this.h = a.getHeight() / 2;
029        this.x = a.getX() + w;
030        this.y = a.getY() + h;
031        this.angStRad = -Math.toRadians(a.getAngleStart());
032        this.affine = at;
033        double ext = -a.getAngleExtent();
034        if (ext >= 360.0 || ext <= -360) {
035            arcSegs = 4;
036            this.increment = Math.PI / 2;
037            // btan(Math.PI / 2);
038            this.cv = 0.5522847498307933;
039            if (ext < 0) {
040                increment = -increment;
041                cv = -cv;
042            }
043        } else {
044            arcSegs = (int) Math.ceil(Math.abs(ext) / 90.0);
045            this.increment = Math.toRadians(ext / arcSegs);
046            this.cv = btan(increment);
047            if (cv == 0) {
048                arcSegs = 0;
049            }
050        }
051        switch (a.getArcType()) {
052        case Arc2D.OPEN:
053            lineSegs = 0;
054            break;
055        case Arc2D.CHORD:
056            lineSegs = 1;
057            break;
058        case Arc2D.PIE:
059            lineSegs = 2;
060            break;
061        }
062        if (w < 0 || h < 0) {
063            arcSegs = lineSegs = -1;
064        }
065    }
066
067    /**
068     * Return the winding rule for determining the insideness of the
069     * path.
070     * @see #WIND_EVEN_ODD
071     * @see #WIND_NON_ZERO
072     */
073    public int getWindingRule() {
074        return PathIterator.WIND_NON_ZERO;
075    }
076
077    /**
078     * Tests if there are more points to read.
079     * @return true if there are more points to read
080     */
081    public boolean isDone() {
082        return index > arcSegs + lineSegs;
083    }
084
085    /**
086     * Moves the iterator to the next segment of the path forwards
087     * along the primary direction of traversal as long as there are
088     * more points in that direction.
089     */
090    public void next() {
091        index++;
092    }
093
094    /*
095     * btan computes the length (k) of the control segments at
096     * the beginning and end of a cubic bezier that approximates
097     * a segment of an arc with extent less than or equal to
098     * 90 degrees.  This length (k) will be used to generate the
099     * 2 bezier control points for such a segment.
100     *
101     *   Assumptions:
102     *     a) arc is centered on 0,0 with radius of 1.0
103     *     b) arc extent is less than 90 degrees
104     *     c) control points should preserve tangent
105     *     d) control segments should have equal length
106     *
107     *   Initial data:
108     *     start angle: ang1
109     *     end angle:   ang2 = ang1 + extent
110     *     start point: P1 = (x1, y1) = (cos(ang1), sin(ang1))
111     *     end point:   P4 = (x4, y4) = (cos(ang2), sin(ang2))
112     *
113     *   Control points:
114     *     P2 = (x2, y2)
115     *     | x2 = x1 - k * sin(ang1) = cos(ang1) - k * sin(ang1)
116     *     | y2 = y1 + k * cos(ang1) = sin(ang1) + k * cos(ang1)
117     *
118     *     P3 = (x3, y3)
119     *     | x3 = x4 + k * sin(ang2) = cos(ang2) + k * sin(ang2)
120     *     | y3 = y4 - k * cos(ang2) = sin(ang2) - k * cos(ang2)
121     *
122     * The formula for this length (k) can be found using the
123     * following derivations:
124     *
125     *   Midpoints:
126     *     a) bezier (t = 1/2)
127     *        bPm = P1 * (1-t)^3 +
128     *              3 * P2 * t * (1-t)^2 + 
129     *              3 * P3 * t^2 * (1-t) +
130     *              P4 * t^3 =
131     *            = (P1 + 3P2 + 3P3 + P4)/8
132     *
133     *     b) arc
134     *        aPm = (cos((ang1 + ang2)/2), sin((ang1 + ang2)/2))
135     *
136     *   Let angb = (ang2 - ang1)/2; angb is half of the angle
137     *   between ang1 and ang2.
138     *
139     *   Solve the equation bPm == aPm
140     *
141     *     a) For xm coord:
142     *        x1 + 3*x2 + 3*x3 + x4 = 8*cos((ang1 + ang2)/2)
143     *
144     *        cos(ang1) + 3*cos(ang1) - 3*k*sin(ang1) +
145     *        3*cos(ang2) + 3*k*sin(ang2) + cos(ang2) =
146     *        = 8*cos((ang1 + ang2)/2)
147     *
148     *        4*cos(ang1) + 4*cos(ang2) + 3*k*(sin(ang2) - sin(ang1)) =
149     *        = 8*cos((ang1 + ang2)/2)
150     *
151     *        8*cos((ang1 + ang2)/2)*cos((ang2 - ang1)/2) +
152     *        6*k*sin((ang2 - ang1)/2)*cos((ang1 + ang2)/2) =
153     *        = 8*cos((ang1 + ang2)/2)
154     *
155     *        4*cos(angb) + 3*k*sin(angb) = 4
156     *
157     *        k = 4 / 3 * (1 - cos(angb)) / sin(angb)
158     *
159     *     b) For ym coord we derive the same formula.
160     *
161     * Since this formula can generate "NaN" values for small
162     * angles, we will derive a safer form that does not involve
163     * dividing by very small values:
164     *     (1 - cos(angb)) / sin(angb) =
165     *     = (1 - cos(angb))*(1 + cos(angb)) / sin(angb)*(1 + cos(angb)) =
166     *     = (1 - cos(angb)^2) / sin(angb)*(1 + cos(angb)) =
167     *     = sin(angb)^2 / sin(angb)*(1 + cos(angb)) =
168     *     = sin(angb) / (1 + cos(angb))
169     *
170     */
171    private static double btan(double increment) {
172        increment /= 2.0;
173        return 4.0 / 3.0 * Math.sin(increment) / (1.0 + Math.cos(increment));
174    }
175
176    /**
177     * Returns the coordinates and type of the current path segment in
178     * the iteration.
179     * The return value is the path segment type:
180     * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE.
181     * A float array of length 6 must be passed in and may be used to
182     * store the coordinates of the point(s).
183     * Each point is stored as a pair of float x,y coordinates.
184     * SEG_MOVETO and SEG_LINETO types will return one point,
185     * SEG_QUADTO will return two points,
186     * SEG_CUBICTO will return 3 points
187     * and SEG_CLOSE will not return any points.
188     * @see #SEG_MOVETO
189     * @see #SEG_LINETO
190     * @see #SEG_QUADTO
191     * @see #SEG_CUBICTO
192     * @see #SEG_CLOSE
193     */
194    public int currentSegmentFlt(float[] coords) {
195        if (isDone()) {
196            //throw new NoSuchElementException("arc iterator out of bounds");
197            System.out.println("arc iterator out of bounds");
198            return -1;
199        }
200        double angle = angStRad;
201        if (index == 0) {
202            coords[0] = (float) (x + Math.cos(angle) * w);
203            coords[1] = (float) (y + Math.sin(angle) * h);
204            return PathIterator.SEG_MOVETO;
205        }
206        if (index > arcSegs) {
207            if (index == arcSegs + lineSegs) {
208                return PathIterator.SEG_CLOSE;
209            }
210            coords[0] = (float) x;
211            coords[1] = (float) y;
212            return PathIterator.SEG_LINETO;
213        }
214        angle += increment * (index - 1);
215        double relx = Math.cos(angle);
216        double rely = Math.sin(angle);
217        coords[0] = (float) (x + (relx - cv * rely) * w);
218        coords[1] = (float) (y + (rely + cv * relx) * h);
219        angle += increment;
220        relx = Math.cos(angle);
221        rely = Math.sin(angle);
222        coords[2] = (float) (x + (relx + cv * rely) * w);
223        coords[3] = (float) (y + (rely - cv * relx) * h);
224        coords[4] = (float) (x + relx * w);
225        coords[5] = (float) (y + rely * h);
226        return PathIterator.SEG_CUBICTO;
227    }
228
229    /**
230     * Returns the coordinates and type of the current path segment in
231     * the iteration.
232     * The return value is the path segment type:
233     * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE.
234     * A double array of length 6 must be passed in and may be used to
235     * store the coordinates of the point(s).
236     * Each point is stored as a pair of double x,y coordinates.
237     * SEG_MOVETO and SEG_LINETO types will return one point,
238     * SEG_QUADTO will return two points,
239     * SEG_CUBICTO will return 3 points
240     * and SEG_CLOSE will not return any points.
241     * @see #SEG_MOVETO
242     * @see #SEG_LINETO
243     * @see #SEG_QUADTO
244     * @see #SEG_CUBICTO
245     * @see #SEG_CLOSE
246     */
247    public int currentSegment(double[] coords) {
248        if (isDone()) {
249            //throw new NoSuchElementException("arc iterator out of bounds");
250        }
251        double angle = angStRad;
252        if (index == 0) {
253            coords[0] = x + Math.cos(angle) * w;
254            coords[1] = y + Math.sin(angle) * h;
255            return PathIterator.SEG_MOVETO;
256        }
257        if (index > arcSegs) {
258            if (index == arcSegs + lineSegs) {
259                return PathIterator.SEG_CLOSE;
260            }
261            coords[0] = x;
262            coords[1] = y;
263            return PathIterator.SEG_LINETO;
264        }
265        angle += increment * (index - 1);
266        double relx = Math.cos(angle);
267        double rely = Math.sin(angle);
268        coords[0] = x + (relx - cv * rely) * w;
269        coords[1] = y + (rely + cv * relx) * h;
270        angle += increment;
271        relx = Math.cos(angle);
272        rely = Math.sin(angle);
273        coords[2] = x + (relx + cv * rely) * w;
274        coords[3] = y + (rely - cv * relx) * h;
275        coords[4] = x + relx * w;
276        coords[5] = y + rely * h;
277        return PathIterator.SEG_CUBICTO;
278    }
279}