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 @ 40435

History | View | Annotate | Download (30.8 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.util.ArrayList;
55
import java.util.List;
56

    
57
import org.cresques.cts.ICoordTrans;
58
import org.gvsig.fmap.geom.Geometry;
59
import org.gvsig.fmap.geom.GeometryLocator;
60
import org.gvsig.fmap.geom.GeometryManager;
61
import org.gvsig.fmap.geom.exception.CreateGeometryException;
62
import org.gvsig.fmap.geom.primitive.impl.Point2DZ;
63
import org.gvsig.jdk.GeomUtilities;
64
import org.slf4j.Logger;
65
import org.slf4j.LoggerFactory;
66

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

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

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

    
103
    private static final Logger LOG = LoggerFactory
104
        .getLogger(DefaultGeneralPathX.class);
105

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

    
109
    private List pointTypes = new ArrayList();
110
    private List pointCoords = new ArrayList();
111

    
112
    int windingRule;
113

    
114
    private boolean isSimple = true;
115

    
116
    static final int EXPAND_MAX = 500;
117

    
118
    private  DefaultGeneralPathX() {
119
            super(true);
120
        setWindingRule(WIND_EVEN_ODD);
121
    }
122
    /**
123
     * Constructs a new <code>GeneralPathX</code> object with the specified
124
     * winding rule to control operations that require the interior of the
125
     * path to be defined.
126
     * 
127
     * @param rule
128
     *            the winding rule
129
     * @see #WIND_EVEN_ODD
130
     * @see #WIND_NON_ZERO
131
     */
132
    public DefaultGeneralPathX(int rule) {
133
            super(true);
134
        setWindingRule(rule);
135
    }
136

    
137
    /**
138
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
139
     * {@link Shape} object.
140
     * All of the initial geometry and the winding rule for this path are
141
     * taken from the specified <code>Shape</code> object.
142
     * 
143
     * @param s
144
     *            the specified <code>Shape</code> object
145
     */
146
    public DefaultGeneralPathX(PathIterator piter) {
147
        this(WIND_EVEN_ODD);
148
        setWindingRule(piter.getWindingRule());
149
        append(piter, false);
150
    }
151

    
152
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
153
        if (needMove && getNumTypes() == 0) {
154
            throw new IllegalPathStateException("missing initial moveto "
155
                + "in path definition");
156
        }
157
    }
158

    
159
    /**
160
     * Adds a point to the path by moving to the specified
161
     * coordinates.
162
     * 
163
     * @param x
164
     *            ,&nbsp;y the specified coordinates
165
     * @deprecated
166
     *             use moveTo(Point)
167
     */
168
    public synchronized void moveTo(double x, double y) {
169
        int numtypes = getNumTypes();
170
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
171
            Point point = getPointAt(getNumCoords() - 1);
172
            point.setX(x);
173
            point.setY(y);
174
        } else {
175
            needRoom(1, 2, false);
176
            pointTypes.add(new Byte(SEG_MOVETO));
177
            addPoint(x, y);
178
        }
179
    }
180

    
181
    public synchronized void moveTo(Point point) {
182
        int numtypes = getNumTypes();
183
        if (numtypes > 0 && getTypeAt(numtypes - 1) == SEG_MOVETO) {
184
            pointCoords.remove(getNumCoords() - 1);
185
            addPoint(point);
186
        } else {
187
            needRoom(1, 2, false);
188
            pointTypes.add(new Byte(SEG_MOVETO));
189
            addPoint(point);
190
        }
191
    }
192

    
193
    /**
194
     * Adds a point to the path by drawing a straight line from the
195
     * current coordinates to the new specified coordinates.
196
     * 
197
     * @param x
198
     *            ,&nbsp;y the specified coordinates
199
     * @deprecated
200
     *             use lineTo(Point)
201
     */
202
    public synchronized void lineTo(double x, double y) {
203
        needRoom(1, 2, true);
204
        pointTypes.add(new Byte(SEG_LINETO));
205
        addPoint(x, y);
206
    }
