/[classpath]/classpath/java/awt/geom/GeneralPath.java
ViewVC logotype

Diff of /classpath/java/awt/geom/GeneralPath.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.6 by brawer, Tue Oct 21 10:09:50 2003 UTC revision 1.7 by smarothy, Sun Aug 1 22:27:36 2004 UTC
# Line 1  Line 1 
1  /* GeneralPath.java -- represents a shape built from subpaths  /* GeneralPath.java -- represents a shape built from subpaths
2     Copyright (C) 2002, 2003 Free Software Foundation     Copyright (C) 2002, 2003, 2004 Free Software Foundation
3    
4  This file is part of GNU Classpath.     This file is part of GNU Classpath.
   
 GNU Classpath is free software; you can redistribute it and/or modify  
 it under the terms of the GNU General Public License as published by  
 the Free Software Foundation; either version 2, or (at your option)  
 any later version.  
   
 GNU Classpath 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  
 General Public License for more details.  
   
 You should have received a copy of the GNU General Public License  
 along with GNU Classpath; see the file COPYING.  If not, write to the  
 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  
 02111-1307 USA.  
   
 Linking this library statically or dynamically with other modules is  
 making a combined work based on this library.  Thus, the terms and  
 conditions of the GNU General Public License cover the whole  
 combination.  
   
 As a special exception, the copyright holders of this library give you  
 permission to link this library with independent modules to produce an  
 executable, regardless of the license terms of these independent  
 modules, and to copy and distribute the resulting executable under  
 terms of your choice, provided that you also meet, for each linked  
 independent module, the terms and conditions of the license of that  
 module.  An independent module is a module which is not derived from  
 or based on this library.  If you modify this library, you may extend  
 this exception to your version of the library, but you are not  
 obligated to do so.  If you do not wish to do so, delete this  
 exception statement from your version. */  
