Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_geometries / src / org / gvsig / fmap / geom / primitive / GeneralPathX.java @ 36199

History | View | Annotate | Download (33.3 KB)

1
/* 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
 * @author FJP
45
 *
46
 */
47
/*
48
 * @(#)GeneralPathX.java        1.58 03/01/23
49
 *
50
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
51
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
52
 */
53

    
54
import java.awt.Shape;
55
import java.awt.geom.AffineTransform;
56
import java.awt.geom.FlatteningPathIterator;
57
import java.awt.geom.IllegalPathStateException;
58
import java.awt.geom.PathIterator;
59
import java.awt.geom.Point2D;
60
import java.awt.geom.Rectangle2D;
61
import java.io.Serializable;
62
import java.util.ArrayList;
63
import java.util.List;
64

    
65
import com.vividsolutions.jts.algorithm.CGAlgorithms;
66
import com.vividsolutions.jts.geom.Coordinate;
67
import com.vividsolutions.jts.geom.CoordinateList;
68
import com.vividsolutions.jts.geom.CoordinateSequences;
69
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
70

    
71
import org.cresques.cts.ICoordTrans;
72
import org.slf4j.Logger;
73
import org.slf4j.LoggerFactory;
74

    
75
import org.gvsig.fmap.geom.Geometry;
76
import org.gvsig.fmap.geom.GeometryLocator;
77
import org.gvsig.fmap.geom.GeometryManager;
78
import org.gvsig.fmap.geom.exception.CreateGeometryException;
79
import org.gvsig.jdk.GeomUtilities;
80

    
81
/**
82
 * The <code>GeneralPathX</code> class represents a geometric path
83
 * constructed from straight lines, and quadratic and cubic
84
 * (B&eacute;zier) curves. It can contain multiple subpaths.
85
 * <p>
86
 * The winding rule specifies how the interior of a path is determined. There
87
 * are two types of winding rules: EVEN_ODD and NON_ZERO.
88
 * <p>
89
 * An EVEN_ODD winding rule means that enclosed regions of the path alternate
90
 * between interior and exterior areas as traversed from the outside of the path
91
 * towards a point inside the region.
92
 * <p>
93
 * A NON_ZERO winding rule means that if a ray is drawn in any direction from a
94
 * given point to infinity and the places where the path intersects the ray are
95
 * examined, the point is inside of the path if and only if the number of times
96
 * that the path crosses the ray from left to right does not equal the number of
97
 * times that the path crosses the ray from right to left.
98
 * 
99
 * @version 1.58, 01/23/03
100
 * @author Jim Graham
101
 * @deprecated
102
 *             use the geometry methods
103
 */
