001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Denis M. Kishenko
019: * @version $Revision$
020: */package java.awt.geom;
021:
022: import java.awt.Rectangle;
023: import java.awt.Shape;
024: import java.util.NoSuchElementException;
025:
026: import org.apache.harmony.awt.gl.Crossing;
027: import org.apache.harmony.awt.internal.nls.Messages;
028:
029: public abstract class CubicCurve2D implements Shape, Cloneable {
030:
031: public static class Float extends CubicCurve2D {
032:
033: public float x1;
034: public float y1;
035: public float ctrlx1;
036: public float ctrly1;
037: public float ctrlx2;
038: public float ctrly2;
039: public float x2;
040: public float y2;
041:
042: public Float() {
043: }
044:
045: public Float(float x1, float y1, float ctrlx1, float ctrly1,
046: float ctrlx2, float ctrly2, float x2, float y2) {
047: setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
048: }
049:
050: @Override
051: public double getX1() {
052: return x1;
053: }
054:
055: @Override
056: public double getY1() {
057: return y1;
058: }
059:
060: @Override
061: public double getCtrlX1() {
062: return ctrlx1;
063: }
064:
065: @Override
066: public double getCtrlY1() {
067: return ctrly1;
068: }
069:
070: @Override
071: public double getCtrlX2() {
072: return ctrlx2;
073: }
074:
075: @Override
076: public double getCtrlY2() {
077: return ctrly2;
078: }
079:
080: @Override
081: public double getX2() {
082: return x2;
083: }
084:
085: @Override
086: public double getY2() {
087: return y2;
088: }
089:
090: @Override
091: public Point2D getP1() {
092: return new Point2D.Float(x1, y1);
093: }
094:
095: @Override
096: public Point2D getCtrlP1() {
097: return new Point2D.Float(ctrlx1, ctrly1);
098: }
099:
100: @Override
101: public Point2D getCtrlP2() {
102: return new Point2D.Float(ctrlx2, ctrly2);
103: }
104:
105: @Override
106: public Point2D getP2() {
107: return new Point2D.Float(x2, y2);
108: }
109:
110: @Override
111: public void setCurve(double x1, double y1, double ctrlx1,
112: double ctrly1, double ctrlx2, double ctrly2, double x2,
113: double y2) {
114: this .x1 = (float) x1;
115: this .y1 = (float) y1;
116: this .ctrlx1 = (float) ctrlx1;
117: this .ctrly1 = (float) ctrly1;
118: this .ctrlx2 = (float) ctrlx2;
119: this .ctrly2 = (float) ctrly2;
120: this .x2 = (float) x2;
121: this .y2 = (float) y2;
122: }
123:
124: public void setCurve(float x1, float y1, float ctrlx1,
125: float ctrly1, float ctrlx2, float ctrly2, float x2,
126: float y2) {
127: this .x1 = x1;
128: this .y1 = y1;
129: this .ctrlx1 = ctrlx1;
130: this .ctrly1 = ctrly1;
131: this .ctrlx2 = ctrlx2;
132: this .ctrly2 = ctrly2;
133: this .x2 = x2;
134: this .y2 = y2;
135: }
136:
137: public Rectangle2D getBounds2D() {
138: float rx1 = Math.min(Math.min(x1, x2), Math.min(ctrlx1,
139: ctrlx2));
140: float ry1 = Math.min(Math.min(y1, y2), Math.min(ctrly1,
141: ctrly2));
142: float rx2 = Math.max(Math.max(x1, x2), Math.max(ctrlx1,
143: ctrlx2));
144: float ry2 = Math.max(Math.max(y1, y2), Math.max(ctrly1,
145: ctrly2));
146: return new Rectangle2D.Float(rx1, ry1, rx2 - rx1, ry2 - ry1);
147: }
148: }
149:
150: public static class Double extends CubicCurve2D {
151:
152: public double x1;
153: public double y1;
154: public double ctrlx1;
155: public double ctrly1;
156: public double ctrlx2;
157: public double ctrly2;
158: public double x2;
159: public double y2;
160:
161: public Double() {
162: }
163:
164: public Double(double x1, double y1, double ctrlx1,
165: double ctrly1, double ctrlx2, double ctrly2, double x2,
166: double y2) {
167: setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
168: }
169:
170: @Override
171: public double getX1() {
172: return x1;
173: }
174:
175: @Override
176: public double getY1() {
177: return y1;
178: }
179:
180: @Override
181: public double getCtrlX1() {
182: return ctrlx1;
183: }
184:
185: @Override
186: public double getCtrlY1() {
187: return ctrly1;
188: }
189:
190: @Override
191: public double getCtrlX2() {
192: return ctrlx2;
193: }
194:
195: @Override
196: public double getCtrlY2() {
197: return ctrly2;
198: }
199:
200: @Override
201: public double getX2() {
202: return x2;
203: }
204:
205: @Override
206: public double getY2() {
207: return y2;
208: }
209:
210: @Override
211: public Point2D getP1() {
212: return new Point2D.Double(x1, y1);
213: }
214:
215: @Override
216: public Point2D getCtrlP1() {
217: return new Point2D.Double(ctrlx1, ctrly1);
218: }
219:
220: @Override
221: public Point2D getCtrlP2() {
222: return new Point2D.Double(ctrlx2, ctrly2);
223: }
224:
225: @Override
226: public Point2D getP2() {
227: return new Point2D.Double(x2, y2);
228: }
229:
230: @Override
231: public void setCurve(double x1, double y1, double ctrlx1,
232: double ctrly1, double ctrlx2, double ctrly2, double x2,
233: double y2) {
234: this .x1 = x1;
235: this .y1 = y1;
236: this .ctrlx1 = ctrlx1;
237: this .ctrly1 = ctrly1;
238: this .ctrlx2 = ctrlx2;
239: this .ctrly2 = ctrly2;
240: this .x2 = x2;
241: this .y2 = y2;
242: }
243:
244: public Rectangle2D getBounds2D() {
245: double rx1 = Math.min(Math.min(x1, x2), Math.min(ctrlx1,
246: ctrlx2));
247: double ry1 = Math.min(Math.min(y1, y2), Math.min(ctrly1,
248: ctrly2));
249: double rx2 = Math.max(Math.max(x1, x2), Math.max(ctrlx1,
250: ctrlx2));
251: double ry2 = Math.max(Math.max(y1, y2), Math.max(ctrly1,
252: ctrly2));
253: return new Rectangle2D.Double(rx1, ry1, rx2 - rx1, ry2
254: - ry1);
255: }
256: }
257:
258: /*
259: * CubicCurve2D path iterator
260: */
261: class Iterator implements PathIterator {
262:
263: /**
264: * The source CubicCurve2D object
265: */
266: CubicCurve2D c;
267:
268: /**
269: * The path iterator transformation
270: */
271: AffineTransform t;
272:
273: /**
274: * The current segmenet index
275: */
276: int index;
277:
278: /**
279: * Constructs a new CubicCurve2D.Iterator for given line and transformation
280: * @param c - the source CubicCurve2D object
281: * @param at - the AffineTransform object to apply rectangle path
282: */
283: Iterator(CubicCurve2D c, AffineTransform t) {
284: this .c = c;
285: this .t = t;
286: }
287:
288: public int getWindingRule() {
289: return WIND_NON_ZERO;
290: }
291:
292: public boolean isDone() {
293: return index > 1;
294: }
295:
296: public void next() {
297: index++;
298: }
299:
300: public int currentSegment(double[] coords) {
301: if (isDone()) {
302: throw new NoSuchElementException(Messages
303: .getString("awt.4B")); //$NON-NLS-1$
304: }
305: int type;
306: int count;
307: if (index == 0) {
308: type = SEG_MOVETO;
309: coords[0] = c.getX1();
310: coords[1] = c.getY1();
311: count = 1;
312: } else {
313: type = SEG_CUBICTO;
314: coords[0] = c.getCtrlX1();
315: coords[1] = c.getCtrlY1();
316: coords[2] = c.getCtrlX2();
317: coords[3] = c.getCtrlY2();
318: coords[4] = c.getX2();
319: coords[5] = c.getY2();
320: count = 3;
321: }
322: if (t != null) {
323: t.transform(coords, 0, coords, 0, count);
324: }
325: return type;
326: }
327:
328: public int currentSegment(float[] coords) {
329: if (isDone()) {
330: throw new NoSuchElementException(Messages
331: .getString("awt.4B")); //$NON-NLS-1$
332: }
333: int type;
334: int count;
335: if (index == 0) {
336: type = SEG_MOVETO;
337: coords[0] = (float) c.getX1();
338: coords[1] = (float) c.getY1();
339: count = 1;
340: } else {
341: type = SEG_CUBICTO;
342: coords[0] = (float) c.getCtrlX1();
343: coords[1] = (float) c.getCtrlY1();
344: coords[2] = (float) c.getCtrlX2();
345: coords[3] = (float) c.getCtrlY2();
346: coords[4] = (float) c.getX2();
347: coords[5] = (float) c.getY2();
348: count = 3;
349: }
350: if (t != null) {
351: t.transform(coords, 0, coords, 0, count);
352: }
353: return type;
354: }
355:
356: }
357:
358: protected CubicCurve2D() {
359: }
360:
361: public abstract double getX1();
362:
363: public abstract double getY1();
364:
365: public abstract Point2D getP1();
366:
367: public abstract double getCtrlX1();
368:
369: public abstract double getCtrlY1();
370:
371: public abstract Point2D getCtrlP1();
372:
373: public abstract double getCtrlX2();
374:
375: public abstract double getCtrlY2();
376:
377: public abstract Point2D getCtrlP2();
378:
379: public abstract double getX2();
380:
381: public abstract double getY2();
382:
383: public abstract Point2D getP2();
384:
385: public abstract void setCurve(double x1, double y1, double ctrlx1,
386: double ctrly1, double ctrlx2, double ctrly2, double x2,
387: double y2);
388:
389: public void setCurve(Point2D p1, Point2D cp1, Point2D cp2,
390: Point2D p2) {
391: setCurve(p1.getX(), p1.getY(), cp1.getX(), cp1.getY(), cp2
392: .getX(), cp2.getY(), p2.getX(), p2.getY());
393: }
394:
395: public void setCurve(double[] coords, int offset) {
396: setCurve(coords[offset + 0], coords[offset + 1],
397: coords[offset + 2], coords[offset + 3],
398: coords[offset + 4], coords[offset + 5],
399: coords[offset + 6], coords[offset + 7]);
400: }
401:
402: public void setCurve(Point2D[] points, int offset) {
403: setCurve(points[offset + 0].getX(), points[offset + 0].getY(),
404: points[offset + 1].getX(), points[offset + 1].getY(),
405: points[offset + 2].getX(), points[offset + 2].getY(),
406: points[offset + 3].getX(), points[offset + 3].getY());
407: }
408:
409: public void setCurve(CubicCurve2D curve) {
410: setCurve(curve.getX1(), curve.getY1(), curve.getCtrlX1(), curve
411: .getCtrlY1(), curve.getCtrlX2(), curve.getCtrlY2(),
412: curve.getX2(), curve.getY2());
413: }
414:
415: public double getFlatnessSq() {
416: return getFlatnessSq(getX1(), getY1(), getCtrlX1(),
417: getCtrlY1(), getCtrlX2(), getCtrlY2(), getX2(), getY2());
418: }
419:
420: public static double getFlatnessSq(double x1, double y1,
421: double ctrlx1, double ctrly1, double ctrlx2, double ctrly2,
422: double x2, double y2) {
423: return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx1,
424: ctrly1), Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx2,
425: ctrly2));
426: }
427:
428: public static double getFlatnessSq(double coords[], int offset) {
429: return getFlatnessSq(coords[offset + 0], coords[offset + 1],
430: coords[offset + 2], coords[offset + 3],
431: coords[offset + 4], coords[offset + 5],
432: coords[offset + 6], coords[offset + 7]);
433: }
434:
435: public double getFlatness() {
436: return getFlatness(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
437: getCtrlX2(), getCtrlY2(), getX2(), getY2());
438: }
439:
440: public static double getFlatness(double x1, double y1,
441: double ctrlx1, double ctrly1, double ctrlx2, double ctrly2,
442: double x2, double y2) {
443: return Math.sqrt(getFlatnessSq(x1, y1, ctrlx1, ctrly1, ctrlx2,
444: ctrly2, x2, y2));
445: }
446:
447: public static double getFlatness(double coords[], int offset) {
448: return getFlatness(coords[offset + 0], coords[offset + 1],
449: coords[offset + 2], coords[offset + 3],
450: coords[offset + 4], coords[offset + 5],
451: coords[offset + 6], coords[offset + 7]);
452: }
453:
454: public void subdivide(CubicCurve2D left, CubicCurve2D right) {
455: subdivide(this , left, right);
456: }
457:
458: public static void subdivide(CubicCurve2D src, CubicCurve2D left,
459: CubicCurve2D right) {
460: double x1 = src.getX1();
461: double y1 = src.getY1();
462: double cx1 = src.getCtrlX1();
463: double cy1 = src.getCtrlY1();
464: double cx2 = src.getCtrlX2();
465: double cy2 = src.getCtrlY2();
466: double x2 = src.getX2();
467: double y2 = src.getY2();
468: double cx = (cx1 + cx2) / 2.0;
469: double cy = (cy1 + cy2) / 2.0;
470: cx1 = (x1 + cx1) / 2.0;
471: cy1 = (y1 + cy1) / 2.0;
472: cx2 = (x2 + cx2) / 2.0;
473: cy2 = (y2 + cy2) / 2.0;
474: double ax = (cx1 + cx) / 2.0;
475: double ay = (cy1 + cy) / 2.0;
476: double bx = (cx2 + cx) / 2.0;
477: double by = (cy2 + cy) / 2.0;
478: cx = (ax + bx) / 2.0;
479: cy = (ay + by) / 2.0;
480: if (left != null) {
481: left.setCurve(x1, y1, cx1, cy1, ax, ay, cx, cy);
482: }
483: if (right != null) {
484: right.setCurve(cx, cy, bx, by, cx2, cy2, x2, y2);
485: }
486: }
487:
488: public static void subdivide(double src[], int srcOff,
489: double left[], int leftOff, double right[], int rightOff) {
490: double x1 = src[srcOff + 0];
491: double y1 = src[srcOff + 1];
492: double cx1 = src[srcOff + 2];
493: double cy1 = src[srcOff + 3];
494: double cx2 = src[srcOff + 4];
495: double cy2 = src[srcOff + 5];
496: double x2 = src[srcOff + 6];
497: double y2 = src[srcOff + 7];
498: double cx = (cx1 + cx2) / 2.0;
499: double cy = (cy1 + cy2) / 2.0;
500: cx1 = (x1 + cx1) / 2.0;
501: cy1 = (y1 + cy1) / 2.0;
502: cx2 = (x2 + cx2) / 2.0;
503: cy2 = (y2 + cy2) / 2.0;
504: double ax = (cx1 + cx) / 2.0;
505: double ay = (cy1 + cy) / 2.0;
506: double bx = (cx2 + cx) / 2.0;
507: double by = (cy2 + cy) / 2.0;
508: cx = (ax + bx) / 2.0;
509: cy = (ay + by) / 2.0;
510: if (left != null) {
511: left[leftOff + 0] = x1;
512: left[leftOff + 1] = y1;
513: left[leftOff + 2] = cx1;
514: left[leftOff + 3] = cy1;
515: left[leftOff + 4] = ax;
516: left[leftOff + 5] = ay;
517: left[leftOff + 6] = cx;
518: left[leftOff + 7] = cy;
519: }
520: if (right != null) {
521: right[rightOff + 0] = cx;
522: right[rightOff + 1] = cy;
523: right[rightOff + 2] = bx;
524: right[rightOff + 3] = by;
525: right[rightOff + 4] = cx2;
526: right[rightOff + 5] = cy2;
527: right[rightOff + 6] = x2;
528: right[rightOff + 7] = y2;
529: }
530: }
531:
532: public static int solveCubic(double eqn[]) {
533: return solveCubic(eqn, eqn);
534: }
535:
536: public static int solveCubic(double eqn[], double res[]) {
537: return Crossing.solveCubic(eqn, res);
538: }
539:
540: public boolean contains(double px, double py) {
541: return Crossing.isInsideEvenOdd(Crossing.crossShape(this , px,
542: py));
543: }
544:
545: public boolean contains(double rx, double ry, double rw, double rh) {
546: int cross = Crossing.intersectShape(this , rx, ry, rw, rh);
547: return cross != Crossing.CROSSING
548: && Crossing.isInsideEvenOdd(cross);
549: }
550:
551: public boolean intersects(double rx, double ry, double rw, double rh) {
552: int cross = Crossing.intersectShape(this , rx, ry, rw, rh);
553: return cross == Crossing.CROSSING
554: || Crossing.isInsideEvenOdd(cross);
555: }
556:
557: public boolean contains(Point2D p) {
558: return contains(p.getX(), p.getY());
559: }
560:
561: public boolean intersects(Rectangle2D r) {
562: return intersects(r.getX(), r.getY(), r.getWidth(), r
563: .getHeight());
564: }
565:
566: public boolean contains(Rectangle2D r) {
567: return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
568: }
569:
570: public Rectangle getBounds() {
571: return getBounds2D().getBounds();
572: }
573:
574: public PathIterator getPathIterator(AffineTransform t) {
575: return new Iterator(this , t);
576: }
577:
578: public PathIterator getPathIterator(AffineTransform at,
579: double flatness) {
580: return new FlatteningPathIterator(getPathIterator(at), flatness);
581: }
582:
583: @Override
584: public Object clone() {
585: try {
586: return super .clone();
587: } catch (CloneNotSupportedException e) {
588: throw new InternalError();
589: }
590: }
591: }
|