source: trunk/gcc/libjava/java/awt/geom/Arc2D.java@ 3148

Last change on this file since 3148 was 1389, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 30.6 KB
Line 
1/* Arc2D.java -- represents an arc in 2-D space
2 Copyright (C) 2002 Free Software Foundation
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38
39package java.awt.geom;
40
41import java.util.NoSuchElementException;
42
43/**
44 * This class represents all arcs (segments of an ellipse in 2-D space). The
45 * arcs are defined by starting angle and extent (arc length) in degrees, as
46 * opposed to radians (like the rest of Java), and can be open, chorded, or
47 * wedge shaped. The angles are skewed according to the ellipse, so that 45
48 * degrees always points to the upper right corner (positive x, negative y)
49 * of the bounding rectangle. A positive extent draws a counterclockwise arc,
50 * and while the angle can be any value, the path iterator only traverses the
51 * first 360 degrees. Storage is up to the subclasses.
52 *
53 * @author Eric Blake <[email protected]>
54 * @since 1.2
55 * @status updated to 1.4, but still missing functionality
56 */
57public abstract class Arc2D extends RectangularShape
58{
59 /**
60 * An open arc, with no segment connecting the endpoints. This type of
61 * arc still contains the same points as a chorded version.
62 */
63 public static final int OPEN = 0;
64
65 /**
66 * A closed arc with a single segment connecting the endpoints (a chord).
67 */
68 public static final int CHORD = 1;
69
70 /**
71 * A closed arc with two segments, one from each endpoint, meeting at the
72 * center of the ellipse.
73 */
74 public static final int PIE = 2;
75
76 /** The closure type of this arc. */
77 private int type;
78
79 /**
80 * Create a new arc, with the specified closure type.
81 *
82 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}.
83 * @throws IllegalArgumentException if type is invalid
84 */
85 protected Arc2D(int type)
86 {
87 if (type < OPEN || type > PIE)
88 throw new IllegalArgumentException();
89 this.type = type;
90 }
91
92 /**
93 * Get the starting angle of the arc in degrees.
94 *
95 * @return the starting angle
96 * @see #setAngleStart(double)
97 */
98 public abstract double getAngleStart();
99
100 /**
101 * Get the extent angle of the arc in degrees.
102 *
103 * @return the extent angle
104 * @see #setAngleExtent(double)
105 */
106 public abstract double getAngleExtent();
107
108 /**
109 * Return the closure type of the arc.
110 *
111 * @return the closure type
112 * @see #OPEN
113 * @see #CHORD
114 * @see #PIE
115 * @see #setArcType(int)
116 */
117 public int getArcType()
118 {
119 return type;
120 }
121
122 /**
123 * Returns the starting point of the arc.
124 *
125 * @return the start point
126 */
127 public Point2D getStartPoint()
128 {
129 double angle = getAngleStart() * (-180 / Math.PI);
130 double x = (Math.cos(angle) * getWidth() + getX()) / 2;
131 double y = (Math.sin(angle) * getHeight() + getY()) / 2;
132 return new Point2D.Double(x, y);
133 }
134
135 /**
136 * Returns the ending point of the arc.
137 *
138 * @return the end point
139 */
140 public Point2D getEndPoint()
141 {
142 double angle = (getAngleStart() + getAngleExtent()) * (-180 / Math.PI);
143 double x = (Math.cos(angle) * getWidth() + getX()) / 2;
144 double y = (Math.sin(angle) * getHeight() + getY()) / 2;
145 return new Point2D.Double(x, y);
146 }
147
148 /**
149 * Set the parameters of the arc. The angles are in degrees, and a positive
150 * extent sweeps counterclockwise (from the positive x-axis to the negative
151 * y-axis).
152 *
153 * @param x the new x coordinate of the lower left of the bounding box
154 * @param y the new y coordinate of the lower left of the bounding box
155 * @param w the new width of the bounding box
156 * @param h the new height of the bounding box
157 * @param start the start angle, in degrees
158 * @param extent the arc extent, in degrees
159 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
160 * @throws IllegalArgumentException if type is invalid
161 */
162 public abstract void setArc(double x, double y, double w, double h,
163 double start, double extent, int type);
164
165 /**
166 * Set the parameters of the arc. The angles are in degrees, and a positive
167 * extent sweeps counterclockwise (from the positive x-axis to the negative
168 * y-axis).
169 *
170 * @param p the lower left point of the bounding box
171 * @param d the dimensions of the bounding box
172 * @param start the start angle, in degrees
173 * @param extent the arc extent, in degrees
174 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
175 * @throws IllegalArgumentException if type is invalid
176 * @throws NullPointerException if p or d is null
177 */
178 public void setArc(Point2D p, Dimension2D d,
179 double start, double extent, int type)
180 {
181 setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(),
182 start, extent, type);
183 }
184
185 /**
186 * Set the parameters of the arc. The angles are in degrees, and a positive
187 * extent sweeps counterclockwise (from the positive x-axis to the negative
188 * y-axis).
189 *
190 * @param r the new bounding box
191 * @param start the start angle, in degrees
192 * @param extent the arc extent, in degrees
193 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
194 * @throws IllegalArgumentException if type is invalid
195 * @throws NullPointerException if r is null
196 */
197 public void setArc(Rectangle2D r, double start, double extent, int type)
198 {
199 setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(),
200 start, extent, type);
201 }
202
203 /**
204 * Set the parameters of the arc from the given one.
205 *
206 * @param a the arc to copy
207 * @throws NullPointerException if a is null
208 */
209 public void setArc(Arc2D a)
210 {
211 setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(),
212 a.getAngleStart(), a.getAngleExtent(), a.getArcType());
213 }
214
215 /**
216 * Set the parameters of the arc. The angles are in degrees, and a positive
217 * extent sweeps counterclockwise (from the positive x-axis to the negative
218 * y-axis). This controls the center point and radius, so the arc will be
219 * circular.
220 *
221 * @param x the x coordinate of the center of the circle
222 * @param y the y coordinate of the center of the circle
223 * @param r the radius of the circle
224 * @param start the start angle, in degrees
225 * @param extent the arc extent, in degrees
226 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
227 * @throws IllegalArgumentException if type is invalid
228 */
229 public void setArcByCenter(double x, double y, double r,
230 double start, double extent, int type)
231 {
232 setArc(x - r, y - r, r + r, r + r, start, extent, type);
233 }
234
235 /**
236 * Sets the parameters of the arc by finding the tangents of two lines, and
237 * using the specified radius. The arc will be circular, will begin on the
238 * tangent point of the line extending from p1 to p2, and will end on the
239 * tangent point of the line extending from p2 to p3.
240 *
241 * XXX What happens if the points are colinear, or the radius negative?
242 *
243 * @param p1 the first point
244 * @param p2 the tangent line intersection point
245 * @param p3 the third point
246 * @param r the radius of the arc
247 * @throws NullPointerException if any point is null
248 */
249 public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r)
250 {
251 // XXX Implement.
252 throw new Error("not implemented");
253 }
254
255 /**
256 * Set the start, in degrees.
257 *
258 * @param start the new start angle
259 * @see #getAngleStart()
260 */
261 public abstract void setAngleStart(double start);
262
263 /**
264 * Set the extent, in degrees.
265 *
266 * @param extent the new extent angle
267 * @see #getAngleExtent()
268 */
269 public abstract void setAngleExtent(double extent);
270
271 /**
272 * Sets the starting angle to the angle of the given point relative to
273 * the center of the arc. The extent remains constant; in other words,
274 * this rotates the arc.
275 *
276 * @param p the new start point
277 * @throws NullPointerException if p is null
278 * @see #getStartPoint()
279 * @see #getAngleStart()
280 */
281 public void setAngleStart(Point2D p)
282 {
283 double x = ((p.getX() * 2) - getX()) / getWidth();
284 double y = ((p.getY() * 2) - getY()) / getHeight();
285 setAngleStart(Math.atan2(y, x) * (-180 / Math.PI));
286 }
287
288 /**
289 * Sets the starting and extent angles to those of the given points
290 * relative to the center of the arc. The arc will be non-empty, and will
291 * extend counterclockwise.
292 *
293 * @param x1 the first x coordinate
294 * @param y1 the first y coordinate
295 * @param x2 the second x coordinate
296 * @param y2 the second y coordinate
297 * @see #setAngleStart(Point2D)
298 */
299 public void setAngles(double x1, double y1, double x2, double y2)
300 {
301 // Normalize the points.
302 double mx = getX();
303 double my = getY();
304 double mw = getWidth();
305 double mh = getHeight();
306 x1 = ((x1 * 2) - mx) / mw;
307 y1 = ((y1 * 2) - my) / mh;
308 x2 = ((x2 * 2) - mx) / mw;
309 y2 = ((y2 * 2) - my) / mh;
310 double start = Math.atan2(y1, x1) * (-180 / Math.PI);
311 double extent = Math.atan2(y2, x2) * (-180 / Math.PI) - start;
312 if (extent < 0)
313 extent += 360;
314 setAngleStart(start);
315 setAngleExtent(extent);
316 }
317
318 /**
319 * Sets the starting and extent angles to those of the given points
320 * relative to the center of the arc. The arc will be non-empty, and will
321 * extend counterclockwise.
322 *
323 * @param p1 the first point
324 * @param p2 the second point
325 * @throws NullPointerException if either point is null
326 * @see #setAngleStart(Point2D)
327 */
328 public void setAngles(Point2D p1, Point2D p2)
329 {
330 setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY());
331 }
332
333 /**
334 * Set the closure type of this arc.
335 *
336 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
337 * @throws IllegalArgumentException if type is invalid
338 * @see #getArcType()
339 */
340 public void setArcType(int type)
341 {
342 if (type < OPEN || type > PIE)
343 throw new IllegalArgumentException();
344 this.type = type;
345 }
346
347 /**
348 * Sets the location and bounds of the ellipse of which this arc is a part.
349 *
350 * @param x the new x coordinate
351 * @param y the new y coordinate
352 * @param w the new width
353 * @param h the new height
354 * @see #getFrame()
355 */
356 public void setFrame(double x, double y, double w, double h)
357 {
358 setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type);
359 }
360
361 /**
362 * Gets the bounds of the arc. This is much tighter than
363 * <code>getBounds</code>, as it takes into consideration the start and
364 * end angles, and the center point of a pie wedge, rather than just the
365 * overall ellipse.
366 *
367 * @return the bounds of the arc
368 * @see #getBounds()
369 */
370 public Rectangle2D getBounds2D()
371 {
372 double extent = getAngleExtent();
373 if (Math.abs(extent) >= 360)
374 return makeBounds(getX(), getY(), getWidth(), getHeight());
375 // XXX Finish implementing.
376 throw new Error("not implemented");
377 }
378
379 /**
380 * Construct a bounding box in a precision appropriate for the subclass.
381 *
382 * @param x the x coordinate
383 * @param y the y coordinate
384 * @param w the width
385 * @param h the height
386 * @return the rectangle for use in getBounds2D
387 */
388 protected abstract Rectangle2D makeBounds(double x, double y,
389 double w, double h);
390
391 /**
392 * Tests if the given angle, in degrees, is included in the arc.
393 *
394 * XXX Does this normalize all angles to -180 - 180 first?
395 *
396 * @param a the angle to test
397 * @return true if it is contained
398 */
399 public boolean containsAngle(double a)
400 {
401 // XXX Implement.
402 throw new Error("not implemented");
403 }
404
405 /**
406 * Determines if the arc contains the given point. If the bounding box
407 * is empty, then this will return false.
408 *
409 * @param x the x coordinate to test
410 * @param y the y coordinate to test
411 * @return true if the point is inside the arc
412 */
413 public boolean contains(double x, double y)
414 {
415 double w = getWidth();
416 double h = getHeight();
417 if (w <= 0 || h <= 0)
418 return false;
419 // XXX Finish implementing.
420 throw new Error("not implemented");
421 }
422
423 /**
424 * Tests if a given rectangle intersects the area of the arc.
425 *
426 * @param x the x coordinate of the rectangle
427 * @param y the y coordinate of the rectangle
428 * @param w the width of the rectangle
429 * @param h the height of the rectangle
430 * @return true if the two shapes share common points
431 */
432 public boolean intersects(double x, double y, double w, double h)
433 {
434 double mw = getWidth();
435 double mh = getHeight();
436 if (mw <= 0 || mh <= 0 || w <= 0 || h <= 0)
437 return false;
438 // XXX Finish implementing.
439 throw new Error("not implemented");
440 }
441
442 /**
443 * Tests if a given rectangle is contained in the area of the arc.
444 *
445 * @param x the x coordinate of the rectangle
446 * @param y the y coordinate of the rectangle
447 * @param w the width of the rectangle
448 * @param h the height of the rectangle
449 * @return true if the arc contains the rectangle
450 */
451 public boolean contains(double x, double y, double w, double h)
452 {
453 double mw = getWidth();
454 double mh = getHeight();
455 if (mw <= 0 || mh <= 0 || w <= 0 || h <= 0)
456 return false;
457 // XXX Finish implementing.
458 throw new Error("not implemented");
459 }
460
461 /**
462 * Tests if a given rectangle is contained in the area of the arc.
463 *
464 * @param r the rectangle
465 * @return true if the arc contains the rectangle
466 */
467 public boolean contains(Rectangle2D r)
468 {
469 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
470 }
471
472 /**
473 * Returns an iterator over this arc, with an optional transformation.
474 * This iterator is threadsafe, so future modifications to the arc do not
475 * affect the iteration.
476 *
477 * @param at the transformation, or null
478 * @return a path iterator
479 */
480 public PathIterator getPathIterator(AffineTransform at)
481 {
482 return new ArcIterator(this, at);
483 }
484
485 /**
486 * This class is used to iterate over an arc. Since ellipses are a subclass
487 * of arcs, this is used by Ellipse2D as well.
488 *
489 * @author Eric Blake <[email protected]>
490 */
491 static final class ArcIterator implements PathIterator
492 {
493 /** The current iteration. */
494 private int current;
495
496 /** The last iteration. */
497 private final int limit;
498
499 /** The optional transformation. */
500 private final AffineTransform xform;
501
502 /** The x coordinate of the bounding box. */
503 private final double x;
504
505 /** The y coordinate of the bounding box. */
506 private final double y;
507
508 /** The width of the bounding box. */
509 private final double w;
510
511 /** The height of the bounding box. */
512 private final double h;
513
514 /** The start angle, in radians (not degrees). */
515 private final double start;
516
517 /** The extent angle, in radians (not degrees). */
518 private final double extent;
519
520 /** The arc closure type. */
521 private final int type;
522
523 /**
524 * Construct a new iterator over an arc.
525 *
526 * @param a the arc
527 * @param xform the transform
528 */
529 ArcIterator(Arc2D a, AffineTransform xform)
530 {
531 this.xform = xform;
532 x = a.getX();
533 y = a.getY();
534 w = a.getWidth();
535 h = a.getHeight();
536 start = a.getAngleStart() * (Math.PI / 180);
537 extent = a.getAngleExtent() * (Math.PI / 180);
538 type = a.type;
539 double e = extent < 0 ? -extent : extent;
540 if (w < 0 || h < 0)
541 limit = -1;
542 else if (e == 0)
543 limit = type;
544 else if (e <= 90)
545 limit = type + 1;
546 else if (e <= 180)
547 limit = type + 2;
548 else if (e <= 270)
549 limit = type + 3;
550 else
551 limit = type + 4;
552 }
553
554 /**
555 * Construct a new iterator over an ellipse.
556 *
557 * @param e the ellipse
558 * @param xform the transform
559 */
560 ArcIterator(Ellipse2D e, AffineTransform xform)
561 {
562 this.xform = xform;
563 x = e.getX();
564 y = e.getY();
565 w = e.getWidth();
566 h = e.getHeight();
567 start = 0;
568 extent = -2 * Math.PI;
569 type = CHORD;
570 limit = (w < 0 || h < 0) ? -1 : 5;
571 }
572
573 /**
574 * Return the winding rule.
575 *
576 * @return {@link PathIterator#WIND_NON_ZERO}
577 */
578 public int getWindingRule()
579 {
580 return WIND_NON_ZERO;
581 }
582
583 /**
584 * Test if the iteration is complete.
585 *
586 * @return true if more segments exist
587 */
588 public boolean isDone()
589 {
590 return current > limit;
591 }
592
593 /**
594 * Advance the iterator.
595 */
596 public void next()
597 {
598 current++;
599 }
600
601 /**
602 * Put the current segment into the array, and return the segment type.
603 *
604 * @param coords an array of 6 elements
605 * @return the segment type
606 * @throws NullPointerException if coords is null
607 * @throws ArrayIndexOutOfBoundsException if coords is too small
608 */
609 public int currentSegment(float[] coords)
610 {
611 if (current > limit)
612 throw new NoSuchElementException("arc iterator out of bounds");
613 if (current == 0)
614 {
615 coords[0] = (float) (Math.cos(start) * w + x) / 2;
616 coords[1] = (float) (Math.sin(start) * h + y) / 2;
617 if (xform != null)
618 xform.transform(coords, 0, coords, 0, 1);
619 return SEG_MOVETO;
620 }
621 if (type != OPEN && current == limit)
622 return SEG_CLOSE;
623 if (type == PIE && current == limit - 1)
624 {
625 coords[0] = (float) (x + w / 2);
626 coords[1] = (float) (y + h / 2);
627 if (xform != null)
628 xform.transform(coords, 0, coords, 0, 1);
629 return SEG_LINETO;
630 }
631 // XXX Fill coords with 2 control points and next quarter point
632 coords[0] = (float) 0;
633 coords[1] = (float) 0;
634 coords[2] = (float) 0;
635 coords[3] = (float) 0;
636 coords[4] = (float) 0;
637 coords[5] = (float) 0;
638 if (xform != null)
639 xform.transform(coords, 0, coords, 0, 3);
640 return SEG_CUBICTO;
641 }
642
643 /**
644 * Put the current segment into the array, and return the segment type.
645 *
646 * @param coords an array of 6 elements
647 * @return the segment type
648 * @throws NullPointerException if coords is null
649 * @throws ArrayIndexOutOfBoundsException if coords is too small
650 */
651 public int currentSegment(double[] coords)
652 {
653 if (current > limit)
654 throw new NoSuchElementException("arc iterator out of bounds");
655 if (current == 0)
656 {
657 coords[0] = (Math.cos(start) * w + x) / 2;
658 coords[1] = (Math.sin(start) * h + y) / 2;
659 if (xform != null)
660 xform.transform(coords, 0, coords, 0, 1);
661 return SEG_MOVETO;
662 }
663 if (type != OPEN && current == limit)
664 return SEG_CLOSE;
665 if (type == PIE && current == limit - 1)
666 {
667 coords[0] = (float) (x + w / 2);
668 coords[1] = (float) (y + h / 2);
669 if (xform != null)
670 xform.transform(coords, 0, coords, 0, 1);
671 return SEG_LINETO;
672 }
673 // XXX Fill coords with 2 control points and next quarter point
674 coords[0] = 0;
675 coords[1] = 0;
676 coords[2] = 0;
677 coords[3] = 0;
678 coords[4] = 0;
679 coords[5] = 0;
680 if (xform != null)
681 xform.transform(coords, 0, coords, 0, 3);
682 return SEG_CUBICTO;
683 }
684 } // class ArcIterator
685
686 /**
687 * This class implements an arc in double precision.
688 *
689 * @author Eric Blake <[email protected]
690 * @since 1.2
691 */
692 public static class Double extends Arc2D
693 {
694 /** The x coordinate of the box bounding the ellipse of this arc. */
695 public double x;
696
697 /** The y coordinate of the box bounding the ellipse of this arc. */
698 public double y;
699
700 /** The width of the box bounding the ellipse of this arc. */
701 public double width;
702
703 /** The height of the box bounding the ellipse of this arc. */
704 public double height;
705
706 /** The start angle of this arc, in degrees. */
707 public double start;
708
709 /** The extent angle of this arc, in degrees. */
710 public double extent;
711
712 /**
713 * Create a new, open arc at (0,0) with 0 extent.
714 */
715 public Double()
716 {
717 super(OPEN);
718 }
719
720 /**
721 * Create a new arc of the given type at (0,0) with 0 extent.
722 *
723 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
724 * @throws IllegalArgumentException if type is invalid
725 */
726 public Double(int type)
727 {
728 super(type);
729 }
730
731 /**
732 * Create a new arc with the given dimensions.
733 *
734 * @param x the x coordinate
735 * @param y the y coordinate
736 * @param w the width
737 * @param h the height
738 * @param start the start angle, in degrees
739 * @param extent the extent, in degrees
740 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
741 * @throws IllegalArgumentException if type is invalid
742 */
743 public Double(double x, double y, double w, double h,
744 double start, double extent, int type)
745 {
746 super(type);
747 this.x = x;
748 this.y = y;
749 width = w;
750 height = h;
751 this.start = start;
752 this.extent = extent;
753 }
754
755 /**
756 * Create a new arc with the given dimensions.
757 *
758 * @param r the bounding box
759 * @param start the start angle, in degrees
760 * @param extent the extent, in degrees
761 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
762 * @throws IllegalArgumentException if type is invalid
763 * @throws NullPointerException if r is null
764 */
765 public Double(Rectangle2D r, double start, double extent, int type)
766 {
767 super(type);
768 x = r.getX();
769 y = r.getY();
770 width = r.getWidth();
771 height = r.getHeight();
772 this.start = start;
773 this.extent = extent;
774 }
775
776 /**
777 * Return the x coordinate of the bounding box.
778 *
779 * @return the value of x
780 */
781 public double getX()
782 {
783 return x;