Statistics
| Revision:

root / trunk / libraries / libFMap_geometries / src / org / gvsig / fmap / geom / primitive / GeneralPathX.java @ 20899

History | View | Annotate | Download (32.4 KB)

1 20761 jmvivo
/*
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 org.gvsig.fmap.geom.primitive;
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
import java.util.ArrayList;
69
70
import org.cresques.cts.ICoordTrans;
71 20861 jiyarza
import org.gvsig.fmap.geom.util.Converter;
72 20761 jmvivo
73
import sun.awt.geom.Crossings;
74
import sun.awt.geom.Curve;
75
76
import com.vividsolutions.jts.algorithm.CGAlgorithms;
77
import com.vividsolutions.jts.geom.Coordinate;
78
import com.vividsolutions.jts.geom.CoordinateList;
79
import com.vividsolutions.jts.geom.CoordinateSequences;
80
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
81
82
/**
83
 * The <code>GeneralPathX</code> class represents a geometric path
84
 * constructed from straight lines, and quadratic and cubic
85
 * (B&eacute;zier) curves.  It can contain multiple subpaths.
86
 * <p>
87
 * The winding rule specifies how the interior of a path is
88
 * determined.  There are two types of winding rules:
89
 * EVEN_ODD and NON_ZERO.
90
 * <p>
91
 * An EVEN_ODD winding rule means that enclosed regions
92
 * of the path alternate between interior and exterior areas as
93
 * traversed from the outside of the path towards a point inside
94
 * the region.
95
 * <p>
96
 * A NON_ZERO winding rule means that if a ray is
97
 * drawn in any direction from a given point to infinity
98
 * and the places where the path intersects
99
 * the ray are examined, the point is inside of the path if and only if
100
 * the number of times that the path crosses the ray from
101
 * left to right does not equal the  number of times that the path crosses
102
 * the ray from right to left.
103
 * @version 1.58, 01/23/03
104
 * @author Jim Graham
105
 */
