Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / fshape / GeneralPathX.java @ 213

History | View | Annotate | Download (22.9 KB)

1 213 fernando
/*
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
package com.iver.cit.gvsig.fmap.fshape;
8
9
/**
10
 * @author FJP
11
 *
12
 * TODO To change the template for this generated type comment go to
13
 * Window - Preferences - Java - Code Generation - Code and Comments
14
 */
15
/*
16
 * @(#)GeneralPathX.java        1.58 03/01/23
17
 *
18
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
19
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
20
 */
21
22
import java.awt.Shape;
23
import java.awt.geom.AffineTransform;
24
import java.awt.geom.FlatteningPathIterator;
25
import java.awt.geom.IllegalPathStateException;
26
import java.awt.geom.PathIterator;
27
import java.awt.geom.Point2D;
28
import java.awt.geom.Rectangle2D;
29
30
import sun.awt.geom.Crossings;
31
import sun.awt.geom.Curve;
32
33
/**
34
 * The <code>GeneralPathX</code> class represents a geometric path
35
 * constructed from straight lines, and quadratic and cubic
36
 * (B&eacute;zier) curves.  It can contain multiple subpaths.
37
 * <p>
38
 * The winding rule specifies how the interior of a path is
39
 * determined.  There are two types of winding rules:
40
 * EVEN_ODD and NON_ZERO.
41
 * <p>
42
 * An EVEN_ODD winding rule means that enclosed regions
43
 * of the path alternate between interior and exterior areas as
44
 * traversed from the outside of the path towards a point inside
45
 * the region.
46
 * <p>
47
 * A NON_ZERO winding rule means that if a ray is
48
 * drawn in any direction from a given point to infinity
49
 * and the places where the path intersects
50
 * the ray are examined, the point is inside of the path if and only if
51
 * the number of times that the path crosses the ray from
52
 * left to right does not equal the  number of times that the path crosses
53
 * the ray from right to left.
54
 * @version 1.58, 01/23/03
55
 * @author Jim Graham
56
 */
