Statistics
| Revision:

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

History | View | Annotate | Download (33.2 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
 * Based on portions of code from java.awt.geom.GeneralPath of the
45
 * OpenJDK project (Copyright (c) 1996, 2006, Oracle and/or its affiliates)
46
 */
47
import java.awt.Shape;
48
import java.awt.geom.AffineTransform;
49
import java.awt.geom.FlatteningPathIterator;
50
import java.awt.geom.IllegalPathStateException;
51
import java.awt.geom.PathIterator;
52
import java.awt.geom.Point2D;
53
import java.awt.geom.Rectangle2D;
54
import java.io.Serializable;
55
import java.util.ArrayList;
56
import java.util.List;
57

    
58
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
import org.cresques.cts.ICoordTrans;
65
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
67

    
68
import org.gvsig.fmap.geom.Geometry;
69
import org.gvsig.fmap.geom.GeometryLocator;
70
import org.gvsig.fmap.geom.GeometryManager;
71
import org.gvsig.fmap.geom.exception.CreateGeometryException;
72
import org.gvsig.jdk.GeomUtilities;
73

    
74
/**
75
 * The <code>GeneralPathX</code> class represents a geometric path
76
 * constructed from straight lines, and quadratic and cubic
77
 * (B&eacute;zier) curves. It can contain multiple subpaths.
78
 * <p>
79
 * 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
 * <p>
82
 * 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
 * <p>
86
 * 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
 * @version 1.58, 01/23/03
93
 * @author Jim Graham
94
 * @deprecated
95
 *             use the geometry methods
96
 */
97
public class GeneralPathX implements Shape, Cloneable, Serializable {
98

    
99
    /**
100
     * Default serial version ID
101
     */
102
    private static final long serialVersionUID = 1L;
103

    
104
    private static final Logger LOG = LoggerFactory
105
        .getLogger(GeneralPathX.class);
106

    
107
    protected static GeometryManager geomManager = GeometryLocator
108
        .getGeometryManager();
109

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

    
112
    /**
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

    
118
    /**
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

    
124
    // For code simplicity, copy these constants to our namespace
125
    // and cast them to byte constants for easy storage.
126
    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
    public static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
130
    public static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;
131

    
132
    private List pointTypes = new ArrayList();
133
    private List pointCoords = new ArrayList();
134

    
135
    int windingRule;
136

    
137
    private boolean isSimple = true;
138

    
139
    static final int INIT_SIZE = 20;
140
    static final int EXPAND_MAX = 500;
141

    
142
    /**
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
     * 
148
     * @see #WIND_NON_ZERO
149
     */
150
    public GeneralPathX() {
151
        this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
152
    }
153

    
154
    /**
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
     * 
159
     * @param rule
160
     *            the winding rule
161
     * @see #WIND_EVEN_ODD
162
     * @see #WIND_NON_ZERO
163
     */
164
    public GeneralPathX(int rule) {
165
        this(rule, INIT_SIZE, INIT_SIZE);
166
    }
167

    
168
    /**
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
     * 
175
     * @param rule
176
     *            the winding rule
177
     * @param initialCapacity
178
     *            the estimate for the number of path segments
179
     *            in the path
180
     * @see #WIND_EVEN_ODD
181
     * @see #WIND_NON_ZERO
182
     * @deprecated
183
     *             the capacity grows dynamically
184
     */
185
    public GeneralPathX(int rule, int initialCapacity) {
186
        this(rule, initialCapacity, initialCapacity);
187
    }
188

    
189
    /**
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
     * 
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
     * @see #WIND_EVEN_ODD
206
     * @see #WIND_NON_ZERO
207
     */
208
    GeneralPathX(int rule, int initialTypes, int initialCoords) {
209
        setWindingRule(rule);
210
    }
211

    
212
    /**
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
     * 
218
     * @param s
219
     *            the specified <code>Shape</code> object
220
     */
221
    public GeneralPathX(PathIterator piter) {
222
        this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
223
        setWindingRule(piter.getWindingRule());
224
        append(piter, false);
225
    }
226

    
227
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
228
        if (needMove && getNumTypes() == 0) {
229
            throw new IllegalPathStateException("missing initial moveto "
230
                + "in path definition");
231
        }
232
    }
233

    
234
    /**
235
     * Adds a point to the path by moving to the specified
236
     * coordinates.
237
     * 
238
     * @param x
239
     *            ,&nbsp;y the specified coordinates
240
     * @deprecated
241
     *             use moveTo(Point)
242
     */
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
        } else {
250
            needRoom(1, 2, false);
251
            pointTypes.add(Byte.valueOf(SEG_MOVETO));
252
            addPoint(x, y);
253
        }
