Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.jts / src / main / java / org / gvsig / fmap / geom / jts / gputils / DefaultGeneralPathX.java @ 42267

History | View | Annotate | Download (32 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.geom.jts.gputils;
25

    
26
/*
27
 * Based on portions of code from java.awt.geom.GeneralPath of the
28
 * OpenJDK project (Copyright (c) 1996, 2006, Oracle and/or its affiliates)
29
 */
30
import java.awt.Shape;
31
import java.awt.geom.AffineTransform;
32
import java.awt.geom.FlatteningPathIterator;
33
import java.awt.geom.IllegalPathStateException;
34
import java.awt.geom.PathIterator;
35
import java.awt.geom.Point2D;
36
import java.awt.geom.Rectangle2D;
37
import java.util.ArrayList;
38
import java.util.List;
39

    
40
import org.cresques.cts.ICoordTrans;
41

    
42
import org.gvsig.fmap.geom.Geometry;
43
import org.gvsig.fmap.geom.GeometryLocator;
44
import org.gvsig.fmap.geom.GeometryManager;
45
import org.gvsig.fmap.geom.exception.CreateGeometryException;
46
import org.gvsig.fmap.geom.jts.primitive.point.Point3D;
47
import org.gvsig.jdk.GeomUtilities;
48

    
49
import org.slf4j.Logger;
50
import org.slf4j.LoggerFactory;
51

    
52
import com.vividsolutions.jts.algorithm.CGAlgorithms;
53
import com.vividsolutions.jts.geom.Coordinate;
54
import com.vividsolutions.jts.geom.CoordinateList;
55
import com.vividsolutions.jts.geom.CoordinateSequences;
56
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
57

    
58
import java.io.Serializable;
59

    
60
import org.gvsig.fmap.geom.primitive.GeneralPathX;
61
import org.gvsig.fmap.geom.primitive.IGeneralPathX;
62
import org.gvsig.fmap.geom.primitive.Point;
63

    
64
/**
65
 * The <code>GeneralPathX</code> class represents a geometric path
66
 * constructed from straight lines, and quadratic and cubic
67
 * (B&eacute;zier) curves. It can contain multiple subpaths.
68
 * <p>
69
 * The winding rule specifies how the interior of a path is determined. There
70
 * are two types of winding rules: EVEN_ODD and NON_ZERO.
71
 * <p>
72
 * An EVEN_ODD winding rule means that enclosed regions of the path alternate
73
 * between interior and exterior areas as traversed from the outside of the path
74
 * towards a point inside the region.
75
 * <p>
76
 * A NON_ZERO winding rule means that if a ray is drawn in any direction from a
77
 * given point to infinity and the places where the path intersects the ray are
78
 * examined, the point is inside of the path if and only if the number of times
79
 * that the path crosses the ray from left to right does not equal the number of
80
 * times that the path crosses the ray from right to left.
81
 *
82
 * @version 1.58, 01/23/03
83
 * @author Jim Graham
84
 * @deprecated
85
 *             use the geometry methods
86
 */
87
public class DefaultGeneralPathX extends GeneralPathX implements  Shape, Cloneable, Serializable {
88

    
89
    /**
90
     * Default serial version ID
91
     */
92
    private static final long serialVersionUID = 1L;
93

    
94
    private static final Logger LOG = LoggerFactory
95
        .getLogger(DefaultGeneralPathX.class);
96

    
97
    protected static GeometryManager geomManager = GeometryLocator
98
        .getGeometryManager();
99

    
100
    private List pointTypes = new ArrayList();
101
    private List pointCoords = new ArrayList();
102

    
103
    private Byte[] SEG_TYPES = new Byte[] {
104
        new Byte((byte)0),
105
        new Byte((byte)1),
106
        new Byte((byte)2),
107
        new Byte((byte)3),
108
        new Byte((byte)4),
109
        new Byte((byte)5),
110
        new Byte((byte)6),
111
        new Byte((byte)7),
112
        new Byte((byte)8),
113
        new Byte((byte)9),
114
        new Byte((byte)10)
115
    };
116

    
117
    int windingRule;
118

    
119
    private boolean isSimple = true;
120

    
121
    private boolean is2Dz=false;
122

    
123
    private double zValue=0.0;
124

    
125
    static final int EXPAND_MAX = 500;
126

    
127
    private  DefaultGeneralPathX() {
128
        super(false);
129
        setWindingRule(WIND_EVEN_ODD);
130
    }
131
    /**
132
     * Constructs a new <code>GeneralPathX</code> object with the specified
133
     * winding rule to control operations that require the interior of the
134
     * path to be defined.
135
     *
136
     * @param rule
137
     *            the winding rule
138
     * @see #WIND_EVEN_ODD
139
     * @see #WIND_NON_ZERO
140
     */
141
    public DefaultGeneralPathX(int rule) {
142
        super(false);
143
        setWindingRule(rule);
144
    }
145

    
146
    /**
147
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
148
     * {@link Shape} object.
149
     * All of the initial geometry and the winding rule for this path are
150
     * taken from the specified <code>Shape</code> object.
151
     *
152
     * @param s
153
     *            the specified <code>Shape</code> object
154
     */
155
    public DefaultGeneralPathX(PathIterator piter, boolean is2Dz, double zValue) {
156
        this(WIND_EVEN_ODD);
157
        this.is2Dz = is2Dz;
158
        this.zValue = zValue;
159
        setWindingRule(piter.getWindingRule());
160
        append(piter, false);
161
    }
162

    
163
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
164
        if (needMove && getNumTypes() == 0) {
165
            throw new IllegalPathStateException("missing initial moveto "
166
                + "in path definition");
167
        }
168
    }
169

    
170
    /**
171
     * Adds a point to the path by moving to the specified
172
     * coordinates.
173
     *
174
     * @param x
175
     *            ,&nbsp;y the specified coordinates
176
     * @deprecated
177
     *             use moveTo(Point)
178
     */
179
    public synchronized void moveTo(double x, double y) {
180
        int numtypes = getNumTypes();
181
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
182
            Point point = getPointAt(getNumCoords() - 1);
183
            point.setX(x);
184
            point.setY(y);
185
        } else {
186
            needRoom(1, 2, false);
187
            pointTypes.add(SEG_TYPES[SEG_MOVETO]);
188
            addPoint(x, y);
189
        }
190
    }