57
public final class GeneralPathX implements Shape, Cloneable {
58
    /**
59
     * An even-odd winding rule for determining the interior of
60
     * a path.
61
     */
62
    public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
63
64
    /**
65
     * A non-zero winding rule for determining the interior of a
66
     * path.
67
     */
68
    public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
69
70
    // For code simplicity, copy these constants to our namespace
71
    // and cast them to byte constants for easy storage.
72
    private static final byte SEG_MOVETO  = (byte) PathIterator.SEG_MOVETO;
73
    private static final byte SEG_LINETO  = (byte) PathIterator.SEG_LINETO;
74
    private static final byte SEG_QUADTO  = (byte) PathIterator.SEG_QUADTO;
75
    private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
76
    private static final byte SEG_CLOSE   = (byte) PathIterator.SEG_CLOSE;
77
78
    byte[] pointTypes;
79
    double[] pointCoords;
80
    int numTypes;
81
    int numCoords;
82
    int windingRule;
83
84
    static final int INIT_SIZE = 20;
85
    static final int EXPAND_MAX = 500;
86
87
    /**
88
     * Constructs a new <code>GeneralPathX</code> object.
89
     * If an operation performed on this path requires the
90
     * interior of the path to be defined then the default NON_ZERO
91
     * winding rule is used.
92
     * @see #WIND_NON_ZERO
93
     */
94
    public GeneralPathX() {
95
        this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
96
    }
97
98
    /**
99
     * Constructs a new <code>GeneralPathX</code> object with the specified
100
     * winding rule to control operations that require the interior of the
101
     * path to be defined.
102
     * @param rule the winding rule
103
     * @see #WIND_EVEN_ODD
104
     * @see #WIND_NON_ZERO
105
     */
106
    public GeneralPathX(int rule) {
107
        this(rule, INIT_SIZE, INIT_SIZE);
108
    }
109
110
    /**
111
     * Constructs a new <code>GeneralPathX</code> object with the specified
112
     * winding rule and the specified initial capacity to store path
113
     * coordinates. This number is an initial guess as to how many path
114
     * segments are in the path, but the storage is expanded
115
     * as needed to store whatever path segments are added to this path.
116
     * @param rule the winding rule
117
     * @param initialCapacity the estimate for the number of path segments
118
     * in the path
119
     * @see #WIND_EVEN_ODD
120
     * @see #WIND_NON_ZERO
121
     */
122
    public GeneralPathX(int rule, int initialCapacity) {
123
        this(rule, initialCapacity, initialCapacity);
124
    }
125
126
    /**
127
     * Constructs a new <code>GeneralPathX</code> object with the specified
128
     * winding rule and the specified initial capacities to store point types
129
     * and coordinates.
130
     * These numbers are an initial guess as to how many path segments
131
     * and how many points are to be in the path, but the
132
     * storage is expanded as needed to store whatever path segments are
133
     * added to this path.
134
     * @param rule the winding rule
135
     * @param initialTypes the estimate for the number of path segments
136
     * in the path
137
     * @param initialCapacity the estimate for the number of points
138
     * @see #WIND_EVEN_ODD
139
     * @see #WIND_NON_ZERO
140
     */
141
    GeneralPathX(int rule, int initialTypes, int initialCoords) {
142
        setWindingRule(rule);
143
        pointTypes = new byte[initialTypes];
144
        pointCoords = new double[initialCoords * 2];
145
    }
146
147
    /**
148
     * Constructs a new <code>GeneralPathX</code> object from an arbitrary
149
     * {@link Shape} object.
150
     * All of the initial geometry and the winding rule for this path are
151
     * taken from the specified <code>Shape</code> object.
152
     * @param s the specified <code>Shape</code> object
153
     */
154
    public GeneralPathX(Shape s) {
155
        this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
156
        PathIterator pi = s.getPathIterator(null);
157
        setWindingRule(pi.getWindingRule());
158
        append(pi, false);
159
    }
160
161
    private void needRoom(int newTypes, int newCoords, boolean needMove) {
162
        if (needMove && numTypes == 0) {
163
            throw new IllegalPathStateException("missing initial moveto "+
164
                                                "in path definition");
165
        }
166
        int size = pointCoords.length;
167
        if (numCoords + newCoords > size) {
168
            int grow = size;
169
            if (grow > EXPAND_MAX * 2) {
170
                grow = EXPAND_MAX * 2;
171
            }
172
            if (grow < newCoords) {
173
                grow = newCoords;
174
            }
175
            double[] arr = new double[size + grow];
176
            System.arraycopy(pointCoords, 0, arr, 0, numCoords);
177
            pointCoords = arr;
178
        }
179
        size = pointTypes.length;
180
        if (numTypes + newTypes > size) {
181
            int grow = size;
182
            if (grow > EXPAND_MAX) {
183
                grow = EXPAND_MAX;
184
            }
185
            if (grow < newTypes) {
186
                grow = newTypes;
187
            }
188
            byte[] arr = new byte[size + grow];
189
            System.arraycopy(pointTypes, 0, arr, 0, numTypes);
190
            pointTypes = arr;
191
        }
192
    }
193
194
    /**
195
     * Adds a point to the path by moving to the specified
196
     * coordinates.
197
     * @param x,&nbsp;y the specified coordinates
198
     */
199
    public synchronized void moveTo(double x, double y) {
200
        if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
201
            pointCoords[numCoords - 2] = x;
202
            pointCoords[numCoords - 1] = y;
203
        } else {
204
            needRoom(1, 2, false);
205
            pointTypes[numTypes++] = SEG_MOVETO;
206
            pointCoords[numCoords++] = x;
207
            pointCoords[numCoords++] = y;
208
        }
209
    }
210
211
    /**
212
     * Adds a point to the path by drawing a straight line from the
213
     * current coordinates to the new specified coordinates.
214
     * @param x,&nbsp;y the specified coordinates
215
     */
216
    public synchronized void lineTo(double x, double y) {
217
        needRoom(1, 2, true);
218
        pointTypes[numTypes++] = SEG_LINETO;
219
        pointCoords[numCoords++] = x;
220
        pointCoords[numCoords++] = y;
221
    }
222
223
    /**
224
     * Adds a curved segment, defined by two new points, to the path by
225
     * drawing a Quadratic curve that intersects both the current
226
     * coordinates and the coordinates (x2,&nbsp;y2), using the
227
     * specified point (x1,&nbsp;y1) as a quadratic parametric control
228
     * point.
229
     * @param x1,&nbsp;y1 the coordinates of the first quadratic control
230
     *                point
231
     * @param x2,&nbsp;y2 the coordinates of the final endpoint
232
     */
