Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / core / GeneralPathX.java @ 5893

History | View | Annotate | Download (30.6 KB)

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

    
49
/**
50
 * @author FJP
51
 *
52
 */
53
/*
54
 * @(#)GeneralPathX.java        1.58 03/01/23
55
 *
56
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
57
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
58
 */
59

    
60
import java.awt.Shape;
61
import java.awt.geom.AffineTransform;
62
import java.awt.geom.FlatteningPathIterator;
63
import java.awt.geom.IllegalPathStateException;
64
import java.awt.geom.PathIterator;
65
import java.awt.geom.Point2D;
66
import java.awt.geom.Rectangle2D;
67
import java.io.Serializable;
68

    
69
import org.cresques.cts.ICoordTrans;
70

    
71
import com.vividsolutions.jts.algorithm.CGAlgorithms;
72
import com.vividsolutions.jts.geom.Coordinate;
73
import com.vividsolutions.jts.geom.CoordinateList;
74
import com.vividsolutions.jts.geom.CoordinateSequence;
75
import com.vividsolutions.jts.geom.CoordinateSequences;
76
import com.vividsolutions.jts.geom.GeometryFactory;
77
import com.vividsolutions.jts.geom.Point;
78
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
79

    
80
import sun.awt.geom.Crossings;
81
import sun.awt.geom.Curve;
82

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

    
114
    /**
115
     * A non-zero winding rule for determining the interior of a
116
     * path.  
117
     */
118
    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
119
    
120
    // For code simplicity, copy these constants to our namespace
121
    // and cast them to byte constants for easy storage.
122
    private static final byte SEG_MOVETO  = (byte) PathIterator.SEG_MOVETO;
123
    private static final byte SEG_LINETO  = (byte) PathIterator.SEG_LINETO;
124
    private static final byte SEG_QUADTO  = (byte) PathIterator.SEG_QUADTO;
125
    private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
126
    private static final byte SEG_CLOSE   = (byte) PathIterator.SEG_CLOSE;
127

    
128
    byte[] pointTypes;
129
    double[] pointCoords;
130
    int numTypes;
131
    int numCoords;
132
    int windingRule;
133

    
134
    static final int INIT_SIZE = 20;
135
    static final int EXPAND_MAX = 500;
136
    
137
    private static final int curvesize[] = {2, 2, 4, 6, 0};
138

    
139
    /**
140
     * Constructs a new <code>GeneralPathX</code> object.
141
     * If an operation performed on this path requires the
142
     * interior of the path to be defined then the default NON_ZERO
143
     * winding rule is used.
144
     * @see #WIND_NON_ZERO
145
     */
146
    public GeneralPathX() {
147
        // this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
148
            this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
149
    }
150

    
151
    /**
152
     * Constructs a new <code>GeneralPathX</code> object with the specified 
153
     * winding rule to control operations that require the interior of the
154
     * path to be defined.
155
     * @param rule the winding rule
156
     * @see #WIND_EVEN_ODD
157
     * @see #WIND_NON_ZERO
158
     */
159
    public GeneralPathX(int rule) {
160
        this(rule, INIT_SIZE, INIT_SIZE);
161
    }
162

    
163
    /**
164
     * Constructs a new <code>GeneralPathX</code> object with the specified 
165
     * winding rule and the specified initial capacity to store path 
166
     * coordinates. This number is an initial guess as to how many path 
167
     * segments are in the path, but the storage is expanded 
168
     * as needed to store whatever path segments are added to this path.
169
     * @param rule the winding rule
170
     * @param initialCapacity the estimate for the number of path segments
171
     * in the path
172
     * @see #WIND_EVEN_ODD
173
     * @see #WIND_NON_ZERO
174
     */
175
    public GeneralPathX(int rule, int initialCapacity) {
176
        this(rule, initialCapacity, initialCapacity);
177
    }
178

    
179
    /**
180
     * Constructs a new <code>GeneralPathX</code> object with the specified 
181
     * winding rule and the specified initial capacities to store point types
182
     * and coordinates.
183
     * These numbers are an initial guess as to how many path segments
184
     * and how many points are to be in the path, but the
185
     * storage is expanded as needed to store whatever path segments are
186
     * added to this path.
187
     * @param rule the winding rule
188
     * @param initialTypes the estimate for the number of path segments
189
     * in the path
190
     * @param initialCapacity the estimate for the number of points
191
     * @see #WIND_EVEN_ODD
192
     * @see #WIND_NON_ZERO
193
     */
