/*
* @(#)SimplifiedPathIterator.java
*
* $Date: 2011-05-02 16:01:45 -0500 (Mon, 02 May 2011) $
*
* Copyright (c) 2011 by Jeremy Wood.
* All rights reserved.
*
* The copyright of this software is owned by Jeremy Wood.
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* Jeremy Wood. For details see accompanying license terms.
*
* This software is probably, but not necessarily, discussed here:
* http://javagraphics.java.net/
*
* That site should also contain the most recent official version
* of this software. (See the SVN repository for more details.)
*/
package com.bric.geom;
import java.awt.geom.PathIterator;
/** This filters a <code>PathIterator</code> and makes sure
* that each curve is of the smallest possible degree.
* <P>In addition to being more efficient, this can avoid
* divide-by-zero errors for some operations.
*
*/
public class SimplifiedPathIterator implements PathIterator {
/** This is the tolerance a term can be to be considered "zero".
* This value is .0001.
*/
final static private double TOL = .0001;
PathIterator i;
double lastX, lastY;
/** Creates a new SimplifiedPathIterator that filters
* the argument.
*
* @param i
*/
public SimplifiedPathIterator(PathIterator i) {
this.i = i;
}
/** Returns true if all 3 points are on the same line.
* This checks to see if:
* <BR><code>x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2)<TOL*TOL</code>
*
* @param x1 the x-coordinate of point #1
* @param y1 the y-coordinate of point #1
* @param x2 the x-coordinate of point #2
* @param y2 the y-coordinate of point #2
* @param x3 the x-coordinate of point #3
* @param y3 the y-coordinate of point #3
* @return true if all 3 points are on the same line.
*/
public static boolean collinear(double x1,double y1,double x2,double y2,double x3,double y3) {
double determinant =
x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2);
return Math.abs(determinant)<TOL*TOL;
}
private static double[] doubleArray = new double[6];
/** This possibly reduces the degree of a segment, if possible.
*
* @param type the current expected type of the segment data
* @param lastX the previous X value from which this segment begins
* @param lastY the previous Y value from which this segment begins
* @param data the data of the current segment.
* @return the new segment type, or the original <code>type</code>
* argument if nothing was modified.
*/
public static int simplify(int type,float lastX,float lastY,float[] data) {
synchronized(doubleArray) {
for(int a = 0; a<data.length; a++) {
doubleArray[a] = data[a];
}
int returnValue = simplify(type, lastX, lastY, doubleArray);
for(int a = 0; a<data.length; a++) {
data[a] = (float)doubleArray[a];
}
return returnValue;
}
}
/** This possibly reduces the degree of a segment, if possible.
*
* @param type the current expected type of the segment data
* @param lastX the previous X value from which this segment begins
* @param lastY the previous Y value from which this segment begins
* @param data the data of the current segment.
* @return the new segment type, or the original <code>type</code>
* argument if nothing was modified.
*/
public static int simplify(int type,double lastX,double lastY,double[] data) {
if(type==SEG_CUBICTO) {
if(collinear(lastX,lastY,data[4],data[5],data[0],data[1])
&& collinear(lastX,lastY,data[4],data[5],data[2],data[3])) {
data[0] = data[4];
data[1] = data[5];
return SEG_LINETO;
}
double ax = -lastX + 3 * data[0] - 3 * data[2] + data[4];
double ay = -lastY + 3 * data[1] - 3 * data[3] + data[5];
if(Math.abs(ax)<.000001 && Math.abs(ay)<.000001) {
double bx = 3 * lastX - 6 * data[0] + 3 * data[2];
double cx = -3 * lastX + 3 * data[0];
//double dx = lastX;
double by = 3 * lastY - 6 * data[1] + 3 * data[3];
double cy = -3 * lastY + 3 * data[1];
//double dy = lastY;
data[1] = (cy+2*lastY)/2.0;
data[3] = by-lastY+2*data[1];
data[0] = (cx+2*lastX)/2.0;
data[2] = bx-lastX+2*data[0];
return simplify(PathIterator.SEG_QUADTO, lastX, lastY, data);
}
} else if(type==SEG_QUADTO) {
if(collinear(lastX,lastY,data[2],data[3],data[0],data[1])) {
data[0] = data[2];
data[1] = data[3];
return SEG_LINETO;
}
double ax = lastX - 2 * data[0] + data[2];
double ay = lastY - 2 * data[1] + data[3];
if(Math.abs(ax)<.000001 && Math.abs(ay)<.000001) {
double bx = -2 * lastX + 2 * data[0];
//double cx = lastX;
double by = -2 * lastY + 2 * data[1];
//double cy = lastY;
data[0] = (bx+2*lastX)/2.0;
data[1] = (by+2*lastY)/2.0;
return PathIterator.SEG_LINETO;
}
}
return type;
}
public int currentSegment(double[] f) {
int type = i.currentSegment(f);
type = simplify(type,lastX,lastY,f);
if(type==PathIterator.SEG_LINETO || type==PathIterator.SEG_MOVETO) {
lastX = f[0];
lastY = f[1];
} else if(type==PathIterator.SEG_QUADTO) {
lastX = f[2];
lastY = f[3];
} else if(type==PathIterator.SEG_CUBICTO) {
lastX = f[4];
lastY = f[5];
}
return type;
}
private double[] d;
public int currentSegment(float[] f) {
if(d==null) {
d = new double[6];
}
int k = currentSegment(d);
f[0] = (float)d[0];
f[1] = (float)d[1];
f[2] = (float)d[2];
f[3] = (float)d[3];
f[4] = (float)d[4];
f[5] = (float)d[5];
return k;
}
public int getWindingRule() {
return i.getWindingRule();
}
public boolean isDone() {
return i.isDone();
}
public void next() {
i.next();
}
}