You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CubicBezierApproximator.java 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.afp.util;
  19. import java.awt.geom.Point2D;
  20. import java.awt.geom.Point2D.Double;
  21. /**
  22. * This class can be used to convert a cubic bezier curve within
  23. * a path into multiple quadratic bezier curves which will approximate
  24. * the original cubic curve.
  25. * The various techniques are described here:
  26. * http://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm
  27. */
  28. public final class CubicBezierApproximator {
  29. private CubicBezierApproximator() {
  30. }
  31. /**
  32. * This method will take in an array containing the x and y coordinates of the four control
  33. * points that describe the cubic bezier curve to be approximated using the fixed mid point
  34. * approximation. The curve will be approximated using four quadratic bezier curves the points
  35. * for which will be returned in a two dimensional array, with each array within that containing
  36. * the points for a single quadratic curve. The returned data will not include the start point
  37. * for any of the curves; the first point passed in to this method should already have been
  38. * set as the current position and will be the assumed start of the first curve.
  39. *
  40. * @param cubicControlPointCoords an array containing the x and y coordinates of the
  41. * four control points.
  42. * @return an array of arrays containing the x and y coordinates of the quadratic curves
  43. * that approximate the original supplied cubic bezier curve.
  44. */
  45. public static double[][] fixedMidPointApproximation(double[] cubicControlPointCoords) {
  46. if (cubicControlPointCoords.length < 8) {
  47. throw new IllegalArgumentException("Must have at least 8 coordinates");
  48. }
  49. //extract point objects from source array
  50. Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]);
  51. Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]);
  52. Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]);
  53. Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]);
  54. //calculates the useful base points
  55. Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0);
  56. Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0);
  57. //get 1/16 of the [P3, P0] segment
  58. double dx = (p3.getX() - p0.getX()) / 16.0;
  59. double dy = (p3.getY() - p0.getY()) / 16.0;
  60. //calculates control point 1
  61. Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0);
  62. //calculates control point 2
  63. Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0);
  64. pc2 = movePoint(pc2, -dx, -dy);
  65. //calculates control point 3
  66. Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0);
  67. pc3 = movePoint(pc3, dx, dy);
  68. //calculates control point 4
  69. Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0);
  70. //calculates the 3 anchor points
  71. Point2D pa1 = getMidPoint(pc1, pc2);
  72. Point2D pa2 = getMidPoint(pa, pb);
  73. Point2D pa3 = getMidPoint(pc3, pc4);
  74. //return the points for the four quadratic curves
  75. return new double[][] {
  76. {pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()},
  77. {pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()},
  78. {pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()},
  79. {pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}};
  80. }
  81. private static Double movePoint(Point2D point, double dx, double dy) {
  82. return new Point2D.Double(point.getX() + dx, point.getY() + dy);
  83. }
  84. /**
  85. * This method will calculate the coordinates of a point half way along a segment [P0, P1]
  86. *
  87. * @param p0 - The point describing the start of the segment.
  88. * @param p1 - The point describing the end of the segment.
  89. * @return a Point object describing the coordinates of the calculated point on the segment.
  90. */
  91. private static Point2D getMidPoint(Point2D p0, Point2D p1) {
  92. return getPointOnSegment(p0, p1, 0.5);
  93. }
  94. /**
  95. * This method will calculate the coordinates of a point on a segment [P0, P1]
  96. * whose distance along the segment [P0, P1] from P0, is the given ratio
  97. * of the length the [P0, P1] segment.
  98. *
  99. * @param p0 The point describing the start of the segment.
  100. * @param p1 The point describing the end of the segment.
  101. * @param ratio The distance of the point being calculated from P0 as a ratio of
  102. * the segment length.
  103. * @return a Point object describing the coordinates of the calculated point on the segment.
  104. */
  105. private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) {
  106. double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio);
  107. double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio);
  108. return new Point2D.Double(x, y);
  109. }
  110. }