Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_geometries / src / org / gvsig / fmap / geom / primitive / GeneralPathX.java @ 29018

History | View | Annotate | Download (30.4 KB)

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

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

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

    
66
import org.cresques.cts.ICoordTrans;
67
import org.gvsig.fmap.geom.util.Converter;
68

    
69
import sun.awt.geom.Crossings;
70
import sun.awt.geom.Curve;
71

    
72
import com.vividsolutions.jts.algorithm.CGAlgorithms;
73
import com.vividsolutions.jts.geom.Coordinate;
74
import com.vividsolutions.jts.geom.CoordinateList;
75
import com.vividsolutions.jts.geom.CoordinateSequences;
76
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
77

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

    
108
        private static Method crossingsForPath = null;
109
        
110
        /**
111
     * An even-odd winding rule for determining the interior of
112
     * a path.
113
     */
114
    public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
115

    
116
    /**
117
     * A non-zero winding rule for determining the interior of a
118
     * path.
119
     */
120
    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
121

    
122
    // For code simplicity, copy these constants to our namespace
123
    // and cast them to byte constants for easy storage.
124
    public static final byte SEG_MOVETO  = (byte) PathIterator.SEG_MOVETO;
125
    public static final byte SEG_LINETO  = (byte) PathIterator.SEG_LINETO;
126
    public static final byte SEG_QUADTO  = (byte) PathIterator.SEG_QUADTO;
127
    public static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
128
    public static final byte SEG_CLOSE   = (byte) PathIterator.SEG_CLOSE;
129

    
130
    private byte[] pointTypes;
131
    private double[] pointCoords;
132
    private int numTypes;
133
    private int numCoords;
134
    int windingRule;
135

    
136
    static final int INIT_SIZE = 20;
137
    static final int EXPAND_MAX = 500;
138

    
139
    public static final int curvesize[] = {2, 2, 4, 6, 0};
140

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

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

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

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

    
202
    /**
203
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
204
     * {@link Shape} object.
205
     * All of the initial geometry and the winding rule for this path are
206
     * taken from the specified <code>Shape</code> object.
207
     * @param s the specified <code>Shape</code> object
208
     */
209
    public GeneralPathX(Shape s) {
210
        // this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
211
            this(WIND_EVEN_ODD, INIT_SIZE, INIT_SIZE);
212
        PathIterator pi = s.getPathIterator(null);
213
        setWindingRule(pi.getWindingRule());
214
        append(pi, false);
215
    }
216

    
217
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
218
        if (needMove && getNumTypes() == 0) {
219
            throw new IllegalPathStateException("missing initial moveto "+
220
                                                "in path definition");
221
        }
222
        int size = getPointCoords().length;
223
        if (getNumCoords() + newCoords > size) {
224
            int grow = size;
225
            if (grow > EXPAND_MAX * 2) {
226
                grow = EXPAND_MAX * 2;
227
            }
228
            if (grow < newCoords) {
229
                grow = newCoords;
230
            }
231
            double[] arr = new double[size + grow];
232
            System.arraycopy(getPointCoords(), 0, arr, 0, getNumCoords());
233
            setPointCoords(arr);
234
        }
235
        size = getPointTypes().length;
236
        if (getNumTypes() + newTypes > size) {
237
            int grow = size;
238
            if (grow > EXPAND_MAX) {
239
                grow = EXPAND_MAX;
240
            }
241
            if (grow < newTypes) {
242
                grow = newTypes;
243
            }
244
            byte[] arr = new byte[size + grow];
245
            System.arraycopy(getPointTypes(), 0, arr, 0, getNumTypes());
246
            setPointTypes(arr);
247
        }
248
    }
249

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

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

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

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

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

    
335
    /**
336
     * Check if the first part is closed.
337
     * @return
338
     */
339
    public boolean isClosed()
340
    {
341
                PathIterator theIterator = getPathIterator(null, Converter.FLATNESS); //polyLine.getPathIterator(null, flatness);
342
                double[] theData = new double[6];
343
        double xFinal = 0;
344
        double yFinal = 0;
345
        double xIni = 0;
346
        double yIni = 0;
347
        boolean first = true;
348

    
349
                while (!theIterator.isDone()) {
350
                        //while not done
351
                        int theType = theIterator.currentSegment(theData);
352

    
353
                        switch (theType) {
354
                                case PathIterator.SEG_MOVETO:
355
                                        xIni = theData[0];
356
                                        yIni = theData[1];
357
                                        if (!first)
358
                                        {
359
                                                break;
360
                                        }
361
                                        first = false;
362
                                        break;
363

    
364
                                case PathIterator.SEG_LINETO:
365
                                        xFinal = theData[0];
366
                                        yFinal = theData[1];
367
                                        break;
368
                                case PathIterator.SEG_CLOSE:
369
                                        return true;
370

    
371
                        } //end switch
372

    
373
                        theIterator.next();
374
                }
375
              if ((xFinal == xIni) && (yFinal == yIni))
376
                    return true;
377
            return false;
378

    
379

    
380

    
381
//        double xFinal = pointCoords[numCoords -2];
382
//        double yFinal = pointCoords[numCoords -1];
383
//        double xIni = pointCoords[0];
384
//        double yIni = pointCoords[1];
385
//
386
//        if (pointTypes[numTypes-1] == SEG_CLOSE)
387
//                return true;
388
//        if ((xFinal == xIni) && (yFinal == yIni))
389
//                return true;
390
//        return false;
391

    
392
    }