254
    }
255

    
256
    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
        } else {
262
            needRoom(1, 2, false);
263
            pointTypes.add(Byte.valueOf(SEG_MOVETO));
264
            addPoint(point);
265
        }
266
    }
267

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

    
283
    public synchronized void lineTo(Point point) {
284
        needRoom(1, 2, true);
285
        pointTypes.add(Byte.valueOf(SEG_LINETO));
286
        addPoint(point);
287
    }
288

    
289
    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
    private void addPoint(double x, double y) {
300
        try {
301
            pointCoords.add(geomManager.createPoint(x, y,
302
                Geometry.SUBTYPES.GEOM2D));
303
        } catch (CreateGeometryException e) {
304
            LOG.error("Error creating a point", e);
305
        }
306
    }
307

    
308
    private void addPoint(Point point) {
309
        pointCoords.add(point);
310
    }
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
     * 
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
     */
327
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
328
        needRoom(1, 4, true);
329
        pointTypes.add(Byte.valueOf(SEG_QUADTO));
330
        addPoint(x1, y1);
331
        addPoint(x2, y2);
332
        isSimple = false;
333
    }
334

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

    
343
    /**
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
     * 
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
     * @deprecated
359
     *             use curveTo(Point, Point, Point)
360
     */
361
    public synchronized void curveTo(double x1, double y1, double x2,
362
        double y2, double x3, double y3) {
363
        needRoom(1, 6, true);
364
        pointTypes.add(Byte.valueOf(SEG_CUBICTO));
365
        addPoint(x1, y1);
366
        addPoint(x2, y2);
367
        addPoint(x3, y3);
368
        isSimple = false;
369
    }
370

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

    
380
    /**
381
     * Closes the current subpath by drawing a straight line back to
382
     * the coordinates of the last <code>moveTo</code>. If the path is already
383
     * 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
            // Adding a geometry like the last geometry
389
            // addPoint(100, 100);
390
            pointTypes.add(Byte.valueOf(SEG_CLOSE));
391
        }
392
    }
393

    
394
    /**
395
     * Check if the first part is closed.
396
     * 
397
     * @return
398
     */
399
    public boolean isClosed() {
400
        PathIterator theIterator =
401
            getPathIterator(null, geomManager.getFlatness());
402
        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

    
409
        while (!theIterator.isDone()) {
410
            // while not done
411
            int theType = theIterator.currentSegment(theData);
412

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

    
423
            case PathIterator.SEG_LINETO:
424
                xFinal = theData[0];
425
                yFinal = theData[1];
426
                break;
427
            case PathIterator.SEG_CLOSE:
428
                return true;
429

    
430
            } // end switch
431

    
432
            theIterator.next();
433
        }
434
        if ((xFinal == xIni) && (yFinal == yIni))
435
            return true;
436
        return false;
437
    }
438

    
439
    /**
440
     * Appends the geometry of the specified {@link PathIterator} object
441
     * 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
     * 
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
     */
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
                if (getTypeAt(getNumTypes() - 1) != SEG_CLOSE
473
                    && getPointAt(getNumCoords() - 1).getX() == coords[0]
474
                    && getPointAt(getNumCoords() - 1).getY() == coords[1]) {
475
                    // 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
                quadTo(coords[0], coords[1], coords[2], coords[3]);
484
                break;
485
            case SEG_CUBICTO:
486
                curveTo(coords[0], coords[1], coords[2], coords[3], coords[4],
487
                    coords[5]);
488
                break;
489
            case SEG_CLOSE:
490
                closePath();
491
                break;
492
            }
493
            pi.next();
494
            connect = false;
495
        }
496
    }
497

    
498
    /**
499
     * Returns the fill style winding rule.
500
     * 
501
     * @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

    
510
    /**
511
     * Sets the winding rule for this path to the specified value.
512
     * 
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
     * @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
            throw new IllegalArgumentException("winding rule must be "
526
                + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
527
        }
528
        windingRule = rule;
529
    }
530

    
531
    /**
532
     * Returns the coordinates most recently added to the end of the path
533
     * as a {@link Point2D} object.
534
     * 
535
     * @return a <code>Point2D</code> object containing the ending
536
     *         coordinates of the path or <code>null</code> if there are no
537
     *         points
538
     *         in the path.
539
     */