191

    
192
    public synchronized void moveTo(Point point) {
193
        int numtypes = getNumTypes();
194
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
195
            pointCoords.remove(getNumCoords() - 1);
196
            addPoint(point);
197
        } else {
198
            needRoom(1, 2, false);
199
            pointTypes.add(SEG_TYPES[SEG_MOVETO]);
200
            addPoint(point);
201
        }
202
    }
203

    
204
    /**
205
     * Adds a point to the path by drawing a straight line from the
206
     * current coordinates to the new specified coordinates.
207
     *
208
     * @param x
209
     *            ,&nbsp;y the specified coordinates
210
     * @deprecated
211
     *             use lineTo(Point)
212
     */
213
    public synchronized void lineTo(double x, double y) {
214
        needRoom(1, 2, true);
215
        pointTypes.add(SEG_TYPES[SEG_LINETO]);
216
        addPoint(x, y);
217
    }
218

    
219
    public synchronized void lineTo(Point point) {
220
        needRoom(1, 2, true);
221
        pointTypes.add(SEG_TYPES[SEG_LINETO]);
222
        addPoint(point);
223
    }
224

    
225
    public synchronized void addSegment(Point[] segment) {
226
        if (segment != null && segment.length > 0) {
227
            needRoom(segment.length, 2 * segment.length, true);
228
            for (int i = 0; i < segment.length; i++) {
229
                pointTypes.add(SEG_TYPES[SEG_LINETO]);
230
                addPoint(segment[i]);
231
            }
232
        }
233
    }
234

    
235
    private void addPoint(double x, double y) {
236
            Point p = createPoint(x, y);
237
            pointCoords.add(p);
238
    }
239

    
240
    private Point createPoint(double x, double y) {
241
        Point p;
242
        try {
243
            if (is2Dz) {
244
                p = geomManager.createPoint(x, y, Geometry.SUBTYPES.GEOM3D);
245
                p.setCoordinateAt(Geometry.DIMENSIONS.Z, zValue);
246
            } else {
247
                p = geomManager.createPoint(x, y, Geometry.SUBTYPES.GEOM2D);
248
            }
249
        } catch (CreateGeometryException e) {
250
            LOG.warn("Error creating a point", e);
251
            throw new RuntimeException(e.getMessage(), e);
252
        }
253
        return p;
254
    }
