Statistics
| Revision:

root / branches / Mobile_Compatible_Hito_1 / libFMap / src / es / prodevelop / gvsig / mobile / fmap / core / GeneralPathX.java @ 21606

History | View | Annotate | Download (34.2 KB)

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