393

    
394

    
395
    /**
396
     * Appends the geometry of the specified <code>Shape</code> object to the
397
     * path, possibly connecting the new geometry to the existing path
398
     * segments with a line segment.
399
     * If the <code>connect</code> parameter is <code>true</code> and the
400
     * path is not empty then any initial <code>moveTo</code> in the
401
     * geometry of the appended <code>Shape</code>
402
     * is turned into a <code>lineTo</code> segment.
403
     * If the destination coordinates of such a connecting <code>lineTo</code>
404
     * segment match the ending coordinates of a currently open
405
     * subpath then the segment is omitted as superfluous.
406
     * The winding rule of the specified <code>Shape</code> is ignored
407
     * and the appended geometry is governed by the winding
408
     * rule specified for this path.
409
     * @param s the <code>Shape</code> whose geometry is appended
410
     * to this path
411
     * @param connect a boolean to control whether or not to turn an
412
     * initial <code>moveTo</code> segment into a <code>lineTo</code>
413
     * segment to connect the new geometry to the existing path
414
     */
415
    public void append(Shape s, boolean connect) {
416
        PathIterator pi = s.getPathIterator(null);
417
        append(pi,connect);
418
    }
419

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

    
479
    /**
480
     * Returns the fill style winding rule.
481
     * @return an integer representing the current winding rule.
482
     * @see #WIND_EVEN_ODD
483
     * @see #WIND_NON_ZERO
484
     * @see #setWindingRule
485
     */
486
    public synchronized int getWindingRule() {
487
        return windingRule;
488
    }
489

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

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

    
547
    /**
548
     * Resets the path to empty.  The append position is set back to the
549
     * beginning of the path and all coordinates and point types are
550
     * forgotten.
551
     */
552
    public synchronized void reset() {
553
        setNumTypes(setNumCoords(0));
554
    }
555

    
556
    /**
557
     * Transforms the geometry of this path using the specified
558
     * {@link AffineTransform}.
559
     * The geometry is transformed in place, which permanently changes the
560
     * boundary defined by this object.
561
     * @param at the <code>AffineTransform</code> used to transform the area
562
     */
563
    public void transform(AffineTransform at) {
564
        at.transform(getPointCoords(), 0, getPointCoords(), 0, getNumCoords() / 2);
565
    }
566

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

    
578
    }
579

    
580

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

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

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

    
630
    /**
631
     * Tests if the specified coordinates are inside the boundary of
632
     * this <code>Shape</code>.
633
     * @param x,&nbsp;y the specified coordinates
634
     * @return <code>true</code> if the specified coordinates are inside this
635
     * <code>Shape</code>; <code>false</code> otherwise
636
     */
637
    public boolean contains(double x, double y) {
638
        if (getNumTypes() < 2) {
639
            return false;
640
        }
641
//        int cross = sun.awt.geom.Curve.pointCrossingsForPath(getPathIterator(null), x, y);
642
//        int cross = Curve.crossingsForPath(getPathIterator(null), x, y);
643
        int cross = curveCrossingsForPath(getPathIterator(null), x, y);
644
        if (windingRule == WIND_NON_ZERO) {
645
            return (cross != 0);
646
        } else {
647
            return ((cross & 1) != 0);
648
        }
649
    }