233
    public synchronized void quadTo(double x1, double y1, double x2, double y2) {
234
        needRoom(1, 4, true);
235
        pointTypes[numTypes++] = SEG_QUADTO;
236
        pointCoords[numCoords++] = x1;
237
        pointCoords[numCoords++] = y1;
238
        pointCoords[numCoords++] = x2;
239
        pointCoords[numCoords++] = y2;
240
    }
241
242
    /**
243
     * Adds a curved segment, defined by three new points, to the path by
244
     * drawing a B&eacute;zier curve that intersects both the current
245
     * coordinates and the coordinates (x3,&nbsp;y3), using the
246
     * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
247
     * B&eacute;zier control points.
248
     * @param x1,&nbsp;y1 the coordinates of the first B&eacute;ezier
249
     *                control point
250
     * @param x2,&nbsp;y2 the coordinates of the second B&eacute;zier
251
     *                control point
252
     * @param x3,&nbsp;y3 the coordinates of the final endpoint
253
     */
254
    public synchronized void curveTo(double x1, double y1,
255
                    double x2, double y2,
256
                    double x3, double y3) {
257
        needRoom(1, 6, true);
258
        pointTypes[numTypes++] = SEG_CUBICTO;
259
        pointCoords[numCoords++] = x1;
260
        pointCoords[numCoords++] = y1;
261
        pointCoords[numCoords++] = x2;
262
        pointCoords[numCoords++] = y2;
263
        pointCoords[numCoords++] = x3;
264
        pointCoords[numCoords++] = y3;
265
    }
266
267
    /**
268
     * Closes the current subpath by drawing a straight line back to
269
     * the coordinates of the last <code>moveTo</code>.  If the path is already
270
     * closed then this method has no effect.
271
     */
272
    public synchronized void closePath() {
273
        if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
274
            needRoom(1, 0, true);
275
            pointTypes[numTypes++] = SEG_CLOSE;
276
        }
277
    }
278
279
    /**
280
     * Appends the geometry of the specified <code>Shape</code> object to the
281
     * path, possibly connecting the new geometry to the existing path
282
     * segments with a line segment.
283
     * If the <code>connect</code> parameter is <code>true</code> and the
284
     * path is not empty then any initial <code>moveTo</code> in the
285
     * geometry of the appended <code>Shape</code>
286
     * is turned into a <code>lineTo</code> segment.
287
     * If the destination coordinates of such a connecting <code>lineTo</code>
288
     * segment match the ending coordinates of a currently open
289
     * subpath then the segment is omitted as superfluous.
290
     * The winding rule of the specified <code>Shape</code> is ignored
291
     * and the appended geometry is governed by the winding
292
     * rule specified for this path.
293
     * @param s the <code>Shape</code> whose geometry is appended
294
     * to this path
295
     * @param connect a boolean to control whether or not to turn an
296
     * initial <code>moveTo</code> segment into a <code>lineTo</code>
297
     * segment to connect the new geometry to the existing path
298
     */
299
    public void append(Shape s, boolean connect) {
300
        PathIterator pi = s.getPathIterator(null);
301
        append(pi,connect);
302
    }
303
304
    /**
305
     * Appends the geometry of the specified
306
     * {@link PathIterator} object
307
     * to the path, possibly connecting the new geometry to the existing
308
     * path segments with a line segment.
309
     * If the <code>connect</code> parameter is <code>true</code> and the
310
     * path is not empty then any initial <code>moveTo</code> in the
311
     * geometry of the appended <code>Shape</code> is turned into a
312
     * <code>lineTo</code> segment.
313
     * If the destination coordinates of such a connecting <code>lineTo</code>
314
     * segment match the ending coordinates of a currently open
315
     * subpath then the segment is omitted as superfluous.
316
     * The winding rule of the specified <code>Shape</code> is ignored
317
     * and the appended geometry is governed by the winding
318
     * rule specified for this path.
319
     * @param pi the <code>PathIterator</code> whose geometry is appended to
320
     * this path
321
     * @param connect a boolean to control whether or not to turn an
322
     * initial <code>moveTo</code> segment into a <code>lineTo</code> segment
323
     * to connect the new geometry to the existing path
324
     */