194
    GeneralPathX(int rule, int initialTypes, int initialCoords) {
195
        setWindingRule(rule);
196
        pointTypes = new byte[initialTypes];
197
        pointCoords = new double[initialCoords * 2];
198
    }
199

    
200
    /**
201
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary 
202
     * {@link Shape} object.
203
     * All of the initial geometry and the winding rule for this path are
204
     * taken from the specified <code>Shape</code> object.
205
     * @param s the specified <code>Shape</code> object
206
     */
207
    public GeneralPathX(Shape s) {
208
        // this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
209
            this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
210
        PathIterator pi = s.getPathIterator(null);
211
        setWindingRule(pi.getWindingRule());
212
        append(pi, false);
213
    }
214
 
215
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
216
        if (needMove && numTypes == 0) {
217
            throw new IllegalPathStateException("missing initial moveto "+
218
                                                "in path definition");
219
        }
220
        int size = pointCoords.length;
221
        if (numCoords + newCoords > size) {
222
            int grow = size;
223
            if (grow > EXPAND_MAX * 2) {
224
                grow = EXPAND_MAX * 2;
225
            }
226
            if (grow < newCoords) {
227
                grow = newCoords;
228
            }
229
            double[] arr = new double[size + grow];
230
            System.arraycopy(pointCoords, 0, arr, 0, numCoords);
231
            pointCoords = arr;
232
        }
233
        size = pointTypes.length;
234
        if (numTypes + newTypes > size) {
235
            int grow = size;
236
            if (grow > EXPAND_MAX) {
237
                grow = EXPAND_MAX;
238
            }
239
            if (grow < newTypes) {
240
                grow = newTypes;
241
            }
242
            byte[] arr = new byte[size + grow];
243
            System.arraycopy(pointTypes, 0, arr, 0, numTypes);
244
            pointTypes = arr;
245
        }
246
    }
247

    
248
    /**
249
     * Adds a point to the path by moving to the specified
250
     * coordinates.
251
     * @param x,&nbsp;y the specified coordinates
252
     */
253
    public synchronized void moveTo(double x, double y) {
254
        if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
255
            pointCoords[numCoords - 2] = x;
256
            pointCoords[numCoords - 1] = y;
257
        } else {
258
            needRoom(1, 2, false);
259
            pointTypes[numTypes++] = SEG_MOVETO;
260
            pointCoords[numCoords++] = x;
261
            pointCoords[numCoords++] = y;
262
        }
263
    }
264

    
265
    /**
266
     * Adds a point to the path by drawing a straight line from the
267
     * current coordinates to the new specified coordinates.
268
     * @param x,&nbsp;y the specified coordinates
269
     */
270
    public synchronized void lineTo(double x, double y) {
271
        needRoom(1, 2, true);
272
        pointTypes[numTypes++] = SEG_LINETO;
273
        pointCoords[numCoords++] = x;
274
        pointCoords[numCoords++] = y;
275
    }
276

    
277
    /**
278
     * Adds a curved segment, defined by two new points, to the path by
279
     * drawing a Quadratic curve that intersects both the current
280
     * coordinates and the coordinates (x2,&nbsp;y2), using the 
281
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
282
     * point.
283
     * @param x1,&nbsp;y1 the coordinates of the first quadratic control
284
     *                point
285
     * @param x2,&nbsp;y2 the coordinates of the final endpoint
286
     */
287
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
288
        needRoom(1, 4, true);
289
        pointTypes[numTypes++] = SEG_QUADTO;
290
        pointCoords[numCoords++] = x1;
291
        pointCoords[numCoords++] = y1;
292
        pointCoords[numCoords++] = x2;
293
        pointCoords[numCoords++] = y2;
294
    }
295

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

    
321
    /**
322
     * Closes the current subpath by drawing a straight line back to
323
     * the coordinates of the last <code>moveTo</code>.  If the path is already
324
     * closed then this method has no effect.
325
     */
326
    public synchronized void closePath() {
327
        if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
328
            needRoom(1, 0, true);
329
            pointTypes[numTypes++] = SEG_CLOSE;
330
        }
331
    }
332

    
333
    public boolean isClosed()
334
    {
335
        double xFinal = pointCoords[numCoords -2];
336
        double yFinal = pointCoords[numCoords -1];
337
        double xIni = pointCoords[0];
338
        double yIni = pointCoords[1];
339

    
340
        if (pointTypes[numTypes-1] == SEG_CLOSE)
341
                return true;
342
        if ((xFinal == xIni) && (yFinal == yIni))
343
                return true;
344
        return false;
345

    
346
    }
