001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.ext.awt.geom;
020:
021: import java.awt.Shape;
022: import java.awt.Rectangle;
023: import java.awt.geom.AffineTransform;
024: import java.awt.geom.Arc2D;
025: import java.awt.geom.GeneralPath;
026: import java.awt.geom.PathIterator;
027: import java.awt.geom.Point2D;
028: import java.awt.geom.Rectangle2D;
029: import java.util.Arrays;
030:
031: /**
032: * The <code>ExtendedGeneralPath</code> class represents a geometric
033: * path constructed from straight lines, quadratic and cubic (Bezier)
034: * curves and elliptical arc. This class delegates lines and curves to
035: * an enclosed <code>GeneralPath</code>. Elliptical arc is implemented
036: * using an <code>Arc2D</code> in float precision.
037: *
038: * <p><b>Warning</b> : An elliptical arc may be composed of several
039: * path segments. For futher details, see the SVG Appendix F.6
040: *
041: * @author <a href="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
042: * @version $Id: ExtendedGeneralPath.java 511565 2007-02-25 18:04:46Z dvholten $
043: */
044: public class ExtendedGeneralPath implements ExtendedShape, Cloneable {
045:
046: /** The enclosed general path. */
047: protected GeneralPath path;
048:
049: int numVals = 0;
050: int numSeg = 0;
051: float[] values = null;
052: int[] types = null;
053:
054: float mx, my, cx, cy;
055:
056: /**
057: * Constructs a new <code>ExtendedGeneralPath</code>.
058: */
059: public ExtendedGeneralPath() {
060: path = new GeneralPath();
061: }
062:
063: /**
064: * Constructs a new <code>ExtendedGeneralPath</code> with the
065: * specified winding rule to control operations that require the
066: * interior of the path to be defined.
067: */
068: public ExtendedGeneralPath(int rule) {
069: path = new GeneralPath(rule);
070: }
071:
072: /**
073: * Constructs a new <code>ExtendedGeneralPath</code> object with
074: * the specified winding rule and the specified initial capacity
075: * to store path coordinates.
076: */
077: public ExtendedGeneralPath(int rule, int initialCapacity) {
078: path = new GeneralPath(rule, initialCapacity);
079: }
080:
081: /**
082: * Constructs a new <code>ExtendedGeneralPath</code> object from
083: * an arbitrary <code>Shape</code> object.
084: */
085: public ExtendedGeneralPath(Shape s) {
086: this ();
087: append(s, false);
088: }
089:
090: /**
091: * Adds an elliptical arc, defined by two radii, an angle from the
092: * x-axis, a flag to choose the large arc or not, a flag to
093: * indicate if we increase or decrease the angles and the final
094: * point of the arc.
095: *
096: * @param rx the x radius of the ellipse
097: * @param ry the y radius of the ellipse
098: *
099: * @param angle the angle from the x-axis of the current
100: * coordinate system to the x-axis of the ellipse in degrees.
101: *
102: * @param largeArcFlag the large arc flag. If true the arc
103: * spanning less than or equal to 180 degrees is chosen, otherwise
104: * the arc spanning greater than 180 degrees is chosen
105: *
106: * @param sweepFlag the sweep flag. If true the line joining
107: * center to arc sweeps through decreasing angles otherwise it
108: * sweeps through increasing angles
109: *
110: * @param x the absolute x coordinate of the final point of the arc.
111: * @param y the absolute y coordinate of the final point of the arc.
112: */
113: public synchronized void arcTo(float rx, float ry, float angle,
114: boolean largeArcFlag, boolean sweepFlag, float x, float y) {
115:
116: // Ensure radii are valid
117: if (rx == 0 || ry == 0) {
118: lineTo((float) x, (float) y);
119: return;
120: }
121:
122: checkMoveTo(); // check if prev command was moveto
123:
124: // Get the current (x, y) coordinates of the path
125: double x0 = cx;
126: double y0 = cy;
127: if (x0 == x && y0 == y) {
128: // If the endpoints (x, y) and (x0, y0) are identical, then this
129: // is equivalent to omitting the elliptical arc segment entirely.
130: return;
131: }
132:
133: Arc2D arc = computeArc(x0, y0, rx, ry, angle, largeArcFlag,
134: sweepFlag, x, y);
135: if (arc == null)
136: return;
137:
138: AffineTransform t = AffineTransform.getRotateInstance(Math
139: .toRadians(angle), arc.getCenterX(), arc.getCenterY());
140: Shape s = t.createTransformedShape(arc);
141: path.append(s, true);
142:
143: makeRoom(7);
144: types[numSeg++] = ExtendedPathIterator.SEG_ARCTO;
145: values[numVals++] = rx;
146: values[numVals++] = ry;
147: values[numVals++] = angle;
148: values[numVals++] = largeArcFlag ? 1 : 0;
149: values[numVals++] = sweepFlag ? 1 : 0;
150: cx = values[numVals++] = x;
151: cy = values[numVals++] = y;
152: }
153:
154: /**
155: * This constructs an unrotated Arc2D from the SVG specification of an
156: * Elliptical arc. To get the final arc you need to apply a rotation
157: * transform such as:
158: *
159: * AffineTransform.getRotateInstance
160: * (angle, arc.getX()+arc.getWidth()/2, arc.getY()+arc.getHeight()/2);
161: */
162: public static Arc2D computeArc(double x0, double y0, double rx,
163: double ry, double angle, boolean largeArcFlag,
164: boolean sweepFlag, double x, double y) {
165: //
166: // Elliptical arc implementation based on the SVG specification notes
167: //
168:
169: // Compute the half distance between the current and the final point
170: double dx2 = (x0 - x) / 2.0;
171: double dy2 = (y0 - y) / 2.0;
172: // Convert angle from degrees to radians
173: angle = Math.toRadians(angle % 360.0);
174: double cosAngle = Math.cos(angle);
175: double sinAngle = Math.sin(angle);
176:
177: //
178: // Step 1 : Compute (x1, y1)
179: //
180: double x1 = (cosAngle * dx2 + sinAngle * dy2);
181: double y1 = (-sinAngle * dx2 + cosAngle * dy2);
182: // Ensure radii are large enough
183: rx = Math.abs(rx);
184: ry = Math.abs(ry);
185: double Prx = rx * rx;
186: double Pry = ry * ry;
187: double Px1 = x1 * x1;
188: double Py1 = y1 * y1;
189: // check that radii are large enough
190: double radiiCheck = Px1 / Prx + Py1 / Pry;
191: if (radiiCheck > 1) {
192: rx = Math.sqrt(radiiCheck) * rx;
193: ry = Math.sqrt(radiiCheck) * ry;
194: Prx = rx * rx;
195: Pry = ry * ry;
196: }
197:
198: //
199: // Step 2 : Compute (cx1, cy1)
200: //
201: double sign = (largeArcFlag == sweepFlag) ? -1 : 1;
202: double sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1))
203: / ((Prx * Py1) + (Pry * Px1));
204: sq = (sq < 0) ? 0 : sq;
205: double coef = (sign * Math.sqrt(sq));
206: double cx1 = coef * ((rx * y1) / ry);
207: double cy1 = coef * -((ry * x1) / rx);
208:
209: //
210: // Step 3 : Compute (cx, cy) from (cx1, cy1)
211: //
212: double sx2 = (x0 + x) / 2.0;
213: double sy2 = (y0 + y) / 2.0;
214: double cx = sx2 + (cosAngle * cx1 - sinAngle * cy1);
215: double cy = sy2 + (sinAngle * cx1 + cosAngle * cy1);
216:
217: //
218: // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)
219: //
220: double ux = (x1 - cx1) / rx;
221: double uy = (y1 - cy1) / ry;
222: double vx = (-x1 - cx1) / rx;
223: double vy = (-y1 - cy1) / ry;
224: double p, n;
225: // Compute the angle start
226: n = Math.sqrt((ux * ux) + (uy * uy));
227: p = ux; // (1 * ux) + (0 * uy)
228: sign = (uy < 0) ? -1.0 : 1.0;
229: double angleStart = Math.toDegrees(sign * Math.acos(p / n));
230:
231: // Compute the angle extent
232: n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
233: p = ux * vx + uy * vy;
234: sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
235: double angleExtent = Math.toDegrees(sign * Math.acos(p / n));
236: if (!sweepFlag && angleExtent > 0) {
237: angleExtent -= 360f;
238: } else if (sweepFlag && angleExtent < 0) {
239: angleExtent += 360f;
240: }
241: angleExtent %= 360f;
242: angleStart %= 360f;
243:
244: //
245: // We can now build the resulting Arc2D in double precision
246: //
247: Arc2D.Double arc = new Arc2D.Double();
248: arc.x = cx - rx;
249: arc.y = cy - ry;
250: arc.width = rx * 2.0;
251: arc.height = ry * 2.0;
252: arc.start = -angleStart;
253: arc.extent = -angleExtent;
254:
255: return arc;
256: }
257:
258: /**
259: * Delegates to the enclosed <code>GeneralPath</code>.
260: */
261: public synchronized void moveTo(float x, float y) {
262: // Don't add moveto to general path unless there is a reason.
263: makeRoom(2);
264: types[numSeg++] = PathIterator.SEG_MOVETO;
265: cx = mx = values[numVals++] = x;
266: cy = my = values[numVals++] = y;
267:
268: }
269:
270: /**
271: * Delegates to the enclosed <code>GeneralPath</code>.
272: */
273: public synchronized void lineTo(float x, float y) {
274: checkMoveTo(); // check if prev command was moveto
275: path.lineTo(x, y);
276:
277: makeRoom(2);
278: types[numSeg++] = PathIterator.SEG_LINETO;
279: cx = values[numVals++] = x;
280: cy = values[numVals++] = y;
281: }
282:
283: /**
284: * Delegates to the enclosed <code>GeneralPath</code>.
285: */
286: public synchronized void quadTo(float x1, float y1, float x2,
287: float y2) {
288: checkMoveTo(); // check if prev command was moveto
289: path.quadTo(x1, y1, x2, y2);
290:
291: makeRoom(4);
292: types[numSeg++] = PathIterator.SEG_QUADTO;
293: values[numVals++] = x1;
294: values[numVals++] = y1;
295: cx = values[numVals++] = x2;
296: cy = values[numVals++] = y2;
297: }
298:
299: /**
300: * Delegates to the enclosed <code>GeneralPath</code>.
301: */
302: public synchronized void curveTo(float x1, float y1, float x2,
303: float y2, float x3, float y3) {
304: checkMoveTo(); // check if prev command was moveto
305: path.curveTo(x1, y1, x2, y2, x3, y3);
306:
307: makeRoom(6);
308: types[numSeg++] = PathIterator.SEG_CUBICTO;
309: values[numVals++] = x1;
310: values[numVals++] = y1;
311: values[numVals++] = x2;
312: values[numVals++] = y2;
313: cx = values[numVals++] = x3;
314: cy = values[numVals++] = y3;
315: }
316:
317: /**
318: * Delegates to the enclosed <code>GeneralPath</code>.
319: */
320: public synchronized void closePath() {
321: // Don't double close path.
322: if ((numSeg != 0)
323: && (types[numSeg - 1] == PathIterator.SEG_CLOSE))
324: return;
325:
326: // Only close path if the previous command wasn't a moveto
327: if ((numSeg != 0)
328: && (types[numSeg - 1] != PathIterator.SEG_MOVETO))
329: path.closePath();
330:
331: makeRoom(0);
332: types[numSeg++] = PathIterator.SEG_CLOSE;
333: cx = mx;
334: cy = my;
335: }
336:
337: /**
338: * Checks if previous command was a moveto command,
339: * skipping a close command (if present).
340: */
341: protected void checkMoveTo() {
342: if (numSeg == 0)
343: return;
344:
345: switch (types[numSeg - 1]) {
346:
347: case PathIterator.SEG_MOVETO:
348: path.moveTo(values[numVals - 2], values[numVals - 1]);
349: break;
350:
351: case PathIterator.SEG_CLOSE:
352: if (numSeg == 1)
353: return;
354: if (types[numSeg - 2] == PathIterator.SEG_MOVETO)
355: path.moveTo(values[numVals - 2], values[numVals - 1]);
356: break;
357:
358: default:
359: break;
360: }
361: }
362:
363: /**
364: * Delegates to the enclosed <code>GeneralPath</code>.
365: */
366: public void append(Shape s, boolean connect) {
367: append(s.getPathIterator(new AffineTransform()), connect);
368: }
369:
370: /**
371: * Delegates to the enclosed <code>GeneralPath</code>.
372: */
373: public void append(PathIterator pi, boolean connect) {
374: double[] vals = new double[6];
375:
376: while (!pi.isDone()) {
377: Arrays.fill(vals, 0);
378: int type = pi.currentSegment(vals);
379: pi.next();
380: if (connect && (numVals != 0)) {
381: if (type == PathIterator.SEG_MOVETO) {
382: double x = vals[0];
383: double y = vals[1];
384: if ((x != cx) || (y != cy)) {
385: // Change MOVETO to LINETO.
386: type = PathIterator.SEG_LINETO;
387: } else {
388: // Redundent segment (move to current loc) drop it...
389: if (pi.isDone())
390: break; // Nothing interesting
391: type = pi.currentSegment(vals);
392: pi.next();
393: }
394: }
395: connect = false;
396: }
397:
398: switch (type) {
399: case PathIterator.SEG_CLOSE:
400: closePath();
401: break;
402: case PathIterator.SEG_MOVETO:
403: moveTo((float) vals[0], (float) vals[1]);
404: break;
405: case PathIterator.SEG_LINETO:
406: lineTo((float) vals[0], (float) vals[1]);
407: break;
408: case PathIterator.SEG_QUADTO:
409: quadTo((float) vals[0], (float) vals[1],
410: (float) vals[2], (float) vals[3]);
411: break;
412: case PathIterator.SEG_CUBICTO:
413: curveTo((float) vals[0], (float) vals[1],
414: (float) vals[2], (float) vals[3],
415: (float) vals[4], (float) vals[5]);
416: break;
417: }
418: }
419: }
420:
421: /**
422: * Delegates to the enclosed <code>GeneralPath</code>.
423: */
424: public void append(ExtendedPathIterator epi, boolean connect) {
425: float[] vals = new float[7];
426: while (!epi.isDone()) {
427: Arrays.fill(vals, 0);
428: int type = epi.currentSegment(vals);
429: epi.next();
430: if (connect && (numVals != 0)) {
431: if (type == PathIterator.SEG_MOVETO) {
432: float x = vals[0];
433: float y = vals[1];
434: if ((x != cx) || (y != cy)) {
435: // Change MOVETO to LINETO.
436: type = PathIterator.SEG_LINETO;
437: } else {
438: // Redundant segment (move to current loc) drop it...
439: if (epi.isDone())
440: break; // Nothing interesting
441: type = epi.currentSegment(vals);
442: epi.next();
443: }
444: }
445: connect = false;
446: }
447:
448: switch (type) {
449: case PathIterator.SEG_CLOSE:
450: closePath();
451: break;
452: case PathIterator.SEG_MOVETO:
453: moveTo((float) vals[0], (float) vals[1]);
454: break;
455: case PathIterator.SEG_LINETO:
456: lineTo((float) vals[0], (float) vals[1]);
457: break;
458: case PathIterator.SEG_QUADTO:
459: quadTo((float) vals[0], (float) vals[1],
460: (float) vals[2], (float) vals[3]);
461: break;
462: case PathIterator.SEG_CUBICTO:
463: curveTo((float) vals[0], (float) vals[1],
464: (float) vals[2], (float) vals[3],
465: (float) vals[4], (float) vals[5]);
466: break;
467: case ExtendedPathIterator.SEG_ARCTO:
468: arcTo(vals[0], vals[1], vals[2], (vals[3] != 0),
469: (vals[4] != 0), vals[5], vals[6]);
470: break;
471: }
472: }
473: }
474:
475: /**
476: * Delegates to the enclosed <code>GeneralPath</code>.
477: */
478: public synchronized int getWindingRule() {
479: return path.getWindingRule();
480: }
481:
482: /**
483: * Delegates to the enclosed <code>GeneralPath</code>.
484: */
485: public void setWindingRule(int rule) {
486: path.setWindingRule(rule);
487: }
488:
489: /**
490: * get the current position or <code>null</code>.
491: */
492: public synchronized Point2D getCurrentPoint() {
493: if (numVals == 0)
494: return null;
495: return new Point2D.Double(cx, cy);
496: }
497:
498: /**
499: * Delegates to the enclosed <code>GeneralPath</code>.
500: */
501: public synchronized void reset() {
502: path.reset();
503:
504: numSeg = 0;
505: numVals = 0;
506: values = null;
507: types = null;
508: }
509:
510: /**
511: * Delegates to the enclosed <code>GeneralPath</code>.
512: */
513: public void transform(AffineTransform at) {
514: if (at.getType() != AffineTransform.TYPE_IDENTITY)
515: throw new IllegalArgumentException(
516: "ExtendedGeneralPaths can not be transformed");
517: }
518:
519: /**
520: * Delegates to the enclosed <code>GeneralPath</code>.
521: */
522: public synchronized Shape createTransformedShape(AffineTransform at) {
523: return path.createTransformedShape(at);
524: }
525:
526: /**
527: * Delegates to the enclosed <code>GeneralPath</code>.
528: */
529: public synchronized Rectangle getBounds() {
530: return path.getBounds();
531: }
532:
533: /**
534: * Delegates to the enclosed <code>GeneralPath</code>.
535: */
536: public synchronized Rectangle2D getBounds2D() {
537: return path.getBounds2D();
538: }
539:
540: /**
541: * Delegates to the enclosed <code>GeneralPath</code>.
542: */
543: public boolean contains(double x, double y) {
544: return path.contains(x, y);
545: }
546:
547: /**
548: * Delegates to the enclosed <code>GeneralPath</code>.
549: */
550: public boolean contains(Point2D p) {
551: return path.contains(p);
552: }
553:
554: /**
555: * Delegates to the enclosed <code>GeneralPath</code>.
556: */
557: public boolean contains(double x, double y, double w, double h) {
558: return path.contains(x, y, w, h);
559: }
560:
561: /**
562: * Delegates to the enclosed <code>GeneralPath</code>.
563: */
564: public boolean contains(Rectangle2D r) {
565: return path.contains(r);
566: }
567:
568: /**
569: * Delegates to the enclosed <code>GeneralPath</code>.
570: */
571: public boolean intersects(double x, double y, double w, double h) {
572: return path.intersects(x, y, w, h);
573: }
574:
575: /**
576: * Delegates to the enclosed <code>GeneralPath</code>.
577: */
578: public boolean intersects(Rectangle2D r) {
579: return path.intersects(r);
580: }
581:
582: /**
583: * Delegates to the enclosed <code>GeneralPath</code>.
584: */
585: public PathIterator getPathIterator(AffineTransform at) {
586: return path.getPathIterator(at);
587: }
588:
589: /**
590: * Delegates to the enclosed <code>GeneralPath</code>.
591: */
592: public PathIterator getPathIterator(AffineTransform at,
593: double flatness) {
594: return path.getPathIterator(at, flatness);
595: }
596:
597: /**
598: * Delegates to the enclosed <code>GeneralPath</code>.
599: */
600: public ExtendedPathIterator getExtendedPathIterator() {
601: return new EPI();
602: }
603:
604: class EPI implements ExtendedPathIterator {
605: int segNum = 0;
606: int valsIdx = 0;
607:
608: public int currentSegment() {
609: return types[segNum];
610: }
611:
612: public int currentSegment(double[] coords) {
613: int ret = types[segNum];
614: switch (ret) {
615: case SEG_CLOSE:
616: break;
617: case SEG_MOVETO:
618: case SEG_LINETO:
619: coords[0] = values[valsIdx];
620: coords[1] = values[valsIdx + 1];
621: break;
622: case SEG_QUADTO:
623: coords[0] = values[valsIdx];
624: coords[1] = values[valsIdx + 1];
625: coords[2] = values[valsIdx + 2];
626: coords[3] = values[valsIdx + 3];
627: break;
628: case SEG_CUBICTO:
629: coords[0] = values[valsIdx];
630: coords[1] = values[valsIdx + 1];
631: coords[2] = values[valsIdx + 2];
632: coords[3] = values[valsIdx + 3];
633: coords[4] = values[valsIdx + 4];
634: coords[5] = values[valsIdx + 5];
635: break;
636: case SEG_ARCTO:
637: coords[0] = values[valsIdx];
638: coords[1] = values[valsIdx + 1];
639: coords[2] = values[valsIdx + 2];
640: coords[3] = values[valsIdx + 3];
641: coords[4] = values[valsIdx + 4];
642: coords[5] = values[valsIdx + 5];
643: coords[6] = values[valsIdx + 6];
644: break;
645: }
646: // System.out.println("Seg: [" + segNum + "] type: " + ret +
647: // " vals: [" + coords[0] + ", " + coords[1] +
648: // "]");
649: return ret;
650: }
651:
652: public int currentSegment(float[] coords) {
653: int ret = types[segNum];
654: switch (ret) {
655: case SEG_CLOSE:
656: break;
657: case SEG_MOVETO:
658: case SEG_LINETO:
659: coords[0] = values[valsIdx];
660: coords[1] = values[valsIdx + 1];
661: break;
662: case SEG_QUADTO:
663: // coords[0] = values[valsIdx];
664: // coords[1] = values[valsIdx+1];
665: // coords[2] = values[valsIdx+2];
666: // coords[3] = values[valsIdx+3];
667: System.arraycopy(values, 0, coords, 0, 4);
668: break;
669: case SEG_CUBICTO:
670: // coords[0] = values[valsIdx];
671: // coords[1] = values[valsIdx+1];
672: // coords[2] = values[valsIdx+2];
673: // coords[3] = values[valsIdx+3];
674: // coords[4] = values[valsIdx+4];
675: // coords[5] = values[valsIdx+5];
676: System.arraycopy(values, 0, coords, 0, 6);
677: break;
678: case SEG_ARCTO:
679: // coords[0] = values[valsIdx];
680: // coords[1] = values[valsIdx+1];
681: // coords[2] = values[valsIdx+2];
682: // coords[3] = values[valsIdx+3];
683: // coords[4] = values[valsIdx+4];
684: // coords[5] = values[valsIdx+5];
685: // coords[6] = values[valsIdx+6];
686: System.arraycopy(values, 0, coords, 0, 7);
687: break;
688: }
689: return ret;
690: }
691:
692: public int getWindingRule() {
693: return path.getWindingRule();
694: }
695:
696: public boolean isDone() {
697: return segNum == numSeg;
698: }
699:
700: public void next() {
701: int type = types[segNum++];
702: switch (type) {
703: case SEG_CLOSE:
704: break;
705: case SEG_MOVETO: // fallthrough is intended
706: case SEG_LINETO:
707: valsIdx += 2;
708: break;
709: case SEG_QUADTO:
710: valsIdx += 4;
711: break;
712: case SEG_CUBICTO:
713: valsIdx += 6;
714: break;
715: case SEG_ARCTO:
716: valsIdx += 7;
717: break;
718: }
719: }
720: }
721:
722: /**
723: * Delegates to the enclosed <code>GeneralPath</code>.
724: */
725: public Object clone() {
726: try {
727: ExtendedGeneralPath result = (ExtendedGeneralPath) super
728: .clone();
729: result.path = (GeneralPath) path.clone();
730:
731: if (values != null) {
732: result.values = new float[values.length];
733: System.arraycopy(values, 0, result.values, 0,
734: values.length);
735: }
736: result.numVals = numVals;
737:
738: if (types != null) {
739: result.types = new int[types.length];
740: System.arraycopy(types, 0, result.types, 0,
741: types.length);
742: }
743: result.numSeg = numSeg;
744:
745: return result;
746: } catch (CloneNotSupportedException ex) {
747: }
748: return null;
749: }
750:
751: /**
752: * Make sure, that the requested number of slots in vales[] are available.
753: * Must be called even for numValues = 0, because it is also
754: * used for initialization of those arrays.
755: *
756: * @param numValues number of requested coordinates
757: */
758: private void makeRoom(int numValues) {
759: if (values == null) {
760: values = new float[2 * numValues];
761: types = new int[2];
762: numVals = 0;
763: numSeg = 0;
764: return;
765: }
766:
767: int newSize = numVals + numValues;
768: if (newSize > values.length) {
769: int nlen = values.length * 2;
770: if (nlen < newSize)
771: nlen = newSize;
772:
773: float[] nvals = new float[nlen];
774: System.arraycopy(values, 0, nvals, 0, numVals);
775: values = nvals;
776: }
777:
778: if (numSeg == types.length) {
779: int[] ntypes = new int[types.length * 2];
780: System.arraycopy(types, 0, ntypes, 0, types.length);
781: types = ntypes;
782: }
783: }
784: }
|