325
    public void append(PathIterator pi, boolean connect) {
326
        double coords[] = new double[6];
327
        while (!pi.isDone()) {
328
            switch (pi.currentSegment(coords)) {
329
            case SEG_MOVETO:
330
                if (!connect || numTypes < 1 || numCoords < 2) {
331
                    moveTo(coords[0], coords[1]);
332
                    break;
333
                }
334
                if (pointTypes[numTypes - 1] != SEG_CLOSE &&
335
                    pointCoords[numCoords - 2] == coords[0] &&
336
                    pointCoords[numCoords - 1] == coords[1])
337
                {
338
                    // Collapse out initial moveto/lineto
339
                    break;
340
                }
341
                // NO BREAK;
342
            case SEG_LINETO:
343
                lineTo(coords[0], coords[1]);
344
                break;
345
            case SEG_QUADTO:
346
                quadTo(coords[0], coords[1],
347
                       coords[2], coords[3]);
348
                break;
349
            case SEG_CUBICTO:
350
                curveTo(coords[0], coords[1],
351
                        coords[2], coords[3],
352
                        coords[4], coords[5]);
353
                break;
354
            case SEG_CLOSE:
355
                closePath();
356
                break;
357
            }
358
            pi.next();
359
            connect = false;
360
        }
361
    }
362
363
    /**
364
     * Returns the fill style winding rule.
365
     * @return an integer representing the current winding rule.
366
     * @see #WIND_EVEN_ODD
367
     * @see #WIND_NON_ZERO
368
     * @see #setWindingRule
369
     */
370
    public synchronized int getWindingRule() {
371
        return windingRule;
372
    }
373
374
    /**
375
     * Sets the winding rule for this path to the specified value.
376
     * @param rule an integer representing the specified
377
     * winding rule
378
     * @exception <code>IllegalArgumentException</code> if
379
     *                <code>rule</code> is not either
380
     *                <code>WIND_EVEN_ODD</code> or
381
     *                <code>WIND_NON_ZERO</code>
382
     * @see #WIND_EVEN_ODD
383
     * @see #WIND_NON_ZERO
384
     * @see #getWindingRule
385
     */
386
    public void setWindingRule(int rule) {
387
        if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
388
            throw new IllegalArgumentException("winding rule must be "+
389
                                               "WIND_EVEN_ODD or "+
390
                                               "WIND_NON_ZERO");
391
        }
392
        windingRule = rule;
393
    }
394
395
    /**
396
     * Returns the coordinates most recently added to the end of the path
397
     * as a {@link Point2D} object.
398
     * @return a <code>Point2D</code> object containing the ending
399
     * coordinates of the path or <code>null</code> if there are no points
400
     * in the path.
401
     */
402
    public synchronized Point2D getCurrentPoint() {
403
        if (numTypes < 1 || numCoords < 2) {
404
            return null;
405
        }
406
        int index = numCoords;
407
        if (pointTypes[numTypes - 1] == SEG_CLOSE) {
408
        loop:
409
            for (int i = numTypes - 2; i > 0; i--) {
410
                switch (pointTypes[i]) {
411
                case SEG_MOVETO:
412
                    break loop;
413
                case SEG_LINETO:
414
                    index -= 2;
415
                    break;
416
                case SEG_QUADTO:
417
                    index -= 4;
418
                    break;
419
                case SEG_CUBICTO:
420
                    index -= 6;
421
                    break;
422
                case SEG_CLOSE:
423
                    break;
424
                }
425
            }
426
        }
427
        return new Point2D.Double(pointCoords[index - 2],
428
                                 pointCoords[index - 1]);
429
    }
430
431
    /**
432
     * Resets the path to empty.  The append position is set back to the
433
     * beginning of the path and all coordinates and point types are
434
     * forgotten.
435
     */
436
    public synchronized void reset() {
437
        numTypes = numCoords = 0;
438
    }
439
440
    /**
441
     * Transforms the geometry of this path using the specified
442
     * {@link AffineTransform}.
443
     * The geometry is transformed in place, which permanently changes the
444
     * boundary defined by this object.
445
     * @param at the <code>AffineTransform</code> used to transform the area
446
     */
447
    public void transform(AffineTransform at) {
448
        at.transform(pointCoords, 0, pointCoords, 0, numCoords / 2);
449
    }
450
451
    /**
452
     * Returns a new transformed <code>Shape</code>.
453
     * @param at the <code>AffineTransform</code> used to transform a
454
     * new <code>Shape</code>.
455
     * @return a new <code>Shape</code>, transformed with the specified
456
     * <code>AffineTransform</code>.
457
     */
