blob: bc1e95c643e6f568abe9d9d280c9851c50cbb403 [file] [log] [blame]
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/**
18 * @author Denis M. Kishenko
19 * @version $Revision$
20 */
21package java.awt.geom;
22
23import java.util.NoSuchElementException;
24
25import org.apache.harmony.awt.internal.nls.Messages;
26
27/**
28 * The Class Arc2D represents a segment of a curve inscribed
29 * in a rectangle. The curve is defined by a start angle and an
30 * extent angle (the end angle minus the start angle) as
31 * a pie wedge whose point is in the center of the rectangle.
32 * The Arc2D as a shape may be either OPEN (including nothing
33 * but the curved arc segment itself), CHORD (the curved arc
34 * segment closed by a connecting segment from the end to the
35 * beginning of the arc, or PIE (the segments from the end
36 * of the arc to the center of the rectangle and from the
37 * center of the rectangle back to the arc's start point are
38 * included).
39 */
40public abstract class Arc2D extends RectangularShape {
41
42 /** The arc type OPEN indicates that the shape includes only the
43 * curved arc segment. */
44 public final static int OPEN = 0;
45
46 /** The arc type CHORD indicates that as a shape the connecting
47 * segment from the end point of the curved arc to the beginning
48 * point is included. */
49 public final static int CHORD = 1;
50
51 /** The arc type PIE indicates that as a shape the two segments
52 * from the arc's endpoint to the center of the rectangle and from
53 * the center of the rectangle to the arc's endpoint are included. */
54 public final static int PIE = 2;
55
56 /**
57 * The Class Float is a subclass of Arc2D in which all of the
58 * data values are given as floats.
59 * @see Arc2D.Double
60 */
61 public static class Float extends Arc2D {
62
63 /** The x coordinate of the upper left corner of the rectangle that
64 * contains the arc. */
65 public float x;
66
67 /** The y coordinate of the upper left corner of the rectangle that
68 * contains the arc. */
69 public float y;
70
71 /** The width of the rectangle that contains the arc. */
72 public float width;
73
74 /** The height of the rectangle that contains the arc. */
75 public float height;
76
77 /** The start angle of the arc in degrees. */
78 public float start;
79
80 /** The width angle of the arc in degrees. */
81 public float extent;
82
83 /**
84 * Instantiates a new Arc2D of type OPEN with float values.
85 */
86 public Float() {
87 super(OPEN);
88 }
89
90 /**
91 * Instantiates a new Arc2D of the specified type with float values.
92 *
93 * @param type the type of the new Arc2D, either {@link Arc2D#OPEN},
94 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
95 */
96 public Float(int type) {
97 super(type);
98 }
99
100 /**
101 * Instantiates a Arc2D with the specified float-valued data.
102 *
103 * @param x the x coordinate of the upper left corner of the rectangle that
104 * contains the arc.
105 * @param y the y coordinate of the upper left corner of the rectangle that
106 * contains the arc.
107 * @param width the width of the rectangle that
108 * contains the arc.
109 * @param height the height of the rectangle that
110 * contains the arc.
111 * @param start the start angle of the arc in degrees.
112 * @param extent the width angle of the arc in degrees.
113 * @param type the type of the new Arc2D, either {@link Arc2D#OPEN},
114 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
115 */
116 public Float(float x, float y, float width, float height, float start, float extent, int type) {
117 super(type);
118 this.x = x;
119 this.y = y;
120 this.width = width;
121 this.height = height;
122 this.start = start;
123 this.extent = extent;
124 }
125
126 /**
127 * Instantiates a new Angle2D with the specified float-valued data
128 * and the bounding rectangle given by the parameter bounds.
129 *
130 * @param bounds the bounding rectangle of the Angle2D.
131 * @param start the start angle of the arc in degrees.
132 * @param extent the width angle of the arc in degrees.
133 * @param type the type of the new Arc2D, either {@link Arc2D#OPEN},
134 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
135 */
136 public Float(Rectangle2D bounds, float start, float extent, int type) {
137 super(type);
138 this.x = (float)bounds.getX();
139 this.y = (float)bounds.getY();
140 this.width = (float)bounds.getWidth();
141 this.height = (float)bounds.getHeight();
142 this.start = start;
143 this.extent = extent;
144 }
145
146 @Override
147 public double getX() {
148 return x;
149 }
150
151 @Override
152 public double getY() {
153 return y;
154 }
155
156 @Override
157 public double getWidth() {
158 return width;
159 }
160
161 @Override
162 public double getHeight() {
163 return height;
164 }
165
166 @Override
167 public double getAngleStart() {
168 return start;
169 }
170
171 @Override
172 public double getAngleExtent() {
173 return extent;
174 }
175
176 @Override
177 public boolean isEmpty() {
178 return width <= 0.0f || height <= 0.0f;
179 }
180
181 @Override
182 public void setArc(double x, double y, double width, double height,
183 double start, double extent, int type)
184 {
185 this.setArcType(type);
186 this.x = (float)x;
187 this.y = (float)y;
188 this.width = (float)width;
189 this.height = (float)height;
190 this.start = (float)start;
191 this.extent = (float)extent;
192 }
193
194 @Override
195 public void setAngleStart(double start) {
196 this.start = (float)start;
197 }
198
199 @Override
200 public void setAngleExtent(double extent) {
201 this.extent = (float)extent;
202 }
203
204 @Override
205 protected Rectangle2D makeBounds(double x, double y, double width, double height) {
206 return new Rectangle2D.Float((float)x, (float)y, (float)width, (float)height);
207 }
208
209 }
210
211 /**
212 * The Class Double is a subclass of Arc2D in which all of the
213 * data values are given as doubles.
214 * @see Arc2D.Float
215 */
216 public static class Double extends Arc2D {
217
218 /** The x coordinate of the upper left corner of the rectangle that
219 * contains the arc. */
220 public double x;
221
222 /** The y coordinate of the upper left corner of the rectangle that
223 * contains the arc. */
224 public double y;
225
226 /** The width of the rectangle that contains the arc. */
227 public double width;
228
229 /** The height of the rectangle that contains the arc. */
230 public double height;
231
232 /** The start angle of the arc in degrees. */
233 public double start;
234
235 /** The width angle of the arc in degrees. */
236 public double extent;
237
238 /**
239 * Instantiates a new Arc2D of type OPEN with double values.
240 */
241 public Double() {
242 super(OPEN);
243 }
244
245 /**
246 * Instantiates a new Arc2D of the specified type with double values.
247 *
248 * @param type the type of the new Arc2D, either {@link Arc2D#OPEN},
249 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
250 */
251 public Double(int type) {
252 super(type);
253 }
254
255 /**
256 * Instantiates a Arc2D with the specified double-valued data.
257 *
258 * @param x the x coordinate of the upper left corner of the rectangle that
259 * contains the arc.
260 * @param y the y coordinate of the upper left corner of the rectangle that
261 * contains the arc.
262 * @param width the width of the rectangle that
263 * contains the arc.
264 * @param height the height of the rectangle that
265 * contains the arc.
266 * @param start the start angle of the arc in degrees.
267 * @param extent the width angle of the arc in degrees.
268 * @param type the type of the new Arc2D, either {@link Arc2D#OPEN},
269 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
270 */
271 public Double(double x, double y, double width, double height,
272 double start, double extent, int type)
273 {
274 super(type);
275 this.x = x;
276 this.y = y;
277 this.width = width;
278 this.height = height;
279 this.start = start;
280 this.extent = extent;
281 }
282
283 /**
284 * Instantiates a new Angle2D with the specified float-valued data
285 * and the bounding rectangle given by the parameter bounds.
286 *
287 * @param bounds the bounding rectangle of the Angle2D.
288 * @param start the start angle of the arc in degrees.
289 * @param extent the width angle of the arc in degrees.
290 * @param type the type of the new Arc2D, either {@link Arc2D#OPEN},
291 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
292 */
293 public Double(Rectangle2D bounds, double start, double extent, int type) {
294 super(type);
295 this.x = bounds.getX();
296 this.y = bounds.getY();
297 this.width = bounds.getWidth();
298 this.height = bounds.getHeight();
299 this.start = start;
300 this.extent = extent;
301 }
302
303 @Override
304 public double getX() {
305 return x;
306 }
307
308 @Override
309 public double getY() {
310 return y;
311 }
312
313 @Override
314 public double getWidth() {
315 return width;
316 }
317
318 @Override
319 public double getHeight() {
320 return height;
321 }
322
323 @Override
324 public double getAngleStart() {
325 return start;
326 }
327
328 @Override
329 public double getAngleExtent() {
330 return extent;
331 }
332
333 @Override
334 public boolean isEmpty() {
335 return width <= 0.0 || height <= 0.0;
336 }
337
338 @Override
339 public void setArc(double x, double y, double width, double height,
340 double start, double extent, int type)
341 {
342 this.setArcType(type);
343 this.x = x;
344 this.y = y;
345 this.width = width;
346 this.height = height;
347 this.start = start;
348 this.extent = extent;
349 }
350
351 @Override
352 public void setAngleStart(double start) {
353 this.start = start;
354 }
355
356 @Override
357 public void setAngleExtent(double extent) {
358 this.extent = extent;
359 }
360
361 @Override
362 protected Rectangle2D makeBounds(double x, double y, double width, double height) {
363 return new Rectangle2D.Double(x, y, width, height);
364 }
365
366 }
367
368 /**
369 * The Class Iterator is the subclass of PathIterator that is used to
370 * traverse the boundary of a shape of type Arc2D.
371 */
372 class Iterator implements PathIterator {
373
374 /** The x coordinate of the center of the arc's bounding rectangle. */
375 double x;
376
377 /** The y coordinate of the center of the arc's bounding rectangle. */
378 double y;
379
380 /** Half of the width of the arc's bounding rectangle (the radius in the case of a circular arc). */
381 double width;
382
383 /** Half of the height of the arc's bounding rectangle (the radius in the case of a circular arc). */
384 double height;
385
386 /** The start angle of the arc in degrees. */
387 double angle;
388
389 /** The angle extent in degrees. */
390 double extent;
391
392 /** The closure type of the arc. */
393 int type;
394
395 /** The path iterator transformation. */
396 AffineTransform t;
397
398 /** The current segment index. */
399 int index;
400
401 /** The number of arc segments the source arc subdivided to be approximated by Bezier curves. Depends on extent value. */
402 int arcCount;
403
404 /** The number of line segments. Depends on closure type. */
405 int lineCount;
406
407 /** The step to calculate next arc subdivision point. */
408 double step;
409
410 /** The temporary value of cosinus of the current angle. */
411 double cos;
412
413 /** The temporary value of sinus of the current angle. */
414 double sin;
415
416 /** The coefficient to calculate control points of Bezier curves. */
417 double k;
418
419 /** The temporary value of x coordinate of the Bezier curve control vector. */
420 double kx;
421
422 /** The temporary value of y coordinate of the Bezier curve control vector. */
423 double ky;
424
425 /** The x coordinate of the first path point (MOVE_TO). */
426 double mx;
427
428 /** The y coordinate of the first path point (MOVE_TO). */
429 double my;
430
431 /**
432 * Constructs a new Arc2D.Iterator for given line and transformation
433 *
434 * @param a - the source Arc2D object
435 * @param t the AffineTransformation.
436 */
437 Iterator(Arc2D a, AffineTransform t) {
438 if (width < 0 || height < 0) {
439 arcCount = 0;
440 lineCount = 0;
441 index = 1;
442 return;
443 }
444
445 this.width = a.getWidth() / 2.0;
446 this.height = a.getHeight() / 2.0;
447 this.x = a.getX() + width;
448 this.y = a.getY() + height;
449 this.angle = -Math.toRadians(a.getAngleStart());
450 this.extent = -a.getAngleExtent();
451 this.type = a.getArcType();
452 this.t = t;
453
454 if (Math.abs(extent) >= 360.0) {
455 arcCount = 4;
456 k = 4.0 / 3.0 * (Math.sqrt(2.0) - 1.0);
457 step = Math.PI / 2.0;
458 if (extent < 0.0) {
459 step = -step;
460 k = -k;
461 }
462 } else {
463 arcCount = (int)Math.rint(Math.abs(extent) / 90.0);
464 step = Math.toRadians(extent / arcCount);
465 k = 4.0 / 3.0 * (1.0 - Math.cos(step / 2.0))
466 / Math.sin(step / 2.0);
467 }
468
469 lineCount = 0;
470 if (type == Arc2D.CHORD) {
471 lineCount++;
472 } else if (type == Arc2D.PIE) {
473 lineCount += 2;
474 }
475 }
476
477 public int getWindingRule() {
478 return WIND_NON_ZERO;
479 }
480
481 public boolean isDone() {
482 return index > arcCount + lineCount;
483 }
484
485 public void next() {
486 index++;
487 }
488
489 public int currentSegment(double[] coords) {
490 if (isDone()) {
491 // awt.4B=Iterator out of bounds
492 throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
493 }
494 int type;
495 int count;
496 if (index == 0) {
497 type = SEG_MOVETO;
498 count = 1;
499 cos = Math.cos(angle);
500 sin = Math.sin(angle);
501 kx = k * width * sin;
502 ky = k * height * cos;
503 coords[0] = mx = x + cos * width;
504 coords[1] = my = y + sin * height;
505 } else if (index <= arcCount) {
506 type = SEG_CUBICTO;
507 count = 3;
508 coords[0] = mx - kx;
509 coords[1] = my + ky;
510 angle += step;
511 cos = Math.cos(angle);
512 sin = Math.sin(angle);
513 kx = k * width * sin;
514 ky = k * height * cos;
515 coords[4] = mx = x + cos * width;
516 coords[5] = my = y + sin * height;
517 coords[2] = mx + kx;
518 coords[3] = my - ky;
519 } else if (index == arcCount + lineCount) {
520 type = SEG_CLOSE;
521 count = 0;
522 } else {
523 type = SEG_LINETO;
524 count = 1;
525 coords[0] = x;
526 coords[1] = y;
527 }
528 if (t != null) {
529 t.transform(coords, 0, coords, 0, count);
530 }
531 return type;
532 }
533
534 public int currentSegment(float[] coords) {
535 if (isDone()) {
536 // awt.4B=Iterator out of bounds
537 throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
538 }
539 int type;
540 int count;
541 if (index == 0) {
542 type = SEG_MOVETO;
543 count = 1;
544 cos = Math.cos(angle);
545 sin = Math.sin(angle);
546 kx = k * width * sin;
547 ky = k * height * cos;
548 coords[0] = (float)(mx = x + cos * width);
549 coords[1] = (float)(my = y + sin * height);
550 } else if (index <= arcCount) {
551 type = SEG_CUBICTO;
552 count = 3;
553 coords[0] = (float)(mx - kx);
554 coords[1] = (float)(my + ky);
555 angle += step;
556 cos = Math.cos(angle);
557 sin = Math.sin(angle);
558 kx = k * width * sin;
559 ky = k * height * cos;
560 coords[4] = (float)(mx = x + cos * width);
561 coords[5] = (float)(my = y + sin * height);
562 coords[2] = (float)(mx + kx);
563 coords[3] = (float)(my - ky);
564 } else if (index == arcCount + lineCount) {
565 type = SEG_CLOSE;
566 count = 0;
567 } else {
568 type = SEG_LINETO;
569 count = 1;
570 coords[0] = (float)x;
571 coords[1] = (float)y;
572 }
573 if (t != null) {
574 t.transform(coords, 0, coords, 0, count);
575 }
576 return type;
577 }
578
579 }
580
581 /** The closure type of the arc. */
582 private int type;
583
584 /**
585 * Instantiates a new arc2D.
586 *
587 * @param type the closure type.
588 */
589 protected Arc2D(int type) {
590 setArcType(type);
591 }
592
593 /**
594 * Takes the double-valued data and creates the corresponding Rectangle2D
595 * object with values either of type float or of type double depending on
596 * whether this Arc2D instance is of type Float or Double.
597 *
598 * @param x the x coordinate of the upper left corner of the bounding rectangle.
599 * @param y the y coordinate of the upper left corner of the bounding rectangle.
600 * @param width the width of the bounding rectangle.
601 * @param height the height of the bounding rectangle.
602 *
603 * @return the corresponding Rectangle2D object.
604 */
605 protected abstract Rectangle2D makeBounds(double x, double y, double width, double height);
606
607 /**
608 * Gets the start angle.
609 *
610 * @return the start angle.
611 */
612 public abstract double getAngleStart();
613
614 /**
615 * Gets the width angle.
616 *
617 * @return the width angle.
618 */
619 public abstract double getAngleExtent();
620
621 /**
622 * Sets the start angle.
623 *
624 * @param start the new start angle.
625 */
626 public abstract void setAngleStart(double start);
627
628 /**
629 * Sets the width angle.
630 *
631 * @param extent the new width angle.
632 */
633 public abstract void setAngleExtent(double extent);
634
635 /**
636 * Sets the data values that define the arc.
637 *
638 * @param x the x coordinate of the upper left corner of the rectangle that
639 * contains the arc.
640 * @param y the y coordinate of the upper left corner of the rectangle that
641 * contains the arc.
642 * @param width the width of the rectangle that
643 * contains the arc.
644 * @param height the height of the rectangle that
645 * contains the arc.
646 * @param start the start angle of the arc in degrees.
647 * @param extent the width angle of the arc in degrees.
648 * @param type the type of the new Arc2D, either {@link Arc2D#OPEN},
649 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
650 */
651 public abstract void setArc(double x, double y, double width,
652 double height, double start, double extent, int type);
653
654 /**
655 * Gets the arc type, either {@link Arc2D#OPEN},
656 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
657 *
658 * @return the arc type.
659 */
660 public int getArcType() {
661 return type;
662 }
663
664 /**
665 * Sets the arc type, either {@link Arc2D#OPEN},
666 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
667 *
668 * @param type the new arc type.
669 */
670 public void setArcType(int type) {
671 if (type != OPEN && type != CHORD && type != PIE) {
672 // awt.205=Invalid type of Arc: {0}
673 throw new IllegalArgumentException(Messages.getString("awt.205", type)); //$NON-NLS-1$
674 }
675 this.type = type;
676 }
677
678 /**
679 * Gets the start point of the arc as a Point2D.
680 *
681 * @return the start point of the curved arc segment.
682 */
683 public Point2D getStartPoint() {
684 double a = Math.toRadians(getAngleStart());
685 return new Point2D.Double(
686 getX() + (1.0 + Math.cos(a)) * getWidth() / 2.0,
687 getY() + (1.0 - Math.sin(a)) * getHeight() / 2.0);
688 }
689
690 /**
691 * Gets the end point of the arc as a Point2D.
692 *
693 * @return the end point of the curved arc segment.
694 */
695 public Point2D getEndPoint() {
696 double a = Math.toRadians(getAngleStart() + getAngleExtent());
697 return new Point2D.Double(
698 getX() + (1.0 + Math.cos(a)) * getWidth() / 2.0,
699 getY() + (1.0 - Math.sin(a)) * getHeight() / 2.0);
700 }
701
702 public Rectangle2D getBounds2D() {
703 if (isEmpty()) {
704 return makeBounds(getX(), getY(), getWidth(), getHeight());
705 }
706 double rx1 = getX();
707 double ry1 = getY();
708 double rx2 = rx1 + getWidth();
709 double ry2 = ry1 + getHeight();
710
711 Point2D p1 = getStartPoint();
712 Point2D p2 = getEndPoint();
713
714 double bx1 = containsAngle(180.0) ? rx1 : Math.min(p1.getX(), p2.getX());
715 double by1 = containsAngle(90.0) ? ry1 : Math.min(p1.getY(), p2.getY());
716 double bx2 = containsAngle(0.0) ? rx2 : Math.max(p1.getX(), p2.getX());
717 double by2 = containsAngle(270.0) ? ry2 : Math.max(p1.getY(), p2.getY());
718
719 if (type == PIE) {
720 double cx = getCenterX();
721 double cy = getCenterY();
722 bx1 = Math.min(bx1, cx);
723 by1 = Math.min(by1, cy);
724 bx2 = Math.max(bx2, cx);
725 by2 = Math.max(by2, cy);
726 }
727 return makeBounds(bx1, by1, bx2 - bx1, by2 - by1);
728 }
729
730 @Override
731 public void setFrame(double x, double y, double width, double height) {
732 setArc(x, y, width, height, getAngleStart(), getAngleExtent(), type);
733 }
734
735 /**
736 * Sets the data that defines the arc.
737 *
738 * @param point the upper left corner of the bounding rectangle.
739 * @param size the size of the bounding rectangle.
740 * @param start the start angle of the arc in degrees.
741 * @param extent the angle witdth of the arc in degrees.
742 * @param type the closure type, either {@link Arc2D#OPEN},
743 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
744 */
745 public void setArc(Point2D point, Dimension2D size, double start, double extent, int type) {
746 setArc(point.getX(), point.getY(), size.getWidth(), size.getHeight(), start, extent, type);
747 }
748
749 /**
750 * Sets the data that defines the arc.
751 *
752 * @param rect the arc's bounding rectangle.
753 * @param start the start angle of the arc in degrees.
754 * @param extent the angle witdth of the arc in degrees.
755 * @param type the closure type, either {@link Arc2D#OPEN},
756 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
757 */
758 public void setArc(Rectangle2D rect, double start, double extent, int type) {
759 setArc(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(), start, extent, type);
760 }
761
762 /**
763 * Sets the data that defines the arc by copying it from another Arc2D.
764 *
765 * @param arc the arc whose data is copied into this arc.
766 */
767 public void setArc(Arc2D arc) {
768 setArc(arc.getX(), arc.getY(), arc.getWidth(), arc.getHeight(), arc
769 .getAngleStart(), arc.getAngleExtent(), arc.getArcType());
770 }
771
772 /**
773 * Sets the data for a circular arc by giving its center and radius.
774 *
775 * @param x the x coordinate of the center of the circle.
776 * @param y the y coordinate of the center of the circle.
777 * @param radius the radius of the circle.
778 * @param start the start angle of the arc in degrees.
779 * @param extent the angle witdth of the arc in degrees.
780 * @param type the closure type, either {@link Arc2D#OPEN},
781 * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}.
782 */
783 public void setArcByCenter(double x, double y, double radius, double start, double extent, int type) {
784 setArc(x - radius, y - radius, radius * 2.0, radius * 2.0, start, extent, type);
785 }
786
787 /**
788 * Sets the arc data for a circular arc based on two tangent lines
789 * and the radius. The two tangent lines are the lines from p1
790 * to p2 and from p2 to p3, which determine a unique circle
791 * with the given radius. The start and end points of the arc
792 * are the points where the circle touches the two lines, and
793 * the arc itself is the shorter of the two circle segments
794 * determined by the two points (in other words, it is the
795 * piece of the circle that is closer to the lines' intersection
796 * point p2 and forms a concave shape with the segments from p1 to p2
797 * and from p2 to p3).
798 *
799 * @param p1 a point which determines one of the two tanget lines (with p2).
800 * @param p2 the point of intersection of the two tangent lines.
801 * @param p3 a point which determines one of the two tanget lines (with p2).
802 * @param radius the radius of the circular arc.
803 */
804 public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double radius) {
805 // Used simple geometric calculations of arc center, radius and angles by tangents
806 double a1 = -Math.atan2(p1.getY() - p2.getY(), p1.getX() - p2.getX());
807 double a2 = -Math.atan2(p3.getY() - p2.getY(), p3.getX() - p2.getX());
808 double am = (a1 + a2) / 2.0;
809 double ah = a1 - am;
810 double d = radius / Math.abs(Math.sin(ah));
811 double x = p2.getX() + d * Math.cos(am);
812 double y = p2.getY() - d * Math.sin(am);
813 ah = ah >= 0.0 ? Math.PI * 1.5 - ah : Math.PI * 0.5 - ah;
814 a1 = getNormAngle(Math.toDegrees(am - ah));
815 a2 = getNormAngle(Math.toDegrees(am + ah));
816 double delta = a2 - a1;
817 if (delta <= 0.0) {
818 delta += 360.0;
819 }
820 setArcByCenter(x, y, radius, a1, delta, type);
821 }
822
823 /**
824 * Sets a new start angle to be the angle given by the the vector
825 * from the current center point to the specified point.
826 *
827 * @param point the point that determines the new start angle.
828 */
829 public void setAngleStart(Point2D point) {
830 double angle = Math.atan2(point.getY() - getCenterY(), point.getX() - getCenterX());
831 setAngleStart(getNormAngle(-Math.toDegrees(angle)));
832 }
833
834 /**
835 * Sets the angles in terms of vectors from the current arc center
836 * to the points (x1, y1) and (x2, y2). The start angle is given
837 * by the vector from the current center to the point (x1, y1) and
838 * the end angle is given by the vector from the center to the point
839 * (x2, y2).
840 *
841 * @param x1 the x coordinate of the point whose vector from the center
842 * point determines the new start angle of the arc.
843 * @param y1 the y coordinate of the point whose vector from the center
844 * point determines the new start angle of the arc.
845 * @param x2 the x coordinate of the point whose vector from the center
846 * point determines the new end angle of the arc.
847 * @param y2 the y coordinate of the point whose vector from the center
848 * point determines the new end angle of the arc.
849 */
850 public void setAngles(double x1, double y1, double x2, double y2) {
851 double cx = getCenterX();
852 double cy = getCenterY();
853 double a1 = getNormAngle(-Math.toDegrees(Math.atan2(y1 - cy, x1 - cx)));
854 double a2 = getNormAngle(-Math.toDegrees(Math.atan2(y2 - cy, x2 - cx)));
855 a2 -= a1;
856 if (a2 <= 0.0) {
857 a2 += 360.0;
858 }
859 setAngleStart(a1);
860 setAngleExtent(a2);
861 }
862
863 /**
864 * Sets the angles in terms of vectors from the current arc center
865 * to the points p1 and p2. The start angle is given
866 * by the vector from the current center to the point p1 and
867 * the end angle is given by the vector from the center to the point
868 * p2.
869 *
870 * @param p1 the point whose vector from the center
871 * point determines the new start angle of the arc.
872 * @param p2 the point whose vector from the center
873 * point determines the new end angle of the arc.
874 */
875 public void setAngles(Point2D p1, Point2D p2) {
876 setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY());
877 }
878
879 /**
880 * Normalizes the angle by removing extra winding (past 360 degrees)
881 * and placing it in the positive degree range.
882 *
883 * @param angle - the source angle in degrees
884 *
885 * @return an angle between 0 and 360 degrees which corresponds
886 * to the same direction vector as the source angle.
887 */
888 double getNormAngle(double angle) {
889 double n = Math.floor(angle / 360.0);
890 return angle - n * 360.0;
891 }
892
893 /**
894 * Determines whether the given angle is contained in the span of the arc.
895 *
896 * @param angle the angle to test in degrees.
897 *
898 * @return true, if the given angle is between the start angle and
899 * the end angle of the arc.
900 */
901 public boolean containsAngle(double angle) {
902 double extent = getAngleExtent();
903 if (extent >= 360.0) {
904 return true;
905 }
906 angle = getNormAngle(angle);
907 double a1 = getNormAngle(getAngleStart());
908 double a2 = a1 + extent;
909 if (a2 > 360.0) {
910 return angle >= a1 || angle <= a2 - 360.0;
911 }
912 if (a2 < 0.0) {
913 return angle >= a2 + 360.0 || angle <= a1;
914 }
915 return extent > 0.0 ? a1 <= angle && angle <= a2 : a2 <= angle
916 && angle <= a1;
917 }
918
919 public boolean contains(double px, double py) {
920 // Normalize point
921 double nx = (px - getX()) / getWidth() - 0.5;
922 double ny = (py - getY()) / getHeight() - 0.5;
923
924 if ((nx * nx + ny * ny) > 0.25) {
925 return false;
926 }
927
928 double extent = getAngleExtent();
929 double absExtent = Math.abs(extent);
930 if (absExtent >= 360.0) {
931 return true;
932 }
933
934 boolean containsAngle = containsAngle(Math.toDegrees(-Math
935 .atan2(ny, nx)));
936 if (type == PIE) {
937 return containsAngle;
938 }
939 if (absExtent <= 180.0 && !containsAngle) {
940 return false;
941 }
942
943 Line2D l = new Line2D.Double(getStartPoint(), getEndPoint());
944 int ccw1 = l.relativeCCW(px, py);
945 int ccw2 = l.relativeCCW(getCenterX(), getCenterY());
946 return ccw1 == 0 || ccw2 == 0
947 || ((ccw1 + ccw2) == 0 ^ absExtent > 180.0);
948 }
949
950 public boolean contains(double rx, double ry, double rw, double rh) {
951
952 if (!(contains(rx, ry) && contains(rx + rw, ry)
953 && contains(rx + rw, ry + rh) && contains(rx, ry + rh))) {
954 return false;
955 }
956
957 double absExtent = Math.abs(getAngleExtent());
958 if (type != PIE || absExtent <= 180.0 || absExtent >= 360.0) {
959 return true;
960 }
961
962 Rectangle2D r = new Rectangle2D.Double(rx, ry, rw, rh);
963
964 double cx = getCenterX();
965 double cy = getCenterY();
966 if (r.contains(cx, cy)) {
967 return false;
968 }
969
970 Point2D p1 = getStartPoint();
971 Point2D p2 = getEndPoint();
972
973 return !r.intersectsLine(cx, cy, p1.getX(), p1.getY())
974 && !r.intersectsLine(cx, cy, p2.getX(), p2.getY());
975 }
976
977 @Override
978 public boolean contains(Rectangle2D rect) {
979 return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
980 }
981
982 public boolean intersects(double rx, double ry, double rw, double rh) {
983
984 if (isEmpty() || rw <= 0.0 || rh <= 0.0) {
985 return false;
986 }
987
988 // Check: Does arc contain rectangle's points
989 if (contains(rx, ry) || contains(rx + rw, ry) || contains(rx, ry + rh)
990 || contains(rx + rw, ry + rh)) {
991 return true;
992 }
993
994 double cx = getCenterX();
995 double cy = getCenterY();
996 Point2D p1 = getStartPoint();
997 Point2D p2 = getEndPoint();
998 Rectangle2D r = new Rectangle2D.Double(rx, ry, rw, rh);
999
1000 // Check: Does rectangle contain arc's points
1001 if (r.contains(p1) || r.contains(p2) || (type == PIE && r.contains(cx, cy))) {
1002 return true;
1003 }
1004
1005 if (type == PIE) {
1006 if (r.intersectsLine(p1.getX(), p1.getY(), cx, cy) ||
1007 r.intersectsLine(p2.getX(), p2.getY(), cx, cy))
1008 {
1009 return true;
1010 }
1011 } else {
1012 if (r.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY())) {
1013 return true;
1014 }
1015 }
1016
1017 // Nearest rectangle point
1018 double nx = cx < rx ? rx : (cx > rx + rw ? rx + rw : cx);
1019 double ny = cy < ry ? ry : (cy > ry + rh ? ry + rh : cy);
1020 return contains(nx, ny);
1021 }
1022
1023 public PathIterator getPathIterator(AffineTransform at) {
1024 return new Iterator(this, at);
1025 }
1026
1027}
1028