347
    
348
    
349
    /**
350
     * Appends the geometry of the specified <code>Shape</code> object to the
351
     * path, possibly connecting the new geometry to the existing path
352
     * segments with a line segment.
353
     * If the <code>connect</code> parameter is <code>true</code> and the 
354
     * path is not empty then any initial <code>moveTo</code> in the
355
     * geometry of the appended <code>Shape</code>
356
     * is turned into a <code>lineTo</code> segment.
357
     * If the destination coordinates of such a connecting <code>lineTo</code>
358
     * segment match the ending coordinates of a currently open
359
     * subpath then the segment is omitted as superfluous.
360
     * The winding rule of the specified <code>Shape</code> is ignored
361
     * and the appended geometry is governed by the winding
362
     * rule specified for this path.
363
     * @param s the <code>Shape</code> whose geometry is appended 
364
     * to this path
365
     * @param connect a boolean to control whether or not to turn an
366
     * initial <code>moveTo</code> segment into a <code>lineTo</code>
367
     * segment to connect the new geometry to the existing path
368
     */
369
    public void append(Shape s, boolean connect) {
370
        PathIterator pi = s.getPathIterator(null);
371
        append(pi,connect);
372
    }
373

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

    
433
    /**
434
     * Returns the fill style winding rule.
435
     * @return an integer representing the current winding rule.
436
     * @see #WIND_EVEN_ODD  
437
     * @see #WIND_NON_ZERO
438
     * @see #setWindingRule
439
     */
440
    public synchronized int getWindingRule() {
441
        return windingRule;
442
    }
443

    
444
    /**
445
     * Sets the winding rule for this path to the specified value.
446
     * @param rule an integer representing the specified 
447
     * winding rule
448
     * @exception <code>IllegalArgumentException</code> if 
449
     *                <code>rule</code> is not either 
450
     *                <code>WIND_EVEN_ODD</code> or
451
     *                <code>WIND_NON_ZERO</code>
452
     * @see #WIND_EVEN_ODD  
453
     * @see #WIND_NON_ZERO
454
     * @see #getWindingRule
455
     */
456
    public void setWindingRule(int rule) {
457
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
458
            throw new IllegalArgumentException("winding rule must be "+
459
                                               "WIND_EVEN_ODD or "+
460
                                               "WIND_NON_ZERO");
461
        }
462
        windingRule = rule;
463
    }
464

    
465
    /**
466
     * Returns the coordinates most recently added to the end of the path
467
     * as a {@link Point2D} object.
468
     * @return a <code>Point2D</code> object containing the ending 
469
     * coordinates of the path or <code>null</code> if there are no points
470
     * in the path.
471
     */
472
    public synchronized Point2D getCurrentPoint() {
473
        if (numTypes < 1 || numCoords < 2) {
474
            return null;
475
        }
476
        int index = numCoords;
477
        if (pointTypes[numTypes - 1] == SEG_CLOSE) {
478
        loop:
479
            for (int i = numTypes - 2; i > 0; i--) {
480
                switch (pointTypes[i]) {
481
                case SEG_MOVETO:
482
                    break loop;
483
                case SEG_LINETO:
484
                    index -= 2;
485
                    break;
486
                case SEG_QUADTO:
487
                    index -= 4;
488
                    break;
489
                case SEG_CUBICTO:
490
                    index -= 6;
491
                    break;
492
                case SEG_CLOSE:
493
                    break;
494
                }
495
            }
496
        }
497
        return new Point2D.Double(pointCoords[index - 2],
498
                                 pointCoords[index - 1]);
499
    }
500

    
501
    /**
502
     * Resets the path to empty.  The append position is set back to the
503
     * beginning of the path and all coordinates and point types are
504
     * forgotten.
505
     */
506
    public synchronized void reset() {
507
        numTypes = numCoords = 0;
508
    }
509

    
510
    /**
511
     * Transforms the geometry of this path using the specified 
512
     * {@link AffineTransform}.
513
     * The geometry is transformed in place, which permanently changes the
514
     * boundary defined by this object.
515
     * @param at the <code>AffineTransform</code> used to transform the area
516
     */
517
    public void transform(AffineTransform at) {
518
        at.transform(pointCoords, 0, pointCoords, 0, numCoords / 2);
519
    }
