Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.impl / src / main / java / org / gvsig / fmap / geom / primitive / DefaultGeneralPathX.java @ 40559

History | View | Annotate | Download (30.6 KB)

1
/**
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.primitive;
25

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

    
40
import org.cresques.cts.ICoordTrans;
41
import org.gvsig.fmap.geom.Geometry;
42
import org.gvsig.fmap.geom.GeometryLocator;
43
import org.gvsig.fmap.geom.GeometryManager;
44
import org.gvsig.fmap.geom.exception.CreateGeometryException;
45
import org.gvsig.fmap.geom.primitive.impl.Point2DZ;
46
import org.gvsig.jdk.GeomUtilities;
47
import org.slf4j.Logger;
48
import org.slf4j.LoggerFactory;
49

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

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

    
81
    /**
82
     * Default serial version ID
83
     */
84
    private static final long serialVersionUID = 1L;
85

    
86
    private static final Logger LOG = LoggerFactory
87
        .getLogger(DefaultGeneralPathX.class);
88

    
89
    protected static GeometryManager geomManager = GeometryLocator
90
        .getGeometryManager();
91

    
92
    private List pointTypes = new ArrayList();
93
    private List pointCoords = new ArrayList();
94

    
95
    int windingRule;
96

    
97
    private boolean isSimple = true;
98

    
99
    static final int EXPAND_MAX = 500;
100

    
101
    private  DefaultGeneralPathX() {
102
            super(true);
103
        setWindingRule(WIND_EVEN_ODD);
104
    }
105
    /**
106
     * Constructs a new <code>GeneralPathX</code> object with the specified
107
     * winding rule to control operations that require the interior of the
108
     * path to be defined.
109
     * 
110
     * @param rule
111
     *            the winding rule
112
     * @see #WIND_EVEN_ODD
113
     * @see #WIND_NON_ZERO
114
     */
115
    public DefaultGeneralPathX(int rule) {
116
            super(true);
117
        setWindingRule(rule);
118
    }
119

    
120
    /**
121
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
122
     * {@link Shape} object.
123
     * All of the initial geometry and the winding rule for this path are
124
     * taken from the specified <code>Shape</code> object.
125
     * 
126
     * @param s
127
     *            the specified <code>Shape</code> object
128
     */
129
    public DefaultGeneralPathX(PathIterator piter) {
130
        this(WIND_EVEN_ODD);
131
        setWindingRule(piter.getWindingRule());
132
        append(piter, false);
133
    }
134

    
135
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
136
        if (needMove && getNumTypes() == 0) {
137
            throw new IllegalPathStateException("missing initial moveto "
138
                + "in path definition");
139
        }
140
    }
141

    
142
    /**
143
     * Adds a point to the path by moving to the specified
144
     * coordinates.
145
     * 
146
     * @param x
147
     *            ,&nbsp;y the specified coordinates
148
     * @deprecated
149
     *             use moveTo(Point)
150
     */
151
    public synchronized void moveTo(double x, double y) {
152
        int numtypes = getNumTypes();
153
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
154
            Point point = getPointAt(getNumCoords() - 1);
155
            point.setX(x);
156
            point.setY(y);
157
        } else {
158
            needRoom(1, 2, false);
159
            pointTypes.add(new Byte(SEG_MOVETO));
160
            addPoint(x, y);
161
        }
162
    }
163

    
164
    public synchronized void moveTo(Point point) {
165
        int numtypes = getNumTypes();
166
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
167
            pointCoords.remove(getNumCoords() - 1);
168
            addPoint(point);
169
        } else {
170
            needRoom(1, 2, false);
171
            pointTypes.add(new Byte(SEG_MOVETO));
172
            addPoint(point);
173
        }
174
    }
175

    
176
    /**
177
     * Adds a point to the path by drawing a straight line from the
178
     * current coordinates to the new specified coordinates.
179
     * 
180
     * @param x
181
     *            ,&nbsp;y the specified coordinates
182
     * @deprecated
183
     *             use lineTo(Point)
184
     */
185
    public synchronized void lineTo(double x, double y) {
186
        needRoom(1, 2, true);
187
        pointTypes.add(new Byte(SEG_LINETO));
188
        addPoint(x, y);
189
    }
190

    
191
    public synchronized void lineTo(Point point) {
192
        needRoom(1, 2, true);
193
        pointTypes.add(new Byte(SEG_LINETO));
194
        addPoint(point);
195
    }