255

    
256
    private void addPoint(Point point) {
257
        if(is2Dz){
258
            pointCoords.add(createPoint(point.getX(), point.getY()));
259
                return;
260
        }
261
        if(point instanceof Point2D) {
262
            pointCoords.add(point);
263
            return;
264
        }
265
        pointCoords.add(createPoint(point.getX(), point.getY()));
266
    }
267

    
268
    /**
269
     * Adds a curved segment, defined by two new points, to the path by
270
     * drawing a Quadratic curve that intersects both the current
271
     * coordinates and the coordinates (x2,&nbsp;y2), using the
272
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
273
     * point.
274
     *
275
     * @param x1
276
     *            ,&nbsp;y1 the coordinates of the first quadratic control
277
     *            point
278
     * @param x2
279
     *            ,&nbsp;y2 the coordinates of the final endpoint
280
     * @deprecated
281
     *             use quadTo(Point, Point)
282
     */
283
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
284
        needRoom(1, 4, true);
285
        pointTypes.add(SEG_TYPES[SEG_QUADTO]);
286
        addPoint(x1, y1);
287
        addPoint(x2, y2);
288
        isSimple = false;
289
    }
290

    
291
    public synchronized void quadTo(Point point1, Point point2) {
292
        needRoom(1, 4, true);
293
        pointTypes.add(SEG_TYPES[SEG_QUADTO]);
294
        addPoint(point1);
295
        addPoint(point2);
296
        isSimple = false;
297
    }
298

    
299
    /**
300
     * Adds a curved segment, defined by three new points, to the path by
301
     * drawing a B&eacute;zier curve that intersects both the current
302
     * coordinates and the coordinates (x3,&nbsp;y3), using the
303
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
304
     * B&eacute;zier control points.
305
     *
306
     * @param x1
307
     *            ,&nbsp;y1 the coordinates of the first B&eacute;ezier
308
     *            control point
309
     * @param x2
310
     *            ,&nbsp;y2 the coordinates of the second B&eacute;zier
311
     *            control point
312
     * @param x3
313
     *            ,&nbsp;y3 the coordinates of the final endpoint
314
     * @deprecated
315
     *             use curveTo(Point, Point, Point)
316
     */
317
    public synchronized void curveTo(double x1, double y1, double x2,
318
        double y2, double x3, double y3) {
319
        needRoom(1, 6, true);
320
        pointTypes.add(SEG_TYPES[SEG_CUBICTO]);
321
        addPoint(x1, y1);
322
        addPoint(x2, y2);
323
        addPoint(x3, y3);
324
        isSimple = false;
325
    }
326

    
327
    public synchronized void curveTo(Point point1, Point point2, Point point3) {
328
        needRoom(1, 6, true);
329
        pointTypes.add(SEG_TYPES[SEG_CUBICTO]);
330
        addPoint(point1);
331
        addPoint(point2);
332
        addPoint(point3);
333
        isSimple = false;
334
    }
335

    
336
    /**
337
     * Closes the current subpath by drawing a straight line back to
338
     * the coordinates of the last <code>moveTo</code>. If the path is already
339
     * closed then this method has no effect.
340
     */
341
    public synchronized void closePath() {
342
        if (getNumTypes() == 0 || getTypeAt(getNumTypes() - 1) != SEG_CLOSE) {
343
            needRoom(1, 0, true);
344
            // Adding a geometry like the last geometry
345
            // addPoint(100, 100);
346
            pointTypes.add(SEG_TYPES[SEG_CLOSE]);
347
        }
348
    }
349

    
350
    /**
351
     * Check if the first part is closed.
352
     *
353
     * @return
354
     */
355
    public boolean isClosed() {
356
        PathIterator theIterator =
357
            getPathIterator(null, geomManager.getFlatness());
358
        double[] theData = new double[6];
359
        double xFinal = 0;
360
        double yFinal = 0;
361
        double xIni = 0;
362
        double yIni = 0;
363
        boolean first = true;
364

    
365
        while (!theIterator.isDone()) {
366
            // while not done
367
            int theType = theIterator.currentSegment(theData);
368

    
369
            switch (theType) {
370
            case PathIterator.SEG_MOVETO:
371
                xIni = theData[0];
372
                yIni = theData[1];
373
                if (!first) {
374
                    break;
375
                }
376
                first = false;
377
                break;
378

    
379
            case PathIterator.SEG_LINETO:
380
                xFinal = theData[0];
381
                yFinal = theData[1];
382
                break;
383
            case PathIterator.SEG_CLOSE:
384
                return true;
385

    
386
            } // end switch
387

    
388
            theIterator.next();
389
        }
390
        if ((xFinal == xIni) && (yFinal == yIni))
391
            return true;
392
        return false;
393
    }
