root / branches / v2_0_0_prep / libraries / libFMap_geometries / src / org / gvsig / fmap / geom / primitive / GeneralPathX.java @ 38597
History | View | Annotate | Download (33.2 KB)
1 | 21308 | jiyarza | /* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
|
---|---|---|---|
2 | 20761 | jmvivo | *
|
3 | * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
|
||
4 | *
|
||
5 | * This program is free software; you can redistribute it and/or
|
||
6 | * modify it under the terms of the GNU General Public License
|
||
7 | * as published by the Free Software Foundation; either version 2
|
||
8 | * of the License, or (at your option) any later version.
|
||
9 | *
|
||
10 | * This program is distributed in the hope that it will be useful,
|
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
13 | * GNU General Public License for more details.
|
||
14 | *
|
||
15 | * You should have received a copy of the GNU General Public License
|
||
16 | * along with this program; if not, write to the Free Software
|
||
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
|
||
18 | *
|
||
19 | * For more information, contact:
|
||
20 | *
|
||
21 | * Generalitat Valenciana
|
||
22 | * Conselleria d'Infraestructures i Transport
|
||
23 | 21308 | jiyarza | * Av. Blasco Ib??ez, 50
|
24 | 20761 | jmvivo | * 46010 VALENCIA
|
25 | * SPAIN
|
||
26 | *
|
||
27 | * +34 963862235
|
||
28 | * gvsig@gva.es
|
||
29 | * www.gvsig.gva.es
|
||
30 | *
|
||
31 | * or
|
||
32 | *
|
||
33 | * IVER T.I. S.A
|
||
34 | * Salamanca 50
|
||
35 | * 46005 Valencia
|
||
36 | * Spain
|
||
37 | *
|
||
38 | * +34 963163400
|
||
39 | * dac@iver.es
|
||
40 | */
|
||
41 | package org.gvsig.fmap.geom.primitive; |
||
42 | |||
43 | /*
|
||
44 | 38067 | cordinyana | * Based on portions of code from java.awt.geom.GeneralPath of the
|
45 | * OpenJDK project (Copyright (c) 1996, 2006, Oracle and/or its affiliates)
|
||
46 | 20761 | jmvivo | */
|
47 | import java.awt.Shape; |
||
48 | 26788 | jpiera | import java.awt.geom.AffineTransform; |
49 | import java.awt.geom.FlatteningPathIterator; |
||
50 | import java.awt.geom.IllegalPathStateException; |
||
51 | import java.awt.geom.PathIterator; |
||
52 | 20761 | jmvivo | import java.awt.geom.Point2D; |
53 | 26788 | jpiera | import java.awt.geom.Rectangle2D; |
54 | 20761 | jmvivo | import java.io.Serializable; |
55 | 22962 | vcaballero | import java.util.ArrayList; |
56 | 35472 | jpiera | import java.util.List; |
57 | 20761 | jmvivo | |
58 | 22962 | vcaballero | import com.vividsolutions.jts.algorithm.CGAlgorithms; |
59 | import com.vividsolutions.jts.geom.Coordinate; |
||
60 | import com.vividsolutions.jts.geom.CoordinateList; |
||
61 | import com.vividsolutions.jts.geom.CoordinateSequences; |
||
62 | import com.vividsolutions.jts.geom.impl.CoordinateArraySequence; |
||
63 | |||
64 | 35325 | cordinyana | import org.cresques.cts.ICoordTrans; |
65 | 35472 | jpiera | import org.slf4j.Logger; |
66 | import org.slf4j.LoggerFactory; |
||
67 | 35325 | cordinyana | |
68 | 35472 | jpiera | import org.gvsig.fmap.geom.Geometry; |
69 | 35325 | cordinyana | import org.gvsig.fmap.geom.GeometryLocator; |
70 | import org.gvsig.fmap.geom.GeometryManager; |
||
71 | 35472 | jpiera | import org.gvsig.fmap.geom.exception.CreateGeometryException; |
72 | 35325 | cordinyana | import org.gvsig.jdk.GeomUtilities; |
73 | |||
74 | 20761 | jmvivo | /**
|
75 | * The <code>GeneralPathX</code> class represents a geometric path
|
||
76 | * constructed from straight lines, and quadratic and cubic
|
||
77 | 36198 | cordinyana | * (Bézier) curves. It can contain multiple subpaths.
|
78 | 20761 | jmvivo | * <p>
|
79 | 36198 | cordinyana | * The winding rule specifies how the interior of a path is determined. There
|
80 | * are two types of winding rules: EVEN_ODD and NON_ZERO.
|
||
81 | 20761 | jmvivo | * <p>
|
82 | 36198 | cordinyana | * An EVEN_ODD winding rule means that enclosed regions of the path alternate
|
83 | * between interior and exterior areas as traversed from the outside of the path
|
||
84 | * towards a point inside the region.
|
||
85 | 20761 | jmvivo | * <p>
|
86 | 36198 | cordinyana | * A NON_ZERO winding rule means that if a ray is drawn in any direction from a
|
87 | * given point to infinity and the places where the path intersects the ray are
|
||
88 | * examined, the point is inside of the path if and only if the number of times
|
||
89 | * that the path crosses the ray from left to right does not equal the number of
|
||
90 | * times that the path crosses the ray from right to left.
|
||
91 | *
|
||
92 | 20761 | jmvivo | * @version 1.58, 01/23/03
|
93 | * @author Jim Graham
|
||
94 | 35472 | jpiera | * @deprecated
|
95 | 36198 | cordinyana | * use the geometry methods
|
96 | 20761 | jmvivo | */
|
97 | public class GeneralPathX implements Shape, Cloneable, Serializable { |
||
98 | 36198 | cordinyana | |
99 | 35472 | jpiera | /**
|
100 | * Default serial version ID
|
||
101 | */
|
||
102 | 36198 | cordinyana | private static final long serialVersionUID = 1L; |
103 | 20761 | jmvivo | |
104 | 36198 | cordinyana | private static final Logger LOG = LoggerFactory |
105 | .getLogger(GeneralPathX.class); |
||
106 | 31671 | jpiera | |
107 | 36198 | cordinyana | protected static GeometryManager geomManager = GeometryLocator |
108 | .getGeometryManager(); |
||
109 | 20761 | jmvivo | |
110 | 36198 | cordinyana | public static final int curvesize[] = { 1, 1, 2, 3, 0 }; |
111 | 20761 | jmvivo | |
112 | 35472 | jpiera | /**
|
113 | * An even-odd winding rule for determining the interior of
|
||
114 | * a path.
|
||
115 | */
|
||
116 | public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; |
||
117 | 20761 | jmvivo | |
118 | 35472 | jpiera | /**
|
119 | * A non-zero winding rule for determining the interior of a
|
||
120 | * path.
|
||
121 | */
|
||
122 | public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; |
||
123 | 20761 | jmvivo | |
124 | 35472 | jpiera | // For code simplicity, copy these constants to our namespace
|
125 | // and cast them to byte constants for easy storage.
|
||
126 | 36198 | cordinyana | public static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO; |
127 | public static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO; |
||
128 | public static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO; |
||
129 | 35472 | jpiera | public static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO; |
130 | 36198 | cordinyana | public static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE; |
131 | 20761 | jmvivo | |
132 | 35472 | jpiera | private List pointTypes = new ArrayList(); |
133 | private List pointCoords = new ArrayList(); |
||
134 | 20761 | jmvivo | |
135 | 35472 | jpiera | int windingRule;
|
136 | 36198 | cordinyana | |
137 | 35472 | jpiera | private boolean isSimple = true; |
138 | 20761 | jmvivo | |
139 | 35472 | jpiera | static final int INIT_SIZE = 20; |
140 | static final int EXPAND_MAX = 500; |
||
141 | 20761 | jmvivo | |
142 | 35472 | jpiera | /**
|
143 | * Constructs a new <code>GeneralPathX</code> object.
|
||
144 | * If an operation performed on this path requires the
|
||
145 | * interior of the path to be defined then the default NON_ZERO
|
||
146 | * winding rule is used.
|
||
147 | 36198 | cordinyana | *
|
148 | 35472 | jpiera | * @see #WIND_NON_ZERO
|
149 | */
|
||
150 | public GeneralPathX() {
|
||
151 | this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
|
||
152 | } |
||
153 | 20761 | jmvivo | |
154 | 35472 | jpiera | /**
|
155 | * Constructs a new <code>GeneralPathX</code> object with the specified
|
||
156 | * winding rule to control operations that require the interior of the
|
||
157 | * path to be defined.
|
||
158 | 36198 | cordinyana | *
|
159 | * @param rule
|
||
160 | * the winding rule
|
||
161 | 35472 | jpiera | * @see #WIND_EVEN_ODD
|
162 | * @see #WIND_NON_ZERO
|
||
163 | */
|
||
164 | public GeneralPathX(int rule) { |
||
165 | this(rule, INIT_SIZE, INIT_SIZE);
|
||
166 | } |
||
167 | 20761 | jmvivo | |
168 | 35472 | jpiera | /**
|
169 | * Constructs a new <code>GeneralPathX</code> object with the specified
|
||
170 | * winding rule and the specified initial capacity to store path
|
||
171 | * coordinates. This number is an initial guess as to how many path
|
||
172 | * segments are in the path, but the storage is expanded
|
||
173 | * as needed to store whatever path segments are added to this path.
|
||
174 | 36198 | cordinyana | *
|
175 | * @param rule
|
||
176 | * the winding rule
|
||
177 | * @param initialCapacity
|
||
178 | * the estimate for the number of path segments
|
||
179 | * in the path
|
||
180 | 35472 | jpiera | * @see #WIND_EVEN_ODD
|
181 | * @see #WIND_NON_ZERO
|
||
182 | * @deprecated
|
||
183 | 36198 | cordinyana | * the capacity grows dynamically
|
184 | 35472 | jpiera | */
|
185 | public GeneralPathX(int rule, int initialCapacity) { |
||
186 | this(rule, initialCapacity, initialCapacity);
|
||
187 | } |
||
188 | 20761 | jmvivo | |
189 | 35472 | jpiera | /**
|
190 | * Constructs a new <code>GeneralPathX</code> object with the specified
|
||
191 | * winding rule and the specified initial capacities to store point types
|
||
192 | * and coordinates.
|
||
193 | * These numbers are an initial guess as to how many path segments
|
||
194 | * and how many points are to be in the path, but the
|
||
195 | * storage is expanded as needed to store whatever path segments are
|
||
196 | * added to this path.
|
||
197 | 36198 | cordinyana | *
|
198 | * @param rule
|
||
199 | * the winding rule
|
||
200 | * @param initialTypes
|
||
201 | * the estimate for the number of path segments
|
||
202 | * in the path
|
||
203 | * @param initialCapacity
|
||
204 | * the estimate for the number of points
|
||
205 | 35472 | jpiera | * @see #WIND_EVEN_ODD
|
206 | * @see #WIND_NON_ZERO
|
||
207 | */
|
||
208 | GeneralPathX(int rule, int initialTypes, int initialCoords) { |
||
209 | 36198 | cordinyana | setWindingRule(rule); |
210 | 35472 | jpiera | } |
211 | 31671 | jpiera | |
212 | 35472 | jpiera | /**
|
213 | * Constructs a new <code>GeneralPathX</code> object from an arbitrary
|
||
214 | * {@link Shape} object.
|
||
215 | * All of the initial geometry and the winding rule for this path are
|
||
216 | * taken from the specified <code>Shape</code> object.
|
||
217 | 36198 | cordinyana | *
|
218 | * @param s
|
||
219 | * the specified <code>Shape</code> object
|
||
220 | 35472 | jpiera | */
|
221 | 36198 | cordinyana | public GeneralPathX(PathIterator piter) { |
222 | 35472 | jpiera | this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
|
223 | setWindingRule(piter.getWindingRule()); |
||
224 | append(piter, false);
|
||
225 | } |
||
226 | 31671 | jpiera | |
227 | 35472 | jpiera | private void needRoom(int newTypes, int newCoords, boolean needMove) { |
228 | if (needMove && getNumTypes() == 0) { |
||
229 | 36198 | cordinyana | throw new IllegalPathStateException("missing initial moveto " |
230 | + "in path definition");
|
||
231 | } |
||
232 | 35472 | jpiera | } |
233 | 20761 | jmvivo | |
234 | 35325 | cordinyana | /**
|
235 | 35472 | jpiera | * Adds a point to the path by moving to the specified
|
236 | * coordinates.
|
||
237 | 36198 | cordinyana | *
|
238 | * @param x
|
||
239 | * , y the specified coordinates
|
||
240 | 35472 | jpiera | * @deprecated
|
241 | 36198 | cordinyana | * use moveTo(Point)
|
242 | 35472 | jpiera | */
|
243 | public synchronized void moveTo(double x, double y) { |
||
244 | int numtypes = getNumTypes();
|
||
245 | if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) { |
||
246 | Point point = getPointAt(getNumCoords() - 1); |
||
247 | point.setX(x); |
||
248 | point.setY(y); |
||
249 | 36198 | cordinyana | } else {
|
250 | 35472 | jpiera | needRoom(1, 2, false); |
251 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_MOVETO));
|
252 | 36198 | cordinyana | addPoint(x, y); |
253 | 35472 | jpiera | } |
254 | } |
||
255 | 36198 | cordinyana | |
256 | 35472 | jpiera | public synchronized void moveTo(Point point) { |
257 | int numtypes = getNumTypes();
|
||
258 | if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) { |
||
259 | pointCoords.remove(getNumCoords() - 1);
|
||
260 | addPoint(point); |
||
261 | 36198 | cordinyana | } else {
|
262 | 35472 | jpiera | needRoom(1, 2, false); |
263 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_MOVETO));
|
264 | 36198 | cordinyana | addPoint(point); |
265 | 35472 | jpiera | } |
266 | 36198 | cordinyana | } |
267 | 35472 | jpiera | |
268 | /**
|
||
269 | * Adds a point to the path by drawing a straight line from the
|
||
270 | * current coordinates to the new specified coordinates.
|
||
271 | 36198 | cordinyana | *
|
272 | * @param x
|
||
273 | * , y the specified coordinates
|
||
274 | 35472 | jpiera | * @deprecated
|
275 | 36198 | cordinyana | * use lineTo(Point)
|
276 | 35472 | jpiera | */
|
277 | public synchronized void lineTo(double x, double y) { |
||
278 | needRoom(1, 2, true); |
||
279 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_LINETO));
|
280 | 36198 | cordinyana | addPoint(x, y); |
281 | 35472 | jpiera | } |
282 | 36198 | cordinyana | |
283 | 35472 | jpiera | public synchronized void lineTo(Point point) { |
284 | needRoom(1, 2, true); |
||
285 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_LINETO));
|
286 | 36198 | cordinyana | addPoint(point); |
287 | 35472 | jpiera | } |
288 | |||
289 | 38067 | cordinyana | public synchronized void addSegment(Point[] segment) { |
290 | if (segment != null && segment.length > 0) { |
||
291 | needRoom(segment.length, 2 * segment.length, true); |
||
292 | for (int i = 0; i < segment.length; i++) { |
||
293 | pointTypes.add(Byte.valueOf(SEG_LINETO));
|
||
294 | addPoint(segment[i]); |
||
295 | } |
||
296 | } |
||
297 | } |
||
298 | |||
299 | 36198 | cordinyana | private void addPoint(double x, double y) { |
300 | 35472 | jpiera | try {
|
301 | 36198 | cordinyana | pointCoords.add(geomManager.createPoint(x, y, |
302 | Geometry.SUBTYPES.GEOM2D)); |
||
303 | 35472 | jpiera | } catch (CreateGeometryException e) {
|
304 | LOG.error("Error creating a point", e);
|
||
305 | 36198 | cordinyana | } |
306 | 35472 | jpiera | } |
307 | |||
308 | 36198 | cordinyana | private void addPoint(Point point) { |
309 | pointCoords.add(point); |
||
310 | 35472 | jpiera | } |
311 | |||
312 | /**
|
||
313 | * Adds a curved segment, defined by two new points, to the path by
|
||
314 | * drawing a Quadratic curve that intersects both the current
|
||
315 | * coordinates and the coordinates (x2, y2), using the
|
||
316 | * specified point (x1, y1) as a quadratic parametric control
|
||
317 | * point.
|
||
318 | 36198 | cordinyana | *
|
319 | * @param x1
|
||
320 | * , y1 the coordinates of the first quadratic control
|
||
321 | * point
|
||
322 | * @param x2
|
||
323 | * , y2 the coordinates of the final endpoint
|
||
324 | * @deprecated
|
||
325 | * use quadTo(Point, Point)
|
||
326 | 35472 | jpiera | */
|
327 | public synchronized void quadTo(double x1, double y1, double x2, double y2) { |
||
328 | needRoom(1, 4, true); |
||
329 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_QUADTO));
|
330 | 35472 | jpiera | addPoint(x1, y1); |
331 | addPoint(x2, y2); |
||
332 | isSimple = false;
|
||
333 | } |
||
334 | 36198 | cordinyana | |
335 | 35472 | jpiera | public synchronized void quadTo(Point point1, Point point2) { |
336 | needRoom(1, 4, true); |
||
337 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_QUADTO));
|
338 | 35472 | jpiera | addPoint(point1); |
339 | addPoint(point2); |
||
340 | isSimple = false;
|
||
341 | } |
||
342 | 20761 | jmvivo | |
343 | 35472 | jpiera | /**
|
344 | * Adds a curved segment, defined by three new points, to the path by
|
||
345 | * drawing a Bézier curve that intersects both the current
|
||
346 | * coordinates and the coordinates (x3, y3), using the
|
||
347 | * specified points (x1, y1) and (x2, y2) as
|
||
348 | * Bézier control points.
|
||
349 | 36198 | cordinyana | *
|
350 | * @param x1
|
||
351 | * , y1 the coordinates of the first Béezier
|
||
352 | * control point
|
||
353 | * @param x2
|
||
354 | * , y2 the coordinates of the second Bézier
|
||
355 | * control point
|
||
356 | * @param x3
|
||
357 | * , y3 the coordinates of the final endpoint
|
||
358 | 35472 | jpiera | * @deprecated
|
359 | 36198 | cordinyana | * use curveTo(Point, Point, Point)
|
360 | 35472 | jpiera | */
|
361 | 36198 | cordinyana | public synchronized void curveTo(double x1, double y1, double x2, |
362 | double y2, double x3, double y3) { |
||
363 | 35472 | jpiera | needRoom(1, 6, true); |
364 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_CUBICTO));
|
365 | 35472 | jpiera | addPoint(x1, y1); |
366 | addPoint(x2, y2); |
||
367 | 36198 | cordinyana | addPoint(x3, y3); |
368 | 35472 | jpiera | isSimple = false;
|
369 | } |
||
370 | 36198 | cordinyana | |
371 | public synchronized void curveTo(Point point1, Point point2, Point point3) { |
||
372 | 35472 | jpiera | needRoom(1, 6, true); |
373 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_CUBICTO));
|
374 | 35472 | jpiera | addPoint(point1); |
375 | addPoint(point2); |
||
376 | 36198 | cordinyana | addPoint(point3); |
377 | 35472 | jpiera | isSimple = false;
|
378 | } |
||
379 | 20761 | jmvivo | |
380 | 35472 | jpiera | /**
|
381 | * Closes the current subpath by drawing a straight line back to
|
||
382 | 36198 | cordinyana | * the coordinates of the last <code>moveTo</code>. If the path is already
|
383 | 35472 | jpiera | * closed then this method has no effect.
|
384 | */
|
||
385 | public synchronized void closePath() { |
||
386 | if (getNumTypes() == 0 || getTypeAt(getNumTypes() - 1) != SEG_CLOSE) { |
||
387 | needRoom(1, 0, true); |
||
388 | 36198 | cordinyana | // Adding a geometry like the last geometry
|
389 | // addPoint(100, 100);
|
||
390 | 36197 | cordinyana | pointTypes.add(Byte.valueOf(SEG_CLOSE));
|
391 | 35472 | jpiera | } |
392 | } |
||
393 | 20761 | jmvivo | |
394 | 35472 | jpiera | /**
|
395 | * Check if the first part is closed.
|
||
396 | 36198 | cordinyana | *
|
397 | 35472 | jpiera | * @return
|
398 | */
|
||
399 | 36198 | cordinyana | public boolean isClosed() { |
400 | PathIterator theIterator =
|
||
401 | getPathIterator(null, geomManager.getFlatness());
|
||
402 | 35472 | jpiera | double[] theData = new double[6]; |
403 | double xFinal = 0; |
||
404 | double yFinal = 0; |
||
405 | double xIni = 0; |
||
406 | double yIni = 0; |
||
407 | boolean first = true; |
||
408 | 20761 | jmvivo | |
409 | 35472 | jpiera | while (!theIterator.isDone()) {
|
410 | 36198 | cordinyana | // while not done
|
411 | 35472 | jpiera | int theType = theIterator.currentSegment(theData);
|
412 | 20761 | jmvivo | |
413 | 35472 | jpiera | switch (theType) {
|
414 | case PathIterator.SEG_MOVETO: |
||
415 | xIni = theData[0];
|
||
416 | yIni = theData[1];
|
||
417 | 36198 | cordinyana | if (!first) {
|
418 | 35472 | jpiera | break;
|
419 | } |
||
420 | first = false;
|
||
421 | break;
|
||
422 | 20761 | jmvivo | |
423 | 35472 | jpiera | case PathIterator.SEG_LINETO: |
424 | xFinal = theData[0];
|
||
425 | yFinal = theData[1];
|
||
426 | break;
|
||
427 | case PathIterator.SEG_CLOSE: |
||
428 | return true; |
||
429 | 20761 | jmvivo | |
430 | 36198 | cordinyana | } // end switch
|
431 | 20761 | jmvivo | |
432 | 35472 | jpiera | theIterator.next(); |
433 | } |
||
434 | if ((xFinal == xIni) && (yFinal == yIni))
|
||
435 | return true; |
||
436 | return false; |
||
437 | } |
||
438 | 20761 | jmvivo | |
439 | 35472 | jpiera | /**
|
440 | 36198 | cordinyana | * Appends the geometry of the specified {@link PathIterator} object
|
441 | 35472 | jpiera | * to the path, possibly connecting the new geometry to the existing
|
442 | * path segments with a line segment.
|
||
443 | * If the <code>connect</code> parameter is <code>true</code> and the
|
||
444 | * path is not empty then any initial <code>moveTo</code> in the
|
||
445 | * geometry of the appended <code>Shape</code> is turned into a
|
||
446 | * <code>lineTo</code> segment.
|
||
447 | * If the destination coordinates of such a connecting <code>lineTo</code>
|
||
448 | * segment match the ending coordinates of a currently open
|
||
449 | * subpath then the segment is omitted as superfluous.
|
||
450 | * The winding rule of the specified <code>Shape</code> is ignored
|
||
451 | * and the appended geometry is governed by the winding
|
||
452 | * rule specified for this path.
|
||
453 | 36198 | cordinyana | *
|
454 | * @param pi
|
||
455 | * the <code>PathIterator</code> whose geometry is appended to
|
||
456 | * this path
|
||
457 | * @param connect
|
||
458 | * a boolean to control whether or not to turn an
|
||
459 | * initial <code>moveTo</code> segment into a <code>lineTo</code>
|
||
460 | * segment
|
||
461 | * to connect the new geometry to the existing path
|
||
462 | 35472 | jpiera | */
|
463 | public void append(PathIterator pi, boolean connect) { |
||
464 | double coords[] = new double[6]; |
||
465 | while (!pi.isDone()) {
|
||
466 | switch (pi.currentSegment(coords)) {
|
||
467 | case SEG_MOVETO:
|
||
468 | if (!connect || getNumTypes() < 1 || getNumCoords() < 2) { |
||
469 | moveTo(coords[0], coords[1]); |
||
470 | break;
|
||
471 | } |
||
472 | 36198 | cordinyana | if (getTypeAt(getNumTypes() - 1) != SEG_CLOSE |
473 | && getPointAt(getNumCoords() - 1).getX() == coords[0] |
||
474 | && getPointAt(getNumCoords() - 1).getY() == coords[1]) { |
||
475 | 35472 | jpiera | // Collapse out initial moveto/lineto
|
476 | break;
|
||
477 | } |
||
478 | // NO BREAK;
|
||
479 | case SEG_LINETO:
|
||
480 | lineTo(coords[0], coords[1]); |
||
481 | break;
|
||
482 | case SEG_QUADTO:
|
||
483 | 36198 | cordinyana | quadTo(coords[0], coords[1], coords[2], coords[3]); |
484 | 35472 | jpiera | break;
|
485 | case SEG_CUBICTO:
|
||
486 | 36198 | cordinyana | curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], |
487 | coords[5]);
|
||
488 | 35472 | jpiera | break;
|
489 | case SEG_CLOSE:
|
||
490 | closePath(); |
||
491 | break;
|
||
492 | } |
||
493 | pi.next(); |
||
494 | connect = false;
|
||
495 | } |
||
496 | } |
||
497 | 20761 | jmvivo | |
498 | 35472 | jpiera | /**
|
499 | * Returns the fill style winding rule.
|
||
500 | 36198 | cordinyana | *
|
501 | 35472 | jpiera | * @return an integer representing the current winding rule.
|
502 | * @see #WIND_EVEN_ODD
|
||
503 | * @see #WIND_NON_ZERO
|
||
504 | * @see #setWindingRule
|
||
505 | */
|
||
506 | public synchronized int getWindingRule() { |
||
507 | return windingRule;
|
||
508 | } |
||
509 | 20761 | jmvivo | |
510 | 35472 | jpiera | /**
|
511 | * Sets the winding rule for this path to the specified value.
|
||
512 | 36198 | cordinyana | *
|
513 | * @param rule
|
||
514 | * an integer representing the specified
|
||
515 | * winding rule
|
||
516 | * @exception <code>IllegalArgumentException</code> if <code>rule</code> is
|
||
517 | * not either <code>WIND_EVEN_ODD</code> or
|
||
518 | * <code>WIND_NON_ZERO</code>
|
||
519 | 35472 | jpiera | * @see #WIND_EVEN_ODD
|
520 | * @see #WIND_NON_ZERO
|
||
521 | * @see #getWindingRule
|
||
522 | */
|
||
523 | public void setWindingRule(int rule) { |
||
524 | if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
|
||
525 | 36198 | cordinyana | throw new IllegalArgumentException("winding rule must be " |
526 | + "WIND_EVEN_ODD or " + "WIND_NON_ZERO"); |
||
527 | 35472 | jpiera | } |
528 | windingRule = rule; |
||
529 | } |
||
530 | 20761 | jmvivo | |
531 | 35472 | jpiera | /**
|
532 | * Returns the coordinates most recently added to the end of the path
|
||
533 | * as a {@link Point2D} object.
|
||
534 | 36198 | cordinyana | *
|
535 | 35472 | jpiera | * @return a <code>Point2D</code> object containing the ending
|
536 | 36198 | cordinyana | * coordinates of the path or <code>null</code> if there are no
|
537 | * points
|
||
538 | * in the path.
|
||
539 | 35472 | jpiera | */
|
540 | public synchronized Point2D getCurrentPoint() { |
||
541 | 36775 | jpiera | if (getNumTypes() < 1 || getNumCoords() < 1) { |
542 | 35472 | jpiera | return null; |
543 | } |
||
544 | int index = getNumCoords();
|
||
545 | if (getTypeAt(getNumTypes() - 1) == SEG_CLOSE) { |
||
546 | 36198 | cordinyana | loop: for (int i = getNumTypes() - 2; i > 0; i--) { |
547 | switch (getTypeAt(i)) {
|
||
548 | case SEG_MOVETO:
|
||
549 | break loop;
|
||
550 | case SEG_LINETO:
|
||
551 | index -= 2;
|
||
552 | break;
|
||
553 | case SEG_QUADTO:
|
||
554 | index -= 4;
|
||
555 | break;
|
||
556 | case SEG_CUBICTO:
|
||
557 | index -= 6;
|
||
558 | break;
|
||
559 | case SEG_CLOSE:
|
||
560 | break;
|
||
561 | 35472 | jpiera | } |
562 | 36198 | cordinyana | } |
563 | 35472 | jpiera | } |
564 | 36198 | cordinyana | return new Point2D.Double(getPointAt(index - 1).getX(), getPointAt( |
565 | index - 1).getY());
|
||
566 | 35472 | jpiera | } |
567 | 20761 | jmvivo | |
568 | 35472 | jpiera | /**
|
569 | 36198 | cordinyana | * Resets the path to empty. The append position is set back to the
|
570 | 35472 | jpiera | * beginning of the path and all coordinates and point types are
|
571 | * forgotten.
|
||
572 | */
|
||
573 | public synchronized void reset() { |
||
574 | pointCoords.clear(); |
||
575 | pointTypes.clear(); |
||
576 | } |
||
577 | 20761 | jmvivo | |
578 | 35472 | jpiera | /**
|
579 | * Transforms the geometry of this path using the specified
|
||
580 | * {@link AffineTransform}.
|
||
581 | * The geometry is transformed in place, which permanently changes the
|
||
582 | * boundary defined by this object.
|
||
583 | 36198 | cordinyana | *
|
584 | * @param at
|
||
585 | * the <code>AffineTransform</code> used to transform the area
|
||
586 | 35472 | jpiera | */
|
587 | public void transform(AffineTransform at) { |
||
588 | 36198 | cordinyana | for (int i = 0; i < getNumCoords(); i++) { |
589 | 38067 | cordinyana | getPointAt(i).transform(at); |
590 | 35472 | jpiera | } |
591 | 20761 | jmvivo | } |
592 | |||
593 | 36198 | cordinyana | public void reProject(ICoordTrans ct) { |
594 | for (int i = 0; i < getNumCoords(); i++) { |
||
595 | 38067 | cordinyana | getPointAt(i).reProject(ct); |
596 | 35472 | jpiera | } |
597 | } |
||
598 | 20761 | jmvivo | |
599 | 35472 | jpiera | /**
|
600 | * Returns a new transformed <code>Shape</code>.
|
||
601 | 36198 | cordinyana | *
|
602 | * @param at
|
||
603 | * the <code>AffineTransform</code> used to transform a
|
||
604 | * new <code>Shape</code>.
|
||
605 | 35472 | jpiera | * @return a new <code>Shape</code>, transformed with the specified
|
606 | 36198 | cordinyana | * <code>AffineTransform</code>.
|
607 | 35472 | jpiera | */
|
608 | public synchronized Shape createTransformedShape(AffineTransform at) { |
||
609 | GeneralPathX gp = (GeneralPathX) clone(); |
||
610 | if (at != null) { |
||
611 | gp.transform(at); |
||
612 | } |
||
613 | return gp;
|
||
614 | } |
||
615 | 31671 | jpiera | |
616 | 35472 | jpiera | /**
|
617 | * Return the bounding box of the path.
|
||
618 | 36198 | cordinyana | *
|
619 | 35472 | jpiera | * @return a {@link java.awt.Rectangle} object that
|
620 | 36198 | cordinyana | * bounds the current path.
|
621 | 35472 | jpiera | */
|
622 | public java.awt.Rectangle getBounds() {
|
||
623 | return getBounds2D().getBounds();
|
||
624 | } |
||
625 | 20761 | jmvivo | |
626 | 35472 | jpiera | /**
|
627 | * Returns the bounding box of the path.
|
||
628 | 36198 | cordinyana | *
|
629 | 35472 | jpiera | * @return a {@link Rectangle2D} object that
|
630 | 36198 | cordinyana | * bounds the current path.
|
631 | 35472 | jpiera | */
|
632 | public synchronized Rectangle2D getBounds2D() { |
||
633 | double x1, y1, x2, y2;
|
||
634 | int i = getNumCoords();
|
||
635 | if (i > 0) { |
||
636 | y1 = y2 = getPointAt(--i).getY(); |
||
637 | x1 = x2 = getPointAt(i).getX(); |
||
638 | while (i > 0) { |
||
639 | double y = getPointAt(--i).getY();
|
||
640 | double x = getPointAt(i).getX();
|
||
641 | 36198 | cordinyana | if (x < x1)
|
642 | x1 = x; |
||
643 | if (y < y1)
|
||
644 | y1 = y; |
||
645 | if (x > x2)
|
||
646 | x2 = x; |
||
647 | if (y > y2)
|
||
648 | y2 = y; |
||
649 | 35472 | jpiera | } |
650 | } else {
|
||
651 | x1 = y1 = x2 = y2 = 0.0f;
|
||
652 | } |
||
653 | return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1); |
||
654 | } |
||
655 | 20761 | jmvivo | |
656 | 35472 | jpiera | /**
|
657 | * Tests if the specified coordinates are inside the boundary of
|
||
658 | * this <code>Shape</code>.
|
||
659 | 36198 | cordinyana | *
|
660 | * @param x
|
||
661 | * , y the specified coordinates
|
||
662 | 35472 | jpiera | * @return <code>true</code> if the specified coordinates are inside this
|
663 | 36198 | cordinyana | * <code>Shape</code>; <code>false</code> otherwise
|
664 | 35472 | jpiera | */
|
665 | public boolean contains(double x, double y) { |
||
666 | if (pointTypes.size() < 2) { |
||
667 | return false; |
||
668 | } |
||
669 | 36198 | cordinyana | int cross =
|
670 | GeomUtilities.pointCrossingsForPath(getPathIterator(null), x, y);
|
||
671 | 35472 | jpiera | if (windingRule == WIND_NON_ZERO) {
|
672 | return (cross != 0); |
||
673 | } else {
|
||
674 | return ((cross & 1) != 0); |
||
675 | } |
||
676 | 36198 | cordinyana | } |
677 | 20761 | jmvivo | |
678 | 35472 | jpiera | /**
|
679 | * Tests if the specified <code>Point2D</code> is inside the boundary
|
||
680 | * of this <code>Shape</code>.
|
||
681 | 36198 | cordinyana | *
|
682 | * @param p
|
||
683 | * the specified <code>Point2D</code>
|
||
684 | 35472 | jpiera | * @return <code>true</code> if this <code>Shape</code> contains the
|
685 | 36198 | cordinyana | * specified <code>Point2D</code>, <code>false</code> otherwise.
|
686 | 35472 | jpiera | */
|
687 | public boolean contains(Point2D p) { |
||
688 | return contains(p.getX(), p.getY());
|
||
689 | } |
||
690 | 20761 | jmvivo | |
691 | 35472 | jpiera | /**
|
692 | * Tests if the specified rectangular area is inside the boundary of
|
||
693 | * this <code>Shape</code>.
|
||
694 | 36198 | cordinyana | *
|
695 | * @param x
|
||
696 | * , y the specified coordinates
|
||
697 | * @param w
|
||
698 | * the width of the specified rectangular area
|
||
699 | * @param h
|
||
700 | * the height of the specified rectangular area
|
||
701 | 35472 | jpiera | * @return <code>true</code> if this <code>Shape</code> contains
|
702 | 36198 | cordinyana | * the specified rectangluar area; <code>false</code> otherwise.
|
703 | 35472 | jpiera | */
|
704 | public boolean contains(double x, double y, double w, double h) { |
||
705 | 36198 | cordinyana | return GeomUtilities
|
706 | .contains(getPathIterator(null), x, y, x + w, y + h);
|
||
707 | 35472 | jpiera | } |
708 | 20761 | jmvivo | |
709 | 35472 | jpiera | /**
|
710 | 36198 | cordinyana | * Tests if the specified <code>Rectangle2D</code> is inside the boundary of
|
711 | * this <code>Shape</code>.
|
||
712 | *
|
||
713 | * @param r
|
||
714 | * a specified <code>Rectangle2D</code>
|
||
715 | 35472 | jpiera | * @return <code>true</code> if this <code>Shape</code> bounds the
|
716 | 36198 | cordinyana | * specified <code>Rectangle2D</code>; <code>false</code> otherwise.
|
717 | 35472 | jpiera | */
|
718 | public boolean contains(Rectangle2D r) { |
||
719 | return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||
720 | } |
||
721 | 20761 | jmvivo | |
722 | 35472 | jpiera | /**
|
723 | * Tests if the interior of this <code>Shape</code> intersects the
|
||
724 | * interior of a specified set of rectangular coordinates.
|
||
725 | 36198 | cordinyana | *
|
726 | * @param x
|
||
727 | * , y the specified coordinates
|
||
728 | * @param w
|
||
729 | * the width of the specified rectangular coordinates
|
||
730 | * @param h
|
||
731 | * the height of the specified rectangular coordinates
|
||
732 | 35472 | jpiera | * @return <code>true</code> if this <code>Shape</code> and the
|
733 | 36198 | cordinyana | * interior of the specified set of rectangular coordinates
|
734 | * intersect
|
||
735 | * each other; <code>false</code> otherwise.
|
||
736 | 35472 | jpiera | */
|
737 | public boolean intersects(double x, double y, double w, double h) { |
||
738 | 36198 | cordinyana | return GeomUtilities.intersects(getPathIterator(null), x, y, w, h); |
739 | 35472 | jpiera | } |
740 | 20761 | jmvivo | |
741 | 35472 | jpiera | /**
|
742 | * Tests if the interior of this <code>Shape</code> intersects the
|
||
743 | * interior of a specified <code>Rectangle2D</code>.
|
||
744 | 36198 | cordinyana | *
|
745 | * @param r
|
||
746 | * the specified <code>Rectangle2D</code>
|
||
747 | 35472 | jpiera | * @return <code>true</code> if this <code>Shape</code> and the interior
|
748 | 36198 | cordinyana | * of the specified <code>Rectangle2D</code> intersect each
|
749 | * other; <code>false</code> otherwise.
|
||
750 | 35472 | jpiera | */
|
751 | public boolean intersects(Rectangle2D r) { |
||
752 | return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||
753 | } |
||
754 | 20761 | jmvivo | |
755 | 35472 | jpiera | /**
|
756 | * Returns a <code>PathIterator</code> object that iterates along the
|
||
757 | * boundary of this <code>Shape</code> and provides access to the
|
||
758 | * geometry of the outline of this <code>Shape</code>.
|
||
759 | * The iterator for this class is not multi-threaded safe,
|
||
760 | * which means that this <code>GeneralPathX</code> class does not
|
||
761 | * guarantee that modifications to the geometry of this
|
||
762 | * <code>GeneralPathX</code> object do not affect any iterations of
|
||
763 | * that geometry that are already in process.
|
||
764 | 36198 | cordinyana | *
|
765 | * @param at
|
||
766 | * an <code>AffineTransform</code>
|
||
767 | 35472 | jpiera | * @return a new <code>PathIterator</code> that iterates along the
|
768 | 36198 | cordinyana | * boundary of this <code>Shape</code> and provides access to the
|
769 | * geometry of this <code>Shape</code>'s outline
|
||
770 | 35472 | jpiera | */
|
771 | public PathIterator getPathIterator(AffineTransform at) { |
||
772 | 36198 | cordinyana | if (isSimple) {
|
773 | 35472 | jpiera | return new GeneralPathXIteratorSimple(this, at); |
774 | 36198 | cordinyana | } else {
|
775 | 35472 | jpiera | return new GeneralPathXIterator(this, at); |
776 | } |
||
777 | } |
||
778 | 20761 | jmvivo | |
779 | 35472 | jpiera | /**
|
780 | * Returns a <code>PathIterator</code> object that iterates along the
|
||
781 | * boundary of the flattened <code>Shape</code> and provides access to the
|
||
782 | * geometry of the outline of the <code>Shape</code>.
|
||
783 | * The iterator for this class is not multi-threaded safe,
|
||
784 | * which means that this <code>GeneralPathX</code> class does not
|
||
785 | * guarantee that modifications to the geometry of this
|
||
786 | * <code>GeneralPathX</code> object do not affect any iterations of
|
||
787 | * that geometry that are already in process.
|
||
788 | 36198 | cordinyana | *
|
789 | * @param at
|
||
790 | * an <code>AffineTransform</code>
|
||
791 | * @param flatness
|
||
792 | * the maximum distance that the line segments used to
|
||
793 | * approximate the curved segments are allowed to deviate
|
||
794 | * from any point on the original curve
|
||
795 | 35472 | jpiera | * @return a new <code>PathIterator</code> that iterates along the flattened
|
796 | 36198 | cordinyana | * <code>Shape</code> boundary.
|
797 | 35472 | jpiera | */
|
798 | public PathIterator getPathIterator(AffineTransform at, double flatness) { |
||
799 | return new FlatteningPathIterator(getPathIterator(at), flatness); |
||
800 | } |
||
801 | 20761 | jmvivo | |
802 | 35472 | jpiera | /**
|
803 | * Creates a new object of the same class as this object.
|
||
804 | 36198 | cordinyana | *
|
805 | * @return a clone of this instance.
|
||
806 | * @exception OutOfMemoryError
|
||
807 | * if there is not enough memory.
|
||
808 | * @see java.lang.Cloneable
|
||
809 | * @since 1.2
|
||
810 | 35472 | jpiera | */
|
811 | 36198 | cordinyana | public Object clone() { |
812 | GeneralPathX copy = new GeneralPathX();
|
||
813 | 35472 | jpiera | copy.windingRule = windingRule; |
814 | 35475 | jpiera | copy.isSimple = isSimple; |
815 | 36198 | cordinyana | for (int i = 0; i < getNumTypes(); i++) { |
816 | 35472 | jpiera | copy.pointTypes.add(pointTypes.get(i)); |
817 | 36198 | cordinyana | } |
818 | for (int i = 0; i < getNumCoords(); i++) { |
||
819 | copy.addPoint((Point) getPointAt(i).cloneGeometry());
|
||
820 | } |
||
821 | 35472 | jpiera | return copy;
|
822 | 20761 | jmvivo | |
823 | 35472 | jpiera | } |
824 | 20761 | jmvivo | |
825 | 36198 | cordinyana | GeneralPathX(int windingRule, byte[] pointTypes, int numTypes, |
826 | double[] pointCoords, int numCoords) { |
||
827 | 20761 | jmvivo | |
828 | 35472 | jpiera | // used to construct from native
|
829 | 20761 | jmvivo | |
830 | 35472 | jpiera | this.windingRule = windingRule;
|
831 | this.setPointTypes(pointTypes);
|
||
832 | this.setNumTypes(numTypes);
|
||
833 | this.setPointCoords(pointCoords);
|
||
834 | this.setNumCoords(numCoords);
|
||
835 | } |
||
836 | 20761 | jmvivo | |
837 | 35472 | jpiera | public void setNumTypes(int numTypes) { |
838 | 20761 | jmvivo | |
839 | 35472 | jpiera | } |
840 | 20761 | jmvivo | |
841 | 35472 | jpiera | public int getNumTypes() { |
842 | return pointTypes.size();
|
||
843 | } |
||
844 | 20761 | jmvivo | |
845 | 35472 | jpiera | public int setNumCoords(int numCoords) { |
846 | return pointCoords.size();
|
||
847 | } |
||
848 | 20761 | jmvivo | |
849 | 35472 | jpiera | public int getNumCoords() { |
850 | return pointCoords.size();
|
||
851 | } |
||
852 | 31671 | jpiera | |
853 | 36198 | cordinyana | public byte getTypeAt(int index) { |
854 | 36197 | cordinyana | return ((Byte) pointTypes.get(index)).byteValue(); |
855 | 35472 | jpiera | } |
856 | 31671 | jpiera | |
857 | 36198 | cordinyana | /**
|
858 | 35472 | jpiera | * @deprecated
|
859 | 36198 | cordinyana | * use the geometry methods.
|
860 | 35472 | jpiera | */
|
861 | public void setPointTypes(byte[] pointTypes) { |
||
862 | 36198 | cordinyana | this.pointTypes.clear();
|
863 | for (int i = 0; i < pointTypes.length; i++) { |
||
864 | 36197 | cordinyana | this.pointTypes.add(Byte.valueOf(pointTypes[i])); |
865 | 36198 | cordinyana | } |
866 | 35472 | jpiera | } |
867 | 31671 | jpiera | |
868 | 36198 | cordinyana | /**
|
869 | 35472 | jpiera | * @deprecated
|
870 | 36198 | cordinyana | * use the geometry methods.
|
871 | 35472 | jpiera | */
|
872 | public byte[] getPointTypes() { |
||
873 | byte[] bytes = new byte[pointTypes.size()]; |
||
874 | 36198 | cordinyana | for (int i = 0; i < pointTypes.size(); i++) { |
875 | 36197 | cordinyana | bytes[i] = ((Byte) pointTypes.get(i)).byteValue();
|
876 | 36198 | cordinyana | } |
877 | 35472 | jpiera | return bytes;
|
878 | } |
||
879 | 20761 | jmvivo | |
880 | 36198 | cordinyana | /**
|
881 | 35472 | jpiera | * @param pointCoords
|
882 | * @deprecated
|
||
883 | 36198 | cordinyana | * use the geometry methods.
|
884 | 35472 | jpiera | */
|
885 | 36198 | cordinyana | public void setPointCoords(double[] pointCoords) { |
886 | this.pointCoords.clear();
|
||
887 | for (int i = 0; i < pointCoords.length; i = i + 2) { |
||
888 | 35472 | jpiera | try {
|
889 | 36198 | cordinyana | addPoint(geomManager.createPoint(pointCoords[i], |
890 | pointCoords[i + 1], Geometry.SUBTYPES.GEOM2D));
|
||
891 | 35472 | jpiera | } catch (CreateGeometryException e) {
|
892 | LOG.error("Error creating a point", e);
|
||
893 | } |
||
894 | 36198 | cordinyana | } |
895 | 35472 | jpiera | } |
896 | 20761 | jmvivo | |
897 | 36198 | cordinyana | /**
|
898 | 35472 | jpiera | * @deprecated
|
899 | 36198 | cordinyana | * use the geometry methods.
|
900 | 35472 | jpiera | */
|
901 | public double[] getPointCoords() { |
||
902 | 36198 | cordinyana | double[] doubles = new double[pointCoords.size() * 2]; |
903 | for (int i = 0; i < getNumCoords(); i++) { |
||
904 | doubles[i * 2] = getPointAt(i).getX();
|
||
905 | doubles[(i * 2) + 1] = getPointAt(i).getY(); |
||
906 | 35472 | jpiera | } |
907 | return doubles;
|
||
908 | } |
||
909 | 20761 | jmvivo | |
910 | 36198 | cordinyana | public Point getPointAt(int index) { |
911 | return (Point) pointCoords.get(index); |
||
912 | 35472 | jpiera | } |
913 | 20761 | jmvivo | |
914 | 36198 | cordinyana | public double[] getCoordinatesAt(int index) { |
915 | 35472 | jpiera | return getPointAt(index).getCoordinates();
|
916 | } |
||
917 | 20761 | jmvivo | |
918 | 35472 | jpiera | /**
|
919 | * Convertimos el path a puntos y luego le damos la vuelta.
|
||
920 | */
|
||
921 | 36198 | cordinyana | public void flip() { |
922 | PathIterator theIterator =
|
||
923 | getPathIterator(null, geomManager.getFlatness());
|
||
924 | 35472 | jpiera | double[] theData = new double[6]; |
925 | CoordinateList coordList = new CoordinateList();
|
||
926 | Coordinate c1; |
||
927 | GeneralPathX newGp = new GeneralPathX();
|
||
928 | ArrayList listOfParts = new ArrayList(); |
||
929 | while (!theIterator.isDone()) {
|
||
930 | 36198 | cordinyana | // while not done
|
931 | 35472 | jpiera | int type = theIterator.currentSegment(theData);
|
932 | 36198 | cordinyana | switch (type) {
|
933 | 35472 | jpiera | case SEG_MOVETO:
|
934 | coordList = new CoordinateList();
|
||
935 | listOfParts.add(coordList); |
||
936 | 36198 | cordinyana | c1 = new Coordinate(theData[0], theData[1]); |
937 | 35472 | jpiera | coordList.add(c1, true);
|
938 | break;
|
||
939 | case SEG_LINETO:
|
||
940 | 36198 | cordinyana | c1 = new Coordinate(theData[0], theData[1]); |
941 | 35472 | jpiera | coordList.add(c1, true);
|
942 | break;
|
||
943 | 20761 | jmvivo | |
944 | 35472 | jpiera | case SEG_CLOSE:
|
945 | coordList.add(coordList.getCoordinate(0));
|
||
946 | break;
|
||
947 | 20761 | jmvivo | |
948 | 35472 | jpiera | } |
949 | theIterator.next(); |
||
950 | } |
||
951 | 20761 | jmvivo | |
952 | 36198 | cordinyana | for (int i = listOfParts.size() - 1; i >= 0; i--) { |
953 | 35472 | jpiera | coordList = (CoordinateList) listOfParts.get(i); |
954 | Coordinate[] coords = coordList.toCoordinateArray();
|
||
955 | CoordinateArraySequence seq = new CoordinateArraySequence(coords);
|
||
956 | CoordinateSequences.reverse(seq); |
||
957 | coords = seq.toCoordinateArray(); |
||
958 | newGp.moveTo(coords[0].x, coords[0].y); |
||
959 | 36198 | cordinyana | for (int j = 1; j < coords.length; j++) { |
960 | 35472 | jpiera | newGp.lineTo(coords[j].x, coords[j].y); |
961 | } |
||
962 | } |
||
963 | reset(); |
||
964 | append(newGp.getPathIterator(null), false); |
||
965 | } |
||
966 | 36198 | cordinyana | |
967 | 35472 | jpiera | /**
|
968 | * Check if the first part is CCW.
|
||
969 | 36198 | cordinyana | *
|
970 | 35472 | jpiera | * @return
|
971 | */
|
||
972 | 36198 | cordinyana | public boolean isCCW() { |
973 | PathIterator theIterator =
|
||
974 | getPathIterator(null, geomManager.getFlatness()); // polyLine.getPathIterator(null, |
||
975 | // flatness);
|
||
976 | 35472 | jpiera | double[] theData = new double[6]; |
977 | Coordinate first = null;
|
||
978 | CoordinateList coordList = new CoordinateList();
|
||
979 | Coordinate c1; |
||
980 | boolean bFirst = true; |
||
981 | while (!theIterator.isDone()) {
|
||
982 | 36198 | cordinyana | // while not done
|
983 | 35472 | jpiera | int type = theIterator.currentSegment(theData);
|
984 | 36198 | cordinyana | switch (type) {
|
985 | 35472 | jpiera | case SEG_MOVETO:
|
986 | 36198 | cordinyana | c1 = new Coordinate(theData[0], theData[1]); |
987 | 35472 | jpiera | if (bFirst == false) // Ya tenemos la primera parte. |
988 | break;
|
||
989 | 36198 | cordinyana | if (bFirst) {
|
990 | bFirst = false;
|
||
991 | 35472 | jpiera | first = c1; |
992 | } |
||
993 | coordList.add(c1, true);
|
||
994 | break;
|
||
995 | case SEG_LINETO:
|
||
996 | 36198 | cordinyana | c1 = new Coordinate(theData[0], theData[1]); |
997 | 35472 | jpiera | coordList.add(c1, true);
|
998 | break;
|
||
999 | 22962 | vcaballero | |
1000 | 35472 | jpiera | } |
1001 | theIterator.next(); |
||
1002 | } |
||
1003 | coordList.add(first, true);
|
||
1004 | return CGAlgorithms.isCCW(coordList.toCoordinateArray());
|
||
1005 | } |
||
1006 | 36198 | cordinyana | |
1007 | 35472 | jpiera | /**
|
1008 | * @return the isSimple
|
||
1009 | */
|
||
1010 | public boolean isSimple() { |
||
1011 | return isSimple;
|
||
1012 | } |
||
1013 | 20761 | jmvivo | } |