1 /* GeneralPath.java -- represents a shape built from subpaths
2 Copyright (C) 2002, 2003 Free Software Foundation
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package java
.awt
.geom
;
41 import java
.awt
.Rectangle
;
42 import java
.awt
.Shape
;
46 * XXX Implement and document. Note that Sun's implementation only expects
47 * float precision, not double.
49 public final class GeneralPath
implements Shape
, Cloneable
51 public static final int WIND_EVEN_ODD
= PathIterator
.WIND_EVEN_ODD
;
52 public static final int WIND_NON_ZERO
= PathIterator
.WIND_NON_ZERO
;
54 /** Initial size if not specified. */
55 private static final int INIT_SIZE
= 20;
57 /** The winding rule. */
60 * The path type in points. Note that points[index] maps to
61 * types[index >> 1]; the control points of quad and cubic paths map as
62 * well but are ignored.
66 * The list of all points seen. Since you can only append floats, it makes
67 * sense for this to be a float[]. I have no idea why Sun didn't choose to
68 * allow a general path of double precision points.
70 private float[] points
;
71 /** The index of the most recent moveto point, or null. */
72 private int subpath
= -1;
73 /** The next available index into points. */
78 this(WIND_NON_ZERO
, INIT_SIZE
);
80 public GeneralPath(int rule
)
82 this(rule
, INIT_SIZE
);
84 public GeneralPath(int rule
, int capacity
)
86 if (rule
!= WIND_EVEN_ODD
&& rule
!= WIND_NON_ZERO
)
87 throw new IllegalArgumentException();
89 if (capacity
< INIT_SIZE
)
91 types
= new byte[capacity
>> 1];
92 points
= new float[capacity
];
94 public GeneralPath(Shape s
)
96 types
= new byte[INIT_SIZE
>> 1];
97 points
= new float[INIT_SIZE
];
98 PathIterator pi
= s
.getPathIterator(null);
99 setWindingRule(pi
.getWindingRule());
103 public void moveTo(float x
, float y
)
106 ensureSize(index
+ 2);
107 types
[index
>> 1] = PathIterator
.SEG_MOVETO
;
111 public void lineTo(float x
, float y
)
113 ensureSize(index
+ 2);
114 types
[index
>> 1] = PathIterator
.SEG_LINETO
;
118 public void quadTo(float x1
, float y1
, float x2
, float y2
)
120 ensureSize(index
+ 4);
121 types
[index
>> 1] = PathIterator
.SEG_QUADTO
;
122 points
[index
++] = x1
;
123 points
[index
++] = y1
;
124 points
[index
++] = x2
;
125 points
[index
++] = y2
;
127 public void curveTo(float x1
, float y1
, float x2
, float y2
,
130 ensureSize(index
+ 6);
131 types
[index
>> 1] = PathIterator
.SEG_CUBICTO
;
132 points
[index
++] = x1
;
133 points
[index
++] = y1
;
134 points
[index
++] = x2
;
135 points
[index
++] = y2
;
136 points
[index
++] = x3
;
137 points
[index
++] = y3
;
139 public void closePath()
141 ensureSize(index
+ 2);
142 types
[index
>> 1] = PathIterator
.SEG_CLOSE
;
143 points
[index
++] = points
[subpath
];
144 points
[index
++] = points
[subpath
+ 1];
147 public void append(Shape s
, boolean connect
)
149 append(s
.getPathIterator(null), connect
);
154 * Appends the segments of a PathIterator to this GeneralPath.
155 * Optionally, the initial {@link PathIterator#SEG_MOVETO} segment
156 * of the appended path is changed into a {@link
157 * PathIterator#SEG_LINETO} segment.
159 * @param iter the PathIterator specifying which segments shall be
162 * @param connect <code>true</code> for substituting the initial
163 * {@link PathIterator#SEG_MOVETO} segment by a {@link
164 * PathIterator#SEG_LINETO}, or <code>false</code> for not
165 * performing any substitution. If this GeneralPath is currently
166 * empty, <code>connect</code> is assumed to be <code>false</code>,
167 * thus leaving the initial {@link PathIterator#SEG_MOVETO}
170 public void append(PathIterator iter
, boolean connect
)
172 // A bad implementation of this method had caused Classpath bug #6076.
173 float[] f
= new float[6];
174 while (!iter
.isDone())
176 switch (iter
.currentSegment(f
))
178 case PathIterator
.SEG_MOVETO
:
179 if (!connect
|| (index
== 0))
185 if ((index
>= 2) && (types
[(index
- 2) >> 2] == PathIterator
.SEG_CLOSE
)
186 && (f
[0] == points
[index
- 2]) && (f
[1] == points
[index
- 1]))
191 case PathIterator
.SEG_LINETO
:
195 case PathIterator
.SEG_QUADTO
:
196 quadTo(f
[0], f
[1], f
[2], f
[3]);
199 case PathIterator
.SEG_CUBICTO
:
200 curveTo(f
[0], f
[1], f
[2], f
[3], f
[4], f
[5]);
203 case PathIterator
.SEG_CLOSE
:
214 public int getWindingRule()
218 public void setWindingRule(int rule
)
220 if (rule
!= WIND_EVEN_ODD
&& rule
!= WIND_NON_ZERO
)
221 throw new IllegalArgumentException();
225 public Point2D
getCurrentPoint()
229 return new Point2D
.Float(points
[index
- 2], points
[index
- 1]);
237 public void transform(AffineTransform xform
)
239 xform
.transform(points
, 0, points
, 0, index
>> 1);
241 public Shape
createTransformedShape(AffineTransform xform
)
243 GeneralPath p
= new GeneralPath(this);
248 public Rectangle
getBounds()
250 return getBounds2D().getBounds();
252 public Rectangle2D
getBounds2D()
255 throw new Error("not implemented");
258 public boolean contains(double x
, double y
)
261 throw new Error("not implemented");
263 public boolean contains(Point2D p
)
265 return contains(p
.getX(), p
.getY());
267 public boolean contains(double x
, double y
, double w
, double h
)
270 throw new Error("not implemented");
272 public boolean contains(Rectangle2D r
)
274 return contains(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight());
277 public boolean intersects(double x
, double y
, double w
, double h
)
280 throw new Error("not implemented");
282 public boolean intersects(Rectangle2D r
)
284 return intersects(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight());
289 * A PathIterator that iterates over the segments of a GeneralPath.
291 * @author Sascha Brawer (brawer@dandelis.ch)
293 private static class GeneralPathIterator
294 implements PathIterator
297 * The number of coordinate values for each segment type.
299 private static final int[] NUM_COORDS
=
301 /* 0: SEG_MOVETO */ 2,
302 /* 1: SEG_LINETO */ 2,
303 /* 2: SEG_QUADTO */ 4,
304 /* 3: SEG_CUBICTO */ 6,
310 * The GeneralPath whose segments are being iterated.
312 private final GeneralPath path
;
316 * The affine transformation used to transform coordinates.
318 private final AffineTransform transform
;
322 * The current position of the iterator.
328 * Constructs a new iterator for enumerating the segments of a
331 * @param at an affine transformation for projecting the returned
332 * points, or <code>null</code> to return the original points
333 * without any mapping.
335 GeneralPathIterator(GeneralPath path
, AffineTransform transform
)
338 this.transform
= transform
;
343 * Returns the current winding rule of the GeneralPath.
345 public int getWindingRule()
352 * Determines whether the iterator has reached the last segment in
355 public boolean isDone()
357 return pos
>= path
.index
;
362 * Advances the iterator position by one segment.
368 /* Increment pos by the number of coordinate values. Note that
369 * we store two values even for a SEG_CLOSE segment, which is
370 * why we increment pos at least by 2.
372 seg
= path
.types
[pos
>> 1];
373 if (seg
== SEG_CLOSE
)
376 pos
+= NUM_COORDS
[seg
];
381 * Returns the current segment in float coordinates.
383 public int currentSegment(float[] coords
)
387 seg
= path
.types
[pos
>> 1];
388 numCoords
= NUM_COORDS
[seg
];
391 if (transform
== null)
392 System
.arraycopy(path
.points
, pos
, coords
, 0, numCoords
);
394 transform
.transform(/* src */ path
.points
, /* srcOffset */ pos
,
395 /* dest */ coords
, /* destOffset */ 0,
396 /* numPoints */ numCoords
>> 1);
403 * Returns the current segment in double coordinates.
405 public int currentSegment(double[] coords
)
409 seg
= path
.types
[pos
>> 1];
410 numCoords
= NUM_COORDS
[seg
];
413 if (transform
== null)
415 // System.arraycopy throws an exception if the source and destination
416 // array are not of the same primitive type.
417 for (int i
= 0; i
< numCoords
; i
++)
418 coords
[i
] = (double) path
.points
[pos
+ i
];
421 transform
.transform(/* src */ path
.points
, /* srcOffset */ pos
,
422 /* dest */ coords
, /* destOffset */ 0,
423 /* numPoints */ numCoords
>> 1);
431 * Creates a PathIterator for iterating along the segments of this path.
433 * @param at an affine transformation for projecting the returned
434 * points, or <code>null</code> to let the created iterator return
435 * the original points without any mapping.
437 public PathIterator
getPathIterator(AffineTransform at
)
439 return new GeneralPathIterator(this, at
);
443 public PathIterator
getPathIterator(AffineTransform at
, double flatness
)
445 return new FlatteningPathIterator(getPathIterator(at
), flatness
);
449 * Create a new shape of the same run-time type with the same contents as
454 * @exception OutOfMemoryError If there is not enough memory available.
458 public Object
clone()
460 // This class is final; no need to use super.clone().
461 return new GeneralPath(this);
464 private void ensureSize(int size
)
467 throw new IllegalPathStateException("need initial moveto");
468 if (size
<= points
.length
)
470 byte[] b
= new byte[points
.length
];
471 System
.arraycopy(types
, 0, b
, 0, index
>> 1);
473 float[] f
= new float[points
.length
<< 1];
474 System
.arraycopy(points
, 0, f
, 0, index
);
477 } // class GeneralPath