394

    
395
    /**
396
     * Appends the geometry of the specified {@link PathIterator} object
397
     * to the path, possibly connecting the new geometry to the existing
398
     * path segments with a line segment.
399
     * If the <code>connect</code> parameter is <code>true</code> and the
400
     * path is not empty then any initial <code>moveTo</code> in the
401
     * geometry of the appended <code>Shape</code> is turned into a
402
     * <code>lineTo</code> segment.
403
     * If the destination coordinates of such a connecting <code>lineTo</code>
404
     * segment match the ending coordinates of a currently open
405
     * subpath then the segment is omitted as superfluous.
406
     * The winding rule of the specified <code>Shape</code> is ignored
407
     * and the appended geometry is governed by the winding
408
     * rule specified for this path.
409
     *
410
     * @param pi
411
     *            the <code>PathIterator</code> whose geometry is appended to
412
     *            this path
413
     * @param connect
414
     *            a boolean to control whether or not to turn an
415
     *            initial <code>moveTo</code> segment into a <code>lineTo</code>
416
     *            segment
417
     *            to connect the new geometry to the existing path
418
     */
419
    public void append(PathIterator pi, boolean connect) {
420
        double coords[] = new double[6];
421
        while (!pi.isDone()) {
422
            switch (pi.currentSegment(coords)) {
423
            case SEG_MOVETO:
424
                if (!connect || getNumTypes() < 1 || getNumCoords() < 2) {
425
                    moveTo(coords[0], coords[1]);
426
                    break;
427
                }
428
                if (getTypeAt(getNumTypes() - 1) != SEG_CLOSE
429
                    && getPointAt(getNumCoords() - 1).getX() == coords[0]
430
                    && getPointAt(getNumCoords() - 1).getY() == coords[1]) {
431
                    // Collapse out initial moveto/lineto
432
                    break;
433
                }
434
                // NO BREAK;
435
            case SEG_LINETO:
436
                lineTo(coords[0], coords[1]);
437
                break;
438
            case SEG_QUADTO:
439
                quadTo(coords[0], coords[1], coords[2], coords[3]);
440
                break;
441
            case SEG_CUBICTO:
442
                curveTo(coords[0], coords[1], coords[2], coords[3], coords[4],
443
                    coords[5]);
444
                break;
445
            case SEG_CLOSE:
446
                closePath();
447
                break;
448
            }
449
            pi.next();
450
            connect = false;
451
        }
452
    }
453

    
454
    public void append(GeneralPathX gp) {
455
        for( int i=0 ; i<gp.getNumCoords(); i++ ) {
456
            byte type = gp.getTypeAt(i);
457
            Point point = gp.getPointAt(i);
458
            pointTypes.add(SEG_TYPES[type]);
459
            addPoint(point);
460
        }
461
    }
462

    
463
    /**
464
     * Returns the fill style winding rule.
465
     *
466
     * @return an integer representing the current winding rule.
467
     * @see #WIND_EVEN_ODD
468
     * @see #WIND_NON_ZERO
469
     * @see #setWindingRule
470
     */
471
    public synchronized int getWindingRule() {
472
        return windingRule;
473
    }
474

    
475
    /**
476
     * Sets the winding rule for this path to the specified value.
477
     *
478
     * @param rule
479
     *            an integer representing the specified
480
     *            winding rule
481
     * @exception <code>IllegalArgumentException</code> if <code>rule</code> is
482
     *            not either <code>WIND_EVEN_ODD</code> or
483
     *            <code>WIND_NON_ZERO</code>
484
     * @see #WIND_EVEN_ODD
485
     * @see #WIND_NON_ZERO
486
     * @see #getWindingRule
487
     */
488
    public void setWindingRule(int rule) {
489
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
490
            throw new IllegalArgumentException("winding rule must be "
491
                + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
492
        }
493
        windingRule = rule;
494
    }