104
public class GeneralPathX implements Shape, Cloneable, Serializable {
105

    
106
    /**
107
     * Default serial version ID
108
     */
109
    private static final long serialVersionUID = 1L;
110

    
111
    private static final Logger LOG = LoggerFactory
112
        .getLogger(GeneralPathX.class);
113

    
114
    protected static GeometryManager geomManager = GeometryLocator
115
        .getGeometryManager();
116

    
117
    public static final int curvesize[] = { 1, 1, 2, 3, 0 };
118

    
119
    /**
120
     * An even-odd winding rule for determining the interior of
121
     * a path.
122
     */
123
    public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
124

    
125
    /**
126
     * A non-zero winding rule for determining the interior of a
127
     * path.
128
     */
129
    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
130

    
131
    // For code simplicity, copy these constants to our namespace
132
    // and cast them to byte constants for easy storage.
133
    public static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
134
    public static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
135
    public static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
136
    public static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
137
    public static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;
138

    
139
    private List pointTypes = new ArrayList();
140
    private List pointCoords = new ArrayList();
141

    
142
    int windingRule;
143

    
144
    private boolean isSimple = true;
145

    
146
    static final int INIT_SIZE = 20;
147
    static final int EXPAND_MAX = 500;
148

    
149
    /**
150
     * Constructs a new <code>GeneralPathX</code> object.
151
     * If an operation performed on this path requires the
152
     * interior of the path to be defined then the default NON_ZERO
153
     * winding rule is used.
154
     * 
155
     * @see #WIND_NON_ZERO
156
     */
157
    public GeneralPathX() {
158
        this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
159
    }
160

    
161
    /**
162
     * Constructs a new <code>GeneralPathX</code> object with the specified
163
     * winding rule to control operations that require the interior of the
164
     * path to be defined.
165
     * 
166
     * @param rule
167
     *            the winding rule
168
     * @see #WIND_EVEN_ODD
169
     * @see #WIND_NON_ZERO
170
     */
171
    public GeneralPathX(int rule) {
172
        this(rule, INIT_SIZE, INIT_SIZE);
173
    }
174

    
175
    /**
176
     * Constructs a new <code>GeneralPathX</code> object with the specified
177
     * winding rule and the specified initial capacity to store path
178
     * coordinates. This number is an initial guess as to how many path
179
     * segments are in the path, but the storage is expanded
180
     * as needed to store whatever path segments are added to this path.
181
     * 
182
     * @param rule
183
     *            the winding rule
184
     * @param initialCapacity
185
     *            the estimate for the number of path segments
186
     *            in the path
187
     * @see #WIND_EVEN_ODD
188
     * @see #WIND_NON_ZERO
189
     * @deprecated
190
     *             the capacity grows dynamically
191
     */
192
    public GeneralPathX(int rule, int initialCapacity) {
193
        this(rule, initialCapacity, initialCapacity);
194
    }
195

    
196
    /**
197
     * Constructs a new <code>GeneralPathX</code> object with the specified
198
     * winding rule and the specified initial capacities to store point types
199
     * and coordinates.
200
     * These numbers are an initial guess as to how many path segments
201
     * and how many points are to be in the path, but the
202
     * storage is expanded as needed to store whatever path segments are
203
     * added to this path.
204
     * 
205
     * @param rule
206
     *            the winding rule
207
     * @param initialTypes
208
     *            the estimate for the number of path segments
209
     *            in the path
210
     * @param initialCapacity
211
     *            the estimate for the number of points
212
     * @see #WIND_EVEN_ODD
213
     * @see #WIND_NON_ZERO
214
     */
215
    GeneralPathX(int rule, int initialTypes, int initialCoords) {
216
        setWindingRule(rule);
217
    }
218

    
219
    /**
220
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
221
     * {@link Shape} object.
222
     * All of the initial geometry and the winding rule for this path are
223
     * taken from the specified <code>Shape</code> object.
224
     * 
225
     * @param s
226
     *            the specified <code>Shape</code> object
227
     */
228
    public GeneralPathX(PathIterator piter) {
229
        this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
230
        setWindingRule(piter.getWindingRule());
231
        append(piter, false);
232
    }
233

    
234
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
235
        if (needMove && getNumTypes() == 0) {
236
            throw new IllegalPathStateException("missing initial moveto "
237
                + "in path definition");
238
        }
239
    }
240

    
241
    /**
242
     * Adds a point to the path by moving to the specified
243
     * coordinates.
244
     * 
245
     * @param x
246
     *            ,&nbsp;y the specified coordinates
247
     * @deprecated
248
     *             use moveTo(Point)
249
     */
250
    public synchronized void moveTo(double x, double y) {
251
        int numtypes = getNumTypes();
252
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
253
            Point point = getPointAt(getNumCoords() - 1);
254
            point.setX(x);
255
            point.setY(y);
256
        } else {
257
            needRoom(1, 2, false);
258
            pointTypes.add(Byte.valueOf(SEG_MOVETO));
259
            addPoint(x, y);
260
        }
261
    }
262

    
263
    public synchronized void moveTo(Point point) {
264
        int numtypes = getNumTypes();
265
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
266
            pointCoords.remove(getNumCoords() - 1);
267
            addPoint(point);
268
        } else {
269
            needRoom(1, 2, false);
270
            pointTypes.add(Byte.valueOf(SEG_MOVETO));
271
            addPoint(point);
272
        }
273
    }
274

    
275
    /**
276
     * Adds a point to the path by drawing a straight line from the
277
     * current coordinates to the new specified coordinates.
278
     * 
279
     * @param x
280
     *            ,&nbsp;y the specified coordinates
281
     * @deprecated
282
     *             use lineTo(Point)
283
     */
284
    public synchronized void lineTo(double x, double y) {
285
        needRoom(1, 2, true);
286
        pointTypes.add(Byte.valueOf(SEG_LINETO));
287
        addPoint(x, y);
288
    }