207

    
208
    public synchronized void lineTo(Point point) {
209
        needRoom(1, 2, true);
210
        pointTypes.add(new Byte(SEG_LINETO));
211
        addPoint(point);
212
    }
213

    
214
    public synchronized void addSegment(Point[] segment) {
215
        if (segment != null && segment.length > 0) {
216
            needRoom(segment.length, 2 * segment.length, true);
217
            for (int i = 0; i < segment.length; i++) {
218
                pointTypes.add(new Byte(SEG_LINETO));
219
                addPoint(segment[i]);
220
            }
221
        }
222
    }
223

    
224
    private void addPoint(double x, double y) {
225
        try {
226
            pointCoords.add(geomManager.createPoint(x, y,
227
                Geometry.SUBTYPES.GEOM2D));
228
        } catch (CreateGeometryException e) {
229
            LOG.error("Error creating a point", e);
230
        }
231
    }
232

    
233
    private void addPoint(Point point) {
234
        pointCoords.add(point);
235
    }
236

    
237
    /**
238
     * Adds a curved segment, defined by two new points, to the path by
239
     * drawing a Quadratic curve that intersects both the current
240
     * coordinates and the coordinates (x2,&nbsp;y2), using the
241
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
242
     * point.
243
     * 
244
     * @param x1
245
     *            ,&nbsp;y1 the coordinates of the first quadratic control
246
     *            point
247
     * @param x2
248
     *            ,&nbsp;y2 the coordinates of the final endpoint
249
     * @deprecated
250
     *             use quadTo(Point, Point)
251
     */
252
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
253
        needRoom(1, 4, true);
254
        pointTypes.add(new Byte(SEG_QUADTO));
255
        addPoint(x1, y1);
256
        addPoint(x2, y2);
257
        isSimple = false;
258
    }
259

    
260
    public synchronized void quadTo(Point point1, Point point2) {
261
        needRoom(1, 4, true);
262
        pointTypes.add(new Byte(SEG_QUADTO));
263
        addPoint(point1);
264
        addPoint(point2);
265
        isSimple = false;
266
    }
267

    
268
    /**
269
     * Adds a curved segment, defined by three new points, to the path by
270
     * drawing a B&eacute;zier curve that intersects both the current
271
     * coordinates and the coordinates (x3,&nbsp;y3), using the
272
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
273
     * B&eacute;zier control points.
274
     * 
275
     * @param x1
276
     *            ,&nbsp;y1 the coordinates of the first B&eacute;ezier
277
     *            control point
278
     * @param x2
279
     *            ,&nbsp;y2 the coordinates of the second B&eacute;zier
280
     *            control point
281
     * @param x3
282
     *            ,&nbsp;y3 the coordinates of the final endpoint
283
     * @deprecated
284
     *             use curveTo(Point, Point, Point)
285
     */
286
    public synchronized void curveTo(double x1, double y1, double x2,
287
        double y2, double x3, double y3) {
288
        needRoom(1, 6, true);
289
        pointTypes.add(new Byte(SEG_CUBICTO));
290
        addPoint(x1, y1);
291
        addPoint(x2, y2);
292
        addPoint(x3, y3);
293
        isSimple = false;
294
    }
295

    
296
    public synchronized void curveTo(Point point1, Point point2, Point point3) {
297
        needRoom(1, 6, true);
298
        pointTypes.add(new Byte(SEG_CUBICTO));
299
        addPoint(point1);
300
        addPoint(point2);
301
        addPoint(point3);
302
        isSimple = false;
303
    }
304

    
305
    /**
306
     * Closes the current subpath by drawing a straight line back to
307
     * the coordinates of the last <code>moveTo</code>. If the path is already
308
     * closed then this method has no effect.
309
     */
310
    public synchronized void closePath() {
311
        if (getNumTypes() == 0 || getTypeAt(getNumTypes() - 1) != SEG_CLOSE) {
312
            needRoom(1, 0, true);
313
            // Adding a geometry like the last geometry
314
            // addPoint(100, 100);
315
            pointTypes.add(new Byte(SEG_CLOSE));
316
        }
317
    }