540
    public synchronized Point2D getCurrentPoint() {
541
        if (getNumTypes() < 1 || getNumCoords() < 1) {
542
            return null;
543
        }
544
        int index = getNumCoords();
545
        if (getTypeAt(getNumTypes() - 1) == SEG_CLOSE) {
546
            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
                }
562
            }
563
        }
564
        return new Point2D.Double(getPointAt(index - 1).getX(), getPointAt(
565
            index - 1).getY());
566
    }
567

    
568
    /**
569
     * Resets the path to empty. The append position is set back to the
570
     * 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

    
578
    /**
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
     * 
584
     * @param at
585
     *            the <code>AffineTransform</code> used to transform the area
586
     */
587
    public void transform(AffineTransform at) {
588
        for (int i = 0; i < getNumCoords(); i++) {
589
            getPointAt(i).transform(at);
590
        }
591
    }
592

    
593
    public void reProject(ICoordTrans ct) {
594
        for (int i = 0; i < getNumCoords(); i++) {
595
            getPointAt(i).reProject(ct);
596
        }
597
    }
598

    
599
    /**
600
     * Returns a new transformed <code>Shape</code>.
601
     * 
602
     * @param at
603
     *            the <code>AffineTransform</code> used to transform a
604
     *            new <code>Shape</code>.
605
     * @return a new <code>Shape</code>, transformed with the specified
606
     *         <code>AffineTransform</code>.
607
     */
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

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

    
626
    /**
627
     * Returns the bounding box of the path.
628
     * 
629
     * @return a {@link Rectangle2D} object that
630
     *         bounds the current path.
631
     */
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
                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
            }
650
        } else {
651
            x1 = y1 = x2 = y2 = 0.0f;
652
        }
653
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
654
    }
655

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

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

    
691
    /**
692
     * Tests if the specified rectangular area is inside the boundary of
693
     * this <code>Shape</code>.
694
     * 
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
     * @return <code>true</code> if this <code>Shape</code> contains
702
     *         the specified rectangluar area; <code>false</code> otherwise.
703
     */
704
    public boolean contains(double x, double y, double w, double h) {
705
        return GeomUtilities
706
            .contains(getPathIterator(null), x, y, x + w, y + h);
707
    }
708

    
709
    /**
710
     * 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
     * @return <code>true</code> if this <code>Shape</code> bounds the
716
     *         specified <code>Rectangle2D</code>; <code>false</code> otherwise.
717
     */
718
    public boolean contains(Rectangle2D r) {
719
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
720
    }
721

    
722
    /**
723
     * Tests if the interior of this <code>Shape</code> intersects the
724
     * interior of a specified set of rectangular coordinates.
725
     * 
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
     * @return <code>true</code> if this <code>Shape</code> and the
733
     *         interior of the specified set of rectangular coordinates
734
     *         intersect
735
     *         each other; <code>false</code> otherwise.
736
     */
737
    public boolean intersects(double x, double y, double w, double h) {
738
        return GeomUtilities.intersects(getPathIterator(null), x, y, w, h);
739
    }
740

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

    
755
    /**
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
     * 
765
     * @param at
766
     *            an <code>AffineTransform</code>
767
     * @return a new <code>PathIterator</code> that iterates along the
768
     *         boundary of this <code>Shape</code> and provides access to the
769
     *         geometry of this <code>Shape</code>'s outline
770
     */
771
    public PathIterator getPathIterator(AffineTransform at) {
772
        if (isSimple) {
773
            return new GeneralPathXIteratorSimple(this, at);
774
        } else {
775
            return new GeneralPathXIterator(this, at);
776
        }
777
    }
778

    
779
    /**
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
     * 
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
     * @return a new <code>PathIterator</code> that iterates along the flattened
796
     *         <code>Shape</code> boundary.
797
     */
798
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
799
        return new FlatteningPathIterator(getPathIterator(at), flatness);
800
    }
801

    
802
    /**
803
     * Creates a new object of the same class as this object.
804
     * 
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
     */
