/*
* $Id: EllipseArc.java 341 2012-06-18 14:38:54Z blowagie $
*
* This file is part of the iText (R) project.
* Copyright (c) 1998-2012 1T3XT BVBA
* Authors: VVB, Bruno Lowagie, et al.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
* 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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 Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA, 02110-1301 USA, or download the license from the following URL:
* http://itextpdf.com/terms-of-use/
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* In accordance with Section 7(b) of the GNU Affero General Public License,
* a covered work must retain the producer line in every PDF that is created
* or manipulated using iText.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the iText software without
* disclosing the source code of your own applications.
* These activities include: offering paid services to customers as an ASP,
* serving PDFs on the fly in a web application, shipping iText with a closed
* source product.
*
* For more information, please contact iText Software Corp. at this
* address: sales@itextpdf.com
*/
package com.itextpdf.tool.xml.svg.utils;
import com.itextpdf.tool.xml.svg.exceptions.EllipseArcException;
public class EllipseArc {
//TODO write an test with all the possible cases
//TODO check all the doubles and floats
//creates an arc defined by two points and the two axes of the ellipse
private final float cx, cy, a, b, startAng, extend;
public float getCx() {
return cx;
}
public float getCy() {
return cy;
}
public float getA() {
return a;
}
public float getB() {
return b;
}
public float getStartAng() {
return startAng;
}
public float getExtend() {
return extend;
}
public static EllipseArc createEllipseArc(float x1, float y1, float x2, float y2, float a, float b, float sweep, float largeArc) throws EllipseArcException{
return getEllipse(x1, y1, x2, y2, a, b, (int)sweep, (int)largeArc);
}
private static double calculateAngle(double x, double y, double cx, double cy, double a, double b){
double result = Math.pow(((x - cx) / a) , 2.0) + Math.pow(((y - cy) / b) , 2.0);
double cos = (x - cx) / a;
double sin = (y - cy) / b;
if(cos >= 0 && sin >= 0){
result = Math.toDegrees(Math.acos(cos)); //I
}
if(cos >= 0 && sin < 0){
result = 360 - Math.toDegrees(Math.acos(cos)); //IV
}
if(cos < 0 && sin >= 0){
result = Math.toDegrees(Math.acos(cos)); // II
}
if(cos < 0 && sin < 0){
result = 360 - Math.toDegrees(Math.acos(cos)); //III
}
return result;
}
private static EllipseArc getEllipse (float x1, float y1, float x2, float y2, float a, float b, int sweep, int largeArc) throws EllipseArcException{
double r1 = (x1 - x2) / (-2.0 * a);
double r2 = (y1 - y2) / (2.0 * b);
if(Math.sqrt(r1*r1 + r2*r2) > 1){
throw new EllipseArcException("the two given points are not on the ellipse");
}
double tussen1 = Math.asin(Math.sqrt(r1*r1 + r2*r2));
double tussen2 = Math.atan(r1 / r2);
EllipseArc result = calculatePossibleMiddle(x1, y1, x2, y2, a, b, tussen1, tussen2, sweep, largeArc);
if(result != null) return result;
result = calculatePossibleMiddle(x1, y1, x2, y2, a, b, Math.PI - tussen1, tussen2, sweep, largeArc);
if(result != null) return result;
result = calculatePossibleMiddle(x1, y1, x2, y2, a, b, tussen1, tussen2 + Math.PI, sweep, largeArc);
if(result != null) return result;
result = calculatePossibleMiddle(x1, y1, x2, y2, a, b, Math.PI - tussen1, tussen2 + Math.PI, sweep, largeArc);
if(result != null) return result;
return null;
}
//TODO check all the boundary cases
private static EllipseArc calculatePossibleMiddle(double x1, double y1, double x2, double y2, double a, double b, double t1, double t2, int sweep, int largeArc){
float[] result = new float[2];
double x0 = x1 - a * Math.cos(t1+t2);
double y0 = y1 - b * Math.sin(t1+t2);
double check = (Math.pow(((x2-x0)/a), 2)+Math.pow(((y2-y0)/b), 2));
//System.out.println(result);
if (check - 1 < 0.0000000001){
result[0] = (float) x0;
result[1] = (float) y0;
double theta1 = calculateAngle(x1, y1, x0, y0, a, b);
double theta2= calculateAngle(x2, y2, x0, y0, a, b);
double startAngl = 0;
double extend = 0;
//both points are on the ellipse, but is this the middle, looked for?
if(largeArc == 1){ //turn more than 180 degrees
if((theta2 > theta1) && (theta2 - theta1 > 180) && sweep == 1){
startAngl = theta1;
extend = theta2 - theta1;
}
if((theta1 > theta2) && (theta1 - theta2 <= 180) && sweep == 1){
startAngl = theta1;
extend = 360 - theta1 + theta2;
}
if((theta2 > theta1) && (theta2 - theta1 <= 180) && sweep == 0){
startAngl = theta2;
extend = 360 - theta2 + theta1; //or the same extend but negative and start at p1
}
if((theta1 > theta2) && (theta1 - theta2 > 180) && sweep == 0){
startAngl = theta2;
extend = theta1 - theta2;
}
}else{
if((theta2 > theta1) && (theta2 - theta1 <= 180) && sweep == 1){
startAngl = theta1;
extend = theta2 - theta1;
}
if((theta1 > theta2) && (theta1 - theta2 > 180) && sweep == 1){
startAngl = theta1;
extend = 360 - theta1 + theta2;
}
if((theta2 > theta1) && (theta2 - theta1 > 180) && sweep == 0){
startAngl = theta2;
extend = 360 - theta2 + theta1; //or the same extend but negative and start at p1
}
if((theta1 > theta2) && (theta1 - theta2 <= 180) && sweep == 0){
startAngl = theta2;
extend = theta1 - theta2;
}
}
if(startAngl >= 0 && extend > 0){
return new EllipseArc((float)x0, (float)y0, (float)a, (float)b, (float)startAngl, (float)extend);
}
}
return null;
}
protected EllipseArc(final float cx, final float cy, final float a, final float b, final float startAng, final float extend){
this.cx = cx;
this.cy = cy;
this.a = a;
this.b = b;
this.startAng = startAng;
this.extend = extend;
}
}