Statistics
| Revision:

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&eacute;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
     *            ,&nbsp;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
     *            ,&nbsp;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,&nbsp;y2), using the
316
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
317
     * point.
318 36198 cordinyana
     *
319
     * @param x1
320
     *            ,&nbsp;y1 the coordinates of the first quadratic control
321
     *            point
322
     * @param x2
323
     *            ,&nbsp;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&eacute;zier curve that intersects both the current
346
     * coordinates and the coordinates (x3,&nbsp;y3), using the
347
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
348
     * B&eacute;zier control points.
349 36198 cordinyana
     *
350
     * @param x1
351
     *            ,&nbsp;y1 the coordinates of the first B&eacute;ezier
352
     *            control point
353
     * @param x2
354
     *            ,&nbsp;y2 the coordinates of the second B&eacute;zier
355
     *            control point
356
     * @param x3
357
     *            ,&nbsp;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
     *            ,&nbsp;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
     *            ,&nbsp;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
     *            ,&nbsp;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
}