106
public class GeneralPathX implements Shape, Cloneable, Serializable {
107
    /**
108
         * Default serial version ID
109
         */
110
        private static final long serialVersionUID = 1L;
111
112
        /**
113
     * An even-odd winding rule for determining the interior of
114
     * a path.
115
     */
116
    public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
117
118
    /**
119
     * A non-zero winding rule for determining the interior of a
120
     * path.
121
     */
122
    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
123
124
    // For code simplicity, copy these constants to our namespace
125
    // and cast them to byte constants for easy storage.
126
    private static final byte SEG_MOVETO  = (byte) PathIterator.SEG_MOVETO;
127
    private static final byte SEG_LINETO  = (byte) PathIterator.SEG_LINETO;
128
    private static final byte SEG_QUADTO  = (byte) PathIterator.SEG_QUADTO;
129
    private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
130
    private static final byte SEG_CLOSE   = (byte) PathIterator.SEG_CLOSE;
131
132
    byte[] pointTypes;
133
    double[] pointCoords;
134
    int numTypes;
135
    int numCoords;
136
    int windingRule;
137
138
    static final int INIT_SIZE = 20;
139
    static final int EXPAND_MAX = 500;
140
141
    private static final int curvesize[] = {2, 2, 4, 6, 0};
142
143
    /**
144
     * Constructs a new <code>GeneralPathX</code> object.
145
     * If an operation performed on this path requires the
146
     * interior of the path to be defined then the default NON_ZERO
147
     * winding rule is used.
148
     * @see #WIND_NON_ZERO
149
     */
150
    public GeneralPathX() {
151
        // this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
152
            this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
153
    }
154
155
    /**
156
     * Constructs a new <code>GeneralPathX</code> object with the specified
157
     * winding rule to control operations that require the interior of the
158
     * path to be defined.
159
     * @param rule the winding rule
160
     * @see #WIND_EVEN_ODD
161
     * @see #WIND_NON_ZERO
162
     */
163
    public GeneralPathX(int rule) {
164
        this(rule, INIT_SIZE, INIT_SIZE);
165
    }
166
167
    /**
168
     * Constructs a new <code>GeneralPathX</code> object with the specified
169
     * winding rule and the specified initial capacity to store path
170
     * coordinates. This number is an initial guess as to how many path
171
     * segments are in the path, but the storage is expanded
172
     * as needed to store whatever path segments are added to this path.
173
     * @param rule the winding rule
174
     * @param initialCapacity the estimate for the number of path segments
175
     * in the path
176
     * @see #WIND_EVEN_ODD
177
     * @see #WIND_NON_ZERO
178
     */
179
    public GeneralPathX(int rule, int initialCapacity) {
180
        this(rule, initialCapacity, initialCapacity);
181
    }
182
183
    /**
184
     * Constructs a new <code>GeneralPathX</code> object with the specified
185
     * winding rule and the specified initial capacities to store point types
186
     * and coordinates.
187
     * These numbers are an initial guess as to how many path segments
188
     * and how many points are to be in the path, but the
189
     * storage is expanded as needed to store whatever path segments are
190
     * added to this path.
191
     * @param rule the winding rule
192
     * @param initialTypes the estimate for the number of path segments
193
     * in the path
194
     * @param initialCapacity the estimate for the number of points
195
     * @see #WIND_EVEN_ODD
196
     * @see #WIND_NON_ZERO
197
     */
198
    GeneralPathX(int rule, int initialTypes, int initialCoords) {
199
        setWindingRule(rule);
200
        pointTypes = new byte[initialTypes];
201
        pointCoords = new double[initialCoords * 2];
202
    }
203
204
    /**
205
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
206
     * {@link Shape} object.
207
     * All of the initial geometry and the winding rule for this path are
208
     * taken from the specified <code>Shape</code> object.
209
     * @param s the specified <code>Shape</code> object
210
     */
211
    public GeneralPathX(Shape s) {
212
        // this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
213
            this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
214
        PathIterator pi = s.getPathIterator(null);
215
        setWindingRule(pi.getWindingRule());
216
        append(pi, false);
217
    }
218
219
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
220
        if (needMove && numTypes == 0) {
221
            throw new IllegalPathStateException("missing initial moveto "+
222
                                                "in path definition");
223
        }
224
        int size = pointCoords.length;
225
        if (numCoords + newCoords > size) {
226
            int grow = size;
227
            if (grow > EXPAND_MAX * 2) {
228
                grow = EXPAND_MAX * 2;
229
            }
230
            if (grow < newCoords) {
231
                grow = newCoords;
232
            }
233
            double[] arr = new double[size + grow];
234
            System.arraycopy(pointCoords, 0, arr, 0, numCoords);
235
            pointCoords = arr;
236
        }
237
        size = pointTypes.length;
238
        if (numTypes + newTypes > size) {
239
            int grow = size;
240
            if (grow > EXPAND_MAX) {
241
                grow = EXPAND_MAX;
242
            }
243
            if (grow < newTypes) {
244
                grow = newTypes;
245
            }
246
            byte[] arr = new byte[size + grow];
247
            System.arraycopy(pointTypes, 0, arr, 0, numTypes);
248
            pointTypes = arr;
249
        }
250
    }
251
252
    /**
253
     * Adds a point to the path by moving to the specified
254
     * coordinates.
255
     * @param x,&nbsp;y the specified coordinates
256
     */
257
    public synchronized void moveTo(double x, double y) {
258
        if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
259
            pointCoords[numCoords - 2] = x;
260
            pointCoords[numCoords - 1] = y;
261
        } else {
262
            needRoom(1, 2, false);
263
            pointTypes[numTypes++] = SEG_MOVETO;
264
            pointCoords[numCoords++] = x;
265
            pointCoords[numCoords++] = y;
266
        }
267
    }
268
269
    /**
270
     * Adds a point to the path by drawing a straight line from the
271
     * current coordinates to the new specified coordinates.
272
     * @param x,&nbsp;y the specified coordinates
273
     */
274
    public synchronized void lineTo(double x, double y) {
275
        needRoom(1, 2, true);
276
        pointTypes[numTypes++] = SEG_LINETO;
277
        pointCoords[numCoords++] = x;
278
        pointCoords[numCoords++] = y;
279
    }
280
281
    /**
282
     * Adds a curved segment, defined by two new points, to the path by
283
     * drawing a Quadratic curve that intersects both the current
284
     * coordinates and the coordinates (x2,&nbsp;y2), using the
285
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
286
     * point.
287
     * @param x1,&nbsp;y1 the coordinates of the first quadratic control
288
     *                point
289
     * @param x2,&nbsp;y2 the coordinates of the final endpoint
290
     */
291
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
292
        needRoom(1, 4, true);
293
        pointTypes[numTypes++] = SEG_QUADTO;
294
        pointCoords[numCoords++] = x1;
295
        pointCoords[numCoords++] = y1;
296
        pointCoords[numCoords++] = x2;
297
        pointCoords[numCoords++] = y2;
298
    }