5    
6       GNU Classpath is free software; you can redistribute it and/or modify
7       it under the terms of the GNU General Public License as published by
8       the Free Software Foundation; either version 2, or (at your option)
9       any later version.
10    
11       GNU Classpath is distributed in the hope that it will be useful, but
12       WITHOUT ANY WARRANTY; without even the implied warranty of
13       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14       General Public License for more details.
15    
16       You should have received a copy of the GNU General Public License
17       along with GNU Classpath; see the file COPYING.  If not, write to the
18       Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19       02111-1307 USA.
20    
21       Linking this library statically or dynamically with other modules is
22       making a combined work based on this library.  Thus, the terms and
23       conditions of the GNU General Public License cover the whole
24       combination.
25    
26       As a special exception, the copyright holders of this library give you
27       permission to link this library with independent modules to produce an
28       executable, regardless of the license terms of these independent
29       modules, and to copy and distribute the resulting executable under
30       terms of your choice, provided that you also meet, for each linked
31       independent module, the terms and conditions of the license of that
32       module.  An independent module is a module which is not derived from
33       or based on this library.  If you modify this library, you may extend
34       this exception to your version of the library, but you are not
35       obligated to do so.  If you do not wish to do so, delete this
36       exception statement from your version. */
37    
38  package java.awt.geom;  package java.awt.geom;
39    
40  import java.awt.Rectangle;  import java.awt.Rectangle;
41  import java.awt.Shape;  import java.awt.Shape;
42    
43    
44  /**  /**
45   * STUBS ONLY   * A general geometric path, consisting of any number of subpaths
46   * XXX Implement and document. Note that Sun's implementation only expects   * constructed out of straight lines and cubic or quadratic Bezier
47   * float precision, not double.   * curves.
48     *
49     * <p>The inside of the curve is defined for drawing purposes by a winding
50     * rule. Either the WIND_EVEN_ODD or WIND_NON_ZERO winding rule can be chosen.
51     *
52     * <p><img src="doc-files/GeneralPath-1.png" width="300" height="210"
53     * alt="A drawing of a GeneralPath" />
54     * <p>The EVEN_ODD winding rule defines a point as inside a path if:
55     * A ray from the point towards infinity in an arbitrary direction
56     * intersects the path an odd number of times. Points <b>A</b> and
57     * <b>C</b> in the image are considered to be outside the path.
58     * (both intersect twice)
59     * Point <b>B</b> intersects once, and is inside.
60     *
61     * <p>The NON_ZERO winding rule defines a point as inside a path if:
62     * The path intersects the ray in an equal number of opposite directions.
63     * Point <b>A</b> in the image is outside (one intersection in the
64     * &#x2019;up&#x2019;
65     * direction, one in the &#x2019;down&#x2019; direction) Point <b>B</b> in
66     * the image is inside (one intersection &#x2019;down&#x2019;)
67     * Point <b>C</b> in the image is outside (two intersections
68     * &#x2019;down&#x2019;)
69     *
70     * @see Line2D
71     * @see CubicCurve2D
72     * @see QuadCurve2D
73     *
74     * @author Sascha Brawer (brawer@dandelis.ch)
75     * @author Sven de Marothy (sven@physto.se)
76     *
77     * @since 1.2
78   */   */
79  public final class GeneralPath implements Shape, Cloneable  public final class GeneralPath implements Shape, Cloneable
80  {  {
# Line 52  public final class GeneralPath implement Line 82  public final class GeneralPath implement
82    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
83    
84    /** Initial size if not specified. */    /** Initial size if not specified. */
85    private static final int INIT_SIZE = 20;    private static final int INIT_SIZE = 10;
86    
87      /** A big number, but not so big it can't survive a few float operations */
88      private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0;
89    
90    /** The winding rule. */    /** The winding rule. */
91    private int rule;    private int rule;
92    
93    /**    /**
94     * The path type in points. Note that points[index] maps to     * The path type in points. Note that xpoints[index] and ypoints[index] maps
95     * types[index >> 1]; the control points of quad and cubic paths map as     * to types[index]; the control points of quad and cubic paths map as
96     * well but are ignored.     * well but are ignored.
97     */     */
98    private byte[] types;    private byte[] types;
99    
100    /**    /**
101     * The list of all points seen. Since you can only append floats, it makes     * The list of all points seen. Since you can only append floats, it makes
102     * sense for this to be a float[]. I have no idea why Sun didn't choose to     * sense for these to be float[]. I have no idea why Sun didn't choose to
103     * allow a general path of double precision points.     * allow a general path of double precision points.
104       * Note: Storing x and y coords seperately makes for a slower transforms,
105       * But it speeds up and simplifies box-intersection checking a lot.
106     */     */
107    private float[] points;    private float[] xpoints;
108      private float[] ypoints;
109    
110    /** The index of the most recent moveto point, or null. */    /** The index of the most recent moveto point, or null. */
111    private int subpath = -1;    private int subpath = -1;
112    
113    /** The next available index into points. */    /** The next available index into points. */
114    private int index;    private int index;
115    
116      /**
117       * Constructs a GeneralPath with the default (NON_ZERO)
118       * winding rule and initial capacity (20).
119       */
120    public GeneralPath()    public GeneralPath()
121    {    {
122      this(WIND_NON_ZERO, INIT_SIZE);      this(WIND_NON_ZERO, INIT_SIZE);
123    }    }
124    
125      /**
126       * Constructs a GeneralPath with a specific winding rule
127       * and the default initial capacity (20).
128       * @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD)
129       */
130    public GeneralPath(int rule)    public GeneralPath(int rule)
131    {    {
132      this(rule, INIT_SIZE);      this(rule, INIT_SIZE);
133    }    }
134    
135      /**
136       * Constructs a GeneralPath with a specific winding rule
137       * and the initial capacity. The initial capacity should be
138       * the approximate number of path segments to be used.
139       * @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD)
140       * @param capacity the inital capacity, in path segments
141       */
142    public GeneralPath(int rule, int capacity)    public GeneralPath(int rule, int capacity)
143    {    {
144      if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)      if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
# Line 88  public final class GeneralPath implement Line 146  public final class GeneralPath implement
146      this.rule = rule;      this.rule = rule;
147      if (capacity < INIT_SIZE)      if (capacity < INIT_SIZE)
148        capacity = INIT_SIZE;        capacity = INIT_SIZE;
149      types = new byte[capacity >> 1];      types = new byte[capacity];
150      points = new float[capacity];      xpoints = new float[capacity];
151        ypoints = new float[capacity];
152    }    }
153    
154      /**
155       * Constructs a GeneralPath from an arbitrary shape object.
156       * The Shapes PathIterator path and winding rule will be used.
157       * @param s the shape
158       */
159    public GeneralPath(Shape s)    public GeneralPath(Shape s)
160    {    {
161      types = new byte[INIT_SIZE >> 1];      types = new byte[INIT_SIZE];
162      points = new float[INIT_SIZE];      xpoints = new float[INIT_SIZE];
163        ypoints = new float[INIT_SIZE];
164      PathIterator pi = s.getPathIterator(null);      PathIterator pi = s.getPathIterator(null);
165      setWindingRule(pi.getWindingRule());      setWindingRule(pi.getWindingRule());
166      append(pi, false);      append(pi, false);
167    }    }
168    
169      /**
170       * Adds a new point to a path.
171       */
172    public void moveTo(float x, float y)    public void moveTo(float x, float y)
173    {    {
174      subpath = index;      subpath = index;
175      ensureSize(index + 2);      ensureSize(index + 1);
176      types[index >> 1] = PathIterator.SEG_MOVETO;      types[index] = PathIterator.SEG_MOVETO;
177      points[index++] = x;      xpoints[index] = x;
178      points[index++] = y;      ypoints[index++] = y;
179    }    }
180    
181      /**
182       * Appends a straight line to the current path.
183       * @param x x coordinate of the line endpoint.
184       * @param y y coordinate of the line endpoint.
185       */
186    public void lineTo(float x, float y)    public void lineTo(float x, float y)
187    {    {
188      ensureSize(index + 2);      ensureSize(index + 1);
189      types[index >> 1] = PathIterator.SEG_LINETO;      types[index] = PathIterator.SEG_LINETO;
190      points[index++] = x;      xpoints[index] = x;
191      points[index++] = y;      ypoints[index++] = y;
192    }    }
193    
194      /**
195       * Appends a quadratic Bezier curve to the current path.
196       * @param x1 x coordinate of the control point
197       * @param y1 y coordinate of the control point
198       * @param x2 x coordinate of the curve endpoint.
199       * @param y2 y coordinate of the curve endpoint.
200       */
201    public void quadTo(float x1, float y1, float x2, float y2)    public void quadTo(float x1, float y1, float x2, float y2)
202    {    {
203      ensureSize(index + 4);      ensureSize(index + 2);
204      types[index >> 1] = PathIterator.SEG_QUADTO;      types[index] = PathIterator.SEG_QUADTO;
205      points[index++] = x1;      xpoints[index] = x1;
206      points[index++] = y1;      ypoints[index++] = y1;
207      points[index++] = x2;      xpoints[index] = x2;
208      points[index++] = y2;      ypoints[index++] = y2;
   }  
   public void curveTo(float x1, float y1, float x2, float y2,  
                       float x3, float y3)  
   {  
     ensureSize(index + 6);  
     types[index >> 1] = PathIterator.SEG_CUBICTO;  
     points[index++] = x1;  
     points[index++] = y1;  
     points[index++] = x2;  
     points[index++] = y2;  
     points[index++] = x3;  
     points[index++] = y3;  
209    }    }
210    
211      /**
212       * Appends a cubic Bezier curve to the current path.
213       * @param x1 x coordinate of the first control point
214       * @param y1 y coordinate of the first control point
215       * @param x2 x coordinate of the second control point
216       * @param y2 y coordinate of the second control point
217       * @param x3 x coordinate of the curve endpoint.
218       * @param y3 y coordinate of the curve endpoint.
219       */
220      public void curveTo(float x1, float y1, float x2, float y2, float x3,
221                          float y3)
222      {
223        ensureSize(index + 3);
224        types[index] = PathIterator.SEG_CUBICTO;
225        xpoints[index] = x1;
226        ypoints[index++] = y1;
227        xpoints[index] = x2;
228        ypoints[index++] = y2;
229        xpoints[index] = x3;
230        ypoints[index++] = y3;
231      }
232    
233      /**
234       * Closes the current subpath by drawing a line
235       * back to the point of the last moveTo.
236       */
237    public void closePath()    public void closePath()
238    {    {
239      ensureSize(index + 2);      ensureSize(index + 1);
240      types[index >> 1] = PathIterator.SEG_CLOSE;      types[index] = PathIterator.SEG_CLOSE;
241      points[index++] = points[subpath];      xpoints[index] = xpoints[subpath];
242      points[index++] = points[subpath + 1];      ypoints[index++] = ypoints[subpath];
243    }    }
244    
245      /**
246       * Appends the segments of a Shape to the path. If <code>connect</code> is
247       * true, the new path segments are connected to the existing one with a line.
248       * The winding rule of the Shape is ignored.
249       */
250    public void append(Shape s, boolean connect)    public void append(Shape s, boolean connect)
251    {    {
252      append(s.getPathIterator(null), connect);      append(s.getPathIterator(null), connect);
253    }    }
254    
   
255    /**    /**
256     * Appends the segments of a PathIterator to this GeneralPath.     * Appends the segments of a PathIterator to this GeneralPath.
257     * Optionally, the initial {@link PathIterator#SEG_MOVETO} segment     * Optionally, the initial {@link PathIterator#SEG_MOVETO} segment
# Line 158  public final class GeneralPath implement Line 260  public final class GeneralPath implement
260     *     *
261     * @param iter the PathIterator specifying which segments shall be     * @param iter the PathIterator specifying which segments shall be
262     * appended.     * appended.
263     *     *
264     * @param connect <code>true</code> for substituting the initial     * @param connect <code>true</code> for substituting the initial
265     * {@link PathIterator#SEG_MOVETO} segment by a {@link     * {@link PathIterator#SEG_MOVETO} segment by a {@link
266     * PathIterator#SEG_LINETO}, or <code>false</code> for not     * PathIterator#SEG_LINETO}, or <code>false</code> for not
# Line 171  public final class GeneralPath implement Line 273  public final class GeneralPath implement
273    {    {
274      // A bad implementation of this method had caused Classpath bug #6076.      // A bad implementation of this method had caused Classpath bug #6076.
275      float[] f = new float[6];      float[] f = new float[6];
276      while (!iter.isDone())      while (! iter.isDone())
     {  
       switch (iter.currentSegment(f))  
277        {        {
278        case PathIterator.SEG_MOVETO:          switch (iter.currentSegment(f))
279          if (!connect || (index == 0))            {
280          {            case PathIterator.SEG_MOVETO:
281            moveTo(f[0], f[1]);              if (! connect || (index == 0))
282            break;                {
283          }                  moveTo(f[0], f[1]);
284                    break;
285                  }
286                if ((index >= 1) && (types[index - 1] == PathIterator.SEG_CLOSE)
287                    && (f[0] == xpoints[index - 1])
288                    && (f[1] == ypoints[index - 1]))
289                  break;
290    
291              // Fall through.
292              case PathIterator.SEG_LINETO:
293                lineTo(f[0], f[1]);
294                break;
295              case PathIterator.SEG_QUADTO:
296                quadTo(f[0], f[1], f[2], f[3]);
297                break;
298              case PathIterator.SEG_CUBICTO:
299                curveTo(f[0], f[1], f[2], f[3], f[4], f[5]);
300                break;
301              case PathIterator.SEG_CLOSE:
302                closePath();
303                break;
304              }
305    
306          if ((index >= 2) && (types[(index - 2) >> 2] == PathIterator.SEG_CLOSE)          connect = false;
307              && (f[0] == points[index - 2]) && (f[1] == points[index - 1]))          iter.next();
           break;  
           
         // Fall through.  
   
       case PathIterator.SEG_LINETO:  
         lineTo(f[0], f[1]);  
         break;  
   
       case PathIterator.SEG_QUADTO:  
         quadTo(f[0], f[1], f[2], f[3]);  
         break;  
   
       case PathIterator.SEG_CUBICTO:  
         curveTo(f[0], f[1], f[2], f[3], f[4], f[5]);  
         break;  
   
       case PathIterator.SEG_CLOSE:  
         closePath();  
         break;  
308        }        }
   
       connect = false;  
       iter.next();  
     }  
309    }    }
310    
311      /**
312       * Returns the path&#x2019;s current winding rule.
313       */
314    public int getWindingRule()    public int getWindingRule()
315    {    {
316      return rule;      return rule;
317    }    }
318    
319      /**
320       * Sets the path&#x2019;s winding rule, which controls which areas are
321       * considered &#x2019;inside&#x2019; or &#x2019;outside&#x2019; the path
322       * on drawing. Valid rules are WIND_EVEN_ODD for an even-odd winding rule,
323       * or WIND_NON_ZERO for a non-zero winding rule.
324       */
325    public void setWindingRule(int rule)    public void setWindingRule(int rule)
326    {    {
327      if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)      if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
# Line 222  public final class GeneralPath implement Line 329  public final class GeneralPath implement
329      this.rule = rule;      this.rule = rule;
330    }    }
331    
332      /**
333       * Returns the current appending point of the path.
334       */
335    public Point2D getCurrentPoint()    public Point2D getCurrentPoint()
336    {    {
337      if (subpath < 0)      if (subpath < 0)
338        return null;        return null;
339      return new Point2D.Float(points[index - 2], points[index - 1]);      return new Point2D.Float(xpoints[index - 1], ypoints[index - 1]);
340    }    }
341    
342      /**
343       * Resets the path. All points and segments are destroyed.
344       */
345    public void reset()    public void reset()
346    {    {
347      subpath = -1;      subpath = -1;
348      index = 0;      index = 0;
349    }    }
350    
351      /**
352       * Applies a transform to the path.
353       */
354    public void transform(AffineTransform xform)    public void transform(AffineTransform xform)
355    {    {
356      xform.transform(points, 0, points, 0, index >> 1);      double nx;
357        double ny;
358        double[] m = new double[6];
359        xform.getMatrix(m);
360        for (int i = 0; i < index; i++)
361          {
362            nx = m[0] * xpoints[i] + m[2] * ypoints[i] + m[4];
363            ny = m[1] * xpoints[i] + m[3] * ypoints[i] + m[5];
364            xpoints[i] = (float) nx;
365            ypoints[i] = (float) ny;
366          }
367    }    }
368    
369      /**
370       * Creates a transformed version of the path.
371       * @param xform the transform to apply
372       * @return a new transformed GeneralPath
373       */
374    public Shape createTransformedShape(AffineTransform xform)    public Shape createTransformedShape(AffineTransform xform)
375    {    {
376      GeneralPath p = new GeneralPath(this);      GeneralPath p = new GeneralPath(this);
# Line 245  public final class GeneralPath implement Line 378  public final class GeneralPath implement
378      return p;      return p;
379    }    }
380    
381      /**
382       * Returns the path&#x2019;s bounding box.
383       */
384    public Rectangle getBounds()    public Rectangle getBounds()
385    {    {
386      return getBounds2D().getBounds();      return getBounds2D().getBounds();
387    }    }
388    
389      /**
390       * Returns the path&#x2019;s bounding box, in <code>float</code> precision
391       */
392    public Rectangle2D getBounds2D()    public Rectangle2D getBounds2D()
393    {    {
394      // XXX Implement.      float x1;
395      throw new Error("not implemented");      float y1;
396        float x2;
397        float y2;
398    
399        if (index > 0)
400          {
401            x1 = x2 = xpoints[0];
402            y1 = y2 = ypoints[0];
403          }
404        else
405          x1 = x2 = y1 = y2 = 0.0f;
406    
407        for (int i = 0; i < index; i++)
408          {
409            x1 = Math.min(xpoints[i], x1);
410            y1 = Math.min(ypoints[i], y1);
411            x2 = Math.max(xpoints[i], x2);
412            y2 = Math.max(ypoints[i], y2);
413          }
414        return (new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1));
415    }    }
416    
417      /**
418       * Evaluates if a point is within the GeneralPath,
419       * The NON_ZERO winding rule is used, regardless of the
420       * set winding rule.
421       * @param x x coordinate of the point to evaluate
422       * @param y y coordinate of the point to evaluate
423       * @return true if the point is within the path, false otherwise
424       */
425    public boolean contains(double x, double y)    public boolean contains(double x, double y)
426    {    {
427      // XXX Implement.      return (getWindingNumber(x, y) != 0);
     throw new Error("not implemented");  
428    }    }
429    
430      /**
431       * Evaluates if a Point2D is within the GeneralPath,
432       * The NON_ZERO winding rule is used, regardless of the
433       * set winding rule.
434       * @param p The Point2D to evaluate
435       * @return true if the point is within the path, false otherwise
436       */
437    public boolean contains(Point2D p)    public boolean contains(Point2D p)
438    {    {
439      return contains(p.getX(), p.getY());      return contains(p.getX(), p.getY());
440    }    }
441    
442      /**
443       * Evaluates if a rectangle is completely contained within the path.
444       * This method will return false in the cases when the box
445       * intersects an inner segment of the path.
446       * (i.e.: The method is accurate for the EVEN_ODD winding rule)
447       */
448    public boolean contains(double x, double y, double w, double h)    public boolean contains(double x, double y, double w, double h)
449    {    {
450      // XXX Implement.      if (! getBounds2D().intersects(x, y, w, h))
451      throw new Error("not implemented");        return false;
452    
453        /* Does any edge intersect? */
454        if (getAxisIntersections(x, y, false, w) != 0 /* top */
455            || getAxisIntersections(x, y + h, false, w) != 0 /* bottom */
456            || getAxisIntersections(x + w, y, true, h) != 0 /* right */
457            || getAxisIntersections(x, y, true, h) != 0) /* left */
458          return false;
459    
460        /* No intersections, is any point inside? */
461        if (getWindingNumber(x, y) != 0)
462          return true;
463    
464        return false;
465    }    }
466    
467      /**
468       * Evaluates if a rectangle is completely contained within the path.
469       * This method will return false in the cases when the box
470       * intersects an inner segment of the path.
471       * (i.e.: The method is accurate for the EVEN_ODD winding rule)
472       * @param r the rectangle
473       * @return <code>true</code> if the rectangle is completely contained
474       * within the path, <code>false</code> otherwise
475       */
476    public boolean contains(Rectangle2D r)    public boolean contains(Rectangle2D r)
477    {    {
478      return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());      return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
479    }    }
480    
481      /**
482       * Evaluates if a rectangle intersects the path.
483       * @param x x coordinate of the rectangle
484       * @param y y coordinate of the rectangle
485       * @param w width of the rectangle
486       * @param h height of the rectangle
487       * @return <code>true</code> if the rectangle intersects the path,
488       * <code>false</code> otherwise
489       */
490    public boolean intersects(double x, double y, double w, double h)    public boolean intersects(double x, double y, double w, double h)
491    {    {
492      // XXX Implement.      /* Does any edge intersect? */
493      throw new Error("not implemented");      if (getAxisIntersections(x, y, false, w) != 0 /* top */
494            || getAxisIntersections(x, y + h, false, w) != 0 /* bottom */
495            || getAxisIntersections(x + w, y, true, h) != 0 /* right */
496            || getAxisIntersections(x, y, true, h) != 0) /* left */
497          return true;
498    
499        /* No intersections, is any point inside? */
500        if (getWindingNumber(x, y) != 0)
501          return true;
502    
503        return false;
504    }    }
505    
506      /**
507       * Evaluates if a Rectangle2D intersects the path.
508       * @param r The rectangle
509       * @return <code>true</code> if the rectangle intersects the path,
510       * <code>false</code> otherwise
511       */
512    public boolean intersects(Rectangle2D r)    public boolean intersects(Rectangle2D r)
513    {    {
514      return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());      return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
515    }    }
516    
   