289

    
290
    public synchronized void lineTo(Point point) {
291
        needRoom(1, 2, true);
292
        pointTypes.add(Byte.valueOf(SEG_LINETO));
293
        addPoint(point);
294
    }
295

    
296
    private void addPoint(double x, double y) {
297
        try {
298
            pointCoords.add(geomManager.createPoint(x, y,
299
                Geometry.SUBTYPES.GEOM2D));
300
        } catch (CreateGeometryException e) {
301
            LOG.error("Error creating a point", e);
302
        }
303
    }
304

    
305
    private void addPoint(Point point) {
306
        pointCoords.add(point);
307
    }
308

    
309
    /**
310
     * Adds a curved segment, defined by two new points, to the path by
311
     * drawing a Quadratic curve that intersects both the current
312
     * coordinates and the coordinates (x2,&nbsp;y2), using the
313
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
314
     * point.
315
     * 
316
     * @param x1
317
     *            ,&nbsp;y1 the coordinates of the first quadratic control
318
     *            point
319
     * @param x2
320
     *            ,&nbsp;y2 the coordinates of the final endpoint
321
     * @deprecated
322
     *             use quadTo(Point, Point)
323
     */
324
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
325
        needRoom(1, 4, true);
326
        pointTypes.add(Byte.valueOf(SEG_QUADTO));
327
        addPoint(x1, y1);
328
        addPoint(x2, y2);
329
        isSimple = false;
330
    }
331

    
332
    public synchronized void quadTo(Point point1, Point point2) {
333
        needRoom(1, 4, true);
334
        pointTypes.add(Byte.valueOf(SEG_QUADTO));
335
        addPoint(point1);
336
        addPoint(point2);
337
        isSimple = false;
338
    }
339

    
340
    /**
341
     * Adds a curved segment, defined by three new points, to the path by
342
     * drawing a B&eacute;zier curve that intersects both the current
343
     * coordinates and the coordinates (x3,&nbsp;y3), using the
344
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
345
     * B&eacute;zier control points.
346
     * 
347
     * @param x1
348
     *            ,&nbsp;y1 the coordinates of the first B&eacute;ezier
349
     *            control point
350
     * @param x2
351
     *            ,&nbsp;y2 the coordinates of the second B&eacute;zier
352
     *            control point
353
     * @param x3
354
     *            ,&nbsp;y3 the coordinates of the final endpoint
355
     * @deprecated
356
     *             use curveTo(Point, Point, Point)
357
     */
358
    public synchronized void curveTo(double x1, double y1, double x2,
359
        double y2, double x3, double y3) {
360
        needRoom(1, 6, true);
361
        pointTypes.add(Byte.valueOf(SEG_CUBICTO));
362
        addPoint(x1, y1);
363
        addPoint(x2, y2);
364
        addPoint(x3, y3);
365
        isSimple = false;
366
    }
367

    
368
    public synchronized void curveTo(Point point1, Point point2, Point point3) {
369
        needRoom(1, 6, true);
370
        pointTypes.add(Byte.valueOf(SEG_CUBICTO));
371
        addPoint(point1);
372
        addPoint(point2);
373
        addPoint(point3);
374
        isSimple = false;
375
    }
376

    
377
    /**
378
     * Closes the current subpath by drawing a straight line back to
379
     * the coordinates of the last <code>moveTo</code>. If the path is already
380
     * closed then this method has no effect.
381
     */
382
    public synchronized void closePath() {
383
        if (getNumTypes() == 0 || getTypeAt(getNumTypes() - 1) != SEG_CLOSE) {
384
            needRoom(1, 0, true);
385
            // Adding a geometry like the last geometry
386
            // addPoint(100, 100);
387
            pointTypes.add(Byte.valueOf(SEG_CLOSE));
388
        }
389
    }
390

    
391
    /**
392
     * Check if the first part is closed.
393
     * 
394
     * @return
395
     */
