1. /*
  2. * @(#)CubicCurve2D.java 1.25 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt.geom;
  11. import java.awt.Shape;
  12. import java.awt.Rectangle;
  13. import java.util.Arrays;
  14. /**
  15. * The <code>CubicCurve2D</code> class defines a cubic parametric curve
  16. * segment in (x,  y) coordinate space.
  17. * <p>
  18. * This class is only the abstract superclass for all objects which
  19. * store a 2D cubic curve segment.
  20. * The actual storage representation of the coordinates is left to
  21. * the subclass.
  22. *
  23. * @version 1.25, 02/02/00
  24. * @author Jim Graham
  25. */
  26. public abstract class CubicCurve2D implements Shape, Cloneable {
  27. /**
  28. * A cubic parametric curve segment specified with float coordinates.
  29. */
  30. public static class Float extends CubicCurve2D {
  31. /**
  32. * The X coordinate of the start point
  33. * of the cubic curve segment.
  34. */
  35. public float x1;
  36. /**
  37. * The Y coordinate of the start point
  38. * of the cubic curve segment.
  39. */
  40. public float y1;
  41. /**
  42. * The X coordinate of the first control point
  43. * of the cubic curve segment.
  44. */
  45. public float ctrlx1;
  46. /**
  47. * The Y coordinate of the first control point
  48. * of the cubic curve segment.
  49. */
  50. public float ctrly1;
  51. /**
  52. * The X coordinate of the second control point
  53. * of the cubic curve segment.
  54. */
  55. public float ctrlx2;
  56. /**
  57. * The Y coordinate of the second control point
  58. * of the cubic curve segment.
  59. */
  60. public float ctrly2;
  61. /**
  62. * The X coordinate of the end point
  63. * of the cubic curve segment.
  64. */
  65. public float x2;
  66. /**
  67. * The Y coordinate of the end point
  68. * of the cubic curve segment.
  69. */
  70. public float y2;
  71. /**
  72. * Constructs and initializes a CubicCurve with coordinates
  73. * (0, 0, 0, 0, 0, 0).
  74. */
  75. public Float() {
  76. }
  77. /**
  78. * Constructs and initializes a <code>CubicCurve2D</code> from
  79. * the specified coordinates.
  80. * @param x1, y1 the first specified coordinates for the start
  81. * point of the resulting <code>CubicCurve2D</code>
  82. * @param ctrlx1, ctrly1 the second specified coordinates for the
  83. * first control point of the resulting
  84. * <code>CubicCurve2D</code>
  85. * @param ctrlx2, ctrly2 the third specified coordinates for the
  86. * second control point of the resulting
  87. * <code>CubicCurve2D</code>
  88. * @param x2, y2 the fourth specified coordinates for the end
  89. * point of the resulting <code>CubicCurve2D</code>
  90. */
  91. public Float(float x1, float y1,
  92. float ctrlx1, float ctrly1,
  93. float ctrlx2, float ctrly2,
  94. float x2, float y2) {
  95. setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
  96. }
  97. /**
  98. * Returns the X coordinate of the start point
  99. * in double precision.
  100. * @return the X coordinate of the start point of the
  101. * <code>CubicCurve2D</code>.
  102. */
  103. public double getX1() {
  104. return (double) x1;
  105. }
  106. /**
  107. * Returns the Y coordinate of the start point
  108. * in double precision.
  109. * @return the Y coordinate of the start point of the
  110. * <code>CubicCurve2D</code>.
  111. */
  112. public double getY1() {
  113. return (double) y1;
  114. }
  115. /**
  116. * Returns the start point.
  117. * @return a {@link Point2D} that is the start point of the
  118. * <code>CubicCurve2D</code>.
  119. */
  120. public Point2D getP1() {
  121. return new Point2D.Float(x1, y1);
  122. }
  123. /**
  124. * Returns the X coordinate of the first control point
  125. * in double precision.
  126. * @return the X coordinate of the first control point of the
  127. * <code>CubicCurve2D</code>.
  128. */
  129. public double getCtrlX1() {
  130. return (double) ctrlx1;
  131. }
  132. /**
  133. * Returns the Y coordinate of the first control point
  134. * in double precision.
  135. * @return the Y coordinate of the first control point of the
  136. * <code>CubicCurve2D</code>.
  137. */
  138. public double getCtrlY1() {
  139. return (double) ctrly1;
  140. }
  141. /**
  142. * Returns the first control point.
  143. * @return a <code>Point2D</code> that is the first control point
  144. * of the <code>CubicCurve2D</code>.
  145. */
  146. public Point2D getCtrlP1() {
  147. return new Point2D.Float(ctrlx1, ctrly1);
  148. }
  149. /**
  150. * Returns the X coordinate of the second control point
  151. * in double precision.
  152. * @return the X coordinate of the second control point of the
  153. * <code>CubicCurve2D</code>.
  154. */
  155. public double getCtrlX2() {
  156. return (double) ctrlx2;
  157. }
  158. /**
  159. * Returns the Y coordinate of the second control point
  160. * in double precision.
  161. * @return the Y coordinate of the second control point of the
  162. * <code>CubicCurve2D</code>.
  163. */
  164. public double getCtrlY2() {
  165. return (double) ctrly2;
  166. }
  167. /**
  168. * Returns the second control point.
  169. * @return a <code>Point2D</code> that is the second control point
  170. * of the <code>CubicCurve2D</code>.
  171. */
  172. public Point2D getCtrlP2() {
  173. return new Point2D.Float(ctrlx2, ctrly2);
  174. }
  175. /**
  176. * Returns the X coordinate of the end point
  177. * in double precision.
  178. * @return the X coordinate of the end point of the
  179. * <code>CubicCurve2D</code>.
  180. */
  181. public double getX2() {
  182. return (double) x2;
  183. }
  184. /**
  185. * Returns the Y coordinate of the end point
  186. * in double precision.
  187. * @return the Y coordinate of the end point of the
  188. * <code>CubicCurve2D</code>.
  189. */
  190. public double getY2() {
  191. return (double) y2;
  192. }
  193. /**
  194. * Returns the end point.
  195. * @return a <code>Point2D</code> that is the end point
  196. * of the <code>CubicCurve2D</code>.
  197. */
  198. public Point2D getP2() {
  199. return new Point2D.Float(x2, y2);
  200. }
  201. /**
  202. * Sets the location of the endpoints and controlpoints
  203. * of this <code>CubicCurve2D</code> to the specified double
  204. * coordinates.
  205. * @param x1, y1 the first specified coordinates used to set the start
  206. * point of this <code>CubicCurve2D</code>
  207. * @param ctrlx1, ctrly1 the second specified coordinates used to set the
  208. * first control point of this <code>CubicCurve2D</code>
  209. * @param ctrlx2, ctrly2 the third specified coordinates used to set the
  210. * second control point of this <code>CubicCurve2D</code>
  211. * @param x2, y2 the fourth specified coordinates used to set the end
  212. * point of this <code>CubicCurve2D</code>
  213. */
  214. public void setCurve(double x1, double y1,
  215. double ctrlx1, double ctrly1,
  216. double ctrlx2, double ctrly2,
  217. double x2, double y2) {
  218. this.x1 = (float) x1;
  219. this.y1 = (float) y1;
  220. this.ctrlx1 = (float) ctrlx1;
  221. this.ctrly1 = (float) ctrly1;
  222. this.ctrlx2 = (float) ctrlx2;
  223. this.ctrly2 = (float) ctrly2;
  224. this.x2 = (float) x2;
  225. this.y2 = (float) y2;
  226. }
  227. /**
  228. * Sets the location of the endpoints and controlpoints
  229. * of this curve to the specified float coordinates.
  230. * @param x1, y1 the first specified coordinates used to set the start
  231. * point of this <code>CubicCurve2D</code>
  232. * @param ctrlx1, ctrly1 the second specified coordinates used to set the
  233. * first control point of this <code>CubicCurve2D</code>
  234. * @param ctrlx2, ctrly2 the third specified coordinates used to set the
  235. * second control point of this <code>CubicCurve2D</code>
  236. * @param x2, y2 the fourth specified coordinates used to set the end
  237. * point of this <code>CubicCurve2D</code>
  238. */
  239. public void setCurve(float x1, float y1,
  240. float ctrlx1, float ctrly1,
  241. float ctrlx2, float ctrly2,
  242. float x2, float y2) {
  243. this.x1 = x1;
  244. this.y1 = y1;
  245. this.ctrlx1 = ctrlx1;
  246. this.ctrly1 = ctrly1;
  247. this.ctrlx2 = ctrlx2;
  248. this.ctrly2 = ctrly2;
  249. this.x2 = x2;
  250. this.y2 = y2;
  251. }
  252. /**
  253. * Returns the bounding box of the shape.
  254. * @return a {@link Rectangle2D} that is the bounding box of the
  255. * shape.
  256. */
  257. public Rectangle2D getBounds2D() {
  258. float left = Math.min(Math.min(x1, x2),
  259. Math.min(ctrlx1, ctrlx2));
  260. float top = Math.min(Math.min(y1, y2),
  261. Math.min(ctrly1, ctrly2));
  262. float right = Math.max(Math.max(x1, x2),
  263. Math.max(ctrlx1, ctrlx2));
  264. float bottom = Math.max(Math.max(y1, y2),
  265. Math.max(ctrly1, ctrly2));
  266. return new Rectangle2D.Float(left, top,
  267. right - left, bottom - top);
  268. }
  269. }
  270. /**
  271. * A cubic parametric curve segment specified with double coordinates.
  272. */
  273. public static class Double extends CubicCurve2D {
  274. /**
  275. * The X coordinate of the start point
  276. * of the cubic curve segment.
  277. */
  278. public double x1;
  279. /**
  280. * The Y coordinate of the start point
  281. * of the cubic curve segment.
  282. */
  283. public double y1;
  284. /**
  285. * The X coordinate of the first control point
  286. * of the cubic curve segment.
  287. */
  288. public double ctrlx1;
  289. /**
  290. * The Y coordinate of the first control point
  291. * of the cubic curve segment.
  292. */
  293. public double ctrly1;
  294. /**
  295. * The X coordinate of the second control point
  296. * of the cubic curve segment.
  297. */
  298. public double ctrlx2;
  299. /**
  300. * The Y coordinate of the second control point
  301. * of the cubic curve segment.
  302. */
  303. public double ctrly2;
  304. /**
  305. * The X coordinate of the end point
  306. * of the cubic curve segment.
  307. */
  308. public double x2;
  309. /**
  310. * The Y coordinate of the end point
  311. * of the cubic curve segment.
  312. */
  313. public double y2;
  314. /**
  315. * Constructs and initializes a CubicCurve with coordinates
  316. * (0, 0, 0, 0, 0, 0).
  317. */
  318. public Double() {
  319. }
  320. /**
  321. * Constructs and initializes a <code>CubicCurve2D</code> from
  322. * the specified coordinates.
  323. * @param x1, y1 the first specified coordinates for the start
  324. * point of the resulting <code>CubicCurve2D</code>
  325. * @param ctrlx1, ctrly1 the second specified coordinates for the
  326. * first control point of the resulting
  327. * <code>CubicCurve2D</code>
  328. * @param ctrlx2, ctrly2 the third specified coordinates for the
  329. * second control point of the resulting
  330. * <code>CubicCurve2D</code>
  331. * @param x2, y2 the fourth specified coordinates for the end
  332. * point of the resulting <code>CubicCurve2D</code>
  333. */
  334. public Double(double x1, double y1,
  335. double ctrlx1, double ctrly1,
  336. double ctrlx2, double ctrly2,
  337. double x2, double y2) {
  338. setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
  339. }
  340. /**
  341. * Returns the X coordinate of the start point
  342. * in double precision.
  343. * @return the X coordinate of the first control point of the
  344. * <code>CubicCurve2D</code>.
  345. */
  346. public double getX1() {
  347. return x1;
  348. }
  349. /**
  350. * Returns the Y coordinate of the start point
  351. * in double precision.
  352. * @return the Y coordinate of the start point of the
  353. * <code>CubicCurve2D</code>.
  354. */
  355. public double getY1() {
  356. return y1;
  357. }
  358. /**
  359. * Returns the start point.
  360. * @return a <code>Point2D</code> that is the start point of the
  361. * <code>CubicCurve2D</code>.
  362. */
  363. public Point2D getP1() {
  364. return new Point2D.Double(x1, y1);
  365. }
  366. /**
  367. * Returns the X coordinate of the first control point
  368. * in double precision.
  369. * @return the X coordinate of the first control point of the
  370. * <code>CubicCurve2D</code>.
  371. */
  372. public double getCtrlX1() {
  373. return ctrlx1;
  374. }
  375. /**
  376. * Returns the Y coordinate of the first control point
  377. * in double precision.
  378. * @return the Y coordinate of the first control point of the
  379. * <code>CubicCurve2D</code>.
  380. */
  381. public double getCtrlY1() {
  382. return ctrly1;
  383. }
  384. /**
  385. * Returns the first control point.
  386. * @return a <code>Point2D</code> that is the first control point of the
  387. * <code>CubicCurve2D</code>.
  388. */
  389. public Point2D getCtrlP1() {
  390. return new Point2D.Double(ctrlx1, ctrly1);
  391. }
  392. /**
  393. * Returns the X coordinate of the second control point
  394. * in double precision.
  395. * @return the X coordinate of the second control point of the
  396. * <code>CubicCurve2D</code>.
  397. */
  398. public double getCtrlX2() {
  399. return ctrlx2;
  400. }
  401. /**
  402. * Returns the Y coordinate of the second control point
  403. * in double precision.
  404. * @return the Y coordinate of the second control point of the
  405. * <code>CubicCurve2D</code>.
  406. */
  407. public double getCtrlY2() {
  408. return ctrly2;
  409. }
  410. /**
  411. * Returns the second control point.
  412. * @return a <code>Point2D</code> that is the second control point of
  413. * the <code>CubicCurve2D</code>.
  414. */
  415. public Point2D getCtrlP2() {
  416. return new Point2D.Double(ctrlx2, ctrly2);
  417. }
  418. /**
  419. * Returns the X coordinate of the end point
  420. * in double precision.
  421. * @return the X coordinate of the end point of the
  422. * <code>CubicCurve2D</code>.
  423. */
  424. public double getX2() {
  425. return x2;
  426. }
  427. /**
  428. * Returns the Y coordinate of the end point
  429. * in double precision.
  430. * @return the Y coordinate of the end point of the
  431. * <code>CubicCurve2D</code>.
  432. */
  433. public double getY2() {
  434. return y2;
  435. }
  436. /**
  437. * Returns the end point.
  438. * @return a <code>Point2D</code> that is the end point of
  439. * the <code>CubicCurve2D</code>.
  440. */
  441. public Point2D getP2() {
  442. return new Point2D.Double(x2, y2);
  443. }
  444. /**
  445. * Sets the location of the endpoints and controlpoints
  446. * of this curve to the specified double coordinates.
  447. * @param x1, y1 the first specified coordinates used to set the start
  448. * point of this <code>CubicCurve2D</code>
  449. * @param ctrlx1, ctrly1 the second specified coordinates used to set the
  450. * first control point of this <code>CubicCurve2D</code>
  451. * @param ctrlx2, ctrly2 the third specified coordinates used to set the
  452. * second control point of this <code>CubicCurve2D</code>
  453. * @param x2, y2 the fourth specified coordinates used to set the end
  454. * point of this <code>CubicCurve2D</code>
  455. */
  456. public void setCurve(double x1, double y1,
  457. double ctrlx1, double ctrly1,
  458. double ctrlx2, double ctrly2,
  459. double x2, double y2) {
  460. this.x1 = x1;
  461. this.y1 = y1;
  462. this.ctrlx1 = ctrlx1;
  463. this.ctrly1 = ctrly1;
  464. this.ctrlx2 = ctrlx2;
  465. this.ctrly2 = ctrly2;
  466. this.x2 = x2;
  467. this.y2 = y2;
  468. }
  469. /**
  470. * Returns the bounding box of the shape.
  471. * @return a <code>Rectangle2D</code> that is the bounding box
  472. * of the shape.
  473. */
  474. public Rectangle2D getBounds2D() {
  475. double left = Math.min(Math.min(x1, x2),
  476. Math.min(ctrlx1, ctrlx2));
  477. double top = Math.min(Math.min(y1, y2),
  478. Math.min(ctrly1, ctrly2));
  479. double right = Math.max(Math.max(x1, x2),
  480. Math.max(ctrlx1, ctrlx2));
  481. double bottom = Math.max(Math.max(y1, y2),
  482. Math.max(ctrly1, ctrly2));
  483. return new Rectangle2D.Double(left, top,
  484. right - left, bottom - top);
  485. }
  486. }
  487. /**
  488. * This is an abstract class that cannot be instantiated directly.
  489. * Type-specific implementation subclasses are available for
  490. * instantiation and provide a number of formats for storing
  491. * the information necessary to satisfy the various accessor
  492. * methods below.
  493. *
  494. * @see java.awt.geom.CubicCurve2D.Float
  495. * @see java.awt.geom.CubicCurve2D.Double
  496. */
  497. protected CubicCurve2D() {
  498. }
  499. /**
  500. * Returns the X coordinate of the start point in double precision.
  501. * @return the X coordinate of the start point of the
  502. * <code>CubicCurve2D</code>.
  503. */
  504. public abstract double getX1();
  505. /**
  506. * Returns the Y coordinate of the start point in double precision.
  507. * @return the Y coordinate of the start point of the
  508. * <code>CubicCurve2D</code>.
  509. */
  510. public abstract double getY1();
  511. /**
  512. * Returns the start point.
  513. * @return a <code>Point2D</code> that is the start point of
  514. * the <code>CubicCurve2D</code>.
  515. */
  516. public abstract Point2D getP1();
  517. /**
  518. * Returns the X coordinate of the first control point in double precision.
  519. * @return the X coordinate of the first control point of the
  520. * <code>CubicCurve2D</code>.
  521. */
  522. public abstract double getCtrlX1();
  523. /**
  524. * Returns the Y coordinate of the first control point in double precision.
  525. * @return the Y coordinate of the first control point of the
  526. * <code>CubicCurve2D</code>.
  527. */
  528. public abstract double getCtrlY1();
  529. /**
  530. * Returns the first control point.
  531. * @return a <code>Point2D</code> that is the first control point of
  532. * the <code>CubicCurve2D</code>.
  533. */
  534. public abstract Point2D getCtrlP1();
  535. /**
  536. * Returns the X coordinate of the second control point
  537. * in double precision.
  538. * @return the X coordinate of the second control point of the
  539. * <code>CubicCurve2D</code>.
  540. */
  541. public abstract double getCtrlX2();
  542. /**
  543. * Returns the Y coordinate of the second control point
  544. * in double precision.
  545. * @return the Y coordinate of the second control point of the
  546. * <code>CubicCurve2D</code>.
  547. */
  548. public abstract double getCtrlY2();
  549. /**
  550. * Returns the second control point.
  551. * @return a <code>Point2D</code> that is the second control point of
  552. * the <code>CubicCurve2D</code>.
  553. */
  554. public abstract Point2D getCtrlP2();
  555. /**
  556. * Returns the X coordinate of the end point in double precision.
  557. * @return the X coordinate of the end point of the
  558. * <code>CubicCurve2D</code>.
  559. */
  560. public abstract double getX2();
  561. /**
  562. * Returns the Y coordinate of the end point in double precision.
  563. * @return the Y coordinate of the end point of the
  564. * <code>CubicCurve2D</code>.
  565. */
  566. public abstract double getY2();
  567. /**
  568. * Returns the end point.
  569. * @return a <code>Point2D</code> that is the end point of
  570. * the <code>CubicCurve2D</code>.
  571. */
  572. public abstract Point2D getP2();
  573. /**
  574. * Sets the location of the endpoints and controlpoints of this curve
  575. * to the specified double coordinates.
  576. * @param x1, y1 the first specified coordinates used to set the start
  577. * point of this <code>CubicCurve2D</code>
  578. * @param ctrlx1, ctrly1 the second specified coordinates used to set the
  579. * first control point of this <code>CubicCurve2D</code>
  580. * @param ctrlx2, ctrly2 the third specified coordinates used to set the
  581. * second control point of this <code>CubicCurve2D</code>
  582. * @param x2, y2 the fourth specified coordinates used to set the end
  583. * point of this <code>CubicCurve2D</code>
  584. */
  585. public abstract void setCurve(double x1, double y1,
  586. double ctrlx1, double ctrly1,
  587. double ctrlx2, double ctrly2,
  588. double x2, double y2);
  589. /**
  590. * Sets the location of the endpoints and controlpoints of this curve
  591. * to the double coordinates at the specified offset in the specified
  592. * array.
  593. * @param coords a double array containing coordinates
  594. * @param offset the index of <code>coords</code> at which to begin
  595. * setting the endpoints and controlpoints of this curve
  596. * to the coordinates contained in <code>coords</code>
  597. */
  598. public void setCurve(double[] coords, int offset) {
  599. setCurve(coords[offset + 0], coords[offset + 1],
  600. coords[offset + 2], coords[offset + 3],
  601. coords[offset + 4], coords[offset + 5],
  602. coords[offset + 6], coords[offset + 7]);
  603. }
  604. /**
  605. * Sets the location of the endpoints and controlpoints of this curve
  606. * to the specified <code>Point2D</code> coordinates.
  607. * @param p1 the first specified <code>Point2D</code> used to set the
  608. * start point of this curve
  609. * @param p2 the second specified <code>Point2D</code> used to set the
  610. * first control point of this curve
  611. * @param p3 the third specified <code>Point2D</code> used to set the
  612. * second control point of this curve
  613. * @param p4 the fourth specified <code>Point2D</code> used to set the
  614. * end point of this curve
  615. */
  616. public void setCurve(Point2D p1, Point2D cp1, Point2D cp2, Point2D p2) {
  617. setCurve(p1.getX(), p1.getY(), cp1.getX(), cp1.getY(),
  618. cp2.getX(), cp2.getY(), p2.getX(), p2.getY());
  619. }
  620. /**
  621. * Sets the location of the endpoints and controlpoints of this curve
  622. * to the coordinates of the <code>Point2D</code> objects at the specified
  623. * offset in the specified array.
  624. * @param pts an array of <code>Point2D</code> objects
  625. * @param offset the index of <code>pts</code> at which to begin setting
  626. * the endpoints and controlpoints of this curve to the
  627. * points contained in <code>pts</code>
  628. */
  629. public void setCurve(Point2D[] pts, int offset) {
  630. setCurve(pts[offset + 0].getX(), pts[offset + 0].getY(),
  631. pts[offset + 1].getX(), pts[offset + 1].getY(),
  632. pts[offset + 2].getX(), pts[offset + 2].getY(),
  633. pts[offset + 3].getX(), pts[offset + 3].getY());
  634. }
  635. /**
  636. * Sets the location of the endpoints and controlpoints of this curve
  637. * to the same as those in the specified <code>CubicCurve2D</code>.
  638. * @param c the specified <code>CubicCurve2D</code>
  639. */
  640. public void setCurve(CubicCurve2D c) {
  641. setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(),
  642. c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2());
  643. }
  644. /**
  645. * Returns the square of the flatness of the cubic curve specified
  646. * by the indicated controlpoints. The flatness is the maximum distance
  647. * of a controlpoint from the line connecting the endpoints.
  648. * @param x1, y1 the first specified coordinates that specify the start
  649. * point of a <code>CubicCurve2D</code>
  650. * @param ctrlx1, ctrly1 the second specified coordinates that specify the
  651. * first control point of a <code>CubicCurve2D</code>
  652. * @param ctrlx2, ctrly2 the third specified coordinates that specify the
  653. * second control point of a <code>CubicCurve2D</code>
  654. * @param x2, y2 the fourth specified coordinates that specify the
  655. * end point of a <code>CubicCurve2D</code>
  656. * @return the square of the flatness of the <code>CubicCurve2D</code>
  657. * represented by the specified coordinates.
  658. */
  659. public static double getFlatnessSq(double x1, double y1,
  660. double ctrlx1, double ctrly1,
  661. double ctrlx2, double ctrly2,
  662. double x2, double y2) {
  663. return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx1, ctrly1),
  664. Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx2, ctrly2));
  665. }
  666. /**
  667. * Returns the flatness of the cubic curve specified
  668. * by the indicated controlpoints. The flatness is the maximum distance
  669. * of a controlpoint from the line connecting the endpoints.
  670. * @param x1, y1 the first specified coordinates that specify the start
  671. * point of a <code>CubicCurve2D</code>
  672. * @param ctrlx1, ctrly1 the second specified coordinates that specify the
  673. * first control point of a <code>CubicCurve2D</code>
  674. * @param ctrlx2, ctrly2 the third specified coordinates that specify the
  675. * second control point of a <code>CubicCurve2D</code>
  676. * @param x2, y2 the fourth specified coordinates that specify the
  677. * end point of a <code>CubicCurve2D</code>
  678. * @return the flatness of the <code>CubicCurve2D</code>
  679. * represented by the specified coordinates.
  680. */
  681. public static double getFlatness(double x1, double y1,
  682. double ctrlx1, double ctrly1,
  683. double ctrlx2, double ctrly2,
  684. double x2, double y2) {
  685. return Math.sqrt(getFlatnessSq(x1, y1, ctrlx1, ctrly1,
  686. ctrlx2, ctrly2, x2, y2));
  687. }
  688. /**
  689. * Returns the square of the flatness of the cubic curve specified
  690. * by the controlpoints stored in the indicated array at the
  691. * indicated index. The flatness is the maximum distance
  692. * of a controlpoint from the line connecting the endpoints.
  693. * @param coords an array containing coordinates
  694. * @param offset the index of <code>coords</code> at which to begin
  695. * setting the endpoints and controlpoints of this curve
  696. * to the coordinates contained in <code>coords</code>
  697. * @return the square of the flatness of the <code>CubicCurve2D</code>
  698. * specified by the coordinates in <code>coords</code> at
  699. * the specified offset.
  700. */
  701. public static double getFlatnessSq(double coords[], int offset) {
  702. return getFlatnessSq(coords[offset + 0], coords[offset + 1],
  703. coords[offset + 2], coords[offset + 3],
  704. coords[offset + 4], coords[offset + 5],
  705. coords[offset + 6], coords[offset + 7]);
  706. }
  707. /**
  708. * Returns the flatness of the cubic curve specified
  709. * by the controlpoints stored in the indicated array at the
  710. * indicated index. The flatness is the maximum distance
  711. * of a controlpoint from the line connecting the endpoints.
  712. * @param coords an array containing coordinates
  713. * @param offset the index of <code>coords</code> at which to begin
  714. * setting the endpoints and controlpoints of this curve
  715. * to the coordinates contained in <code>coords</code>
  716. * @return the flatness of the <code>CubicCurve2D</code>
  717. * specified by the coordinates in <code>coords</code> at
  718. * the specified offset.
  719. */
  720. public static double getFlatness(double coords[], int offset) {
  721. return getFlatness(coords[offset + 0], coords[offset + 1],
  722. coords[offset + 2], coords[offset + 3],
  723. coords[offset + 4], coords[offset + 5],
  724. coords[offset + 6], coords[offset + 7]);
  725. }
  726. /**
  727. * Returns the square of the flatness of this curve. The flatness is the
  728. * maximum distance of a controlpoint from the line connecting the
  729. * endpoints.
  730. * @return the square of the flatness of this curve.
  731. */
  732. public double getFlatnessSq() {
  733. return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
  734. getCtrlX2(), getCtrlY2(), getX2(), getY2());
  735. }
  736. /**
  737. * Returns the flatness of this curve. The flatness is the
  738. * maximum distance of a controlpoint from the line connecting the
  739. * endpoints.
  740. * @return the flatness of this curve.
  741. */
  742. public double getFlatness() {
  743. return getFlatness(getX1(), getY1(), getCtrlX1(), getCtrlY1(),
  744. getCtrlX2(), getCtrlY2(), getX2(), getY2());
  745. }
  746. /**
  747. * Subdivides this cubic curve and stores the resulting two
  748. * subdivided curves into the left and right curve parameters.
  749. * Either or both of the left and right objects may be the same
  750. * as this object or null.
  751. * @param left the cubic curve object for storing for the left or
  752. * first half of the subdivided curve
  753. * @param right the cubic curve object for storing for the right or
  754. * second half of the subdivided curve
  755. */
  756. public void subdivide(CubicCurve2D left, CubicCurve2D right) {
  757. subdivide(this, left, right);
  758. }
  759. /**
  760. * Subdivides the cubic curve specified by the <code>src</code> parameter
  761. * and stores the resulting two subdivided curves into the
  762. * <code>left</code> and <code>right</code> curve parameters.
  763. * Either or both of the <code>left</code> and <code>right</code> objects
  764. * may be the same as the <code>src</code> object or <code>null</code>.
  765. * @param src the cubic curve to be subdivided
  766. * @param left the cubic curve object for storing the left or
  767. * first half of the subdivided curve
  768. * @param right the cubic curve object for storing the right or
  769. * second half of the subdivided curve
  770. */
  771. public static void subdivide(CubicCurve2D src,
  772. CubicCurve2D left,
  773. CubicCurve2D right) {
  774. double x1 = src.getX1();
  775. double y1 = src.getY1();
  776. double ctrlx1 = src.getCtrlX1();
  777. double ctrly1 = src.getCtrlY1();
  778. double ctrlx2 = src.getCtrlX2();
  779. double ctrly2 = src.getCtrlY2();
  780. double x2 = src.getX2();
  781. double y2 = src.getY2();
  782. double centerx = (ctrlx1 + ctrlx2) / 2.0;
  783. double centery = (ctrly1 + ctrly2) / 2.0;
  784. ctrlx1 = (x1 + ctrlx1) / 2.0;
  785. ctrly1 = (y1 + ctrly1) / 2.0;
  786. ctrlx2 = (x2 + ctrlx2) / 2.0;
  787. ctrly2 = (y2 + ctrly2) / 2.0;
  788. double ctrlx12 = (ctrlx1 + centerx) / 2.0;
  789. double ctrly12 = (ctrly1 + centery) / 2.0;
  790. double ctrlx21 = (ctrlx2 + centerx) / 2.0;
  791. double ctrly21 = (ctrly2 + centery) / 2.0;
  792. centerx = (ctrlx12 + ctrlx21) / 2.0;
  793. centery = (ctrly12 + ctrly21) / 2.0;
  794. if (left != null) {
  795. left.setCurve(x1, y1, ctrlx1, ctrly1,
  796. ctrlx12, ctrly12, centerx, centery);
  797. }
  798. if (right != null) {
  799. right.setCurve(centerx, centery, ctrlx21, ctrly21,
  800. ctrlx2, ctrly2, x2, y2);
  801. }
  802. }
  803. /**
  804. * Subdivides the cubic curve specified by the coordinates
  805. * stored in the <code>src</code> array at indices <code>srcoff</code>
  806. * through (<code>srcoff</code> + 7) and stores the
  807. * resulting two subdivided curves into the two result arrays at the
  808. * corresponding indices.
  809. * Either or both of the <code>left</code> and <code>right</code>
  810. * arrays may be <code>null</code> or a reference to the same array
  811. * as the <code>src</code> array.
  812. * Note that the last point in the first subdivided curve is the
  813. * same as the first point in the second subdivided curve. Thus,
  814. * it is possible to pass the same array for <code>left</code>
  815. * and <code>right</code> and to use offsets, such as <code>rightoff</code>
  816. * equals (<code>leftoff</code> + 6), in order
  817. * to avoid allocating extra storage for this common point.
  818. * @param src the array holding the coordinates for the source curve
  819. * @param srcoff the offset into the array of the beginning of the
  820. * the 6 source coordinates
  821. * @param left the array for storing the coordinates for the first
  822. * half of the subdivided curve
  823. * @param leftoff the offset into the array of the beginning of the
  824. * the 6 left coordinates
  825. * @param right the array for storing the coordinates for the second
  826. * half of the subdivided curve
  827. * @param rightoff the offset into the array of the beginning of the
  828. * the 6 right coordinates
  829. */
  830. public static void subdivide(double src[], int srcoff,
  831. double left[], int leftoff,
  832. double right[], int rightoff) {
  833. double x1 = src[srcoff + 0];
  834. double y1 = src[srcoff + 1];
  835. double ctrlx1 = src[srcoff + 2];
  836. double ctrly1 = src[srcoff + 3];
  837. double ctrlx2 = src[srcoff + 4];
  838. double ctrly2 = src[srcoff + 5];
  839. double x2 = src[srcoff + 6];
  840. double y2 = src[srcoff + 7];
  841. if (left != null) {
  842. left[leftoff + 0] = x1;
  843. left[leftoff + 1] = y1;
  844. }
  845. if (right != null) {
  846. right[rightoff + 6] = x2;
  847. right[rightoff + 7] = y2;
  848. }
  849. x1 = (x1 + ctrlx1) / 2.0;
  850. y1 = (y1 + ctrly1) / 2.0;
  851. x2 = (x2 + ctrlx2) / 2.0;
  852. y2 = (y2 + ctrly2) / 2.0;
  853. double centerx = (ctrlx1 + ctrlx2) / 2.0;
  854. double centery = (ctrly1 + ctrly2) / 2.0;
  855. ctrlx1 = (x1 + centerx) / 2.0;
  856. ctrly1 = (y1 + centery) / 2.0;
  857. ctrlx2 = (x2 + centerx) / 2.0;
  858. ctrly2 = (y2 + centery) / 2.0;
  859. centerx = (ctrlx1 + ctrlx2) / 2.0;
  860. centery = (ctrly1 + ctrly2) / 2.0;
  861. if (left != null) {
  862. left[leftoff + 2] = x1;
  863. left[leftoff + 3] = y1;
  864. left[leftoff + 4] = ctrlx1;
  865. left[leftoff + 5] = ctrly1;
  866. left[leftoff + 6] = centerx;
  867. left[leftoff + 7] = centery;
  868. }
  869. if (right != null) {
  870. right[rightoff + 0] = centerx;
  871. right[rightoff + 1] = centery;
  872. right[rightoff + 2] = ctrlx2;
  873. right[rightoff + 3] = ctrly2;
  874. right[rightoff + 4] = x2;
  875. right[rightoff + 5] = y2;
  876. }
  877. }
  878. /**
  879. * Solves the cubic whose coefficients are in the <code>eqn</code>
  880. * array and places the non-complex roots back into the same array,
  881. * returning the number of roots. The solved cubic is represented
  882. * by the equation:
  883. * <pre>
  884. * eqn = {c, b, a, d}
  885. * dx^3 + ax^2 + bx + c = 0
  886. * </pre>
  887. * A return value of -1 is used to distinguish a constant equation
  888. * that might be always 0 or never 0 from an equation that has no
  889. * zeroes.
  890. * @param eqn an array containing coefficients for a cubic
  891. * @return the number of roots, or -1 if the equation is a constant.
  892. */
  893. public static int solveCubic(double eqn[]) {
  894. return solveCubic(eqn, eqn);
  895. }
  896. /**
  897. * Solve the cubic whose coefficients are in the <code>eqn</code>
  898. * array and place the non-complex roots into the <code>res</code>
  899. * array, returning the number of roots.
  900. * The cubic solved is represented by the equation:
  901. * eqn = {c, b, a, d}
  902. * dx^3 + ax^2 + bx + c = 0
  903. * A return value of -1 is used to distinguish a constant equation,
  904. * which may be always 0 or never 0, from an equation which has no
  905. * zeroes.
  906. * @return the number of roots, or -1 if the equation is a constant
  907. */
  908. public static int solveCubic(double eqn[], double res[]) {
  909. // From Numerical Recipes, 5.6, Quadratic and Cubic Equations
  910. double d = eqn[3];
  911. if (d == 0.0) {
  912. // The cubic has degenerated to quadratic (or line or ...).
  913. return QuadCurve2D.solveQuadratic(eqn, res);
  914. }
  915. double a = eqn[2] / d;
  916. double b = eqn[1] / d;
  917. double c = eqn[0] / d;
  918. int roots = 0;
  919. double Q = (a * a - 3.0 * b) / 9.0;
  920. double R = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0;
  921. double R2 = R * R;
  922. double Q3 = Q * Q * Q;
  923. a = a / 3.0;
  924. if (R2 < Q3) {
  925. double theta = Math.acos(R / Math.sqrt(Q3));
  926. Q = -2.0 * Math.sqrt(Q);
  927. if (res == eqn) {
  928. // Copy the eqn so that we don't clobber it with the
  929. // roots. This is needed so that fixRoots can do its
  930. // work with the original equation.
  931. eqn = new double[4];
  932. System.arraycopy(res, 0, eqn, 0, 4);
  933. }
  934. res[roots++] = Q * Math.cos(theta / 3.0) - a;
  935. res[roots++] = Q * Math.cos((theta + Math.PI * 2.0)/ 3.0) - a;
  936. res[roots++] = Q * Math.cos((theta - Math.PI * 2.0)/ 3.0) - a;
  937. fixRoots(res, eqn);
  938. } else {
  939. boolean neg = (R < 0.0);
  940. double S = Math.sqrt(R2 - Q3);
  941. if (neg) {
  942. R = -R;
  943. }
  944. double A = Math.pow(R + S, 1.0 / 3.0);
  945. if (!neg) {
  946. A = -A;
  947. }
  948. double B = (A == 0.0) ? 0.0 : (Q / A);
  949. res[roots++] = (A + B) - a;
  950. }
  951. return roots;
  952. }
  953. /*
  954. * This pruning step is necessary since solveCubic uses the
  955. * cosine function to calculate the roots when there are 3
  956. * of them. Since the cosine method can have an error of
  957. * +/- 1E-14 we need to make sure that we don't make any
  958. * bad decisions due to an error.
  959. *
  960. * If the root is not near one of the endpoints, then we will
  961. * only have a slight inaccuracy in calculating the x intercept
  962. * which will only cause a slightly wrong answer for some
  963. * points very close to the curve. While the results in that
  964. * case are not as accurate as they could be, they are not
  965. * disastrously inaccurate either.
  966. *
  967. * On the other hand, if the error happens near one end of
  968. * the curve, then our processing to reject values outside
  969. * of the t=[0,1] range will fail and the results of that
  970. * failure will be disastrous since for an entire horizontal
  971. * range of test points, we will either overcount or undercount
  972. * the crossings and get a wrong answer for all of them, even
  973. * when they are clearly and obviously inside or outside the
  974. * curve.
  975. *
  976. * To work around this problem, we try a couple of Newton-Raphson
  977. * iterations to see if the true root is closer to the endpoint
  978. * or further away. If it is further away, then we can stop
  979. * since we know we are on the right side of the endpoint. If
  980. * we change direction, then either we are now being dragged away
  981. * from the endpoint in which case the first condition will cause
  982. * us to stop, or we have passed the endpoint and are headed back.
  983. * In the second case, we simply evaluate the slope at the
  984. * endpoint itself and place ourselves on the appropriate side
  985. * of it or on it depending on that result.
  986. */
  987. private static void fixRoots(double res[], double eqn[]) {
  988. final double EPSILON = 1E-5;
  989. for (int i = 0; i < 3; i++) {
  990. double t = res[i];
  991. if (Math.abs(t) < EPSILON) {
  992. res[i] = findZero(t, 0, eqn);
  993. } else if (Math.abs(t - 1) < EPSILON) {
  994. res[i] = findZero(t, 1, eqn);
  995. }
  996. }
  997. }
  998. private static double solveEqn(double eqn[], int order, double t) {
  999. double v = eqn[order];
  1000. while (--order >= 0) {
  1001. v = v * t + eqn[order];
  1002. }
  1003. return v;
  1004. }
  1005. private static double findZero(double t, double target, double eqn[]) {
  1006. double slopeqn[] = {eqn[1], 2*eqn[2], 3*eqn[3]};
  1007. double slope;
  1008. double origdelta = 0;
  1009. double origt = t;
  1010. while (true) {
  1011. slope = solveEqn(slopeqn, 2, t);
  1012. if (slope == 0) {
  1013. // At a local minima - must return
  1014. return t;
  1015. }
  1016. double y = solveEqn(eqn, 3, t);
  1017. if (y == 0) {
  1018. // Found it! - return it
  1019. return t;
  1020. }
  1021. // assert(slope != 0 && y != 0);
  1022. double delta = - (y / slope);
  1023. // assert(delta != 0);
  1024. if (origdelta == 0) {
  1025. origdelta = delta;
  1026. }
  1027. if (t < target) {
  1028. if (delta < 0) return t;
  1029. } else if (t > target) {
  1030. if (delta > 0) return t;
  1031. } else { /* t == target */
  1032. return (delta > 0
  1033. ? (target + java.lang.Double.MIN_VALUE)
  1034. : (target - java.lang.Double.MIN_VALUE));
  1035. }
  1036. double newt = t + delta;
  1037. if (t == newt) {
  1038. // The deltas are so small that we aren't moving...
  1039. return t;
  1040. }
  1041. if (delta * origdelta < 0) {
  1042. // We have reversed our path.
  1043. int tag = (origt < t
  1044. ? getTag(target, origt, t)
  1045. : getTag(target, t, origt));
  1046. if (tag != INSIDE) {
  1047. // Local minima found away from target - return the middle
  1048. return (origt + t) / 2;
  1049. }
  1050. // Local minima somewhere near target - move to target
  1051. // and let the slope determine the resulting t.
  1052. t = target;
  1053. } else {
  1054. t = newt;
  1055. }
  1056. }
  1057. }
  1058. /**
  1059. * Tests if a specified coordinate is inside the boundary of the shape.
  1060. * @param x, y the specified coordinate to be tested
  1061. * @return <code>true</code> if the coordinate is inside the boundary of
  1062. * the shape; <code>false</code> otherwise.
  1063. */
  1064. public boolean contains(double x, double y) {
  1065. // We count the "Y" crossings to determine if the point is
  1066. // inside the curve bounded by its closing line.
  1067. int crossings = 0;
  1068. double x1 = getX1();
  1069. double y1 = getY1();
  1070. double x2 = getX2();
  1071. double y2 = getY2();
  1072. // First check for a crossing of the line connecting the endpoints
  1073. double dy = y2 - y1;
  1074. if ((dy > 0.0 && y >= y1 && y <= y2) ||
  1075. (dy < 0.0 && y <= y1 && y >= y2))
  1076. {
  1077. if (x < x1 + (y - y1) * (x2 - x1) / dy) {
  1078. crossings++;
  1079. }
  1080. }
  1081. // Solve the Y parametric equation for intersections with y
  1082. double ctrlx1 = getCtrlX1();
  1083. double ctrly1 = getCtrlY1();
  1084. double ctrlx2 = getCtrlX2();
  1085. double ctrly2 = getCtrlY2();
  1086. boolean include0 = ((y2 - y1) * (ctrly1 - y1) >= 0);
  1087. boolean include1 = ((y1 - y2) * (ctrly2 - y2) >= 0);
  1088. double eqn[] = new double[4];
  1089. double res[] = new double[4];
  1090. fillEqn(eqn, y, y1, ctrly1, ctrly2, y2);
  1091. int roots = solveCubic(eqn, res);
  1092. roots = evalCubic(res, roots,
  1093. include0, include1, eqn,
  1094. x1, ctrlx1, ctrlx2, x2);
  1095. while (--roots >= 0) {
  1096. if (x < res[roots]) {
  1097. crossings++;
  1098. }
  1099. }
  1100. return ((crossings & 1) == 1);
  1101. }
  1102. /**
  1103. * Tests if a specified <code>Point2D</code> is inside the boundary of
  1104. * the shape.
  1105. * @param p the specified <code>Point2D</code> to be tested
  1106. * @return <code>true</code> if the <code>p</code> is inside the boundary
  1107. * of the shape; <code>false</code> otherwise.
  1108. */
  1109. public boolean contains(Point2D p) {
  1110. return contains(p.getX(), p.getY());
  1111. }
  1112. /*
  1113. * Fill an array with the coefficients of the parametric equation
  1114. * in t, ready for solving against val with solveCubic.
  1115. * We currently have:
  1116. * val = P(t) = C1(1-t)^3 + 3CP1 t(1-t)^2 + 3CP2 t^2(1-t) + C2 t^3
  1117. * = C1 - 3C1t + 3C1t^2 - C1t^3 +
  1118. * 3CP1t - 6CP1t^2 + 3CP1t^3 +
  1119. * 3CP2t^2 - 3CP2t^3 +
  1120. * C2t^3
  1121. * 0 = (C1 - val) +
  1122. * (3CP1 - 3C1) t +
  1123. * (3C1 - 6CP1 + 3CP2) t^2 +
  1124. * (C2 - 3CP2 + 3CP1 - C1) t^3
  1125. * 0 = C + Bt + At^2 + Dt^3
  1126. * C = C1 - val
  1127. * B = 3*CP1 - 3*C1
  1128. * A = 3*CP2 - 6*CP1 + 3*C1
  1129. * D = C2 - 3*CP2 + 3*CP1 - C1
  1130. * @param x, y the coordinates of the upper left corner of the specified
  1131. * rectangular shape
  1132. * @param w the width of the specified rectangular shape
  1133. * @param h the height of the specified rectangular shape
  1134. * @return <code>true</code> if the shape intersects the interior of the
  1135. * the specified set of rectangular coordinates;
  1136. * <code>false</code> otherwise.
  1137. */
  1138. private static void fillEqn(double eqn[], double val,
  1139. double c1, double cp1, double cp2, double c2) {
  1140. eqn[0] = c1 - val;
  1141. eqn[1] = (cp1 - c1) * 3.0;
  1142. eqn[2] = (cp2 - cp1 - cp1 + c1) * 3.0;
  1143. eqn[3] = c2 + (cp1 - cp2) * 3.0 - c1;
  1144. return;
  1145. }
  1146. /*
  1147. * Evaluate the t values in the first num slots of the vals[] array
  1148. * and place the evaluated values back into the same array. Only
  1149. * evaluate t values that are within the range <0, 1>, including
  1150. * the 0 and 1 ends of the range iff the include0 or include1
  1151. * booleans are true. If an "inflection" equation is handed in,
  1152. * then any points which represent a point of inflection for that
  1153. * cubic equation are also ignored.
  1154. */
  1155. private static int evalCubic(double vals[], int num,
  1156. boolean include0,
  1157. boolean include1,
  1158. double inflect[],
  1159. double c1, double cp1,
  1160. double cp2, double c2) {
  1161. int j = 0;
  1162. for (int i = 0; i < num; i++) {
  1163. double t = vals[i];
  1164. if ((include0 ? t >= 0 : t > 0) &&
  1165. (include1 ? t <= 1 : t < 1) &&
  1166. (inflect == null ||
  1167. inflect[1] + (2*inflect[2] + 3*inflect[3]*t)*t != 0))
  1168. {
  1169. double u = 1 - t;
  1170. vals[j++] = c1*u*u*u + 3*cp1*t*u*u + 3*cp2*t*t*u + c2*t*t*t;
  1171. }
  1172. }
  1173. return j;
  1174. }
  1175. private static final int BELOW = -2;
  1176. private static final int LOWEDGE = -1;
  1177. private static final int INSIDE = 0;
  1178. private static final int HIGHEDGE = 1;
  1179. private static final int ABOVE = 2;
  1180. /*
  1181. * Determine where coord lies with respect to the range from
  1182. * low to high. It is assumed that low <= high. The return
  1183. * value is one of the 5 values BELOW, LOWEDGE, INSIDE, HIGHEDGE,
  1184. * or ABOVE.
  1185. */
  1186. private static int getTag(double coord, double low, double high) {
  1187. if (coord <= low) {
  1188. return (coord < low ? BELOW : LOWEDGE);
  1189. }
  1190. if (coord >= high) {
  1191. return (coord > high ? ABOVE : HIGHEDGE);
  1192. }
  1193. return INSIDE;
  1194. }
  1195. /*
  1196. * Determine if the pttag represents a coordinate that is already
  1197. * in its test range, or is on the border with either of the two
  1198. * opttags representing another coordinate that is "towards the
  1199. * inside" of that test range. In other words, are either of the
  1200. * two "opt" points "drawing the pt inward"?
  1201. */
  1202. private static boolean inwards(int pttag, int opt1tag, int opt2tag) {
  1203. switch (pttag) {
  1204. case BELOW:
  1205. case ABOVE:
  1206. default:
  1207. return false;
  1208. case LOWEDGE:
  1209. return (opt1tag >= INSIDE || opt2tag >= INSIDE);
  1210. case INSIDE:
  1211. return true;
  1212. case HIGHEDGE:
  1213. return (opt1tag <= INSIDE || opt2tag <= INSIDE);
  1214. }
  1215. }
  1216. /**
  1217. * Tests if the shape intersects the interior of a specified
  1218. * set of rectangular coordinates.
  1219. * @param x, y the coordinates of the upper left corner
  1220. * of the specified rectangular area
  1221. * @param w the width of the specified rectangular area
  1222. * @param h the height of the specified rectangular area
  1223. * @return <code>true</code> if the shape intersects the
  1224. * interior of the specified rectangular area;
  1225. * <code>false</code> otherwise.
  1226. */
  1227. public boolean intersects(double x, double y, double w, double h) {
  1228. // Trivially reject non-existant rectangles
  1229. if (w < 0 || h < 0) {
  1230. return false;
  1231. }
  1232. // Trivially accept if either endpoint is inside the rectangle
  1233. // (not on its border since it may end there and not go inside)
  1234. // Record where they lie with respect to the rectangle.
  1235. // -1 => left, 0 => inside, 1 => right
  1236. double x1 = getX1();
  1237. double y1 = getY1();
  1238. int x1tag = getTag(x1, x, x+w);
  1239. int y1tag = getTag(y1, y, y+h);
  1240. if (x1tag == INSIDE && y1tag == INSIDE) {
  1241. return true;
  1242. }
  1243. double x2 = getX2();
  1244. double y2 = getY2();
  1245. int x2tag = getTag(x2, x, x+w);
  1246. int y2tag = getTag(y2, y, y+h);
  1247. if (x2tag == INSIDE && y2tag == INSIDE) {
  1248. return true;
  1249. }
  1250. double ctrlx1 = getCtrlX1();
  1251. double ctrly1 = getCtrlY1();
  1252. double ctrlx2 = getCtrlX2();
  1253. double ctrly2 = getCtrlY2();
  1254. int ctrlx1tag = getTag(ctrlx1, x, x+w);
  1255. int ctrly1tag = getTag(ctrly1, y, y+h);
  1256. int ctrlx2tag = getTag(ctrlx2, x, x+w);
  1257. int ctrly2tag = getTag(ctrly2, y, y+h);
  1258. // Trivially reject if all points are entirely to one side of
  1259. // the rectangle.
  1260. if (x1tag < INSIDE && x2tag < INSIDE &&
  1261. ctrlx1tag < INSIDE && ctrlx2tag < INSIDE)
  1262. {
  1263. return false; // All points left
  1264. }
  1265. if (y1tag < INSIDE && y2tag < INSIDE &&
  1266. ctrly1tag < INSIDE && ctrly2tag < INSIDE)
  1267. {
  1268. return false; // All points above
  1269. }
  1270. if (x1tag > INSIDE && x2tag > INSIDE &&
  1271. ctrlx1tag > INSIDE && ctrlx2tag > INSIDE)
  1272. {
  1273. return false; // All points right
  1274. }
  1275. if (y1tag > INSIDE && y2tag > INSIDE &&
  1276. ctrly1tag > INSIDE && ctrly2tag > INSIDE)
  1277. {
  1278. return false; // All points below
  1279. }
  1280. // Test for endpoints on the edge where either the segment
  1281. // or the curve is headed "inwards" from them
  1282. // Note: These tests are a superset of the fast endpoint tests
  1283. // above and thus repeat those tests, but take more time
  1284. // and cover more cases
  1285. if (inwards(x1tag, x2tag, ctrlx1tag) &&
  1286. inwards(y1tag, y2tag, ctrly1tag))
  1287. {
  1288. // First endpoint on border with either edge moving inside
  1289. return true;
  1290. }
  1291. if (inwards(x2tag, x1tag, ctrlx2tag) &&
  1292. inwards(y2tag, y1tag, ctrly2tag))
  1293. {
  1294. // Second endpoint on border with either edge moving inside
  1295. return true;
  1296. }
  1297. // Trivially accept if endpoints span directly across the rectangle
  1298. boolean xoverlap = (x1tag * x2tag <= 0);
  1299. boolean yoverlap = (y1tag * y2tag <= 0);
  1300. if (x1tag == INSIDE && x2tag == INSIDE && yoverlap) {
  1301. return true;
  1302. }
  1303. if (y1tag == INSIDE && y2tag == INSIDE && xoverlap) {
  1304. return true;
  1305. }
  1306. // We now know that both endpoints are outside the rectangle
  1307. // but the 4 points are not all on one side of the rectangle.
  1308. // Therefore the curve cannot be contained inside the rectangle,
  1309. // but the rectangle might be contained inside the curve, or
  1310. // the curve might intersect the boundary of the rectangle.
  1311. double[] eqn = new double[4];
  1312. double[] res = new double[4];
  1313. if (!yoverlap) {
  1314. // Both y coordinates for the closing segment are above or
  1315. // below the rectangle which means that we can only intersect
  1316. // if the curve crosses the top (or bottom) of the rectangle
  1317. // in more than one place and if those crossing locations
  1318. // span the horizontal range of the rectangle.
  1319. fillEqn(eqn, (y1tag < INSIDE ? y : y+h), y1, ctrly1, ctrly2, y2);
  1320. int num = solveCubic(eqn, res);
  1321. num = evalCubic(res, num, true, true, null,
  1322. x1, ctrlx1, ctrlx2, x2);
  1323. // odd counts imply the crossing was out of [0,1] bounds
  1324. // otherwise there is no way for that part of the curve to
  1325. // "return" to meet its endpoint
  1326. return (num == 2 &&
  1327. getTag(res[0], x, x+w) * getTag(res[1], x, x+w) <= 0);
  1328. }
  1329. // Y ranges overlap. Now we examine the X ranges
  1330. if (!xoverlap) {
  1331. // Both x coordinates for the closing segment are left of
  1332. // or right of the rectangle which means that we can only
  1333. // intersect if the curve crosses the left (or right) edge
  1334. // of the rectangle in more than one place and if those
  1335. // crossing locations span the vertical range of the rectangle.
  1336. fillEqn(eqn, (x1tag < INSIDE ? x : x+w), x1, ctrlx1, ctrlx2, x2);
  1337. int num = solveCubic(eqn, res);
  1338. num = evalCubic(res, num, true, true, null,
  1339. y1, ctrly1, ctrly2, y2);
  1340. // odd counts imply the crossing was out of [0,1] bounds
  1341. // otherwise there is no way for that part of the curve to
  1342. // "return" to meet its endpoint
  1343. return (num == 2 &&
  1344. getTag(res[0], y, y+h) * getTag(res[1], y, y+h) <= 0);
  1345. }
  1346. // The X and Y ranges of the endpoints overlap the X and Y
  1347. // ranges of the rectangle, now find out how the endpoint
  1348. // line segment intersects the Y range of the rectangle
  1349. double dx = x2 - x1;
  1350. double dy = y2 - y1;
  1351. double k = y2 * x1 - x2 * y1;
  1352. int c1tag, c2tag;
  1353. if (y1tag == INSIDE) {
  1354. c1tag = x1tag;
  1355. } else {
  1356. c1tag = getTag((k + dx * (y1tag < INSIDE ? y : y+h)) / dy, x, x+w);
  1357. }
  1358. if (y2tag == INSIDE) {
  1359. c2tag = x2tag;
  1360. } else {
  1361. c2tag = getTag((k + dx * (y2tag < INSIDE ? y : y+h)) / dy, x, x+w);
  1362. }
  1363. // If the part of the line segment that intersects the Y range
  1364. // of the rectangle crosses it horizontally - trivially accept
  1365. if (c1tag * c2tag <= 0) {
  1366. return true;
  1367. }
  1368. // Now we know that both the X and Y ranges intersect and that
  1369. // the endpoint line segment does not directly cross the rectangle.
  1370. //
  1371. // We can almost treat this case like one of the cases above
  1372. // where both endpoints are to one side, except that we may
  1373. // get one or three intersections of the curve with the vertical
  1374. // side of the rectangle. This is because the endpoint segment
  1375. // accounts for the other intersection in an even pairing. Thus,
  1376. // with the endpoint crossing we end up with 2 or 4 total crossings.
  1377. //
  1378. // (Remember there is overlap in both the X and Y ranges which
  1379. // means that the segment itself must cross at least one vertical
  1380. // edge of the rectangle - in particular, the "near vertical side"
  1381. // - leaving an odd number of intersections for the curve.)
  1382. //
  1383. // Now we calculate the y tags of all the intersections on the
  1384. // "near vertical side" of the rectangle. We will have one with
  1385. // the endpoint segment, and one or three with the curve. If
  1386. // any pair of those vertical intersections overlap the Y range
  1387. // of the rectangle, we have an intersection. Otherwise, we don't.
  1388. // c1tag = vertical intersection class of the endpoint segment
  1389. //
  1390. // Choose the y tag of the endpoint that was not on the same
  1391. // side of the rectangle as the subsegment calculated above.
  1392. // Note that we can "steal" the existing Y tag of that endpoint
  1393. // since it will be provably the same as the vertical intersection.
  1394. c1tag = ((c1tag * x1tag <= 0) ? y1tag : y2tag);
  1395. // Now we have to calculate an array of solutions of the curve
  1396. // with the "near vertical side" of the rectangle. Then we
  1397. // need to sort the tags and do a pairwise range test to see
  1398. // if either of the pairs of crossings spans the Y range of
  1399. // the rectangle.
  1400. //
  1401. // Note that the c2tag can still tell us which vertical edge
  1402. // to test against.
  1403. fillEqn(eqn, (c2tag < INSIDE ? x : x+w), x1, ctrlx1, ctrlx2, x2);
  1404. int num = solveCubic(eqn, res);
  1405. num = evalCubic(res, num, true, true, null, y1, ctrly1, ctrly2, y2);
  1406. // Now put all of the tags into a bucket and sort them. There
  1407. // is an intersection iff one of the pairs of tags "spans" the
  1408. // Y range of the rectangle.
  1409. int tags[] = new int[num+1];
  1410. for (int i = 0; i < num; i++) {
  1411. tags[i] = getTag(res[i], y, y+h);
  1412. }
  1413. tags[num] = c1tag;
  1414. Arrays.sort(tags);
  1415. return ((num >= 1 && tags[0] * tags[1] <= 0) ||
  1416. (num >= 3 && tags[2] * tags[3] <= 0));
  1417. }
  1418. /**
  1419. * Tests if the shape intersects the interior of a specified
  1420. * <code>Rectangle2D</code>.
  1421. * @param r the specified <code>Rectangle2D</code> to be tested
  1422. * @return <code>true</code> if the shape intersects the interior of
  1423. * the specified <code>Rectangle2D</code>
  1424. * <code>false</code> otherwise.
  1425. */
  1426. public boolean intersects(Rectangle2D r) {
  1427. return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  1428. }
  1429. /**
  1430. * Tests if the interior of the shape entirely contains the specified
  1431. * set of rectangular coordinates.
  1432. * @param x, y the coordinates of the upper left corner of the specified
  1433. * rectangular shape
  1434. * @param w the width of the specified rectangular shape
  1435. * @param h the height of the specified rectangular shape
  1436. * @return <code>true</code> if the shape entirely contains
  1437. * the specified set of rectangular coordinates;
  1438. * <code>false</code> otherwise.
  1439. */
  1440. public boolean contains(double x, double y, double w, double h) {
  1441. // Assertion: Cubic curves closed by connecting their
  1442. // endpoints form either one or two convex halves with
  1443. // the closing line segment as an edge of both sides.
  1444. if (!(contains(x, y) &&
  1445. contains(x + w, y) &&
  1446. contains(x + w, y + h) &&
  1447. contains(x, y + h))) {
  1448. return false;
  1449. }
  1450. // Either the rectangle is entirely inside one of the convex
  1451. // halves or it crosses from one to the other, in which case
  1452. // it must intersect the closing line segment.
  1453. Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
  1454. return !rect.intersectsLine(getX1(), getY1(), getX2(), getY2());
  1455. }
  1456. /**
  1457. * Tests if the interior of the shape entirely contains the specified
  1458. * <code>Rectangle2D</code>.
  1459. * @param r the specified <code>Rectangle2D</code> to be tested
  1460. * @return <code>true</code> if the shape entirely contains
  1461. * the specified <code>Rectangle2D</code>
  1462. * <code>false</code> otherwise.
  1463. */
  1464. public boolean contains(Rectangle2D r) {
  1465. return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  1466. }
  1467. /**
  1468. * Returns the bounding box of the shape.
  1469. * @return a {@link Rectangle} that is the bounding box of the shape.
  1470. */
  1471. public Rectangle getBounds() {
  1472. return getBounds2D().getBounds();
  1473. }
  1474. /**
  1475. * Returns an iteration object that defines the boundary of the
  1476. * shape.
  1477. * The iterator for this class is not multi-threaded safe,
  1478. * which means that this <code>CubicCurve2D</code> class does not
  1479. * guarantee that modifications to the geometry of this
  1480. * <code>CubicCurve2D</code> object do not affect any iterations of
  1481. * that geometry that are already in process.
  1482. * @param at an optional <code>AffineTransform</code> to be applied to the
  1483. * coordinates as they are returned in the iteration, or <code>null</code>
  1484. * if untransformed coordinates are desired
  1485. * @return the <code>PathIterator</code> object that returns the
  1486. * geometry of the outline of this <code>CubicCurve2D</code>, one
  1487. * segment at a time.
  1488. */
  1489. public PathIterator getPathIterator(AffineTransform at) {
  1490. return new CubicIterator(this, at);
  1491. }
  1492. /**
  1493. * Return an iteration object that defines the boundary of the
  1494. * flattened shape.
  1495. * The iterator for this class is not multi-threaded safe,
  1496. * which means that this <code>CubicCurve2D</code> class does not
  1497. * guarantee that modifications to the geometry of this
  1498. * <code>CubicCurve2D</code> object do not affect any iterations of
  1499. * that geometry that are already in process.
  1500. * @param at an optional <code>AffineTransform</code> to be applied to the
  1501. * coordinates as they are returned in the iteration, or <code>null</code>
  1502. * if untransformed coordinates are desired
  1503. * @param flatness the maximum amount that the control points
  1504. * for a given curve can vary from colinear before a subdivided
  1505. * curve is replaced by a straight line connecting the endpoints
  1506. * @return the <code>PathIterator</code> object that returns the
  1507. * geometry of the outline of this <code>CubicCurve2D</code>, one segment at a time.
  1508. */
  1509. public PathIterator getPathIterator(AffineTransform at, double flatness) {
  1510. return new FlatteningPathIterator(getPathIterator(at), flatness);
  1511. }
  1512. /**
  1513. * Creates a new object of the same class as this object.
  1514. *
  1515. * @return a clone of this instance.
  1516. * @exception OutOfMemoryError if there is not enough memory.
  1517. * @see java.lang.Cloneable
  1518. * @since 1.2
  1519. */
  1520. public Object clone() {
  1521. try {
  1522. return super.clone();
  1523. } catch (CloneNotSupportedException e) {
  1524. // this shouldn't happen, since we are Cloneable
  1525. throw new InternalError();
  1526. }
  1527. }
  1528. }