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 |
|
* ’up’ |
65 |
|
* direction, one in the ’down’ direction) Point <b>B</b> in |
66 |
|
* the image is inside (one intersection ’down’) |
67 |
|
* Point <b>C</b> in the image is outside (two intersections |
68 |
|
* ’down’) |
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 |
{ |
{ |
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) |
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 |
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 |
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’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’s winding rule, which controls which areas are |
321 |
|
* considered ’inside’ or ’outside’ 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) |
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); |
378 |
return p; |
return p; |
379 |
} |
} |
380 |
|
|
381 |
|
/** |
382 |
|
* Returns the path’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’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. |
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 |
*/ |
*/ |
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. |
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 |
*/ |
*/ |
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 |
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 |
* |
* |
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 |