396
    public boolean isClosed() {
397
        PathIterator theIterator =
398
            getPathIterator(null, geomManager.getFlatness());
399
        double[] theData = new double[6];
400
        double xFinal = 0;
401
        double yFinal = 0;
402
        double xIni = 0;
403
        double yIni = 0;
404
        boolean first = true;
405

    
406
        while (!theIterator.isDone()) {
407
            // while not done
408
            int theType = theIterator.currentSegment(theData);
409

    
410
            switch (theType) {
411
            case PathIterator.SEG_MOVETO:
412
                xIni = theData[0];
413
                yIni = theData[1];
414
                if (!first) {
415
                    break;
416
                }
417
                first = false;
418
                break;
419

    
420
            case PathIterator.SEG_LINETO:
421
                xFinal = theData[0];
422
                yFinal = theData[1];
423
                break;
424
            case PathIterator.SEG_CLOSE:
425
                return true;
426

    
427
            } // end switch
428

    
429
            theIterator.next();
430
        }
431
        if ((xFinal == xIni) && (yFinal == yIni))
432
            return true;
433
        return false;
434
    }
435

    
436
    /**
437
     * Appends the geometry of the specified {@link PathIterator} object
438
     * to the path, possibly connecting the new geometry to the existing
439
     * path segments with a line segment.
440
     * If the <code>connect</code> parameter is <code>true</code> and the
441
     * path is not empty then any initial <code>moveTo</code> in the
442
     * geometry of the appended <code>Shape</code> is turned into a
443
     * <code>lineTo</code> segment.
444
     * If the destination coordinates of such a connecting <code>lineTo</code>
445
     * segment match the ending coordinates of a currently open
446
     * subpath then the segment is omitted as superfluous.
447
     * The winding rule of the specified <code>Shape</code> is ignored
448
     * and the appended geometry is governed by the winding
449
     * rule specified for this path.
450
     * 
451
     * @param pi
452
     *            the <code>PathIterator</code> whose geometry is appended to
453
     *            this path
454
     * @param connect
455
     *            a boolean to control whether or not to turn an
456
     *            initial <code>moveTo</code> segment into a <code>lineTo</code>
457
     *            segment
458
     *            to connect the new geometry to the existing path
459
     */
460
    public void append(PathIterator pi, boolean connect) {
461
        double coords[] = new double[6];
462
        while (!pi.isDone()) {
463
            switch (pi.currentSegment(coords)) {
464
            case SEG_MOVETO:
465
                if (!connect || getNumTypes() < 1 || getNumCoords() < 2) {
466
                    moveTo(coords[0], coords[1]);
467
                    break;
468
                }
469
                if (getTypeAt(getNumTypes() - 1) != SEG_CLOSE
470
                    && getPointAt(getNumCoords() - 1).getX() == coords[0]
471
                    && getPointAt(getNumCoords() - 1).getY() == coords[1]) {
472
                    // Collapse out initial moveto/lineto
473
                    break;
474
                }
475
                // NO BREAK;
476
            case SEG_LINETO:
477
                lineTo(coords[0], coords[1]);
478
                break;
479
            case SEG_QUADTO:
480
                quadTo(coords[0], coords[1], coords[2], coords[3]);
481
                break;
482
            case SEG_CUBICTO:
483
                curveTo(coords[0], coords[1], coords[2], coords[3], coords[4],
484
                    coords[5]);
485
                break;
486
            case SEG_CLOSE:
487
                closePath();
488
                break;
489
            }
490
            pi.next();
491
            connect = false;
492
        }
493
    }
494

    
495
    /**
496
     * Returns the fill style winding rule.
497
     * 
498
     * @return an integer representing the current winding rule.
499
     * @see #WIND_EVEN_ODD
500
     * @see #WIND_NON_ZERO
501
     * @see #setWindingRule
502
     */
503
    public synchronized int getWindingRule() {
504
        return windingRule;
505
    }
506

    
507
    /**
508
     * Sets the winding rule for this path to the specified value.
509
     * 
510
     * @param rule
511
     *            an integer representing the specified
512
     *            winding rule
513
     * @exception <code>IllegalArgumentException</code> if <code>rule</code> is
514
     *            not either <code>WIND_EVEN_ODD</code> or
515
     *            <code>WIND_NON_ZERO</code>
516
     * @see #WIND_EVEN_ODD
517
     * @see #WIND_NON_ZERO
518
     * @see #getWindingRule
519
     */
520
    public void setWindingRule(int rule) {
521
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
522
            throw new IllegalArgumentException("winding rule must be "
523
                + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
524
        }
525
        windingRule = rule;
526
    }
