/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: CubicBezierApproximator.java 985537 2010-08-14 17:17:00Z jeremias $ */

package org.apache.fop.afp.util;

import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;

This class can be used to convert a cubic bezier curve within a path into multiple quadratic bezier curves which will approximate the original cubic curve. The various techniques are described here: http://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm
/** * This class can be used to convert a cubic bezier curve within * a path into multiple quadratic bezier curves which will approximate * the original cubic curve. * The various techniques are described here: * http://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm */
public final class CubicBezierApproximator { private CubicBezierApproximator() { }
This method will take in an array containing the x and y coordinates of the four control points that describe the cubic bezier curve to be approximated using the fixed mid point approximation. The curve will be approximated using four quadratic bezier curves the points for which will be returned in a two dimensional array, with each array within that containing the points for a single quadratic curve. The returned data will not include the start point for any of the curves; the first point passed in to this method should already have been set as the current position and will be the assumed start of the first curve.
Params:
  • cubicControlPointCoords – an array containing the x and y coordinates of the four control points.
Returns:an array of arrays containing the x and y coordinates of the quadratic curves that approximate the original supplied cubic bezier curve.
/** * This method will take in an array containing the x and y coordinates of the four control * points that describe the cubic bezier curve to be approximated using the fixed mid point * approximation. The curve will be approximated using four quadratic bezier curves the points * for which will be returned in a two dimensional array, with each array within that containing * the points for a single quadratic curve. The returned data will not include the start point * for any of the curves; the first point passed in to this method should already have been * set as the current position and will be the assumed start of the first curve. * * @param cubicControlPointCoords an array containing the x and y coordinates of the * four control points. * @return an array of arrays containing the x and y coordinates of the quadratic curves * that approximate the original supplied cubic bezier curve. */
public static double[][] fixedMidPointApproximation(double[] cubicControlPointCoords) { if (cubicControlPointCoords.length < 8) { throw new IllegalArgumentException("Must have at least 8 coordinates"); } //extract point objects from source array Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]); Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]); Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]); Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]); //calculates the useful base points Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0); Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0); //get 1/16 of the [P3, P0] segment double dx = (p3.getX() - p0.getX()) / 16.0; double dy = (p3.getY() - p0.getY()) / 16.0; //calculates control point 1 Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0); //calculates control point 2 Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0); pc2 = movePoint(pc2, -dx, -dy); //calculates control point 3 Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0); pc3 = movePoint(pc3, dx, dy); //calculates control point 4 Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0); //calculates the 3 anchor points Point2D pa1 = getMidPoint(pc1, pc2); Point2D pa2 = getMidPoint(pa, pb); Point2D pa3 = getMidPoint(pc3, pc4); //return the points for the four quadratic curves return new double[][] { {pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()}, {pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()}, {pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()}, {pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}}; } private static Double movePoint(Point2D point, double dx, double dy) { return new Point2D.Double(point.getX() + dx, point.getY() + dy); }
This method will calculate the coordinates of a point half way along a segment [P0, P1]
Params:
  • p0 – - The point describing the start of the segment.
  • p1 – - The point describing the end of the segment.
Returns:a Point object describing the coordinates of the calculated point on the segment.
/** * This method will calculate the coordinates of a point half way along a segment [P0, P1] * * @param p0 - The point describing the start of the segment. * @param p1 - The point describing the end of the segment. * @return a Point object describing the coordinates of the calculated point on the segment. */
private static Point2D getMidPoint(Point2D p0, Point2D p1) { return getPointOnSegment(p0, p1, 0.5); }
This method will calculate the coordinates of a point on a segment [P0, P1] whose distance along the segment [P0, P1] from P0, is the given ratio of the length the [P0, P1] segment.
Params:
  • p0 – The point describing the start of the segment.
  • p1 – The point describing the end of the segment.
  • ratio – The distance of the point being calculated from P0 as a ratio of the segment length.
Returns:a Point object describing the coordinates of the calculated point on the segment.
/** * This method will calculate the coordinates of a point on a segment [P0, P1] * whose distance along the segment [P0, P1] from P0, is the given ratio * of the length the [P0, P1] segment. * * @param p0 The point describing the start of the segment. * @param p1 The point describing the end of the segment. * @param ratio The distance of the point being calculated from P0 as a ratio of * the segment length. * @return a Point object describing the coordinates of the calculated point on the segment. */
private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) { double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio); double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio); return new Point2D.Double(x, y); } }