299
300
    /**
301
     * Adds a curved segment, defined by three new points, to the path by
302
     * drawing a B&eacute;zier curve that intersects both the current
303
     * coordinates and the coordinates (x3,&nbsp;y3), using the
304
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
305
     * B&eacute;zier control points.
306
     * @param x1,&nbsp;y1 the coordinates of the first B&eacute;ezier
307
     *                control point
308
     * @param x2,&nbsp;y2 the coordinates of the second B&eacute;zier
309
     *                control point
310
     * @param x3,&nbsp;y3 the coordinates of the final endpoint
311
     */
312
    public synchronized void curveTo(double x1, double y1,
313
                    double x2, double y2,
314
                    double x3, double y3) {
315
        needRoom(1, 6, true);
316
        pointTypes[numTypes++] = SEG_CUBICTO;
317
        pointCoords[numCoords++] = x1;
318
        pointCoords[numCoords++] = y1;
319
        pointCoords[numCoords++] = x2;
320
        pointCoords[numCoords++] = y2;
321
        pointCoords[numCoords++] = x3;
322
        pointCoords[numCoords++] = y3;
323
    }
324
325
    /**
326
     * Closes the current subpath by drawing a straight line back to
327
     * the coordinates of the last <code>moveTo</code>.  If the path is already
328
     * closed then this method has no effect.
329
     */
330
    public synchronized void closePath() {
331
        if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
332
            needRoom(1, 0, true);
333
            pointTypes[numTypes++] = SEG_CLOSE;
334
        }
335
    }
336
337
    /**
338
     * Check if the first part is closed.
339
     * @return
340
     */
341
    public boolean isClosed()
342
    {
343 20861 jiyarza
                PathIterator theIterator = getPathIterator(null, Converter.FLATNESS); //polyLine.getPathIterator(null, flatness);
344 20761 jmvivo
                double[] theData = new double[6];
345
        double xFinal = 0;
346
        double yFinal = 0;
347
        double xIni = 0;
348
        double yIni = 0;
349
        boolean first = true;
350
351
                while (!theIterator.isDone()) {
352
                        //while not done
353
                        int theType = theIterator.currentSegment(theData);
354
355
                        switch (theType) {
356
                                case PathIterator.SEG_MOVETO:
357
                                        xIni = theData[0];
358
                                        yIni = theData[1];
359
                                        if (!first)
360
                                        {
361
                                                break;
362
                                        }
363
                                        first = false;
364
                                        break;
365
366
                                case PathIterator.SEG_LINETO:
367
                                        xFinal = theData[0];
368
                                        yFinal = theData[1];
369
                                        break;
370
                                case PathIterator.SEG_CLOSE:
371
                                        return true;
372
373
                        } //end switch
374
375
                        theIterator.next();
376
                }
377
              if ((xFinal == xIni) && (yFinal == yIni))
378
                    return true;
379
            return false;
380
381
382
383
//        double xFinal = pointCoords[numCoords -2];
384
//        double yFinal = pointCoords[numCoords -1];
385
//        double xIni = pointCoords[0];
386
//        double yIni = pointCoords[1];
387
//
388
//        if (pointTypes[numTypes-1] == SEG_CLOSE)
389
//                return true;
390
//        if ((xFinal == xIni) && (yFinal == yIni))
391
//                return true;
392
//        return false;
393
394
    }
395
396
397
    /**
398
     * Appends the geometry of the specified <code>Shape</code> object to the
399
     * path, possibly connecting the new geometry to the existing path
400
     * segments with a line segment.
401
     * If the <code>connect</code> parameter is <code>true</code> and the
402
     * path is not empty then any initial <code>moveTo</code> in the
403
     * geometry of the appended <code>Shape</code>
404
     * is turned into a <code>lineTo</code> segment.
405
     * If the destination coordinates of such a connecting <code>lineTo</code>
406
     * segment match the ending coordinates of a currently open
407
     * subpath then the segment is omitted as superfluous.
408
     * The winding rule of the specified <code>Shape</code> is ignored
409
     * and the appended geometry is governed by the winding
410
     * rule specified for this path.
411
     * @param s the <code>Shape</code> whose geometry is appended
412
     * to this path
413
     * @param connect a boolean to control whether or not to turn an
414
     * initial <code>moveTo</code> segment into a <code>lineTo</code>
415
     * segment to connect the new geometry to the existing path
416
     */
417
    public void append(Shape s, boolean connect) {
418
        PathIterator pi = s.getPathIterator(null);
419
        append(pi,connect);
420
    }
