Statistics
| Revision:

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

History | View | Annotate | Download (32.4 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
package org.gvsig.fmap.geom.primitive;
42

    
43
/**
44
 * @author FJP
45
 *
46
 */
47
/*
48
 * @(#)GeneralPathX.java        1.58 03/01/23
49
 *
50
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
51
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
52
 */
53

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

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

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

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

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

    
113
    private static final Logger LOG =
114
        LoggerFactory.getLogger(GeneralPathX.class);
115

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

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

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

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

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

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

    
143
    int windingRule;
144
    
145
    private boolean isSimple = true;
146

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

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

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

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

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

    
210
    /**
211
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
212
     * {@link Shape} object.
213
     * All of the initial geometry and the winding rule for this path are
214
     * taken from the specified <code>Shape</code> object.
215
     * @param s the specified <code>Shape</code> object
216
     */
217
    public GeneralPathX(PathIterator piter) {        
218
        this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
219
        setWindingRule(piter.getWindingRule());
220
        append(piter, false);
221
    }
222

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

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

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

    
281
    private void addPoint(double x, double y){
282
        try {
283
            pointCoords.add(geomManager.createPoint(x, y, Geometry.SUBTYPES.GEOM2D));
284
        } catch (CreateGeometryException e) {
285
            LOG.error("Error creating a point", e);
286
        }  
287
    }
288

    
289
    private void addPoint(Point point){
290
        pointCoords.add(point);       
291
    }
292

    
293
    /**
294
     * Adds a curved segment, defined by two new points, to the path by
295
     * drawing a Quadratic curve that intersects both the current
296
     * coordinates and the coordinates (x2,&nbsp;y2), using the
297
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
298
     * point.
299
     * @param x1,&nbsp;y1 the coordinates of the first quadratic control
300
     *                point
301
     * @param x2,&nbsp;y2 the coordinates of the final endpoint
302
     * @deprecated  
303
     *          use quadTo(Point, Point)
304
     */
305
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
306
        needRoom(1, 4, true);
307
        pointTypes.add(Byte.valueOf(SEG_QUADTO));
308
        addPoint(x1, y1);
309
        addPoint(x2, y2);
310
        isSimple = false;
311
    }
312
    
313
    public synchronized void quadTo(Point point1, Point point2) {
314
        needRoom(1, 4, true);
315
        pointTypes.add(Byte.valueOf(SEG_QUADTO));
316
        addPoint(point1);
317
        addPoint(point2);
318
        isSimple = false;
319
    }
320

    
321
    /**
322
     * Adds a curved segment, defined by three new points, to the path by
323
     * drawing a B&eacute;zier curve that intersects both the current
324
     * coordinates and the coordinates (x3,&nbsp;y3), using the
325
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
326
     * B&eacute;zier control points.
327
     * @param x1,&nbsp;y1 the coordinates of the first B&eacute;ezier
328
     *                control point
329
     * @param x2,&nbsp;y2 the coordinates of the second B&eacute;zier
330
     *                control point
331
     * @param x3,&nbsp;y3 the coordinates of the final endpoint
332
     * @deprecated
333
     *          use curveTo(Point, Point, Point)
334
     */
335
    public synchronized void curveTo(double x1, double y1,
336
        double x2, double y2,
337
        double x3, double y3) {
338
        needRoom(1, 6, true);
339
        pointTypes.add(Byte.valueOf(SEG_CUBICTO));
340
        addPoint(x1, y1);
341
        addPoint(x2, y2);
342
        addPoint(x3, y3);  
343
        isSimple = false;
344
    }
345
    
346
    public synchronized void curveTo(Point point1, Point point2, Point point3){       
347
        needRoom(1, 6, true);
348
        pointTypes.add(Byte.valueOf(SEG_CUBICTO));
349
        addPoint(point1);
350
        addPoint(point2);
351
        addPoint(point3);  
352
        isSimple = false;
353
    }
354

    
355
    /**
356
     * Closes the current subpath by drawing a straight line back to
357
     * the coordinates of the last <code>moveTo</code>.  If the path is already
358
     * closed then this method has no effect.
359
     */
360
    public synchronized void closePath() {
361
        if (getNumTypes() == 0 || getTypeAt(getNumTypes() - 1) != SEG_CLOSE) {
362
            needRoom(1, 0, true);
363
            //Adding a geometry like the last geometry
364
            //addPoint(100, 100);
365
            pointTypes.add(Byte.valueOf(SEG_CLOSE));
366
        }
367
    }
368

    
369
    /**
370
     * Check if the first part is closed.
371
     * @return
372
     */
373
    public boolean isClosed()