517    /**    /**
518     * A PathIterator that iterates over the segments of a GeneralPath.     * A PathIterator that iterates over the segments of a GeneralPath.
519     *     *
520     * @author Sascha Brawer (brawer@dandelis.ch)     * @author Sascha Brawer (brawer@dandelis.ch)
521     */     */
522    private static class GeneralPathIterator    private static class GeneralPathIterator implements PathIterator
     implements PathIterator  
523    {    {
524      /**      /**
525       * The number of coordinate values for each segment type.       * The number of coordinate values for each segment type.
526       */       */
527      private static final int[] NUM_COORDS =      private static final int[] NUM_COORDS = {
528      {                                              /* 0: SEG_MOVETO */ 1,
529        /* 0: SEG_MOVETO */ 2,                                              /* 1: SEG_LINETO */ 1,
530        /* 1: SEG_LINETO */ 2,                                              /* 2: SEG_QUADTO */ 2,
531        /* 2: SEG_QUADTO */ 4,                                              /* 3: SEG_CUBICTO */ 3,
532        /* 3: SEG_CUBICTO */ 6,                                              /* 4: SEG_CLOSE */ 0};
       /* 4: SEG_CLOSE */ 0  
     };  
   
533    
534      /**      /**
535       * The GeneralPath whose segments are being iterated.       * The GeneralPath whose segments are being iterated.
536       */       */
537      private final GeneralPath path;      private final GeneralPath path;
538    
   
539      /**      /**
540       * The affine transformation used to transform coordinates.       * The affine transformation used to transform coordinates.
541       */       */
542      private final AffineTransform transform;      private final AffineTransform transform;
543    
   
544      /**      /**
545       * The current position of the iterator.       * The current position of the iterator.
546       */       */
547      private int pos;      private int pos;
548    
   
549      /**      /**
550       * Constructs a new iterator for enumerating the segments of a       * Constructs a new iterator for enumerating the segments of a
551       * GeneralPath.       * GeneralPath.
# Line 338  public final class GeneralPath implement Line 560  public final class GeneralPath implement
560        this.transform = transform;        this.transform = transform;
561      }      }
562    
   
563      /**      /**
564       * Returns the current winding rule of the GeneralPath.       * Returns the current winding rule of the GeneralPath.
565       */       */
# Line 347  public final class GeneralPath implement Line 568  public final class GeneralPath implement
568        return path.rule;        return path.rule;
569      }      }
570    
   
