1. /*
  2. * @(#)RoundRectangle2D.java 1.17 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt.geom;
  8. /**
  9. * The <code>RoundRectangle2D</code> class defines a rectangle with
  10. * rounded corners defined by a location (x, y), a
  11. * dimension (w x h), and the width and height of an arc
  12. * with which to round the corners.
  13. * <p>
  14. * This class is the abstract superclass for all objects that
  15. * store a 2D rounded rectangle.
  16. * The actual storage representation of the coordinates is left to
  17. * the subclass.
  18. *
  19. * @version 1.17, 01/23/03
  20. * @author Jim Graham
  21. */
  22. public abstract class RoundRectangle2D extends RectangularShape {
  23. /**
  24. * The <code>Float</code> class defines a rectangle with rounded
  25. * corners all specified in <code>float</code> coordinates.
  26. */
  27. public static class Float extends RoundRectangle2D {
  28. /**
  29. * The X coordinate of this <code>RoundRectangle2D</code>.
  30. */
  31. public float x;
  32. /**
  33. * The Y coordinate of this <code>RoundRectangle2D</code>.
  34. */
  35. public float y;
  36. /**
  37. * The width of this <code>RoundRectangle2D</code>.
  38. */
  39. public float width;
  40. /**
  41. * The height of this <code>RoundRectangle2D</code>.
  42. */
  43. public float height;
  44. /**
  45. * The width of the arc that rounds off the corners.
  46. */
  47. public float arcwidth;
  48. /**
  49. * The height of the arc that rounds off the corners.
  50. */
  51. public float archeight;
  52. /**
  53. * Constructs a new <code>RoundRectangle2D</code>, initialized to
  54. * location (0.0, 0), size (0.0, 0.0), and corner arcs
  55. * of radius 0.0.
  56. */
  57. public Float() {
  58. }
  59. /**
  60. * Constructs and initializes a <code>RoundRectangle2D</code>
  61. * from the specified coordinates.
  62. * @param x, y the coordinates to which to set the newly
  63. * constructed <code>RoundRectangle2D</code>
  64. * @param w the width to which to set the newly
  65. * constructed <code>RoundRectangle2D</code>
  66. * @param h the height to which to set the newly
  67. * constructed <code>RoundRectangle2D</code>
  68. * @param arcw the width of the arc to use to round off the
  69. * corners of the newly constructed <code>RoundRectangle2D</code>
  70. * @param arch the height of the arc to use to round off the
  71. * corners of the newly constructed <code>RoundRectangle2D</code>
  72. */
  73. public Float(float x, float y, float w, float h,
  74. float arcw, float arch) {
  75. setRoundRect(x, y, w, h, arcw, arch);
  76. }
  77. /**
  78. * Returns the X coordinate of this <code>RoundRectangle2D</code>
  79. * in <code>double</code> precision.
  80. * @return the X coordinate of this <code>RoundRectangle2D</code>.
  81. */
  82. public double getX() {
  83. return (double) x;
  84. }
  85. /**
  86. * Returns the Y coordinate of this <code>RoundRectangle2D</code>
  87. * in <code>double</code> precision.
  88. * @return the Y coordinate of this <code>RoundRectangle2D</code>.
  89. */
  90. public double getY() {
  91. return (double) y;
  92. }
  93. /**
  94. * Returns the width of this <code>RoundRectangle2D</code>
  95. * in <code>double</code> precision.
  96. * @return the width of this <code>RoundRectangle2D</code>.
  97. */
  98. public double getWidth() {
  99. return (double) width;
  100. }
  101. /**
  102. * Returns the height of this <code>RoundRectangle2D</code>
  103. * in <code>double</code> precision.
  104. * @return the height of this <code>RoundRectangle2D</code>.
  105. */
  106. public double getHeight() {
  107. return (double) height;
  108. }
  109. /**
  110. * Returns the width of the arc that rounds off the corners.
  111. * @return the width of the arc that rounds off the corners
  112. * of this <code>RoundRectangle2D</code>.
  113. */
  114. public double getArcWidth() {
  115. return (double) arcwidth;
  116. }
  117. /**
  118. * Returns the height of the arc that rounds off the corners.
  119. * @return the height of the arc that rounds off the corners
  120. * of this <code>RoundRectangle2D</code>.
  121. */
  122. public double getArcHeight() {
  123. return (double) archeight;
  124. }
  125. /**
  126. * Determines whether or not this <code>RoundRectangle2D</code>
  127. * is empty.
  128. * @return <code>true</code> if this <code>RoundRectangle2D</code>
  129. * is empty; <code>false</code> othwerwise.
  130. */
  131. public boolean isEmpty() {
  132. return (width <= 0.0f) || (height <= 0.0f);
  133. }
  134. /**
  135. * Sets the location, size, and arc radii of this
  136. * <code>RoundRectangle2D</code> to the
  137. * specified <code>float</code> values.
  138. * @param x, y the coordinates to which to set the
  139. * location of this <code>RoundRectangle2D</code>
  140. * @param w the width to which to set this
  141. * <code>RoundRectangle2D</code>
  142. * @param h the height to which to set this
  143. * <code>RoundRectangle2D</code>
  144. * @param arcw the width to which to set the arc of this
  145. * <code>RoundRectangle2D</code>
  146. * @param arch the height to which to set the arc of this
  147. * <code>RoundRectangle2D</code>
  148. */
  149. public void setRoundRect(float x, float y, float w, float h,
  150. float arcw, float arch) {
  151. this.x = x;
  152. this.y = y;
  153. this.width = w;
  154. this.height = h;
  155. this.arcwidth = arcw;
  156. this.archeight = arch;
  157. }
  158. /**
  159. * Sets the location, size, and arc radii of this
  160. * <code>RoundRectangle2D</code> to the
  161. * specified <code>double</code> values.
  162. * @param x, y the coordinates to which to set the
  163. * location of this <code>RoundRectangle2D</code>
  164. * @param w the width to which to set this
  165. * <code>RoundRectangle2D</code>
  166. * @param h the height to which to set this
  167. * <code>RoundRectangle2D</code>
  168. * @param arcw the width to which to set the arc of this
  169. * <code>RoundRectangle2D</code>
  170. * @param arch the height to which to set the arc of this
  171. * <code>RoundRectangle2D</code>
  172. */
  173. public void setRoundRect(double x, double y, double w, double h,
  174. double arcw, double arch) {
  175. this.x = (float) x;
  176. this.y = (float) y;
  177. this.width = (float) w;
  178. this.height = (float) h;
  179. this.arcwidth = (float) arcw;
  180. this.archeight = (float) arch;
  181. }
  182. /**
  183. * Sets this <code>RoundRectangle2D</code> to be the same as the
  184. * specified <code>RoundRectangle2D</code>.
  185. * @param rr the specified <code>RoundRectangle2D</code>
  186. */
  187. public void setRoundRect(RoundRectangle2D rr) {
  188. this.x = (float) rr.getX();
  189. this.y = (float) rr.getY();
  190. this.width = (float) rr.getWidth();
  191. this.height = (float) rr.getHeight();
  192. this.arcwidth = (float) rr.getArcWidth();
  193. this.archeight = (float) rr.getArcHeight();
  194. }
  195. /**
  196. * Returns the high precision bounding box of this
  197. * <code>RoundRectangle2D</code>.
  198. * @return a {@link Rectangle2D} that is the bounding
  199. * box of this <code>RoundRectangle2D</code>.
  200. */
  201. public Rectangle2D getBounds2D() {
  202. return new Rectangle2D.Float(x, y, width, height);
  203. }
  204. }
  205. /**
  206. * The <code>Double</code> class defines a rectangle with rounded
  207. * corners all specified in <code>double</code> coordinates.
  208. */
  209. public static class Double extends RoundRectangle2D {
  210. /**
  211. * The X coordinate of this <code>RoundRectangle2D</code>.
  212. */
  213. public double x;
  214. /**
  215. * The Y coordinate of this <code>RoundRectangle2D</code>.
  216. */
  217. public double y;
  218. /**
  219. * The width of this <code>RoundRectangle2D</code>.
  220. */
  221. public double width;
  222. /**
  223. * The height of this <code>RoundRectangle2D</code>.
  224. */
  225. public double height;
  226. /**
  227. * The width of the arc that rounds off the corners.
  228. */
  229. public double arcwidth;
  230. /**
  231. * The height of the arc that rounds off the corners.
  232. */
  233. public double archeight;
  234. /**
  235. * Constructs a new <code>RoundRectangle2D</code>, initialized to
  236. * location (0.0, 0), size (0.0, 0.0), and corner arcs
  237. * of radius 0.0.
  238. */
  239. public Double() {
  240. }
  241. /**
  242. * Constructs and initializes a <code>RoundRectangle2D</code>
  243. * from the specified coordinates.
  244. * @param x, y the coordinates to which to set the newly
  245. * constructed <code>RoundRectangle2D</code>
  246. * @param w the width to which to set the newly
  247. * constructed <code>RoundRectangle2D</code>
  248. * @param h the height to which to set the newly
  249. * constructed <code>RoundRectangle2D</code>
  250. * @param arcw the width of the arc to use to round off the
  251. * corners of the newly constructed <code>RoundRectangle2D</code>
  252. * @param arch the height of the arc to use to round off the
  253. * corners of the newly constructed <code>RoundRectangle2D</code>
  254. */
  255. public Double(double x, double y, double w, double h,
  256. double arcw, double arch) {
  257. setRoundRect(x, y, w, h, arcw, arch);
  258. }
  259. /**
  260. * Returns the X coordinate of this <code>RoundRectangle2D</code>
  261. * in <code>double</code> precision.
  262. * @return the X coordinate of this <code>RoundRectangle2D</code>.
  263. */
  264. public double getX() {
  265. return x;
  266. }
  267. /**
  268. * Returns the Y coordinate of this <code>RoundRectangle2D</code>
  269. * in <code>double</code> precision.
  270. * @return the Y coordinate of this <code>RoundRectangle2D</code>.
  271. */
  272. public double getY() {
  273. return y;
  274. }
  275. /**
  276. * Returns the width of this <code>RoundRectangle2D</code>
  277. * in <code>double</code> precision.
  278. * @return the width of this <code>RoundRectangle2D</code>.
  279. */
  280. public double getWidth() {
  281. return width;
  282. }
  283. /**
  284. * Returns the height of this <code>RoundRectangle2D</code>
  285. * in <code>double</code> precision.
  286. * @return the height of this <code>RoundRectangle2D</code>.
  287. */
  288. public double getHeight() {
  289. return height;
  290. }
  291. /**
  292. * Returns the width of the arc that rounds off the corners.
  293. * @return the width of the arc that rounds off the corners
  294. * of this <code>RoundRectangle2D</code>.
  295. */
  296. public double getArcWidth() {
  297. return arcwidth;
  298. }
  299. /**
  300. * Returns the height of the arc that rounds off the corners.
  301. * @return the height of the arc that rounds off the corners
  302. * of this <code>RoundRectangle2D</code>.
  303. */
  304. public double getArcHeight() {
  305. return archeight;
  306. }
  307. /**
  308. * Determines whether or not this <code>RoundRectangle2D</code>
  309. * is empty.
  310. * @return <code>true</code> if this <code>RoundRectangle2D</code>
  311. * is empty; <code>false</code> othwerwise.
  312. */
  313. public boolean isEmpty() {
  314. return (width <= 0.0f) || (height <= 0.0f);
  315. }
  316. /**
  317. * Sets the location, size, and arc radii of this
  318. * <code>RoundRectangle2D</code> to the
  319. * specified <code>double</code> values.
  320. * @param x, y the coordinates to which to set the
  321. * location of this <code>RoundRectangle2D</code>
  322. * @param w the width to which to set this
  323. * <code>RoundRectangle2D</code>
  324. * @param h the height to which to set this
  325. * <code>RoundRectangle2D</code>
  326. * @param arcw the width to which to set the arc of this
  327. * <code>RoundRectangle2D</code>
  328. * @param arch the height to which to set the arc of this
  329. * <code>RoundRectangle2D</code>
  330. */
  331. public void setRoundRect(double x, double y, double w, double h,
  332. double arcw, double arch) {
  333. this.x = x;
  334. this.y = y;
  335. this.width = w;
  336. this.height = h;
  337. this.arcwidth = arcw;
  338. this.archeight = arch;
  339. }
  340. /**
  341. * Sets this <code>RoundRectangle2D</code> to be the same as the
  342. * specified <code>RoundRectangle2D</code>.
  343. * @param rr the specified <code>RoundRectangle2D</code>
  344. */
  345. public void setRoundRect(RoundRectangle2D rr) {
  346. this.x = rr.getX();
  347. this.y = rr.getY();
  348. this.width = rr.getWidth();
  349. this.height = rr.getHeight();
  350. this.arcwidth = rr.getArcWidth();
  351. this.archeight = rr.getArcHeight();
  352. }
  353. /**
  354. * Returns the high precision bounding box of this
  355. * <code>RoundRectangle2D</code>.
  356. * @return a {@link Rectangle2D} that is the bounding
  357. * box of this <code>RoundRectangle2D</code>.
  358. */
  359. public Rectangle2D getBounds2D() {
  360. return new Rectangle2D.Double(x, y, width, height);
  361. }
  362. }
  363. /**
  364. * This is an abstract class that cannot be instantiated directly.
  365. * Type-specific implementation subclasses are available for
  366. * instantiation and provide a number of formats for storing
  367. * the information necessary to satisfy the various accessor
  368. * methods below.
  369. *
  370. * @see java.awt.geom.RoundRectangle2D.Float
  371. * @see java.awt.geom.RoundRectangle2D.Double
  372. */
  373. protected RoundRectangle2D() {
  374. }
  375. /**
  376. * Gets the width of the arc that rounds off the corners.
  377. * @return the width of the arc that rounds off the corners
  378. * of this <code>RoundRectangle2D</code>.
  379. */
  380. public abstract double getArcWidth();
  381. /**
  382. * Gets the height of the arc that rounds off the corners.
  383. * @return the height of the arc that rounds off the corners
  384. * of this <code>RoundRectangle2D</code>.
  385. */
  386. public abstract double getArcHeight();
  387. /**
  388. * Sets the location, size, and corner radii of this
  389. * <code>RoundRectangle2D</code> to the specified
  390. * <code>double</code> values.
  391. * @param x, y the coordinates to which to set the
  392. * location of this <code>RoundRectangle2D</code>
  393. * @param w the width to which to set this
  394. * <code>RoundRectangle2D</code>
  395. * @param h the height to which to set this
  396. * <code>RoundRectangle2D</code>
  397. * @param arcWidth the width to which to set the arc of this
  398. * <code>RoundRectangle2D</code>
  399. * @param arcHeight the height to which to set the arc of this
  400. * <code>RoundRectangle2D</code>
  401. */
  402. public abstract void setRoundRect(double x, double y, double w, double h,
  403. double arcWidth, double arcHeight);
  404. /**
  405. * Sets this <code>RoundRectangle2D</code> to be the same as the
  406. * specified <code>RoundRectangle2D</code>.
  407. * @param rr the specified <code>RoundRectangle2D</code>
  408. */
  409. public void setRoundRect(RoundRectangle2D rr) {
  410. setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
  411. rr.getArcWidth(), rr.getArcHeight());
  412. }
  413. /**
  414. * Sets the location and size of the outer bounds of this
  415. * <code>RoundRectangle2D</code> to the specified rectangular values.
  416. * @param x, y the coordinates to which to set the location
  417. * of this <code>RoundRectangle2D</code>
  418. * @param w the width to which to set this
  419. * <code>RoundRectangle2D</code>
  420. * @param h the height to which to set this
  421. * <code>RoundRectangle2D</code>
  422. */
  423. public void setFrame(double x, double y, double w, double h) {
  424. setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
  425. }
  426. /**
  427. * Tests if the specified coordinates are inside the boundary of
  428. * this <code>RoundRectangle2D</code>.
  429. * @param x, y the coordinates to test
  430. * @return <code>true</code> if the specified coordinates are
  431. * inside the boundary of this <code>RoundRectangle2D</code>
  432. * <code>false</code> otherwise.
  433. */
  434. public boolean contains(double x, double y) {
  435. if (isEmpty()) {
  436. return false;
  437. }
  438. double rrx0 = getX();
  439. double rry0 = getY();
  440. double rrx1 = rrx0 + getWidth();
  441. double rry1 = rry0 + getHeight();
  442. // Check for trivial rejection - point is outside bounding rectangle
  443. if (x < rrx0 || y < rry0 || x >= rrx1 || y >= rry1) {
  444. return false;
  445. }
  446. double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0;
  447. double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0;
  448. // Check which corner point is in and do circular containment
  449. // test - otherwise simple acceptance
  450. if (x >= (rrx0 += aw) && x < (rrx0 = rrx1 - aw)) {
  451. return true;
  452. }
  453. if (y >= (rry0 += ah) && y < (rry0 = rry1 - ah)) {
  454. return true;
  455. }
  456. x = (x - rrx0) / aw;
  457. y = (y - rry0) / ah;
  458. return (x * x + y * y <= 1.0);
  459. }
  460. private int classify(double coord, double left, double right,
  461. double arcsize) {
  462. if (coord < left) {
  463. return 0;
  464. } else if (coord < left + arcsize) {
  465. return 1;
  466. } else if (coord < right - arcsize) {
  467. return 2;
  468. } else if (coord < right) {
  469. return 3;
  470. } else {
  471. return 4;
  472. }
  473. }
  474. /**
  475. * Tests if the interior of this <code>RoundRectangle2D</code>
  476. * intersects the interior of a specified set of rectangular
  477. * coordinates.
  478. * @param x, y the coordinates of the upper left corner
  479. * of the specified set of rectangular coordinates
  480. * @param w the width of the specified set of rectangular
  481. * coordinates
  482. * @param h the height of the specified set of rectangular
  483. * coordinates
  484. * @return <code>true</code> if the interior of this
  485. * <code>RoundRectangle2D</code> intersects the interior of the
  486. * specified set of rectangular coordinates.
  487. */
  488. public boolean intersects(double x, double y, double w, double h) {
  489. if (isEmpty() || w <= 0 || h <= 0) {
  490. return false;
  491. }
  492. double rrx0 = getX();
  493. double rry0 = getY();
  494. double rrx1 = rrx0 + getWidth();
  495. double rry1 = rry0 + getHeight();
  496. // Check for trivial rejection - bounding rectangles do not intersect
  497. if (x + w <= rrx0 || x >= rrx1 || y + h <= rry0 || y >= rry1) {
  498. return false;
  499. }
  500. double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0;
  501. double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0;
  502. int x0class = classify(x, rrx0, rrx1, aw);
  503. int x1class = classify(x + w, rrx0, rrx1, aw);
  504. int y0class = classify(y, rry0, rry1, ah);
  505. int y1class = classify(y + h, rry0, rry1, ah);
  506. // Trivially accept if any point is inside inner rectangle
  507. if (x0class == 2 || x1class == 2 || y0class == 2 || y1class == 2) {
  508. return true;
  509. }
  510. // Trivially accept if either edge spans inner rectangle
  511. if ((x0class < 2 && x1class > 2) || (y0class < 2 && y1class > 2)) {
  512. return true;
  513. }
  514. // Since neither edge spans the center, then one of the corners
  515. // must be in one of the rounded edges. We detect this case if
  516. // a [xy]0class is 3 or a [xy]1class is 1. One of those two cases
  517. // must be true for each direction.
  518. // We now find a "nearest point" to test for being inside a rounded
  519. // corner.
  520. x = (x1class == 1) ? (x = x + w - (rrx0 + aw)) : (x = x - (rrx1 - aw));
  521. y = (y1class == 1) ? (y = y + h - (rry0 + ah)) : (y = y - (rry1 - ah));
  522. x = x / aw;
  523. y = y / ah;
  524. return (x * x + y * y <= 1.0);
  525. }
  526. /**
  527. * Tests if the interior of this <code>RoundRectangle2D</code>
  528. * entirely contains the specified set of rectangular coordinates.
  529. * @param x, y the coordinates of the specified set of
  530. * rectangular coordinates
  531. * @param w the width of the specified set of rectangular
  532. * coordinates
  533. * @param h the height of the specified set of rectangular
  534. * coordinates
  535. * @return <code>true</code> if the interior of this
  536. * <code>RoundRectangle2D</code> entirely contains the specified
  537. * set of rectangular coordinates; <code>false</code> otherwise.
  538. */
  539. public boolean contains(double x, double y, double w, double h) {
  540. if (isEmpty() || w <= 0 || h <= 0) {
  541. return false;
  542. }
  543. return (contains(x, y) &&
  544. contains(x + w, y) &&
  545. contains(x, y + h) &&
  546. contains(x + w, y + h));
  547. }
  548. /**
  549. * Returns an iteration object that defines the boundary of this
  550. * <code>RoundRectangle2D</code>.
  551. * The iterator for this class is multi-threaded safe, which means
  552. * that this <code>RoundRectangle2D</code> class guarantees that
  553. * modifications to the geometry of this <code>RoundRectangle2D</code>
  554. * object do not affect any iterations of that geometry that
  555. * are already in process.
  556. * @param at an optional <code>AffineTransform</code> to be applied to
  557. * the coordinates as they are returned in the iteration, or
  558. * <code>null</code> if untransformed coordinates are desired
  559. * @return the <code>PathIterator</code> object that returns the
  560. * geometry of the outline of this
  561. * <code>RoundRectangle2D</code>, one segment at a time.
  562. */
  563. public PathIterator getPathIterator(AffineTransform at) {
  564. return new RoundRectIterator(this, at);
  565. }
  566. }