374
    {
375
        PathIterator theIterator = getPathIterator(null, geomManager.getFlatness()); //polyLine.getPathIterator(null, flatness);
376
        double[] theData = new double[6];
377
        double xFinal = 0;
378
        double yFinal = 0;
379
        double xIni = 0;
380
        double yIni = 0;
381
        boolean first = true;
382

    
383
        while (!theIterator.isDone()) {
384
            //while not done
385
            int theType = theIterator.currentSegment(theData);
386

    
387
            switch (theType) {
388
            case PathIterator.SEG_MOVETO:
389
                xIni = theData[0];
390
                yIni = theData[1];
391
                if (!first)
392
                {
393
                    break;
394
                }
395
                first = false;
396
                break;
397

    
398
            case PathIterator.SEG_LINETO:
399
                xFinal = theData[0];
400
                yFinal = theData[1];
401
                break;
402
            case PathIterator.SEG_CLOSE:
403
                return true;
404

    
405
            } //end switch
406

    
407
            theIterator.next();
408
        }
409
        if ((xFinal == xIni) && (yFinal == yIni))
410
            return true;
411
        return false;
412
    }
413

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

    
473
    /**
474
     * Returns the fill style winding rule.
475
     * @return an integer representing the current winding rule.
476
     * @see #WIND_EVEN_ODD
477
     * @see #WIND_NON_ZERO
478
     * @see #setWindingRule
479
     */
480
    public synchronized int getWindingRule() {
481
        return windingRule;
482
    }
483

    
484
    /**
485
     * Sets the winding rule for this path to the specified value.
486
     * @param rule an integer representing the specified
487
     * winding rule
488
     * @exception <code>IllegalArgumentException</code> if
489
     *                <code>rule</code> is not either
490
     *                <code>WIND_EVEN_ODD</code> or
491
     *                <code>WIND_NON_ZERO</code>
492
     * @see #WIND_EVEN_ODD
493
     * @see #WIND_NON_ZERO
494
     * @see #getWindingRule
495
     */
496
    public void setWindingRule(int rule) {
497
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
498
            throw new IllegalArgumentException("winding rule must be "+
499
                "WIND_EVEN_ODD or "+
500
            "WIND_NON_ZERO");
501
        }
502
        windingRule = rule;
503
    }
504

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

    
540
    /**
541
     * Resets the path to empty.  The append position is set back to the
542
     * beginning of the path and all coordinates and point types are
543
     * forgotten.
544
     */
545
    public synchronized void reset() {
546
        pointCoords.clear();
547
        pointTypes.clear();
548
    }
549

    
550
    /**
551
     * Transforms the geometry of this path using the specified
552
     * {@link AffineTransform}.
553
     * The geometry is transformed in place, which permanently changes the
554
     * boundary defined by this object.
555
     * @param at the <code>AffineTransform</code> used to transform the area
556
     */
557
    public void transform(AffineTransform at) {
558
        for (int i=0 ; i<getNumCoords() ; i++){
559
            double[] coordinates = getCoordinatesAt(i);
560
            at.transform(coordinates, 0, coordinates, 0, coordinates.length-1);
561
        }
562
    }
563

    
564
    public void reProject(ICoordTrans ct)
565
    {
566
        Point2D pt = new Point2D.Double();
567
        for (int i = 0; i < getNumCoords(); i++)
568
        {
569
            double[] coordinates = getCoordinatesAt(i);
570
            pt.setLocation(coordinates[0], coordinates[1]);
571
            pt = ct.convert(pt,null);
572
            coordinates[0] = pt.getX();
573
            coordinates[1] = pt.getY();
574
        }
575
    }
576

    
577

    
578
    /**
579
     * Returns a new transformed <code>Shape</code>.
580
     * @param at the <code>AffineTransform</code> used to transform a
581
     * new <code>Shape</code>.
582
     * @return a new <code>Shape</code>, transformed with the specified
583
     * <code>AffineTransform</code>.
584
     */
585
    public synchronized Shape createTransformedShape(AffineTransform at) {
586
        GeneralPathX gp = (GeneralPathX) clone();
587
        if (at != null) {
588
            gp.transform(at);
589
        }
590
        return gp;
591
    }
592

    
593
    /**
594
     * Return the bounding box of the path.
595
     * @return a {@link java.awt.Rectangle} object that
596
     * bounds the current path.
597
     */
598
    public java.awt.Rectangle getBounds() {
599
        return getBounds2D().getBounds();
600
    }
601

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

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

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

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

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

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

    
697
    /**
698
     * Tests if the interior of this <code>Shape</code> intersects the
699
     * interior of a specified <code>Rectangle2D</code>.
700
     * @param r the specified <code>Rectangle2D</code>
701
     * @return <code>true</code> if this <code>Shape</code> and the interior
702
     *                 of the specified <code>Rectangle2D</code> intersect each
703
     *                 other; <code>false</code> otherwise.
704
     */