527

    
528
    /**
529
     * Returns the coordinates most recently added to the end of the path
530
     * as a {@link Point2D} object.
531
     * 
532
     * @return a <code>Point2D</code> object containing the ending
533
     *         coordinates of the path or <code>null</code> if there are no
534
     *         points
535
     *         in the path.
536
     */
537
    public synchronized Point2D getCurrentPoint() {
538
        if (getNumTypes() < 1 || getNumCoords() < 2) {
539
            return null;
540
        }
541
        int index = getNumCoords();
542
        if (getTypeAt(getNumTypes() - 1) == SEG_CLOSE) {
543
            loop: for (int i = getNumTypes() - 2; i > 0; i--) {
544
                switch (getTypeAt(i)) {
545
                case SEG_MOVETO:
546
                    break loop;
547
                case SEG_LINETO:
548
                    index -= 2;
549
                    break;
550
                case SEG_QUADTO:
551
                    index -= 4;
552
                    break;
553
                case SEG_CUBICTO:
554
                    index -= 6;
555
                    break;
556
                case SEG_CLOSE:
557
                    break;
558
                }
559
            }
560
        }
561
        return new Point2D.Double(getPointAt(index - 1).getX(), getPointAt(
562
            index - 1).getY());
563
    }
564

    
565
    /**
566
     * Resets the path to empty. The append position is set back to the
567
     * beginning of the path and all coordinates and point types are
568
     * forgotten.
569
     */
570
    public synchronized void reset() {
571
        pointCoords.clear();
572
        pointTypes.clear();
573
    }
574

    
575
    /**
576
     * Transforms the geometry of this path using the specified
577
     * {@link AffineTransform}.
578
     * The geometry is transformed in place, which permanently changes the
579
     * boundary defined by this object.
580
     * 
581
     * @param at
582
     *            the <code>AffineTransform</code> used to transform the area
583
     */
584
    public void transform(AffineTransform at) {
585
        for (int i = 0; i < getNumCoords(); i++) {
586
            double[] coordinates = getCoordinatesAt(i);
587
            at.transform(coordinates, 0, coordinates, 0, coordinates.length - 1);
588
        }
589
    }
590

    
591
    public void reProject(ICoordTrans ct) {
592
        Point2D pt = new Point2D.Double();
593
        for (int i = 0; i < getNumCoords(); i++) {
594
            double[] coordinates = getCoordinatesAt(i);
595
            pt.setLocation(coordinates[0], coordinates[1]);
596
            pt = ct.convert(pt, null);
597
            coordinates[0] = pt.getX();
598
            coordinates[1] = pt.getY();
599
        }
600
    }
601

    
602
    /**
603
     * Returns a new transformed <code>Shape</code>.
604
     * 
605
     * @param at
606
     *            the <code>AffineTransform</code> used to transform a
607
     *            new <code>Shape</code>.
608
     * @return a new <code>Shape</code>, transformed with the specified
609
     *         <code>AffineTransform</code>.
610
     */
611
    public synchronized Shape createTransformedShape(AffineTransform at) {
612
        GeneralPathX gp = (GeneralPathX) clone();
613
        if (at != null) {
614
            gp.transform(at);
615
        }
616
        return gp;
617
    }
618

    
619
    /**
620
     * Return the bounding box of the path.
621
     * 
622
     * @return a {@link java.awt.Rectangle} object that
623
     *         bounds the current path.
624
     */
625
    public java.awt.Rectangle getBounds() {
626
        return getBounds2D().getBounds();
627
    }
628

    
629
    /**
630
     * Returns the bounding box of the path.
631
     * 
632
     * @return a {@link Rectangle2D} object that
633
     *         bounds the current path.
634
     */
635
    public synchronized Rectangle2D getBounds2D() {
636
        double x1, y1, x2, y2;
637
        int i = getNumCoords();
638
        if (i > 0) {
639
            y1 = y2 = getPointAt(--i).getY();
640
            x1 = x2 = getPointAt(i).getX();
641
            while (i > 0) {
642
                double y = getPointAt(--i).getY();
643
                double x = getPointAt(i).getX();
644
                if (x < x1)
645
                    x1 = x;
646
                if (y < y1)
647
                    y1 = y;
648
                if (x > x2)
649
                    x2 = x;
650
                if (y > y2)
651
                    y2 = y;
652
            }
653
        } else {
654
            x1 = y1 = x2 = y2 = 0.0f;
655
        }
656
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
657
    }
