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