458
    public synchronized Shape createTransformedShape(AffineTransform at) {
459
        GeneralPathX gp = (GeneralPathX) clone();
460
        if (at != null) {
461
            gp.transform(at);
462
        }
463
        return gp;
464
    }
465
466
    /**
467
     * Return the bounding box of the path.
468
     * @return a {@link java.awt.Rectangle} object that
469
     * bounds the current path.
470
     */
471
    public java.awt.Rectangle getBounds() {
472
        return getBounds2D().getBounds();
473
    }
474
475
    /**
476
     * Returns the bounding box of the path.
477
     * @return a {@link Rectangle2D} object that
478
     *          bounds the current path.
479
     */
480
    public synchronized Rectangle2D getBounds2D() {
481
        double x1, y1, x2, y2;
482
        int i = numCoords;
483
        if (i > 0) {
484
            y1 = y2 = pointCoords[--i];
485
            x1 = x2 = pointCoords[--i];
486
            while (i > 0) {
487
                double y = pointCoords[--i];
488
                double x = pointCoords[--i];
489
                if (x < x1) x1 = x;
490
                if (y < y1) y1 = y;
491
                if (x > x2) x2 = x;
492
                if (y > y2) y2 = y;
493
            }
494
        } else {
495
            x1 = y1 = x2 = y2 = 0.0f;
496
        }
497
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
498
    }
499
500
    /**
501
     * Tests if the specified coordinates are inside the boundary of
502
     * this <code>Shape</code>.
503
     * @param x,&nbsp;y the specified coordinates
504
     * @return <code>true</code> if the specified coordinates are inside this
505
     * <code>Shape</code>; <code>false</code> otherwise
506
     */
507
    public boolean contains(double x, double y) {
508
        if (numTypes < 2) {
509
            return false;
510
        }
511
        int cross = Curve.crossingsForPath(getPathIterator(null), x, y);
512
        if (windingRule == WIND_NON_ZERO) {
513
            return (cross != 0);
514
        } else {
515
            return ((cross & 1) != 0);
516
        }
517
    }
518
519
    /**
520
     * Tests if the specified <code>Point2D</code> is inside the boundary
521
     * of this <code>Shape</code>.
522
     * @param p the specified <code>Point2D</code>
523
     * @return <code>true</code> if this <code>Shape</code> contains the
524
     * specified <code>Point2D</code>, <code>false</code> otherwise.
525
     */
526
    public boolean contains(Point2D p) {
527
        return contains(p.getX(), p.getY());
528
    }
529
530
    /**
531
     * Tests if the specified rectangular area is inside the boundary of
532
     * this <code>Shape</code>.
533
     * @param x,&nbsp;y the specified coordinates
534
     * @param w the width of the specified rectangular area
535
     * @param h the height of the specified rectangular area
536
     * @return <code>true</code> if this <code>Shape</code> contains
537
     * the specified rectangluar area; <code>false</code> otherwise.
538
     */
539
    public boolean contains(double x, double y, double w, double h) {
540
        Crossings c = Crossings.findCrossings(getPathIterator(null),
541
                                              x, y, x+w, y+h);
542
        return (c != null && c.covers(y, y+h));
543
    }
544
545
    /**
546
     * Tests if the specified <code>Rectangle2D</code>
547
     * is inside the boundary of this <code>Shape</code>.
548
     * @param r a specified <code>Rectangle2D</code>
549
     * @return <code>true</code> if this <code>Shape</code> bounds the
550
     * specified <code>Rectangle2D</code>; <code>false</code> otherwise.
551
     */
552
    public boolean contains(Rectangle2D r) {
553
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
554
    }
555
556
    /**
557
     * Tests if the interior of this <code>Shape</code> intersects the
558
     * interior of a specified set of rectangular coordinates.
559
     * @param x,&nbsp;y the specified coordinates
560
     * @param w the width of the specified rectangular coordinates
561
     * @param h the height of the specified rectangular coordinates
562
     * @return <code>true</code> if this <code>Shape</code> and the
563
     * interior of the specified set of rectangular coordinates intersect
564
     * each other; <code>false</code> otherwise.
565
     */
566
    public boolean intersects(double x, double y, double w, double h) {
567
        Crossings c = Crossings.findCrossings(getPathIterator(null),
568
                                              x, y, x+w, y+h);
569
        return (c == null || !c.isEmpty());
570
    }