571      /**      /**
572       * Determines whether the iterator has reached the last segment in       * Determines whether the iterator has reached the last segment in
573       * the path.       * the path.
# Line 357  public final class GeneralPath implement Line 577  public final class GeneralPath implement
577        return pos >= path.index;        return pos >= path.index;
578      }      }
579    
   
580      /**      /**
581       * Advances the iterator position by one segment.       * Advances the iterator position by one segment.
582       */       */
# Line 365  public final class GeneralPath implement Line 584  public final class GeneralPath implement
584      {      {
585        int seg;        int seg;
586    
587        /* Increment pos by the number of coordinate values. Note that        /*
588         * we store two values even for a SEG_CLOSE segment, which is         * Increment pos by the number of coordinate pairs.
        * why we increment pos at least by 2.  
589         */         */
590        seg = path.types[pos >> 1];        seg = path.types[pos];
591        if (seg == SEG_CLOSE)        if (seg == SEG_CLOSE)
592          pos += 2;          pos++;
593        else        else
594          pos += NUM_COORDS[seg];          pos += NUM_COORDS[seg];
595      }      }
596    
   
597      /**      /**
598       * Returns the current segment in float coordinates.       * Returns the current segment in float coordinates.
599       */       */
600      public int currentSegment(float[] coords)      public int currentSegment(float[] coords)
601      {      {
602        int seg, numCoords;        int seg;
603          int numCoords;
604    
605        seg = path.types[pos >> 1];        seg = path.types[pos];
606        numCoords = NUM_COORDS[seg];        numCoords = NUM_COORDS[seg];
607        if (numCoords > 0)        if (numCoords > 0)
608        {          {
609          if (transform == null)            for (int i = 0; i < numCoords; i++)
610            System.arraycopy(path.points, pos, coords, 0, numCoords);              {
611          else                coords[i << 1] = path.xpoints[pos + i];
612            transform.transform(/* src */ path.points, /* srcOffset */ pos,                coords[(i << 1) + 1] = path.ypoints[pos + i];
613                                /* dest */ coords, /* destOffset */ 0,              }
614                                /* numPoints */ numCoords >> 1);  
615        }            if (transform != null)
616                transform.transform( /* src */
617                coords, /* srcOffset */
618                0, /* dest */ coords, /* destOffset */
619                0, /* numPoints */ numCoords);
620            }
621        return seg;        return seg;
622      }      }
623    
   