658

    
659
    /**
660
     * Tests if the specified coordinates are inside the boundary of
661
     * this <code>Shape</code>.
662
     * 
663
     * @param x
664
     *            ,&nbsp;y the specified coordinates
665
     * @return <code>true</code> if the specified coordinates are inside this
666
     *         <code>Shape</code>; <code>false</code> otherwise
667
     */
668
    public boolean contains(double x, double y) {
669
        if (pointTypes.size() < 2) {
670
            return false;
671
        }
672
        int cross =
673
            GeomUtilities.pointCrossingsForPath(getPathIterator(null), x, y);
674
        if (windingRule == WIND_NON_ZERO) {
675
            return (cross != 0);
676
        } else {
677
            return ((cross & 1) != 0);
678
        }
679
    }
680

    
681
    /**
682
     * Tests if the specified <code>Point2D</code> is inside the boundary
683
     * of this <code>Shape</code>.
684
     * 
685
     * @param p
686
     *            the specified <code>Point2D</code>
687
     * @return <code>true</code> if this <code>Shape</code> contains the
688
     *         specified <code>Point2D</code>, <code>false</code> otherwise.
689
     */
690
    public boolean contains(Point2D p) {
691
        return contains(p.getX(), p.getY());
692
    }
693

    
694
    /**
695
     * Tests if the specified rectangular area is inside the boundary of
696
     * this <code>Shape</code>.
697
     * 
698
     * @param x
699
     *            ,&nbsp;y the specified coordinates
700
     * @param w
701
     *            the width of the specified rectangular area
702
     * @param h
703
     *            the height of the specified rectangular area
704
     * @return <code>true</code> if this <code>Shape</code> contains
705
     *         the specified rectangluar area; <code>false</code> otherwise.
706
     */
707
    public boolean contains(double x, double y, double w, double h) {
708
        return GeomUtilities
709
            .contains(getPathIterator(null), x, y, x + w, y + h);
710
    }
711

    
712
    /**
713
     * Tests if the specified <code>Rectangle2D</code> is inside the boundary of
714
     * this <code>Shape</code>.
715
     * 
716
     * @param r
717
     *            a specified <code>Rectangle2D</code>
718
     * @return <code>true</code> if this <code>Shape</code> bounds the
719
     *         specified <code>Rectangle2D</code>; <code>false</code> otherwise.
720
     */
721
    public boolean contains(Rectangle2D r) {
722
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
723
    }
724

    
725
    /**
726
     * Tests if the interior of this <code>Shape</code> intersects the
727
     * interior of a specified set of rectangular coordinates.
728
     * 
729
     * @param x
730
     *            ,&nbsp;y the specified coordinates
731
     * @param w
732
     *            the width of the specified rectangular coordinates
733
     * @param h
734
     *            the height of the specified rectangular coordinates
735
     * @return <code>true</code> if this <code>Shape</code> and the
736
     *         interior of the specified set of rectangular coordinates
737
     *         intersect
738
     *         each other; <code>false</code> otherwise.
739
     */
740
    public boolean intersects(double x, double y, double w, double h) {
741
        return GeomUtilities.intersects(getPathIterator(null), x, y, w, h);
742
    }
743

    
744
    /**
745
     * Tests if the interior of this <code>Shape</code> intersects the
746
     * interior of a specified <code>Rectangle2D</code>.
747
     * 
748
     * @param r
749
     *            the specified <code>Rectangle2D</code>
750
     * @return <code>true</code> if this <code>Shape</code> and the interior
751
     *         of the specified <code>Rectangle2D</code> intersect each
752
     *         other; <code>false</code> otherwise.
753
     */
754
    public boolean intersects(Rectangle2D r) {
755
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
756
    }
757

    
758
    /**
759
     * Returns a <code>PathIterator</code> object that iterates along the
760
     * boundary of this <code>Shape</code> and provides access to the
761
     * geometry of the outline of this <code>Shape</code>.
762
     * The iterator for this class is not multi-threaded safe,
763
     * which means that this <code>GeneralPathX</code> class does not
764
     * guarantee that modifications to the geometry of this
765
     * <code>GeneralPathX</code> object do not affect any iterations of
766
     * that geometry that are already in process.
767
     * 
768
     * @param at
769
     *            an <code>AffineTransform</code>
770
     * @return a new <code>PathIterator</code> that iterates along the
771
     *         boundary of this <code>Shape</code> and provides access to the
772
     *         geometry of this <code>Shape</code>'s outline
773
     */
