1. /*
  2. * @(#)Arc2D.java 1.27 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt.geom;
  8. /**
  9. * <CODE>Arc2D</CODE> is the abstract superclass for all objects that
  10. * store a 2D arc defined by a bounding rectangle,
  11. * start angle, angular extent (length of the arc), and a closure type
  12. * (<CODE>OPEN</CODE>, <CODE>CHORD</CODE>, or <CODE>PIE</CODE>).
  13. * <p>
  14. * The bounding rectangle defines the outer boundary of the full ellipse
  15. * of which this arc is a partial section.
  16. * The angles are specified relative to the non-square extents of the
  17. * bounding rectangle such that 45 degrees always falls on the line from
  18. * the center of the ellipse to the upper right corner of the bounding
  19. * rectangle.
  20. * As a result, if the bounding rectangle is noticeably longer along one
  21. * axis than the other, the angles to the start and end of the arc segment
  22. * will be skewed farther along the longer axis of the bounds.
  23. * <p>
  24. * The actual storage representation of the coordinates is left to
  25. * the subclass.
  26. *
  27. * @version 10 Feb 1997
  28. * @author Jim Graham
  29. */
  30. public abstract class Arc2D extends RectangularShape {
  31. /**
  32. * The closure type for an open arc with no path segments
  33. * connecting the two ends of the arc segment.
  34. */
  35. public final static int OPEN = 0;
  36. /**
  37. * The closure type for an arc closed by drawing a straight
  38. * line segment from the start of the arc segment to the end of the
  39. * arc segment.
  40. */
  41. public final static int CHORD = 1;
  42. /**
  43. * The closure type for an arc closed by drawing straight line
  44. * segments from the start of the arc segment to the center
  45. * of the full ellipse and from that point to the end of the arc segment.
  46. */
  47. public final static int PIE = 2;
  48. /**
  49. * This class defines an arc specified in float precision.
  50. */
  51. public static class Float extends Arc2D {
  52. /**
  53. * The x coordinate of the upper left corner of the arc.
  54. */
  55. public float x;
  56. /**
  57. * The y coordinate of the upper left corner of the arc.
  58. */
  59. public float y;
  60. /**
  61. * The overall width of the full ellipse of which this arc is
  62. * a partial section (not considering the
  63. * angular extents).
  64. */
  65. public float width;
  66. /**
  67. * The overall height of the full ellipse of which this arc is
  68. * a partial section (not considering the
  69. * angular extents).
  70. */
  71. public float height;
  72. /**
  73. * The starting angle of the arc in degrees.
  74. */
  75. public float start;
  76. /**
  77. * The angular extent of the arc in degrees.
  78. */
  79. public float extent;
  80. /**
  81. * Constructs a new OPEN arc, initialized to location (0, 0),
  82. * size (0, 0), angular extents (start = 0, extent = 0).
  83. */
  84. public Float() {
  85. super(OPEN);
  86. }
  87. /**
  88. * Constructs a new arc, initialized to location (0, 0),
  89. * size (0, 0), angular extents (start = 0, extent = 0), and
  90. * the specified closure type.
  91. *
  92. * @param type The closure type for the arc:
  93. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  94. */
  95. public Float(int type) {
  96. super(type);
  97. }
  98. /**
  99. * Constructs a new arc, initialized to the specified location,
  100. * size, angular extents, and closure type.
  101. *
  102. * @param x, y The coordinates of the upper left corner of
  103. * the arc. (Specified in float precision.)
  104. * @param w The overall width of the full ellipse of which
  105. * this arc is a partial section. (Specified in float precision.)
  106. * @param h The overall height of the full ellipse of which this
  107. * arc is a partial section. (Specified in float precision.)
  108. * @param start The starting angle of the arc in degrees.
  109. * (Specified in float precision.)
  110. * @param extent The angular extent of the arc in degrees.
  111. * (Specified in float precision.)
  112. * @param type The closure type for the arc:
  113. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  114. */
  115. public Float(float x, float y, float w, float h,
  116. float start, float extent, int type) {
  117. super(type);
  118. this.x = x;
  119. this.y = y;
  120. this.width = w;
  121. this.height = h;
  122. this.start = start;
  123. this.extent = extent;
  124. }
  125. /**
  126. * Constructs a new arc, initialized to the specified location,
  127. * size, angular extents, and closure type.
  128. *
  129. * @param ellipseBounds The bounding rectangle that defines the
  130. * outer boundary of the full ellipse of which this arc is a
  131. * partial section.
  132. * @param start The starting angle of the arc in degrees.
  133. * (Specified in float precision.)
  134. * @param extent The angular extent of the arc in degrees.
  135. * (Specified in float precision.)
  136. * @param type The closure type for the arc:
  137. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  138. */
  139. public Float(Rectangle2D ellipseBounds,
  140. float start, float extent, int type) {
  141. super(type);
  142. this.x = (float) ellipseBounds.getX();
  143. this.y = (float) ellipseBounds.getY();
  144. this.width = (float) ellipseBounds.getWidth();
  145. this.height = (float) ellipseBounds.getHeight();
  146. this.start = start;
  147. this.extent = extent;
  148. }
  149. /**
  150. * Returns the x coordinate of the upper left corner of the arc.
  151. *
  152. * @return The x coordinate of arc's upper left coordinate in
  153. * double precision.
  154. */
  155. public double getX() {
  156. return (double) x;
  157. }
  158. /**
  159. * Returns the y coordinate of the upper left corner of the arc.
  160. *
  161. * @return The y coordinate of arc's upper left coordinate in
  162. * double precision.
  163. */
  164. public double getY() {
  165. return (double) y;
  166. }
  167. /**
  168. * Returns the width of the ellipse of which this arc is
  169. * a partial section.
  170. *
  171. * @return A double value that represents the width of the full
  172. * ellipse of which this arc is a partial section.
  173. */
  174. public double getWidth() {
  175. return (double) width;
  176. }
  177. /**
  178. * Returns the height of the ellipse of which this arc is
  179. * a partial section.
  180. *
  181. * @return A double value that represents the height of the full
  182. * ellipse of which this arc is a partial section.
  183. */
  184. public double getHeight() {
  185. return (double) height;
  186. }
  187. /**
  188. * Returns the starting angle of the arc.
  189. *
  190. * @return A double value that represents the starting angle of
  191. * the arc in degrees.
  192. * @see #setAngleStart
  193. */
  194. public double getAngleStart() {
  195. return (double) start;
  196. }
  197. /**
  198. * Returns the angular extent of the arc.
  199. *
  200. * @return A double value that represents the angular extent of
  201. * the arc in degrees.
  202. * @see #setAngleExtent
  203. */
  204. public double getAngleExtent() {
  205. return (double) extent;
  206. }
  207. /**
  208. * Determines whether the arc is empty.
  209. *
  210. * @return <CODE>true</CODE> if the arc is empty, <CODE>false</CODE>
  211. * if it is not.
  212. */
  213. public boolean isEmpty() {
  214. return (width <= 0.0 || height <= 0.0);
  215. }
  216. /**
  217. * Sets the location, size, angular extents, and closure type of
  218. * this arc to the specified double values.
  219. *
  220. * @param x, y The coordinates of the upper left corner of
  221. * the arc.
  222. * @param w The overall width of the full ellipse of which this
  223. * arc is a partial section.
  224. * @param h The overall height of the full ellipse of which this
  225. * arc is a partial section.
  226. * @param angSt The starting angle of the arc in degrees.
  227. * @param angExt The angular extent of the arc in degrees.
  228. * @param closure The closure type for the arc:
  229. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  230. */
  231. public void setArc(double x, double y, double w, double h,
  232. double angSt, double angExt, int closure) {
  233. this.setArcType(closure);
  234. this.x = (float) x;
  235. this.y = (float) y;
  236. this.width = (float) w;
  237. this.height = (float) h;
  238. this.start = (float) angSt;
  239. this.extent = (float) angExt;
  240. }
  241. /**
  242. * Sets the starting angle of this arc to the specified double
  243. * value.
  244. *
  245. * @param angSt The starting angle of the arc in degrees.
  246. * @see #getAngleStart
  247. */
  248. public void setAngleStart(double angSt) {
  249. this.start = (float) angSt;
  250. }
  251. /**
  252. * Sets the angular extent of this arc to the specified double
  253. * value.
  254. *
  255. * @param angExt The angular extent of the arc in degrees.
  256. * @see #getAngleExtent
  257. */
  258. public void setAngleExtent(double angExt) {
  259. this.extent = (float) angExt;
  260. }
  261. /**
  262. * Return the high-precision bounding box of the arc.
  263. *
  264. * @param x, y The coordinates of the upper left corner
  265. * of the arc.
  266. * @param w The overall width of the full ellipse of which
  267. * this arc is a partial section.
  268. * @param h The overall height of the full ellipse of which
  269. * this arc is a partial section.
  270. *
  271. * @return The bounding box as a <CODE>Rectangle2D</CODE> object.
  272. */
  273. protected Rectangle2D makeBounds(double x, double y,
  274. double w, double h) {
  275. return new Rectangle2D.Float((float) x, (float) y,
  276. (float) w, (float) h);
  277. }
  278. }
  279. /**
  280. * This class defines an arc specified in double precision.
  281. */
  282. public static class Double extends Arc2D {
  283. /**
  284. * The x coordinate of the upper left corner of the arc.
  285. */
  286. public double x;
  287. /**
  288. * The y coordinate of the upper left corner of the arc.
  289. */
  290. public double y;
  291. /**
  292. * The overall width of the full ellipse (not considering the
  293. * angular extents).
  294. */
  295. public double width;
  296. /**
  297. * The overall height of the full ellipse (not considering the
  298. * angular extents).
  299. */
  300. public double height;
  301. /**
  302. * The starting angle of the arc in degrees.
  303. */
  304. public double start;
  305. /**
  306. * The angular extent of the arc in degrees.
  307. */
  308. public double extent;
  309. /**
  310. * Constructs a new OPEN arc, initialized to location (0, 0),
  311. * size (0, 0), angular extents (start = 0, extent = 0).
  312. */
  313. public Double() {
  314. super(OPEN);
  315. }
  316. /**
  317. * Constructs a new arc, initialized to location (0, 0),
  318. * size (0, 0), angular extents (start = 0, extent = 0), and
  319. * the specified closure type.
  320. *
  321. * @param type The closure type for the arc:
  322. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  323. */
  324. public Double(int type) {
  325. super(type);
  326. }
  327. /**
  328. * Constructs a new arc, initialized to the specified location,
  329. * size, angular extents, and closure type.
  330. *
  331. * @param x, y The coordinates of the upper left corner
  332. * of the arc. (Specified in double precision.)
  333. * @param w The overall width of the full ellipse of which this
  334. * arc is a partial section. (Specified in double precision.)
  335. * @param h The overall height of the full ellipse of which this
  336. * arc is a partial section. (Specified in double precision.)
  337. * @param start The starting angle of the arc in degrees.
  338. * (Specified in double precision.)
  339. * @param extent The angular extent of the arc in degrees.
  340. * (Specified in double precision.)
  341. * @param type The closure type for the arc:
  342. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  343. */
  344. public Double(double x, double y, double w, double h,
  345. double start, double extent, int type) {
  346. super(type);
  347. this.x = x;
  348. this.y = y;
  349. this.width = w;
  350. this.height = h;
  351. this.start = start;
  352. this.extent = extent;
  353. }
  354. /**
  355. * Constructs a new arc, initialized to the specified location,
  356. * size, angular extents, and closure type.
  357. *
  358. * @param ellipseBounds The bounding rectangle that defines the
  359. * outer boundary of the full ellipse of which this arc is a
  360. * partial section.
  361. * @param start The starting angle of the arc in degrees.
  362. * (Specified in double precision.)
  363. * @param extent The angular extent of the arc in degrees.
  364. * (Specified in double precision.)
  365. * @param type The closure type for the arc:
  366. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  367. */
  368. public Double(Rectangle2D ellipseBounds,
  369. double start, double extent, int type) {
  370. super(type);
  371. this.x = ellipseBounds.getX();
  372. this.y = ellipseBounds.getY();
  373. this.width = ellipseBounds.getWidth();
  374. this.height = ellipseBounds.getHeight();
  375. this.start = start;
  376. this.extent = extent;
  377. }
  378. /**
  379. * Returns the x coordinate of the upper left corner of the arc.
  380. *
  381. * @return The x coordinate of arc's upper left coordinate in
  382. * double precision.
  383. */
  384. public double getX() {
  385. return x;
  386. }
  387. /**
  388. * Returns the y coordinate of the upper left corner of the arc.
  389. *
  390. * @return The y coordinate of arc's upper left coordinate in
  391. * double precision.
  392. */
  393. public double getY() {
  394. return y;
  395. }
  396. /**
  397. * Returns the width of the ellipse of which this arc is
  398. * a partial section.
  399. *
  400. * @return A double value that represents the width of the full
  401. * ellipse of which this arc is a partial section.
  402. */
  403. public double getWidth() {
  404. return width;
  405. }
  406. /**
  407. * Returns the height of the ellipse of which this arc is
  408. * a partial section.
  409. *
  410. * @return A double value that represents the height of the full
  411. * ellipse of which this arc is a partial section.
  412. */
  413. public double getHeight() {
  414. return height;
  415. }
  416. /**
  417. * Returns the starting angle of the arc.
  418. *
  419. * @return a double value that represents the starting angle
  420. * of the arc in degrees.
  421. * @see #setAngleStart
  422. */
  423. public double getAngleStart() {
  424. return start;
  425. }
  426. /**
  427. * Returns the angular extent of the arc.
  428. *
  429. * @return A double value that represents the angular extent of
  430. * the arc in degrees.
  431. * @see #setAngleExtent
  432. */
  433. public double getAngleExtent() {
  434. return extent;
  435. }
  436. /**
  437. * Determines whether the arc is empty.
  438. *
  439. * @return <CODE>true</CODE> if the arc is empty, <CODE>false</CODE>
  440. * if it not.
  441. */
  442. public boolean isEmpty() {
  443. return (width <= 0.0 || height <= 0.0);
  444. }
  445. /**
  446. * Sets the location, size, angular extents, and closure type of
  447. * this arc to the specified double values.
  448. *
  449. * @param x, y The coordinates of the upper left corner
  450. * of the arc.
  451. * @param w The overall width of the full ellipse of which
  452. * this arc is a partial section.
  453. * @param h The overall height of the full ellipse of which
  454. * this arc is a partial section.
  455. * @param angSt The starting angle of the arc in degrees.
  456. * @param angExt The angular extent of the arc in degrees.
  457. * @param closure The closure type for the arc:
  458. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  459. */
  460. public void setArc(double x, double y, double w, double h,
  461. double angSt, double angExt, int closure) {
  462. this.setArcType(closure);
  463. this.x = x;
  464. this.y = y;
  465. this.width = w;
  466. this.height = h;
  467. this.start = angSt;
  468. this.extent = angExt;
  469. }
  470. /**
  471. * Sets the starting angle of this arc to the specified double
  472. * value.
  473. *
  474. * @param angSt The starting angle of the arc in degrees.
  475. * @see #getAngleStart
  476. */
  477. public void setAngleStart(double angSt) {
  478. this.start = angSt;
  479. }
  480. /**
  481. * Sets the angular extent of this arc to the specified double
  482. * value.
  483. *
  484. * @param angExt The angular extent of the arc in degrees.
  485. * @see #getAngleExtent
  486. */
  487. public void setAngleExtent(double angExt) {
  488. this.extent = angExt;
  489. }
  490. /**
  491. * Returns the high-precision bounding box of the arc.
  492. *
  493. * @param x, y The coordinates of the upper left corner
  494. * of the arc.
  495. * @param w The overall width of the full ellipse of which
  496. * this arc is a partial section.
  497. * @param h The overall height of the full ellipse of which
  498. * this arc is a partial section.
  499. *
  500. * @return The bounding box as a <CODE>Rectangle2D</CODE> object.
  501. */
  502. protected Rectangle2D makeBounds(double x, double y,
  503. double w, double h) {
  504. return new Rectangle2D.Double(x, y, w, h);
  505. }
  506. }
  507. private int type;
  508. /**
  509. * This is an abstract class that cannot be instantiated directly.
  510. * Type-specific implementation subclasses are available for
  511. * instantiation and provide a number of formats for storing
  512. * the information necessary to satisfy the various accessor
  513. * methods below.
  514. *
  515. * @param type The closure type of this arc:
  516. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  517. * @see java.awt.geom.Arc2D.Float
  518. * @see java.awt.geom.Arc2D.Double
  519. */
  520. protected Arc2D(int type) {
  521. setArcType(type);
  522. }
  523. /**
  524. * Returns the starting angle of the arc.
  525. *
  526. * @return A double value that represents the starting angle
  527. * of the arc in degrees.
  528. * @see #setAngleStart
  529. */
  530. public abstract double getAngleStart();
  531. /**
  532. * Returns the angular extent of the arc.
  533. *
  534. * @return A double value that represents the angular extent
  535. * of the arc in degrees.
  536. * @see #setAngleExtent
  537. */
  538. public abstract double getAngleExtent();
  539. /**
  540. * Returns the arc closure type of the arc: {@link #OPEN OPEN},
  541. * {@link #CHORD CHORD}, or {@link #PIE PIE}.
  542. * @return One of the integer constant closure types defined
  543. * in this class.
  544. * @see #setArcType
  545. */
  546. public int getArcType() {
  547. return type;
  548. }
  549. /**
  550. * Returns the starting point of the arc. This point is the
  551. * intersection of the ray from the center defined by the
  552. * starting angle and the elliptical boundary of the arc.
  553. *
  554. * @return A <CODE>Point2D</CODE> object representing the
  555. * x,y coordinates of the starting point of the arc.
  556. */
  557. public Point2D getStartPoint() {
  558. double angle = Math.toRadians(-getAngleStart());
  559. double x = getX() + (Math.cos(angle) * 0.5 + 0.5) * getWidth();
  560. double y = getY() + (Math.sin(angle) * 0.5 + 0.5) * getHeight();
  561. return new Point2D.Double(x, y);
  562. }
  563. /**
  564. * Returns the ending point of the arc. This point is the
  565. * intersection of the ray from the center defined by the
  566. * starting angle plus the angular extent of the arc and the
  567. * elliptical boundary of the arc.
  568. *
  569. * @return A <CODE>Point2D</CODE> object representing the
  570. * x,y coordinates of the ending point of the arc.
  571. */
  572. public Point2D getEndPoint() {
  573. double angle = Math.toRadians(-getAngleStart() - getAngleExtent());
  574. double x = getX() + (Math.cos(angle) * 0.5 + 0.5) * getWidth();
  575. double y = getY() + (Math.sin(angle) * 0.5 + 0.5) * getHeight();
  576. return new Point2D.Double(x, y);
  577. }
  578. /**
  579. * Sets the location, size, angular extents, and closure type of
  580. * this arc to the specified double values.
  581. *
  582. * @param x, y The coordinates of the upper left corner of
  583. * the arc.
  584. * @param w The overall width of the full ellipse of which
  585. * this arc is a partial section.
  586. * @param h The overall height of the full ellipse of which
  587. * this arc is a partial section.
  588. * @param angSt The starting angle of the arc in degrees.
  589. * @param angExt The angular extent of the arc in degrees.
  590. * @param closure The closure type for the arc:
  591. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  592. */
  593. public abstract void setArc(double x, double y, double w, double h,
  594. double angSt, double angExt, int closure);
  595. /**
  596. * Sets the location, size, angular extents, and closure type of
  597. * this arc to the specified values.
  598. *
  599. * @param loc The <CODE>Point2D</CODE> representing the coordinates of
  600. * the upper left corner of the arc.
  601. * @param size The <CODE>Dimension2D</CODE> representing the width
  602. * and height of the full ellipse of which this arc is
  603. * a partial section.
  604. * @param angSt The starting angle of the arc in degrees.
  605. * (Specified in double precision.)
  606. * @param angExt The angular extent of the arc in degrees.
  607. * (Specified in double precision.)
  608. * @param closure The closure type for the arc:
  609. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  610. */
  611. public void setArc(Point2D loc, Dimension2D size,
  612. double angSt, double angExt, int closure) {
  613. setArc(loc.getX(), loc.getY(), size.getWidth(), size.getHeight(),
  614. angSt, angExt, closure);
  615. }
  616. /**
  617. * Sets the location, size, angular extents, and closure type of
  618. * this arc to the specified values.
  619. *
  620. * @param rect The bounding rectangle that defines the
  621. * outer boundary of the full ellipse of which this arc is a
  622. * partial section.
  623. * @param angSt The starting angle of the arc in degrees.
  624. * (Specified in double precision.)
  625. * @param angExt The angular extent of the arc in degrees.
  626. * (Specified in double precision.)
  627. * @param closure The closure type for the arc:
  628. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  629. */
  630. public void setArc(Rectangle2D rect, double angSt, double angExt,
  631. int closure) {
  632. setArc(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(),
  633. angSt, angExt, closure);
  634. }
  635. /**
  636. * Sets this arc to be the same as the specified arc.
  637. *
  638. * @param a The <CODE>Arc2D</CODE> to use to set the arc's values.
  639. */
  640. public void setArc(Arc2D a) {
  641. setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(),
  642. a.getAngleStart(), a.getAngleExtent(), a.type);
  643. }
  644. /**
  645. * Sets the position, bounds, angular extents, and closure type of
  646. * this arc to the specified values. The arc is defined by a center
  647. * point and a radius rather than a bounding box for the full ellipse.
  648. *
  649. * @param x, y The coordinates of the center of the arc.
  650. * (Specified in double precision.)
  651. * @param radius The radius of the arc. (Specified in double precision.)
  652. * @param angSt The starting angle of the arc in degrees.
  653. * (Specified in double precision.)
  654. * @param angExt The angular extent of the arc in degrees.
  655. * (Specified in double precision.)
  656. * @param closure The closure type for the arc:
  657. * {@link #OPEN OPEN}, {@link #CHORD CHORD}, or {@link #PIE PIE}.
  658. */
  659. public void setArcByCenter(double x, double y, double radius,
  660. double angSt, double angExt, int closure) {
  661. setArc(x - radius, y - radius, radius * 2.0, radius * 2.0,
  662. angSt, angExt, closure);
  663. }
  664. /**
  665. * Sets the position, bounds, and angular extents of this arc to the
  666. * specified value. The starting angle of the arc is tangent to the
  667. * line specified by points (p1, p2), the ending angle is tangent to
  668. * the line specified by points (p2, p3), and the arc has the
  669. * specified radius.
  670. *
  671. * @param p1 The first point that defines the arc. The starting
  672. * angle of the arc is tangent to the line specified by points (p1, p2).
  673. * @param p2 The second point that defines the arc. The starting
  674. * angle of the arc is tangent to the line specified by points (p1, p2).
  675. * The ending angle of the arc is tangent to the line specified by
  676. * points (p2, p3).
  677. * @param p3 The third point that defines the arc. The ending angle
  678. * of the arc is tangent to the line specified by points (p2, p3).
  679. * @param radius The radius of the arc. (Specified in double precision.)
  680. */
  681. public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3,
  682. double radius) {
  683. double ang1 = Math.atan2(p1.getY() - p2.getY(),
  684. p1.getX() - p2.getX());
  685. double ang2 = Math.atan2(p3.getY() - p2.getY(),
  686. p3.getX() - p2.getX());
  687. double diff = ang2 - ang1;
  688. if (diff > Math.PI) {
  689. ang2 -= Math.PI * 2.0;
  690. } else if (diff < -Math.PI) {
  691. ang2 += Math.PI * 2.0;
  692. }
  693. double bisect = (ang1 + ang2) / 2.0;
  694. double theta = Math.abs(ang2 - bisect);
  695. double dist = radius / Math.sin(theta);
  696. double x = p2.getX() + dist * Math.cos(bisect);
  697. double y = p2.getY() + dist * Math.sin(bisect);
  698. // REMIND: This needs some work...
  699. if (ang1 < ang2) {
  700. ang1 -= Math.PI / 2.0;
  701. ang2 += Math.PI / 2.0;
  702. } else {
  703. ang1 += Math.PI / 2.0;
  704. ang2 -= Math.PI / 2.0;
  705. }
  706. ang1 = Math.toDegrees(-ang1);
  707. ang2 = Math.toDegrees(-ang2);
  708. diff = ang2 - ang1;
  709. if (diff < 0) {
  710. diff += 360;
  711. } else {
  712. diff -= 360;
  713. }
  714. setArcByCenter(x, y, radius, ang1, diff, type);
  715. }
  716. /**
  717. * Sets the starting angle of this arc to the specified double
  718. * value.
  719. *
  720. * @param angSt The starting angle of the arc in degrees.
  721. * @see #getAngleStart
  722. */
  723. public abstract void setAngleStart(double angSt);
  724. /**
  725. * Sets the angular extent of this arc to the specified double
  726. * value.
  727. *
  728. * @param angExt The angular extent of the arc in degrees.
  729. * @see #getAngleExtent
  730. */
  731. public abstract void setAngleExtent(double angExt);
  732. /**
  733. * Sets the starting angle of this arc to the angle that the
  734. * specified point defines relative to the center of this arc.
  735. * The angular extent of the arc will remain the same.
  736. *
  737. * @param p The <CODE>Point2D</CODE> that defines the starting angle.
  738. * @see #getAngleStart
  739. */
  740. public void setAngleStart(Point2D p) {
  741. // Bias the dx and dy by the height and width of the oval.
  742. double dx = getHeight() * (p.getX() - getCenterX());
  743. double dy = getWidth() * (p.getY() - getCenterY());
  744. setAngleStart(-Math.toDegrees(Math.atan2(dy, dx)));
  745. }
  746. /**
  747. * Sets the starting angle and angular extent of this arc using two
  748. * sets of coordinates. The first set of coordinates is used to
  749. * determine the angle of the starting point relative to the arc's
  750. * center. The second set of coordinates is used to determine the
  751. * angle of the end point relative to the arc's center.
  752. * The arc will always be non-empty and extend counterclockwise
  753. * from the first point around to the second point.
  754. *
  755. * @param x1, y1 The coordinates of the arc's starting point.
  756. * @param x2, y2 The coordinates of the arc's ending point.
  757. */
  758. public void setAngles(double x1, double y1, double x2, double y2) {
  759. double x = getCenterX();
  760. double y = getCenterY();
  761. double w = getWidth();
  762. double h = getHeight();
  763. // Note: reversing the Y equations negates the angle to adjust
  764. // for the upside down coordinate system.
  765. // Also we should bias atans by the height and width of the oval.
  766. double ang1 = Math.atan2(w * (y - y1), h * (x1 - x));
  767. double ang2 = Math.atan2(w * (y - y2), h * (x2 - x));
  768. ang2 -= ang1;
  769. if (ang2 <= 0.0) {
  770. ang2 += Math.PI * 2.0;
  771. }
  772. setAngleStart(Math.toDegrees(ang1));
  773. setAngleExtent(Math.toDegrees(ang2));
  774. }
  775. /**
  776. * Sets the starting angle and angular extent of this arc using
  777. * two points. The first point is used to determine the angle of
  778. * the starting point relative to the arc's center.
  779. * The second point is used to determine the angle of the end point
  780. * relative to the arc's center.
  781. * The arc will always be non-empty and extend counterclockwise
  782. * from the first point around to the second point.
  783. *
  784. * @param p1 The <CODE>Point2D</CODE> that defines the arc's
  785. * starting point.
  786. * @param p2 The <CODE>Point2D</CODE> that defines the arc's
  787. * ending point.
  788. */
  789. public void setAngles(Point2D p1, Point2D p2) {
  790. setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY());
  791. }
  792. /**
  793. * Sets the closure type of this arc to the specified value:
  794. * <CODE>OPEN</CODE>, <CODE>CHORD</CODE>, or <CODE>PIE</CODE>.
  795. *
  796. * @param type The integer constant that represents the closure
  797. * type of this arc: {@link #OPEN}, {@link #CHORD}, or
  798. * {@link #PIE}.
  799. *
  800. * @throws IllegalArgumentException if <code>type</code> is not
  801. * 0, 1, or 2.+
  802. * @see #getArcType
  803. */
  804. public void setArcType(int type) {
  805. if (type < OPEN || type > PIE) {
  806. throw new IllegalArgumentException("invalid type for Arc: "+type);
  807. }
  808. this.type = type;
  809. }
  810. /**
  811. * Sets the location and size of the outer bounds of this arc
  812. * to the specified values.
  813. *
  814. * @param x, y The coordinates of the upper left corner of the
  815. * arc's bounding box. (Specified in double precision.)
  816. * @param w The width of the arc's bounding box. (Specified in
  817. * double precision.)
  818. * @param h The height of the arc's bounding box. (Specified in
  819. * double precision.)
  820. */
  821. public void setFrame(double x, double y, double w, double h) {
  822. setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type);
  823. }
  824. /**
  825. * Returns the high-precision bounding box of the arc. The bounding
  826. * box contains only the part of this <code>Arc2D</code> that is
  827. * in between the starting and ending angles and contains the pie
  828. * wedge, if this <code>Arc2D</code> has a <code>PIE</code> closure type.
  829. * <p>
  830. * This method differs from the
  831. * {@link RectangularShape#getBounds() getBounds} in that the
  832. * <code>getBounds</code> method only returns the bounds of the
  833. * enclosing ellipse of this <code>Arc2D</code> without considering
  834. * the starting and ending angles of this <code>Arc2D</code>.
  835. *
  836. * @return the <CODE>Rectangle2D</CODE> that represents the arc's
  837. * bounding box.
  838. */
  839. public Rectangle2D getBounds2D() {
  840. if (isEmpty()) {
  841. return makeBounds(getX(), getY(), getWidth(), getHeight());
  842. }
  843. double x1, y1, x2, y2;
  844. if (getArcType() == PIE) {
  845. x1 = y1 = x2 = y2 = 0.0;
  846. } else {
  847. x1 = y1 = 1.0;
  848. x2 = y2 = -1.0;
  849. }
  850. double angle = 0.0;
  851. for (int i = 0; i < 6; i++) {
  852. if (i < 4) {
  853. // 0-3 are the four quadrants
  854. angle += 90.0;
  855. if (!containsAngle(angle)) {
  856. continue;
  857. }
  858. } else if (i == 4) {
  859. // 4 is start angle
  860. angle = getAngleStart();
  861. } else {
  862. // 5 is end angle
  863. angle += getAngleExtent();
  864. }
  865. double rads = Math.toRadians(-angle);
  866. double xe = Math.cos(rads);
  867. double ye = Math.sin(rads);
  868. x1 = Math.min(x1, xe);
  869. y1 = Math.min(y1, ye);
  870. x2 = Math.max(x2, xe);
  871. y2 = Math.max(y2, ye);
  872. }
  873. double w = getWidth();
  874. double h = getHeight();
  875. x2 = (x2 - x1) * 0.5 * w;
  876. y2 = (y2 - y1) * 0.5 * h;
  877. x1 = getX() + (x1 * 0.5 + 0.5) * w;
  878. y1 = getY() + (y1 * 0.5 + 0.5) * h;
  879. return makeBounds(x1, y1, x2, y2);
  880. }
  881. /**
  882. * Constructs a <code>Rectangle2D</code> of the appropriate precision
  883. * to hold the parameters calculated to be the bounding box
  884. * of this arc.
  885. *
  886. * @param x, y The coordinates of the upper left corner of the
  887. * bounding box. (Specified in double precision.)
  888. * @param w The width of the bounding box. (Specified in
  889. * double precision.)
  890. * @param h The height of the bounding box. (Specified in
  891. * double precision.)
  892. * @return a <code>Rectangle2D</code> that is the bounding box
  893. * of this arc.
  894. */
  895. protected abstract Rectangle2D makeBounds(double x, double y,
  896. double w, double h);
  897. /*
  898. * Normalizes the specified angle into the range -180 to 180.
  899. */
  900. static double normalizeDegrees(double angle) {
  901. if (angle > 180.0) {
  902. if (angle <= (180.0 + 360.0)) {
  903. angle = angle - 360.0;
  904. } else {
  905. angle = Math.IEEEremainder(angle, 360.0);
  906. // IEEEremainder can return -180 here for some input values...
  907. if (angle == -180.0) {
  908. angle = 180.0;
  909. }
  910. }
  911. } else if (angle <= -180.0) {
  912. if (angle > (-180.0 - 360.0)) {
  913. angle = angle + 360.0;
  914. } else {
  915. angle = Math.IEEEremainder(angle, 360.0);
  916. // IEEEremainder can return -180 here for some input values...
  917. if (angle == -180.0) {
  918. angle = 180.0;
  919. }
  920. }
  921. }
  922. return angle;
  923. }
  924. /**
  925. * Determines whether or not the specified angle is within the
  926. * angular extents of the arc.
  927. *
  928. * @param angle The angle to test. (Specified in double precision.)
  929. *
  930. * @return <CODE>true</CODE> if the arc contains the angle,
  931. * <CODE>false</CODE> if the arc doesn't contain the angle.
  932. */
  933. public boolean containsAngle(double angle) {
  934. double angExt = getAngleExtent();
  935. boolean backwards = (angExt < 0.0);
  936. if (backwards) {
  937. angExt = -angExt;
  938. }
  939. if (angExt >= 360.0) {
  940. return true;
  941. }
  942. angle = normalizeDegrees(angle) - normalizeDegrees(getAngleStart());
  943. if (backwards) {
  944. angle = -angle;
  945. }
  946. if (angle < 0.0) {
  947. angle += 360.0;
  948. }
  949. return (angle >= 0.0) && (angle < angExt);
  950. }
  951. /**
  952. * Determines whether or not the specified point is inside the boundary
  953. * of the arc.
  954. *
  955. * @param x, y The coordinates of the point to test. (Specified in
  956. * double precision.)
  957. *
  958. * @return <CODE>true</CODE> if the point lies within the bound of
  959. * the arc, <CODE>false</CODE> if the point lies outside of the
  960. * arc's bounds.
  961. */
  962. public boolean contains(double x, double y) {
  963. // Normalize the coordinates compared to the ellipse
  964. // having a center at 0,0 and a radius of 0.5.
  965. double ellw = getWidth();
  966. if (ellw <= 0.0) {
  967. return false;
  968. }
  969. double normx = (x - getX()) / ellw - 0.5;
  970. double ellh = getHeight();
  971. if (ellh <= 0.0) {
  972. return false;
  973. }
  974. double normy = (y - getY()) / ellh - 0.5;
  975. double distSq = (normx * normx + normy * normy);
  976. if (distSq >= 0.25) {
  977. return false;
  978. }
  979. double angExt = Math.abs(getAngleExtent());
  980. if (angExt >= 360.0) {
  981. return true;
  982. }
  983. boolean inarc = containsAngle(-Math.toDegrees(Math.atan2(normy,
  984. normx)));
  985. if (type == PIE) {
  986. return inarc;
  987. }
  988. // CHORD and OPEN behave the same way
  989. if (inarc) {
  990. if (angExt >= 180.0) {
  991. return true;
  992. }
  993. // point must be outside the "pie triangle"
  994. } else {
  995. if (angExt <= 180.0) {
  996. return false;
  997. }
  998. // point must be inside the "pie triangle"
  999. }
  1000. // The point is inside the pie triangle iff it is on the same
  1001. // side of the line connecting the ends of the arc as the center.
  1002. double angle = Math.toRadians(-getAngleStart());
  1003. double x1 = Math.cos(angle);
  1004. double y1 = Math.sin(angle);
  1005. angle += Math.toRadians(-getAngleExtent());
  1006. double x2 = Math.cos(angle);
  1007. double y2 = Math.sin(angle);
  1008. boolean inside = (Line2D.relativeCCW(x1, y1, x2, y2, 2*normx, 2*normy) *
  1009. Line2D.relativeCCW(x1, y1, x2, y2, 0, 0) >= 0);
  1010. return inarc ? !inside : inside;
  1011. }
  1012. /**
  1013. * Determines whether or not the interior of the arc intersects
  1014. * the interior of the specified rectangle.
  1015. *
  1016. * @param x, y The coordinates of the rectangle's upper left corner.
  1017. * (Specified in double precision.)
  1018. * @param w The width of the rectangle. (Specified in double precision.)
  1019. * @param h The height of the rectangle. (Specified in double precision.)
  1020. *
  1021. * @return <CODE>true</CODE> if the arc intersects the rectangle,
  1022. * <CODE>false</CODE> if the arc doesn't intersect the rectangle.
  1023. */
  1024. public boolean intersects(double x, double y, double w, double h) {
  1025. double aw = getWidth();
  1026. double ah = getHeight();
  1027. if ( w <= 0 || h <= 0 || aw <= 0 || ah <= 0 ) {
  1028. return false;
  1029. }
  1030. double ext = getAngleExtent();
  1031. if (ext == 0) {
  1032. return false;
  1033. }
  1034. double ax = getX();
  1035. double ay = getY();
  1036. double axw = ax + aw;
  1037. double ayh = ay + ah;
  1038. double xw = x + w;
  1039. double yh = y + h;
  1040. // check bbox
  1041. if (x >= axw || y >= ayh || xw <= ax || yh <= ay) {
  1042. return false;
  1043. }
  1044. // extract necessary data
  1045. double axc = getCenterX();
  1046. double ayc = getCenterY();
  1047. Point2D sp = getStartPoint();
  1048. Point2D ep = getEndPoint();
  1049. double sx = sp.getX();
  1050. double sy = sp.getY();
  1051. double ex = ep.getX();
  1052. double ey = ep.getY();
  1053. /*
  1054. * Try to catch rectangles that intersect arc in areas
  1055. * outside of rectagle with left top corner coordinates
  1056. * (min(center x, start point x, end point x),
  1057. * min(center y, start point y, end point y))
  1058. * and rigth bottom corner coordinates
  1059. * (max(center x, start point x, end point x),
  1060. * max(center y, start point y, end point y)).
  1061. * So we'll check axis segments outside of rectangle above.
  1062. */
  1063. if (ayc >= y && ayc <= yh) { // 0 and 180
  1064. if ((sx < xw && ex < xw && axc < xw &&
  1065. axw > x && containsAngle(0)) ||
  1066. (sx > x && ex > x && axc > x &&
  1067. ax < xw && containsAngle(180))) {
  1068. return true;
  1069. }
  1070. }
  1071. if (axc >= x && axc <= xw) { // 90 and 270
  1072. if ((sy > y && ey > y && ayc > y &&
  1073. ay < yh && containsAngle(90)) ||
  1074. (sy < yh && ey < yh && ayc < yh &&
  1075. ayh > y && containsAngle(270))) {
  1076. return true;
  1077. }
  1078. }
  1079. /*
  1080. * For PIE we should check intersection with pie slices;
  1081. * also we should do the same for arcs with extent is greater
  1082. * than 180, because we should cover case of rectangle, which
  1083. * situated between center of arc and chord, but does not
  1084. * intersect the chord.
  1085. */
  1086. Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
  1087. if (type == PIE || Math.abs(ext) > 180) {
  1088. // for PIE: try to find intersections with pie slices
  1089. if (rect.intersectsLine(axc, ayc, sx, sy) ||
  1090. rect.intersectsLine(axc, ayc, ex, ey)) {
  1091. return true;
  1092. }
  1093. } else {
  1094. // for CHORD and OPEN: try to find intersections with chord
  1095. if (rect.intersectsLine(sx, sy, ex, ey)) {
  1096. return true;
  1097. }
  1098. }
  1099. // finally check the rectangle corners inside the arc
  1100. if (contains(x, y) || contains(x + w, y) ||
  1101. contains(x, y + h) || contains(x + w, y + h)) {
  1102. return true;
  1103. }
  1104. return false;
  1105. }
  1106. /**
  1107. * Determine whether or not the interior of the arc entirely contains
  1108. * the specified rectangle.
  1109. *
  1110. * @param x, y The coordinates of the rectangle's upper left corner.
  1111. * (Specified in double precision.)
  1112. * @param w The width of the rectangle. (Specified in double precision.)
  1113. * @param h The height of the rectangle. (Specified in double precision.)
  1114. *
  1115. * @return <CODE>true</CODE> if the arc contains the rectangle,
  1116. * <CODE>false</CODE> if the arc doesn't contain the rectangle.
  1117. */
  1118. public boolean contains(double x, double y, double w, double h) {
  1119. return contains(x, y, w, h, null);
  1120. }
  1121. /**
  1122. * Determine whether or not the interior of the arc entirely contains
  1123. * the specified rectangle.
  1124. *
  1125. * @param r The <CODE>Rectangle2D</CODE> to test.
  1126. *
  1127. * @return <CODE>true</CODE> if the arc contains the rectangle,
  1128. * <CODE>false</CODE> if the arc doesn't contain the rectangle.
  1129. */
  1130. public boolean contains(Rectangle2D r) {
  1131. return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight(), r);
  1132. }
  1133. private boolean contains(double x, double y, double w, double h,
  1134. Rectangle2D origrect) {
  1135. if (!(contains(x, y) &&
  1136. contains(x + w, y) &&
  1137. contains(x, y + h) &&
  1138. contains(x + w, y + h))) {
  1139. return false;
  1140. }
  1141. // If the shape is convex then we have done all the testing
  1142. // we need. Only PIE arcs can be concave and then only if
  1143. // the angular extents are greater than 180 degrees.
  1144. if (type != PIE || Math.abs(getAngleExtent()) <= 180.0) {
  1145. return true;
  1146. }
  1147. // For a PIE shape we have an additional test for the case where
  1148. // the angular extents are greater than 180 degrees and all four
  1149. // rectangular corners are inside the shape but one of the
  1150. // rectangle edges spans across the "missing wedge" of the arc.
  1151. // We can test for this case by checking if the rectangle intersects
  1152. // either of the pie angle segments.
  1153. if (origrect == null) {
  1154. origrect = new Rectangle2D.Double(x, y, w, h);
  1155. }
  1156. double halfW = getWidth() / 2.0;
  1157. double halfH = getHeight() / 2.0;
  1158. double xc = getX() + halfW;
  1159. double yc = getY() + halfH;
  1160. double angle = Math.toRadians(-getAngleStart());
  1161. double xe = xc + halfW * Math.cos(angle);
  1162. double ye = yc + halfH * Math.sin(angle);
  1163. if (origrect.intersectsLine(xc, yc, xe, ye)) {
  1164. return false;
  1165. }
  1166. angle += Math.toRadians(-getAngleExtent());
  1167. xe = xc + halfW * Math.cos(angle);
  1168. ye = yc + halfH * Math.sin(angle);
  1169. return !origrect.intersectsLine(xc, yc, xe, ye);
  1170. }
  1171. /**
  1172. * Returns an iteration object that defines the boundary of the
  1173. * arc.
  1174. * This iterator is multithread safe.
  1175. * <code>Arc2D</code> guarantees that
  1176. * modifications to the geometry of the arc
  1177. * do not affect any iterations of that geometry that
  1178. * are already in process.
  1179. *
  1180. * @param at an optional <CODE>AffineTransform</CODE> to be applied
  1181. * to the coordinates as they are returned in the iteration, or null
  1182. * if the untransformed coordinates are desired.
  1183. *
  1184. * @return A <CODE>PathIterator</CODE> that defines the arc's boundary.
  1185. */
  1186. public PathIterator getPathIterator(AffineTransform at) {
  1187. return new ArcIterator(this, at);
  1188. }
  1189. }