495

    
496
    /**
497
     * Returns the coordinates most recently added to the end of the path
498
     * as a {@link Point2D} object.
499
     *
500
     * @return a <code>Point2D</code> object containing the ending
501
     *         coordinates of the path or <code>null</code> if there are no
502
     *         points
503
     *         in the path.
504
     */
505
    public synchronized Point2D getCurrentPoint() {
506
        if (getNumTypes() < 1 || getNumCoords() < 1) {
507
            return null;
508
        }
509
        int index = getNumCoords();
510
        if (getTypeAt(getNumTypes() - 1) == SEG_CLOSE) {
511
            loop: for (int i = getNumTypes() - 2; i > 0; i--) {
512
                switch (getTypeAt(i)) {
513
                case SEG_MOVETO:
514
                    break loop;
515
                case SEG_LINETO:
516
                    index -= 2;
517
                    break;
518
                case SEG_QUADTO:
519
                    index -= 4;
520
                    break;
521
                case SEG_CUBICTO:
522
                    index -= 6;
523
                    break;
524
                case SEG_CLOSE:
525
                    break;
526
                }
527
            }
528
        }
529
        return new Point2D.Double(getPointAt(index - 1).getX(), getPointAt(
530
            index - 1).getY());
531
    }
532

    
533
    /**
534
     * Resets the path to empty. The append position is set back to the
535
     * beginning of the path and all coordinates and point types are
536
     * forgotten.
537
     */
538
    public synchronized void reset() {
539
        pointCoords.clear();
540
        pointTypes.clear();
541
    }
542

    
543
    /**
544
     * Transforms the geometry of this path using the specified
545
     * {@link AffineTransform}.
546
     * The geometry is transformed in place, which permanently changes the
547
     * boundary defined by this object.
548
     *
549
     * @param at
550
     *            the <code>AffineTransform</code> used to transform the area
551
     */
552
    public void transform(AffineTransform at) {
553
        for (int i = 0; i < getNumCoords(); i++) {
554
            getPointAt(i).transform(at);
555
        }
556
    }
557

    
558
    public void reProject(ICoordTrans ct) {
559
        for (int i = 0; i < getNumCoords(); i++) {
560
            getPointAt(i).reProject(ct);
561
        }
562
    }
563

    
564
    /**
565
     * Returns a new transformed <code>Shape</code>.
566
     *
567
     * @param at
568
     *            the <code>AffineTransform</code> used to transform a
569
     *            new <code>Shape</code>.
570
     * @return a new <code>Shape</code>, transformed with the specified
571
     *         <code>AffineTransform</code>.
572
     */
573
    public synchronized Shape createTransformedShape(AffineTransform at) {
574
        DefaultGeneralPathX gp = (DefaultGeneralPathX) clone();
575
        if (at != null) {
576
            gp.transform(at);
577
        }
578
        return gp;
579
    }
580

    
581
    /**
582
     * Return the bounding box of the path.
583
     *
584
     * @return a {@link java.awt.Rectangle} object that
585
     *         bounds the current path.
586
     */
587
    public java.awt.Rectangle getBounds() {
588
        return getBounds2D().getBounds();
589
    }
590

    
591
    /**
592
     * Returns the bounding box of the path.
593
     *
594
     * @return a {@link Rectangle2D} object that
595
     *         bounds the current path.
596
     */
597
    public synchronized Rectangle2D getBounds2D() {
598
        double x1, y1, x2, y2;
599
        int i = getNumCoords();
600
        if (i > 0) {
601
            y1 = y2 = getPointAt(--i).getY();
602
            x1 = x2 = getPointAt(i).getX();
603
            while (i > 0) {
604
                double y = getPointAt(--i).getY();
605
                double x = getPointAt(i).getX();
606
                if (x < x1)
607
                    x1 = x;
608
                if (y < y1)
609
                    y1 = y;
610
                if (x > x2)
611
                    x2 = x;
612
                if (y > y2)
613
                    y2 = y;
614
            }
615
        } else {
616
            x1 = y1 = x2 = y2 = 0.0f;
617
        }
618
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
619
    }
620

    
621
    /**
622
     * Tests if the specified coordinates are inside the boundary of
623
     * this <code>Shape</code>.
624
     *
625
     * @param x
626
     *            ,&nbsp;y the specified coordinates
627
     * @return <code>true</code> if the specified coordinates are inside this
628
     *         <code>Shape</code>; <code>false</code> otherwise
629
     */