196

    
197
    public synchronized void addSegment(Point[] segment) {
198
        if (segment != null && segment.length > 0) {
199
            needRoom(segment.length, 2 * segment.length, true);
200
            for (int i = 0; i < segment.length; i++) {
201
                pointTypes.add(new Byte(SEG_LINETO));
202
                addPoint(segment[i]);
203
            }
204
        }
205
    }
206

    
207
    private void addPoint(double x, double y) {
208
        try {
209
            pointCoords.add(geomManager.createPoint(x, y,
210
                Geometry.SUBTYPES.GEOM2D));
211
        } catch (CreateGeometryException e) {
212
            LOG.error("Error creating a point", e);
213
        }
214
    }
215

    
216
    private void addPoint(Point point) {
217
        pointCoords.add(point);
218
    }
219

    
220
    /**
221
     * Adds a curved segment, defined by two new points, to the path by
222
     * drawing a Quadratic curve that intersects both the current
223
     * coordinates and the coordinates (x2,&nbsp;y2), using the
224
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
225
     * point.
226
     * 
227
     * @param x1
228
     *            ,&nbsp;y1 the coordinates of the first quadratic control
229
     *            point
230
     * @param x2
231
     *            ,&nbsp;y2 the coordinates of the final endpoint
232
     * @deprecated
233
     *             use quadTo(Point, Point)
234
     */
235
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
236
        needRoom(1, 4, true);
237
        pointTypes.add(new Byte(SEG_QUADTO));
238
        addPoint(x1, y1);
239
        addPoint(x2, y2);
240
        isSimple = false;
241
    }
242

    
243
    public synchronized void quadTo(Point point1, Point point2) {
244
        needRoom(1, 4, true);
245
        pointTypes.add(new Byte(SEG_QUADTO));
246
        addPoint(point1);
247
        addPoint(point2);
248
        isSimple = false;
249
    }
250

    
251
    /**
252
     * Adds a curved segment, defined by three new points, to the path by
253
     * drawing a B&eacute;zier curve that intersects both the current
254
     * coordinates and the coordinates (x3,&nbsp;y3), using the
255
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
256
     * B&eacute;zier control points.
257
     * 
258
     * @param x1
259
     *            ,&nbsp;y1 the coordinates of the first B&eacute;ezier
260
     *            control point
261
     * @param x2
262
     *            ,&nbsp;y2 the coordinates of the second B&eacute;zier
263
     *            control point
264
     * @param x3
265
     *            ,&nbsp;y3 the coordinates of the final endpoint
266
     * @deprecated
267
     *             use curveTo(Point, Point, Point)
268
     */
269
    public synchronized void curveTo(double x1, double y1, double x2,
270
        double y2, double x3, double y3) {
271
        needRoom(1, 6, true);
272
        pointTypes.add(new Byte(SEG_CUBICTO));
273
        addPoint(x1, y1);
274
        addPoint(x2, y2);
275
        addPoint(x3, y3);
276
        isSimple = false;
277
    }
278

    
279
    public synchronized void curveTo(Point point1, Point point2, Point point3) {
280
        needRoom(1, 6, true);
281
        pointTypes.add(new Byte(SEG_CUBICTO));
282
        addPoint(point1);
283
        addPoint(point2);
284
        addPoint(point3);
285
        isSimple = false;
286
    }
287

    
288
    /**
289
     * Closes the current subpath by drawing a straight line back to
290
     * the coordinates of the last <code>moveTo</code>. If the path is already
291
     * closed then this method has no effect.
292
     */
293
    public synchronized void closePath() {
294
        if (getNumTypes() == 0 || getTypeAt(getNumTypes() - 1) != SEG_CLOSE) {
295
            needRoom(1, 0, true);
296
            // Adding a geometry like the last geometry
297
            // addPoint(100, 100);
298
            pointTypes.add(new Byte(SEG_CLOSE));
299
        }
300
    }
301

    
302
    /**
303
     * Check if the first part is closed.
304
     * 
305
     * @return
306
     */