571
572
    /**
573
     * Tests if the interior of this <code>Shape</code> intersects the
574
     * interior of a specified <code>Rectangle2D</code>.
575
     * @param r the specified <code>Rectangle2D</code>
576
     * @return <code>true</code> if this <code>Shape</code> and the interior
577
     *                 of the specified <code>Rectangle2D</code> intersect each
578
     *                 other; <code>false</code> otherwise.
579
     */
580
    public boolean intersects(Rectangle2D r) {
581
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
582
    }
583
584
    /**
585
     * Returns a <code>PathIterator</code> object that iterates along the
586
     * boundary of this <code>Shape</code> and provides access to the
587
     * geometry of the outline of this <code>Shape</code>.
588
     * The iterator for this class is not multi-threaded safe,
589
     * which means that this <code>GeneralPathX</code> class does not
590
     * guarantee that modifications to the geometry of this
591
     * <code>GeneralPathX</code> object do not affect any iterations of
592
     * that geometry that are already in process.
593
     * @param at an <code>AffineTransform</code>
594
     * @return a new <code>PathIterator</code> that iterates along the
595
     * boundary of this <code>Shape</code> and provides access to the
596
     * geometry of this <code>Shape</code>'s outline
597
     */
598
    public PathIterator getPathIterator(AffineTransform at) {
599
        return new GeneralPathXIterator(this, at);
600
    }
601
602
    /**
603
     * Returns a <code>PathIterator</code> object that iterates along the
604
     * boundary of the flattened <code>Shape</code> and provides access to the
605
     * geometry of the outline of the <code>Shape</code>.
606
     * The iterator for this class is not multi-threaded safe,
607
     * which means that this <code>GeneralPathX</code> class does not
608
     * guarantee that modifications to the geometry of this
609
     * <code>GeneralPathX</code> object do not affect any iterations of
610
     * that geometry that are already in process.
611
     * @param at an <code>AffineTransform</code>
612
     * @param flatness the maximum distance that the line segments used to
613
     *                approximate the curved segments are allowed to deviate
614
     *                from any point on the original curve
615
     * @return a new <code>PathIterator</code> that iterates along the flattened
616
     * <code>Shape</code> boundary.
617
     */
618
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
619
        return new FlatteningPathIterator(getPathIterator(at), flatness);
620
    }
621
622
    /**
623
     * Creates a new object of the same class as this object.
624
     *
625
     * @return     a clone of this instance.
626
     * @exception  OutOfMemoryError            if there is not enough memory.
627
     * @see        java.lang.Cloneable
628
     * @since      1.2
629
     */
630
    public Object clone() {
631
        try {
632
            GeneralPathX copy = (GeneralPathX) super.clone();
633
            copy.pointTypes = (byte[]) pointTypes.clone();
634
            copy.pointCoords = (double[]) pointCoords.clone();
635
            return copy;
636
        } catch (CloneNotSupportedException e) {
637
            // this shouldn't happen, since we are Cloneable
638
            throw new InternalError();
639
        }
640
    }
641
642
    GeneralPathX(int windingRule,
643
                byte[] pointTypes,
644
                int numTypes,
645
                double[] pointCoords,
646
                int numCoords) {
647
648
    // used to construct from native
649
650
        this.windingRule = windingRule;
651
        this.pointTypes = pointTypes;
652
        this.numTypes = numTypes;
653
        this.pointCoords = pointCoords;
654
        this.numCoords = numCoords;
655
    }
656
657
    public void flip()
658
        {
659
        byte[] pointTypesAux = new byte[numTypes];
660
        double[] pointCoordsAux = new double[numCoords];
661
        int i;
662
663
        for (i=0; i< numTypes; i++)
664
                pointTypesAux[numTypes - i -1] = pointTypes[i];
665
        int numPoints = numCoords/2;
666
        for (i=0; i< numPoints; i++)
667
        {
668
                // la x
669
                pointCoordsAux[2*(numPoints - i -1)] = pointCoords[2*i];
670
                // la y
671
                pointCoordsAux[2*(numPoints - i -1) + 1] = pointCoords[2*i + 1];
672
        }
673
674
        pointTypes = pointTypesAux;
675
        pointCoords = pointCoordsAux;
676
        }
677
}