421
422
    /**
423
     * Appends the geometry of the specified
424
     * {@link PathIterator} object
425
     * to the path, possibly connecting the new geometry to the existing
426
     * path segments with a line segment.
427
     * If the <code>connect</code> parameter is <code>true</code> and the
428
     * path is not empty then any initial <code>moveTo</code> in the
429
     * geometry of the appended <code>Shape</code> is turned into a
430
     * <code>lineTo</code> segment.
431
     * If the destination coordinates of such a connecting <code>lineTo</code>
432
     * segment match the ending coordinates of a currently open
433
     * subpath then the segment is omitted as superfluous.
434
     * The winding rule of the specified <code>Shape</code> is ignored
435
     * and the appended geometry is governed by the winding
436
     * rule specified for this path.
437
     * @param pi the <code>PathIterator</code> whose geometry is appended to
438
     * this path
439
     * @param connect a boolean to control whether or not to turn an
440
     * initial <code>moveTo</code> segment into a <code>lineTo</code> segment
441
     * to connect the new geometry to the existing path
442
     */
443
    public void append(PathIterator pi, boolean connect) {
444
        double coords[] = new double[6];
445
        while (!pi.isDone()) {
446
            switch (pi.currentSegment(coords)) {
447
            case SEG_MOVETO:
448
                if (!connect || numTypes < 1 || numCoords < 2) {
449
                    moveTo(coords[0], coords[1]);
450
                    break;
451
                }
452
                if (pointTypes[numTypes - 1] != SEG_CLOSE &&
453
                    pointCoords[numCoords - 2] == coords[0] &&
454
                    pointCoords[numCoords - 1] == coords[1])
455
                {
456
                    // Collapse out initial moveto/lineto
457
                    break;
458
                }
459
                // NO BREAK;
460
            case SEG_LINETO:
461
                lineTo(coords[0], coords[1]);
462
                break;
463
            case SEG_QUADTO:
464
                quadTo(coords[0], coords[1],
465
                       coords[2], coords[3]);
466
                break;
467
            case SEG_CUBICTO:
468
                curveTo(coords[0], coords[1],
469
                        coords[2], coords[3],
470
                        coords[4], coords[5]);
471
                break;
472
            case SEG_CLOSE:
473
                closePath();
474
                break;
475
            }
476
            pi.next();
477
            connect = false;
478
        }
479
    }
480
481
    /**
482
     * Returns the fill style winding rule.
483
     * @return an integer representing the current winding rule.
484
     * @see #WIND_EVEN_ODD
485
     * @see #WIND_NON_ZERO
486
     * @see #setWindingRule
487
     */
488
    public synchronized int getWindingRule() {
489
        return windingRule;
490
    }
491
492
    /**
493
     * Sets the winding rule for this path to the specified value.
494
     * @param rule an integer representing the specified
495
     * winding rule
496
     * @exception <code>IllegalArgumentException</code> if
497
     *                <code>rule</code> is not either
498
     *                <code>WIND_EVEN_ODD</code> or
499
     *                <code>WIND_NON_ZERO</code>
500
     * @see #WIND_EVEN_ODD
501
     * @see #WIND_NON_ZERO
502
     * @see #getWindingRule
503
     */
504
    public void setWindingRule(int rule) {
505
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
506
            throw new IllegalArgumentException("winding rule must be "+
507
                                               "WIND_EVEN_ODD or "+
508
                                               "WIND_NON_ZERO");
509
        }
510
        windingRule = rule;
511
    }
512
513
    /**
514
     * Returns the coordinates most recently added to the end of the path
515
     * as a {@link Point2D} object.
516
     * @return a <code>Point2D</code> object containing the ending
517
     * coordinates of the path or <code>null</code> if there are no points
518
     * in the path.
519
     */
520
    public synchronized Point2D getCurrentPoint() {
521
        if (numTypes < 1 || numCoords < 2) {
522
            return null;
523
        }
524
        int index = numCoords;
525
        if (pointTypes[numTypes - 1] == SEG_CLOSE) {
526
        loop:
527
            for (int i = numTypes - 2; i > 0; i--) {
528
                switch (pointTypes[i]) {
529
                case SEG_MOVETO:
530
                    break loop;
531
                case SEG_LINETO:
532
                    index -= 2;
533
                    break;
534
                case SEG_QUADTO:
535
                    index -= 4;
536
                    break;
537
                case SEG_CUBICTO:
538
                    index -= 6;
539
                    break;
540
                case SEG_CLOSE:
541
                    break;
542
                }
543
            }
544
        }
545
        return new Point2D.Double(pointCoords[index - 2],
546
                                 pointCoords[index - 1]);
547
    }