307
    public boolean isClosed() {
308
        PathIterator theIterator =
309
            getPathIterator(null, geomManager.getFlatness());
310
        double[] theData = new double[6];
311
        double xFinal = 0;
312
        double yFinal = 0;
313
        double xIni = 0;
314
        double yIni = 0;
315
        boolean first = true;
316

    
317
        while (!theIterator.isDone()) {
318
            // while not done
319
            int theType = theIterator.currentSegment(theData);
320

    
321
            switch (theType) {
322
            case PathIterator.SEG_MOVETO:
323
                xIni = theData[0];
324
                yIni = theData[1];
325
                if (!first) {
326
                    break;
327
                }
328
                first = false;
329
                break;
330

    
331
            case PathIterator.SEG_LINETO:
332
                xFinal = theData[0];
333
                yFinal = theData[1];
334
                break;
335
            case PathIterator.SEG_CLOSE:
336
                return true;
337

    
338
            } // end switch
339

    
340
            theIterator.next();
341
        }
342
        if ((xFinal == xIni) && (yFinal == yIni))
343
            return true;
344
        return false;
345
    }
346

    
347
    /**
348
     * Appends the geometry of the specified {@link PathIterator} object
349
     * to the path, possibly connecting the new geometry to the existing
350
     * path segments with a line segment.
351
     * If the <code>connect</code> parameter is <code>true</code> and the
352
     * path is not empty then any initial <code>moveTo</code> in the
353
     * geometry of the appended <code>Shape</code> is turned into a
354
     * <code>lineTo</code> segment.
355
     * If the destination coordinates of such a connecting <code>lineTo</code>
356
     * segment match the ending coordinates of a currently open
357
     * subpath then the segment is omitted as superfluous.
358
     * The winding rule of the specified <code>Shape</code> is ignored
359
     * and the appended geometry is governed by the winding
360
     * rule specified for this path.
361
     * 
362
     * @param pi
363
     *            the <code>PathIterator</code> whose geometry is appended to
364
     *            this path
365
     * @param connect
366
     *            a boolean to control whether or not to turn an
367
     *            initial <code>moveTo</code> segment into a <code>lineTo</code>
368
     *            segment
369
     *            to connect the new geometry to the existing path
370
     */
371
    public void append(PathIterator pi, boolean connect) {
372
        double coords[] = new double[6];
373
        while (!pi.isDone()) {
374
            switch (pi.currentSegment(coords)) {
375
            case SEG_MOVETO:
376
                if (!connect || getNumTypes() < 1 || getNumCoords() < 2) {
377
                    moveTo(coords[0], coords[1]);
378
                    break;
379
                }
380
                if (getTypeAt(getNumTypes() - 1) != SEG_CLOSE
381
                    && getPointAt(getNumCoords() - 1).getX() == coords[0]
382
                    && getPointAt(getNumCoords() - 1).getY() == coords[1]) {
383
                    // Collapse out initial moveto/lineto
384
                    break;
385
                }
386
                // NO BREAK;
387
            case SEG_LINETO:
388
                lineTo(coords[0], coords[1]);
389
                break;
390
            case SEG_QUADTO:
391
                quadTo(coords[0], coords[1], coords[2], coords[3]);
392
                break;
393
            case SEG_CUBICTO:
394
                curveTo(coords[0], coords[1], coords[2], coords[3], coords[4],
395
                    coords[5]);
396
                break;
397
            case SEG_CLOSE:
398
                closePath();
399
                break;
400
            }
401
            pi.next();
402
            connect = false;
403
        }
404
    }
405

    
406
    /**
407
     * Returns the fill style winding rule.
408
     * 
409
     * @return an integer representing the current winding rule.
410
     * @see #WIND_EVEN_ODD
411
     * @see #WIND_NON_ZERO
412
     * @see #setWindingRule
413
     */
414
    public synchronized int getWindingRule() {
415
        return windingRule;
416
    }
417

    
418
    /**
419
     * Sets the winding rule for this path to the specified value.
420
     * 
421
     * @param rule
422
     *            an integer representing the specified
423
     *            winding rule
424
     * @exception <code>IllegalArgumentException</code> if <code>rule</code> is
425
     *            not either <code>WIND_EVEN_ODD</code> or
426
     *            <code>WIND_NON_ZERO</code>
427
     * @see #WIND_EVEN_ODD
428
     * @see #WIND_NON_ZERO
429
     * @see #getWindingRule
430
     */
431
    public void setWindingRule(int rule) {
432
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
433
            throw new IllegalArgumentException("winding rule must be "
434
                + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
435
        }
436
        windingRule = rule;
437
    }