630
    public boolean contains(double x, double y) {
631
        if (pointTypes.size() < 2) {
632
            return false;
633
        }
634
        int cross =
635
            GeomUtilities.pointCrossingsForPath(getPathIterator(null), x, y);
636
        if (windingRule == WIND_NON_ZERO) {
637
            return (cross != 0);
638
        } else {
639
            return ((cross & 1) != 0);
640
        }
641
    }
642

    
643
    /**
644
     * Tests if the specified <code>Point2D</code> is inside the boundary
645
     * of this <code>Shape</code>.
646
     *
647
     * @param p
648
     *            the specified <code>Point2D</code>
649
     * @return <code>true</code> if this <code>Shape</code> contains the
650
     *         specified <code>Point2D</code>, <code>false</code> otherwise.
651
     */
652
    public boolean contains(Point2D p) {
653
        return contains(p.getX(), p.getY());
654
    }
655

    
656
    /**
657
     * Tests if the specified rectangular area is inside the boundary of
658
     * this <code>Shape</code>.
659
     *
660
     * @param x
661
     *            ,&nbsp;y the specified coordinates
662
     * @param w
663
     *            the width of the specified rectangular area
664
     * @param h
665
     *            the height of the specified rectangular area
666
     * @return <code>true</code> if this <code>Shape</code> contains
667
     *         the specified rectangluar area; <code>false</code> otherwise.
668
     */
669
    public boolean contains(double x, double y, double w, double h) {
670
        return GeomUtilities
671
            .contains(getPathIterator(null), x, y, x + w, y + h);
672
    }
673

    
674
    /**
675
     * Tests if the specified <code>Rectangle2D</code> is inside the boundary of
676
     * this <code>Shape</code>.
677
     *
678
     * @param r
679
     *            a specified <code>Rectangle2D</code>
680
     * @return <code>true</code> if this <code>Shape</code> bounds the
681
     *         specified <code>Rectangle2D</code>; <code>false</code> otherwise.
682
     */
683
    public boolean contains(Rectangle2D r) {
684
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
685
    }
686

    
687
    /**
688
     * Tests if the interior of this <code>Shape</code> intersects the
689
     * interior of a specified set of rectangular coordinates.
690
     *
691
     * @param x
692
     *            ,&nbsp;y the specified coordinates
693
     * @param w
694
     *            the width of the specified rectangular coordinates
695
     * @param h
696
     *            the height of the specified rectangular coordinates
697
     * @return <code>true</code> if this <code>Shape</code> and the
698
     *         interior of the specified set of rectangular coordinates
699
     *         intersect
700
     *         each other; <code>false</code> otherwise.
701
     */
702
    public boolean intersects(double x, double y, double w, double h) {
703
        return GeomUtilities.intersects(getPathIterator(null), x, y, w, h);
704
    }
705

    
706
    /**
707
     * Tests if the interior of this <code>Shape</code> intersects the
708
     * interior of a specified <code>Rectangle2D</code>.
709
     *
710
     * @param r
711
     *            the specified <code>Rectangle2D</code>
712
     * @return <code>true</code> if this <code>Shape</code> and the interior
713
     *         of the specified <code>Rectangle2D</code> intersect each
714
     *         other; <code>false</code> otherwise.
715
     */
716
    public boolean intersects(Rectangle2D r) {
717
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
718
    }
719

    
720
    /**
721
     * Returns a <code>PathIterator</code> object that iterates along the
722
     * boundary of this <code>Shape</code> and provides access to the
723
     * geometry of the outline of this <code>Shape</code>.
724
     * The iterator for this class is not multi-threaded safe,
725
     * which means that this <code>GeneralPathX</code> class does not
726
     * guarantee that modifications to the geometry of this
727
     * <code>GeneralPathX</code> object do not affect any iterations of
728
     * that geometry that are already in process.
729
     *
730
     * @param at
731
     *            an <code>AffineTransform</code>
732
     * @return a new <code>PathIterator</code> that iterates along the
733
     *         boundary of this <code>Shape</code> and provides access to the
734
     *         geometry of this <code>Shape</code>'s outline
735
     */