548
549
    /**
550
     * Resets the path to empty.  The append position is set back to the
551
     * beginning of the path and all coordinates and point types are
552
     * forgotten.
553
     */
554
    public synchronized void reset() {
555
        numTypes = numCoords = 0;
556
    }
557
558
    /**
559
     * Transforms the geometry of this path using the specified
560
     * {@link AffineTransform}.
561
     * The geometry is transformed in place, which permanently changes the
562
     * boundary defined by this object.
563
     * @param at the <code>AffineTransform</code> used to transform the area
564
     */
565
    public void transform(AffineTransform at) {
566
        at.transform(pointCoords, 0, pointCoords, 0, numCoords / 2);
567
    }
568
569
    public void reProject(ICoordTrans ct)
570
    {
571
            Point2D pt = new Point2D.Double();
572
            for (int i = 0; i < numCoords; i+=2)
573
            {
574
                    pt.setLocation(pointCoords[i], pointCoords[i+1]);
575
                    pt = ct.convert(pt,null);
576
                    pointCoords[i] = pt.getX();
577
                    pointCoords[i+1] = pt.getY();
578
            }
579
580
    }
581
582
583
    /**
584
     * Returns a new transformed <code>Shape</code>.
585
     * @param at the <code>AffineTransform</code> used to transform a
586
     * new <code>Shape</code>.
587
     * @return a new <code>Shape</code>, transformed with the specified
588
     * <code>AffineTransform</code>.
589
     */
590
    public synchronized Shape createTransformedShape(AffineTransform at) {
591
        GeneralPathX gp = (GeneralPathX) clone();
592
        if (at != null) {
593
            gp.transform(at);
594
        }
595
        return gp;
596
    }
597
598
    /**
599
     * Return the bounding box of the path.
600
     * @return a {@link java.awt.Rectangle} object that
601
     * bounds the current path.
602
     */
603
    public java.awt.Rectangle getBounds() {
604
        return getBounds2D().getBounds();
605
    }
606
607
    /**
608
     * Returns the bounding box of the path.
609
     * @return a {@link Rectangle2D} object that
610
     *          bounds the current path.
611
     */
612
    public synchronized Rectangle2D getBounds2D() {
613
        double x1, y1, x2, y2;
614
        int i = numCoords;
615
        if (i > 0) {
616
            y1 = y2 = pointCoords[--i];
617
            x1 = x2 = pointCoords[--i];
618
            while (i > 0) {
619
                double y = pointCoords[--i];
620
                double x = pointCoords[--i];
621
                if (x < x1) x1 = x;
622
                if (y < y1) y1 = y;
623
                if (x > x2) x2 = x;
624
                if (y > y2) y2 = y;
625
            }
626
        } else {
627
            x1 = y1 = x2 = y2 = 0.0f;
628
        }
629
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
630
    }
631
632
    /**
633
     * Tests if the specified coordinates are inside the boundary of
634
     * this <code>Shape</code>.
635
     * @param x,&nbsp;y the specified coordinates
636
     * @return <code>true</code> if the specified coordinates are inside this
637
     * <code>Shape</code>; <code>false</code> otherwise
638
     */
639
    public boolean contains(double x, double y) {
640
        if (numTypes < 2) {
641
            return false;
642
        }
643
//        int cross = sun.awt.geom.Curve.pointCrossingsForPath(getPathIterator(null), x, y);
644
        int cross = Curve.crossingsForPath(getPathIterator(null), x, y);
645
        if (windingRule == WIND_NON_ZERO) {
646
            return (cross != 0);
647
        } else {
648
            return ((cross & 1) != 0);
649
        }
650
    }
651
652
    /**
653
     * Tests if the specified <code>Point2D</code> is inside the boundary
654
     * of this <code>Shape</code>.
655
     * @param p the specified <code>Point2D</code>
656
     * @return <code>true</code> if this <code>Shape</code> contains the
657
     * specified <code>Point2D</code>, <code>false</code> otherwise.
658
     */
659
    public boolean contains(Point2D p) {
660
        return contains(p.getX(), p.getY());
661
    }
662
663
    /**
664
     * Tests if the specified rectangular area is inside the boundary of
665
     * this <code>Shape</code>.
666
     * @param x,&nbsp;y the specified coordinates
667
     * @param w the width of the specified rectangular area
668
     * @param h the height of the specified rectangular area
669
     * @return <code>true</code> if this <code>Shape</code> contains
670
     * the specified rectangluar area; <code>false</code> otherwise.
671
     */