318

    
319
    /**
320
     * Check if the first part is closed.
321
     * 
322
     * @return
323
     */
324
    public boolean isClosed() {
325
        PathIterator theIterator =
326
            getPathIterator(null, geomManager.getFlatness());
327
        double[] theData = new double[6];
328
        double xFinal = 0;
329
        double yFinal = 0;
330
        double xIni = 0;
331
        double yIni = 0;
332
        boolean first = true;
333

    
334
        while (!theIterator.isDone()) {
335
            // while not done
336
            int theType = theIterator.currentSegment(theData);
337

    
338
            switch (theType) {
339
            case PathIterator.SEG_MOVETO:
340
                xIni = theData[0];
341
                yIni = theData[1];
342
                if (!first) {
343
                    break;
344
                }
345
                first = false;
346
                break;
347

    
348
            case PathIterator.SEG_LINETO:
349
                xFinal = theData[0];
350
                yFinal = theData[1];
351
                break;
352
            case PathIterator.SEG_CLOSE:
353
                return true;
354

    
355
            } // end switch
356

    
357
            theIterator.next();
358
        }
359
        if ((xFinal == xIni) && (yFinal == yIni))
360
            return true;
361
        return false;
362
    }
363

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

    
423
    /**
424
     * Returns the fill style winding rule.
425
     * 
426
     * @return an integer representing the current winding rule.
427
     * @see #WIND_EVEN_ODD
428
     * @see #WIND_NON_ZERO
429
     * @see #setWindingRule
430
     */
431
    public synchronized int getWindingRule() {
432
        return windingRule;
433
    }
434

    
435
    /**
436
     * Sets the winding rule for this path to the specified value.
437
     * 
438
     * @param rule
439
     *            an integer representing the specified
440
     *            winding rule
441
     * @exception <code>IllegalArgumentException</code> if <code>rule</code> is
442
     *            not either <code>WIND_EVEN_ODD</code> or
443
     *            <code>WIND_NON_ZERO</code>
444
     * @see #WIND_EVEN_ODD
445
     * @see #WIND_NON_ZERO
446
     * @see #getWindingRule
447
     */
448
    public void setWindingRule(int rule) {
449
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
450
            throw new IllegalArgumentException("winding rule must be "
451
                + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
452
        }
453
        windingRule = rule;
454
    }
455

    
456
    /**
457
     * Returns the coordinates most recently added to the end of the path
458
     * as a {@link Point2D} object.
459
     * 
460
     * @return a <code>Point2D</code> object containing the ending
461
     *         coordinates of the path or <code>null</code> if there are no
462
     *         points
463
     *         in the path.
464
     */
465
    public synchronized Point2D getCurrentPoint() {
466
        if (getNumTypes() < 1 || getNumCoords() < 1) {
467
            return null;
468
        }
469
        int index = getNumCoords();
470
        if (getTypeAt(getNumTypes() - 1) == SEG_CLOSE) {
471
            loop: for (int i = getNumTypes() - 2; i > 0; i--) {
472
                switch (getTypeAt(i)) {
473
                case SEG_MOVETO:
474
                    break loop;
475
                case SEG_LINETO:
476
                    index -= 2;
477
                    break;
478
                case SEG_QUADTO:
479
                    index -= 4;
480
                    break;
481
                case SEG_CUBICTO:
482
                    index -= 6;
483
                    break;
484
                case SEG_CLOSE:
485
                    break;
486
                }
487
            }
488
        }
489
        return new Point2D.Double(getPointAt(index - 1).getX(), getPointAt(
490
            index - 1).getY());
491
    }
492

    
493
    /**
494
     * Resets the path to empty. The append position is set back to the
495
     * beginning of the path and all coordinates and point types are
496
     * forgotten.
497
     */