650

    
651
    private static int curveCrossingsForPath(PathIterator pathIterator, double x, double y) {
652
            
653
            if( crossingsForPath == null ) {
654
                        Class curve = sun.awt.geom.Curve.class;
655
                        
656
                        try {
657
                                crossingsForPath = curve.getMethod("pointCrossingsForPath", new Class[] { PathIterator.class, double.class, double.class });
658
                        } catch (SecurityException e) {
659
                                throw new RuntimeException(e);
660
                        } catch (NoSuchMethodException e) {
661
                                try {
662
                                        crossingsForPath = curve.getMethod("crossingsForPath", new Class[] { PathIterator.class, double.class, double.class });
663
                                } catch (SecurityException e1) {
664
                                        throw new RuntimeException(e);
665
                                } catch (NoSuchMethodException e1) {
666
                                        throw new RuntimeException(e);
667
                                }
668
                        }
669
            }
670
            try {
671
                Object[] params = new Object[] { pathIterator, new Double(x),
672
                    new Double(y) };
673
                        return ((Integer) crossingsForPath.invoke(Curve.class, params))
674
                    .intValue();
675
                } catch (IllegalArgumentException e) {
676
                        throw new RuntimeException(e);
677
                } catch (IllegalAccessException e) {
678
                        throw new RuntimeException(e);
679
                } catch (InvocationTargetException e) {
680
                        throw new RuntimeException(e);
681
                }
682
    }
683
    
684
    /**
685
     * Tests if the specified <code>Point2D</code> is inside the boundary
686
     * of this <code>Shape</code>.
687
     * @param p the specified <code>Point2D</code>
688
     * @return <code>true</code> if this <code>Shape</code> contains the
689
     * specified <code>Point2D</code>, <code>false</code> otherwise.
690
     */
691
    public boolean contains(Point2D p) {
692
        return contains(p.getX(), p.getY());
693
    }
694

    
695
    /**
696
     * Tests if the specified rectangular area is inside the boundary of
697
     * this <code>Shape</code>.
698
     * @param x,&nbsp;y the specified coordinates
699
     * @param w the width of the specified rectangular area
700
     * @param h the height of the specified rectangular area
701
     * @return <code>true</code> if this <code>Shape</code> contains
702
     * the specified rectangluar area; <code>false</code> otherwise.
703
     */
704
    public boolean contains(double x, double y, double w, double h) {
705
        Crossings c = Crossings.findCrossings(getPathIterator(null),
706
                                              x, y, x+w, y+h);
707
        return (c != null && c.covers(y, y+h));
708
    }
709

    
710
    /**
711
     * Tests if the specified <code>Rectangle2D</code>
712
     * is inside the boundary of this <code>Shape</code>.
713
     * @param r a specified <code>Rectangle2D</code>
714
     * @return <code>true</code> if this <code>Shape</code> bounds the
715
     * specified <code>Rectangle2D</code>; <code>false</code> otherwise.
716
     */
717
    public boolean contains(Rectangle2D r) {
718
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
719
    }
720

    
721
    /**
722
     * Tests if the interior of this <code>Shape</code> intersects the
723
     * interior of a specified set of rectangular coordinates.
724
     * @param x,&nbsp;y the specified coordinates
725
     * @param w the width of the specified rectangular coordinates
726
     * @param h the height of the specified rectangular coordinates
727
     * @return <code>true</code> if this <code>Shape</code> and the
728
     * interior of the specified set of rectangular coordinates intersect
729
     * each other; <code>false</code> otherwise.
730
     */
731
    public boolean intersects(double x, double y, double w, double h) {
732
        Crossings c = Crossings.findCrossings(getPathIterator(null),
733
                                              x, y, x+w, y+h);
734
        return (c == null || !c.isEmpty());
735
    }
736

    
737
    /**
738
     * Tests if the interior of this <code>Shape</code> intersects the
739
     * interior of a specified <code>Rectangle2D</code>.
740
     * @param r the specified <code>Rectangle2D</code>
741
     * @return <code>true</code> if this <code>Shape</code> and the interior
742
     *                 of the specified <code>Rectangle2D</code> intersect each
743
     *                 other; <code>false</code> otherwise.
744
     */
745
    public boolean intersects(Rectangle2D r) {
746
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
747
    }
748

    
749
    /**
750
     * Returns a <code>PathIterator</code> object that iterates along the
751
     * boundary of this <code>Shape</code> and provides access to the
752
     * geometry of the outline of this <code>Shape</code>.
753
     * The iterator for this class is not multi-threaded safe,
754
     * which means that this <code>GeneralPathX</code> class does not
755
     * guarantee that modifications to the geometry of this
756
     * <code>GeneralPathX</code> object do not affect any iterations of
757
     * that geometry that are already in process.
758
     * @param at an <code>AffineTransform</code>
759
     * @return a new <code>PathIterator</code> that iterates along the
760
     * boundary of this <code>Shape</code> and provides access to the
761
     * geometry of this <code>Shape</code>'s outline
762
     */
763
    public PathIterator getPathIterator(AffineTransform at) {
764
        return new GeneralPathXIterator(this, at);
765
    }