672
    public boolean contains(double x, double y, double w, double h) {
673
        Crossings c = Crossings.findCrossings(getPathIterator(null),
674
                                              x, y, x+w, y+h);
675
        return (c != null && c.covers(y, y+h));
676
    }
677
678
    /**
679
     * Tests if the specified <code>Rectangle2D</code>
680
     * is inside the boundary of this <code>Shape</code>.
681
     * @param r a specified <code>Rectangle2D</code>
682
     * @return <code>true</code> if this <code>Shape</code> bounds the
683
     * specified <code>Rectangle2D</code>; <code>false</code> otherwise.
684
     */
685
    public boolean contains(Rectangle2D r) {
686
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
687
    }
688
689
    /**
690
     * Tests if the interior of this <code>Shape</code> intersects the
691
     * interior of a specified set of rectangular coordinates.
692
     * @param x,&nbsp;y the specified coordinates
693
     * @param w the width of the specified rectangular coordinates
694
     * @param h the height of the specified rectangular coordinates
695
     * @return <code>true</code> if this <code>Shape</code> and the
696
     * interior of the specified set of rectangular coordinates intersect
697
     * each other; <code>false</code> otherwise.
698
     */
699
    public boolean intersects(double x, double y, double w, double h) {
700
        Crossings c = Crossings.findCrossings(getPathIterator(null),
701
                                              x, y, x+w, y+h);
702
        return (c == null || !c.isEmpty());
703
    }
704
705
    /**
706
     * Tests if the interior of this <code>Shape</code> intersects the
707
     * interior of a specified <code>Rectangle2D</code>.
708
     * @param r the specified <code>Rectangle2D</code>
709
     * @return <code>true</code> if this <code>Shape</code> and the interior
710
     *                 of the specified <code>Rectangle2D</code> intersect each
711
     *                 other; <code>false</code> otherwise.
712
     */
713
    public boolean intersects(Rectangle2D r) {
714
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
715
    }
716
717
    /**
718
     * Returns a <code>PathIterator</code> object that iterates along the
719
     * boundary of this <code>Shape</code> and provides access to the
720
     * geometry of the outline of this <code>Shape</code>.
721
     * The iterator for this class is not multi-threaded safe,
722
     * which means that this <code>GeneralPathX</code> class does not
723
     * guarantee that modifications to the geometry of this
724
     * <code>GeneralPathX</code> object do not affect any iterations of
725
     * that geometry that are already in process.
726
     * @param at an <code>AffineTransform</code>
727
     * @return a new <code>PathIterator</code> that iterates along the
728
     * boundary of this <code>Shape</code> and provides access to the
729
     * geometry of this <code>Shape</code>'s outline
730
     */
731
    public PathIterator getPathIterator(AffineTransform at) {
732
        return new GeneralPathXIterator(this, at);
733
    }
734
735
    /**
736
     * Returns a <code>PathIterator</code> object that iterates along the
737
     * boundary of the flattened <code>Shape</code> and provides access to the
738
     * geometry of the outline of the <code>Shape</code>.
739
     * The iterator for this class is not multi-threaded safe,
740
     * which means that this <code>GeneralPathX</code> class does not
741
     * guarantee that modifications to the geometry of this
742
     * <code>GeneralPathX</code> object do not affect any iterations of
743
     * that geometry that are already in process.
744
     * @param at an <code>AffineTransform</code>
745
     * @param flatness the maximum distance that the line segments used to
746
     *                approximate the curved segments are allowed to deviate
747
     *                from any point on the original curve
748
     * @return a new <code>PathIterator</code> that iterates along the flattened
749
     * <code>Shape</code> boundary.
750
     */
751
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
752
        return new FlatteningPathIterator(getPathIterator(at), flatness);
753
    }
754
755
    /**
756
     * Creates a new object of the same class as this object.
757
     *
758
     * @return     a clone of this instance.
759
     * @exception  OutOfMemoryError            if there is not enough memory.
760
     * @see        java.lang.Cloneable
761
     * @since      1.2
762
     */
763
    public Object clone() {
764
        try {
765
            GeneralPathX copy = (GeneralPathX) super.clone();
766
            copy.pointTypes = (byte[]) pointTypes.clone();
767
            copy.pointCoords = (double[]) pointCoords.clone();
768
            return copy;
769
        } catch (CloneNotSupportedException e) {
770
            // this shouldn't happen, since we are Cloneable
771
            throw new InternalError();
772
        }
773
    }