498
    public synchronized void reset() {
499
        pointCoords.clear();
500
        pointTypes.clear();
501
    }
502

    
503
    /**
504
     * Transforms the geometry of this path using the specified
505
     * {@link AffineTransform}.
506
     * The geometry is transformed in place, which permanently changes the
507
     * boundary defined by this object.
508
     * 
509
     * @param at
510
     *            the <code>AffineTransform</code> used to transform the area
511
     */
512
    public void transform(AffineTransform at) {
513
        for (int i = 0; i < getNumCoords(); i++) {
514
            getPointAt(i).transform(at);
515
        }
516
    }
517

    
518
    public void reProject(ICoordTrans ct) {
519
        for (int i = 0; i < getNumCoords(); i++) {
520
            getPointAt(i).reProject(ct);
521
        }
522
    }
523

    
524
    /**
525
     * Returns a new transformed <code>Shape</code>.
526
     * 
527
     * @param at
528
     *            the <code>AffineTransform</code> used to transform a
529
     *            new <code>Shape</code>.
530
     * @return a new <code>Shape</code>, transformed with the specified
531
     *         <code>AffineTransform</code>.
532
     */
533
    public synchronized Shape createTransformedShape(AffineTransform at) {
534
        DefaultGeneralPathX gp = (DefaultGeneralPathX) clone();
535
        if (at != null) {
536
            gp.transform(at);
537
        }
538
        return gp;
539
    }
540

    
541
    /**
542
     * Return the bounding box of the path.
543
     * 
544
     * @return a {@link java.awt.Rectangle} object that
545
     *         bounds the current path.
546
     */
547
    public java.awt.Rectangle getBounds() {
548
        return getBounds2D().getBounds();
549
    }
550

    
551
    /**
552
     * Returns the bounding box of the path.
553
     * 
554
     * @return a {@link Rectangle2D} object that
555
     *         bounds the current path.
556
     */
557
    public synchronized Rectangle2D getBounds2D() {
558
        double x1, y1, x2, y2;
559
        int i = getNumCoords();
560
        if (i > 0) {
561
            y1 = y2 = getPointAt(--i).getY();
562
            x1 = x2 = getPointAt(i).getX();
563
            while (i > 0) {
564
                double y = getPointAt(--i).getY();
565
                double x = getPointAt(i).getX();
566
                if (x < x1)
567
                    x1 = x;
568
                if (y < y1)
569
                    y1 = y;
570
                if (x > x2)
571
                    x2 = x;
572
                if (y > y2)
573
                    y2 = y;
574
            }
575
        } else {
576
            x1 = y1 = x2 = y2 = 0.0f;
577
        }
578
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
579
    }
580

    
581
    /**
582
     * Tests if the specified coordinates are inside the boundary of
583
     * this <code>Shape</code>.
584
     * 
585
     * @param x
586
     *            ,&nbsp;y the specified coordinates
587
     * @return <code>true</code> if the specified coordinates are inside this
588
     *         <code>Shape</code>; <code>false</code> otherwise
589
     */
590
    public boolean contains(double x, double y) {
591
        if (pointTypes.size() < 2) {
592
            return false;
593
        }
594
        int cross =
595
            GeomUtilities.pointCrossingsForPath(getPathIterator(null), x, y);
596
        if (windingRule == WIND_NON_ZERO) {
597
            return (cross != 0);
598
        } else {
599
            return ((cross & 1) != 0);
600
        }
601
    }
602

    
603
    /**
604
     * Tests if the specified <code>Point2D</code> is inside the boundary
605
     * of this <code>Shape</code>.
606
     * 
607
     * @param p
608
     *            the specified <code>Point2D</code>
609
     * @return <code>true</code> if this <code>Shape</code> contains the
610
     *         specified <code>Point2D</code>, <code>false</code> otherwise.
611
     */
612
    public boolean contains(Point2D p) {
613
        return contains(p.getX(), p.getY());
614
    }