766

    
767
    /**
768
     * Returns a <code>PathIterator</code> object that iterates along the
769
     * boundary of the flattened <code>Shape</code> and provides access to the
770
     * geometry of the outline of the <code>Shape</code>.
771
     * The iterator for this class is not multi-threaded safe,
772
     * which means that this <code>GeneralPathX</code> class does not
773
     * guarantee that modifications to the geometry of this
774
     * <code>GeneralPathX</code> object do not affect any iterations of
775
     * that geometry that are already in process.
776
     * @param at an <code>AffineTransform</code>
777
     * @param flatness the maximum distance that the line segments used to
778
     *                approximate the curved segments are allowed to deviate
779
     *                from any point on the original curve
780
     * @return a new <code>PathIterator</code> that iterates along the flattened
781
     * <code>Shape</code> boundary.
782
     */
783
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
784
        return new FlatteningPathIterator(getPathIterator(at), flatness);
785
    }
786

    
787
    /**
788
     * Creates a new object of the same class as this object.
789
     *
790
     * @return     a clone of this instance.
791
     * @exception  OutOfMemoryError            if there is not enough memory.
792
     * @see        java.lang.Cloneable
793
     * @since      1.2
794
     */
795
    public Object clone() {
796
        try {
797
            GeneralPathX copy = (GeneralPathX) super.clone();
798
            copy.setPointTypes((byte[]) getPointTypes().clone());
799
            copy.setPointCoords((double[]) getPointCoords().clone());
800
            return copy;
801
        } catch (CloneNotSupportedException e) {
802
            // this shouldn't happen, since we are Cloneable
803
            throw new InternalError();
804
        }
805
    }
806

    
807
    GeneralPathX(int windingRule,
808
                byte[] pointTypes,
809
                int numTypes,
810
                double[] pointCoords,
811
                int numCoords) {
812

    
813
    // used to construct from native
814

    
815
        this.windingRule = windingRule;
816
        this.setPointTypes(pointTypes);
817
        this.setNumTypes(numTypes);
818
        this.setPointCoords(pointCoords);
819
        this.setNumCoords(numCoords);
820
    }
821

    
822
        public void setNumTypes(int numTypes) {
823
                this.numTypes = numTypes;
824
        }
825

    
826
        public int getNumTypes() {
827
                return numTypes;
828
        }
829

    
830
        public int setNumCoords(int numCoords) {
831
                return this.numCoords = numCoords;
832
        }
833

    
834
        public int getNumCoords() {
835
                return numCoords;
836
        }
837

    
838
        public void setPointTypes(byte[] pointTypes) {
839
                this.pointTypes = pointTypes;
840
        }
841

    
842
        public byte[] getPointTypes() {
843
                return pointTypes;
844
        }
845

    
846
        public void setPointCoords(double[] pointCoords) {
847
                this.pointCoords = pointCoords;
848
        }
849

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

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

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

    
889
                for (int i=listOfParts.size()-1; i>=0; i--)
890
                {
891
                        coordList = (CoordinateList) listOfParts.get(i);
892
                        Coordinate[] coords = coordList.toCoordinateArray();
893
                        CoordinateArraySequence seq = new CoordinateArraySequence(coords);
894
                        CoordinateSequences.reverse(seq);
895
                        coords = seq.toCoordinateArray();
896
                        newGp.moveTo(coords[0].x, coords[0].y);
897
                        for (int j=1; j < coords.length; j++)
898
                        {
899
                                newGp.lineTo(coords[j].x, coords[j].y);
900
                        }
901
                }
902
                reset();
903
                append(newGp, false);
904
        }
905
    /**
906
     * Check if the first part is CCW.
907
     * @return
908
     */
909
    public boolean isCCW()
910
    {
911
        int i;
912

    
913
                PathIterator theIterator = getPathIterator(null, Converter.FLATNESS); //polyLine.getPathIterator(null, flatness);
914
                double[] theData = new double[6];
915
        Coordinate first = null;
916
        CoordinateList coordList = new CoordinateList();
917
        Coordinate c1;
918
        boolean bFirst = true;
919
                while (!theIterator.isDone()) {
920
                        //while not done
921
                        int type = theIterator.currentSegment(theData);
922
                switch (type)
923
                {
924
                case SEG_MOVETO:
925
                        c1= new Coordinate(theData[0], theData[1]);
926
                        if (bFirst == false) // Ya tenemos la primera parte.
927
                                break;
928
                        if (bFirst)
929
                        {
930
                                bFirst=false;
931
                                first = c1;
932
                        }
933
                        coordList.add(c1, true);
934
                        break;
935
                case SEG_LINETO:
936
                        c1= new Coordinate(theData[0], theData[1]);
937
                        coordList.add(c1, true);
938
                        break;
939

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

    
946
    }
947
}