736
    public PathIterator getPathIterator(AffineTransform at) {
737
        if (isSimple) {
738
            return new GeneralPathXIteratorSimple(this, at);
739
        } else {
740
            return new GeneralPathXIterator(this, at);
741
        }
742
    }
743

    
744
    /**
745
     * Returns a <code>PathIterator</code> object that iterates along the
746
     * boundary of the flattened <code>Shape</code> and provides access to the
747
     * geometry of the outline of the <code>Shape</code>.
748
     * The iterator for this class is not multi-threaded safe,
749
     * which means that this <code>GeneralPathX</code> class does not
750
     * guarantee that modifications to the geometry of this
751
     * <code>GeneralPathX</code> object do not affect any iterations of
752
     * that geometry that are already in process.
753
     *
754
     * @param at
755
     *            an <code>AffineTransform</code>
756
     * @param flatness
757
     *            the maximum distance that the line segments used to
758
     *            approximate the curved segments are allowed to deviate
759
     *            from any point on the original curve
760
     * @return a new <code>PathIterator</code> that iterates along the flattened
761
     *         <code>Shape</code> boundary.
762
     */
763
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
764
        return new FlatteningPathIterator(getPathIterator(at), flatness);
765
    }
766

    
767
    /**
768
     * Creates a new object of the same class as this object.
769
     *
770
     * @return a clone of this instance.
771
     * @exception OutOfMemoryError
772
     *                if there is not enough memory.
773
     * @see java.lang.Cloneable
774
     * @since 1.2
775
     */
776
    public Object clone() {
777
        DefaultGeneralPathX copy = new DefaultGeneralPathX();
778
        copy.windingRule = windingRule;
779
        copy.isSimple = isSimple;
780
        for (int i = 0; i < getNumTypes(); i++) {
781
            copy.pointTypes.add(pointTypes.get(i));
782
        }
783
        for (int i = 0; i < getNumCoords(); i++) {
784
            copy.addPoint((Point) getPointAt(i).cloneGeometry());
785
        }
786
        return copy;
787

    
788
    }
789

    
790
    DefaultGeneralPathX(int windingRule, byte[] pointTypes, int numTypes,
791
        double[] pointCoords, int numCoords) {
792

    
793
        // used to construct from native
794
        super(false);
795

    
796
        this.windingRule = windingRule;
797
        this.setPointTypes(pointTypes);
798
        this.setNumTypes(numTypes);
799
        this.setPointCoords(pointCoords);
800
        this.setNumCoords(numCoords);
801
    }
802

    
803
    public void setNumTypes(int numTypes) {
804

    
805
    }
806

    
807
    public int getNumTypes() {
808
        return pointTypes.size();
809
    }
810

    
811
    public int setNumCoords(int numCoords) {
812
        return pointCoords.size();
813
    }
814

    
815
    public int getNumCoords() {
816
        return pointCoords.size();
817
    }
818

    
819
    public byte getTypeAt(int index) {
820
        return ((Byte) pointTypes.get(index)).byteValue();
821
    }
822

    
823
    /**
824
     * @deprecated
825
     *             use the geometry methods.
826
     */
827
    public void setPointTypes(byte[] pointTypes) {
828
        this.pointTypes.clear();
829
        for (int i = 0; i < pointTypes.length; i++) {
830
            this.pointTypes.add(SEG_TYPES[pointTypes[i]]);
831
        }
832
    }
833

    
834
    /**
835
     * @deprecated
836
     *             use the geometry methods.
837
     */
838
    public byte[] getPointTypes() {
839
        byte[] bytes = new byte[pointTypes.size()];
840
        for (int i = 0; i < pointTypes.size(); i++) {
841
            bytes[i] = ((Byte) pointTypes.get(i)).byteValue();
842
        }
843
        return bytes;
844
    }
845

    
846
    /**
847
     * @param pointCoords
848
     * @deprecated
849
     *             use the geometry methods.
850
     */
851
    public void setPointCoords(double[] pointCoords) {
852
        this.pointCoords.clear();
853
        for (int i = 0; i < pointCoords.length; i = i + 2) {
854
            addPoint(pointCoords[i], pointCoords[i + 1]);
855
        }
856
    }
857

    
858
    /**
859
     * @deprecated
860
     *             use the geometry methods.
861
     */