774
775
    GeneralPathX(int windingRule,
776
                byte[] pointTypes,
777
                int numTypes,
778
                double[] pointCoords,
779
                int numCoords) {
780
781
    // used to construct from native
782
783
        this.windingRule = windingRule;
784
        this.pointTypes = pointTypes;
785
        this.numTypes = numTypes;
786
        this.pointCoords = pointCoords;
787
        this.numCoords = numCoords;
788
    }
789
790
    /**
791
     * Convertimos el path a puntos y luego le damos la vuelta.
792
     */
793
    public void flip()
794
        {
795 20861 jiyarza
                PathIterator theIterator = getPathIterator(null, Converter.FLATNESS); //polyLine.getPathIterator(null, flatness);
796 20761 jmvivo
                double[] theData = new double[6];
797
        //Coordinate first = null;
798
        CoordinateList coordList = new CoordinateList();
799
        Coordinate c1;
800
        GeneralPathX newGp = new GeneralPathX();
801
        ArrayList listOfParts = new ArrayList();
802
                while (!theIterator.isDone()) {
803
                        //while not done
804
                        int type = theIterator.currentSegment(theData);
805
                switch (type)
806
                {
807
                case SEG_MOVETO:
808
                        coordList = new CoordinateList();
809
                        listOfParts.add(coordList);
810
                        c1= new Coordinate(theData[0], theData[1]);
811
                        coordList.add(c1, true);
812
                        break;
813
                case SEG_LINETO:
814
                        c1= new Coordinate(theData[0], theData[1]);
815
                        coordList.add(c1, true);
816
                        break;
817
818
                case SEG_CLOSE:
819
                        coordList.add(coordList.getCoordinate(0));
820
                        break;
821
822
                }
823
                theIterator.next();
824
                }
825
826
                for (int i=listOfParts.size()-1; i>=0; i--)
827
                {
828
                        coordList = (CoordinateList) listOfParts.get(i);
829
                        Coordinate[] coords = coordList.toCoordinateArray();
830
                        CoordinateArraySequence seq = new CoordinateArraySequence(coords);
831
                        CoordinateSequences.reverse(seq);
832
                        coords = seq.toCoordinateArray();
833
                        newGp.moveTo(coords[0].x, coords[0].y);
834
                        for (int j=1; j < coords.length; j++)
835
                        {
836
                                newGp.lineTo(coords[j].x, coords[j].y);
837
                        }
838
                }
839
                reset();
840
                append(newGp, false);
841
        }
842
843
        /**
844
         * Use this function to ensure you get real polygons or holes
845
         * En JTS, con bCCW = false obtienes un pol�gono exterior.
846
         * Nota: Solo se le da la vuelta (si es que lo necesita) al
847
         * pol�gono exterior. El resto, por ahora, no se tocan.
848
         * Si se necesita tenerlos en cuenta, habr�a que mirar
849
         * si est�n dentro del otro, y entonces revisar que tiene
850
         * un CCW contrario al exterior.
851
         * @param bCCW true if you want the GeneralPath in CCW order
852
         * @return true si se le ha dado la vuelta. (true if flipped)
853
         * TODO: TERMINAR ESTO!! NO EST� COMPLETO!! NO sirve para multipoligonos
854
         */