811
    public Object clone() {
812
        GeneralPathX copy = new GeneralPathX();
813
        copy.windingRule = windingRule;
814
        copy.isSimple = isSimple;
815
        for (int i = 0; i < getNumTypes(); i++) {
816
            copy.pointTypes.add(pointTypes.get(i));
817
        }
818
        for (int i = 0; i < getNumCoords(); i++) {
819
            copy.addPoint((Point) getPointAt(i).cloneGeometry());
820
        }
821
        return copy;
822

    
823
    }
824

    
825
    GeneralPathX(int windingRule, byte[] pointTypes, int numTypes,
826
        double[] pointCoords, int numCoords) {
827

    
828
        // used to construct from native
829

    
830
        this.windingRule = windingRule;
831
        this.setPointTypes(pointTypes);
832
        this.setNumTypes(numTypes);
833
        this.setPointCoords(pointCoords);
834
        this.setNumCoords(numCoords);
835
    }
836

    
837
    public void setNumTypes(int numTypes) {
838

    
839
    }
840

    
841
    public int getNumTypes() {
842
        return pointTypes.size();
843
    }
844

    
845
    public int setNumCoords(int numCoords) {
846
        return pointCoords.size();
847
    }
848

    
849
    public int getNumCoords() {
850
        return pointCoords.size();
851
    }
852

    
853
    public byte getTypeAt(int index) {
854
        return ((Byte) pointTypes.get(index)).byteValue();
855
    }
856

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

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

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

    
897
    /**
898
     * @deprecated
899
     *             use the geometry methods.
900
     */
901
    public double[] getPointCoords() {
902
        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
        }
907
        return doubles;
908
    }
909

    
910
    public Point getPointAt(int index) {
911
        return (Point) pointCoords.get(index);
912
    }
913

    
914
    public double[] getCoordinatesAt(int index) {
915
        return getPointAt(index).getCoordinates();
916
    }
917

    
918
    /**
919
     * Convertimos el path a puntos y luego le damos la vuelta.
920
     */
921
    public void flip() {
922
        PathIterator theIterator =
923
            getPathIterator(null, geomManager.getFlatness());
924
        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
            // while not done
931
            int type = theIterator.currentSegment(theData);
932
            switch (type) {
933
            case SEG_MOVETO:
934
                coordList = new CoordinateList();
935
                listOfParts.add(coordList);
936
                c1 = new Coordinate(theData[0], theData[1]);
937
                coordList.add(c1, true);
938
                break;
939
            case SEG_LINETO:
940
                c1 = new Coordinate(theData[0], theData[1]);
941
                coordList.add(c1, true);
942
                break;
943

    
944
            case SEG_CLOSE:
945
                coordList.add(coordList.getCoordinate(0));
946
                break;
947

    
948
            }
949
            theIterator.next();
950
        }
951

    
952
        for (int i = listOfParts.size() - 1; i >= 0; i--) {
953
            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
            for (int j = 1; j < coords.length; j++) {
960
                newGp.lineTo(coords[j].x, coords[j].y);
961
            }
962
        }
963
        reset();
964
        append(newGp.getPathIterator(null), false);
965
    }
966

    
967
    /**
968
     * Check if the first part is CCW.
969
     * 
970
     * @return
971
     */
972
    public boolean isCCW() {
973
        PathIterator theIterator =
974
            getPathIterator(null, geomManager.getFlatness()); // polyLine.getPathIterator(null,
975
                                                              // flatness);
976
        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
            // while not done
983
            int type = theIterator.currentSegment(theData);
984
            switch (type) {
985
            case SEG_MOVETO:
986
                c1 = new Coordinate(theData[0], theData[1]);
987
                if (bFirst == false) // Ya tenemos la primera parte.
988
                    break;
989
                if (bFirst) {
990
                    bFirst = false;
991
                    first = c1;
992
                }
993
                coordList.add(c1, true);
994
                break;
995
            case SEG_LINETO:
996
                c1 = new Coordinate(theData[0], theData[1]);
997
                coordList.add(c1, true);
998
                break;
999

    
1000
            }
1001
            theIterator.next();
1002
        }
1003
        coordList.add(first, true);
1004
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
1005
    }
1006

    
1007
    /**
1008
     * @return the isSimple
1009
     */
1010
    public boolean isSimple() {
1011
        return isSimple;
1012
    }
1013
}