615

    
616
    /**
617
     * Tests if the specified rectangular area is inside the boundary of
618
     * this <code>Shape</code>.
619
     * 
620
     * @param x
621
     *            ,&nbsp;y the specified coordinates
622
     * @param w
623
     *            the width of the specified rectangular area
624
     * @param h
625
     *            the height of the specified rectangular area
626
     * @return <code>true</code> if this <code>Shape</code> contains
627
     *         the specified rectangluar area; <code>false</code> otherwise.
628
     */
629
    public boolean contains(double x, double y, double w, double h) {
630
        return GeomUtilities
631
            .contains(getPathIterator(null), x, y, x + w, y + h);
632
    }
633

    
634
    /**
635
     * Tests if the specified <code>Rectangle2D</code> is inside the boundary of
636
     * this <code>Shape</code>.
637
     * 
638
     * @param r
639
     *            a specified <code>Rectangle2D</code>
640
     * @return <code>true</code> if this <code>Shape</code> bounds the
641
     *         specified <code>Rectangle2D</code>; <code>false</code> otherwise.
642
     */
643
    public boolean contains(Rectangle2D r) {
644
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
645
    }
646

    
647
    /**
648
     * Tests if the interior of this <code>Shape</code> intersects the
649
     * interior of a specified set of rectangular coordinates.
650
     * 
651
     * @param x
652
     *            ,&nbsp;y the specified coordinates
653
     * @param w
654
     *            the width of the specified rectangular coordinates
655
     * @param h
656
     *            the height of the specified rectangular coordinates
657
     * @return <code>true</code> if this <code>Shape</code> and the
658
     *         interior of the specified set of rectangular coordinates
659
     *         intersect
660
     *         each other; <code>false</code> otherwise.
661
     */
662
    public boolean intersects(double x, double y, double w, double h) {
663
        return GeomUtilities.intersects(getPathIterator(null), x, y, w, h);
664
    }
665

    
666
    /**
667
     * Tests if the interior of this <code>Shape</code> intersects the
668
     * interior of a specified <code>Rectangle2D</code>.
669
     * 
670
     * @param r
671
     *            the specified <code>Rectangle2D</code>
672
     * @return <code>true</code> if this <code>Shape</code> and the interior
673
     *         of the specified <code>Rectangle2D</code> intersect each
674
     *         other; <code>false</code> otherwise.
675
     */
676
    public boolean intersects(Rectangle2D r) {
677
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
678
    }
679

    
680
    /**
681
     * Returns a <code>PathIterator</code> object that iterates along the
682
     * boundary of this <code>Shape</code> and provides access to the
683
     * geometry of the outline of this <code>Shape</code>.
684
     * The iterator for this class is not multi-threaded safe,
685
     * which means that this <code>GeneralPathX</code> class does not
686
     * guarantee that modifications to the geometry of this
687
     * <code>GeneralPathX</code> object do not affect any iterations of
688
     * that geometry that are already in process.
689
     * 
690
     * @param at
691
     *            an <code>AffineTransform</code>
692
     * @return a new <code>PathIterator</code> that iterates along the
693
     *         boundary of this <code>Shape</code> and provides access to the
694
     *         geometry of this <code>Shape</code>'s outline
695
     */
696
    public PathIterator getPathIterator(AffineTransform at) {
697
        if (isSimple) {
698
            return new GeneralPathXIteratorSimple(this, at);
699
        } else {
700
            return new GeneralPathXIterator(this, at);
701
        }
702
    }
703

    
704
    /**
705
     * Returns a <code>PathIterator</code> object that iterates along the
706
     * boundary of the flattened <code>Shape</code> and provides access to the
707
     * geometry of the outline of the <code>Shape</code>.
708
     * The iterator for this class is not multi-threaded safe,
709
     * which means that this <code>GeneralPathX</code> class does not
710
     * guarantee that modifications to the geometry of this
711
     * <code>GeneralPathX</code> object do not affect any iterations of
712
     * that geometry that are already in process.
713
     * 
714
     * @param at
715
     *            an <code>AffineTransform</code>
716
     * @param flatness
717
     *            the maximum distance that the line segments used to
718
     *            approximate the curved segments are allowed to deviate
719
     *            from any point on the original curve
720
     * @return a new <code>PathIterator</code> that iterates along the flattened
721
     *         <code>Shape</code> boundary.
722
     */