855
        public boolean ensureOrientation(boolean bCCW) {
856
        byte[] pointTypesAux = new byte[numTypes+1];
857
        double[] pointCoordsAux = new double[numCoords+2];
858
        int i;
859
        int pointIdx = 0;
860
861
        Coordinate c1, c2, c3;
862
        CoordinateList coordList = new CoordinateList();
863
        CoordinateList firstList = new CoordinateList();
864
        boolean bFirstList = true;
865
        Coordinate cInicio = null;
866
867
        for (i=0; i< numTypes; i++)
868
        {
869
                int type = pointTypes[i];
870
871
                switch (type)
872
                {
873
                case SEG_MOVETO:
874
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
875
                        cInicio = c1;
876
                        coordList.add(c1, true);
877
                        if (i>0) bFirstList = false;
878
                        if (bFirstList)
879
                        {
880
                                firstList.add(c1,true);
881
                        }
882
                        break;
883
                case SEG_LINETO:
884
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
885
                        coordList.add(c1, true);
886
                        if (bFirstList)
887
                        {
888
                                firstList.add(c1,true);
889
                        }
890
                        break;
891
                case SEG_QUADTO:
892
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
893
                        coordList.add(c1, true);
894
                        c2= new Coordinate(pointCoords[pointIdx+2], pointCoords[pointIdx+3]);
895
                        coordList.add(c2, true);
896
                        if (bFirstList)
897
                        {
898
                                firstList.add(c1,true);
899
                                firstList.add(c2,true);
900
                        }
901
902
                        break;
903
                case SEG_CUBICTO:
904
                        c1= new Coordinate(pointCoords[pointIdx], pointCoords[pointIdx+1]);
905
                        coordList.add(c1, true);
906
                        c2= new Coordinate(pointCoords[pointIdx+2], pointCoords[pointIdx+3]);
907
                        coordList.add(c2, true);
908
                        c3= new Coordinate(pointCoords[pointIdx+4], pointCoords[pointIdx+5]);
909
                        coordList.add(c3, true);
910
                        if (bFirstList)
911
                        {
912
                                firstList.add(c1,true);
913
                                firstList.add(c2,true);
914
                                firstList.add(c3,true);
915
                        }
916
917
                        break;
918
                case SEG_CLOSE:
919
                        coordList.add(cInicio, true);
920
                        if (bFirstList)
921
                        {
922
                                firstList.add(cInicio,true);
923
                        }
924
                        break;
925
926
                }
927
                pointIdx += curvesize[type];
928
        }
929
                // Guardamos el path dandole la vuelta
930
                Coordinate[] coords = coordList.toCoordinateArray();
931
                boolean bFlipped = false;
932
                if (CGAlgorithms.isCCW(coords) != bCCW) // Le damos la vuelta
933
                {
934
                        CoordinateArraySequence seq = new CoordinateArraySequence(coords);
935
                        CoordinateSequences.reverse(seq);
936
                        coords = seq.toCoordinateArray();
937
938
939
                        // En el primer punto metemos un moveto
940
                        pointCoordsAux[0] = coords[0].x;
941
                        pointCoordsAux[1] = coords[0].y;
942
                        pointTypesAux[0] = SEG_MOVETO;
943
                        int idx = 2;
944
                        i=0;
945
                        int j=1;
946
                        for (int k=0; k < coords.length; k++)
947
                        {
948
                                pointCoordsAux[idx++] = coords[k].x;
949
                                pointCoordsAux[idx++] = coords[k].y;
950
                        int type = pointTypes[i++];
951
                        pointIdx += curvesize[type];
952
                        switch (type)
953
                        {
954
                        case SEG_MOVETO:
955
                                pointTypesAux[j] = SEG_LINETO;
956
                                break;
957
                        case SEG_LINETO:
958
                                pointTypesAux[j] = SEG_LINETO;
959
                                break;
960
                        case SEG_QUADTO:
961
                                pointTypesAux[j] = SEG_QUADTO;
962
                                break;
963
                        case SEG_CUBICTO:
964
                                pointTypesAux[j] = SEG_CUBICTO;
965
                                break;
966
                        case SEG_CLOSE:
967
                                // TODO: IMPLEMENTAR ESTO!!!
968
                                break;
969
970
                        }
971
                        j++;
972
973
                        }
974
975
                pointTypes = pointTypesAux;
976
                pointCoords = pointCoordsAux;
977
                numCoords= numCoords+2;
978
                numTypes++;
979
                bFlipped  = true;
980
981
                }
982
                return bFlipped;
983
        }
984
985
    /**
986
     * Check if the first part is CCW.
987
     * @return
988
     */
989
    public boolean isCCW()
990
    {
991
        //int i;
992
993 20861 jiyarza
                PathIterator theIterator = getPathIterator(null, Converter.FLATNESS); //polyLine.getPathIterator(null, flatness);
994 20761 jmvivo
                double[] theData = new double[6];
995
        Coordinate first = null;
996
        CoordinateList coordList = new CoordinateList();
997
        Coordinate c1;
998
        boolean bFirst = true;
999
                while (!theIterator.isDone()) {
1000
                        //while not done
1001
                        int type = theIterator.currentSegment(theData);
1002
                switch (type)
1003
                {
1004
                case SEG_MOVETO:
1005
                        c1= new Coordinate(theData[0], theData[1]);
1006
                        if (bFirst == false) // Ya tenemos la primera parte.
1007
                                break;
1008
                        if (bFirst)
1009
                        {
1010
                                bFirst=false;
1011
                                first = c1;
1012
                        }
1013
                        coordList.add(c1, true);
1014
                        break;
1015
                case SEG_LINETO:
1016
                        c1= new Coordinate(theData[0], theData[1]);
1017
                        coordList.add(c1, true);
1018
                        break;
1019
1020
                }
1021
                theIterator.next();
1022
                }
1023
                coordList.add(first, true);
1024
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
1025
1026
    }
1027
1028
}