|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.awt.geom; |
|
|
|
import java.awt.geom.Rectangle2D; |
|
import java.awt.geom.PathIterator; |
|
import java.awt.geom.QuadCurve2D; |
|
import java.util.Vector; |
|
|
|
final class Order3 extends Curve { |
|
private double x0; |
|
private double y0; |
|
private double cx0; |
|
private double cy0; |
|
private double cx1; |
|
private double cy1; |
|
private double x1; |
|
private double y1; |
|
|
|
private double xmin; |
|
private double xmax; |
|
|
|
private double xcoeff0; |
|
private double xcoeff1; |
|
private double xcoeff2; |
|
private double xcoeff3; |
|
|
|
private double ycoeff0; |
|
private double ycoeff1; |
|
private double ycoeff2; |
|
private double ycoeff3; |
|
|
|
public static void insert(Vector curves, double tmp[], |
|
double x0, double y0, |
|
double cx0, double cy0, |
|
double cx1, double cy1, |
|
double x1, double y1, |
|
int direction) |
|
{ |
|
int numparams = getHorizontalParams(y0, cy0, cy1, y1, tmp); |
|
if (numparams == 0) { |
|
// We are using addInstance here to avoid inserting horisontal |
|
|
|
addInstance(curves, x0, y0, cx0, cy0, cx1, cy1, x1, y1, direction); |
|
return; |
|
} |
|
|
|
tmp[3] = x0; tmp[4] = y0; |
|
tmp[5] = cx0; tmp[6] = cy0; |
|
tmp[7] = cx1; tmp[8] = cy1; |
|
tmp[9] = x1; tmp[10] = y1; |
|
double t = tmp[0]; |
|
if (numparams > 1 && t > tmp[1]) { |
|
|
|
tmp[0] = tmp[1]; |
|
tmp[1] = t; |
|
t = tmp[0]; |
|
} |
|
split(tmp, 3, t); |
|
if (numparams > 1) { |
|
|
|
t = (tmp[1] - t) / (1 - t); |
|
split(tmp, 9, t); |
|
} |
|
int index = 3; |
|
if (direction == DECREASING) { |
|
index += numparams * 6; |
|
} |
|
while (numparams >= 0) { |
|
addInstance(curves, |
|
tmp[index + 0], tmp[index + 1], |
|
tmp[index + 2], tmp[index + 3], |
|
tmp[index + 4], tmp[index + 5], |
|
tmp[index + 6], tmp[index + 7], |
|
direction); |
|
numparams--; |
|
if (direction == INCREASING) { |
|
index += 6; |
|
} else { |
|
index -= 6; |
|
} |
|
} |
|
} |
|
|
|
public static void addInstance(Vector curves, |
|
double x0, double y0, |
|
double cx0, double cy0, |
|
double cx1, double cy1, |
|
double x1, double y1, |
|
int direction) { |
|
if (y0 > y1) { |
|
curves.add(new Order3(x1, y1, cx1, cy1, cx0, cy0, x0, y0, |
|
-direction)); |
|
} else if (y1 > y0) { |
|
curves.add(new Order3(x0, y0, cx0, cy0, cx1, cy1, x1, y1, |
|
direction)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static int getHorizontalParams(double c0, double cp0, |
|
double cp1, double c1, |
|
double ret[]) { |
|
if (c0 <= cp0 && cp0 <= cp1 && cp1 <= c1) { |
|
return 0; |
|
} |
|
c1 -= cp1; |
|
cp1 -= cp0; |
|
cp0 -= c0; |
|
ret[0] = cp0; |
|
ret[1] = (cp1 - cp0) * 2; |
|
ret[2] = (c1 - cp1 - cp1 + cp0); |
|
int numroots = QuadCurve2D.solveQuadratic(ret, ret); |
|
int j = 0; |
|
for (int i = 0; i < numroots; i++) { |
|
double t = ret[i]; |
|
|
|
if (t > 0 && t < 1) { |
|
if (j < i) { |
|
ret[j] = t; |
|
} |
|
j++; |
|
} |
|
} |
|
return j; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static void split(double coords[], int pos, double t) { |
|
double x0, y0, cx0, cy0, cx1, cy1, x1, y1; |
|
coords[pos+12] = x1 = coords[pos+6]; |
|
coords[pos+13] = y1 = coords[pos+7]; |
|
cx1 = coords[pos+4]; |
|
cy1 = coords[pos+5]; |
|
x1 = cx1 + (x1 - cx1) * t; |
|
y1 = cy1 + (y1 - cy1) * t; |
|
x0 = coords[pos+0]; |
|
y0 = coords[pos+1]; |
|
cx0 = coords[pos+2]; |
|
cy0 = coords[pos+3]; |
|
x0 = x0 + (cx0 - x0) * t; |
|
y0 = y0 + (cy0 - y0) * t; |
|
cx0 = cx0 + (cx1 - cx0) * t; |
|
cy0 = cy0 + (cy1 - cy0) * t; |
|
cx1 = cx0 + (x1 - cx0) * t; |
|
cy1 = cy0 + (y1 - cy0) * t; |
|
cx0 = x0 + (cx0 - x0) * t; |
|
cy0 = y0 + (cy0 - y0) * t; |
|
coords[pos+2] = x0; |
|
coords[pos+3] = y0; |
|
coords[pos+4] = cx0; |
|
coords[pos+5] = cy0; |
|
coords[pos+6] = cx0 + (cx1 - cx0) * t; |
|
coords[pos+7] = cy0 + (cy1 - cy0) * t; |
|
coords[pos+8] = cx1; |
|
coords[pos+9] = cy1; |
|
coords[pos+10] = x1; |
|
coords[pos+11] = y1; |
|
} |
|
|
|
public Order3(double x0, double y0, |
|
double cx0, double cy0, |
|
double cx1, double cy1, |
|
double x1, double y1, |
|
int direction) |
|
{ |
|
super(direction); |
|
// REMIND: Better accuracy in the root finding methods would |
|
// ensure that cys are in range. As it stands, they are never |
|
|
|
if (cy0 < y0) cy0 = y0; |
|
if (cy1 > y1) cy1 = y1; |
|
this.x0 = x0; |
|
this.y0 = y0; |
|
this.cx0 = cx0; |
|
this.cy0 = cy0; |
|
this.cx1 = cx1; |
|
this.cy1 = cy1; |
|
this.x1 = x1; |
|
this.y1 = y1; |
|
xmin = Math.min(Math.min(x0, x1), Math.min(cx0, cx1)); |
|
xmax = Math.max(Math.max(x0, x1), Math.max(cx0, cx1)); |
|
xcoeff0 = x0; |
|
xcoeff1 = (cx0 - x0) * 3.0; |
|
xcoeff2 = (cx1 - cx0 - cx0 + x0) * 3.0; |
|
xcoeff3 = x1 - (cx1 - cx0) * 3.0 - x0; |
|
ycoeff0 = y0; |
|
ycoeff1 = (cy0 - y0) * 3.0; |
|
ycoeff2 = (cy1 - cy0 - cy0 + y0) * 3.0; |
|
ycoeff3 = y1 - (cy1 - cy0) * 3.0 - y0; |
|
YforT1 = YforT2 = YforT3 = y0; |
|
} |
|
|
|
public int getOrder() { |
|
return 3; |
|
} |
|
|
|
public double getXTop() { |
|
return x0; |
|
} |
|
|
|
public double getYTop() { |
|
return y0; |
|
} |
|
|
|
public double getXBot() { |
|
return x1; |
|
} |
|
|
|
public double getYBot() { |
|
return y1; |
|
} |
|
|
|
public double getXMin() { |
|
return xmin; |
|
} |
|
|
|
public double getXMax() { |
|
return xmax; |
|
} |
|
|
|
public double getX0() { |
|
return (direction == INCREASING) ? x0 : x1; |
|
} |
|
|
|
public double getY0() { |
|
return (direction == INCREASING) ? y0 : y1; |
|
} |
|
|
|
public double getCX0() { |
|
return (direction == INCREASING) ? cx0 : cx1; |
|
} |
|
|
|
public double getCY0() { |
|
return (direction == INCREASING) ? cy0 : cy1; |
|
} |
|
|
|
public double getCX1() { |
|
return (direction == DECREASING) ? cx0 : cx1; |
|
} |
|
|
|
public double getCY1() { |
|
return (direction == DECREASING) ? cy0 : cy1; |
|
} |
|
|
|
public double getX1() { |
|
return (direction == DECREASING) ? x0 : x1; |
|
} |
|
|
|
public double getY1() { |
|
return (direction == DECREASING) ? y0 : y1; |
|
} |
|
|
|
private double TforY1; |
|
private double YforT1; |
|
private double TforY2; |
|
private double YforT2; |
|
private double TforY3; |
|
private double YforT3; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public double TforY(double y) { |
|
if (y <= y0) return 0; |
|
if (y >= y1) return 1; |
|
if (y == YforT1) return TforY1; |
|
if (y == YforT2) return TforY2; |
|
if (y == YforT3) return TforY3; |
|
|
|
if (ycoeff3 == 0.0) { |
|
|
|
return Order2.TforY(y, ycoeff0, ycoeff1, ycoeff2); |
|
} |
|
double a = ycoeff2 / ycoeff3; |
|
double b = ycoeff1 / ycoeff3; |
|
double c = (ycoeff0 - y) / ycoeff3; |
|
int roots = 0; |
|
double Q = (a * a - 3.0 * b) / 9.0; |
|
double R = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0; |
|
double R2 = R * R; |
|
double Q3 = Q * Q * Q; |
|
double a_3 = a / 3.0; |
|
double t; |
|
if (R2 < Q3) { |
|
double theta = Math.acos(R / Math.sqrt(Q3)); |
|
Q = -2.0 * Math.sqrt(Q); |
|
t = refine(a, b, c, y, Q * Math.cos(theta / 3.0) - a_3); |
|
if (t < 0) { |
|
t = refine(a, b, c, y, |
|
Q * Math.cos((theta + Math.PI * 2.0)/ 3.0) - a_3); |
|
} |
|
if (t < 0) { |
|
t = refine(a, b, c, y, |
|
Q * Math.cos((theta - Math.PI * 2.0)/ 3.0) - a_3); |
|
} |
|
} else { |
|
boolean neg = (R < 0.0); |
|
double S = Math.sqrt(R2 - Q3); |
|
if (neg) { |
|
R = -R; |
|
} |
|
double A = Math.pow(R + S, 1.0 / 3.0); |
|
if (!neg) { |
|
A = -A; |
|
} |
|
double B = (A == 0.0) ? 0.0 : (Q / A); |
|
t = refine(a, b, c, y, (A + B) - a_3); |
|
} |
|
if (t < 0) { |
|
|
|
double t0 = 0; |
|
double t1 = 1; |
|
while (true) { |
|
t = (t0 + t1) / 2; |
|
if (t == t0 || t == t1) { |
|
break; |
|
} |
|
double yt = YforT(t); |
|
if (yt < y) { |
|
t0 = t; |
|
} else if (yt > y) { |
|
t1 = t; |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
if (t >= 0) { |
|
TforY3 = TforY2; |
|
YforT3 = YforT2; |
|
TforY2 = TforY1; |
|
YforT2 = YforT1; |
|
TforY1 = t; |
|
YforT1 = y; |
|
} |
|
return t; |
|
} |
|
|
|
public double refine(double a, double b, double c, |
|
double target, double t) |
|
{ |
|
if (t < -0.1 || t > 1.1) { |
|
return -1; |
|
} |
|
double y = YforT(t); |
|
double t0, t1; |
|
if (y < target) { |
|
t0 = t; |
|
t1 = 1; |
|
} else { |
|
t0 = 0; |
|
t1 = t; |
|
} |
|
double origt = t; |
|
double origy = y; |
|
boolean useslope = true; |
|
while (y != target) { |
|
if (!useslope) { |
|
double t2 = (t0 + t1) / 2; |
|
if (t2 == t0 || t2 == t1) { |
|
break; |
|
} |
|
t = t2; |
|
} else { |
|
double slope = dYforT(t, 1); |
|
if (slope == 0) { |
|
useslope = false; |
|
continue; |
|
} |
|
double t2 = t + ((target - y) / slope); |
|
if (t2 == t || t2 <= t0 || t2 >= t1) { |
|
useslope = false; |
|
continue; |
|
} |
|
t = t2; |
|
} |
|
y = YforT(t); |
|
if (y < target) { |
|
t0 = t; |
|
} else if (y > target) { |
|
t1 = t; |
|
} else { |
|
break; |
|
} |
|
} |
|
boolean verbose = false; |
|
if (false && t >= 0 && t <= 1) { |
|
y = YforT(t); |
|
long tdiff = diffbits(t, origt); |
|
long ydiff = diffbits(y, origy); |
|
long yerr = diffbits(y, target); |
|
if (yerr > 0 || (verbose && tdiff > 0)) { |
|
System.out.println("target was y = "+target); |
|
System.out.println("original was y = "+origy+", t = "+origt); |
|
System.out.println("final was y = "+y+", t = "+t); |
|
System.out.println("t diff is "+tdiff); |
|
System.out.println("y diff is "+ydiff); |
|
System.out.println("y error is "+yerr); |
|
double tlow = prev(t); |
|
double ylow = YforT(tlow); |
|
double thi = next(t); |
|
double yhi = YforT(thi); |
|
if (Math.abs(target - ylow) < Math.abs(target - y) || |
|
Math.abs(target - yhi) < Math.abs(target - y)) |
|
{ |
|
System.out.println("adjacent y's = ["+ylow+", "+yhi+"]"); |
|
} |
|
} |
|
} |
|
return (t > 1) ? -1 : t; |
|
} |
|
|
|
public double XforY(double y) { |
|
if (y <= y0) { |
|
return x0; |
|
} |
|
if (y >= y1) { |
|
return x1; |
|
} |
|
return XforT(TforY(y)); |
|
} |
|
|
|
public double XforT(double t) { |
|
return (((xcoeff3 * t) + xcoeff2) * t + xcoeff1) * t + xcoeff0; |
|
} |
|
|
|
public double YforT(double t) { |
|
return (((ycoeff3 * t) + ycoeff2) * t + ycoeff1) * t + ycoeff0; |
|
} |
|
|
|
public double dXforT(double t, int deriv) { |
|
switch (deriv) { |
|
case 0: |
|
return (((xcoeff3 * t) + xcoeff2) * t + xcoeff1) * t + xcoeff0; |
|
case 1: |
|
return ((3 * xcoeff3 * t) + 2 * xcoeff2) * t + xcoeff1; |
|
case 2: |
|
return (6 * xcoeff3 * t) + 2 * xcoeff2; |
|
case 3: |
|
return 6 * xcoeff3; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
public double dYforT(double t, int deriv) { |
|
switch (deriv) { |
|
case 0: |
|
return (((ycoeff3 * t) + ycoeff2) * t + ycoeff1) * t + ycoeff0; |
|
case 1: |
|
return ((3 * ycoeff3 * t) + 2 * ycoeff2) * t + ycoeff1; |
|
case 2: |
|
return (6 * ycoeff3 * t) + 2 * ycoeff2; |
|
case 3: |
|
return 6 * ycoeff3; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
public double nextVertical(double t0, double t1) { |
|
double eqn[] = {xcoeff1, 2 * xcoeff2, 3 * xcoeff3}; |
|
int numroots = QuadCurve2D.solveQuadratic(eqn, eqn); |
|
for (int i = 0; i < numroots; i++) { |
|
if (eqn[i] > t0 && eqn[i] < t1) { |
|
t1 = eqn[i]; |
|
} |
|
} |
|
return t1; |
|
} |
|
|
|
public void enlarge(Rectangle2D r) { |
|
r.add(x0, y0); |
|
double eqn[] = {xcoeff1, 2 * xcoeff2, 3 * xcoeff3}; |
|
int numroots = QuadCurve2D.solveQuadratic(eqn, eqn); |
|
for (int i = 0; i < numroots; i++) { |
|
double t = eqn[i]; |
|
if (t > 0 && t < 1) { |
|
r.add(XforT(t), YforT(t)); |
|
} |
|
} |
|
r.add(x1, y1); |
|
} |
|
|
|
public Curve getSubCurve(double ystart, double yend, int dir) { |
|
if (ystart <= y0 && yend >= y1) { |
|
return getWithDirection(dir); |
|
} |
|
double eqn[] = new double[14]; |
|
double t0, t1; |
|
t0 = TforY(ystart); |
|
t1 = TforY(yend); |
|
eqn[0] = x0; |
|
eqn[1] = y0; |
|
eqn[2] = cx0; |
|
eqn[3] = cy0; |
|
eqn[4] = cx1; |
|
eqn[5] = cy1; |
|
eqn[6] = x1; |
|
eqn[7] = y1; |
|
if (t0 > t1) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
double t = t0; |
|
t0 = t1; |
|
t1 = t; |
|
} |
|
if (t1 < 1) { |
|
split(eqn, 0, t1); |
|
} |
|
int i; |
|
if (t0 <= 0) { |
|
i = 0; |
|
} else { |
|
split(eqn, 0, t0 / t1); |
|
i = 6; |
|
} |
|
return new Order3(eqn[i+0], ystart, |
|
eqn[i+2], eqn[i+3], |
|
eqn[i+4], eqn[i+5], |
|
eqn[i+6], yend, |
|
dir); |
|
} |
|
|
|
public Curve getReversedCurve() { |
|
return new Order3(x0, y0, cx0, cy0, cx1, cy1, x1, y1, -direction); |
|
} |
|
|
|
public int getSegment(double coords[]) { |
|
if (direction == INCREASING) { |
|
coords[0] = cx0; |
|
coords[1] = cy0; |
|
coords[2] = cx1; |
|
coords[3] = cy1; |
|
coords[4] = x1; |
|
coords[5] = y1; |
|
} else { |
|
coords[0] = cx1; |
|
coords[1] = cy1; |
|
coords[2] = cx0; |
|
coords[3] = cy0; |
|
coords[4] = x0; |
|
coords[5] = y0; |
|
} |
|
return PathIterator.SEG_CUBICTO; |
|
} |
|
|
|
public String controlPointString() { |
|
return (("("+round(getCX0())+", "+round(getCY0())+"), ")+ |
|
("("+round(getCX1())+", "+round(getCY1())+"), ")); |
|
} |
|
} |