774
    public PathIterator getPathIterator(AffineTransform at) {
775
        if (isSimple) {
776
            return new GeneralPathXIteratorSimple(this, at);
777
        } else {
778
            return new GeneralPathXIterator(this, at);
779
        }
780
    }
781

    
782
    /**
783
     * Returns a <code>PathIterator</code> object that iterates along the
784
     * boundary of the flattened <code>Shape</code> and provides access to the
785
     * geometry of the outline of the <code>Shape</code>.
786
     * The iterator for this class is not multi-threaded safe,
787
     * which means that this <code>GeneralPathX</code> class does not
788
     * guarantee that modifications to the geometry of this
789
     * <code>GeneralPathX</code> object do not affect any iterations of
790
     * that geometry that are already in process.
791
     * 
792
     * @param at
793
     *            an <code>AffineTransform</code>
794
     * @param flatness
795
     *            the maximum distance that the line segments used to
796
     *            approximate the curved segments are allowed to deviate
797
     *            from any point on the original curve
798
     * @return a new <code>PathIterator</code> that iterates along the flattened
799
     *         <code>Shape</code> boundary.
800
     */
801
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
802
        return new FlatteningPathIterator(getPathIterator(at), flatness);
803
    }
804

    
805
    /**
806
     * Creates a new object of the same class as this object.
807
     * 
808
     * @return a clone of this instance.
809
     * @exception OutOfMemoryError
810
     *                if there is not enough memory.
811
     * @see java.lang.Cloneable
812
     * @since 1.2
813
     */
814
    public Object clone() {
815
        GeneralPathX copy = new GeneralPathX();
816
        copy.windingRule = windingRule;
817
        copy.isSimple = isSimple;
818
        for (int i = 0; i < getNumTypes(); i++) {
819
            copy.pointTypes.add(pointTypes.get(i));
820
        }
821
        for (int i = 0; i < getNumCoords(); i++) {
822
            copy.addPoint((Point) getPointAt(i).cloneGeometry());
823
        }
824
        return copy;
825

    
826
    }
827

    
828
    GeneralPathX(int windingRule, byte[] pointTypes, int numTypes,
829
        double[] pointCoords, int numCoords) {
830

    
831
        // used to construct from native
832

    
833
        this.windingRule = windingRule;
834
        this.setPointTypes(pointTypes);
835
        this.setNumTypes(numTypes);
836
        this.setPointCoords(pointCoords);
837
        this.setNumCoords(numCoords);
838
    }
839

    
840
    public void setNumTypes(int numTypes) {
841

    
842
    }
843

    
844
    public int getNumTypes() {
845
        return pointTypes.size();
846
    }
847

    
848
    public int setNumCoords(int numCoords) {
849
        return pointCoords.size();
850
    }
851

    
852
    public int getNumCoords() {
853
        return pointCoords.size();
854
    }
855

    
856
    public byte getTypeAt(int index) {
857
        return ((Byte) pointTypes.get(index)).byteValue();
858
    }
859

    
860
    /**
861
     * @deprecated
862
     *             use the geometry methods.
863
     */
864
    public void setPointTypes(byte[] pointTypes) {
865
        this.pointTypes.clear();
866
        for (int i = 0; i < pointTypes.length; i++) {
867
            this.pointTypes.add(Byte.valueOf(pointTypes[i]));
868
        }
869
    }
870

    
871
    /**
872
     * @deprecated
873
     *             use the geometry methods.
874
     */
875
    public byte[] getPointTypes() {
876
        byte[] bytes = new byte[pointTypes.size()];
877
        for (int i = 0; i < pointTypes.size(); i++) {
878
            bytes[i] = ((Byte) pointTypes.get(i)).byteValue();
879
        }
880
        return bytes;
881
    }
882

    
883
    /**
884
     * @param pointCoords
885
     * @deprecated
886
     *             use the geometry methods.
887
     */