862
    public double[] getPointCoords() {
863
        double[] doubles = new double[pointCoords.size() * 2];
864
        for (int i = 0; i < getNumCoords(); i++) {
865
            doubles[i * 2] = getPointAt(i).getX();
866
            doubles[(i * 2) + 1] = getPointAt(i).getY();
867
        }
868
        return doubles;
869
    }
870

    
871
    public Point getPointAt(int index) {
872
        return (Point) pointCoords.get(index);
873
    }
874

    
875
    public double[] getCoordinatesAt(int index) {
876
        return getPointAt(index).getCoordinates();
877
    }
878

    
879
    public double[] get3DCoordinatesAt(int index) {
880
            Point p = getPointAt(index);
881
            if(p instanceof Point3D) {
882
                    return p.getCoordinates();
883
            }
884
            double[] coords = new double[3];
885
            coords[0] = p.getX();
886
            coords[1] = p.getY();
887
            coords[2] = 0D;
888
            return coords;
889
    }
890

    
891
    /**
892
     * Convertimos el path a puntos y luego le damos la vuelta.
893
     */
894
    public void flip() {
895
        PathIterator theIterator =
896
            getPathIterator(null, geomManager.getFlatness());
897
        double[] theData = new double[6];
898
        CoordinateList coordList = new CoordinateList();
899
        Coordinate c1;
900
        DefaultGeneralPathX newGp = new DefaultGeneralPathX();
901
        ArrayList listOfParts = new ArrayList();
902
        while (!theIterator.isDone()) {
903
            // while not done
904
            int type = theIterator.currentSegment(theData);
905
            switch (type) {
906
            case SEG_MOVETO:
907
                coordList = new CoordinateList();
908
                listOfParts.add(coordList);
909
                c1 = new Coordinate(theData[0], theData[1]);
910
                coordList.add(c1, true);
911
                break;
912
            case SEG_LINETO:
913
                c1 = new Coordinate(theData[0], theData[1]);
914
                coordList.add(c1, true);
915
                break;
916

    
917
            case SEG_CLOSE:
918
                coordList.add(coordList.getCoordinate(0));
919
                break;
920

    
921
            }
922
            theIterator.next();
923
        }
924

    
925
        for (int i = listOfParts.size() - 1; i >= 0; i--) {
926
            coordList = (CoordinateList) listOfParts.get(i);
927
            Coordinate[] coords = coordList.toCoordinateArray();
928
            CoordinateArraySequence seq = new CoordinateArraySequence(coords);
929
            CoordinateSequences.reverse(seq);
930
            coords = seq.toCoordinateArray();
931
            newGp.moveTo(coords[0].x, coords[0].y);
932
            for (int j = 1; j < coords.length; j++) {
933
                newGp.lineTo(coords[j].x, coords[j].y);
934
            }
935
        }
936
        reset();
937
        append(newGp.getPathIterator(null), false);
938
    }
939

    
940
    /**
941
     * Check if the first part is CCW.
942
     *
943
     * @return
944
     */
945
    public boolean isCCW() {
946
        PathIterator theIterator =
947
            getPathIterator(null, geomManager.getFlatness()); // polyLine.getPathIterator(null,
948
                                                              // flatness);
949
        double[] theData = new double[6];
950
        Coordinate first = null;
951
        CoordinateList coordList = new CoordinateList();
952
        Coordinate c1;
953
        boolean bFirst = true;
954
        while (!theIterator.isDone()) {
955
            // while not done
956
            int type = theIterator.currentSegment(theData);
957
            switch (type) {
958
            case SEG_MOVETO:
959
                c1 = new Coordinate(theData[0], theData[1]);
960
                if (bFirst == false) // Ya tenemos la primera parte.
961
                    break;
962
                if (bFirst) {
963
                    bFirst = false;
964
                    first = c1;
965
                }
966
                coordList.add(c1, true);
967
                break;
968
            case SEG_LINETO:
969
                c1 = new Coordinate(theData[0], theData[1]);
970
                coordList.add(c1, true);
971
                break;
972

    
973
            }
974
            theIterator.next();
975
        }
976
        coordList.add(first, true);
977
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
978
    }
979

    
980
    /**
981
     * @return the isSimple
982
     */
983
    public boolean isSimple() {
984
        return isSimple;
985
    }
986

    
987
    public void ensureCapacity(int capacity) {
988

    
989
    }
990
}