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