438

    
439
    /**
440
     * Returns the coordinates most recently added to the end of the path
441
     * as a {@link Point2D} object.
442
     * 
443
     * @return a <code>Point2D</code> object containing the ending
444
     *         coordinates of the path or <code>null</code> if there are no
445
     *         points
446
     *         in the path.
447
     */
448
    public synchronized Point2D getCurrentPoint() {
449
        if (getNumTypes() < 1 || getNumCoords() < 1) {
450
            return null;
451
        }
452
        int index = getNumCoords();
453
        if (getTypeAt(getNumTypes() - 1) == SEG_CLOSE) {
454
            loop: for (int i = getNumTypes() - 2; i > 0; i--) {
455
                switch (getTypeAt(i)) {
456
                case SEG_MOVETO:
457
                    break loop;
458
                case SEG_LINETO:
459
                    index -= 2;
460
                    break;
461
                case SEG_QUADTO:
462
                    index -= 4;
463
                    break;
464
                case SEG_CUBICTO:
465
                    index -= 6;
466
                    break;
467
                case SEG_CLOSE:
468
                    break;
469
                }
470
            }
471
        }
472
        return new Point2D.Double(getPointAt(index - 1).getX(), getPointAt(
473
            index - 1).getY());
474
    }
475

    
476
    /**
477
     * Resets the path to empty. The append position is set back to the
478
     * beginning of the path and all coordinates and point types are
479
     * forgotten.
480
     */
481
    public synchronized void reset() {
482
        pointCoords.clear();
483
        pointTypes.clear();
484
    }
485

    
486
    /**
487
     * Transforms the geometry of this path using the specified
488
     * {@link AffineTransform}.
489
     * The geometry is transformed in place, which permanently changes the
490
     * boundary defined by this object.
491
     * 
492
     * @param at
493
     *            the <code>AffineTransform</code> used to transform the area
494
     */
495
    public void transform(AffineTransform at) {
496
        for (int i = 0; i < getNumCoords(); i++) {
497
            getPointAt(i).transform(at);
498
        }
499
    }
500

    
501
    public void reProject(ICoordTrans ct) {
502
        for (int i = 0; i < getNumCoords(); i++) {
503
            getPointAt(i).reProject(ct);
504
        }
505
    }
506

    
507
    /**
508
     * Returns a new transformed <code>Shape</code>.
509
     * 
510
     * @param at
511
     *            the <code>AffineTransform</code> used to transform a
512
     *            new <code>Shape</code>.
513
     * @return a new <code>Shape</code>, transformed with the specified
514
     *         <code>AffineTransform</code>.
515
     */
516
    public synchronized Shape createTransformedShape(AffineTransform at) {
517
        DefaultGeneralPathX gp = (DefaultGeneralPathX) clone();
518
        if (at != null) {
519
            gp.transform(at);
520
        }
521
        return gp;
522
    }
523

    
524
    /**
525
     * Return the bounding box of the path.
526
     * 
527
     * @return a {@link java.awt.Rectangle} object that
528
     *         bounds the current path.
529
     */
530
    public java.awt.Rectangle getBounds() {
531
        return getBounds2D().getBounds();
532
    }
533

    
534
    /**
535
     * Returns the bounding box of the path.
536
     * 
537
     * @return a {@link Rectangle2D} object that
538
     *         bounds the current path.
539
     */
540
    public synchronized Rectangle2D getBounds2D() {
541
        double x1, y1, x2, y2;
542
        int i = getNumCoords();
543
        if (i > 0) {
544
            y1 = y2 = getPointAt(--i).getY();
545
            x1 = x2 = getPointAt(i).getX();
546
            while (i > 0) {
547
                double y = getPointAt(--i).getY();
548
                double x = getPointAt(i).getX();
549
                if (x < x1)
550
                    x1 = x;
551
                if (y < y1)
552
                    y1 = y;
553
                if (x > x2)
554
                    x2 = x;
555
                if (y > y2)
556
                    y2 = y;
557
            }
558
        } else {
559
            x1 = y1 = x2 = y2 = 0.0f;
560
        }
561
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
562
    }
563

    
564
    /**
565
     * Tests if the specified coordinates are inside the boundary of
566
     * this <code>Shape</code>.
567
     * 
568
     * @param x
569
     *            ,&nbsp;y the specified coordinates
570
     * @return <code>true</code> if the specified coordinates are inside this
571
     *         <code>Shape</code>; <code>false</code> otherwise
572
     */
