package com.kreative.paint.material.shape;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ParameterizedPath extends ParameterizedShape {
private final List<Character> opcodes;
private final List<List<ParameterizedValue>> operands;
public ParameterizedPath() {
this.opcodes = new ArrayList<Character>();
this.operands = new ArrayList<List<ParameterizedValue>>();
}
public void add(char opcode, ParameterizedValue... operands) {
List<ParameterizedValue> operandsIntl = new ArrayList<ParameterizedValue>();
if (operands != null) operandsIntl.addAll(Arrays.asList(operands));
operandsIntl = Collections.unmodifiableList(operandsIntl);
this.operands.add(operandsIntl);
this.opcodes.add(opcode);
}
public void add(char opcode, List<ParameterizedValue> operands) {
List<ParameterizedValue> operandsIntl = new ArrayList<ParameterizedValue>();
if (operands != null) operandsIntl.addAll(operands);
operandsIntl = Collections.unmodifiableList(operandsIntl);
this.operands.add(operandsIntl);
this.opcodes.add(opcode);
}
public void clear() {
this.opcodes.clear();
this.operands.clear();
}
public char getOpcode(int index) {
return this.opcodes.get(index);
}
public List<ParameterizedValue> getOperands(int index) {
return this.operands.get(index);
}
public boolean isEmpty() {
return this.opcodes.isEmpty() && this.operands.isEmpty();
}
public int size() {
return Math.min(this.opcodes.size(), this.operands.size());
}
public static int operandCount(char opcode) {
switch (opcode) {
case 'Z': case 'z':
return 0;
case 'H': case 'h':
case 'V': case 'v':
return 1;
case 'M': case 'm':
case 'L': case 'l':
case 'T': case 't':
return 2;
case 'Q': case 'q':
case 'S': case 's':
case 'G': case 'g':
return 4;
case 'C': case 'c':
case 'R': case 'r':
return 6;
case 'A': case 'a':
case 'E': case 'e':
return 7;
default:
return -1;
}
}
@Override
public Shape awtShape(Bindings bindings) {
GeneralPath p = new GeneralPath();
double lcx = 0.0, lcy = 0.0, lx = 0.0, ly = 0.0;
double ccx, ccy, arx, ary, aa;
boolean large, sweep;
double rx, ry, rw, rh, rrx, rry, ras, rae;
int rat;
for (int i = 0, n = size(); i < n; i++) {
char inst = getOpcode(i);
List<ParameterizedValue> args = getOperands(i);
switch (inst) {
case 'M':
lcx = lx = args.get(0).value(bindings);
lcy = ly = args.get(1).value(bindings);
p.moveTo(lx, ly);
break;
case 'm':
lcx = lx += args.get(0).value(bindings);
lcy = ly += args.get(1).value(bindings);
p.moveTo(lx, ly);
break;
case 'H':
lcx = lx = args.get(0).value(bindings);
p.lineTo(lx, lcy = ly);
break;
case 'h':
lcx = lx += args.get(0).value(bindings);
p.lineTo(lx, lcy = ly);
break;
case 'V':
lcy = ly = args.get(0).value(bindings);
p.lineTo(lcx = lx, ly);
break;
case 'v':
lcy = ly += args.get(0).value(bindings);
p.lineTo(lcx = lx, ly);
break;
case 'L':
lcx = lx = args.get(0).value(bindings);
lcy = ly = args.get(1).value(bindings);
p.lineTo(lx, ly);
break;
case 'l':
lcx = lx += args.get(0).value(bindings);
lcy = ly += args.get(1).value(bindings);
p.lineTo(lx, ly);
break;
case 'Q':
lcx = args.get(0).value(bindings);
lcy = args.get(1).value(bindings);
lx = args.get(2).value(bindings);
ly = args.get(3).value(bindings);
p.quadTo(lcx, lcy, lx, ly);
break;
case 'q':
lcx = lx + args.get(0).value(bindings);
lcy = ly + args.get(1).value(bindings);
lx += args.get(2).value(bindings);
ly += args.get(3).value(bindings);
p.quadTo(lcx, lcy, lx, ly);
break;
case 'T':
lcx = lx + lx - lcx;
lcy = ly + ly - lcy;
lx = args.get(0).value(bindings);
ly = args.get(1).value(bindings);
p.quadTo(lcx, lcy, lx, ly);
break;
case 't':
lcx = lx + lx - lcx;
lcy = ly + ly - lcy;
lx += args.get(0).value(bindings);
ly += args.get(1).value(bindings);
p.quadTo(lcx, lcy, lx, ly);
break;
case 'C':
ccx = args.get(0).value(bindings);
ccy = args.get(1).value(bindings);
lcx = args.get(2).value(bindings);
lcy = args.get(3).value(bindings);
lx = args.get(4).value(bindings);
ly = args.get(5).value(bindings);
p.curveTo(ccx, ccy, lcx, lcy, lx, ly);
break;
case 'c':
ccx = lx + args.get(0).value(bindings);
ccy = ly + args.get(1).value(bindings);
lcx = lx + args.get(2).value(bindings);
lcy = ly + args.get(3).value(bindings);
lx += args.get(4).value(bindings);
ly += args.get(5).value(bindings);
p.curveTo(ccx, ccy, lcx, lcy, lx, ly);
break;
case 'S':
ccx = lx + lx - lcx;
ccy = ly + ly - lcy;
lcx = args.get(0).value(bindings);
lcy = args.get(1).value(bindings);
lx = args.get(2).value(bindings);
ly = args.get(3).value(bindings);
p.curveTo(ccx, ccy, lcx, lcy, lx, ly);
break;
case 's':
ccx = lx + lx - lcx;
ccy = ly + ly - lcy;
lcx = lx + args.get(0).value(bindings);
lcy = ly + args.get(1).value(bindings);
lx += args.get(2).value(bindings);
ly += args.get(3).value(bindings);
p.curveTo(ccx, ccy, lcx, lcy, lx, ly);
break;
case 'A':
arx = args.get(0).value(bindings);
ary = args.get(1).value(bindings);
aa = args.get(2).value(bindings);
large = (args.get(3).value(bindings) != 0);
sweep = (args.get(4).value(bindings) != 0);
lcx = lx = args.get(5).value(bindings);
lcy = ly = args.get(6).value(bindings);
arcTo(p, arx, ary, aa, large, sweep, lx, ly);
break;
case 'a':
arx = args.get(0).value(bindings);
ary = args.get(1).value(bindings);
aa = args.get(2).value(bindings);
large = (args.get(3).value(bindings) != 0);
sweep = (args.get(4).value(bindings) != 0);
lcx = lx += args.get(5).value(bindings);
lcy = ly += args.get(6).value(bindings);
arcTo(p, arx, ary, aa, large, sweep, lx, ly);
break;
case 'Z':
case 'z':
p.closePath();
lcx = lx = p.getCurrentPoint().getX();
lcy = ly = p.getCurrentPoint().getY();
break;
case 'G':
ccx = args.get(0).value(bindings);
ccy = args.get(1).value(bindings);
lcx = lx = args.get(2).value(bindings);
lcy = ly = args.get(3).value(bindings);
coArcTo(p, ccx, ccy, lx, ly);
break;
case 'g':
ccx = lx + args.get(0).value(bindings);
ccy = ly + args.get(1).value(bindings);
lcx = lx += args.get(2).value(bindings);
lcy = ly += args.get(3).value(bindings);
coArcTo(p, ccx, ccy, lx, ly);
break;
case 'R':
rx = args.get(0).value(bindings);
ry = args.get(1).value(bindings);
rw = args.get(2).value(bindings);
rh = args.get(3).value(bindings);
rrx = args.get(4).value(bindings);
rry = args.get(5).value(bindings);
if (rrx == 0 || rry == 0) {
p.append(new Rectangle2D.Double(rx, ry, rw, rh), false);
} else {
p.append(new RoundRectangle2D.Double(rx, ry, rw, rh, rrx, rry), false);
}
p.moveTo(lcx = lx, lcy = ly);
break;
case 'r':
rx = lx + args.get(0).value(bindings);
ry = ly + args.get(1).value(bindings);
rw = args.get(2).value(bindings);
rh = args.get(3).value(bindings);
rrx = args.get(4).value(bindings);
rry = args.get(5).value(bindings);
if (rrx == 0 || rry == 0) {
p.append(new Rectangle2D.Double(rx, ry, rw, rh), false);
} else {
p.append(new RoundRectangle2D.Double(rx, ry, rw, rh, rrx, rry), false);
}
p.moveTo(lcx = lx, lcy = ly);
break;
case 'E':
rx = args.get(0).value(bindings);
ry = args.get(1).value(bindings);
rw = args.get(2).value(bindings);
rh = args.get(3).value(bindings);
ras = args.get(4).value(bindings);
rae = args.get(5).value(bindings);
rat = (Math.abs((int)Math.round(args.get(6).value(bindings))) % 5);
if (rae <= -360 || rae >= 360) {
p.append(new Ellipse2D.Double(rx, ry, rw, rh), false);
p.moveTo(lcx = lx, lcy = ly);
} else if (rat < 3) {
p.append(new Arc2D.Double(rx, ry, rw, rh, ras, rae, rat), false);
p.moveTo(lcx = lx, lcy = ly);
} else {
p.append(new Arc2D.Double(rx, ry, rw, rh, ras, rae, Arc2D.OPEN), rat > 3);
Point2D cp = p.getCurrentPoint();
lcx = lx = cp.getX();
lcy = ly = cp.getY();
}
break;
case 'e':
rx = lx + args.get(0).value(bindings);
ry = ly + args.get(1).value(bindings);
rw = args.get(2).value(bindings);
rh = args.get(3).value(bindings);
ras = args.get(4).value(bindings);
rae = args.get(5).value(bindings);
rat = (Math.abs((int)Math.round(args.get(6).value(bindings))) % 5);
if (rae <= -360 || rae >= 360) {
p.append(new Ellipse2D.Double(rx, ry, rw, rh), false);
p.moveTo(lcx = lx, lcy = ly);
} else if (rat < 3) {
p.append(new Arc2D.Double(rx, ry, rw, rh, ras, rae, rat), false);
p.moveTo(lcx = lx, lcy = ly);
} else {
p.append(new Arc2D.Double(rx, ry, rw, rh, ras, rae, Arc2D.OPEN), rat > 3);
Point2D cp = p.getCurrentPoint();
lcx = lx = cp.getX();
lcy = ly = cp.getY();
}
break;
}
}
return p;
}
@Override
public boolean equals(Object that) {
if (that instanceof ParameterizedPath) {
List<Character> thisOpcodes = this.opcodes;
List<List<ParameterizedValue>> thisOperands = this.operands;
List<Character> thatOpcodes = ((ParameterizedPath)that).opcodes;
List<List<ParameterizedValue>> thatOperands = ((ParameterizedPath)that).operands;
return thisOpcodes.equals(thatOpcodes) && thisOperands.equals(thatOperands);
} else {
return false;
}
}
@Override
public int hashCode() {
return opcodes.hashCode() ^ operands.hashCode();
}
private static void arcTo(
GeneralPath p, double rx, double ry, double a,
boolean large, boolean sweep, double x, double y
) {
Point2D p0 = p.getCurrentPoint();
double x0 = p0.getX();
double y0 = p0.getY();
if (x0 == x && y0 == y) return;
if (rx == 0 || ry == 0) { p.lineTo(x, y); return; }
double dx2 = (x0 - x) / 2;
double dy2 = (y0 - y) / 2;
a = Math.toRadians(a % 360);
double ca = Math.cos(a);
double sa = Math.sin(a);
double x1 = sa * dy2 + ca * dx2;
double y1 = ca * dy2 - sa * dx2;
rx = Math.abs(rx);
ry = Math.abs(ry);
double Prx = rx * rx;
double Pry = ry * ry;
double Px1 = x1 * x1;
double Py1 = y1 * y1;
double rc = Px1/Prx + Py1/Pry;
if (rc > 1) {
rx = Math.sqrt(rc) * rx;
ry = Math.sqrt(rc) * ry;
Prx = rx * rx;
Pry = ry * ry;
}
double s = (large == sweep) ? -1 : 1;
double sq = ((Prx*Pry)-(Prx*Py1)-(Pry*Px1)) / ((Prx*Py1)+(Pry*Px1));
if (sq < 0) sq = 0;
double m = s * Math.sqrt(sq);
double cx1 = m * ((rx * y1) / ry);
double cy1 = m * -((ry * x1) / rx);
double sx2 = (x0 + x) / 2;
double sy2 = (y0 + y) / 2;
double cx = sx2 + ca * cx1 - sa * cy1;
double cy = sy2 + sa * cx1 + ca * cy1;
double ux = (x1 - cx1) / rx;
double uy = (y1 - cy1) / ry;
double vx = (-x1 -cx1) / rx;
double vy = (-y1 -cy1) / ry;
double sn = Math.sqrt(ux*ux + uy*uy);
double sp = ux;
double ss = (uy < 0) ? -1 : 1;
double as = Math.toDegrees(ss * Math.acos(sp / sn));
double en = Math.sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy));
double ep = ux * vx + uy * vy;
double es = (ux * vy - uy * vx < 0) ? -1 : 1;
double ae = Math.toDegrees(es * Math.acos(ep / en));
if (!sweep && ae > 0) ae -= 360;
if (sweep && ae < 0) ae += 360;
ae %= 360;
as %= 360;
Arc2D.Double arc = new Arc2D.Double();
arc.x = cx - rx;
arc.y = cy - ry;
arc.width = rx * 2;
arc.height = ry * 2;
arc.start = -as;
arc.extent = -ae;
double acx = arc.getCenterX();
double acy = arc.getCenterY();
AffineTransform t = AffineTransform.getRotateInstance(a, acx, acy);
p.append(t.createTransformedShape(arc), true);
}
private static void coArcTo(GeneralPath p, double x2, double y2, double x3, double y3) {
Point2D p1 = p.getCurrentPoint();
double x1 = p1.getX();
double y1 = p1.getY();
boolean xe = (x1 == x2 && x2 == x3);
boolean ye = (y1 == y2 && y2 == y3);
if (xe && ye) return;
if (xe || ye) { p.lineTo(x3, y3); return; }
double d = arcHK(x1, y1, x2, y2, x3, y3);
double h = arcH(x1, y1, x2, y2, x3, y3) / d;
double k = arcK(x1, y1, x2, y2, x3, y3) / d;
if (Double.isNaN(h) || Double.isInfinite(h)) { p.lineTo(x3, y3); return; }
if (Double.isNaN(k) || Double.isInfinite(k)) { p.lineTo(x3, y3); return; }
double r = Math.hypot(k - y1, x1 - h);
double a1 = Math.toDegrees(Math.atan2(k - y1, x1 - h));
double a2 = Math.toDegrees(Math.atan2(k - y2, x2 - h));
double a3 = Math.toDegrees(Math.atan2(k - y3, x3 - h));
Arc2D.Double arc = new Arc2D.Double();
arc.x = h - r;
arc.y = k - r;
arc.width = r + r;
arc.height = r + r;
arc.start = a1;
if ((a1 <= a2 && a2 <= a3) || (a3 <= a2 && a2 <= a1)) {
arc.extent = a3 - a1;
} else if (a3 <= a1) {
arc.extent = a3 - a1 + 360;
} else {
arc.extent = a3 - a1 - 360;
}
p.append(arc, true);
}
private static double arcdet(double a, double b, double c, double d, double e, double f, double g, double h, double i) {
return a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g;
}
private static double arcHK(double x1, double y1, double x2, double y2, double x3, double y3) {
return arcdet(x1, y1, 1, x2, y2, 1, x3, y3, 1) * 2;
}
private static double arcH(double x1, double y1, double x2, double y2, double x3, double y3) {
return arcdet(x1*x1 + y1*y1, y1, 1, x2*x2 + y2*y2, y2, 1, x3*x3 + y3*y3, y3, 1);
}
private static double arcK(double x1, double y1, double x2, double y2, double x3, double y3) {
return arcdet(x1, x1*x1 + y1*y1, 1, x2, x2*x2 + y2*y2, 1, x3, x3*x3 + y3*y3, 1);
}
}