520
    
521
    public void reProject(ICoordTrans ct)
522
    {
523
            Point2D pt = new Point2D.Double();
524
            for (int i = 0; i < numCoords; i+=2)
525
            {
526
                    pt.setLocation(pointCoords[i], pointCoords[i+1]);
527
                    pt = ct.convert(pt,null);
528
                    pointCoords[i] = pt.getX();
529
                    pointCoords[i+1] = pt.getY();
530
            }
531
            
532
    }
533

    
534

    
535
    /**
536
     * Returns a new transformed <code>Shape</code>.
537
     * @param at the <code>AffineTransform</code> used to transform a 
538
     * new <code>Shape</code>.
539
     * @return a new <code>Shape</code>, transformed with the specified 
540
     * <code>AffineTransform</code>.
541
     */
542
    public synchronized Shape createTransformedShape(AffineTransform at) {
543
        GeneralPathX gp = (GeneralPathX) clone();
544
        if (at != null) {
545
            gp.transform(at);
546
        }
547
        return gp;
548
    }
549

    
550
    /**
551
     * Return the bounding box of the path.
552
     * @return a {@link java.awt.Rectangle} object that
553
     * bounds the current path.
554
     */
555
    public java.awt.Rectangle getBounds() {
556
        return getBounds2D().getBounds();
557
    }
558

    
559
    /**
560
     * Returns the bounding box of the path.
561
     * @return a {@link Rectangle2D} object that
562
     *          bounds the current path.
563
     */
564
    public synchronized Rectangle2D getBounds2D() {
565
        double x1, y1, x2, y2;
566
        int i = numCoords;
567
        if (i > 0) {
568
            y1 = y2 = pointCoords[--i];
569
            x1 = x2 = pointCoords[--i];
570
            while (i > 0) {
571
                double y = pointCoords[--i];
572
                double x = pointCoords[--i];
573
                if (x < x1) x1 = x;
574
                if (y < y1) y1 = y;
575
                if (x > x2) x2 = x;
576
                if (y > y2) y2 = y;
577
            }
578
        } else {
579
            x1 = y1 = x2 = y2 = 0.0f;
580
        }
581
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
582
    }
583

    
584
    /**
585
     * Tests if the specified coordinates are inside the boundary of 
586
     * this <code>Shape</code>.
587
     * @param x,&nbsp;y the specified coordinates
588
     * @return <code>true</code> if the specified coordinates are inside this 
589
     * <code>Shape</code>; <code>false</code> otherwise
590
     */