723
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
724
        return new FlatteningPathIterator(getPathIterator(at), flatness);
725
    }
726

    
727
    /**
728
     * Creates a new object of the same class as this object.
729
     * 
730
     * @return a clone of this instance.
731
     * @exception OutOfMemoryError
732
     *                if there is not enough memory.
733
     * @see java.lang.Cloneable
734
     * @since 1.2
735
     */
736
    public Object clone() {
737
        DefaultGeneralPathX copy = new DefaultGeneralPathX();
738
        copy.windingRule = windingRule;
739
        copy.isSimple = isSimple;
740
        for (int i = 0; i < getNumTypes(); i++) {
741
            copy.pointTypes.add(pointTypes.get(i));
742
        }
743
        for (int i = 0; i < getNumCoords(); i++) {
744
            copy.addPoint((Point) getPointAt(i).cloneGeometry());
745
        }
746
        return copy;
747

    
748
    }
749

    
750
    DefaultGeneralPathX(int windingRule, byte[] pointTypes, int numTypes,
751
        double[] pointCoords, int numCoords) {
752

    
753
        // used to construct from native
754

    
755
        this.windingRule = windingRule;
756
        this.setPointTypes(pointTypes);
757
        this.setNumTypes(numTypes);
758
        this.setPointCoords(pointCoords);
759
        this.setNumCoords(numCoords);
760
    }
761

    
762
    public void setNumTypes(int numTypes) {
763

    
764
    }
765

    
766
    public int getNumTypes() {
767
        return pointTypes.size();
768
    }
769

    
770
    public int setNumCoords(int numCoords) {
771
        return pointCoords.size();
772
    }
773

    
774
    public int getNumCoords() {
775
        return pointCoords.size();
776
    }
777

    
778
    public byte getTypeAt(int index) {
779
        return ((Byte) pointTypes.get(index)).byteValue();
780
    }
781

    
782
    /**
783
     * @deprecated
784
     *             use the geometry methods.
785
     */
786
    public void setPointTypes(byte[] pointTypes) {
787
        this.pointTypes.clear();
788
        for (int i = 0; i < pointTypes.length; i++) {
789
            this.pointTypes.add(new Byte(pointTypes[i]));
790
        }
791
    }
792

    
793
    /**
794
     * @deprecated
795
     *             use the geometry methods.
796
     */
797
    public byte[] getPointTypes() {
798
        byte[] bytes = new byte[pointTypes.size()];
799
        for (int i = 0; i < pointTypes.size(); i++) {
800
            bytes[i] = ((Byte) pointTypes.get(i)).byteValue();
801
        }
802
        return bytes;
803
    }
804

    
805
    /**
806
     * @param pointCoords
807
     * @deprecated
808
     *             use the geometry methods.
809
     */
810
    public void setPointCoords(double[] pointCoords) {
811
        this.pointCoords.clear();
812
        for (int i = 0; i < pointCoords.length; i = i + 2) {
813
            try {
814
                addPoint(geomManager.createPoint(pointCoords[i],
815
                    pointCoords[i + 1], Geometry.SUBTYPES.GEOM2D));
816
            } catch (CreateGeometryException e) {
817
                LOG.error("Error creating a point", e);
818
            }
819
        }
820
    }
821

    
822
    /**
823
     * @deprecated
824
     *             use the geometry methods.
825
     */
826
    public double[] getPointCoords() {
827
        double[] doubles = new double[pointCoords.size() * 2];
828
        for (int i = 0; i < getNumCoords(); i++) {
829
            doubles[i * 2] = getPointAt(i).getX();
830
            doubles[(i * 2) + 1] = getPointAt(i).getY();
831
        }
832
        return doubles;
833
    }