705
    public boolean intersects(Rectangle2D r) {
706
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
707
    }
708

    
709
    /**
710
     * Returns a <code>PathIterator</code> object that iterates along the
711
     * boundary of this <code>Shape</code> and provides access to the
712
     * geometry of the outline of this <code>Shape</code>.
713
     * The iterator for this class is not multi-threaded safe,
714
     * which means that this <code>GeneralPathX</code> class does not
715
     * guarantee that modifications to the geometry of this
716
     * <code>GeneralPathX</code> object do not affect any iterations of
717
     * that geometry that are already in process.
718
     * @param at an <code>AffineTransform</code>
719
     * @return a new <code>PathIterator</code> that iterates along the
720
     * boundary of this <code>Shape</code> and provides access to the
721
     * geometry of this <code>Shape</code>'s outline
722
     */
723
    public PathIterator getPathIterator(AffineTransform at) {
724
        if (isSimple){
725
            return new GeneralPathXIteratorSimple(this, at);
726
        }else{
727
            return new GeneralPathXIterator(this, at);
728
        }
729
    }
730

    
731
    /**
732
     * Returns a <code>PathIterator</code> object that iterates along the
733
     * boundary of the flattened <code>Shape</code> and provides access to the
734
     * geometry of the outline of the <code>Shape</code>.
735
     * The iterator for this class is not multi-threaded safe,
736
     * which means that this <code>GeneralPathX</code> class does not
737
     * guarantee that modifications to the geometry of this
738
     * <code>GeneralPathX</code> object do not affect any iterations of
739
     * that geometry that are already in process.
740
     * @param at an <code>AffineTransform</code>
741
     * @param flatness the maximum distance that the line segments used to
742
     *                approximate the curved segments are allowed to deviate
743
     *                from any point on the original curve
744
     * @return a new <code>PathIterator</code> that iterates along the flattened
745
     * <code>Shape</code> boundary.
746
     */
747
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
748
        return new FlatteningPathIterator(getPathIterator(at), flatness);
749
    }
750

    
751
    /**
752
     * Creates a new object of the same class as this object.
753
     *
754
     * @return     a clone of this instance.
755
     * @exception  OutOfMemoryError            if there is not enough memory.
756
     * @see        java.lang.Cloneable
757
     * @since      1.2
758
     */
759
    public Object clone() {    
760
        GeneralPathX copy = new GeneralPathX();         
761
        copy.windingRule = windingRule;
762
        copy.isSimple = isSimple;
763
        for (int i=0 ; i<getNumTypes() ; i++){
764
            copy.pointTypes.add(pointTypes.get(i));
765
        }             
766
        for (int i=0 ; i<getNumCoords() ; i++){
767
            copy.addPoint((Point)getPointAt(i).cloneGeometry());
768
        }          
769
        return copy;
770

    
771
    }
772

    
773
    GeneralPathX(int windingRule,
774
        byte[] pointTypes,
775
        int numTypes,
776
        double[] pointCoords,
777
        int numCoords) {
778

    
779
        // used to construct from native
780

    
781
        this.windingRule = windingRule;
782
        this.setPointTypes(pointTypes);
783
        this.setNumTypes(numTypes);
784
        this.setPointCoords(pointCoords);
785
        this.setNumCoords(numCoords);
786
    }
787

    
788
    public void setNumTypes(int numTypes) {
789

    
790
    }
791

    
792
    public int getNumTypes() {
793
        return pointTypes.size();
794
    }
795

    
796
    public int setNumCoords(int numCoords) {
797
        return pointCoords.size();
798
    }
799

    
800
    public int getNumCoords() {
801
        return pointCoords.size();
802
    }
803

    
804
    public byte getTypeAt(int index){
805
        return ((Byte) pointTypes.get(index)).byteValue();
806
    }
807

    
808
    /** 
809
     * @deprecated
810
     *      use the geometry methods.
811
     */
812
    public void setPointTypes(byte[] pointTypes) {
813
        this.pointTypes.clear();        
814
        for (int i=0 ; i<pointTypes.length ; i++){           
815
            this.pointTypes.add(Byte.valueOf(pointTypes[i]));
816
        }      
817
    }
818

    
819
    /** 
820
     * @deprecated
821
     *     use the geometry methods.
822
     */
823
    public byte[] getPointTypes() {
824
        byte[] bytes = new byte[pointTypes.size()];
825
        for (int i=0 ; i<pointTypes.size() ; i++){           
826
            bytes[i] = ((Byte) pointTypes.get(i)).byteValue();
827
        }  
828
        return bytes;
829
    }