591
    public boolean contains(double x, double y) {
592
        if (numTypes < 2) {
593
            return false;
594
        }
595
        int cross = Curve.crossingsForPath(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
     * @param p the specified <code>Point2D</code>
607
     * @return <code>true</code> if this <code>Shape</code> contains the 
608
     * specified <code>Point2D</code>, <code>false</code> otherwise.
609
     */
610
    public boolean contains(Point2D p) {
611
        return contains(p.getX(), p.getY());
612
    }
613

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

    
629
    /**
630
     * Tests if the specified <code>Rectangle2D</code>
631
     * is inside the boundary of this <code>Shape</code>.
632
     * @param r a specified <code>Rectangle2D</code>
633
     * @return <code>true</code> if this <code>Shape</code> bounds the 
634
     * specified <code>Rectangle2D</code>; <code>false</code> otherwise.
635
     */
636
    public boolean contains(Rectangle2D r) {
637
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
638
    }
639

    
640
    /**
641
     * Tests if the interior of this <code>Shape</code> intersects the 
642
     * interior of a specified set of rectangular coordinates.
643
     * @param x,&nbsp;y the specified coordinates
644
     * @param w the width of the specified rectangular coordinates
645
     * @param h the height of the specified rectangular coordinates
646
     * @return <code>true</code> if this <code>Shape</code> and the 
647
     * interior of the specified set of rectangular coordinates intersect
648
     * each other; <code>false</code> otherwise.
649
     */
650
    public boolean intersects(double x, double y, double w, double h) {
651
        Crossings c = Crossings.findCrossings(getPathIterator(null),
652
                                              x, y, x+w, y+h);
653
        return (c == null || !c.isEmpty());
654
    }
655

    
656
    /**
657
     * Tests if the interior of this <code>Shape</code> intersects the 
658
     * interior of a specified <code>Rectangle2D</code>.
659
     * @param r the specified <code>Rectangle2D</code>
660
     * @return <code>true</code> if this <code>Shape</code> and the interior 
661
     *                 of the specified <code>Rectangle2D</code> intersect each
662
     *                 other; <code>false</code> otherwise.
663
     */
664
    public boolean intersects(Rectangle2D r) {
665
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
666
    }
667

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

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

    
706
    /**
707
     * Creates a new object of the same class as this object.
708
     *
709
     * @return     a clone of this instance.
710
     * @exception  OutOfMemoryError            if there is not enough memory.
711
     * @see        java.lang.Cloneable
712
     * @since      1.2
713
     */
714
    public Object clone() {
715
        try {
716
            GeneralPathX copy = (GeneralPathX) super.clone();
717
            copy.pointTypes = (byte[]) pointTypes.clone();
718
            copy.pointCoords = (double[]) pointCoords.clone();
719
            return copy;
720
        } catch (CloneNotSupportedException e) {
721
            // this shouldn't happen, since we are Cloneable
722
            throw new InternalError();
723
        }
724
    }
725

    
726
    GeneralPathX(int windingRule, 
727
                byte[] pointTypes,
728
                int numTypes,
729
                double[] pointCoords,
730
                int numCoords) {
731

    
732
    // used to construct from native
733

    
734
        this.windingRule = windingRule;
735
        this.pointTypes = pointTypes;
736
        this.numTypes = numTypes;
737
        this.pointCoords = pointCoords;
738
        this.numCoords = numCoords;
739
    }
740
    
741
    /**
742
     * TODO: TERMINAR ESTO!!
743
     */
744
    public void flip()
745
        {
746
        byte[] pointTypesAux = new byte[numTypes];
747
        double[] pointCoordsAux = new double[numCoords];
748
        int i;
749
        
750
        for (i=0; i< numTypes; i++)
751
                pointTypesAux[numTypes - i -1] = pointTypes[i];
752
        int pointIdx = 0;
753
        for (i=0; i< numTypes; i++)
754
        {
755
                int type = pointTypes[i++];
756
                pointIdx += curvesize[type];
757
                switch (type)
758
                {
759
                case SEG_MOVETO:
760
                        break;
761
                case SEG_LINETO:
762
                        break;
763
                case SEG_QUADTO:
764
                        break;
765
                case SEG_CUBICTO:
766
                        break;
767
                        
768
                }
769
        }
770
        
771
        pointTypes = pointTypesAux;
772
        pointCoords = pointCoordsAux;            
773
        }
774

    
775
        /**
776
         * Use this function to ensure you get real polygons or holes
777
         * En JTS, con bCCW = false obtienes un pol?gono exterior.
778
         * Nota: Solo se le da la vuelta (si es que lo necesita) al
779
         * pol?gono exterior. El resto, por ahora, no se tocan.
780
         * Si se necesita tenerlos en cuenta, habr?a que mirar
781
         * si est?n dentro del otro, y entonces revisar que tiene
782
         * un CCW contrario al exterior.
783
         * @param bCCW true if you want the GeneralPath in CCW order
784
         * @return true si se le ha dado la vuelta. (true if flipped)
785
         * TODO: TERMINAR ESTO!! NO EST? COMPLETO!! NO sirve para multipoligonos
786
         */
787
        public boolean ensureOrientation(boolean bCCW) {
788
        byte[] pointTypesAux = new byte[numTypes+1];
789
        double[] pointCoordsAux = new double[numCoords+2];
790
        int i;
791
        int pointIdx = 0;
792
        
793
        Coordinate c1, c2, c3;
794
        CoordinateList coordList = new CoordinateList();
795
        CoordinateList firstList = new CoordinateList();
796
        boolean bFirstList = true;
797
        Coordinate cInicio = null;
798
        
799
        for (i=0; i< numTypes; i++)
800
        {
801
                int type = pointTypes[i];
802
                
803
                switch (type)
804
                {
805
                case SEG_MOVETO:
806
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
807
                        cInicio = c1;
808
                        coordList.add(c1, true);
809
                        if (i>0) bFirstList = false;
810
                        if (bFirstList)
811
                        {
812
                                firstList.add(c1,true);
813
                        }
814
                        break;
815
                case SEG_LINETO:
816
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
817
                        coordList.add(c1, true);
818
                        if (bFirstList)
819
                        {
820
                                firstList.add(c1,true);                                
821
                        }
822
                        break;
823
                case SEG_QUADTO:
824
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
825
                        coordList.add(c1, true);
826
                        c2= new Coordinate(pointCoords[pointIdx+2], pointCoords[pointIdx+3]);
827
                        coordList.add(c2, true);
828
                        if (bFirstList)
829
                        {
830
                                firstList.add(c1,true);
831
                                firstList.add(c2,true);
832
                        }
833
                        
834
                        break;
835
                case SEG_CUBICTO:
836
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
837
                        coordList.add(c1, true);
838
                        c2= new Coordinate(pointCoords[pointIdx+2], pointCoords[pointIdx+3]);
839
                        coordList.add(c2, true);
840
                        c3= new Coordinate(pointCoords[pointIdx+4], pointCoords[pointIdx+5]);
841
                        coordList.add(c3, true);                        
842
                        if (bFirstList)
843
                        {
844
                                firstList.add(c1,true);
845
                                firstList.add(c2,true);
846
                                firstList.add(c3,true);
847
                        }
848
                        
849
                        break;
850
                case SEG_CLOSE:
851
                        coordList.add(cInicio, true);
852
                        if (bFirstList)
853
                        {
854
                                firstList.add(cInicio,true);
855
                        }                        
856
                        break;
857
                        
858
                }
859
                pointIdx += curvesize[type];
860
        }
861
                // Guardamos el path dandole la vuelta
862
                Coordinate[] coords = coordList.toCoordinateArray();
863
                boolean bFlipped = false;
864
                if (CGAlgorithms.isCCW(coords) != bCCW) // Le damos la vuelta
865
                {
866
                        CoordinateArraySequence seq = new CoordinateArraySequence(coords);
867
                        CoordinateSequences.reverse(seq);
868
                        coords = seq.toCoordinateArray();
869

    
870

    
871
                        // En el primer punto metemos un moveto
872
                        pointCoordsAux[0] = coords[0].x;
873
                        pointCoordsAux[1] = coords[0].y;
874
                        pointTypesAux[0] = SEG_MOVETO;
875
                        int idx = 2;
876
                        i=0;
877
                        int j=1;
878
                        for (int k=0; k < coords.length; k++)
879
                        {
880
                                pointCoordsAux[idx++] = coords[k].x;
881
                                pointCoordsAux[idx++] = coords[k].y;
882
                        int type = pointTypes[i++];
883
                        pointIdx += curvesize[type];
884
                        switch (type)
885
                        {
886
                        case SEG_MOVETO:
887
                                pointTypesAux[j] = SEG_LINETO;
888
                                break;
889
                        case SEG_LINETO:
890
                                pointTypesAux[j] = SEG_LINETO;
891
                                break;
892
                        case SEG_QUADTO:
893
                                pointTypesAux[j] = SEG_QUADTO;
894
                                break;
895
                        case SEG_CUBICTO:
896
                                pointTypesAux[j] = SEG_CUBICTO;
897
                                break;
898
                        case SEG_CLOSE:
899
                                // TODO: IMPLEMENTAR ESTO!!!
900
                                break;
901
                                
902
                        }
903
                        j++;
904
                                
905
                        }
906
                
907
                pointTypes = pointTypesAux;
908
                pointCoords = pointCoordsAux;
909
                numCoords= numCoords+2;
910
                numTypes++;
911
                bFlipped  = true;
912

    
913
                }
914
                return bFlipped;
915
        }
916
        
917
    public boolean isCCW()
918
    {
919
        int i;
920
        
921
        int pointIdx = 0;
922
        Coordinate c1;
923
        CoordinateList coordList = new CoordinateList();
924
        for (i=0; i< numTypes; i++)
925
        {
926
                int type = pointTypes[i++];
927
                pointIdx += curvesize[type];
928
                switch (type)
929
                {
930
                case SEG_MOVETO:
931
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
932
                        coordList.add(c1, true);
933
                        break;
934
                case SEG_LINETO:
935
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
936
                        coordList.add(c1, true);
937
                        break;
938
                case SEG_QUADTO:
939
                        // TODO: IMPLEMENTAR ESTO!!!
940
                        break;
941
                case SEG_CUBICTO:
942
                        // TODO: IMPLEMENTAR ESTO!!!
943
                        break;
944
                case SEG_CLOSE:
945
                        // TODO: IMPLEMENTAR ESTO!!!
946
                        break;
947
                        
948
                }
949
        }
950
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
951
            
952
    }
953

    
954
}