mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-18 22:18:09 +00:00
Optimized speed of Shape fixer
This commit is contained in:
@@ -67,6 +67,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param points Points
|
||||
*/
|
||||
public BezierEdge(List<Point2D> points) {
|
||||
@@ -81,6 +82,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Is the edge empty?
|
||||
*
|
||||
* @return True if the edge is empty, false otherwise.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
@@ -89,6 +91,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param x0 Start x
|
||||
* @param y0 Start y
|
||||
* @param x1 End x
|
||||
@@ -102,6 +105,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param x0 Start x
|
||||
* @param y0 Start y
|
||||
* @param cx Control x
|
||||
@@ -118,6 +122,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Gets the begin point.
|
||||
*
|
||||
* @return Begin point
|
||||
*/
|
||||
public Point2D getBeginPoint() {
|
||||
@@ -126,6 +131,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Gets the end point.
|
||||
*
|
||||
* @return End point
|
||||
*/
|
||||
public Point2D getEndPoint() {
|
||||
@@ -134,6 +140,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the begin point.
|
||||
*
|
||||
* @param p Begin point
|
||||
*/
|
||||
public void setBeginPoint(Point2D p) {
|
||||
@@ -143,6 +150,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the end point.
|
||||
*
|
||||
* @param p End point
|
||||
*/
|
||||
public void setEndPoint(Point2D p) {
|
||||
@@ -152,6 +160,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Gets the point at specified position.
|
||||
*
|
||||
* @param t Position
|
||||
* @return Point at position
|
||||
*/
|
||||
@@ -172,26 +181,31 @@ public class BezierEdge implements Serializable {
|
||||
}
|
||||
|
||||
private void calcParams() {
|
||||
double minX = Double.MAX_VALUE;
|
||||
double minY = Double.MAX_VALUE;
|
||||
double maxX = -Double.MAX_VALUE;
|
||||
double maxY = -Double.MAX_VALUE;
|
||||
for (Point2D p : points) {
|
||||
if (p.getX() < minX) {
|
||||
minX = p.getX();
|
||||
}
|
||||
if (p.getX() > maxX) {
|
||||
maxX = p.getX();
|
||||
}
|
||||
if (p.getY() < minY) {
|
||||
minY = p.getY();
|
||||
}
|
||||
if (p.getY() > maxY) {
|
||||
maxY = p.getY();
|
||||
if (points.size() == 2) {
|
||||
double minX = Double.MAX_VALUE;
|
||||
double minY = Double.MAX_VALUE;
|
||||
double maxX = -Double.MAX_VALUE;
|
||||
double maxY = -Double.MAX_VALUE;
|
||||
for (Point2D p : points) {
|
||||
if (p.getX() < minX) {
|
||||
minX = p.getX();
|
||||
}
|
||||
if (p.getX() > maxX) {
|
||||
maxX = p.getX();
|
||||
}
|
||||
if (p.getY() < minY) {
|
||||
minY = p.getY();
|
||||
}
|
||||
if (p.getY() > maxY) {
|
||||
maxY = p.getY();
|
||||
}
|
||||
}
|
||||
|
||||
this.bbox = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
|
||||
} else {
|
||||
this.bbox = quadraticBBox(points.get(0), points.get(1), points.get(2));
|
||||
}
|
||||
|
||||
this.bbox = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
|
||||
this.hash = points.hashCode();
|
||||
|
||||
revPoints = new ArrayList<>();
|
||||
@@ -212,6 +226,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Calculates the bounding box.
|
||||
*
|
||||
* @return Bounding box
|
||||
*/
|
||||
public Rectangle2D bbox() {
|
||||
@@ -222,6 +237,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Calculates the area.
|
||||
*
|
||||
* @return Area
|
||||
*/
|
||||
public double area() {
|
||||
@@ -258,6 +274,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Gets the intersections.
|
||||
*
|
||||
* @param b2 Bezier edge
|
||||
* @return List of intersections
|
||||
*/
|
||||
@@ -282,6 +299,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Gets the intersections. Old version.
|
||||
*
|
||||
* @param b2 Bezier edge 2
|
||||
* @param t1Ref T1 reference
|
||||
* @param t2Ref T2 reference
|
||||
@@ -329,6 +347,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Checks if the edges intersect.
|
||||
*
|
||||
* @param b2 Bezier edge 2
|
||||
* @param t1Ref T1 reference
|
||||
* @param t2Ref T2 reference
|
||||
@@ -420,6 +439,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Gets the length.
|
||||
*
|
||||
* @return Length
|
||||
*/
|
||||
public double length() {
|
||||
@@ -456,6 +476,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Converts the edge to SVG.
|
||||
*
|
||||
* @return SVG string
|
||||
*/
|
||||
public String toSvg() {
|
||||
@@ -488,6 +509,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Splits the edge.
|
||||
*
|
||||
* @param t Position
|
||||
* @param left Left edge
|
||||
* @param right Right edge
|
||||
@@ -502,9 +524,30 @@ public class BezierEdge implements Serializable {
|
||||
right.setVal(new BezierEdge(rightPoints));
|
||||
}
|
||||
|
||||
public List<BezierEdge> split(List<Double> tList) {
|
||||
List<BezierEdge> result = new ArrayList<>();
|
||||
|
||||
double prevT = 0.0;
|
||||
BezierEdge remaining = this;
|
||||
|
||||
for (double t : tList) {
|
||||
double localT = (t - prevT) / (1.0 - prevT);
|
||||
|
||||
Reference<BezierEdge> leftRef = new Reference<>(null);
|
||||
Reference<BezierEdge> rightRef = new Reference<>(null);
|
||||
remaining.split(localT, leftRef, rightRef);
|
||||
result.add(leftRef.getVal());
|
||||
remaining = rightRef.getVal();
|
||||
prevT = t;
|
||||
}
|
||||
|
||||
result.add(remaining);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the edge.
|
||||
*
|
||||
* @return Reversed edge
|
||||
*/
|
||||
public BezierEdge reverse() {
|
||||
@@ -536,9 +579,9 @@ public class BezierEdge implements Serializable {
|
||||
}
|
||||
calcParams();
|
||||
}
|
||||
|
||||
public static final double ROUND_VALUE = 1;
|
||||
|
||||
|
||||
public static final double ROUND_VALUE = 2;
|
||||
|
||||
public void roundX() {
|
||||
for (int i = 0; i < this.points.size(); i++) {
|
||||
this.points.set(i, new Point2D.Double(
|
||||
@@ -576,6 +619,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
/**
|
||||
* Checks if the edge is equal to another edge in reverse order.
|
||||
*
|
||||
* @param other Other edge
|
||||
* @return True if the edges are equal in reverse order, false otherwise
|
||||
*/
|
||||
@@ -594,163 +638,12 @@ public class BezierEdge implements Serializable {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test main.
|
||||
* @param args Arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
List<Double> t1 = new ArrayList<>();
|
||||
List<Double> t2 = new ArrayList<>();
|
||||
/*/BezierEdge be1 = new BezierEdge(0,0,100,100);
|
||||
BezierEdge be2 = new BezierEdge(75,100,75,0);
|
||||
|
||||
System.out.println("lines "+ be1+ " and "+be2);
|
||||
|
||||
|
||||
|
||||
|
||||
System.out.println("hasIntersection = "+be1.intersects(be2, t1, t2));
|
||||
System.out.println("intersection points: "+t1+", "+t2);
|
||||
|
||||
|
||||
BezierEdge be3 = new BezierEdge(0,0,100,0);
|
||||
BezierEdge be4 = new BezierEdge(0,100,100,100);
|
||||
|
||||
System.out.println("lines "+ be3+ " and "+be4);
|
||||
|
||||
System.out.println("hasIntersection = "+be3.intersects(be4, t1, t2));
|
||||
System.out.println("intersection points: "+t1+", "+t2);*/
|
||||
|
||||
BezierEdge be5 = new BezierEdge(25, 0, 25, 100);
|
||||
BezierEdge be6 = new BezierEdge(0, 50, 100, 50);
|
||||
|
||||
//System.err.println("lines " + be5 + " and " + be6);
|
||||
BezierEdge q1 = new BezierEdge(3469, 3124, 3320, 3148, 3355, 3215);
|
||||
BezierEdge q2 = new BezierEdge(3442, 3191, 3316, 3146, 3317, 3071);
|
||||
BezierEdge q3 = new BezierEdge(3310, 3222, 3450, 3172, 3300, 3181);
|
||||
BezierEdge li = new BezierEdge(3423, 3040, 3277, 3164);
|
||||
BezierEdge li2 = new BezierEdge(3399, 3095, 3365, 3039);
|
||||
|
||||
List<Point2D> ints;
|
||||
/*ints = q2.getIntersections(q1);
|
||||
System.err.println("intersections is "+ints);
|
||||
ints = q2.getIntersections(li);
|
||||
System.err.println("intersections is "+ints);
|
||||
ints = li2.getIntersections(li);
|
||||
System.err.println("intersections is "+ints);
|
||||
ints = q1.getIntersections(q3);
|
||||
System.err.println("intersections is "+ints);*/
|
||||
|
||||
BezierEdge qa = new BezierEdge(-81.0, -78.0, -85.0, -76.0, -86.0, -66.0);
|
||||
BezierEdge qb = new BezierEdge(-166.0, 37.0, -172.0, -21.0, -81.0, -78.0);
|
||||
/*ints = qa.getIntersections(qb);
|
||||
System.err.println("intersections is " + ints);
|
||||
BezierEdge qc = new BezierEdge(-106.0,39.0, -104.0,33.0);
|
||||
BezierEdge qd = new BezierEdge(-104.0,33.0,-105.0,36.0,-102.0,26.0);
|
||||
ints = qc.getIntersections(qd);
|
||||
System.err.println("intersections is " + ints);
|
||||
|
||||
ints = Intersections.intersectLineLine(new Point2D.Double(0,0), new Point2D.Double(10,0), new Point2D.Double(2,0), new Point2D.Double(5,0), true);
|
||||
System.err.println("intersections is " + ints);
|
||||
BezierEdge qe = new BezierEdge(-104,33,-104.5,35,-104,32);
|
||||
BezierEdge qf = new BezierEdge(-106,39 ,-104,33);
|
||||
ints = qe.getIntersections(qf);*/
|
||||
BezierEdge qg = new BezierEdge(-66, 139, -67, 140, -61, 135);
|
||||
BezierEdge qh = new BezierEdge(-64, 169, -66.5, 139.5, -66, 139);
|
||||
|
||||
//Error Y values of bounds must be of opposite sign
|
||||
BezierEdge qi = new BezierEdge(6369.0, 13040.0, 6380.0, 13030.0, 6427.0, 13018.0);
|
||||
BezierEdge qj = new BezierEdge(6338.0, 13099.0, 6358.0, 13050.0, 6369.0, 13040.0);
|
||||
|
||||
//Error Y values of bounds must be of opposite sign
|
||||
BezierEdge qk = new BezierEdge(45334.0, 2421.0, 45348.0, 2373.0, 45348.0, 2330.0);
|
||||
BezierEdge ql = new BezierEdge(45348.0, 2330.0, 45348.0, 2263.0, 45314.0, 2223.0);
|
||||
|
||||
BezierEdge qm = new BezierEdge(-1957, 2676, -1957, 2676.5, -1957.5, 2676.5);
|
||||
BezierEdge qn = new BezierEdge(-1957, 2675.5, -1957, 2676, -1957, 2676);
|
||||
|
||||
/*List<Point2D> ps = new ArrayList<>();
|
||||
qm.intersects(qn, t1, t2, ps);
|
||||
System.err.println("t1 is " + t1);
|
||||
System.err.println("t2 is " + t2);
|
||||
System.err.println("intersections is " + ps);
|
||||
*/
|
||||
/*Shape r1 = new Rectangle2D.Double(0, 0, 200, 100);
|
||||
GeneralPath r2 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
r2.moveTo(0, 0);
|
||||
r2.lineTo(100, 0);
|
||||
r2.lineTo(150, 0);
|
||||
r2.lineTo(200, 0);
|
||||
r2.lineTo(200, 100);
|
||||
r2.lineTo(0, 100);
|
||||
r2.closePath();
|
||||
|
||||
GeneralPath sh1 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
sh1.moveTo(0, 0);
|
||||
sh1.quadTo(100, 100, 0, 100);
|
||||
sh1.lineTo(0, 0);
|
||||
sh1.closePath();
|
||||
|
||||
BezierEdge bex = new BezierEdge(0, 0, 100, 100, 0, 100);
|
||||
Reference<BezierEdge> bex1Ref = new Reference<>(null);
|
||||
Reference<BezierEdge> bex2Ref = new Reference<>(null);
|
||||
bex.split(0.2, bex1Ref, bex2Ref);
|
||||
BezierEdge bex1 = bex1Ref.getVal();
|
||||
BezierEdge bex2 = bex2Ref.getVal();
|
||||
GeneralPath sh2 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
sh2.moveTo(bex1.getBeginPoint().getX(), bex1.getBeginPoint().getY());
|
||||
sh2.quadTo(10+bex1.points.get(1).getX(), bex1.points.get(1).getY(), bex1.getEndPoint().getX(), bex1.getEndPoint().getY());
|
||||
sh2.quadTo(bex2.points.get(1).getX(), bex2.points.get(1).getY(), bex2.getEndPoint().getX(), bex2.getEndPoint().getY());
|
||||
sh2.lineTo(0, 0);
|
||||
sh2.closePath();
|
||||
|
||||
Area a1 = new Area(r1);
|
||||
Area a2 = new Area(r2);
|
||||
//a1.exclusiveOr(a2);
|
||||
|
||||
|
||||
*/
|
||||
/*GeneralPath p1 = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
p1.moveTo(0, 0);
|
||||
p1.lineTo(200, 0);
|
||||
p1.lineTo(200, 200);
|
||||
p1.lineTo(0, 200);
|
||||
p1.lineTo(0, 0);
|
||||
p1.closePath();
|
||||
p1.moveTo(50, 50);
|
||||
p1.lineTo(150, 50);
|
||||
p1.lineTo(150, 150);
|
||||
p1.lineTo(50, 150);
|
||||
p1.lineTo(50, 50);
|
||||
p1.closePath();
|
||||
|
||||
System.err.println("cont:" + p1.contains(100, 100));
|
||||
System.err.println("cont:" + p1.contains(150, 150)); */
|
||||
//System.err.println("minDist = " + minDist+", maxDist = "+ maxDist);
|
||||
//System.err.println("eArea = " + Areas.calcArea(a1));
|
||||
|
||||
/*Point2D c = new Point2D.Double(
|
||||
(1 - t1.get(0)) * be5.points.get(0).getX() + t1.get(0) * be5.points.get(1).getX(),
|
||||
(1 - t1.get(0)) * be5.points.get(0).getY() + t1.get(0) * be5.points.get(1).getY()
|
||||
);
|
||||
|
||||
System.out.println("Intersection point: " + c);
|
||||
|
||||
BezierEdge be = new BezierEdge(0, 0, 100, 50, 0, 100);
|
||||
|
||||
System.out.println("be5.dist: " + be.length());*/
|
||||
//Rectangle2D out = new Rectangle2D.Double();
|
||||
//rectIntersection(new Rectangle2D.Double(0,0,50,50), new Rectangle2D.Double(0,50,50,50), out);
|
||||
//System.out.println("out = "+out);
|
||||
}
|
||||
|
||||
|
||||
public void shrinkToLine() {
|
||||
if (points.size() == 3) {
|
||||
double det = (points.get(1).getX() - points.get(0).getX())
|
||||
* (points.get(2).getY() - points.get(0).getY())
|
||||
- (points.get(1).getY() - points.get(0).getY())
|
||||
* (points.get(2).getX() - points.get(0).getX());
|
||||
double det = (points.get(1).getX() - points.get(0).getX())
|
||||
* (points.get(2).getY() - points.get(0).getY())
|
||||
- (points.get(1).getY() - points.get(0).getY())
|
||||
* (points.get(2).getX() - points.get(0).getX());
|
||||
if (det == 0) {
|
||||
points.remove(1);
|
||||
revPoints.remove(1);
|
||||
@@ -758,4 +651,175 @@ public class BezierEdge implements Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Double findXCriticalT() {
|
||||
double EPS = 1e-12;
|
||||
|
||||
if (points.size() < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// dx/dt = 2 * ((x1 - x0) + t * (x2 - 2*x1 + x0))
|
||||
final double a = (points.get(2).getX() - 2.0 * points.get(1).getX() + points.get(0).getX()); // coefficient of t
|
||||
final double b = (points.get(1).getX() - points.get(0).getX()); // constant term
|
||||
|
||||
// If a ~ 0, dx/dt is (almost) constant -> x(t) is (almost) linear -> x-monotone already.
|
||||
if (Math.abs(a) < EPS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double t = -b / a; // (x0 - x1) / (x0 - 2*x1 + x2)
|
||||
|
||||
// Accept strictly inside (0,1); relax by EPS to be robust.
|
||||
if (t > EPS && t < 1.0 - EPS) {
|
||||
return t;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double quad(double p0, double p1, double p2, double t) {
|
||||
double u = 1.0 - t;
|
||||
return u * u * p0 + 2.0 * u * t * p1 + t * t * p2;
|
||||
}
|
||||
|
||||
public double yAt(double x) {
|
||||
double EPS = 1e-12;
|
||||
if (points.size() == 2) {
|
||||
//line
|
||||
double x0 = points.get(0).getX();
|
||||
double y0 = points.get(0).getY();
|
||||
double x1 = points.get(1).getX();
|
||||
double y1 = points.get(1).getY();
|
||||
double dx = x1 - x0;
|
||||
|
||||
// Guard for near-vertical segments: return stable representative y
|
||||
if (Math.abs(dx) < EPS) {
|
||||
return (y0 + y1) * 0.5;
|
||||
}
|
||||
|
||||
double t = (x - x0) / dx;
|
||||
if (t < 0) {
|
||||
t = 0;
|
||||
} else if (t > 1) {
|
||||
t = 1;
|
||||
}
|
||||
return y0 + t * (y1 - y0);
|
||||
}
|
||||
//quad
|
||||
final double A = (points.get(0).getX() - 2.0 * points.get(1).getX() + points.get(2).getX());
|
||||
final double B = 2.0 * (points.get(1).getX() - points.get(0).getX());
|
||||
final double C = points.get(0).getX() - x; // solve A t^2 + B t + C = 0
|
||||
|
||||
double t;
|
||||
if (Math.abs(A) < EPS) {
|
||||
t = (Math.abs(B) < EPS) ? 0.5 : (-C / B);
|
||||
} else {
|
||||
double disc = B * B - 4.0 * A * C;
|
||||
if (disc < 0 && disc > -1e-14) {
|
||||
disc = 0;
|
||||
}
|
||||
if (disc < 0) {
|
||||
t = 0.5;
|
||||
} else {
|
||||
double sqrtD = Math.sqrt(disc);
|
||||
double q = -0.5 * (B + Math.copySign(sqrtD, B));
|
||||
double t1 = q / A;
|
||||
double t2 = (Math.abs(q) < EPS) ? t1 : (C / q);
|
||||
boolean t1In = t1 > -1e-9 && t1 < 1.0 + 1e-9;
|
||||
boolean t2In = t2 > -1e-9 && t2 < 1.0 + 1e-9;
|
||||
if (t1In && !t2In) {
|
||||
t = t1;
|
||||
} else if (!t1In && t2In) {
|
||||
t = t2;
|
||||
} else if (t1In && t2In) {
|
||||
t = (Math.abs(t1 - 0.5) < Math.abs(t2 - 0.5)) ? t1 : t2;
|
||||
} else {
|
||||
double c1 = Math.min(Math.max(t1, 0.0), 1.0);
|
||||
double c2 = Math.min(Math.max(t2, 0.0), 1.0);
|
||||
double x1 = quad(points.get(0).getX(), points.get(1).getX(), points.get(2).getX(), c1);
|
||||
double x2 = quad(points.get(0).getX(), points.get(1).getX(), points.get(2).getX(), c2);
|
||||
t = (Math.abs(x1 - x) <= Math.abs(x2 - x)) ? c1 : c2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t < 0) {
|
||||
t = 0;
|
||||
} else if (t > 1) {
|
||||
t = 1;
|
||||
}
|
||||
return quad(points.get(0).getY(), points.get(1).getY(), points.get(2).getY(), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute B(t) for a quadratic Bezier defined by P0, P1, P2.
|
||||
*/
|
||||
private static Point2D.Double evalQuad(Point2D p0, Point2D p1, Point2D p2, double t) {
|
||||
// All math in double; t assumed in [0,1], but we'll still accept slightly outside due to numeric noise.
|
||||
double u = 1.0 - t;
|
||||
double x = u * u * p0.getX() + 2 * u * t * p1.getX() + t * t * p2.getX();
|
||||
double y = u * u * p0.getY() + 2 * u * t * p1.getY() + t * t * p2.getY();
|
||||
return new Point2D.Double(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the bounding box of a quadratic Bezier (P0, P1, P2). We include
|
||||
* endpoints and inner extrema where the derivative in x or y equals 0.
|
||||
*/
|
||||
public static Rectangle2D quadraticBBox(Point2D p0, Point2D p1, Point2D p2) {
|
||||
// Collect candidate t values: endpoints and potential inner extrema for x and y.
|
||||
List<Double> candidates = new ArrayList<>();
|
||||
candidates.add(0.0);
|
||||
candidates.add(1.0);
|
||||
|
||||
// Derivative B'(t) = 2( (1 - t)(P1 - P0) + t(P2 - P1) )
|
||||
// Setting x' = 0 gives linear equation in t with solution:
|
||||
// t = (P0x - P1x) / (P0x - 2P1x + P2x), if denominator != 0
|
||||
double denomX = p0.getX() - 2.0 * p1.getX() + p2.getX();
|
||||
double denomY = p0.getY() - 2.0 * p1.getY() + p2.getY();
|
||||
|
||||
// Small epsilon to avoid floating point issues around 0 denominators
|
||||
final double EPS = 1e-12;
|
||||
|
||||
if (Math.abs(denomX) > EPS) {
|
||||
double tx = (p0.getX() - p1.getX()) / denomX;
|
||||
if (tx > 0.0 && tx < 1.0) {
|
||||
candidates.add(tx);
|
||||
}
|
||||
}
|
||||
if (Math.abs(denomY) > EPS) {
|
||||
double ty = (p0.getY() - p1.getY()) / denomY;
|
||||
if (ty > 0.0 && ty < 1.0) {
|
||||
candidates.add(ty);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate all candidate points and take min/max
|
||||
double minX = Double.POSITIVE_INFINITY;
|
||||
double minY = Double.POSITIVE_INFINITY;
|
||||
double maxX = Double.NEGATIVE_INFINITY;
|
||||
double maxY = Double.NEGATIVE_INFINITY;
|
||||
|
||||
for (double t : candidates) {
|
||||
// Clamp t just in case of tiny numeric drifts
|
||||
double tt = Math.max(0.0, Math.min(1.0, t));
|
||||
Point2D.Double pt = evalQuad(p0, p1, p2, tt);
|
||||
double x = pt.x;
|
||||
double y = pt.y;
|
||||
if (x < minX) {
|
||||
minX = x;
|
||||
}
|
||||
if (y < minY) {
|
||||
minY = y;
|
||||
}
|
||||
if (x > maxX) {
|
||||
maxX = x;
|
||||
}
|
||||
if (y > maxY) {
|
||||
maxY = y;
|
||||
}
|
||||
}
|
||||
|
||||
return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ public class XFLConverter {
|
||||
*/
|
||||
private final boolean DEBUG_EXPORT_LAYER_DEPTHS = false;
|
||||
|
||||
private static final DecimalFormat EDGE_DECIMAL_FORMAT = new DecimalFormat("0.##", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
|
||||
private static final DecimalFormat EDGE_DECIMAL_FORMAT = new DecimalFormat("0.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
|
||||
|
||||
private static final double SMALL_DIVISOR = 20;
|
||||
|
||||
@@ -311,9 +311,9 @@ public class XFLConverter {
|
||||
if (value % 1 == 0) {
|
||||
return "" + (int) value;
|
||||
}
|
||||
//value = Math.round(value * 1000.0) / 1000.0;
|
||||
//return EDGE_DECIMAL_FORMAT.format(value);
|
||||
return numEdgeToString(value);
|
||||
//value = Math.round(value * 2.0) / 2.0;
|
||||
return EDGE_DECIMAL_FORMAT.format(value);
|
||||
//return numEdgeToString(value);
|
||||
}
|
||||
|
||||
private static String convertShapeEdge(MATRIX mat, ShapeRecordAdvanced record, double x, double y) {
|
||||
|
||||
@@ -9,10 +9,11 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Path orientation detector.
|
||||
* Path area calculator and orientation detector.
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public final class PathOrientation {
|
||||
public final class PathArea {
|
||||
|
||||
public enum Orientation {
|
||||
CLOCKWISE, COUNTER_CLOCKWISE, DEGENERATE, OPEN_CONTOUR
|
||||
@@ -33,7 +34,7 @@ public final class PathOrientation {
|
||||
|
||||
double[] coords = new double[6];
|
||||
List<double[]> current = new ArrayList<>();
|
||||
|
||||
|
||||
double startX = 0;
|
||||
double startY = 0;
|
||||
double lastX = 0;
|
||||
@@ -94,7 +95,6 @@ public final class PathOrientation {
|
||||
hasOpen = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,9 +121,9 @@ public final class PathOrientation {
|
||||
areaRef.setVal(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
resultRef.setVal(area2 > 0 ? Orientation.CLOCKWISE : Orientation.COUNTER_CLOCKWISE);
|
||||
areaRef.setVal(Math.abs(area2/2));
|
||||
areaRef.setVal(Math.abs(area2 / 2));
|
||||
}
|
||||
|
||||
public static void orientationSingleClosed(Shape shape, Reference<Orientation> orientationRef, Reference<Double> areaRef) {
|
||||
@@ -27,9 +27,21 @@ import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Shape fixer. This will walk a shape and split crossed edges so FLA editor can
|
||||
@@ -97,6 +109,277 @@ public class ShapeFixer {
|
||||
) {
|
||||
}
|
||||
|
||||
private static class BezierEdgeWrapper {
|
||||
|
||||
BezierEdge be;
|
||||
int layer;
|
||||
int shapeIndex;
|
||||
int edgeIndex;
|
||||
|
||||
public BezierEdgeWrapper(BezierEdge be, int layer, int shapeIndex, int edgeIndex) {
|
||||
this.be = be;
|
||||
this.layer = layer;
|
||||
this.shapeIndex = shapeIndex;
|
||||
this.edgeIndex = edgeIndex;
|
||||
}
|
||||
|
||||
double minX() {
|
||||
return bbox().getMinX();
|
||||
}
|
||||
|
||||
double maxX() {
|
||||
return bbox().getMaxX();
|
||||
}
|
||||
|
||||
double minY() {
|
||||
return bbox().getMinY();
|
||||
}
|
||||
|
||||
double maxY() {
|
||||
return bbox().getMaxY();
|
||||
}
|
||||
|
||||
Rectangle2D bbox() {
|
||||
return be.bbox();
|
||||
}
|
||||
}
|
||||
|
||||
static final class Event implements Comparable<Event> {
|
||||
|
||||
final Type type;
|
||||
final double x;
|
||||
final BezierEdgeWrapper e;
|
||||
|
||||
enum Type {
|
||||
START, END
|
||||
}
|
||||
|
||||
public Event(Type type, double x, BezierEdgeWrapper e) {
|
||||
this.type = type;
|
||||
this.x = x;
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Event o) {
|
||||
int cx = Double.compare(this.x, o.x);
|
||||
if (cx != 0) {
|
||||
return cx;
|
||||
}
|
||||
int ct = this.type.ordinal() - o.type.ordinal();
|
||||
if (ct != 0) {
|
||||
return ct;
|
||||
}
|
||||
int ce = System.identityHashCode(this.e) - System.identityHashCode(o.e);
|
||||
return ce;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Sweep {
|
||||
|
||||
Map<BezierEdgeWrapper, List<Double>> splitPoints = new LinkedHashMap<>();
|
||||
final java.util.Comparator<BezierEdgeWrapper> statusCmp = (e1, e2) -> {
|
||||
int cMinY = Double.compare(e1.minY(), e2.minY());
|
||||
if (cMinY != 0) {
|
||||
return cMinY;
|
||||
}
|
||||
return Integer.compare(System.identityHashCode(e1), System.identityHashCode(e2));
|
||||
};
|
||||
final java.util.TreeSet<BezierEdgeWrapper> status = new TreeSet<>(statusCmp);
|
||||
//final java.util.Set<BezierEdgeWrapper> status = new HashSet<>();
|
||||
final java.util.PriorityQueue<Event> pq = new java.util.PriorityQueue<>();
|
||||
|
||||
// eps values for numeric robustness
|
||||
static final double EPS = 1e-9;
|
||||
|
||||
void addEdge(BezierEdgeWrapper e) {
|
||||
pq.add(new Event(Event.Type.START, e.minX(), e));
|
||||
pq.add(new Event(Event.Type.END, e.maxX(), e));
|
||||
}
|
||||
|
||||
void run() {
|
||||
//int total = pq.size();
|
||||
//int cnt = 0;
|
||||
while (!pq.isEmpty()) {
|
||||
/*if (cnt % 1000 == 0) {
|
||||
System.err.println("Percent done: " + (Math.round((cnt * 100.0 / total) * 100.0) / 100.0));
|
||||
}
|
||||
cnt++;*/
|
||||
Event ev = pq.poll();
|
||||
|
||||
switch (ev.type) {
|
||||
case START:
|
||||
BezierEdgeWrapper beMaxY = new BezierEdgeWrapper(null, 0, 0, 0) {
|
||||
@Override
|
||||
double minY() {
|
||||
return ev.e.maxY();
|
||||
}
|
||||
};
|
||||
|
||||
for (BezierEdgeWrapper e2 : status.headSet(beMaxY, true)) {
|
||||
/*if (e2.minY() > maxY) {
|
||||
break;
|
||||
}*/
|
||||
checkPair(ev.e, e2);
|
||||
}
|
||||
status.add(ev.e);
|
||||
break;
|
||||
case END:
|
||||
status.remove(ev.e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPair(BezierEdgeWrapper e1, BezierEdgeWrapper e2) {
|
||||
if (e1 == null || e2 == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Double> t1Ref = new ArrayList<>();
|
||||
List<Double> t2Ref = new ArrayList<>();
|
||||
List<Point2D> intPoint = new ArrayList<>();
|
||||
if (!e1.be.intersects(e2.be, t1Ref, t2Ref, intPoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!splitPoints.containsKey(e1)) {
|
||||
splitPoints.put(e1, new ArrayList<>());
|
||||
}
|
||||
if (!splitPoints.containsKey(e2)) {
|
||||
splitPoints.put(e2, new ArrayList<>());
|
||||
}
|
||||
splitPoints.get(e1).addAll(t1Ref);
|
||||
splitPoints.get(e2).addAll(t2Ref);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBewList(List<BezierEdgeWrapper> bewList, List<List<BezierEdge>> shapes) {
|
||||
Map<Integer, List<BezierEdgeWrapper>> bewMap = bewList.stream()
|
||||
.collect(Collectors.groupingBy(b -> b.layer));
|
||||
|
||||
for (Map.Entry<Integer, List<BezierEdgeWrapper>> entry : bewMap.entrySet()) {
|
||||
|
||||
Set<BezierEdgeWrapper> bewsToIgnore = new LinkedHashSet<>();
|
||||
|
||||
Map<BezierEdge, BezierEdgeWrapper> existingEdges = new HashMap<>();
|
||||
|
||||
//eliminate duplicates
|
||||
for (BezierEdgeWrapper bew1 : entry.getValue()) {
|
||||
BezierEdge be = bew1.be;
|
||||
BezierEdge rev = bew1.be.reverse();
|
||||
|
||||
BezierEdgeWrapper prevBew = existingEdges.get(be);
|
||||
if (prevBew != null) {
|
||||
bewsToIgnore.add(prevBew);
|
||||
}
|
||||
existingEdges.put(be, bew1);
|
||||
|
||||
BezierEdgeWrapper prevRevBew = existingEdges.get(rev);
|
||||
if (prevRevBew != null) {
|
||||
bewsToIgnore.add(prevRevBew);
|
||||
}
|
||||
existingEdges.put(rev, bew1);
|
||||
}
|
||||
|
||||
//eliminate duplicates
|
||||
/*for (BezierEdgeWrapper bew1 : entry.getValue()) {
|
||||
for (BezierEdgeWrapper bew2 : entry.getValue()) {
|
||||
if (bew1 != bew2) {
|
||||
if (bew1.beOriginal.equals(bew2.beOriginal)
|
||||
|| bew1.beOriginal.equalsReverse(bew2.beOriginal)) {
|
||||
bewsToIgnore.add(bew1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
boolean useSweep = true;
|
||||
|
||||
Map<BezierEdgeWrapper, List<Double>> splitPointsMap = new LinkedHashMap<>();
|
||||
|
||||
if (useSweep) {
|
||||
Sweep sweep = new Sweep();
|
||||
for (BezierEdgeWrapper bew : entry.getValue()) {
|
||||
if (bewsToIgnore.contains(bew)) {
|
||||
continue;
|
||||
}
|
||||
sweep.addEdge(bew);
|
||||
}
|
||||
sweep.run();
|
||||
splitPointsMap = sweep.splitPoints;
|
||||
} else {
|
||||
|
||||
for (BezierEdgeWrapper bew1 : entry.getValue()) {
|
||||
for (BezierEdgeWrapper bew2 : entry.getValue()) {
|
||||
if (bew1 != bew2) {
|
||||
List<Double> t1Ref = new ArrayList<>();
|
||||
List<Double> t2Ref = new ArrayList<>();
|
||||
List<Point2D> intPoints = new ArrayList<>();
|
||||
if (bew1.be.intersects(bew2.be, t1Ref, t2Ref, intPoints)) {
|
||||
if (!splitPointsMap.containsKey(bew1)) {
|
||||
splitPointsMap.put(bew1, new ArrayList<>());
|
||||
}
|
||||
splitPointsMap.get(bew1).addAll(t1Ref);
|
||||
|
||||
if (!splitPointsMap.containsKey(bew2)) {
|
||||
splitPointsMap.put(bew2, new ArrayList<>());
|
||||
}
|
||||
splitPointsMap.get(bew2).addAll(t2Ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<BezierEdgeWrapper> splittedBewList = new ArrayList<>(splitPointsMap.keySet());
|
||||
|
||||
splittedBewList.sort((BezierEdgeWrapper o1, BezierEdgeWrapper o2) -> {
|
||||
int dShapeIndex = o1.shapeIndex - o2.shapeIndex;
|
||||
if (dShapeIndex != 0) {
|
||||
return dShapeIndex;
|
||||
}
|
||||
int dEIndex = o1.edgeIndex - o2.edgeIndex;
|
||||
if (dEIndex != 0) {
|
||||
return dEIndex;
|
||||
}
|
||||
return System.identityHashCode(o1) - System.identityHashCode(o2);
|
||||
});
|
||||
|
||||
for (int i = splittedBewList.size() - 1; i >= 0; i--) {
|
||||
BezierEdgeWrapper bew = splittedBewList.get(i);
|
||||
|
||||
List<Double> splitT = splitPointsMap.get(bew);
|
||||
splitT.sort((a, b) -> Double.compare(a, b));
|
||||
|
||||
BezierEdge be = bew.be;
|
||||
List<Double> realSplitT = new ArrayList<>();
|
||||
for (double t : splitT) {
|
||||
if (t == 0.0 || t == 1.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
realSplitT.add(t);
|
||||
}
|
||||
|
||||
if (realSplitT.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<BezierEdge> splitted = be.split(realSplitT);
|
||||
shapes.get(bew.shapeIndex).remove(bew.edgeIndex);
|
||||
int pos = 0;
|
||||
for (BezierEdge bes : splitted) {
|
||||
bes.roundX();
|
||||
if (bes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
shapes.get(bew.shapeIndex).add(bew.edgeIndex + pos, bes);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void detectOverlappingEdges(
|
||||
List<List<BezierEdge>> shapes,
|
||||
List<Integer> fillStyles0,
|
||||
@@ -104,26 +387,79 @@ public class ShapeFixer {
|
||||
List<Integer> lineStyles,
|
||||
List<Integer> layers
|
||||
) {
|
||||
|
||||
/*if (true) {
|
||||
detectOverlappingEdgesOld(shapes, fillStyles0, fillStyles1, lineStyles, layers);
|
||||
return;
|
||||
}*/
|
||||
|
||||
List<BezierEdgeWrapper> allBewList = new ArrayList<>();
|
||||
for (int i1 = 0; i1 < shapes.size(); i1++) {
|
||||
int layer = layers.get(i1);
|
||||
for (int j1 = 0; j1 < shapes.get(i1).size(); j1++) {
|
||||
BezierEdge be = shapes.get(i1).get(j1);
|
||||
allBewList.add(new BezierEdgeWrapper(be, layer, i1, j1));
|
||||
}
|
||||
}
|
||||
|
||||
List<BezierEdgeWrapper> strokesBewList = new ArrayList<>();
|
||||
List<BezierEdgeWrapper> fillsBewList = new ArrayList<>();
|
||||
|
||||
for (BezierEdgeWrapper bew : allBewList) {
|
||||
if (fillStyles0.get(bew.shapeIndex) == 0 && fillStyles1.get(bew.shapeIndex) == 0) {
|
||||
strokesBewList.add(bew);
|
||||
} else {
|
||||
fillsBewList.add(bew);
|
||||
}
|
||||
}
|
||||
|
||||
handleBewList(strokesBewList, shapes);
|
||||
handleBewList(fillsBewList, shapes);
|
||||
|
||||
for (int i1 = 0; i1 < shapes.size(); i1++) {
|
||||
for (int j1 = 0; j1 < shapes.get(i1).size(); j1++) {
|
||||
BezierEdge be1 = shapes.get(i1).get(j1);
|
||||
be1.shrinkToLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Old unoptimized overlapping
|
||||
private void detectOverlappingEdgesOld(
|
||||
List<List<BezierEdge>> shapes,
|
||||
List<Integer> fillStyles0,
|
||||
List<Integer> fillStyles1,
|
||||
List<Integer> lineStyles,
|
||||
List<Integer> layers
|
||||
) {
|
||||
/*Map<BezierEdge, Double> splitPoints = new TreeMap<>(new Comparator<BezierEdge>() {
|
||||
@Override
|
||||
public int compare(BezierEdge o1, BezierEdge o2) {
|
||||
int dSize = o1.points.size() - o2.points.size();
|
||||
if (dSize != 0) {
|
||||
return dSize;
|
||||
}
|
||||
for (int i = 0; i < o1.points.size(); i++) {
|
||||
int dX = Double.compare(o1.points.get(i).getX(), o2.points.get(i).getX());
|
||||
if (dX != 0) {
|
||||
return dX;
|
||||
}
|
||||
int dY = Double.compare(o1.points.get(i).getY(), o2.points.get(i).getY());
|
||||
if (dY != 0) {
|
||||
return dY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
});*/
|
||||
|
||||
//------------------- detecting overlapping edges --------------------
|
||||
List<BezierPair> splittedPairs = new ArrayList<>();
|
||||
|
||||
//long t1 = System.currentTimeMillis();
|
||||
//int oldpct = 0;
|
||||
loopi1:
|
||||
for (int i1 = 0; i1 < shapes.size(); i1++) {
|
||||
int layer = layers.get(i1);
|
||||
/*if (i1 % 10 == 0) {
|
||||
int pct = i1 * 100 / shapes.size();
|
||||
if (oldpct != pct) {
|
||||
//System.err.println("pct: " + pct + "%");
|
||||
if (pct == 10) {
|
||||
long t2 = System.currentTimeMillis();
|
||||
long dt = t2 - t1;
|
||||
System.err.println("Time per 10%: " + Helper.formatTimeSec(dt));
|
||||
}
|
||||
}
|
||||
oldpct = pct;
|
||||
}*/
|
||||
|
||||
loopj1:
|
||||
for (int j1 = 0; j1 < shapes.get(i1).size(); j1++) {
|
||||
@@ -229,16 +565,6 @@ public class ShapeFixer {
|
||||
}
|
||||
}
|
||||
|
||||
/*if (t1Ref.size() == 2) {
|
||||
if (intPoints.get(0).distance(intPoints.get(1)) < 1/256f) {
|
||||
t1Ref.remove(1);
|
||||
t2Ref.remove(1);
|
||||
intPoints.remove(1);
|
||||
}
|
||||
}
|
||||
if (t1Ref.size() == 3) {
|
||||
|
||||
}*/
|
||||
if (t1Ref.size() == 1) {
|
||||
if ((t1Ref.get(0) == 0 || t1Ref.get(0) == 1)
|
||||
&& (t2Ref.get(0) == 0 || t2Ref.get(0) == 1)) {
|
||||
@@ -300,15 +626,13 @@ public class ShapeFixer {
|
||||
be2L.setEndPoint(intP);
|
||||
be2R.setBeginPoint(intP);
|
||||
|
||||
/*be1L.roundX();
|
||||
be1R.roundX();
|
||||
be2L.roundX();
|
||||
be2R.roundX();*/
|
||||
be1L.roundX();
|
||||
be1R.roundX();
|
||||
be2L.roundX();
|
||||
be2R.roundX();
|
||||
|
||||
//splitPoints.put(be1, t1Ref.get(splitPointIndex));
|
||||
//splitPoints.put(be2, t2Ref.get(splitPointIndex));
|
||||
if (i1 == i2) {
|
||||
if (j1 < j2) {
|
||||
shapes.get(i1).remove(j2);
|
||||
@@ -565,4 +889,37 @@ public class ShapeFixer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Map<BezierEdge, Double> splitPoints = new TreeMap<>(new Comparator<BezierEdge>() {
|
||||
@Override
|
||||
public int compare(BezierEdge o1, BezierEdge o2) {
|
||||
int dSize = o1.points.size() - o2.points.size();
|
||||
if (dSize != 0) {
|
||||
return dSize;
|
||||
}
|
||||
for (int i = 0; i < o1.points.size(); i++) {
|
||||
int dX = Double.compare(o1.points.get(i).getX(), o2.points.get(i).getX());
|
||||
if (dX != 0) {
|
||||
return dX;
|
||||
}
|
||||
int dY = Double.compare(o1.points.get(i).getY(), o2.points.get(i).getY());
|
||||
if (dY != 0) {
|
||||
return dY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
ShapeFixer f = new ShapeFixer();
|
||||
List<List<BezierEdge>> shapes = new ArrayList<>();
|
||||
List<BezierEdge> beList = new ArrayList<>();
|
||||
beList.add(new BezierEdge(10000.0, 4980.0, 10040.0, 5060.0));
|
||||
beList.add(new BezierEdge(10040.0, 5040.0, 9900.0, 4900.0, 9900.0, 4860.0));
|
||||
shapes.add(beList);
|
||||
f.detectOverlappingEdges(shapes, Arrays.asList(1), Arrays.asList(2), Arrays.asList(0), Arrays.asList(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,16 +97,11 @@ public class SwitchedFillSidesFixer {
|
||||
|
||||
public Polygon(List<IEdge> list, int fillStyle) {
|
||||
this.list = list;
|
||||
/*double polyArea = polygonArea(list);
|
||||
if (polyArea < 0) {
|
||||
ccw = true;
|
||||
}*/
|
||||
path = toPath();
|
||||
//this.ccw = PathOrientation.orientationSingleClosed(path) == PathOrientation.Orientation.COUNTER_CLOCKWISE;
|
||||
Reference<PathOrientation.Orientation> orientationRef = new Reference<>(null);
|
||||
Reference<PathArea.Orientation> orientationRef = new Reference<>(null);
|
||||
Reference<Double> areaRef = new Reference<>(0.0);
|
||||
PathOrientation.orientationSingleClosed(path, orientationRef, areaRef);
|
||||
this.ccw = orientationRef.getVal() == PathOrientation.Orientation.COUNTER_CLOCKWISE;
|
||||
PathArea.orientationSingleClosed(path, orientationRef, areaRef);
|
||||
this.ccw = orientationRef.getVal() == PathArea.Orientation.COUNTER_CLOCKWISE;
|
||||
this.area = areaRef.getVal();
|
||||
this.areaObj = new Area(path);
|
||||
this.bbox = this.areaObj.getBounds2D();
|
||||
@@ -168,12 +163,14 @@ public class SwitchedFillSidesFixer {
|
||||
// Simple uniform grid over bbox domain
|
||||
private final double cellSize;
|
||||
private final Map<Long, List<Polygon>> cells = new HashMap<>();
|
||||
private final double minX, minY;
|
||||
private final double minX;
|
||||
private final double minY;
|
||||
|
||||
GridIndex(Collection<Polygon> polys, double cellSize) {
|
||||
this.cellSize = cellSize;
|
||||
// Compute global origin (minX/minY) to keep keys small
|
||||
double minx = Double.POSITIVE_INFINITY, miny = Double.POSITIVE_INFINITY;
|
||||
double minx = Double.POSITIVE_INFINITY;
|
||||
double miny = Double.POSITIVE_INFINITY;
|
||||
for (Polygon w : polys) {
|
||||
Rectangle2D b = w.bbox;
|
||||
if (b.getMinX() < minx) {
|
||||
@@ -237,7 +234,7 @@ public class SwitchedFillSidesFixer {
|
||||
void accept(int gx, int gy);
|
||||
}
|
||||
|
||||
public static void buildContainment(List<Polygon> polygons) {
|
||||
private static void buildContainment(List<Polygon> polygons) {
|
||||
|
||||
Map<Integer, List<Polygon>> byStyle = polygons.stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(w -> w.fillStyle));
|
||||
@@ -337,35 +334,6 @@ public class SwitchedFillSidesFixer {
|
||||
polygons.add(new Polygon(list, listFills.get(i)));
|
||||
}
|
||||
|
||||
/*for (Polygon outer : polygons) {
|
||||
for (Polygon inner : polygons) {
|
||||
if (outer != inner && inner.fillStyle == outer.fillStyle) {
|
||||
boolean cont = outer.contains(inner);
|
||||
|
||||
if (cont) {
|
||||
if (inner.children.contains(outer)) {
|
||||
inner.children.remove(outer);
|
||||
}
|
||||
outer.children.add(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loopmod:
|
||||
while (true) {
|
||||
for (Polygon poly : polygons) {
|
||||
for (int c = 0; c < poly.children.size(); c++) {
|
||||
for (int c2 = 0; c2 < poly.children.size(); c2++) {
|
||||
if (poly.children.get(c).children.contains(poly.children.get(c2))) {
|
||||
poly.children.remove(c2);
|
||||
continue loopmod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}*/
|
||||
buildContainment(polygons);
|
||||
|
||||
for (Polygon poly : polygons) {
|
||||
@@ -403,11 +371,10 @@ public class SwitchedFillSidesFixer {
|
||||
BezierEdge beRev = be.reverse();
|
||||
int localFs = globalToLocalFillStyleMap.get(fillStyleIdx);
|
||||
|
||||
BezierEdge search = new BezierEdge(180.0, -3040.0, 480.0, -3400.0);
|
||||
|
||||
//BezierEdge search = new BezierEdge(180.0, -3040.0, 480.0, -3400.0);
|
||||
boolean print = false;
|
||||
|
||||
if (be.equals(search)) {
|
||||
/*if (be.equals(search)) {
|
||||
System.err.println("xxx");
|
||||
System.err.println("" + polygon);
|
||||
print = true;
|
||||
@@ -415,8 +382,7 @@ public class SwitchedFillSidesFixer {
|
||||
if (be.equals(search.reverse())) {
|
||||
System.err.println("yyy");
|
||||
print = true;
|
||||
}
|
||||
|
||||
}*/
|
||||
if (print) {
|
||||
System.err.println("localFS: " + localFs);
|
||||
System.err.println("filled: " + polygon.filled);
|
||||
|
||||
Reference in New Issue
Block a user