830

    
831
    /** 
832
     * @param pointCoords
833
     * @deprecated
834
     *      use the geometry methods.
835
     */
836
    public void setPointCoords(double[] pointCoords) {     
837
        this.pointCoords.clear();        
838
        for (int i=0 ; i<pointCoords.length ; i=i+2){           
839
            try {
840
                addPoint(geomManager.createPoint(pointCoords[i], pointCoords[i+1], Geometry.SUBTYPES.GEOM2D));
841
            } catch (CreateGeometryException e) {
842
                LOG.error("Error creating a point", e);
843
            }
844
        }           
845
    }
846

    
847
    /**         
848
     * @deprecated
849
     *      use the geometry methods.
850
     */
851
    public double[] getPointCoords() {
852
        double[] doubles = new double[pointCoords.size()*2];
853
        for (int i=0 ; i<getNumCoords() ; i++){
854
            doubles[i*2] = getPointAt(i).getX();
855
            doubles[(i*2)+1] = getPointAt(i).getY();
856
        }
857
        return doubles;
858
    }
859

    
860
    public Point getPointAt(int index){
861
        return (Point)pointCoords.get(index);
862
    }
863

    
864
    public double[] getCoordinatesAt(int index){
865
        return getPointAt(index).getCoordinates();
866
    }
867

    
868
    /**
869
     * Convertimos el path a puntos y luego le damos la vuelta.
870
     */
871
    public void flip()
872
    {
873
        PathIterator theIterator = getPathIterator(null, geomManager.getFlatness()); //polyLine.getPathIterator(null, flatness);
874
        double[] theData = new double[6];
875
        Coordinate first = null;
876
        CoordinateList coordList = new CoordinateList();
877
        Coordinate c1;
878
        GeneralPathX newGp = new GeneralPathX();
879
        ArrayList listOfParts = new ArrayList();
880
        while (!theIterator.isDone()) {
881
            //while not done
882
            int type = theIterator.currentSegment(theData);
883
            switch (type)
884
            {
885
            case SEG_MOVETO:
886
                coordList = new CoordinateList();
887
                listOfParts.add(coordList);
888
                c1= new Coordinate(theData[0], theData[1]);
889
                coordList.add(c1, true);
890
                break;
891
            case SEG_LINETO:
892
                c1= new Coordinate(theData[0], theData[1]);
893
                coordList.add(c1, true);
894
                break;
895

    
896
            case SEG_CLOSE:
897
                coordList.add(coordList.getCoordinate(0));
898
                break;
899

    
900
            }
901
            theIterator.next();
902
        }
903

    
904
        for (int i=listOfParts.size()-1; i>=0; i--)
905
        {
906
            coordList = (CoordinateList) listOfParts.get(i);
907
            Coordinate[] coords = coordList.toCoordinateArray();
908
            CoordinateArraySequence seq = new CoordinateArraySequence(coords);
909
            CoordinateSequences.reverse(seq);
910
            coords = seq.toCoordinateArray();
911
            newGp.moveTo(coords[0].x, coords[0].y);
912
            for (int j=1; j < coords.length; j++)
913
            {
914
                newGp.lineTo(coords[j].x, coords[j].y);
915
            }
916
        }
917
        reset();
918
        append(newGp.getPathIterator(null), false);
919
    }
920
    /**
921
     * Check if the first part is CCW.
922
     * @return
923
     */
924
    public boolean isCCW()
925
    {
926
        PathIterator theIterator = getPathIterator(null, geomManager.getFlatness()); //polyLine.getPathIterator(null, flatness);
927
        double[] theData = new double[6];
928
        Coordinate first = null;
929
        CoordinateList coordList = new CoordinateList();
930
        Coordinate c1;
931
        boolean bFirst = true;
932
        while (!theIterator.isDone()) {
933
            //while not done
934
            int type = theIterator.currentSegment(theData);
935
            switch (type)
936
            {
937
            case SEG_MOVETO:
938
                c1= new Coordinate(theData[0], theData[1]);
939
                if (bFirst == false) // Ya tenemos la primera parte.
940
                    break;
941
                if (bFirst)
942
                {
943
                    bFirst=false;
944
                    first = c1;
945
                }
946
                coordList.add(c1, true);
947
                break;
948
            case SEG_LINETO:
949
                c1= new Coordinate(theData[0], theData[1]);
950
                coordList.add(c1, true);
951
                break;
952

    
953
            }
954
            theIterator.next();
955
        }
956
        coordList.add(first, true);
957
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
958
    }
959
    
960
    /**
961
     * @return the isSimple
962
     */
963
    public boolean isSimple() {
964
        return isSimple;
965
    }
966
}