624      /**      /**
625       * Returns the current segment in double coordinates.       * Returns the current segment in double coordinates.
626       */       */
627      public int currentSegment(double[] coords)      public int currentSegment(double[] coords)
628      {      {
629        int seg, numCoords;        int seg;
630          int numCoords;
631    
632        seg = path.types[pos >> 1];        seg = path.types[pos];
633        numCoords = NUM_COORDS[seg];        numCoords = NUM_COORDS[seg];
634        if (numCoords > 0)        if (numCoords > 0)
       {  
         if (transform == null)  
635          {          {
636            // System.arraycopy throws an exception if the source and destination            for (int i = 0; i < numCoords; i++)
637            // array are not of the same primitive type.              {
638            for (int i = 0; i < numCoords; i++)                coords[i << 1] = (double) path.xpoints[pos + i];
639              coords[i] = (double) path.points[pos + i];                coords[(i << 1) + 1] = (double) path.ypoints[pos + i];
640                }
641              if (transform != null)
642                transform.transform( /* src */
643                coords, /* srcOffset */
644                pos, /* dest */ coords, /* destOffset */
645                0, /* numPoints */ numCoords);
646          }          }
         else  
           transform.transform(/* src */ path.points, /* srcOffset */ pos,  
                               /* dest */ coords, /* destOffset */ 0,  
                               /* numPoints */ numCoords >> 1);  
       }  
647        return seg;        return seg;
648      }      }
649    }    }
650    
   