573
    public boolean contains(double x, double y) {
574
        if (pointTypes.size() < 2) {
575
            return false;
576
        }
577
        int cross =
578
            GeomUtilities.pointCrossingsForPath(getPathIterator(null), x, y);
579
        if (windingRule == WIND_NON_ZERO) {
580
            return (cross != 0);
581
        } else {
582
            return ((cross & 1) != 0);
583
        }
584
    }
585

    
586
    /**
587
     * Tests if the specified <code>Point2D</code> is inside the boundary
588
     * of this <code>Shape</code>.
589
     * 
590
     * @param p
591
     *            the specified <code>Point2D</code>
592
     * @return <code>true</code> if this <code>Shape</code> contains the
593
     *         specified <code>Point2D</code>, <code>false</code> otherwise.
594
     */
595
    public boolean contains(Point2D p) {
596
        return contains(p.getX(), p.getY());
597
    }
598

    
599
    /**
600
     * Tests if the specified rectangular area is inside the boundary of
601
     * this <code>Shape</code>.
602
     * 
603
     * @param x
604
     *            ,&nbsp;y the specified coordinates
605
     * @param w
606
     *            the width of the specified rectangular area
607
     * @param h
608
     *            the height of the specified rectangular area
609
     * @return <code>true</code> if this <code>Shape</code> contains
610
     *         the specified rectangluar area; <code>false</code> otherwise.
611
     */
612
    public boolean contains(double x, double y, double w, double h) {
613
        return GeomUtilities
614
            .contains(getPathIterator(null), x, y, x + w, y + h);
615
    }
616

    
617
    /**
618
     * Tests if the specified <code>Rectangle2D</code> is inside the boundary of
619
     * this <code>Shape</code>.
620
     * 
621
     * @param r
622
     *            a specified <code>Rectangle2D</code>
623
     * @return <code>true</code> if this <code>Shape</code> bounds the
624
     *         specified <code>Rectangle2D</code>; <code>false</code> otherwise.
625
     */
626
    public boolean contains(Rectangle2D r) {
627
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
628
    }
629

    
630
    /**
631
     * Tests if the interior of this <code>Shape</code> intersects the
632
     * interior of a specified set of rectangular coordinates.
633
     * 
634
     * @param x
635
     *            ,&nbsp;y the specified coordinates
636
     * @param w
637
     *            the width of the specified rectangular coordinates
638
     * @param h
639
     *            the height of the specified rectangular coordinates
640
     * @return <code>true</code> if this <code>Shape</code> and the
641
     *         interior of the specified set of rectangular coordinates
642
     *         intersect
643
     *         each other; <code>false</code> otherwise.
644
     */
645
    public boolean intersects(double x, double y, double w, double h) {
646
        return GeomUtilities.intersects(getPathIterator(null), x, y, w, h);
647
    }
648

    
649
    /**
650
     * Tests if the interior of this <code>Shape</code> intersects the
651
     * interior of a specified <code>Rectangle2D</code>.
652
     * 
653
     * @param r
654
     *            the specified <code>Rectangle2D</code>
655
     * @return <code>true</code> if this <code>Shape</code> and the interior
656
     *         of the specified <code>Rectangle2D</code> intersect each
657
     *         other; <code>false</code> otherwise.
658
     */
659
    public boolean intersects(Rectangle2D r) {
660
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
661
    }
662

    
663
    /**
664
     * Returns a <code>PathIterator</code> object that iterates along the
665
     * boundary of this <code>Shape</code> and provides access to the
666
     * geometry of the outline of this <code>Shape</code>.
667
     * The iterator for this class is not multi-threaded safe,
668
     * which means that this <code>GeneralPathX</code> class does not
669
     * guarantee that modifications to the geometry of this
670
     * <code>GeneralPathX</code> object do not affect any iterations of
671
     * that geometry that are already in process.
672
     * 
673
     * @param at
674
     *            an <code>AffineTransform</code>
675
     * @return a new <code>PathIterator</code> that iterates along the
676
     *         boundary of this <code>Shape</code> and provides access to the
677
     *         geometry of this <code>Shape</code>'s outline
678
     */