834

    
835
    public Point getPointAt(int index) {
836
        return (Point) pointCoords.get(index);
837
    }
838

    
839
    public double[] getCoordinatesAt(int index) {
840
        return getPointAt(index).getCoordinates();
841
    }
842
    
843
    public double[] get3DCoordinatesAt(int index) {
844
            Point p = getPointAt(index);
845
            if(p instanceof Point2DZ) {
846
                    return getPointAt(index).getCoordinates();
847
            }
848
            double[] coords = new double[3];
849
            coords[0] = p.getX();
850
            coords[1] = p.getY();
851
            coords[2] = 0D;
852
            return coords;
853
    }
854

    
855
    /**
856
     * Convertimos el path a puntos y luego le damos la vuelta.
857
     */
858
    public void flip() {
859
        PathIterator theIterator =
860
            getPathIterator(null, geomManager.getFlatness());
861
        double[] theData = new double[6];
862
        CoordinateList coordList = new CoordinateList();
863
        Coordinate c1;
864
        DefaultGeneralPathX newGp = new DefaultGeneralPathX();
865
        ArrayList listOfParts = new ArrayList();
866
        while (!theIterator.isDone()) {
867
            // while not done
868
            int type = theIterator.currentSegment(theData);
869
            switch (type) {
870
            case SEG_MOVETO:
871
                coordList = new CoordinateList();
872
                listOfParts.add(coordList);
873
                c1 = new Coordinate(theData[0], theData[1]);
874
                coordList.add(c1, true);
875
                break;
876
            case SEG_LINETO:
877
                c1 = new Coordinate(theData[0], theData[1]);
878
                coordList.add(c1, true);
879
                break;
880

    
881
            case SEG_CLOSE:
882
                coordList.add(coordList.getCoordinate(0));
883
                break;
884

    
885
            }
886
            theIterator.next();
887
        }
888

    
889
        for (int i = listOfParts.size() - 1; i >= 0; i--) {
890
            coordList = (CoordinateList) listOfParts.get(i);
891
            Coordinate[] coords = coordList.toCoordinateArray();
892
            CoordinateArraySequence seq = new CoordinateArraySequence(coords);
893
            CoordinateSequences.reverse(seq);
894
            coords = seq.toCoordinateArray();
895
            newGp.moveTo(coords[0].x, coords[0].y);
896
            for (int j = 1; j < coords.length; j++) {
897
                newGp.lineTo(coords[j].x, coords[j].y);
898
            }
899
        }
900
        reset();
901
        append(newGp.getPathIterator(null), false);
902
    }
903

    
904
    /**
905
     * Check if the first part is CCW.
906
     * 
907
     * @return
908
     */
909
    public boolean isCCW() {
910
        PathIterator theIterator =
911
            getPathIterator(null, geomManager.getFlatness()); // polyLine.getPathIterator(null,
912
                                                              // flatness);
913
        double[] theData = new double[6];
914
        Coordinate first = null;
915
        CoordinateList coordList = new CoordinateList();
916
        Coordinate c1;
917
        boolean bFirst = true;
918
        while (!theIterator.isDone()) {
919
            // while not done
920
            int type = theIterator.currentSegment(theData);
921
            switch (type) {
922
            case SEG_MOVETO:
923
                c1 = new Coordinate(theData[0], theData[1]);
924
                if (bFirst == false) // Ya tenemos la primera parte.
925
                    break;
926
                if (bFirst) {
927
                    bFirst = false;
928
                    first = c1;
929
                }
930
                coordList.add(c1, true);
931
                break;
932
            case SEG_LINETO:
933
                c1 = new Coordinate(theData[0], theData[1]);
934
                coordList.add(c1, true);
935
                break;
936

    
937
            }
938
            theIterator.next();
939
        }
940
        coordList.add(first, true);
941
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
942
    }
943

    
944
    /**
945
     * @return the isSimple
946
     */
947
    public boolean isSimple() {
948
        return isSimple;
949
    }
950
}