651    /**    /**
652     * Creates a PathIterator for iterating along the segments of this path.     * Creates a PathIterator for iterating along the segments of the path.
653     *     *
654     * @param at an affine transformation for projecting the returned     * @param at an affine transformation for projecting the returned
655     * points, or <code>null</code> to let the created iterator return     * points, or <code>null</code> to let the created iterator return
# Line 439  public final class GeneralPath implement Line 660  public final class GeneralPath implement
660      return new GeneralPathIterator(this, at);      return new GeneralPathIterator(this, at);
661    }    }
662    
663      /**
664       * Creates a new FlatteningPathIterator for the path
665       */
666    public PathIterator getPathIterator(AffineTransform at, double flatness)    public PathIterator getPathIterator(AffineTransform at, double flatness)
667    {    {
668      return new FlatteningPathIterator(getPathIterator(at), flatness);      return new FlatteningPathIterator(getPathIterator(at), flatness);
669    }    }
670    
671    /**    /**
672     * Create a new shape of the same run-time type with the same contents as     * Creates a new shape of the same run-time type with the same contents
673     * this one.     * as this one.
674     *     *
675     * @return the clone     * @return the clone
676     *     *
# Line 461  public final class GeneralPath implement Line 684  public final class GeneralPath implement
684      return new GeneralPath(this);      return new GeneralPath(this);
685    }    }
686    
687      /**
688       * Helper method - ensure the size of the data arrays,
689       * otherwise, reallocate new ones twice the size
690       */
691    private void ensureSize(int size)    private void ensureSize(int size)
692    {    {
693      if (subpath < 0)      if (subpath < 0)
694        throw new IllegalPathStateException("need initial moveto");        throw new IllegalPathStateException("need initial moveto");
695      if (size <= points.length)      if (size <= xpoints.length)
696        return;        return;
697      byte[] b = new byte[points.length];      byte[] b = new byte[types.length << 1];
698      System.arraycopy(types, 0, b, 0, index >> 1);      System.arraycopy(types, 0, b, 0, index);
699      types = b;      types = b;
700      float[] f = new float[points.length << 1];      float[] f = new float[xpoints.length << 1];
701      System.arraycopy(points, 0, f, 0, index);      System.arraycopy(xpoints, 0, f, 0, index);
702      points = f;      xpoints = f;
703        f = new float[ypoints.length << 1];
704        System.arraycopy(ypoints, 0, f, 0, index);
705        ypoints = f;
706      }
707    
708      /**
709       * Helper method - Get the total number of intersections from (x,y) along
710       * a given axis, within a given distance.
711       */
712      private int getAxisIntersections(double x, double y, boolean useYaxis,
713                                       double distance)
714      {
715        return (evaluateCrossings(x, y, false, useYaxis, distance));
716      }
717    
718      /**
719       * Helper method - returns the winding number of a point.
720       */
721      private int getWindingNumber(double x, double y)
722      {
723        /* Evaluate the crossings from x,y to infinity on the y axis (arbitrary
724           choice). Note that we don't actually use Double.INFINITY, since that's
725           slower, and may cause problems. */
726        return (evaluateCrossings(x, y, true, true, BIG_VALUE));
727      }
728    
729      /**
730       * Helper method - evaluates the number of intersections on an axis from
731       * the point (x,y) to the point (x,y+distance) or (x+distance,y).
732       * @param x x coordinate.
733       * @param y y coordinate.
734       * @param neg True if opposite-directed intersections should cancel,
735       * false to sum all intersections.
736       * @param useYaxis Use the Y axis, false uses the X axis.
737       * @param distance Interval from (x,y) on the selected axis to find
738       * intersections.
739       */
740      private int evaluateCrossings(double x, double y, boolean neg,
741                                    boolean useYaxis, double distance)
742      {
743        float cx = 0.0f;
744        float cy = 0.0f;
745        float firstx = 0.0f;
746        float firsty = 0.0f;
747    
748        int negative = (neg) ? -1 : 1;
749        double x0;
750        double x1;
751        double x2;
752        double x3;
753        double y0;
754        double y1;
755        double y2;
756        double y3;
757        double[] r = new double[4];
758        int nRoots;
759        double epsilon = 0.0;
760        int pos = 0;
761        int windingNumber = 0;
762        boolean pathStarted = false;
763    
764        if (index == 0)
765          return (0);
766        if (useYaxis)
767          {
768            float[] swap1;
769            swap1 = ypoints;
770            ypoints = xpoints;
771            xpoints = swap1;
772            double swap2;
773            swap2 = y;
774            y = x;
775            x = swap2;
776          }
777    
778        /* Get a value which is hopefully small but not insignificant relative
779         the path. */
780        epsilon = ypoints[0] * 1E-9;
781    
782        pos = 0;
783        while (pos < index)
784          {
785            switch (types[pos])
786              {
787              case PathIterator.SEG_MOVETO:
788                if (pathStarted) // close old path
789                  {
790                    x0 = cx;
791                    y0 = cy;
792                    x1 = firstx;
793                    y1 = firsty;
794    
795                    if (y0 == 0.0)
796                      y0 += epsilon;
797                    if (y1 == 0.0)
798                      y1 += epsilon;
799                    if (Line2D.linesIntersect(x0, y0, x1, y1, 0.0, 0.0, distance,
800                                              0.0))
801                      windingNumber += (y1 < y0) ? 1 : negative;
802    
803                    cx = firstx;
804                    cy = firsty;
805                  }
806                cx = firstx = xpoints[pos] - (float) x;
807                cy = firsty = ypoints[pos++] - (float) y;
808                pathStarted = true;
809                break;
810              case PathIterator.SEG_CLOSE:
811                x0 = cx;
812                y0 = cy;
813                x1 = firstx;
814                y1 = firsty;
815    
816                if (y0 == 0.0)
817                  y0 += epsilon;
818                if (y1 == 0.0)
819                  y1 += epsilon;
820                if (Line2D.linesIntersect(x0, y0, x1, y1, 0.0, 0.0, distance, 0.0))
821                  windingNumber += (y1 < y0) ? 1 : negative;
822    
823                cx = firstx;
824                cy = firsty;
825                pos++;
826                pathStarted = false;
827                break;
828              case PathIterator.SEG_LINETO:
829                x0 = cx;
830                y0 = cy;
831                x1 = xpoints[pos] - (float) x;
832                y1 = ypoints[pos++] - (float) y;
833    
834                if (y0 == 0.0)
835                  y0 += epsilon;
836                if (y1 == 0.0)
837                  y1 += epsilon;
838                if (Line2D.linesIntersect(x0, y0, x1, y1, 0.0, 0.0, distance, 0.0))
839                  windingNumber += (y1 < y0) ? 1 : negative;
840    
841                cx = xpoints[pos - 1] - (float) x;
842                cy = ypoints[pos - 1] - (float) y;
843                break;
844              case PathIterator.SEG_QUADTO:
845                x0 = cx;
846                y0 = cy;
847                x1 = xpoints[pos] - x;
848                y1 = ypoints[pos++] - y;
849                x2 = xpoints[pos] - x;
850                y2 = ypoints[pos++] - y;
851    
852                /* check if curve may intersect X+ axis. */
853                if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0)
854                    && (y0 * y1 <= 0 || y1 * y2 <= 0))
855                  {
856                    if (y0 == 0.0)
857                      y0 += epsilon;
858                    if (y2 == 0.0)
859                      y2 += epsilon;
860    
861                    r[0] = y0;
862                    r[1] = 2 * (y1 - y0);
863                    r[2] = (y2 - 2 * y1 + y0);
864    
865                    /* degenerate roots (=tangent points) do not
866                       contribute to the winding number. */
867                    if ((nRoots = QuadCurve2D.solveQuadratic(r)) == 2)
868                      for (int i = 0; i < nRoots; i++)
869                        {
870                          float t = (float) r[i];
871                          if (t > 0.0f && t < 1.0f)
872                            {
873                              double crossing = t * t * (x2 - 2 * x1 + x0)
874                                                + 2 * t * (x1 - x0) + x0;
875                              if (crossing >= 0.0 && crossing <= distance)
876                                windingNumber += (2 * t * (y2 - 2 * y1 + y0)
877                                               + 2 * (y1 - y0) < 0) ? 1 : negative;
878                            }
879                        }
880                  }
881    
882                cx = xpoints[pos - 1] - (float) x;
883                cy = ypoints[pos - 1] - (float) y;
884                break;
885              case PathIterator.SEG_CUBICTO:
886                x0 = cx;
887                y0 = cy;
888                x1 = xpoints[pos] - x;
889                y1 = ypoints[pos++] - y;
890                x2 = xpoints[pos] - x;
891                y2 = ypoints[pos++] - y;
892                x3 = xpoints[pos] - x;
893                y3 = ypoints[pos++] - y;
894    
895                /* check if curve may intersect X+ axis. */
896                if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0)
897                    && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0))
898                  {
899                    if (y0 == 0.0)
900                      y0 += epsilon;
901                    if (y3 == 0.0)
902                      y3 += epsilon;
903    
904                    r[0] = y0;
905                    r[1] = 3 * (y1 - y0);
906                    r[2] = 3 * (y2 + y0 - 2 * y1);
907                    r[3] = y3 - 3 * y2 + 3 * y1 - y0;
908    
909                    if ((nRoots = CubicCurve2D.solveCubic(r)) != 0)
910                      for (int i = 0; i < nRoots; i++)
911                        {
912                          float t = (float) r[i];
913                          if (t > 0.0 && t < 1.0)
914                            {
915                              double crossing = -(t * t * t) * (x0 - 3 * x1
916                                                + 3 * x2 - x3)
917                                                + 3 * t * t * (x0 - 2 * x1 + x2)
918                                                + 3 * t * (x1 - x0) + x0;
919                              if (crossing >= 0 && crossing <= distance)
920                                windingNumber += (3 * t * t * (y3 + 3 * y1
921                                                 - 3 * y2 - y0)
922                                                 + 6 * t * (y0 - 2 * y1 + y2)
923                                               + 3 * (y1 - y0) < 0) ? 1 : negative;
924                            }
925                        }
926                  }
927    
928                cx = xpoints[pos - 1] - (float) x;
929                cy = ypoints[pos - 1] - (float) y;
930                break;
931              }
932          }
933    
934        // swap coordinates back
935        if (useYaxis)
936          {
937            float[] swap;
938            swap = ypoints;
939            ypoints = xpoints;
940            xpoints = swap;
941          }
942        return (windingNumber);
943    }    }
944  } // class GeneralPath  } // class GeneralPath

Legend:
Removed from v.1.6  
changed lines
  Added in v.1.7

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26