Statistics
| Revision:

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

History | View | Annotate | Download (30.6 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.GeometryLocator;
68
import org.gvsig.fmap.geom.GeometryManager;
69
import org.gvsig.fmap.geom.util.Converter;
70

    
71
import sun.awt.geom.Crossings;
72
import sun.awt.geom.Curve;
73

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

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

    
110
        private static Method crossingsForPath = null;
111
        
112
        protected static GeometryManager geomManager = GeometryLocator.getGeometryManager();
113
        
114
        /**
115
     * An even-odd winding rule for determining the interior of
116
     * a path.
117
     */
118
    public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
119

    
120
    /**
121
     * A non-zero winding rule for determining the interior of a
122
     * path.
123
     */
124
    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
125

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

    
134
    private byte[] pointTypes;
135
    private double[] pointCoords;
136
    private int numTypes;
137
    private int numCoords;
138
    int windingRule;
139

    
140
    static final int INIT_SIZE = 20;
141
    static final int EXPAND_MAX = 500;
142

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

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

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

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

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

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

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

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

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

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

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

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

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

    
353
                while (!theIterator.isDone()) {
354
                        //while not done
355
                        int theType = theIterator.currentSegment(theData);
356

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

    
368
                                case PathIterator.SEG_LINETO:
369
                                        xFinal = theData[0];
370
                                        yFinal = theData[1];
371
                                        break;
372
                                case PathIterator.SEG_CLOSE:
373
                                        return true;
374

    
375
                        } //end switch
376

    
377
                        theIterator.next();
378
                }
379
              if ((xFinal == xIni) && (yFinal == yIni))
380
                    return true;
381
            return false;
382

    
383

    
384

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

    
396
    }
397

    
398

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

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

    
485
    /**
486
     * Returns the fill style winding rule.
487
     * @return an integer representing the current winding rule.
488
     * @see #WIND_EVEN_ODD
489
     * @see #WIND_NON_ZERO
490
     * @see #setWindingRule
491
     */
492
    public synchronized int getWindingRule() {
493
        return windingRule;
494
    }
495

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

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

    
553
    /**
554
     * Resets the path to empty.  The append position is set back to the
555
     * beginning of the path and all coordinates and point types are
556
     * forgotten.
557
     */
558
    public synchronized void reset() {
559
        setNumTypes(setNumCoords(0));
560
    }
561

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

    
573
    public void reProject(ICoordTrans ct)
574
    {
575
            Point2D pt = new Point2D.Double();
576
            for (int i = 0; i < getNumCoords(); i+=2)
577
            {
578
                    pt.setLocation(getPointCoords()[i], getPointCoords()[i+1]);
579
                    pt = ct.convert(pt,null);
580
                    getPointCoords()[i] = pt.getX();
581
                    getPointCoords()[i+1] = pt.getY();
582
            }
583

    
584
    }
585

    
586

    
587
    /**
588
     * Returns a new transformed <code>Shape</code>.
589
     * @param at the <code>AffineTransform</code> used to transform a
590
     * new <code>Shape</code>.
591
     * @return a new <code>Shape</code>, transformed with the specified
592
     * <code>AffineTransform</code>.
593
     */
594
    public synchronized Shape createTransformedShape(AffineTransform at) {
595
        GeneralPathX gp = (GeneralPathX) clone();
596
        if (at != null) {
597
            gp.transform(at);
598
        }
599
        return gp;
600
    }
601

    
602
    /**
603
     * Return the bounding box of the path.
604
     * @return a {@link java.awt.Rectangle} object that
605
     * bounds the current path.
606
     */
607
    public java.awt.Rectangle getBounds() {
608
        return getBounds2D().getBounds();
609
    }
610

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

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

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

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

    
716
    /**
717
     * Tests if the specified <code>Rectangle2D</code>
718
     * is inside the boundary of this <code>Shape</code>.
719
     * @param r a specified <code>Rectangle2D</code>
720
     * @return <code>true</code> if this <code>Shape</code> bounds the
721
     * specified <code>Rectangle2D</code>; <code>false</code> otherwise.
722
     */
723
    public boolean contains(Rectangle2D r) {
724
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
725
    }
726

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

    
743
    /**
744
     * Tests if the interior of this <code>Shape</code> intersects the
745
     * interior of a specified <code>Rectangle2D</code>.
746
     * @param r the specified <code>Rectangle2D</code>
747
     * @return <code>true</code> if this <code>Shape</code> and the interior
748
     *                 of the specified <code>Rectangle2D</code> intersect each
749
     *                 other; <code>false</code> otherwise.
750
     */
751
    public boolean intersects(Rectangle2D r) {
752
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
753
    }
754

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

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

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

    
813
    GeneralPathX(int windingRule,
814
                byte[] pointTypes,
815
                int numTypes,
816
                double[] pointCoords,
817
                int numCoords) {
818

    
819
    // used to construct from native
820

    
821
        this.windingRule = windingRule;
822
        this.setPointTypes(pointTypes);
823
        this.setNumTypes(numTypes);
824
        this.setPointCoords(pointCoords);
825
        this.setNumCoords(numCoords);
826
    }
827

    
828
        public void setNumTypes(int numTypes) {
829
                this.numTypes = numTypes;
830
        }
831

    
832
        public int getNumTypes() {
833
                return numTypes;
834
        }
835

    
836
        public int setNumCoords(int numCoords) {
837
                return this.numCoords = numCoords;
838
        }
839

    
840
        public int getNumCoords() {
841
                return numCoords;
842
        }
843

    
844
        public void setPointTypes(byte[] pointTypes) {
845
                this.pointTypes = pointTypes;
846
        }
847

    
848
        public byte[] getPointTypes() {
849
                return pointTypes;
850
        }
851

    
852
        public void setPointCoords(double[] pointCoords) {
853
                this.pointCoords = pointCoords;
854
        }
855

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

    
887
                case SEG_CLOSE:
888
                        coordList.add(coordList.getCoordinate(0));
889
                        break;
890

    
891
                }
892
                theIterator.next();
893
                }
894

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

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

    
946
                }
947
                theIterator.next();
948
                }
949
                coordList.add(first, true);
950
        return CGAlgorithms.isCCW(coordList.toCoordinateArray());
951

    
952
    }
953
}