679
    public PathIterator getPathIterator(AffineTransform at) {
680
        if (isSimple) {
681
            return new GeneralPathXIteratorSimple(this, at);
682
        } else {
683
            return new GeneralPathXIterator(this, at);
684
        }
685
    }
686

    
687
    /**
688
     * Returns a <code>PathIterator</code> object that iterates along the
689
     * boundary of the flattened <code>Shape</code> and provides access to the
690
     * geometry of the outline of the <code>Shape</code>.
691
     * The iterator for this class is not multi-threaded safe,
692
     * which means that this <code>GeneralPathX</code> class does not
693
     * guarantee that modifications to the geometry of this
694
     * <code>GeneralPathX</code> object do not affect any iterations of
695
     * that geometry that are already in process.
696
     * 
697
     * @param at
698
     *            an <code>AffineTransform</code>
699
     * @param flatness
700
     *            the maximum distance that the line segments used to
701
     *            approximate the curved segments are allowed to deviate
702
     *            from any point on the original curve
703
     * @return a new <code>PathIterator</code> that iterates along the flattened
704
     *         <code>Shape</code> boundary.
705
     */
706
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
707
        return new FlatteningPathIterator(getPathIterator(at), flatness);
708
    }
709

    
710
    /**
711
     * Creates a new object of the same class as this object.
712
     * 
713
     * @return a clone of this instance.
714
     * @exception OutOfMemoryError
715
     *                if there is not enough memory.
716
     * @see java.lang.Cloneable
717
     * @since 1.2
718
     */
719
    public Object clone() {
720
        DefaultGeneralPathX copy = new DefaultGeneralPathX();
721
        copy.windingRule = windingRule;
722
        copy.isSimple = isSimple;
723
        for (int i = 0; i < getNumTypes(); i++) {
724
            copy.pointTypes.add(pointTypes.get(i));
725
        }
726
        for (int i = 0; i < getNumCoords(); i++) {
727
            copy.addPoint((Point) getPointAt(i).cloneGeometry());
728
        }
729
        return copy;
730

    
731
    }
732

    
733
    DefaultGeneralPathX(int windingRule, byte[] pointTypes, int numTypes,
734
        double[] pointCoords, int numCoords) {
735

    
736
        // used to construct from native
737

    
738
        this.windingRule = windingRule;
739
        this.setPointTypes(pointTypes);
740
        this.setNumTypes(numTypes);
741
        this.setPointCoords(pointCoords);
742
        this.setNumCoords(numCoords);
743
    }
744

    
745
    public void setNumTypes(int numTypes) {
746

    
747
    }
748

    
749
    public int getNumTypes() {
750
        return pointTypes.size();
751
    }
752

    
753
    public int setNumCoords(int numCoords) {
754
        return pointCoords.size();
755
    }
756

    
757
    public int getNumCoords() {
758
        return pointCoords.size();
759
    }
760

    
761
    public byte getTypeAt(int index) {
762
        return ((Byte) pointTypes.get(index)).byteValue();
763
    }
764

    
765
    /**
766
     * @deprecated
767
     *             use the geometry methods.
768
     */
769
    public void setPointTypes(byte[] pointTypes) {
770
        this.pointTypes.clear();
771
        for (int i = 0; i < pointTypes.length; i++) {
772
            this.pointTypes.add(new Byte(pointTypes[i]));
773
        }
774
    }
775

    
776
    /**
777
     * @deprecated
778
     *             use the geometry methods.
779
     */
780
    public byte[] getPointTypes() {
781
        byte[] bytes = new byte[pointTypes.size()];
782
        for (int i = 0; i < pointTypes.size(); i++) {
783
            bytes[i] = ((Byte) pointTypes.get(i)).byteValue();
784
        }
785
        return bytes;
786
    }
787

    
788
    /**
789
     * @param pointCoords
790
     * @deprecated
791
     *             use the geometry methods.
792
     */
793
    public void setPointCoords(double[] pointCoords) {
794
        this.pointCoords.clear();
795
        for (int i = 0; i < pointCoords.length; i = i + 2) {
796
            try {
797
                addPoint(geomManager.createPoint(pointCoords[i],
798
                    pointCoords[i + 1], Geometry.SUBTYPES.GEOM2D));
799
            } catch (CreateGeometryException e) {
800
                LOG.error("Error creating a point", e);
801
            }
802
        }
803
    }
804

    
805
    /**
806
     * @deprecated
807
     *             use the geometry methods.
808
     */