888
    public void setPointCoords(double[] pointCoords) {
889
        this.pointCoords.clear();
890
        for (int i = 0; i < pointCoords.length; i = i + 2) {
891
            try {
892
                addPoint(geomManager.createPoint(pointCoords[i],
893
                    pointCoords[i + 1], Geometry.SUBTYPES.GEOM2D));
894
            } catch (CreateGeometryException e) {
895
                LOG.error("Error creating a point", e);
896
            }
897
        }
898
    }
899

    
900
    /**
901
     * @deprecated
902
     *             use the geometry methods.
903
     */
904
    public double[] getPointCoords() {
905
        double[] doubles = new double[pointCoords.size() * 2];
906
        for (int i = 0; i < getNumCoords(); i++) {
907
            doubles[i * 2] = getPointAt(i).getX();
908
            doubles[(i * 2) + 1] = getPointAt(i).getY();
909
        }
910
        return doubles;
911
    }
912

    
913
    public Point getPointAt(int index) {
914
        return (Point) pointCoords.get(index);
915
    }
916

    
917
    public double[] getCoordinatesAt(int index) {
918
        return getPointAt(index).getCoordinates();
919
    }
920

    
921
    /**
922
     * Convertimos el path a puntos y luego le damos la vuelta.
923
     */
924
    public void flip() {
925
        PathIterator theIterator =
926
            getPathIterator(null, geomManager.getFlatness());
927
        double[] theData = new double[6];
928
        CoordinateList coordList = new CoordinateList();
929
        Coordinate c1;
930
        GeneralPathX newGp = new GeneralPathX();
931
        ArrayList listOfParts = new ArrayList();
932
        while (!theIterator.isDone()) {
933
            // while not done
934
            int type = theIterator.currentSegment(theData);
935
            switch (type) {
936
            case SEG_MOVETO:
937
                coordList = new CoordinateList();
938
                listOfParts.add(coordList);
939
                c1 = new Coordinate(theData[0], theData[1]);
940
                coordList.add(c1, true);
941
                break;
942
            case SEG_LINETO:
943
                c1 = new Coordinate(theData[0], theData[1]);
944
                coordList.add(c1, true);
945
                break;
946

    
947
            case SEG_CLOSE:
948
                coordList.add(coordList.getCoordinate(0));
949
                break;
950

    
951
            }
952
            theIterator.next();
953
        }
954

    
955
        for (int i = listOfParts.size() - 1; i >= 0; i--) {
956
            coordList = (CoordinateList) listOfParts.get(i);
957
            Coordinate[] coords = coordList.toCoordinateArray();
958
            CoordinateArraySequence seq = new CoordinateArraySequence(coords);
959
            CoordinateSequences.reverse(seq);
960
            coords = seq.toCoordinateArray();
961
            newGp.moveTo(coords[0].x, coords[0].y);
962
            for (int j = 1; j < coords.length; j++) {
963
                newGp.lineTo(coords[j].x, coords[j].y);
964
            }
965
        }
966
        reset();
967
        append(newGp.getPathIterator(null), false);
968
    }
969

    
970
    /**
971
     * Check if the first part is CCW.
972
     * 
973
     * @return
974
     */
975
    public boolean isCCW() {
976
        PathIterator theIterator =
977
            getPathIterator(null, geomManager.getFlatness()); // polyLine.getPathIterator(null,
978
                                                              // flatness);
979
        double[] theData = new double[6];
980
        Coordinate first = null;
981
        CoordinateList coordList = new CoordinateList();
982
        Coordinate c1;
983
        boolean bFirst = true;
984
        while (!theIterator.isDone()) {
985
            // while not done
986
            int type = theIterator.currentSegment(theData);
987
            switch (type) {
988
            case SEG_MOVETO:
989
                c1 = new Coordinate(theData[0], theData[1]);
990
                if (bFirst == false) // Ya tenemos la primera parte.
991
                    break;
992
                if (bFirst) {
993
                    bFirst = false;
994
                    first = c1;
995
                }
996
                coordList.add(c1, true);
997
                break;
998
            case SEG_LINETO:
999
                c1 = new Coordinate(theData[0], theData[1]);
1000
                coordList.add(c1, true);
1001
                break;
1002

    
1003
            }
1004
            theIterator.next();
1005
        }
1006
        coordList.add(first, true);
1007
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
1008
    }
1009

    
1010
    /**
1011
     * @return the isSimple
1012
     */
1013
    public boolean isSimple() {
1014
        return isSimple;
1015
    }
1016
}