/*
* $Id: CubicSegment.java,v 1.2 2004/12/27 04:56:02 eed3si9n Exp $
*
* $Copyright: copyright (c) 2004, e.e d3si9n $
* $License:
* This source code is part of DoubleType.
* DoubleType is a graphical typeface designer.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, e.e d3si9n gives permission to
* link the code of this program with any Java Platform that is available
* to public with free of charge, including but not limited to
* Sun Microsystem's JAVA(TM) 2 RUNTIME ENVIRONMENT (J2RE),
* and distribute linked combinations including the two.
* You must obey the GNU General Public License in all respects for all
* of the code used other than Java Platform. If you modify this file,
* you may extend this exception to your version of the file, but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version.
* $
*/
package org.doubletype.ossa.adapter;
import java.awt.geom.Point2D;
import java.util.ArrayList;
/**
* @author e.e
*/
public class CubicSegment {
public static final int LINE = 0;
public static final int CURVE = 1;
/**
* converts cubic contour into cubic segments.
*
* @param a_contour
* @return
*/
public static ArrayList<CubicSegment> toSegments(EContour a_contour) {
ArrayList<CubicSegment> retval = new ArrayList<>();
ArrayList<EContourPoint> points = a_contour.getContourPoints();
if (points.size() < 2) {
return retval;
}
EContourPoint startPoint = (EContourPoint) points.get(points.size() - 1);
for (int i = 0; i < points.size(); i++) {
EContourPoint endPoint = (EContourPoint) points.get(i);
retval.add(new CubicSegment(startPoint, endPoint));
startPoint = endPoint;
} // for
return retval;
}
private EContourPoint m_startPoint = null;
private EContourPoint m_controlPoint1 = null;
private EContourPoint m_controlPoint2 = null;
private EContourPoint m_endPoint = null;
private int m_type = LINE;
public CubicSegment(EContourPoint a_startPoint, EContourPoint a_endPoint) {
m_startPoint = a_startPoint;
m_endPoint = a_endPoint;
if ((!a_startPoint.hasControlPoint2()) && (!a_endPoint.hasControlPoint1())) {
m_type = LINE;
return;
}
if (a_startPoint.hasControlPoint2() || a_endPoint.hasControlPoint1()) {
m_type = CURVE;
if (a_startPoint.hasControlPoint2()) {
m_controlPoint1 = a_startPoint.getControlPoint2().getContourPoint();
} else {
m_controlPoint1 = a_startPoint;
}
if (a_endPoint.hasControlPoint1()) {
m_controlPoint2 = a_endPoint.getControlPoint1().getContourPoint();
} else {
m_controlPoint1 = a_endPoint;
}
}
}
public CubicSegment(EContourPoint a_startPoint, EContourPoint a_controlPoint1,
EContourPoint a_controlPoint2, EContourPoint a_endPoint) {
m_startPoint = a_startPoint;
m_controlPoint1 = a_controlPoint1;
m_controlPoint2 = a_controlPoint2;
m_endPoint = a_endPoint;
m_type = CURVE;
}
public ArrayList<QuadraticSegment> toQuadraticSegments() {
ArrayList<QuadraticSegment> retval = new ArrayList<>();
if (m_type == LINE) {
retval.add(new QuadraticSegment(m_startPoint, null, m_endPoint));
return retval;
}
return toQuadraticSegments(0);
}
//JPEXS start
private static Point2D.Double movePoint(Point2D point, double dx, double dy) {
return new Point2D.Double(point.getX() + dx, point.getY() + dy);
}
private static Point2D getMidPoint(Point2D p0, Point2D p1) {
return getPointOnSegment(p0, p1, 0.5);
}
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);
}
private static double[][] approximateCubic(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()}};
}
//JPEXS end
private ArrayList<QuadraticSegment> toQuadraticSegments(int a_trial) {
ArrayList<QuadraticSegment> retval = new ArrayList<>();
double[][] quadCoords = approximateCubic(new double[]{m_startPoint.getX(), m_startPoint.getY(), m_controlPoint1.getX(), m_controlPoint1.getY(), m_controlPoint2.getX(), m_controlPoint2.getY(), m_endPoint.getX(), m_endPoint.getY()});
EContourPoint lastPoint = m_startPoint;
for (int i = 0; i < quadCoords.length; i++) {
retval.add(new QuadraticSegment(
lastPoint,
new EContourPoint(quadCoords[i][0], quadCoords[i][1], true),
new EContourPoint(quadCoords[i][2], quadCoords[i][3], false)));
lastPoint = new EContourPoint(quadCoords[i][2], quadCoords[i][3], true);
}
return retval;
}
}