809
    public double[] getPointCoords() {
810
        double[] doubles = new double[pointCoords.size() * 2];
811
        for (int i = 0; i < getNumCoords(); i++) {
812
            doubles[i * 2] = getPointAt(i).getX();
813
            doubles[(i * 2) + 1] = getPointAt(i).getY();
814
        }
815
        return doubles;
816
    }
817

    
818
    public Point getPointAt(int index) {
819
        return (Point) pointCoords.get(index);
820
    }
821

    
822
    public double[] getCoordinatesAt(int index) {
823
        return getPointAt(index).getCoordinates();
824
    }
825
    
826
    public double[] get3DCoordinatesAt(int index) {
827
            Point p = getPointAt(index);
828
            if(p instanceof Point2DZ) {
829
                    return getPointAt(index).getCoordinates();
830
            }
831
            double[] coords = new double[3];
832
            coords[0] = p.getX();
833
            coords[1] = p.getY();
834
            coords[2] = 0D;
835
            return coords;
836
    }
837

    
838
    /**
839
     * Convertimos el path a puntos y luego le damos la vuelta.
840
     */
841
    public void flip() {
842
        PathIterator theIterator =
843
            getPathIterator(null, geomManager.getFlatness());
844
        double[] theData = new double[6];
845
        CoordinateList coordList = new CoordinateList();
846
        Coordinate c1;
847
        DefaultGeneralPathX newGp = new DefaultGeneralPathX();
848
        ArrayList listOfParts = new ArrayList();
849
        while (!theIterator.isDone()) {
850
            // while not done
851
            int type = theIterator.currentSegment(theData);
852
            switch (type) {
853
            case SEG_MOVETO:
854
                coordList = new CoordinateList();
855
                listOfParts.add(coordList);
856
                c1 = new Coordinate(theData[0], theData[1]);
857
                coordList.add(c1, true);
858
                break;
859
            case SEG_LINETO:
860
                c1 = new Coordinate(theData[0], theData[1]);
861
                coordList.add(c1, true);
862
                break;
863

    
864
            case SEG_CLOSE:
865
                coordList.add(coordList.getCoordinate(0));
866
                break;
867

    
868
            }
869
            theIterator.next();
870
        }
871

    
872
        for (int i = listOfParts.size() - 1; i >= 0; i--) {
873
            coordList = (CoordinateList) listOfParts.get(i);
874
            Coordinate[] coords = coordList.toCoordinateArray();
875
            CoordinateArraySequence seq = new CoordinateArraySequence(coords);
876
            CoordinateSequences.reverse(seq);
877
            coords = seq.toCoordinateArray();
878
            newGp.moveTo(coords[0].x, coords[0].y);
879
            for (int j = 1; j < coords.length; j++) {
880
                newGp.lineTo(coords[j].x, coords[j].y);
881
            }
882
        }
883
        reset();
884
        append(newGp.getPathIterator(null), false);
885
    }
886

    
887
    /**
888
     * Check if the first part is CCW.
889
     * 
890
     * @return
891
     */
892
    public boolean isCCW() {
893
        PathIterator theIterator =
894
            getPathIterator(null, geomManager.getFlatness()); // polyLine.getPathIterator(null,
895
                                                              // flatness);
896
        double[] theData = new double[6];
897
        Coordinate first = null;
898
        CoordinateList coordList = new CoordinateList();
899
        Coordinate c1;
900
        boolean bFirst = true;
901
        while (!theIterator.isDone()) {
902
            // while not done
903
            int type = theIterator.currentSegment(theData);
904
            switch (type) {
905
            case SEG_MOVETO:
906
                c1 = new Coordinate(theData[0], theData[1]);
907
                if (bFirst == false) // Ya tenemos la primera parte.
908
                    break;
909
                if (bFirst) {
910
                    bFirst = false;
911
                    first = c1;
912
                }
913
                coordList.add(c1, true);
914
                break;
915
            case SEG_LINETO:
916
                c1 = new Coordinate(theData[0], theData[1]);
917
                coordList.add(c1, true);
918
                break;
919

    
920
            }
921
            theIterator.next();
922
        }
923
        coordList.add(first, true);
924
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
925
    }
926

    
927
    /**
928
     * @return the isSimple
929
     */
930
    public boolean isSimple() {
931
        return isSimple;
932
    }
933
}