mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-12 13:38:22 +00:00
#1903 Another unsuccessful try to fix FLA :-(
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2022 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.math;
|
||||
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class BezierEdge {
|
||||
|
||||
public List<Point2D> points = new ArrayList<>();
|
||||
|
||||
public BezierEdge(List<Point2D> points) {
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
public BezierEdge(double x0, double y0, double x1, double y1) {
|
||||
points.add(new Point2D.Double(x0, y0));
|
||||
points.add(new Point2D.Double(x1, y1));
|
||||
}
|
||||
|
||||
public BezierEdge(double x0, double y0, double cx, double cy, double x1, double y1) {
|
||||
points.add(new Point2D.Double(x0, y0));
|
||||
points.add(new Point2D.Double(cx, cy));
|
||||
points.add(new Point2D.Double(x1, y1));
|
||||
}
|
||||
|
||||
public Point2D pointAt(double t) {
|
||||
if (points.size() == 2) {
|
||||
double x = (1-t)*points.get(0).getX() + t * points.get(1).getX();
|
||||
double y = (1-t)*points.get(0).getY() + t * points.get(1).getY();
|
||||
return new Point2D.Double(x, y);
|
||||
}
|
||||
//points size == 3
|
||||
double x = (1 - t) * (1 - t) * points.get(0).getX() +
|
||||
2 * t * (1 - t) * points.get(1).getX() +
|
||||
t * t * points.get(2).getX();
|
||||
double y = (1 - t) * (1 - t) * points.get(0).getY() +
|
||||
2 * t * (1 - t) * points.get(1).getY() +
|
||||
t * t * points.get(2).getY();
|
||||
return new Point2D.Double(x, y);
|
||||
}
|
||||
|
||||
public Rectangle2D bbox() {
|
||||
double minX = Double.MAX_VALUE;
|
||||
double minY = Double.MAX_VALUE;
|
||||
double maxX = -Double.MAX_VALUE;
|
||||
double maxY = -Double.MAX_VALUE;
|
||||
//System.err.println("calculating bbox");
|
||||
for (Point2D p : points) {
|
||||
//System.err.println(" point " + p);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle2D b = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
|
||||
/*System.err.println(" bbox = "+b);
|
||||
System.err.println(" maxx = "+maxX);
|
||||
System.err.println(" maxy = "+maxY);*/
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
private final double MIN_SIZE = 1.0;
|
||||
|
||||
public double area() {
|
||||
Rectangle2D rect = bbox();
|
||||
double w = rect.getWidth();
|
||||
double h = rect.getHeight();
|
||||
|
||||
|
||||
if (w < MIN_SIZE) {
|
||||
w = MIN_SIZE;
|
||||
}
|
||||
if (h < MIN_SIZE) {
|
||||
h = MIN_SIZE;
|
||||
}
|
||||
return w * h;
|
||||
}
|
||||
|
||||
|
||||
private static boolean rectIntersection(Rectangle2D r1, Rectangle2D r2) {
|
||||
double xmin = Math.max(r1.getX(), r2.getX());
|
||||
double xmax1 = r1.getX() + r1.getWidth();
|
||||
double xmax2 = r2.getX() + r2.getWidth();
|
||||
double xmax = Math.min(xmax1, xmax2);
|
||||
if (Double.compare(xmax, xmin) >= 0) {
|
||||
double ymin = Math.max(r1.getY(), r2.getY());
|
||||
double ymax1 = r1.getY() + r1.getHeight();
|
||||
double ymax2 = r2.getY() + r2.getHeight();
|
||||
double ymax = Math.min(ymax1, ymax2);
|
||||
if (Double.compare(ymax, ymin) >= 0) {
|
||||
//out.setRect(xmin, ymin, xmax-xmin, ymax-ymin);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean intersects(BezierEdge b2, List<Double> t1Ref, List<Double> t2Ref) {
|
||||
return intersects(b2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1, t1Ref, t2Ref);
|
||||
}
|
||||
|
||||
private boolean intersects(
|
||||
BezierEdge b2,
|
||||
double start1,
|
||||
double end1,
|
||||
double start2,
|
||||
double end2,
|
||||
List<Double> t1Ref,
|
||||
List<Double> t2Ref) {
|
||||
final double threshold = MIN_SIZE * 2.0; //?
|
||||
Rectangle2D bb1 = bbox();
|
||||
Rectangle2D bb2 = b2.bbox();
|
||||
if (!rectIntersection(bb1, bb2)) {
|
||||
return false;
|
||||
}
|
||||
double sumAreas = area()+b2.area();
|
||||
//System.err.println("sumAreas="+sumAreas);
|
||||
if(Double.compare(sumAreas, threshold) <= 0) {
|
||||
double t1 = (start1+end1) / 2;
|
||||
double t2 = (start2+end2) / 2;
|
||||
|
||||
t1Ref.add(t1);
|
||||
t2Ref.add(t2);
|
||||
return true;
|
||||
}
|
||||
|
||||
//System.err.println("subdividing "+this+ " and "+b2);
|
||||
BezierUtils bu = new BezierUtils();
|
||||
List<Point2D> b1aPoints = new ArrayList<>();
|
||||
List<Point2D> b1bPoints = new ArrayList<>();
|
||||
bu.subdivide(points, 0.5, b1aPoints, b1bPoints);
|
||||
|
||||
List<Point2D> b2aPoints = new ArrayList<>();
|
||||
List<Point2D> b2bPoints = new ArrayList<>();
|
||||
bu.subdivide(b2.points, 0.5, b2aPoints, b2bPoints);
|
||||
BezierEdge b1a = new BezierEdge(b1aPoints);
|
||||
BezierEdge b1b = new BezierEdge(b1bPoints);
|
||||
BezierEdge b2a = new BezierEdge(b2aPoints);
|
||||
BezierEdge b2b = new BezierEdge(b2bPoints);
|
||||
|
||||
double half1 = start1 + (end1- start1) / 2.0;
|
||||
double half2 = start2 + (end2- start2) / 2.0;
|
||||
|
||||
boolean ok = false;
|
||||
if(b1a.intersects(b2a, start1, half1, start2, half2, t1Ref, t2Ref)) {
|
||||
ok = true;
|
||||
}
|
||||
if (b1a.intersects(b2b, start1, half1, half2, end2, t1Ref, t2Ref)) {
|
||||
ok = true;
|
||||
}
|
||||
if (b1b.intersects(b2a, half1, end1, start2, half2, t1Ref, t2Ref)) {
|
||||
ok = true;
|
||||
}
|
||||
if (b1b.intersects(b2b, half1, end1, half2, end2, t1Ref, t2Ref)) {
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> list = new ArrayList<>();
|
||||
for(Point2D p:points) {
|
||||
list.add("["+p.getX()+","+p.getY()+"]");
|
||||
}
|
||||
return "{"+String.join("-", list)+"}";
|
||||
}
|
||||
|
||||
public void split(double t, Reference<BezierEdge> left, Reference<BezierEdge> right) {
|
||||
List<Point2D> leftPoints = new ArrayList<>();
|
||||
List<Point2D> rightPoints = new ArrayList<>();
|
||||
BezierUtils bu = new BezierUtils();
|
||||
bu.subdivide(points, t, leftPoints, rightPoints);
|
||||
left.setVal(new BezierEdge(leftPoints));
|
||||
right.setVal(new BezierEdge(rightPoints));
|
||||
}
|
||||
|
||||
|
||||
|
||||
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.out.println("lines "+ be5+ " and "+be6);
|
||||
|
||||
System.out.println("hasIntersection = "+be5.intersects(be6, t1, t2));
|
||||
System.out.println("intersection ts: "+t1+", "+t2);
|
||||
|
||||
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);
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2022 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.math;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* Bezier utils.
|
||||
* Based on
|
||||
* https://code.google.com/archive/p/degrafa/source/default/source
|
||||
* which is derived from an algorithm in the book "Graphics Gems"
|
||||
*
|
||||
* This modification does not have cubic curves support.
|
||||
*/
|
||||
public class BezierUtils {
|
||||
|
||||
private static final int MAX_DEPTH = 64; // maximum recursion depth
|
||||
private static final double[] Z_QUAD = new double[]{1.0, 2.0 / 3.0, 1.0 / 3.0, 1 / 3.0, 2.0 / 3.0, 1.0};
|
||||
private static final double EPSILON = 1.0 * Math.pow(2, -MAX_DEPTH - 1); // flatness tolerance
|
||||
|
||||
public Point2D pointAt(double t, Point2D p0, Point2D p1, Point2D p2) {
|
||||
double xt = (1 - t) * (1 - t) * p0.getX() + 2 * (1 - t) * t * p1.getX() + t * t * p2.getX();
|
||||
double yt = (1 - t) * (1 - t) * p0.getY() + 2 * (1 - t) * t * p1.getY() + t * t * p2.getY();
|
||||
return new Point2D.Double(xt, yt);
|
||||
}
|
||||
|
||||
public double closestPointToBezier(Point2D _p, Point2D p0, Point2D p1, Point2D p2) {
|
||||
Point2D p = p0;
|
||||
double deltaX = p.getX() - _p.getX();
|
||||
double deltaY = p.getY() - _p.getY();
|
||||
double d0 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
p = p2;
|
||||
deltaX = p.getX() - _p.getX();
|
||||
deltaY = p.getY() - _p.getY();
|
||||
double d1 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
int n = 2; // degree of input Bezier curve
|
||||
|
||||
// array of control points
|
||||
List<Point2D> v = new ArrayList<>();
|
||||
v.add(p0);
|
||||
v.add(p1);
|
||||
v.add(p2);
|
||||
|
||||
// instead of power form, convert the function whose zeros are required to Bezier form
|
||||
List<Point2D> w = toBezierForm(_p, v);
|
||||
|
||||
// Find roots of the Bezier curve with control points stored in 'w' (algorithm is recursive, this is root depth of 0)
|
||||
List<Double> roots = findRoots(w, 2 * n - 1, 0);
|
||||
|
||||
// compare the candidate distances to the endpoints and declare a winner :)
|
||||
double tMinimum;
|
||||
double dMinimum;
|
||||
if (d0 < d1) {
|
||||
tMinimum = 0;
|
||||
dMinimum = d0;
|
||||
} else {
|
||||
tMinimum = 1;
|
||||
dMinimum = d1;
|
||||
}
|
||||
|
||||
// tbd - compare 2-norm squared
|
||||
for (int i = 0; i < roots.size(); i++) {
|
||||
double t = roots.get(i);
|
||||
if (t >= 0 && t <= 1) {
|
||||
p = pointAt(t, p0, p1, p2);
|
||||
deltaX = p.getX() - _p.getX();
|
||||
deltaY = p.getY() - _p.getY();
|
||||
double d = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
if (d < dMinimum) {
|
||||
tMinimum = t;
|
||||
dMinimum = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tbd - alternate optima.
|
||||
return tMinimum;
|
||||
|
||||
}
|
||||
|
||||
private List<Point2D> toBezierForm(Point2D _p, List<Point2D> _v) {
|
||||
List<Point2D> c = new ArrayList<>(); // V(i) - P
|
||||
List<Point2D> d = new ArrayList<>(); // V(i+1) - V(i)
|
||||
List<Point2D> w = new ArrayList<>(); // control-points for Bezier curve whose zeros represent candidates for closest point to the input parametric curve
|
||||
int n = _v.size() - 1; //degree of B(t)
|
||||
int degree = 2 * n - 1; // degree of B(t) . P
|
||||
double pX = _p.getX();
|
||||
double pY = _p.getY();
|
||||
|
||||
Point2D v;
|
||||
for (int i = 0; i <= n; i++) {
|
||||
v = _v.get(i);
|
||||
c.add(new Point2D.Double(v.getX() - pX, v.getY() - pY));
|
||||
}
|
||||
|
||||
double s = n;
|
||||
for (int i = 0; i <= n - 1; i++) {
|
||||
v = _v.get(i);
|
||||
Point2D v1 = _v.get(i + 1);
|
||||
d.add(new Point2D.Double(s * (v1.getX() - v.getX()), s * (v1.getY() - v.getY())));
|
||||
}
|
||||
|
||||
List<Double> cd = new ArrayList<>();
|
||||
for (int row = 0; row <= n - 1; row++) {
|
||||
Point2D di = d.get(row);
|
||||
double dX = di.getX();
|
||||
double dY = di.getY();
|
||||
for (int col = 0; col <= n; col++) {
|
||||
int k = getLinearIndex(n + 1, row, col);
|
||||
cd.add(dX * c.get(col).getX() + dY * c.get(col).getY());
|
||||
}
|
||||
}
|
||||
|
||||
// Bezier is uniform parameterized
|
||||
double dInv = 1.0 / degree;
|
||||
for (int i = 0; i <= degree; i++) {
|
||||
w.add(new Point2D.Double(i * dInv, 0));
|
||||
}
|
||||
|
||||
// reference to appropriate pre-computed coefficients
|
||||
double[] z = Z_QUAD;
|
||||
|
||||
// accumulate y-coords of the control points along the skew diagonal of the (n-1) x n matrix of c.d and z values
|
||||
int m = n - 1;
|
||||
for (int k = 0; k <= n + m; k++) {
|
||||
int lb = Math.max(0, k - m);
|
||||
int ub = Math.min(k, n);
|
||||
for (int i = lb; i <= ub; i++) {
|
||||
int j = k - i;
|
||||
Point2D p = w.get(i + j);
|
||||
int index = getLinearIndex(n + 1, j, i);
|
||||
p.setLocation(p.getX(), p.getY() + cd.get(index) * z[index]);
|
||||
w.set(i + j, p);
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
// how many times does the Bezier curve cross the horizontal axis - the number of roots is less than or equal to this count
|
||||
private int crossingCount(List<Point2D> _v, int _degree) {
|
||||
int nCrossings = 0;
|
||||
int sign = _v.get(0).getY() < 0 ? -1 : 1;
|
||||
int oldSign = sign;
|
||||
for (int i = 1; i <= _degree; i++) {
|
||||
sign = _v.get(i).getY() < 0 ? -1 : 1;
|
||||
if (sign != oldSign) {
|
||||
nCrossings++;
|
||||
}
|
||||
|
||||
oldSign = sign;
|
||||
}
|
||||
|
||||
return nCrossings;
|
||||
}
|
||||
|
||||
// convert 2D array indices in a k x n matrix to a linear index (this is an interim step ahead of a future implementation optimized for 1D array indexing)
|
||||
private int getLinearIndex(int _n, int _row, int _col) {
|
||||
// no range-checking; you break it ... you buy it!
|
||||
return _row * _n + _col;
|
||||
}
|
||||
|
||||
/**
|
||||
* subdivide( _c:Array, _t:Number, _left:Array, _right:Array ) - deCasteljau
|
||||
* subdivision of an arbitrary-order Bezier curve
|
||||
*
|
||||
* @param _c:Array array of control points for the Bezier curve
|
||||
* @param _t:Number t-parameter at which the curve is subdivided (must be in
|
||||
* (0,1) = no check at this point
|
||||
* @param _left:Array reference to an array in which the control points,
|
||||
* <code>Array</code> of <code>Point</code> references, of the left control
|
||||
* cage after subdivision are stored
|
||||
* @param _right:Array reference to an array in which the control points,
|
||||
* <code>Array</code> of <code>Point</code> references, of the right control
|
||||
* cage after subdivision are stored
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public void subdivide(List<Point2D> _c, double _t, List<Point2D> _left, List<Point2D> _right) {
|
||||
int degree = _c.size() - 1;
|
||||
int n = degree + 1;
|
||||
List<Point2D> p = new ArrayList<>(_c);
|
||||
double t1 = 1.0 - _t;
|
||||
|
||||
for (int i = 1; i <= degree; ++i) {
|
||||
for (int j = 0; j <= degree - i; ++j) {
|
||||
Point2D vertex = new Point2D.Double();
|
||||
int ij = getLinearIndex(n, i, j);
|
||||
int im1j = getLinearIndex(n, i - 1, j);
|
||||
int im1jp1 = getLinearIndex(n, i - 1, j + 1);
|
||||
|
||||
vertex.setLocation(t1 * p.get(im1j).getX() + _t * p.get(im1jp1).getX(), t1 * p.get(im1j).getY() + _t * p.get(im1jp1).getY());
|
||||
while (ij >= p.size()) {
|
||||
p.add(new Point2D.Double(0, 0));
|
||||
}
|
||||
p.set(ij, vertex);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j <= degree; j++) {
|
||||
int index = getLinearIndex(n, j, 0);
|
||||
_left.add(p.get(index));
|
||||
}
|
||||
|
||||
for (int j = 0; j <= degree; j++) {
|
||||
int index = getLinearIndex(n, degree - j, j);
|
||||
_right.add(p.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
// is the control polygon for a Bezier curve suitably linear for subdivision to terminate?
|
||||
private boolean isControlPolygonLinear(List<Point2D> _v, int _degree) {
|
||||
// Given array of control points, _v, find the distance from each interior control point to line connecting v[0] and v[degree]
|
||||
|
||||
// implicit equation for line connecting first and last control points
|
||||
double a = _v.get(0).getY() - _v.get(_degree).getY();
|
||||
double b = _v.get(_degree).getX() - _v.get(0).getX();
|
||||
double c = _v.get(0).getX() * _v.get(_degree).getY() - _v.get(_degree).getX() * _v.get(0).getY();
|
||||
|
||||
double abSquared = a * a + b * b;
|
||||
List<Double> distance = new ArrayList<>(); // Distances from control points to line
|
||||
|
||||
distance.add(0.0);
|
||||
for (int i = 1; i < _degree; i++) {
|
||||
// Compute distance from each of the points to that line
|
||||
distance.add(a * _v.get(i).getX() + b * _v.get(i).getY() + c);
|
||||
if (distance.get(i) > 0.0) {
|
||||
distance.set(i, (distance.get(i) * distance.get(i)) / abSquared);
|
||||
}
|
||||
if (distance.get(i) < 0.0) {
|
||||
distance.set(i, -((distance.get(i) * distance.get(i)) / abSquared));
|
||||
}
|
||||
}
|
||||
|
||||
// Find the largest distance
|
||||
double maxDistanceAbove = 0.0;
|
||||
double maxDistanceBelow = 0.0;
|
||||
for (int i = 1; i < _degree; i++) {
|
||||
if (distance.get(i) < 0.0) {
|
||||
maxDistanceBelow = Math.min(maxDistanceBelow, distance.get(i));
|
||||
}
|
||||
if (distance.get(i) > 0.0) {
|
||||
maxDistanceAbove = Math.max(maxDistanceAbove, distance.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Implicit equation for zero line
|
||||
double a1 = 0.0;
|
||||
double b1 = 1.0;
|
||||
double c1 = 0.0;
|
||||
|
||||
// Implicit equation for "above" line
|
||||
double a2 = a;
|
||||
double b2 = b;
|
||||
double c2 = c + maxDistanceAbove;
|
||||
|
||||
double det = a1 * b2 - a2 * b1;
|
||||
double dInv = 1.0 / det;
|
||||
|
||||
double intercept1 = (b1 * c2 - b2 * c1) * dInv;
|
||||
|
||||
// Implicit equation for "below" line
|
||||
a2 = a;
|
||||
b2 = b;
|
||||
c2 = c + maxDistanceBelow;
|
||||
|
||||
double intercept2 = (b1 * c2 - b2 * c1) * dInv;
|
||||
|
||||
// Compute intercepts of bounding box
|
||||
double leftIntercept = Math.min(intercept1, intercept2);
|
||||
double rightIntercept = Math.max(intercept1, intercept2);
|
||||
|
||||
double error = 0.5 * (rightIntercept - leftIntercept);
|
||||
|
||||
return error < EPSILON;
|
||||
}
|
||||
|
||||
// return roots in [0,1] of a polynomial in Bernstein-Bezier form
|
||||
private List<Double> findRoots(List<Point2D> _w, int _degree, int _depth) {
|
||||
List<Double> t = new ArrayList<>(); // t-values of roots
|
||||
int m = 2 * _degree - 1;
|
||||
|
||||
switch (crossingCount(_w, _degree)) {
|
||||
case 0:
|
||||
return new ArrayList<>();
|
||||
case 1:
|
||||
// Unique solution - stop recursion when the tree is deep enough (return 1 solution at midpoint)
|
||||
if (_depth >= MAX_DEPTH) {
|
||||
t.add(0.5 * (_w.get(0).getX() + _w.get(m).getX()));
|
||||
return t;
|
||||
}
|
||||
|
||||
if (isControlPolygonLinear(_w, _degree)) {
|
||||
t.add(computeXIntercept(_w, _degree));
|
||||
return t;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, solve recursively after subdividing control polygon
|
||||
List<Point2D> left = new ArrayList<>();
|
||||
List<Point2D> right = new ArrayList<>();
|
||||
|
||||
// child solutions
|
||||
subdivide(_w, 0.5, left, right);
|
||||
List<Double> leftT = findRoots(left, _degree, _depth + 1);
|
||||
List<Double> rightT = findRoots(right, _degree, _depth + 1);
|
||||
|
||||
t.addAll(leftT);
|
||||
t.addAll(rightT);
|
||||
return t;
|
||||
}
|
||||
|
||||
// compute intersection of line segnet from first to last control point with horizontal axis
|
||||
private double computeXIntercept(List<Point2D> _v, int _degree) {
|
||||
double XNM = _v.get(_degree).getX() - _v.get(0).getX();
|
||||
double YNM = _v.get(_degree).getY() - _v.get(0).getY();
|
||||
double XMK = _v.get(0).getX();
|
||||
double YMK = _v.get(0).getY();
|
||||
|
||||
double detInv = -1.0 / YNM;
|
||||
|
||||
return (XNM * YMK - YNM * XMK) * detInv;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,11 @@
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.xfl;
|
||||
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.CurvedEdgeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeFixer;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.StraightEdgeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.StyleChangeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
|
||||
import com.jpexs.decompiler.flash.ReadOnlyTagList;
|
||||
import com.jpexs.decompiler.flash.RetryTask;
|
||||
@@ -140,6 +145,7 @@ import com.jpexs.helpers.SerializableImage;
|
||||
import com.jpexs.helpers.utf8.Utf8Helper;
|
||||
import java.awt.Font;
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -147,12 +153,15 @@ import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
@@ -215,51 +224,67 @@ public class XFLConverter {
|
||||
*/
|
||||
private final boolean DEBUG_EXPORT_LAYER_DEPTHS = false;
|
||||
|
||||
private static void convertShapeEdge(MATRIX mat, SHAPERECORD record, int x, int y, StringBuilder ret) {
|
||||
if (record instanceof StyleChangeRecord) {
|
||||
StyleChangeRecord scr = (StyleChangeRecord) record;
|
||||
Point p = new Point(scr.moveDeltaX, scr.moveDeltaY);
|
||||
|
||||
private static String formatEdgeDouble(double value, boolean curved) {
|
||||
if (value % 1 == 0) {
|
||||
return "" + (int) value;
|
||||
}
|
||||
DecimalFormat df = new DecimalFormat("0.##", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
|
||||
df.setGroupingUsed(false);
|
||||
String strValue = "" + df.format(value);
|
||||
|
||||
if (curved) {
|
||||
String parts[] = strValue.split("\\.");
|
||||
return ("#" + Integer.toHexString(Integer.parseInt(parts[0])) + "." + Integer.toHexString(Integer.parseInt(parts[1]))).toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
return "" + strValue;
|
||||
}
|
||||
|
||||
private static void convertShapeEdge(MATRIX mat, ShapeRecordAdvanced record, double x, double y, StringBuilder ret) {
|
||||
if (record instanceof StyleChangeRecordAdvanced) {
|
||||
StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) record;
|
||||
Point2D p = new Point2D.Double(scr.moveDeltaX, scr.moveDeltaY);
|
||||
if (scr.stateMoveTo) {
|
||||
ret.append("! ").append(p.x).append(" ").append(p.y);
|
||||
ret.append("! ").append(formatEdgeDouble(p.getX(), false)).append(" ").append(formatEdgeDouble(p.getY(), false));
|
||||
}
|
||||
} else if (record instanceof StraightEdgeRecord) {
|
||||
StraightEdgeRecord ser = (StraightEdgeRecord) record;
|
||||
if (ser.generalLineFlag || ser.vertLineFlag) {
|
||||
y += ser.deltaY;
|
||||
}
|
||||
if (ser.generalLineFlag || (!ser.vertLineFlag)) {
|
||||
x += ser.deltaX;
|
||||
}
|
||||
Point p = new Point(x, y);
|
||||
ret.append("| ").append(p.x).append(" ").append(p.y);
|
||||
} else if (record instanceof CurvedEdgeRecord) {
|
||||
CurvedEdgeRecord cer = (CurvedEdgeRecord) record;
|
||||
int controlX = cer.controlDeltaX + x;
|
||||
int controlY = cer.controlDeltaY + y;
|
||||
int anchorX = cer.anchorDeltaX + controlX;
|
||||
int anchorY = cer.anchorDeltaY + controlY;
|
||||
Point control = new Point(controlX, controlY);
|
||||
Point anchor = new Point(anchorX, anchorY);
|
||||
ret.append("[ ").append(control.x).append(" ").append(control.y).append(" ").append(anchor.x).append(" ").append(anchor.y);
|
||||
} else if (record instanceof StraightEdgeRecordAdvanced) {
|
||||
StraightEdgeRecordAdvanced ser = (StraightEdgeRecordAdvanced) record;
|
||||
y += ser.deltaY;
|
||||
x += ser.deltaX;
|
||||
Point2D p = new Point2D.Double(x, y);
|
||||
ret.append("| ");
|
||||
ret.append(formatEdgeDouble(p.getX(), false));
|
||||
ret.append(" ");
|
||||
ret.append(formatEdgeDouble(p.getY(), false));
|
||||
} else if (record instanceof CurvedEdgeRecordAdvanced) {
|
||||
CurvedEdgeRecordAdvanced cer = (CurvedEdgeRecordAdvanced) record;
|
||||
double controlX = cer.controlDeltaX + x;
|
||||
double controlY = cer.controlDeltaY + y;
|
||||
double anchorX = cer.anchorDeltaX + controlX;
|
||||
double anchorY = cer.anchorDeltaY + controlY;
|
||||
Point2D control = new Point2D.Double(controlX, controlY);
|
||||
Point2D anchor = new Point.Double(anchorX, anchorY);
|
||||
ret.append("[ ").append(formatEdgeDouble(control.getX(), true)).append(" ").append(formatEdgeDouble(control.getY(), true)).append(" ").append(formatEdgeDouble(anchor.getX(), true)).append(" ").append(formatEdgeDouble(anchor.getY(), true));
|
||||
}
|
||||
}
|
||||
|
||||
private static void convertShapeEdges(int startX, int startY, MATRIX mat, List<SHAPERECORD> records, StringBuilder ret) {
|
||||
int x = startX;
|
||||
int y = startY;
|
||||
private static void convertShapeEdges(double startX, double startY, MATRIX mat, List<ShapeRecordAdvanced> recordsAdvanced, StringBuilder ret) {
|
||||
|
||||
double x = startX;
|
||||
double y = startY;
|
||||
boolean hasMove = false;
|
||||
if (!records.isEmpty()) {
|
||||
if (records.get(0) instanceof StyleChangeRecord) {
|
||||
StyleChangeRecord scr = (StyleChangeRecord) records.get(0);
|
||||
if (!recordsAdvanced.isEmpty()) {
|
||||
if (recordsAdvanced.get(0) instanceof StyleChangeRecordAdvanced) {
|
||||
StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) recordsAdvanced.get(0);
|
||||
if (scr.stateMoveTo) {
|
||||
hasMove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasMove) {
|
||||
ret.append("! ").append(startX).append(" ").append(startY);
|
||||
ret.append("! ").append(formatEdgeDouble(startX, false)).append(" ").append(formatEdgeDouble(startY, false));
|
||||
}
|
||||
for (SHAPERECORD rec : records) {
|
||||
for (ShapeRecordAdvanced rec : recordsAdvanced) {
|
||||
convertShapeEdge(mat, rec, x, y, ret);
|
||||
x = rec.changeX(x);
|
||||
y = rec.changeY(y);
|
||||
@@ -678,76 +703,27 @@ public class XFLConverter {
|
||||
index++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove bugs in shape:
|
||||
*
|
||||
* ... straightrecord straightrecord stylechange straightrecord (-2,0) <--
|
||||
* merge this with previous stylegchange
|
||||
*
|
||||
* @param shapeRecords
|
||||
* @return
|
||||
*/
|
||||
private static List<SHAPERECORD> smoothShape(List<SHAPERECORD> shapeRecords) {
|
||||
List<SHAPERECORD> ret = new ArrayList<>(shapeRecords.size());
|
||||
for (SHAPERECORD rec : shapeRecords) {
|
||||
ret.add(rec.clone());
|
||||
}
|
||||
|
||||
for (int i = 1; i < ret.size() - 1; i++) {
|
||||
if (ret.get(i) instanceof StraightEdgeRecord && (ret.get(i - 1) instanceof StyleChangeRecord) && (ret.get(i + 1) instanceof StyleChangeRecord)) {
|
||||
StraightEdgeRecord ser = (StraightEdgeRecord) ret.get(i);
|
||||
StyleChangeRecord scr = (StyleChangeRecord) ret.get(i - 1);
|
||||
StyleChangeRecord scr2 = (StyleChangeRecord) ret.get(i + 1);
|
||||
if ((!scr.stateMoveTo && !scr.stateNewStyles) && Math.abs(ser.deltaX) < 5 && Math.abs(ser.deltaY) < 5) {
|
||||
if (i >= 2) {
|
||||
SHAPERECORD rbef = ret.get(i - 2);
|
||||
if (rbef instanceof StraightEdgeRecord) {
|
||||
StraightEdgeRecord ser_b = (StraightEdgeRecord) rbef;
|
||||
ser_b.generalLineFlag = true;
|
||||
ser_b.deltaX = ser.changeX(ser_b.deltaX);
|
||||
ser_b.deltaY = ser.changeY(ser_b.deltaY);
|
||||
} else if (rbef instanceof CurvedEdgeRecord) {
|
||||
CurvedEdgeRecord cer_b = (CurvedEdgeRecord) rbef;
|
||||
cer_b.anchorDeltaX = ser.changeX(cer_b.anchorDeltaX);
|
||||
cer_b.anchorDeltaY = ser.changeY(cer_b.anchorDeltaY);
|
||||
} else {
|
||||
//???
|
||||
}
|
||||
|
||||
ret.remove(i - 1);
|
||||
ret.remove(i - 1);
|
||||
if (scr.stateFillStyle0 && !scr2.stateFillStyle0) {
|
||||
scr2.stateFillStyle0 = true;
|
||||
scr2.fillStyle0 = scr.fillStyle0;
|
||||
}
|
||||
if (scr.stateFillStyle1 && !scr2.stateFillStyle1) {
|
||||
scr2.stateFillStyle1 = true;
|
||||
scr2.fillStyle1 = scr.fillStyle1;
|
||||
}
|
||||
if (scr.stateLineStyle && !scr2.stateLineStyle) {
|
||||
scr2.stateLineStyle = true;
|
||||
scr2.lineStyle = scr.lineStyle;
|
||||
}
|
||||
|
||||
i -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getShapeLayers(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape) throws XMLStreamException {
|
||||
if (mat == null) {
|
||||
mat = new MATRIX();
|
||||
}
|
||||
|
||||
//smoothing fixes some shapes in #503, but also breaks some shapes of #1257
|
||||
//shapeRecords = smoothShape(shapeRecords);
|
||||
List<SHAPERECORD> edges = new ArrayList<>();
|
||||
List<ShapeRecordAdvanced> shapeRecordsAdvanced = new ArrayList<>();
|
||||
for(SHAPERECORD rec:shapeRecords) {
|
||||
ShapeRecordAdvanced arec = ShapeRecordAdvanced.createFromSHAPERECORD(rec);
|
||||
if (arec != null) {
|
||||
shapeRecordsAdvanced.add(arec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//#1903, #503, #1257 This is not working at all :-(
|
||||
//ShapeFixer fixer = new ShapeFixer();
|
||||
//shapeRecordsAdvanced = fixer.fixShape(shapeRecordsAdvanced);
|
||||
|
||||
List<ShapeRecordAdvanced> edges = new ArrayList<>();
|
||||
int lineStyleCount = 0;
|
||||
int fillStyle0 = -1;
|
||||
int fillStyle1 = -1;
|
||||
@@ -799,17 +775,17 @@ public class XFLConverter {
|
||||
currentLayer.writeStartElement("edges");
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int startEdgeX = 0;
|
||||
int startEdgeY = 0;
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
double startEdgeX = 0;
|
||||
double startEdgeY = 0;
|
||||
|
||||
LINESTYLEARRAY actualLinestyles = lineStyles;
|
||||
int strokeStyleOrig = 0;
|
||||
fillStyleCount = fillStyles == null ? 0 : fillStyles.fillStyles.length;
|
||||
for (SHAPERECORD edge : shapeRecords) {
|
||||
if (edge instanceof StyleChangeRecord) {
|
||||
StyleChangeRecord scr = (StyleChangeRecord) edge;
|
||||
for (ShapeRecordAdvanced edge : shapeRecordsAdvanced) {
|
||||
if (edge instanceof StyleChangeRecordAdvanced) {
|
||||
StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) edge;
|
||||
boolean styleChange = false;
|
||||
int lastFillStyle1 = fillStyle1;
|
||||
int lastFillStyle0 = fillStyle0;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2022 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class CurvedEdgeRecordAdvanced extends ShapeRecordAdvanced {
|
||||
public double controlDeltaX;
|
||||
public double controlDeltaY;
|
||||
public double anchorDeltaX;
|
||||
public double anchorDeltaY;
|
||||
|
||||
public CurvedEdgeRecordAdvanced() {
|
||||
}
|
||||
|
||||
public CurvedEdgeRecordAdvanced(int controlDeltaX, int controlDeltaY, int anchorDeltaX, int anchorDeltaY) {
|
||||
this.controlDeltaX = controlDeltaX;
|
||||
this.controlDeltaY = controlDeltaY;
|
||||
this.anchorDeltaX = anchorDeltaX;
|
||||
this.anchorDeltaY = anchorDeltaY;
|
||||
}
|
||||
|
||||
public CurvedEdgeRecordAdvanced(CurvedEdgeRecord cer) {
|
||||
this.controlDeltaX = cer.controlDeltaX;
|
||||
this.controlDeltaY = cer.controlDeltaY;
|
||||
this.anchorDeltaX = cer.anchorDeltaX;
|
||||
this.anchorDeltaY = cer.anchorDeltaY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double changeX(double x) {
|
||||
return x + controlDeltaX + anchorDeltaX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double changeY(double y) {
|
||||
return y + controlDeltaY + anchorDeltaY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurvedEdgeRecord toBasicRecord() {
|
||||
CurvedEdgeRecord ret = new CurvedEdgeRecord();
|
||||
ret.controlDeltaX = (int)Math.round(controlDeltaX);
|
||||
ret.controlDeltaY = (int)Math.round(controlDeltaY);
|
||||
ret.anchorDeltaX = (int)Math.round(anchorDeltaX);
|
||||
ret.anchorDeltaY = (int)Math.round(anchorDeltaY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void round() {
|
||||
controlDeltaX = Math.round(controlDeltaX);
|
||||
controlDeltaY = Math.round(controlDeltaY);
|
||||
anchorDeltaX = Math.round(anchorDeltaX);
|
||||
anchorDeltaY = Math.round(anchorDeltaY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2022 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.math.BezierEdge;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ShapeFixer {
|
||||
|
||||
private void addToEdgeMap(Map<Point2D, List<Edge>> edgeMap, Edge edge) {
|
||||
if (!edgeMap.containsKey(edge.getFrom())) {
|
||||
edgeMap.put(edge.getFrom(), new ArrayList<>());
|
||||
}
|
||||
if (!edgeMap.containsKey(edge.getTo())) {
|
||||
edgeMap.put(edge.getTo(), new ArrayList<>());
|
||||
}
|
||||
edgeMap.get(edge.getFrom()).add(edge);
|
||||
edgeMap.get(edge.getTo()).add(edge.invert());
|
||||
}
|
||||
|
||||
private boolean fixSingleEdge(List<ShapeRecordAdvanced> records) {
|
||||
Map<Point2D, List<Edge>> edgeMap = new LinkedHashMap<>();
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
int fillStyle0 = 0;
|
||||
int fillStyle1 = 0;
|
||||
|
||||
for (int r = 0; r < records.size(); r++) {
|
||||
ShapeRecordAdvanced rec = records.get(r);
|
||||
if (rec instanceof StyleChangeRecordAdvanced) {
|
||||
StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) rec;
|
||||
if (scr.stateNewStyles) {
|
||||
fillStyle0 = 0;
|
||||
fillStyle1 = 0;
|
||||
}
|
||||
if (scr.stateFillStyle0) {
|
||||
fillStyle0 = scr.fillStyle0;
|
||||
}
|
||||
if (scr.stateFillStyle1) {
|
||||
fillStyle1 = scr.fillStyle1;
|
||||
}
|
||||
}
|
||||
if (rec instanceof StraightEdgeRecordAdvanced) {
|
||||
StraightEdgeRecordAdvanced ser = (StraightEdgeRecordAdvanced) rec;
|
||||
addToEdgeMap(edgeMap, new Edge(fillStyle0, fillStyle1, r, false, x, y, x + ser.deltaX, y + ser.deltaY));
|
||||
}
|
||||
if (rec instanceof CurvedEdgeRecordAdvanced) {
|
||||
CurvedEdgeRecordAdvanced cer = (CurvedEdgeRecordAdvanced) rec;
|
||||
addToEdgeMap(edgeMap, new Edge(fillStyle0, fillStyle1, r, false,
|
||||
x, y,
|
||||
x + cer.controlDeltaX, y + cer.controlDeltaY,
|
||||
x + cer.controlDeltaX + cer.anchorDeltaX, y + cer.controlDeltaY + cer.anchorDeltaY
|
||||
));
|
||||
}
|
||||
x = rec.changeX(x);
|
||||
y = rec.changeY(y);
|
||||
}
|
||||
|
||||
for (Point2D p : edgeMap.keySet()) {
|
||||
List<Edge> edges = edgeMap.get(p);
|
||||
for (int i = 0; i < edges.size(); i++) {
|
||||
for (int j = 0; j < edges.size(); j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
Edge edge1 = edges.get(i);
|
||||
Edge edge2 = edges.get(j);
|
||||
List<Edge> newEdges1 = new ArrayList<>();
|
||||
List<Edge> newEdges2 = new ArrayList<>();
|
||||
|
||||
if (edge1.intersection(edge2, newEdges1, newEdges2)) {
|
||||
|
||||
/*System.out.println("---------------------------------------");
|
||||
System.out.println("intersection edge "+edge1 + " and " +edge2);
|
||||
System.out.println("newEdges1:");
|
||||
for(Edge e:newEdges1) {
|
||||
System.out.println("..."+e);
|
||||
}
|
||||
System.out.println("newEdges2:");
|
||||
for(Edge e:newEdges2) {
|
||||
System.out.println("..."+e);
|
||||
}*/
|
||||
|
||||
Point2D a = edge1.getFrom();
|
||||
Point2D b = edge1.getTo();
|
||||
Point2D c = edge2.getTo();
|
||||
|
||||
boolean edge2isRight = ((b.getX() - a.getX()) * (c.getY() - a.getY())) - ((b.getY() - a.getY()) * (c.getX() - a.getX())) > 0;
|
||||
//edge2isRight = !edge2isRight;
|
||||
|
||||
fillStyle0 = edge2isRight ? edge1.fillStyle0 : edge2.fillStyle0;
|
||||
fillStyle1 = edge2isRight ? edge2.fillStyle1 : edge1.fillStyle1;
|
||||
|
||||
StyleChangeRecordAdvanced moveCenter = new StyleChangeRecordAdvanced();
|
||||
moveCenter.stateMoveTo = true;
|
||||
moveCenter.moveDeltaX = edge1.getFrom().getX();
|
||||
moveCenter.moveDeltaY = edge1.getFrom().getY();
|
||||
moveCenter.stateFillStyle0 = true;
|
||||
moveCenter.stateFillStyle1 = true;
|
||||
moveCenter.fillStyle0 = fillStyle0;
|
||||
moveCenter.fillStyle1 = fillStyle1;
|
||||
|
||||
//common line
|
||||
StraightEdgeRecordAdvanced ser = new StraightEdgeRecordAdvanced();
|
||||
ser.deltaX = newEdges1.get(0).getTo().getX() - edge1.getFrom().getX();
|
||||
ser.deltaY = newEdges1.get(0).getTo().getY() - edge1.getFrom().getY();
|
||||
|
||||
StyleChangeRecordAdvanced scrStart1 = new StyleChangeRecordAdvanced();
|
||||
scrStart1.stateMoveTo = false;
|
||||
scrStart1.stateFillStyle0 = true;
|
||||
scrStart1.stateFillStyle1 = true;
|
||||
scrStart1.fillStyle0 = newEdges1.get(1).fillStyle0;
|
||||
scrStart1.fillStyle1 = newEdges1.get(1).fillStyle1;
|
||||
|
||||
StyleChangeRecordAdvanced moveBack1 = new StyleChangeRecordAdvanced();
|
||||
moveBack1.stateMoveTo = true;
|
||||
Edge edge1NotInverted = edge1.inverted ? edge1.invert() : edge1;
|
||||
|
||||
moveBack1.moveDeltaX = edge1NotInverted.getTo().getX();
|
||||
moveBack1.moveDeltaY = edge1NotInverted.getTo().getY();
|
||||
moveBack1.stateFillStyle0 = true;
|
||||
moveBack1.stateFillStyle1 = true;
|
||||
moveBack1.fillStyle0 = edge1NotInverted.fillStyle0;
|
||||
moveBack1.fillStyle1 = edge1NotInverted.fillStyle1;
|
||||
|
||||
records.remove(edge1.recordIndex);
|
||||
records.add(edge1.recordIndex, moveCenter);
|
||||
records.add(edge1.recordIndex + 1, ser);
|
||||
records.add(edge1.recordIndex + 2, scrStart1);
|
||||
records.add(edge1.recordIndex + 3, newEdges1.get(1).toShapeRecordAdvanced());
|
||||
records.add(edge1.recordIndex + 4, moveBack1);
|
||||
|
||||
if (edge2.recordIndex > edge1.recordIndex) {
|
||||
edge2.recordIndex += 4;
|
||||
}
|
||||
|
||||
StyleChangeRecordAdvanced moveStart2 = new StyleChangeRecordAdvanced();
|
||||
moveStart2.stateMoveTo = true;
|
||||
moveStart2.moveDeltaX = newEdges2.get(1).getFrom().getX();
|
||||
moveStart2.moveDeltaY = newEdges2.get(1).getFrom().getY();
|
||||
moveStart2.stateFillStyle0 = true;
|
||||
moveStart2.stateFillStyle1 = true;
|
||||
moveStart2.fillStyle0 = newEdges2.get(1).fillStyle0;
|
||||
moveStart2.fillStyle1 = newEdges2.get(1).fillStyle1;
|
||||
|
||||
StyleChangeRecordAdvanced moveBack2 = new StyleChangeRecordAdvanced();
|
||||
moveBack2.stateMoveTo = true;
|
||||
Edge edge2NotInverted = edge2.inverted ? edge2.invert() : edge2;
|
||||
|
||||
moveBack2.moveDeltaX = edge2NotInverted.getTo().getX();
|
||||
moveBack2.moveDeltaY = edge2NotInverted.getTo().getY();
|
||||
moveBack2.stateFillStyle0 = true;
|
||||
moveBack2.stateFillStyle1 = true;
|
||||
moveBack2.fillStyle0 = edge2NotInverted.fillStyle0;
|
||||
moveBack2.fillStyle1 = edge2NotInverted.fillStyle1;
|
||||
|
||||
records.remove(edge2.recordIndex);
|
||||
records.add(edge2.recordIndex, moveStart2);
|
||||
records.add(edge2.recordIndex + 1, newEdges2.get(1).toShapeRecordAdvanced());
|
||||
records.add(edge2.recordIndex + 2, moveBack2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<ShapeRecordAdvanced> fixShape(List<ShapeRecordAdvanced> records) {
|
||||
List<ShapeRecordAdvanced> ret = Helper.deepCopy(records);
|
||||
while(fixSingleEdge(ret)) {
|
||||
//nothing
|
||||
}
|
||||
for(ShapeRecordAdvanced rec:ret) {
|
||||
rec.round();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
class Edge {
|
||||
|
||||
int recordIndex;
|
||||
boolean inverted;
|
||||
int fillStyle0 = 0;
|
||||
int fillStyle1 = 0;
|
||||
|
||||
List<Point2D> points = new ArrayList<>();
|
||||
|
||||
public Point2D getFrom() {
|
||||
return points.get(0);
|
||||
}
|
||||
|
||||
public Point2D getTo() {
|
||||
return points.get(points.size() - 1);
|
||||
}
|
||||
|
||||
public BezierEdge toBezierEdge() {
|
||||
return new BezierEdge(new ArrayList<>(points));
|
||||
}
|
||||
|
||||
public boolean intersection(Edge otherEdge, List<Edge> newThisEdges, List<Edge> newOtherEdges) {
|
||||
List<Double> t1s = new ArrayList<>();
|
||||
List<Double> t2s = new ArrayList<>();
|
||||
BezierEdge be1 = this.toBezierEdge();
|
||||
BezierEdge be2 = otherEdge.toBezierEdge();
|
||||
if (!be1.intersects(be2, t1s, t2s)) {
|
||||
return false;
|
||||
}
|
||||
Reference<BezierEdge> be1aRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be1bRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be2aRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be2bRef = new Reference<>(null);
|
||||
|
||||
t1s.sort(new Comparator<Double>() {
|
||||
@Override
|
||||
public int compare(Double o1, Double o2) {
|
||||
return Double.compare(o1, o2);
|
||||
}
|
||||
});
|
||||
|
||||
t2s.sort(new Comparator<Double>() {
|
||||
@Override
|
||||
public int compare(Double o1, Double o2) {
|
||||
return Double.compare(o1, o2);
|
||||
}
|
||||
});
|
||||
|
||||
if (t1s.size() == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double t1 = t1s.get(t1s.size() - 1);
|
||||
double t2 = t2s.get(t2s.size() - 1);
|
||||
/*System.out.println("t1 = "+t1);
|
||||
System.out.println("t2 = "+t2);*/
|
||||
|
||||
be1.split(t1, be1aRef, be1bRef);
|
||||
be2.split(t2, be2aRef, be2bRef);
|
||||
|
||||
newThisEdges.add(new Edge(this.fillStyle0, this.fillStyle1, -1, this.inverted, be1aRef.getVal()));
|
||||
newThisEdges.add(new Edge(this.fillStyle0, this.fillStyle1, -1, this.inverted, be1bRef.getVal()));
|
||||
|
||||
newOtherEdges.add(new Edge(otherEdge.fillStyle0, otherEdge.fillStyle1, -1, otherEdge.inverted, be2aRef.getVal()));
|
||||
newOtherEdges.add(new Edge(otherEdge.fillStyle0, otherEdge.fillStyle1, -1, otherEdge.inverted, be2bRef.getVal()));
|
||||
|
||||
if (newThisEdges.get(0).isEmpty() || newOtherEdges.get(0).isEmpty()) {
|
||||
newThisEdges.clear();
|
||||
newOtherEdges.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Point2D pointAt(double t) {
|
||||
return toBezierEdge().pointAt(t);
|
||||
}
|
||||
|
||||
public Edge invert() {
|
||||
List<Point2D> newPoints = new ArrayList<>();
|
||||
for (int i = points.size() - 1; i >= 0; i--) {
|
||||
newPoints.add(points.get(i));
|
||||
}
|
||||
return new Edge(fillStyle1, fillStyle0, recordIndex, !inverted, newPoints);
|
||||
}
|
||||
|
||||
public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, BezierEdge be) {
|
||||
List<Point2D> points2D = be.points;
|
||||
List<Point2D> points = new ArrayList<>();
|
||||
for (Point2D p : points2D) {
|
||||
points.add(new Point2D.Double(p.getX(),p.getY()));
|
||||
}
|
||||
this.points = points;
|
||||
this.recordIndex = recordIndex;
|
||||
this.inverted = inverted;
|
||||
this.fillStyle0 = fillStyle0;
|
||||
this.fillStyle1 = fillStyle1;
|
||||
}
|
||||
|
||||
public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, List<Point2D> points) {
|
||||
this.points = points;
|
||||
this.recordIndex = recordIndex;
|
||||
this.inverted = inverted;
|
||||
this.fillStyle0 = fillStyle0;
|
||||
this.fillStyle1 = fillStyle1;
|
||||
}
|
||||
|
||||
public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, double fromX, double fromY, double toX, double toY) {
|
||||
points.add(new Point2D.Double(fromX, fromY));
|
||||
points.add(new Point2D.Double(toX, toY));
|
||||
this.recordIndex = recordIndex;
|
||||
this.inverted = inverted;
|
||||
this.fillStyle0 = fillStyle0;
|
||||
this.fillStyle1 = fillStyle1;
|
||||
}
|
||||
|
||||
public Edge(int fillStyle0, int fillStyle1, int recordIndex, boolean inverted, double fromX, double fromY, double controlX, double controlY, double toX, double toY) {
|
||||
points.add(new Point2D.Double(fromX, fromY));
|
||||
points.add(new Point2D.Double(controlX, controlY));
|
||||
points.add(new Point2D.Double(toX, toY));
|
||||
this.recordIndex = recordIndex;
|
||||
this.inverted = inverted;
|
||||
this.fillStyle0 = fillStyle0;
|
||||
this.fillStyle1 = fillStyle1;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Point2D p : points) {
|
||||
list.add("[" + p.getX() + "," + p.getY() + "]");
|
||||
}
|
||||
return "{" + String.join("-", list) + "}";
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return getFrom().equals(getTo());
|
||||
}
|
||||
|
||||
public ShapeRecordAdvanced toShapeRecordAdvanced() {
|
||||
if (points.size() == 3) {
|
||||
CurvedEdgeRecordAdvanced cer = new CurvedEdgeRecordAdvanced();
|
||||
cer.controlDeltaX = points.get(1).getX() - points.get(0).getX();
|
||||
cer.controlDeltaY = points.get(1).getY() - points.get(0).getY();
|
||||
cer.anchorDeltaX = points.get(2).getX() - points.get(1).getX();
|
||||
cer.anchorDeltaY = points.get(2).getY() - points.get(1).getY();
|
||||
return cer;
|
||||
}
|
||||
StraightEdgeRecordAdvanced ser = new StraightEdgeRecordAdvanced();
|
||||
ser.deltaX = points.get(1).getX() - points.get(0).getX();
|
||||
ser.deltaY = points.get(1).getY() - points.get(0).getY();
|
||||
return ser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2022 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public abstract class ShapeRecordAdvanced implements Serializable {
|
||||
public abstract double changeX(double x);
|
||||
public abstract double changeY(double y);
|
||||
|
||||
public abstract SHAPERECORD toBasicRecord();
|
||||
|
||||
public static ShapeRecordAdvanced createFromSHAPERECORD(SHAPERECORD rec) {
|
||||
if(rec instanceof StyleChangeRecord) {
|
||||
return new StyleChangeRecordAdvanced((StyleChangeRecord)rec);
|
||||
}
|
||||
if(rec instanceof CurvedEdgeRecord) {
|
||||
return new CurvedEdgeRecordAdvanced((CurvedEdgeRecord)rec);
|
||||
}
|
||||
if(rec instanceof StraightEdgeRecord) {
|
||||
return new StraightEdgeRecordAdvanced((StraightEdgeRecord)rec);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void round();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2022 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class StraightEdgeRecordAdvanced extends ShapeRecordAdvanced {
|
||||
public double deltaX;
|
||||
public double deltaY;
|
||||
|
||||
public StraightEdgeRecordAdvanced() {
|
||||
}
|
||||
|
||||
public StraightEdgeRecordAdvanced(double deltaX, double deltaY) {
|
||||
this.deltaX = deltaX;
|
||||
this.deltaY = deltaY;
|
||||
}
|
||||
|
||||
public StraightEdgeRecordAdvanced(StraightEdgeRecord ser) {
|
||||
this.deltaX = ser.deltaX;
|
||||
this.deltaY = ser.deltaY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double changeX(double x) {
|
||||
return x + deltaX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double changeY(double y) {
|
||||
return y + deltaY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StraightEdgeRecord toBasicRecord() {
|
||||
StraightEdgeRecord ret = new StraightEdgeRecord();
|
||||
ret.generalLineFlag = true;
|
||||
ret.deltaX = (int)Math.round(deltaX);
|
||||
ret.deltaY = (int)Math.round(deltaY);
|
||||
ret.simplify();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void round() {
|
||||
deltaX = Math.round(deltaX);
|
||||
deltaY = Math.round(deltaY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2022 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class StyleChangeRecordAdvanced extends ShapeRecordAdvanced{
|
||||
public boolean stateNewStyles;
|
||||
|
||||
public boolean stateLineStyle;
|
||||
|
||||
public boolean stateFillStyle1;
|
||||
|
||||
public boolean stateFillStyle0;
|
||||
|
||||
public boolean stateMoveTo;
|
||||
|
||||
public double moveDeltaX;
|
||||
|
||||
public double moveDeltaY;
|
||||
|
||||
public int fillStyle0;
|
||||
|
||||
public int fillStyle1;
|
||||
|
||||
public int lineStyle;
|
||||
|
||||
public FILLSTYLEARRAY fillStyles;
|
||||
|
||||
public LINESTYLEARRAY lineStyles;
|
||||
|
||||
public StyleChangeRecordAdvanced() {
|
||||
|
||||
}
|
||||
public StyleChangeRecordAdvanced(StyleChangeRecord scr) {
|
||||
this.stateNewStyles = scr.stateNewStyles;
|
||||
this.stateLineStyle = scr.stateLineStyle;
|
||||
this.stateFillStyle0 = scr.stateFillStyle0;
|
||||
this.stateFillStyle1 = scr.stateFillStyle1;
|
||||
this.stateMoveTo = scr.stateMoveTo;
|
||||
this.moveDeltaX = scr.moveDeltaX;
|
||||
this.moveDeltaY = scr.moveDeltaY;
|
||||
this.fillStyle0 = scr.fillStyle0;
|
||||
this.fillStyle1 = scr.fillStyle1;
|
||||
this.lineStyle = scr.lineStyle;
|
||||
this.fillStyles = scr.fillStyles;
|
||||
this.lineStyles = scr.lineStyles;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public StyleChangeRecord toBasicRecord() {
|
||||
StyleChangeRecord ret = new StyleChangeRecord();
|
||||
ret.stateNewStyles = this.stateNewStyles;
|
||||
ret.stateLineStyle = this.stateLineStyle;
|
||||
ret.stateFillStyle0 = this.stateFillStyle0;
|
||||
ret.stateFillStyle1 = this.stateFillStyle1;
|
||||
ret.stateMoveTo = this.stateMoveTo;
|
||||
ret.moveDeltaX = (int)Math.round(this.moveDeltaX);
|
||||
ret.moveDeltaY = (int)Math.round(this.moveDeltaY);
|
||||
ret.fillStyle0 = this.fillStyle0;
|
||||
ret.fillStyle1 = this.fillStyle1;
|
||||
ret.lineStyle = this.lineStyle;
|
||||
ret.fillStyles = this.fillStyles;
|
||||
ret.lineStyles = this.lineStyles;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double changeX(double x) {
|
||||
if (stateMoveTo) {
|
||||
return moveDeltaX;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double changeY(double y) {
|
||||
if (stateMoveTo) {
|
||||
return moveDeltaY;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void round() {
|
||||
if (stateMoveTo) {
|
||||
moveDeltaX = Math.round(moveDeltaX);
|
||||
moveDeltaY = Math.round(moveDeltaY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user