mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-05 11:14:50 +00:00
Do not show depths in FLA layer name
This commit is contained in:
@@ -249,7 +249,7 @@ public class BitmapExporter extends ShapeExporterBase {
|
||||
finalizePath();
|
||||
if (color == null) {
|
||||
fillPaint = defaultColor;
|
||||
} else {
|
||||
} else {
|
||||
fillPaint = color.toColor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.jpexs.decompiler.flash.types.LINESTYLE2;
|
||||
import com.jpexs.decompiler.flash.types.RGB;
|
||||
import com.jpexs.decompiler.flash.types.RGBA;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -55,9 +54,7 @@ public class ShapeForMorphExporter extends ShapeExporterBase {
|
||||
private int currentSegmentCount = 0;
|
||||
private int currentLineStyle = -1;
|
||||
private int currentFillStyle = -1;
|
||||
|
||||
double currentShapeLen = 0;
|
||||
|
||||
|
||||
public List<FILLSTYLE> fillStyles = new ArrayList<>();
|
||||
public List<LINESTYLE2> lineStyles = new ArrayList<>();
|
||||
|
||||
@@ -294,7 +291,6 @@ public class ShapeForMorphExporter extends ShapeExporterBase {
|
||||
fillStyleIndices.add(currentFillStyle);
|
||||
lineStyleIndices.add(currentLineStyle);
|
||||
}
|
||||
currentShapeLen = 0;
|
||||
currentShape = new ArrayList<>();
|
||||
currentBezierLengths = new ArrayList<>();
|
||||
currentPointsSum = new Point2D.Double();
|
||||
@@ -397,7 +393,6 @@ public class ShapeForMorphExporter extends ShapeExporterBase {
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
currentBezierLengths.add(be.length());
|
||||
currentShapeLen += be.length();
|
||||
|
||||
currentSegmentCount++;
|
||||
}
|
||||
@@ -419,7 +414,6 @@ public class ShapeForMorphExporter extends ShapeExporterBase {
|
||||
lastX = anchorX;
|
||||
lastY = anchorY;
|
||||
currentBezierLengths.add(be.length());
|
||||
currentShapeLen += be.length();
|
||||
|
||||
currentSegmentCount++;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,12 @@ import com.jpexs.helpers.Reference;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.Serializable;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
@@ -36,6 +40,21 @@ public class BezierEdge implements Serializable {
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BezierEdge clone() {
|
||||
return new BezierEdge(new ArrayList<>(points));
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
Point2D p1 = getBeginPoint();
|
||||
for (int i = 1; i < points.size(); i++) {
|
||||
if (!points.get(i).equals(p1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public BezierEdge(double x0, double y0, double x1, double y1) {
|
||||
points.add(new Point2D.Double(x0, y0));
|
||||
points.add(new Point2D.Double(x1, y1));
|
||||
@@ -50,11 +69,19 @@ public class BezierEdge implements Serializable {
|
||||
public Point2D getBeginPoint() {
|
||||
return points.get(0);
|
||||
}
|
||||
|
||||
|
||||
public Point2D getEndPoint() {
|
||||
return points.get(points.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
public void setBeginPoint(Point2D p) {
|
||||
points.set(0, p);
|
||||
}
|
||||
|
||||
public void setEndPoint(Point2D p) {
|
||||
points.set(points.size() - 1, p);
|
||||
}
|
||||
|
||||
public Point2D pointAt(double t) {
|
||||
if (points.size() == 2) {
|
||||
double x = (1 - t) * points.get(0).getX() + t * points.get(1).getX();
|
||||
@@ -100,7 +127,7 @@ public class BezierEdge implements Serializable {
|
||||
return b;
|
||||
}
|
||||
|
||||
private final double MIN_SIZE = 1.0;
|
||||
private final double MIN_SIZE = 0.5;
|
||||
|
||||
public double area() {
|
||||
Rectangle2D rect = bbox();
|
||||
@@ -134,12 +161,82 @@ public class BezierEdge implements Serializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean intersects(BezierEdge b2, List<Double> t1Ref, List<Double> t2Ref) {
|
||||
return intersects(b2,
|
||||
public List<Point2D> getIntersections(BezierEdge b2) {
|
||||
if (points.size() == 2) {
|
||||
if (b2.points.size() == 2) {
|
||||
return Intersections.intersectLineLine(points.get(0), points.get(1), b2.points.get(0), b2.points.get(1), true);
|
||||
} else {
|
||||
return Intersections.intersectBezier2Line(b2.points.get(0), b2.points.get(1), b2.points.get(2), points.get(0), points.get(1));
|
||||
}
|
||||
} else {
|
||||
if (b2.points.size() == 2) {
|
||||
return Intersections.intersectBezier2Line(points.get(0), points.get(1), points.get(2), b2.points.get(0), b2.points.get(1));
|
||||
} else {
|
||||
return Intersections.intersectBezier2Bezier2(points.get(0), points.get(1), points.get(2), b2.points.get(0), b2.points.get(1), b2.points.get(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean intersectsOld(BezierEdge b2, List<Double> t1Ref, List<Double> t2Ref) {
|
||||
List<Point2D> interPoints = new ArrayList<>();
|
||||
List<Double> t1RefA = new ArrayList<>();
|
||||
List<Double> t2RefA = new ArrayList<>();
|
||||
boolean ret = intersects(b2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1, t1Ref, t2Ref);
|
||||
1, t1RefA, t2RefA, interPoints);
|
||||
Point2D last = new Point2D.Double(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||
int numSame = 0;
|
||||
double sumT1 = 0;
|
||||
double sumT2 = 0;
|
||||
for (int i = 0; i < interPoints.size(); i++) {
|
||||
double dist = interPoints.get(i).distance(last);
|
||||
System.err.println("dist=" + dist);
|
||||
if (dist <= 5.0) {
|
||||
numSame++;
|
||||
sumT1 += t1RefA.get(i);
|
||||
sumT2 += t2RefA.get(i);
|
||||
last = interPoints.get(i);
|
||||
} else {
|
||||
if (numSame > 0) {
|
||||
t1Ref.add(sumT1 / numSame);
|
||||
t2Ref.add(sumT2 / numSame);
|
||||
}
|
||||
numSame = 1;
|
||||
last = interPoints.get(i);
|
||||
sumT1 = t1RefA.get(i);
|
||||
sumT2 = t2RefA.get(i);
|
||||
}
|
||||
}
|
||||
if (numSame > 0) {
|
||||
t1Ref.add(sumT1 / numSame);
|
||||
t2Ref.add(sumT2 / numSame);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public boolean intersects(BezierEdge b2, List<Double> t1Ref, List<Double> t2Ref, List<Point2D> intPoints) {
|
||||
List<Point2D> inter = getIntersections(b2);
|
||||
BezierUtils utils = new BezierUtils();
|
||||
for (Point2D p : inter) {
|
||||
Point2D p1;
|
||||
Point2D p3;
|
||||
Point2D p2;
|
||||
p1 = this.points.get(0);
|
||||
p3 = this.points.get(this.points.size() - 1);
|
||||
p2 = this.points.size() == 3 ? this.points.get(1) : new Point2D.Double((p1.getX() + p3.getX()) / 2, (p1.getY() + p3.getY()) / 2);
|
||||
|
||||
t1Ref.add(utils.closestPointToBezier(p, p1, p2, p3));
|
||||
|
||||
p1 = b2.points.get(0);
|
||||
p3 = b2.points.get(b2.points.size() - 1);
|
||||
p2 = b2.points.size() == 3 ? b2.points.get(1) : new Point2D.Double((p1.getX() + p3.getX()) / 2, (p1.getY() + p3.getY()) / 2);
|
||||
t2Ref.add(utils.closestPointToBezier(p, p1, p2, p3));
|
||||
}
|
||||
intPoints.addAll(inter);
|
||||
return !inter.isEmpty();
|
||||
}
|
||||
|
||||
private boolean intersects(
|
||||
@@ -149,7 +246,9 @@ public class BezierEdge implements Serializable {
|
||||
double start2,
|
||||
double end2,
|
||||
List<Double> t1Ref,
|
||||
List<Double> t2Ref) {
|
||||
List<Double> t2Ref,
|
||||
List<Point2D> interPoints
|
||||
) {
|
||||
final double threshold = MIN_SIZE * 2.0; //?
|
||||
Rectangle2D bb1 = bbox();
|
||||
Rectangle2D bb2 = b2.bbox();
|
||||
@@ -161,7 +260,8 @@ public class BezierEdge implements Serializable {
|
||||
if (Double.compare(sumAreas, threshold) <= 0) {
|
||||
double t1 = (start1 + end1) / 2;
|
||||
double t2 = (start2 + end2) / 2;
|
||||
|
||||
Point2D selPoint = getBeginPoint();
|
||||
interPoints.add(selPoint);
|
||||
t1Ref.add(t1);
|
||||
t2Ref.add(t2);
|
||||
return true;
|
||||
@@ -185,26 +285,25 @@ public class BezierEdge implements Serializable {
|
||||
double half2 = start2 + (end2 - start2) / 2.0;
|
||||
|
||||
boolean ok = false;
|
||||
if (b1a.intersects(b2a, start1, half1, start2, half2, t1Ref, t2Ref)) {
|
||||
if (b1a.intersects(b2a, start1, half1, start2, half2, t1Ref, t2Ref, interPoints)) {
|
||||
ok = true;
|
||||
}
|
||||
if (b1a.intersects(b2b, start1, half1, half2, end2, t1Ref, t2Ref)) {
|
||||
if (b1a.intersects(b2b, start1, half1, half2, end2, t1Ref, t2Ref, interPoints)) {
|
||||
ok = true;
|
||||
}
|
||||
if (b1b.intersects(b2a, half1, end1, start2, half2, t1Ref, t2Ref)) {
|
||||
if (b1b.intersects(b2a, half1, end1, start2, half2, t1Ref, t2Ref, interPoints)) {
|
||||
ok = true;
|
||||
}
|
||||
if (b1b.intersects(b2b, half1, end1, half2, end2, t1Ref, t2Ref)) {
|
||||
if (b1b.intersects(b2b, half1, end1, half2, end2, t1Ref, t2Ref, interPoints)) {
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
public double length() {
|
||||
double distance = 0;
|
||||
double epsilon = 1;
|
||||
|
||||
|
||||
Stack<BezierEdge> parts = new Stack<BezierEdge>();
|
||||
parts.push(this);
|
||||
|
||||
@@ -233,15 +332,44 @@ public class BezierEdge implements Serializable {
|
||||
return "{" + String.join("-", list) + "}";
|
||||
}
|
||||
|
||||
public String toSvg() {
|
||||
|
||||
DecimalFormat df = new DecimalFormat("0.###", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
|
||||
df.setGroupingUsed(false);
|
||||
|
||||
String ret = "";
|
||||
ret += "M ";
|
||||
ret += df.format(points.get(0).getX());
|
||||
ret += " ";
|
||||
ret += df.format(points.get(0).getY());
|
||||
ret += " ";
|
||||
if (points.size() == 3) {
|
||||
ret += "Q ";
|
||||
} else {
|
||||
ret += "L ";
|
||||
}
|
||||
ret += df.format(points.get(1).getX());
|
||||
ret += " ";
|
||||
ret += df.format(points.get(1).getY());
|
||||
if (points.size() == 3) {
|
||||
ret += " ";
|
||||
ret += df.format(points.get(2).getX());
|
||||
ret += " ";
|
||||
ret += df.format(points.get(2).getY());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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));
|
||||
rightPoints.set(0, leftPoints.get(leftPoints.size() - 1));
|
||||
right.setVal(new BezierEdge(rightPoints));
|
||||
}
|
||||
|
||||
|
||||
public BezierEdge reverse() {
|
||||
List<Point2D> revPoints = new ArrayList<>();
|
||||
for (int i = points.size() - 1; i >= 0; i--) {
|
||||
@@ -250,6 +378,46 @@ public class BezierEdge implements Serializable {
|
||||
return new BezierEdge(revPoints);
|
||||
}
|
||||
|
||||
public void round() {
|
||||
for (int i = 0; i < this.points.size(); i++) {
|
||||
this.points.set(i, new Point2D.Double(
|
||||
Math.round(this.points.get(i).getX()),
|
||||
Math.round(this.points.get(i).getY())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public void roundHalf() {
|
||||
for (int i = 0; i < this.points.size(); i++) {
|
||||
this.points.set(i, new Point2D.Double(
|
||||
Math.round(this.points.get(i).getX() * 2.0) / 2.0,
|
||||
Math.round(this.points.get(i).getY() * 2.0) / 2.0
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 41 * hash + Objects.hashCode(this.points);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final BezierEdge other = (BezierEdge) obj;
|
||||
return Objects.equals(this.points, other.points);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<Double> t1 = new ArrayList<>();
|
||||
List<Double> t2 = new ArrayList<>();
|
||||
@@ -276,12 +444,48 @@ public class BezierEdge implements Serializable {
|
||||
BezierEdge be5 = new BezierEdge(25, 0, 25, 100);
|
||||
BezierEdge be6 = new BezierEdge(0, 50, 100, 50);
|
||||
|
||||
System.out.println("lines " + be5 + " and " + be6);
|
||||
//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);
|
||||
|
||||
System.out.println("hasIntersection = " + be5.intersects(be6, t1, t2));
|
||||
System.out.println("intersection ts: " + t1 + ", " + t2);
|
||||
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);*/
|
||||
|
||||
Point2D c = new Point2D.Double(
|
||||
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);
|
||||
|
||||
List<Point2D> ps = new ArrayList<>();
|
||||
qg.intersects(qh, t1, t2, ps);
|
||||
System.err.println("t1 is " + t1);
|
||||
System.err.println("t2 is " + t2);
|
||||
System.err.println("intersections is " + ps);
|
||||
|
||||
|
||||
/*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()
|
||||
);
|
||||
@@ -290,8 +494,7 @@ public class BezierEdge implements Serializable {
|
||||
|
||||
BezierEdge be = new BezierEdge(0, 0, 100, 50, 0, 100);
|
||||
|
||||
System.out.println("be5.dist: " + be.length());
|
||||
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2023 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.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Intersection calculating. Based on Node.js library kld-intersections:
|
||||
* https://github.com/thelonious/kld-intersections/
|
||||
*/
|
||||
public class Intersections {
|
||||
|
||||
private static final double FLATNESS = 0.01;
|
||||
|
||||
private static Point2D min(Point2D p1, Point2D p2) {
|
||||
return new Point2D.Double(Math.min(p1.getX(), p2.getX()), Math.min(p1.getY(), p2.getY()));
|
||||
}
|
||||
|
||||
private static Point2D max(Point2D p1, Point2D p2) {
|
||||
return new Point2D.Double(Math.max(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()));
|
||||
}
|
||||
|
||||
private static Point2D multiply(Point2D p, double scalar) {
|
||||
return new Point2D.Double(p.getX() * scalar, p.getY() * scalar);
|
||||
}
|
||||
|
||||
private static Point2D add(Point2D p1, Point2D p2) {
|
||||
return new Point2D.Double(p1.getX() + p2.getX(), p1.getY() + p2.getY());
|
||||
}
|
||||
|
||||
private static Point2D lerp(Point2D p1, Point2D p2, double t) {
|
||||
double omt = 1.0 - t;
|
||||
|
||||
return new Point2D.Double(
|
||||
p1.getX() * omt + p2.getX() * t,
|
||||
p1.getY() * omt + p2.getY() * t
|
||||
);
|
||||
}
|
||||
|
||||
private static List<Point2D> intersectLinePolyline(Point2D a1, Point2D a2, List<Point2D> points) {
|
||||
List<Point2D> result = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < points.size() - 1; i++) {
|
||||
Point2D b1 = points.get(i);
|
||||
Point2D b2 = points.get(i + 1);
|
||||
List<Point2D> inter = intersectLineLine(a1, a2, b1, b2, false);
|
||||
for (Point2D p : inter) { //???
|
||||
if (!result.contains(p)) {
|
||||
result.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Point2D> intersectPolylinePolyline(List<Point2D> points1, List<Point2D> points2) {
|
||||
List<Point2D> result = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < points1.size() - 1; i++) {
|
||||
Point2D a1 = points1.get(i);
|
||||
Point2D a2 = points1.get(i + 1);
|
||||
List<Point2D> inter = intersectLinePolyline(a1, a2, points2);
|
||||
|
||||
result.addAll(inter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Point2D vectorFromPoints(Point2D p1, Point2D p2) {
|
||||
return new Point2D.Double(
|
||||
p2.getX() - p1.getX(),
|
||||
p2.getY() - p1.getY()
|
||||
);
|
||||
}
|
||||
|
||||
private static Point2D subtract(Point2D p1, Point2D p2) {
|
||||
return new Point2D.Double(p1.getX() - p2.getX(), p1.getY() - p2.getY());
|
||||
}
|
||||
|
||||
private static Point2D project(Point2D p1, Point2D that) {
|
||||
double percent = dot(p1, that) / dot(that, that);
|
||||
|
||||
return multiply(that, percent);
|
||||
}
|
||||
|
||||
private static Point2D perpendicular(Point2D p1, Point2D that) {
|
||||
return subtract(p1, project(p1, that));
|
||||
}
|
||||
|
||||
private static double vectorLength(Point2D p) {
|
||||
return Math.sqrt(p.getX() * p.getX() + p.getY() * p.getY());
|
||||
}
|
||||
|
||||
private static void tesselateInterior(double flatness, Point2D zeroVector, Point2D p1, Point2D p2, Point2D p3, List<Point2D> points) {
|
||||
// round 1
|
||||
Point2D p4 = lerp(p1, p2, 0.5);
|
||||
Point2D p5 = lerp(p2, p3, 0.5);
|
||||
|
||||
// round 2
|
||||
Point2D p6 = lerp(p4, p5, 0.5);
|
||||
|
||||
Point2D baseline = vectorFromPoints(p1, p3);
|
||||
Point2D tangent = vectorFromPoints(p1, p2);
|
||||
double dmax = 0;
|
||||
|
||||
if (!zeroVector.equals(tangent)) {
|
||||
Point2D perpendicular = perpendicular(baseline, tangent);
|
||||
|
||||
dmax = vectorLength(perpendicular);
|
||||
}
|
||||
|
||||
if (dmax > flatness) {
|
||||
tesselateInterior(flatness, zeroVector, p1, p4, p6, points);
|
||||
points.add(new Point2D.Double(p6.getX(), p6.getY()));
|
||||
tesselateInterior(flatness, zeroVector, p6, p5, p3, points);
|
||||
} else {
|
||||
points.add(new Point2D.Double(p6.getX(), p6.getY()));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Point2D> quadraticBezierToToPolyline(Point2D p1, Point2D p2, Point2D p3) {
|
||||
return quadraticBezierToToPolyline(p1, p2, p3, null);
|
||||
}
|
||||
|
||||
public static List<Point2D> quadraticBezierToToPolyline(Point2D p1, Point2D p2, Point2D p3, Double flatness) {
|
||||
List<Point2D> points = new ArrayList<>();
|
||||
Point2D zeroVector = new Point2D.Double(0, 0);
|
||||
|
||||
flatness = flatness != null ? flatness : 1.0;
|
||||
|
||||
// add first point
|
||||
points.add(p1);
|
||||
|
||||
// add interior points
|
||||
tesselateInterior(flatness, zeroVector, p1, p2, p3, points);
|
||||
|
||||
// add last point
|
||||
points.add(p3);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
public static List<Point2D> intersectBezier2Bezier2Slow(Point2D a1, Point2D a2, Point2D a3, Point2D b1, Point2D b2, Point2D b3) {
|
||||
List<Point2D> a = quadraticBezierToToPolyline(a1, a2, a3, FLATNESS);
|
||||
List<Point2D> b = quadraticBezierToToPolyline(b1, b2, b3, FLATNESS);
|
||||
return intersectPolylinePolyline(a, b);
|
||||
}
|
||||
|
||||
public static List<Point2D> intersectBezier2Bezier2(Point2D a1, Point2D a2, Point2D a3, Point2D b1, Point2D b2, Point2D b3) {
|
||||
Point2D pa;
|
||||
Point2D pb;
|
||||
List<Point2D> result = new ArrayList<>();
|
||||
pa = multiply(a2, -2);
|
||||
Point2D x = add(pa, a3);
|
||||
Point2D c12 = add(a1, x);
|
||||
|
||||
pa = multiply(a1, -2);
|
||||
pb = multiply(a2, 2);
|
||||
Point2D c11 = add(pa, pb);
|
||||
|
||||
Point2D c10 = new Point2D.Double(a1.getX(), a1.getY());
|
||||
|
||||
pa = multiply(b2, -2);
|
||||
Point2D c22 = add(b1, add(pa, b3));
|
||||
|
||||
pa = multiply(b1, -2);
|
||||
pb = multiply(b2, 2);
|
||||
Point2D c21 = add(pa, pb);
|
||||
|
||||
Point2D c20 = new Point2D.Double(b1.getX(), b1.getY());
|
||||
|
||||
// bezout
|
||||
double a = c12.getX() * c11.getY() - c11.getX() * c12.getY();
|
||||
double b = c22.getX() * c11.getY() - c11.getX() * c22.getY();
|
||||
double c = c21.getX() * c11.getY() - c11.getX() * c21.getY();
|
||||
double d = c11.getX() * (c10.getY() - c20.getY()) + c11.getY() * (-c10.getX() + c20.getX());
|
||||
double e = c22.getX() * c12.getY() - c12.getX() * c22.getY();
|
||||
double f = c21.getX() * c12.getY() - c12.getX() * c21.getY();
|
||||
double g = c12.getX() * (c10.getY() - c20.getY()) + c12.getY() * (-c10.getX() + c20.getX());
|
||||
|
||||
// determinant
|
||||
Polynomial poly = new Polynomial(Arrays.asList(
|
||||
-e * e,
|
||||
-2 * e * f,
|
||||
a * b - f * f - 2 * e * g,
|
||||
a * c - 2 * f * g,
|
||||
a * d - g * g
|
||||
)
|
||||
);
|
||||
|
||||
List<Double> roots = poly.getRoots();
|
||||
for (double s : roots) {
|
||||
if (0 <= s && s <= 1) {
|
||||
Polynomial xp = new Polynomial(Arrays.asList(
|
||||
c12.getX(),
|
||||
c11.getX(),
|
||||
c10.getX() - c20.getX() - s * c21.getX() - s * s * c22.getX()
|
||||
)
|
||||
);
|
||||
xp.simplifyEquals();
|
||||
List<Double> xRoots = xp.getRoots();
|
||||
Polynomial yp = new Polynomial(Arrays.asList(
|
||||
c12.getY(),
|
||||
c11.getY(),
|
||||
c10.getY() - c20.getY() - s * c21.getY() - s * s * c22.getY()
|
||||
)
|
||||
);
|
||||
yp.simplifyEquals();
|
||||
List<Double> yRoots = yp.getRoots();
|
||||
|
||||
if (!xRoots.isEmpty() && !yRoots.isEmpty()) {
|
||||
double TOLERANCE = 1e-4;
|
||||
|
||||
checkRoots:
|
||||
for (double xRoot : xRoots) {
|
||||
if (0 <= xRoot && xRoot <= 1) {
|
||||
for (int k = 0; k < yRoots.size(); k++) {
|
||||
if (Math.abs(xRoot - yRoots.get(k)) < TOLERANCE) {
|
||||
result.add(add(multiply(c22, s * s), (add(multiply(c21, s), c20))));
|
||||
break checkRoots;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//JPEXS: fix rounding errors
|
||||
if (a1.equals(b1) && !result.contains(a1)) {
|
||||
result.add(0, a1);
|
||||
}
|
||||
if (a3.equals(b3) && !result.contains(a3)) {
|
||||
result.add(a3);
|
||||
}
|
||||
if (a1.equals(b3) && !result.contains(a1)) {
|
||||
result.add(0, a1);
|
||||
}
|
||||
if (a3.equals(b1) && !result.contains(a3)) {
|
||||
result.add(0, a3);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static double dot(Point2D p1, Point2D p2) {
|
||||
return p1.getX() * p2.getX() + p1.getY() * p2.getY();
|
||||
}
|
||||
|
||||
public static List<Point2D> intersectBezier2LineSlow(Point2D p1, Point2D p2, Point2D p3, Point2D a1, Point2D a2) {
|
||||
List<Point2D> p = quadraticBezierToToPolyline(p1, p2, p3, FLATNESS);
|
||||
return intersectLinePolyline(a1, a2, p);
|
||||
}
|
||||
|
||||
public static List<Point2D> intersectBezier2Line(Point2D p1, Point2D p2, Point2D p3, Point2D a1, Point2D a2) {
|
||||
Point2D a; // temporary variables
|
||||
Point2D min = min(a1, a2); // used to determine if point is on line segment
|
||||
Point2D max = max(a1, a2); // used to determine if point is on line segment
|
||||
List<Point2D> result = new ArrayList<>();
|
||||
|
||||
a = multiply(p2, -2);
|
||||
Point2D c2 = add(p1, add(a, p3));
|
||||
|
||||
a = multiply(p1, -2);
|
||||
Point2D b = multiply(p2, 2);
|
||||
Point2D c1 = add(a, b);
|
||||
|
||||
Point2D c0 = new Point2D.Double(p1.getX(), p1.getY());
|
||||
|
||||
// Convert line to normal form: ax + by + c = 0
|
||||
// Find normal to line: negative inverse of original line's slope
|
||||
Point2D n = new Point2D.Double(a1.getY() - a2.getY(), a2.getX() - a1.getX());
|
||||
|
||||
// Determine new c coefficient
|
||||
double cl = a1.getX() * a2.getY() - a2.getX() * a1.getY();
|
||||
|
||||
// Transform cubic coefficients to line's coordinate system and find roots
|
||||
// of cubic
|
||||
List<Double> roots = new Polynomial(
|
||||
Arrays.asList(
|
||||
dot(n, c2),
|
||||
dot(n, c1),
|
||||
dot(n, c0) + cl
|
||||
)
|
||||
).getRoots();
|
||||
|
||||
// Any roots in closed interval [0,1] are intersections on Bezier, but
|
||||
// might not be on the line segment.
|
||||
// Find intersections and calculate point coordinates
|
||||
for (double t : roots) {
|
||||
if (0 <= t && t <= 1) {
|
||||
// We're within the Bezier curve
|
||||
// Find point on Bezier
|
||||
Point2D p4 = lerp(p1, p2, t);
|
||||
Point2D p5 = lerp(p2, p3, t);
|
||||
|
||||
Point2D p6 = lerp(p4, p5, t);
|
||||
|
||||
// See if point is on line segment
|
||||
// Had to make special cases for vertical and horizontal lines due
|
||||
// to slight errors in calculation of p6
|
||||
if (a1.getX() == a2.getX()) {
|
||||
if (min.getY() <= p6.getY() && p6.getY() <= max.getY()) {
|
||||
result.add(p6);
|
||||
}
|
||||
} else if (a1.getY() == a2.getY()) {
|
||||
if (min.getX() <= p6.getX() && p6.getX() <= max.getX()) {
|
||||
result.add(p6);
|
||||
}
|
||||
} else if (min.getX() <= p6.getX() && p6.getX() <= max.getX() && min.getY() <= p6.getY() && p6.getY() <= max.getY()) {
|
||||
result.add(p6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//JPEXS: fix rounding errors
|
||||
if (a1.equals(p1) && !result.contains(a1)) {
|
||||
result.add(0, a1);
|
||||
}
|
||||
if (a2.equals(p3) && !result.contains(a2)) {
|
||||
result.add(a2);
|
||||
}
|
||||
if (a1.equals(p3) && !result.contains(a1)) {
|
||||
result.add(0, a1);
|
||||
}
|
||||
if (a2.equals(p1) && !result.contains(a2)) {
|
||||
result.add(0, a2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<Point2D> intersectLineLine(Point2D a1, Point2D a2, Point2D b1, Point2D b2, boolean addCoincident) {
|
||||
List<Point2D> result = new ArrayList<>();
|
||||
|
||||
double ua_t = (b2.getX() - b1.getX()) * (a1.getY() - b1.getY()) - (b2.getY() - b1.getY()) * (a1.getX() - b1.getX());
|
||||
double ub_t = (a2.getX() - a1.getX()) * (a1.getY() - b1.getY()) - (a2.getY() - a1.getY()) * (a1.getX() - b1.getX());
|
||||
double u_b = (b2.getY() - b1.getY()) * (a2.getX() - a1.getX()) - (b2.getX() - b1.getX()) * (a2.getY() - a1.getY());
|
||||
|
||||
if (u_b != 0) {
|
||||
double ua = ua_t / u_b;
|
||||
double ub = ub_t / u_b;
|
||||
|
||||
if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
|
||||
result.add(
|
||||
new Point2D.Double(
|
||||
a1.getX() + ua * (a2.getX() - a1.getX()),
|
||||
a1.getY() + ua * (a2.getY() - a1.getY())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
//No Intersection
|
||||
}
|
||||
} else if (ua_t == 0 || ub_t == 0) {
|
||||
if (!addCoincident) {
|
||||
return result;
|
||||
}
|
||||
//WARNING: This is actually not an intersection,
|
||||
//but Coincident. But we treat it equally
|
||||
|
||||
double a1v;
|
||||
double a2v;
|
||||
double b1v;
|
||||
double b2v;
|
||||
|
||||
if (!(a1.getX() == b1.getX() && a2.getX() == b2.getX() && a1.getX() == a2.getX())) {
|
||||
a1v = a1.getX();
|
||||
a2v = a2.getX();
|
||||
b1v = b1.getX();
|
||||
b2v = b2.getX();
|
||||
} else {
|
||||
a1v = a1.getY();
|
||||
a2v = a2.getY();
|
||||
b1v = b1.getY();
|
||||
b2v = b2.getY();
|
||||
}
|
||||
|
||||
if (a1v > a2v) {
|
||||
double td;
|
||||
td = a1v;
|
||||
a1v = a2v;
|
||||
a2v = td;
|
||||
}
|
||||
|
||||
if (b1v > b2v) {
|
||||
double td;
|
||||
td = b1v;
|
||||
b1v = b2v;
|
||||
b2v = td;
|
||||
}
|
||||
|
||||
if (b1v < a1v) {
|
||||
//swap a, b
|
||||
|
||||
Point2D t;
|
||||
|
||||
t = b1;
|
||||
b1 = a1;
|
||||
a1 = t;
|
||||
|
||||
t = b2;
|
||||
b2 = a2;
|
||||
a2 = t;
|
||||
|
||||
double td;
|
||||
|
||||
td = b1v;
|
||||
b1v = a1v;
|
||||
a1v = td;
|
||||
|
||||
td = b2v;
|
||||
b2v = a2v;
|
||||
a2v = td;
|
||||
}
|
||||
|
||||
if (a2v < b1v) {
|
||||
//no overlap
|
||||
return result;
|
||||
}
|
||||
|
||||
if (a2v == b1v) {
|
||||
//single point, ignore
|
||||
return result;
|
||||
}
|
||||
//A1 B1 A2 B2
|
||||
// |----|----|----|
|
||||
result.add(b1);
|
||||
result.add(a2);
|
||||
} else {
|
||||
//Parallel
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2023 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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Based on Node.js library kld-polynomial:
|
||||
* https://github.com/thelonious/kld-polynomial
|
||||
*/
|
||||
public class Polynomial {
|
||||
|
||||
private double[] coefs;
|
||||
private String _variable;
|
||||
private int _s;
|
||||
|
||||
public Polynomial(List<Double> coefs) {
|
||||
this.coefs = new double[coefs.size()];
|
||||
|
||||
for (int i = 0; i < coefs.size(); i++) {
|
||||
this.coefs[i] = coefs.get(coefs.size() - 1 - i);
|
||||
}
|
||||
|
||||
this._variable = "t";
|
||||
this._s = 0;
|
||||
}
|
||||
|
||||
public int getDegree() {
|
||||
return this.coefs.length - 1;
|
||||
}
|
||||
|
||||
private List<Double> getLinearRoot() {
|
||||
List<Double> result = new ArrayList<>();
|
||||
double a = this.coefs[1];
|
||||
|
||||
if (a != 0) {
|
||||
result.add(-this.coefs[0] / a);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Double> getQuadraticRoots() {
|
||||
List<Double> results = new ArrayList<>();
|
||||
|
||||
if (this.getDegree() == 2) {
|
||||
double a = this.coefs[2];
|
||||
double b = this.coefs[1] / a;
|
||||
double c = this.coefs[0] / a;
|
||||
double d = b * b - 4 * c;
|
||||
|
||||
if (d > 0) {
|
||||
double e = Math.sqrt(d);
|
||||
|
||||
results.add(0.5 * (-b + e));
|
||||
results.add(0.5 * (-b - e));
|
||||
} else if (d == 0) {
|
||||
// really two roots with same value, but we only return one
|
||||
results.add(0.5 * -b);
|
||||
}
|
||||
// else imaginary results
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private boolean coefSelectionFunc(int i, int n, double[] a) {
|
||||
return i < n && a[i] < 0;
|
||||
}
|
||||
|
||||
private void find2Max(Reference<Double> max, Reference<Double> nearmax, double bi, int i, int n, double[] a) {
|
||||
if (coefSelectionFunc(i, n, a)) {
|
||||
if (max.getVal() < bi) {
|
||||
nearmax.setVal(max.getVal());
|
||||
max.setVal(bi);
|
||||
} else if (nearmax.getVal() < bi) {
|
||||
nearmax.setVal(bi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return negX, posX
|
||||
*/
|
||||
private double[] boundsUpperRealFujiwara() {
|
||||
double[] ax = this.coefs;
|
||||
double[] a = ax;
|
||||
int n = a.length - 1;
|
||||
double an = a[n];
|
||||
|
||||
if (an != 1) {
|
||||
a = new double[a.length];
|
||||
for (int i = 0; i < this.coefs.length; i++) {
|
||||
a[i] = ax[i] / an;
|
||||
}
|
||||
}
|
||||
|
||||
double[] b = new double[a.length];
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
double v = a[i];
|
||||
if (i < n) {
|
||||
b[i] = Math.pow(Math.abs((i == 0) ? v / 2 : v), 1 / (double) (n - i));
|
||||
} else {
|
||||
b[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line unicorn/no-fn-reference-in-iterator
|
||||
double max_pos = 0;
|
||||
double nearmax_pos = 0;
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
if (i < n && a[i] < 0) {
|
||||
if (max_pos < b[i]) {
|
||||
nearmax_pos = max_pos;
|
||||
max_pos = b[i];
|
||||
} else if (nearmax_pos < b[i]) {
|
||||
nearmax_pos = b[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double max_neg = 0;
|
||||
double nearmax_neg = 0;
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
if (i < n && ((n % 2 == i % 2) ? a[i] < 0 : a[i] > 0)) {
|
||||
if (max_neg < b[i]) {
|
||||
nearmax_neg = max_neg;
|
||||
max_neg = b[i];
|
||||
} else if (nearmax_neg < b[i]) {
|
||||
nearmax_neg = b[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new double[]{
|
||||
-2 * max_neg,
|
||||
2 * max_pos
|
||||
};
|
||||
}
|
||||
|
||||
private class Rect {
|
||||
|
||||
double minX;
|
||||
double minY;
|
||||
double maxX;
|
||||
double maxY;
|
||||
|
||||
public Rect(double minX, double minY, double maxX, double maxY) {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private double[] boundsLowerRealFujiwara() {
|
||||
Polynomial poly = new Polynomial(new ArrayList<>());
|
||||
|
||||
poly.coefs = new double[coefs.length];
|
||||
for (int i = 0; i < coefs.length; i++) {
|
||||
poly.coefs[coefs.length - 1 - i] = coefs[i];
|
||||
}
|
||||
|
||||
double[] res = poly.boundsUpperRealFujiwara();
|
||||
|
||||
res[0] = 1 / res[0];
|
||||
res[1] = 1 / res[1];
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private Rect bounds() {
|
||||
double[] urb = this.boundsUpperRealFujiwara();
|
||||
Rect rb = new Rect(urb[0], 0, urb[1], 0);
|
||||
|
||||
if (urb[0] == 0 && urb[1] == 0) {
|
||||
return rb;
|
||||
}
|
||||
|
||||
if (urb[0] == 0) {
|
||||
rb.minX = this.boundsLowerRealFujiwara()[1];
|
||||
} else if (urb[1] == 0) {
|
||||
rb.maxX = this.boundsLowerRealFujiwara()[0];
|
||||
}
|
||||
|
||||
if (rb.minX > rb.maxX) {
|
||||
rb.minX = rb.maxX = 0;
|
||||
}
|
||||
|
||||
return rb;
|
||||
// TODO: if sure that there are no complex roots
|
||||
// (maybe by using Sturm's theorem) use:
|
||||
// return this.boundsRealLaguerre();
|
||||
}
|
||||
|
||||
private double eval(double x) {
|
||||
if (Double.isNaN(x)) {
|
||||
throw new RuntimeException("Parameter must be a number");
|
||||
}
|
||||
|
||||
double result = 0;
|
||||
|
||||
for (int i = this.coefs.length - 1; i >= 0; i--) {
|
||||
result = result * x + this.coefs[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public double zeroErrorEstimate() {
|
||||
return zeroErrorEstimate(null);
|
||||
}
|
||||
|
||||
public double zeroErrorEstimate(Double maxAbsX) {
|
||||
Polynomial poly = this;
|
||||
double ERRF = 1e-15;
|
||||
|
||||
if (maxAbsX == null) {
|
||||
Rect rb = poly.bounds();
|
||||
|
||||
maxAbsX = Math.max(Math.abs(rb.minX), Math.abs(rb.maxX));
|
||||
}
|
||||
|
||||
if (maxAbsX < 0.001) {
|
||||
return 2 * Math.abs(poly.eval(ERRF));
|
||||
}
|
||||
|
||||
int n = poly.coefs.length - 1;
|
||||
double an = poly.coefs[n];
|
||||
|
||||
double m = 0;
|
||||
for (int i = 0; i < poly.coefs.length; i++) {
|
||||
double v = poly.coefs[i];
|
||||
double nm = v / an * Math.pow(maxAbsX, i);
|
||||
m = nm > m ? nm : m;
|
||||
}
|
||||
|
||||
double x = 10 * ERRF * m;
|
||||
return x;
|
||||
}
|
||||
|
||||
public List<Double> getCubicRoots() {
|
||||
List<Double> results = new ArrayList<>();
|
||||
|
||||
if (this.getDegree() == 3) {
|
||||
double c3 = this.coefs[3];
|
||||
double c2 = this.coefs[2] / c3;
|
||||
double c1 = this.coefs[1] / c3;
|
||||
double c0 = this.coefs[0] / c3;
|
||||
|
||||
double a = (3 * c1 - c2 * c2) / 3;
|
||||
double b = (2 * c2 * c2 * c2 - 9 * c1 * c2 + 27 * c0) / 27;
|
||||
double offset = c2 / 3;
|
||||
double discrim = b * b / 4 + a * a * a / 27;
|
||||
double halfB = b / 2;
|
||||
|
||||
double ZEROepsilon = this.zeroErrorEstimate();
|
||||
|
||||
if (Math.abs(discrim) <= ZEROepsilon) {
|
||||
discrim = 0;
|
||||
}
|
||||
|
||||
if (discrim > 0) {
|
||||
double e = Math.sqrt(discrim);
|
||||
double root; // eslint-disable-line no-shadow
|
||||
|
||||
double tmp = -halfB + e;
|
||||
|
||||
if (tmp >= 0) {
|
||||
root = Math.pow(tmp, 1 / 3.0);
|
||||
} else {
|
||||
root = -Math.pow(-tmp, 1 / 3.0);
|
||||
}
|
||||
|
||||
tmp = -halfB - e;
|
||||
|
||||
if (tmp >= 0) {
|
||||
root += Math.pow(tmp, 1 / 3.0);
|
||||
} else {
|
||||
root -= Math.pow(-tmp, 1 / 3.0);
|
||||
}
|
||||
|
||||
results.add(root - offset);
|
||||
} else if (discrim < 0) {
|
||||
double distance = Math.sqrt(-a / 3.0);
|
||||
double angle = Math.atan2(Math.sqrt(-discrim), -halfB) / 3.0;
|
||||
double cos = Math.cos(angle);
|
||||
double sin = Math.sin(angle);
|
||||
double sqrt3 = Math.sqrt(3);
|
||||
|
||||
results.add(2 * distance * cos - offset);
|
||||
results.add(-distance * (cos + sqrt3 * sin) - offset);
|
||||
results.add(-distance * (cos - sqrt3 * sin) - offset);
|
||||
} else {
|
||||
double tmp;
|
||||
|
||||
if (halfB >= 0) {
|
||||
tmp = -Math.pow(halfB, 1 / 3.0);
|
||||
} else {
|
||||
tmp = Math.pow(-halfB, 1 / 3.0);
|
||||
}
|
||||
|
||||
results.add(2 * tmp - offset);
|
||||
// really should return next root twice, but we return only one
|
||||
results.add(-tmp - offset);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public void simplifyEquals() {
|
||||
simplifyEquals(1e-12);
|
||||
}
|
||||
|
||||
public void simplifyEquals(double tolerance) {
|
||||
for (int i = this.getDegree(); i >= 0; i--) {
|
||||
if (Math.abs(this.coefs[i]) <= tolerance) {
|
||||
double[] newc = new double[this.coefs.length - 1];
|
||||
for (int j = 0; j < newc.length; j++) {
|
||||
newc[j] = this.coefs[j];
|
||||
}
|
||||
this.coefs = newc;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void divideEqualsScalar(double scalar) {
|
||||
for (int i = 0; i < this.coefs.length; i++) {
|
||||
this.coefs[i] /= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
private Polynomial getDerivative() {
|
||||
Polynomial pol = new Polynomial(new ArrayList<>());
|
||||
List<Double> newCoefs = new ArrayList<>();
|
||||
pol.coefs = new double[this.coefs.length - 1];
|
||||
for (int i = 1; i < this.coefs.length; i++) {
|
||||
pol.coefs[i - 1] = i * this.coefs[i];
|
||||
}
|
||||
|
||||
return pol;
|
||||
}
|
||||
|
||||
public List<Double> getRoots() {
|
||||
List<Double> result;
|
||||
|
||||
this.simplifyEquals();
|
||||
|
||||
switch (this.getDegree()) {
|
||||
case 0:
|
||||
result = new ArrayList<>();
|
||||
break;
|
||||
case 1:
|
||||
result = this.getLinearRoot();
|
||||
break;
|
||||
case 2:
|
||||
result = this.getQuadraticRoots();
|
||||
break;
|
||||
case 3:
|
||||
result = this.getCubicRoots();
|
||||
break;
|
||||
case 4:
|
||||
result = this.getQuarticRoots();
|
||||
break;
|
||||
default:
|
||||
result = new ArrayList<>();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Double sign(Double x) {
|
||||
// eslint-disable-next-line no-self-compare
|
||||
if (x == null) {
|
||||
return null;
|
||||
}
|
||||
return x < 0 ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
private interface DoubleFunc {
|
||||
|
||||
public double apply(double val);
|
||||
}
|
||||
|
||||
private static double newtonSecantBisection(double x0, DoubleFunc f, DoubleFunc df, int max_iterations, Double min, Double max) {
|
||||
double x;
|
||||
double prev_dfx = 0;
|
||||
double dfx;
|
||||
double prev_x_ef_correction = 0;
|
||||
double x_correction;
|
||||
double x_new;
|
||||
double y;
|
||||
Double y_atmin = null;
|
||||
Double y_atmax = null;
|
||||
|
||||
x = x0;
|
||||
|
||||
double ACCURACY = 14;
|
||||
double min_correction_factor = Math.pow(10, -ACCURACY);
|
||||
boolean isBounded = min != null && max != null;
|
||||
|
||||
if (isBounded) {
|
||||
if (min > max) {
|
||||
throw new RuntimeException("Min must be greater than max");
|
||||
}
|
||||
|
||||
y_atmin = f.apply(min);
|
||||
y_atmax = f.apply(max);
|
||||
|
||||
if (Double.compare(sign(y_atmin), sign(y_atmax)) == 0) {
|
||||
throw new RuntimeException("Y values of bounds must be of opposite sign");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < max_iterations; i++) {
|
||||
dfx = df.apply(x);
|
||||
|
||||
if (dfx == 0) {
|
||||
if (prev_dfx == 0) {
|
||||
// error
|
||||
throw new RuntimeException("df(x) is zero");
|
||||
} else {
|
||||
// use previous derivation value
|
||||
dfx = prev_dfx;
|
||||
}
|
||||
// or move x a little?
|
||||
// dfx = df(x != 0 ? x + x * 1e-15 : 1e-15);
|
||||
}
|
||||
|
||||
prev_dfx = dfx;
|
||||
y = f.apply(x);
|
||||
x_correction = y / dfx;
|
||||
x_new = x - x_correction;
|
||||
|
||||
boolean isEnoughCorrection = (Math.abs(x_correction) <= min_correction_factor * Math.abs(x))
|
||||
|| (prev_x_ef_correction == (x - x_correction) - x);
|
||||
if (isEnoughCorrection) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isBounded) {
|
||||
if (Double.compare(sign(y), sign(y_atmax)) == 0) {
|
||||
max = x;
|
||||
y_atmax = y;
|
||||
} else if (Double.compare(sign(y), sign(y_atmin)) == 0) {
|
||||
min = x;
|
||||
y_atmin = y;
|
||||
} else {
|
||||
x = x_new;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((x_new < min) || (x_new > max)) {
|
||||
if (Double.compare(sign(y_atmin), sign(y_atmax)) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
double RATIO_LIMIT = 50;
|
||||
double AIMED_BISECT_OFFSET = 0.25; // [0, 0.5)
|
||||
double dy = y_atmax - y_atmin;
|
||||
double dx = max - min;
|
||||
|
||||
if (dy == 0) {
|
||||
x_correction = x - (min + dx * 0.5);
|
||||
} else if (Math.abs(dy / Math.min(y_atmin, y_atmax)) > RATIO_LIMIT) {
|
||||
x_correction = x - (min + dx * (0.5 + (Math.abs(y_atmin) < Math.abs(y_atmax) ? -AIMED_BISECT_OFFSET : AIMED_BISECT_OFFSET)));
|
||||
} else {
|
||||
x_correction = x - (min - y_atmin / dy * dx);
|
||||
}
|
||||
x_new = x - x_correction;
|
||||
|
||||
isEnoughCorrection = (Math.abs(x_correction) <= min_correction_factor * Math.abs(x))
|
||||
|| (prev_x_ef_correction == (x - x_correction) - x);
|
||||
if (isEnoughCorrection) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prev_x_ef_correction = x - x_new;
|
||||
x = x_new;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
private List<Double> getQuarticRoots() {
|
||||
List<Double> results = new ArrayList<>();
|
||||
int n = this.getDegree();
|
||||
|
||||
if (n == 4) {
|
||||
Polynomial poly = new Polynomial(new ArrayList<>());
|
||||
|
||||
poly.coefs = Arrays.copyOf(this.coefs, this.coefs.length);
|
||||
poly.divideEqualsScalar(poly.coefs[n]);
|
||||
|
||||
double ERRF = 1e-15;
|
||||
|
||||
if (Math.abs(poly.coefs[0]) < 10 * ERRF * Math.abs(poly.coefs[3])) {
|
||||
poly.coefs[0] = 0;
|
||||
}
|
||||
|
||||
Polynomial poly_d = poly.getDerivative();
|
||||
List<Double> derrt = poly_d.getRoots();
|
||||
derrt.sort(new Comparator<Double>() {
|
||||
@Override
|
||||
public int compare(Double a, Double b) {
|
||||
if (Double.compare(a, b) == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (a - b < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
List<Double> dery = new ArrayList<>();
|
||||
int nr = derrt.size() - 1;
|
||||
Rect rb = this.bounds();
|
||||
|
||||
double maxabsX = Math.max(Math.abs(rb.minX), Math.abs(rb.maxX));
|
||||
double ZEROepsilon = this.zeroErrorEstimate(maxabsX);
|
||||
|
||||
for (int i = 0; i <= nr; i++) {
|
||||
dery.add(poly.eval(derrt.get(i)));
|
||||
}
|
||||
|
||||
for (int i = 0; i <= nr; i++) {
|
||||
if (Math.abs(dery.get(i)) < ZEROepsilon) {
|
||||
dery.set(i, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
double dx = Math.max(0.1 * (rb.maxX - rb.minX) / n, ERRF);
|
||||
List<Double> guesses = new ArrayList<>();
|
||||
List<double[]> minmax = new ArrayList<>();
|
||||
|
||||
if (nr > -1) {
|
||||
if (dery.get(0) != 0) {
|
||||
if (Double.compare(sign(dery.get(0)), sign(poly.eval(derrt.get(0) - dx) - dery.get(0))) != 0) {
|
||||
guesses.add(derrt.get(0) - dx);
|
||||
minmax.add(new double[]{rb.minX, derrt.get(0)});
|
||||
}
|
||||
} else {
|
||||
results.add(derrt.get(0));
|
||||
results.add(derrt.get(0));
|
||||
i++;
|
||||
}
|
||||
|
||||
for (; i < nr; i++) {
|
||||
if (dery.get(i + 1) == 0) {
|
||||
results.add(derrt.get(i + 1));
|
||||
results.add(derrt.get(i + 1));
|
||||
i++;
|
||||
} else if (Double.compare(sign(dery.get(i)), sign(dery.get(i + 1))) != 0) {
|
||||
guesses.add((derrt.get(i) + derrt.get(i + 1)) / 2);
|
||||
minmax.add(new double[]{derrt.get(i), derrt.get(i + 1)});
|
||||
}
|
||||
}
|
||||
if (dery.get(nr) != 0 && Double.compare(sign(dery.get(nr)), sign(poly.eval(derrt.get(nr) + dx) - dery.get(nr))) != 0) {
|
||||
guesses.add(derrt.get(nr) + dx);
|
||||
minmax.add(new double[]{derrt.get(nr), rb.maxX});
|
||||
}
|
||||
}
|
||||
|
||||
DoubleFunc f = new DoubleFunc() {
|
||||
@Override
|
||||
public double apply(double x) {
|
||||
return poly.eval(x);
|
||||
}
|
||||
};
|
||||
|
||||
DoubleFunc df = new DoubleFunc() {
|
||||
@Override
|
||||
public double apply(double x) {
|
||||
return poly_d.eval(x);
|
||||
}
|
||||
};
|
||||
|
||||
if (!guesses.isEmpty()) {
|
||||
for (i = 0; i < guesses.size(); i++) {
|
||||
guesses.set(i, Polynomial.newtonSecantBisection(guesses.get(i), f, df, 32, minmax.get(i)[0], minmax.get(i)[1]));
|
||||
}
|
||||
}
|
||||
|
||||
results.addAll(guesses);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@@ -92,4 +92,40 @@ public class CurvedEdgeRecord extends SHAPERECORD {
|
||||
calculateBits();
|
||||
return !SWFOutputStream.fitsInUB(4, numBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 97 * hash + this.controlDeltaX;
|
||||
hash = 97 * hash + this.controlDeltaY;
|
||||
hash = 97 * hash + this.anchorDeltaX;
|
||||
hash = 97 * hash + this.anchorDeltaY;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final CurvedEdgeRecord other = (CurvedEdgeRecord) obj;
|
||||
if (this.controlDeltaX != other.controlDeltaX) {
|
||||
return false;
|
||||
}
|
||||
if (this.controlDeltaY != other.controlDeltaY) {
|
||||
return false;
|
||||
}
|
||||
if (this.anchorDeltaX != other.anchorDeltaX) {
|
||||
return false;
|
||||
}
|
||||
return this.anchorDeltaY == other.anchorDeltaY;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -62,4 +62,28 @@ public class EndShapeRecord extends SHAPERECORD {
|
||||
public boolean isTooLarge() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 37 * hash + this.endOfShape;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final EndShapeRecord other = (EndShapeRecord) obj;
|
||||
return this.endOfShape == other.endOfShape;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -124,4 +124,40 @@ public class StraightEdgeRecord extends SHAPERECORD {
|
||||
calculateBits();
|
||||
return !SWFOutputStream.fitsInUB(4, numBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 29 * hash + (this.generalLineFlag ? 1 : 0);
|
||||
hash = 29 * hash + (this.vertLineFlag ? 1 : 0);
|
||||
hash = 29 * hash + this.deltaX;
|
||||
hash = 29 * hash + this.deltaY;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final StraightEdgeRecord other = (StraightEdgeRecord) obj;
|
||||
if (this.generalLineFlag != other.generalLineFlag) {
|
||||
return false;
|
||||
}
|
||||
if (this.vertLineFlag != other.vertLineFlag) {
|
||||
return false;
|
||||
}
|
||||
if (this.deltaX != other.deltaX) {
|
||||
return false;
|
||||
}
|
||||
return this.deltaY == other.deltaY;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Calculated;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Conditional;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -157,4 +158,73 @@ public final class StyleChangeRecord extends SHAPERECORD implements Cloneable {
|
||||
calculateBits();
|
||||
return !SWFOutputStream.fitsInUB(5, moveBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 59 * hash + (this.stateNewStyles ? 1 : 0);
|
||||
hash = 59 * hash + (this.stateLineStyle ? 1 : 0);
|
||||
hash = 59 * hash + (this.stateFillStyle1 ? 1 : 0);
|
||||
hash = 59 * hash + (this.stateFillStyle0 ? 1 : 0);
|
||||
hash = 59 * hash + (this.stateMoveTo ? 1 : 0);
|
||||
hash = 59 * hash + this.moveDeltaX;
|
||||
hash = 59 * hash + this.moveDeltaY;
|
||||
hash = 59 * hash + this.fillStyle0;
|
||||
hash = 59 * hash + this.fillStyle1;
|
||||
hash = 59 * hash + this.lineStyle;
|
||||
/*hash = 59 * hash + Objects.hashCode(this.fillStyles);
|
||||
hash = 59 * hash + Objects.hashCode(this.lineStyles);*/
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final StyleChangeRecord other = (StyleChangeRecord) obj;
|
||||
if (this.stateNewStyles != other.stateNewStyles) {
|
||||
return false;
|
||||
}
|
||||
if (this.stateLineStyle != other.stateLineStyle) {
|
||||
return false;
|
||||
}
|
||||
if (this.stateFillStyle1 != other.stateFillStyle1) {
|
||||
return false;
|
||||
}
|
||||
if (this.stateFillStyle0 != other.stateFillStyle0) {
|
||||
return false;
|
||||
}
|
||||
if (this.stateMoveTo != other.stateMoveTo) {
|
||||
return false;
|
||||
}
|
||||
if (this.moveDeltaX != other.moveDeltaX) {
|
||||
return false;
|
||||
}
|
||||
if (this.moveDeltaY != other.moveDeltaY) {
|
||||
return false;
|
||||
}
|
||||
if (this.fillStyle0 != other.fillStyle0) {
|
||||
return false;
|
||||
}
|
||||
if (this.fillStyle1 != other.fillStyle1) {
|
||||
return false;
|
||||
}
|
||||
if (this.lineStyle != other.lineStyle) {
|
||||
return false;
|
||||
}
|
||||
/*if (!Objects.equals(this.fillStyles, other.fillStyles)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(this.lineStyles, other.lineStyles);*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ import com.jpexs.decompiler.flash.types.sound.MP3FRAME;
|
||||
import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA;
|
||||
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.CurvedEdgeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeFixer;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.StraightEdgeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.StyleChangeRecordAdvanced;
|
||||
@@ -229,58 +230,53 @@ public class XFLConverter {
|
||||
/**
|
||||
* Adds "(depht xxx)" to layer name
|
||||
*/
|
||||
private final boolean DEBUG_EXPORT_LAYER_DEPTHS = true;
|
||||
private final boolean DEBUG_EXPORT_LAYER_DEPTHS = false;
|
||||
|
||||
private static final DecimalFormat EDGE_DECIMAL_FORMAT = new DecimalFormat("0.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
|
||||
|
||||
static {
|
||||
EDGE_DECIMAL_FORMAT.setGroupingUsed(false);
|
||||
}
|
||||
|
||||
private static String formatEdgeDouble(double value, boolean curved, boolean morphshape) {
|
||||
if (value % 1 == 0) {
|
||||
return "" + (int) value;
|
||||
}
|
||||
if (morphshape) {
|
||||
value = Math.round(value * 2.0) / 2.0;
|
||||
}
|
||||
DecimalFormat df = new DecimalFormat(morphshape ? "0.#" : "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;
|
||||
value = Math.round(value * 2.0) / 2.0;
|
||||
return EDGE_DECIMAL_FORMAT.format(value);
|
||||
}
|
||||
|
||||
private static String convertShapeEdge(MATRIX mat, ShapeRecordAdvanced record, double x, double y, boolean morphshape) {
|
||||
if (record instanceof StyleChangeRecordAdvanced) {
|
||||
StyleChangeRecordAdvanced scr = (StyleChangeRecordAdvanced) record;
|
||||
Point2D p = new Point2D.Double(scr.moveDeltaX, scr.moveDeltaY);
|
||||
//p = new Matrix(mat).transform(p);
|
||||
p = new Matrix(mat).transform(p);
|
||||
if (scr.stateMoveTo) {
|
||||
//return "! " + formatEdgeDouble(p.getX(), false) + " " + formatEdgeDouble(p.getY(), false);
|
||||
return "";
|
||||
return "! " + formatEdgeDouble(p.getX(), false, morphshape) + " " + formatEdgeDouble(p.getY(), false, morphshape);
|
||||
}
|
||||
} else if (record instanceof StraightEdgeRecordAdvanced) {
|
||||
StraightEdgeRecordAdvanced ser = (StraightEdgeRecordAdvanced) record;
|
||||
Point2D p1 = new Point2D.Double(x, y);
|
||||
Point2D p1 = new Point2D.Double(x, y);
|
||||
Point2D p = new Point2D.Double(x + ser.deltaX, y + ser.deltaY);
|
||||
p = new Matrix(mat).transform(p);
|
||||
p1 = new Matrix(mat).transform(p1);
|
||||
|
||||
return "! " + formatEdgeDouble(p1.getX(), false, morphshape) + " " + formatEdgeDouble(p1.getY(), false, morphshape)
|
||||
+ "| " + formatEdgeDouble(p.getX(), false, morphshape) + " " + formatEdgeDouble(p.getY(), false, morphshape);
|
||||
|
||||
return /*"! " + formatEdgeDouble(p1.getX(), false, morphshape) + " " + formatEdgeDouble(p1.getY(), false, morphshape)
|
||||
+*/ "| " + formatEdgeDouble(p.getX(), false, morphshape) + " " + formatEdgeDouble(p.getY(), false, morphshape);
|
||||
} 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 p1 = new Point2D.Double(x, y);
|
||||
Point2D p1 = new Point2D.Double(x, y);
|
||||
Point2D control = new Point2D.Double(controlX, controlY);
|
||||
Point2D anchor = new Point.Double(anchorX, anchorY);
|
||||
p1 = new Matrix(mat).transform(p1);
|
||||
control = new Matrix(mat).transform(control);
|
||||
anchor = new Matrix(mat).transform(anchor);
|
||||
return "! " + formatEdgeDouble(p1.getX(), false, morphshape) + " " + formatEdgeDouble(p1.getY(), false, morphshape)
|
||||
+ "[ " + formatEdgeDouble(control.getX(), true, morphshape) + " " + formatEdgeDouble(control.getY(), true, morphshape) + " " + formatEdgeDouble(anchor.getX(), true, morphshape) + " " + formatEdgeDouble(anchor.getY(), true, morphshape);
|
||||
return /*"! " + formatEdgeDouble(p1.getX(), false, morphshape) + " " + formatEdgeDouble(p1.getY(), false, morphshape)
|
||||
+*/ "[ " + formatEdgeDouble(control.getX(), true, morphshape) + " " + formatEdgeDouble(control.getY(), true, morphshape) + " " + formatEdgeDouble(anchor.getX(), true, morphshape) + " " + formatEdgeDouble(anchor.getY(), true, morphshape);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -299,7 +295,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
if (!hasMove) {
|
||||
//ret.append("! ").append(formatEdgeDouble(startX, false)).append(" ").append(formatEdgeDouble(startY, false));
|
||||
ret.append("! ").append(formatEdgeDouble(startX, false, morphshape)).append(" ").append(formatEdgeDouble(startY, false, morphshape));
|
||||
}
|
||||
double lastMoveToX = startX;
|
||||
double lastMoveToY = startY;
|
||||
@@ -321,7 +317,7 @@ public class XFLConverter {
|
||||
lineStyle = 0;
|
||||
}
|
||||
if (scr.stateLineStyle) {
|
||||
lineStyle = scr.lineStyle;
|
||||
lineStyle = scr.lineStyle;
|
||||
}
|
||||
if (scr.stateFillStyle0) {
|
||||
fillStyle0 = scr.fillStyle0;
|
||||
@@ -330,21 +326,21 @@ public class XFLConverter {
|
||||
fillStyle1 = scr.fillStyle1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
String edge = convertShapeEdge(mat, rec, x, y, morphshape);
|
||||
|
||||
|
||||
String curPos = "! " + formatEdgeDouble(x, false, morphshape) + " " + formatEdgeDouble(y, false, morphshape);
|
||||
//ignore duplicated edges with only strokes #2031
|
||||
if (fillStyle0 == 0 && fillStyle1 == 0 && lineStyle != 0) {
|
||||
if (!edges.contains(edge)) {
|
||||
edges.add(edge);
|
||||
if (!edges.contains(curPos + edge)) {
|
||||
edges.add(curPos + edge);
|
||||
ret.append(edge);
|
||||
}
|
||||
} else {
|
||||
edges.add(edge);
|
||||
edges.add(curPos + edge);
|
||||
ret.append(edge);
|
||||
}
|
||||
|
||||
|
||||
x = rec.changeX(x);
|
||||
y = rec.changeY(y);
|
||||
}
|
||||
@@ -473,7 +469,7 @@ public class XFLConverter {
|
||||
writer.writeAttribute("bitmapIsClipped", true);
|
||||
}
|
||||
|
||||
writer.writeStartElement("matrix");
|
||||
writer.writeStartElement("matrix");
|
||||
MATRIX bitmapMatrix = fs.bitmapMatrix;
|
||||
bitmapMatrix = (new Matrix(mat)).concatenate(new Matrix(bitmapMatrix)).toMATRIX();
|
||||
convertMatrix(bitmapMatrix, writer);
|
||||
@@ -526,10 +522,10 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
writer.writeStartElement("matrix");
|
||||
|
||||
|
||||
MATRIX gradientMatrix = fs.gradientMatrix;
|
||||
gradientMatrix = (new Matrix(mat)).concatenate(new Matrix(gradientMatrix)).toMATRIX();
|
||||
|
||||
gradientMatrix = (new Matrix(mat)).concatenate(new Matrix(gradientMatrix)).toMATRIX();
|
||||
|
||||
convertMatrix(gradientMatrix, writer);
|
||||
writer.writeEndElement();
|
||||
GRADRECORD[] records;
|
||||
@@ -573,13 +569,13 @@ public class XFLConverter {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private static boolean shapeHasMultiLayers(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles) throws XMLStreamException {
|
||||
List<String> layers = getShapeLayers(characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, false);
|
||||
private static boolean shapeHasMultiLayers(SWF swf, HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles) throws XMLStreamException {
|
||||
List<String> layers = getShapeLayers(swf, characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, false);
|
||||
return layers.size() > 1;
|
||||
}
|
||||
|
||||
private static void convertShape(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape, boolean useLayers, XFLXmlWriter writer) throws XMLStreamException {
|
||||
List<String> layers = getShapeLayers(characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, morphshape);
|
||||
private static void convertShape(SWF swf, HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape, boolean useLayers, XFLXmlWriter writer) throws XMLStreamException {
|
||||
List<String> layers = getShapeLayers(swf, characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, morphshape);
|
||||
if (!useLayers) {
|
||||
for (int l = layers.size() - 1; l >= 0; l--) {
|
||||
writer.writeCharactersRaw(layers.get(l));
|
||||
@@ -771,22 +767,27 @@ public class XFLConverter {
|
||||
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 {
|
||||
private static List<String> getShapeLayers(SWF swf, 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();
|
||||
}
|
||||
|
||||
List<ShapeRecordAdvanced> shapeRecordsAdvanced = new ArrayList<>();
|
||||
for (SHAPERECORD rec : shapeRecords) {
|
||||
ShapeRecordAdvanced arec = ShapeRecordAdvanced.createFromSHAPERECORD(rec);
|
||||
if (arec != null) {
|
||||
shapeRecordsAdvanced.add(arec);
|
||||
boolean doFix = true;
|
||||
|
||||
List<ShapeRecordAdvanced> shapeRecordsAdvanced;
|
||||
if (doFix) {
|
||||
ShapeFixer fixer = new ShapeFixer();
|
||||
shapeRecordsAdvanced = fixer.fix(shapeRecords);
|
||||
} else {
|
||||
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;
|
||||
@@ -877,6 +878,7 @@ public class XFLConverter {
|
||||
currentLayer.writeAttribute("edges", edgesSb.toString());
|
||||
currentLayer.writeEndElement();
|
||||
hasEdge = true;
|
||||
//edges.clear();
|
||||
}
|
||||
}
|
||||
if (currentLayer.length() > 0) {
|
||||
@@ -1145,7 +1147,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void walkMorphShapeUsages(ReadOnlyTagList timeLineTags, Map<Integer, CharacterTag> characters, Map<Integer, Integer> usages) {
|
||||
Map<Integer, Integer> depthMap = new HashMap<>();
|
||||
for (Tag t : timeLineTags) {
|
||||
@@ -1171,24 +1173,23 @@ public class XFLConverter {
|
||||
if (ch == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
CharacterTag ct = characters.get(ch);
|
||||
if (ct instanceof MorphShapeTag) {
|
||||
if (!usages.containsKey(ch)) {
|
||||
usages.put(ch, 0);
|
||||
}
|
||||
int usageCount = usages.get(ch);
|
||||
if (po.getRatio() <= 0) {
|
||||
usageCount++;
|
||||
if (po.getRatio() <= 0) {
|
||||
usageCount++;
|
||||
usages.put(ch, usageCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static List<Integer> getMultiUsageMorphShapes(ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters) {
|
||||
List<Integer> ret = new ArrayList<>();
|
||||
Map<Integer, Integer> usages = new TreeMap<>();
|
||||
@@ -1200,8 +1201,8 @@ public class XFLConverter {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static List<Integer> getNonLibraryShapes(ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters) {
|
||||
|
||||
private static List<Integer> getNonLibraryShapes(SWF swf, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters) {
|
||||
HashMap<Integer, Integer> usages = new HashMap<>();
|
||||
walkShapeUsages(tags, characters, usages);
|
||||
List<Integer> ret = new ArrayList<>();
|
||||
@@ -1210,7 +1211,7 @@ public class XFLConverter {
|
||||
if (usages.get(ch) < 2) {
|
||||
if (characters.get(ch) instanceof ShapeTag) {
|
||||
ShapeTag shp = (ShapeTag) characters.get(ch);
|
||||
if (!shapeHasMultiLayers(characters, null, shp.getShapeNum(), shp.getShapes().shapeRecords, shp.getShapes().fillStyles, shp.getShapes().lineStyles)) {
|
||||
if (!shapeHasMultiLayers(swf, characters, null, shp.getShapeNum(), shp.getShapes().shapeRecords, shp.getShapes().fillStyles, shp.getShapes().lineStyles)) {
|
||||
ret.add(ch);
|
||||
}
|
||||
}
|
||||
@@ -1631,7 +1632,7 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
private void convertSymbols(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, Map<Integer, ScriptPack> characterScriptPacks, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
boolean hasSymbol = false;
|
||||
boolean hasSymbol = false;
|
||||
Reference<Integer> nextClipId = new Reference<>(-1);
|
||||
writer.writeStartElement("symbols");
|
||||
for (int ch : characters.keySet()) {
|
||||
@@ -1639,10 +1640,6 @@ public class XFLConverter {
|
||||
if ((symbol instanceof ShapeTag) && nonLibraryShapes.contains(symbol.getCharacterId())) {
|
||||
continue; //shapes with 1 ocurrence and single layer are not added to library
|
||||
}
|
||||
|
||||
/*if (!hasSymbol) {
|
||||
|
||||
}*/
|
||||
|
||||
if ((symbol instanceof ShapeTag) || (symbol instanceof DefineSpriteTag) || (symbol instanceof ButtonTag)) {
|
||||
XFLXmlWriter symbolStr = new XFLXmlWriter();
|
||||
@@ -1794,7 +1791,7 @@ public class XFLConverter {
|
||||
int characterId = character.getCharacterId();
|
||||
if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId))) {
|
||||
ShapeTag shape = (ShapeTag) character;
|
||||
convertShape(characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, recCharWriter);
|
||||
convertShape(swf, characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, recCharWriter);
|
||||
} else if (character instanceof TextTag) {
|
||||
convertText(null, (TextTag) character, matrix, filters, null, recCharWriter);
|
||||
} else if (character instanceof DefineVideoStreamTag) {
|
||||
@@ -1842,10 +1839,10 @@ public class XFLConverter {
|
||||
continue;
|
||||
}
|
||||
final ScriptPack spriteScriptPack = characterScriptPacks.containsKey(sprite.spriteId) ? characterScriptPacks.get(sprite.spriteId) : null;
|
||||
|
||||
|
||||
extractMultilevelClips(sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
|
||||
convertTimeline(swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
|
||||
convertTimeline(swf, swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
|
||||
} else if (symbol instanceof ShapeTag) {
|
||||
symbolStr.writeStartElement("timeline");
|
||||
@@ -1855,7 +1852,7 @@ public class XFLConverter {
|
||||
symbolStr.writeStartElement("layers");
|
||||
SHAPEWITHSTYLE shapeWithStyle = shape.getShapes();
|
||||
if (shapeWithStyle != null) {
|
||||
convertShape(characters, null, shape.getShapeNum(), shapeWithStyle.shapeRecords, shapeWithStyle.fillStyles, shapeWithStyle.lineStyles, false, true, symbolStr);
|
||||
convertShape(swf, characters, null, shape.getShapeNum(), shapeWithStyle.shapeRecords, shapeWithStyle.fillStyles, shapeWithStyle.lineStyles, false, true, symbolStr);
|
||||
}
|
||||
|
||||
symbolStr.writeEndElement(); // layers
|
||||
@@ -1866,7 +1863,7 @@ public class XFLConverter {
|
||||
symbolStr.writeEndElement(); // DOMSymbolItem
|
||||
String symbolStr2 = prettyFormatXML(symbolStr.toString());
|
||||
String symbolFile = "Symbol " + symbol.getCharacterId() + ".xml";
|
||||
files.put(symbolFile, Utf8Helper.getBytes(symbolStr2));
|
||||
files.put(symbolFile, Utf8Helper.getBytes(symbolStr2));
|
||||
|
||||
// write symbLink
|
||||
writer.writeStartElement("Include", new String[]{"href", symbolFile});
|
||||
@@ -1890,8 +1887,6 @@ public class XFLConverter {
|
||||
}*/
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void convertMedia(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException {
|
||||
boolean hasMedia = false;
|
||||
@@ -2356,7 +2351,7 @@ public class XFLConverter {
|
||||
writer.writeEndElement(); // SoundEnvelope
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void convertFrame(boolean shapeTween, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap<String, byte[]> files, XFLXmlWriter writer) throws XMLStreamException {
|
||||
DefineSoundTag sound = null;
|
||||
if (startSound != null) {
|
||||
@@ -2434,7 +2429,7 @@ public class XFLConverter {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private static void convertFrames(List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, int depth, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
private static void convertFrames(SWF swf, List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, int depth, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
boolean lastIn = true;
|
||||
XFLXmlWriter writer2 = new XFLXmlWriter();
|
||||
prevStr += "<frames>";
|
||||
@@ -2463,14 +2458,12 @@ public class XFLConverter {
|
||||
//Add ShowFrameTag to the end when there is one last missing
|
||||
List<Tag> timTags = timelineTags.toArrayList();
|
||||
boolean needsFrameAdd = false;
|
||||
SWF swf = null;
|
||||
for (int i = timTags.size() - 1; i >= 0; i--) {
|
||||
if (timTags.get(i) instanceof ShowFrameTag) {
|
||||
break;
|
||||
}
|
||||
if (timTags.get(i) instanceof PlaceObjectTypeTag) {
|
||||
needsFrameAdd = true;
|
||||
swf = timTags.get(i).getSwf();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2551,7 +2544,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*if (t instanceof ShowFrameTag) {
|
||||
if (t == timTags.get(timTags.size() - 1)) {
|
||||
if (shapeTween && character != null && (character instanceof MorphShapeTag)) {
|
||||
@@ -2563,7 +2556,6 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if (t instanceof RemoveTag) {
|
||||
RemoveTag rt = (RemoveTag) t;
|
||||
if (rt.getDepth() == depth) {
|
||||
@@ -2588,13 +2580,12 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (t instanceof ShowFrameTag) {
|
||||
/*if (prevWasShapeTween) {
|
||||
prevWasShapeTween = false;
|
||||
continue;
|
||||
}*/
|
||||
|
||||
|
||||
boolean shapeTweenNow = false;
|
||||
if (frame + 1 >= startFrame && (onlyFrames == null || onlyFrames.contains(frame + 1))) {
|
||||
|
||||
@@ -2606,7 +2597,7 @@ public class XFLConverter {
|
||||
MorphShapeTag m = shapeTweener;
|
||||
XFLXmlWriter addLastWriter = new XFLXmlWriter();
|
||||
SHAPEWITHSTYLE endShape = m.getShapeAtRatio(65535); //lastTweenRatio);
|
||||
convertShape(characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter);
|
||||
convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter);
|
||||
//duration--;
|
||||
convertFrame(true, null, null, frame - duration, duration, "", lastElements, files, writer2);
|
||||
duration = 1;
|
||||
@@ -2620,10 +2611,10 @@ public class XFLConverter {
|
||||
standaloneShapeTweener = null;
|
||||
} else if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId))) { // || shapeTweener != null)) {
|
||||
ShapeTag shape = (ShapeTag) character;
|
||||
convertShape(characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, elementsWriter);
|
||||
|
||||
convertShape(swf, characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, elementsWriter);
|
||||
|
||||
shapeTween = false;
|
||||
shapeTweener = null;
|
||||
shapeTweener = null;
|
||||
} else if (character != null) {
|
||||
if (character instanceof MorphShapeTag) {
|
||||
MorphShapeTag m = (MorphShapeTag) character;
|
||||
@@ -2634,11 +2625,11 @@ public class XFLConverter {
|
||||
standaloneShapeTweenerMatrix = matrix;
|
||||
convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, metadata, character, characters, tags, flaVersion, elementsWriter);
|
||||
} else {
|
||||
convertShape(characters, matrix, m.getShapeNum() == 1 ? 3 : 4, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false, elementsWriter);
|
||||
shapeTween = true;
|
||||
convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false, elementsWriter);
|
||||
shapeTween = true;
|
||||
}
|
||||
} else {
|
||||
shapeTween = false;
|
||||
shapeTween = false;
|
||||
if (character instanceof TextTag) {
|
||||
convertText(instanceName, (TextTag) character, matrix, filters, clipActions, elementsWriter);
|
||||
} else if (character instanceof DefineVideoStreamTag) {
|
||||
@@ -2650,7 +2641,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shapeTweenNow) {
|
||||
frame++;
|
||||
@@ -2663,7 +2654,7 @@ public class XFLConverter {
|
||||
} else {
|
||||
duration++;
|
||||
}
|
||||
|
||||
|
||||
lastElements = elements;
|
||||
if (frame > endFrame) {
|
||||
if (lastIn) {
|
||||
@@ -3175,32 +3166,30 @@ public class XFLConverter {
|
||||
ret.add(0, varName);
|
||||
return String.join(".", ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void addExtractedClip(
|
||||
ReadOnlyTagList timelineTags,
|
||||
XFLXmlWriter writer,
|
||||
SWF swf,
|
||||
Reference<Integer> nextClipId,
|
||||
List<Integer> nonLibraryShapes,
|
||||
String backgroundColor,
|
||||
HashMap<Integer, CharacterTag> characters,
|
||||
FLAVersion flaVersion,
|
||||
HashMap<String, byte[]> files,
|
||||
Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol,
|
||||
List<Integer> multiUsageMorphShapes
|
||||
ReadOnlyTagList timelineTags,
|
||||
XFLXmlWriter writer,
|
||||
SWF swf,
|
||||
Reference<Integer> nextClipId,
|
||||
List<Integer> nonLibraryShapes,
|
||||
String backgroundColor,
|
||||
HashMap<Integer, CharacterTag> characters,
|
||||
FLAVersion flaVersion,
|
||||
HashMap<String, byte[]> files,
|
||||
Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol,
|
||||
List<Integer> multiUsageMorphShapes
|
||||
) throws XMLStreamException {
|
||||
XFLXmlWriter symbolStr = new XFLXmlWriter();
|
||||
|
||||
|
||||
if (nextClipId.getVal() < 0) {
|
||||
nextClipId.setVal(swf.getNextCharacterId());
|
||||
} else {
|
||||
nextClipId.setVal(nextClipId.getVal() + 1);
|
||||
}
|
||||
|
||||
|
||||
int objectId = nextClipId.getVal();
|
||||
|
||||
|
||||
symbolStr.writeStartElement("DOMSymbolItem", new String[]{
|
||||
"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance",
|
||||
"xmlns", "http://ns.adobe.com/xfl/2008/",
|
||||
@@ -3208,14 +3197,13 @@ public class XFLConverter {
|
||||
"lastModified", Long.toString(getTimestamp(swf))});
|
||||
symbolStr.writeAttribute("symbolType", "graphic");
|
||||
|
||||
convertTimeline(swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
|
||||
convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
|
||||
symbolStr.writeEndElement(); // DOMSymbolItem
|
||||
String symbolStr2 = prettyFormatXML(symbolStr.toString());
|
||||
String symbolFile = generateMaskedSymbolName(objectId) + ".xml";
|
||||
files.put(symbolFile, Utf8Helper.getBytes(symbolStr2));
|
||||
|
||||
|
||||
writer.writeStartElement("Include", new String[]{"href", symbolFile});
|
||||
writer.writeAttribute("itemIcon", "1");
|
||||
writer.writeAttribute("loadImmediate", false);
|
||||
@@ -3224,15 +3212,14 @@ public class XFLConverter {
|
||||
//TODO: itemID="518de416-00000341"
|
||||
}
|
||||
writer.writeEndElement();
|
||||
|
||||
|
||||
extractMultilevelClips(timelineTags, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
}
|
||||
|
||||
|
||||
private String generateMaskedSymbolName(int symbolId) {
|
||||
return (DEBUG_EXPORT_LAYER_DEPTHS ? "MaskedSymbol " : "Symbol ") + symbolId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean getMorphshapeTimeline(int morphShapeId, ReadOnlyTagList tags, List<Tag> outTimelineTags) {
|
||||
int morphDepth = -2;
|
||||
boolean onTrack = false;
|
||||
@@ -3251,8 +3238,7 @@ public class XFLConverter {
|
||||
PlaceObjectTypeTag place = (PlaceObjectTypeTag) t;
|
||||
if (morphDepth == place.getDepth()
|
||||
&& place.getCharacterId() != -1
|
||||
&& place.getCharacterId() != morphShapeId
|
||||
) {
|
||||
&& place.getCharacterId() != morphShapeId) {
|
||||
outTimelineTags.add(t);
|
||||
onTrack = false;
|
||||
} else if ((morphDepth == -2 && place.getCharacterId() == morphShapeId) || morphDepth == place.getDepth()) {
|
||||
@@ -3281,18 +3267,18 @@ public class XFLConverter {
|
||||
outTimelineTags.add(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!outTimelineTags.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void extractMultiUsageMorphShapes(
|
||||
XFLXmlWriter writer,
|
||||
SWF swf,
|
||||
SWF swf,
|
||||
List<Integer> nonLibraryShapes,
|
||||
String backgroundColor,
|
||||
HashMap<Integer, CharacterTag> characters,
|
||||
@@ -3300,9 +3286,9 @@ public class XFLConverter {
|
||||
HashMap<String, byte[]> files,
|
||||
List<Integer> multiUsageMorphShapes
|
||||
) throws XMLStreamException {
|
||||
|
||||
|
||||
for (int objectId : multiUsageMorphShapes) {
|
||||
XFLXmlWriter symbolStr = new XFLXmlWriter();
|
||||
XFLXmlWriter symbolStr = new XFLXmlWriter();
|
||||
symbolStr.writeStartElement("DOMSymbolItem", new String[]{
|
||||
"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance",
|
||||
"xmlns", "http://ns.adobe.com/xfl/2008/",
|
||||
@@ -3313,10 +3299,10 @@ public class XFLConverter {
|
||||
List<Tag> timelineTags = new ArrayList<>();
|
||||
|
||||
getMorphshapeTimeline(objectId, swf.getTags(), timelineTags);
|
||||
|
||||
|
||||
timelineTags = Helper.deepCopy(timelineTags);
|
||||
|
||||
for (Tag t : timelineTags) {
|
||||
|
||||
for (Tag t : timelineTags) {
|
||||
t.setSwf(swf);
|
||||
if (t instanceof PlaceObjectTypeTag) {
|
||||
PlaceObjectTypeTag place = (PlaceObjectTypeTag) t;
|
||||
@@ -3325,9 +3311,8 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convertTimeline(swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, swf.getTags(), new ReadOnlyTagList(timelineTags), characters, "Symbol " + objectId, flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>());
|
||||
|
||||
convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, swf.getTags(), new ReadOnlyTagList(timelineTags), characters, "Symbol " + objectId, flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>());
|
||||
|
||||
symbolStr.writeEndElement(); // DOMSymbolItem
|
||||
String symbolStr2 = prettyFormatXML(symbolStr.toString());
|
||||
@@ -3344,10 +3329,10 @@ public class XFLConverter {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void extractMultilevelClips(ReadOnlyTagList timelineTags,
|
||||
XFLXmlWriter writer,
|
||||
SWF swf,
|
||||
SWF swf,
|
||||
Reference<Integer> nextClipId,
|
||||
List<Integer> nonLibraryShapes,
|
||||
String backgroundColor,
|
||||
@@ -3363,7 +3348,7 @@ public class XFLConverter {
|
||||
Map<Integer, PlaceObjectTypeTag> depthToClipPlace = new HashMap<>();
|
||||
Map<PlaceObjectTypeTag, Integer> clipFinishFrames = new HashMap<>();
|
||||
Map<PlaceObjectTypeTag, Integer> clipStartFrames = new HashMap<>();
|
||||
|
||||
|
||||
int maxDepth = getMaxDepth(timelineTags);
|
||||
Tag lastTag = null;
|
||||
for (Tag t : timelineTags) {
|
||||
@@ -3399,7 +3384,7 @@ public class XFLConverter {
|
||||
if (clipPlaces.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//Some sprites do not end with ShowFrame:
|
||||
if (lastTag != null && !(lastTag instanceof ShowFrameTag)) {
|
||||
f++;
|
||||
@@ -3420,10 +3405,10 @@ public class XFLConverter {
|
||||
depthToFramesList.get(d).add(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Map<Integer, Map<Integer, List<PlaceObjectTypeTag>>> frameToDepthToClips = new TreeMap<>();
|
||||
|
||||
for (f = 0; f < frameCount; f++) {
|
||||
|
||||
for (f = 0; f < frameCount; f++) {
|
||||
for (int d = 0; d < maxDepth; d++) {
|
||||
for (int p = 0; p < clipPlaces.size(); p++) {
|
||||
PlaceObjectTypeTag po = clipPlaces.get(p);
|
||||
@@ -3443,27 +3428,27 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<PlaceObjectTypeTag> delegatedPlaces = new HashSet<>();
|
||||
|
||||
|
||||
Set<PlaceObjectTypeTag> delegatedPlaces = new HashSet<>();
|
||||
|
||||
for (int fr : frameToDepthToClips.keySet()) {
|
||||
for (int d : frameToDepthToClips.get(fr).keySet()) {
|
||||
List<PlaceObjectTypeTag> places = frameToDepthToClips.get(fr).get(d);
|
||||
if (places.size() > 1) {
|
||||
depthToFramesList.get(d).remove((Integer) fr);
|
||||
depthToFramesList.get(d).remove((Integer) fr);
|
||||
PlaceObjectTypeTag secondPlace = places.get(1);
|
||||
if (delegatedPlaces.contains(secondPlace)) {
|
||||
continue;
|
||||
}
|
||||
delegatedPlaces.add(secondPlace);
|
||||
|
||||
List<Tag> delegatedTimeline = new ArrayList<>();
|
||||
f = 0;
|
||||
|
||||
List<Tag> delegatedTimeline = new ArrayList<>();
|
||||
f = 0;
|
||||
boolean removed = false;
|
||||
int numFrames = 0;
|
||||
lastTag = null;
|
||||
Map<Integer, Integer> depthStates = new HashMap<>();
|
||||
|
||||
Map<Integer, Integer> depthStates = new HashMap<>();
|
||||
|
||||
for (Tag t : timelineTags) {
|
||||
if (f < fr) {
|
||||
if (t instanceof PlaceObjectTypeTag) {
|
||||
@@ -3473,7 +3458,7 @@ public class XFLConverter {
|
||||
}
|
||||
if (place.getCharacterId() != -1) {
|
||||
depthStates.put(place.getDepth(), place.getCharacterId());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t instanceof RemoveTag) {
|
||||
depthStates.remove(((RemoveTag) t).getDepth());
|
||||
@@ -3482,7 +3467,7 @@ public class XFLConverter {
|
||||
if (f >= fr) {
|
||||
if (t instanceof PlaceObjectTypeTag) {
|
||||
PlaceObjectTypeTag place = (PlaceObjectTypeTag) t;
|
||||
if (place.getDepth() == secondPlace.getDepth()) {
|
||||
if (place.getDepth() == secondPlace.getDepth()) {
|
||||
if (place.flagMove()) {
|
||||
removed = false;
|
||||
} else if (place.getClipDepth() == secondPlace.getClipDepth()) {
|
||||
@@ -3513,15 +3498,15 @@ public class XFLConverter {
|
||||
break;
|
||||
}
|
||||
f++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(lastTag instanceof ShowFrameTag)) {
|
||||
numFrames++;
|
||||
ShowFrameTag showFrame = new ShowFrameTag(swf);
|
||||
//set timelined?
|
||||
delegatedTimeline.add(showFrame);
|
||||
}
|
||||
|
||||
delegatedTimeline.add(showFrame);
|
||||
}
|
||||
|
||||
/*
|
||||
List<Tag> delegatedTimeline2 = Helper.deepCopy(delegatedTimeline);
|
||||
for (int i = 0; i < delegatedTimeline2.size(); i++) {
|
||||
@@ -3627,8 +3612,8 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertTimeline(AbcIndexing abcIndex, int spriteId, String linkageIdentifier, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, String name, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, ScriptPack scriptPack, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
|
||||
private void convertTimeline(SWF swf, AbcIndexing abcIndex, int spriteId, String linkageIdentifier, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, String name, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, ScriptPack scriptPack, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
|
||||
List<String> classNames = new ArrayList<>();
|
||||
//Searches for Object.registerClass("linkageIdentifier",mypkg.MyClass);
|
||||
@@ -3741,12 +3726,12 @@ public class XFLConverter {
|
||||
clipFrameSplitters.add(f);
|
||||
clipStartFrames.put(po, f);
|
||||
clipPlaces.add(po);
|
||||
|
||||
|
||||
if (depthToClipPlace.containsKey(po.getDepth())) {
|
||||
clipFinishFrames.put(depthToClipPlace.get(po.getDepth()), f - 1);
|
||||
}
|
||||
|
||||
depthToClipPlace.put(po.getDepth(), po);
|
||||
|
||||
depthToClipPlace.put(po.getDepth(), po);
|
||||
for (int j = tpos + 1; j <= timelineTags.size(); j++) {
|
||||
Tag t2 = timelineTags.get(j);
|
||||
if (t2 instanceof PlaceObject2Tag) {
|
||||
@@ -3759,7 +3744,7 @@ public class XFLConverter {
|
||||
if (t2 instanceof ShowFrameTag) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
if (!po.flagMove() && depthToClipPlace.containsKey(po.getDepth())) {
|
||||
@@ -3807,10 +3792,10 @@ public class XFLConverter {
|
||||
depthToFramesList.get(d).add(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Map<Integer, Map<Integer, List<PlaceObjectTypeTag>>> frameToDepthToClips = new TreeMap<>();
|
||||
|
||||
for (f = 0; f < frameCount; f++) {
|
||||
|
||||
for (f = 0; f < frameCount; f++) {
|
||||
for (int d = 0; d < maxDepth; d++) {
|
||||
for (int p = 0; p < clipPlaces.size() - 1; p++) {
|
||||
PlaceObjectTypeTag po = clipPlaces.get(p);
|
||||
@@ -3833,9 +3818,9 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Set<PlaceObjectTypeTag> multiLevelsPlaces = new HashSet<>();
|
||||
|
||||
|
||||
Set<PlaceObjectTypeTag> secondLevelPlaces = new HashSet<>();
|
||||
Map<PlaceObjectTypeTag, PlaceObjectTypeTag> secondToFirstLevelPlace = new HashMap<>();
|
||||
for (int fr : frameToDepthToClips.keySet()) {
|
||||
@@ -3851,12 +3836,11 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Set<PlaceObjectTypeTag> handledClips = new HashSet<>();
|
||||
for (int d = maxDepth; d >= 0; d--) {
|
||||
loopp: for (int p = 0; p < clipPlaces.size() - 1; p++) {
|
||||
loopp:
|
||||
for (int p = 0; p < clipPlaces.size() - 1; p++) {
|
||||
PlaceObjectTypeTag po = clipPlaces.get(p);
|
||||
/* if (po != null && po.getClipDepth() == d && secondLevelPlaces.contains(po)) {
|
||||
int clipFrame = clipFrameSplitters.get(p);
|
||||
@@ -3873,39 +3857,37 @@ public class XFLConverter {
|
||||
}
|
||||
if (po != null && po.getClipDepth() == d) {
|
||||
int clipFrame = clipFrameSplitters.get(p);
|
||||
int nextFrame = clipFinishFrames.get(po);
|
||||
int nextFrame = clipFinishFrames.get(po);
|
||||
handledClips.add(po);
|
||||
|
||||
|
||||
|
||||
int lastFrame = nextFrame;
|
||||
for (int p2 = 0; p2 < clipPlaces.size() - 1; p2++) {
|
||||
PlaceObjectTypeTag po2 = clipPlaces.get(p2);
|
||||
if (po2 == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int clipFrame2 = clipFrameSplitters.get(p2);
|
||||
int nextFrame2 = clipFinishFrames.get(po2);
|
||||
if (lastFrame + 1 == clipFrame2
|
||||
&& po.getDepth() == po2.getDepth()
|
||||
if (lastFrame + 1 == clipFrame2
|
||||
&& po.getDepth() == po2.getDepth()
|
||||
&& po.getClipDepth() == po2.getClipDepth()
|
||||
&& !multiLevelsPlaces.contains(po2)
|
||||
) {
|
||||
&& !multiLevelsPlaces.contains(po2)) {
|
||||
lastFrame = nextFrame2;
|
||||
handledClips.add(po2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
writer.writeStartElement("DOMLayer", new String[]{
|
||||
"name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + po.getDepth() + " clipdepth:" + po.getClipDepth() + ")" : ""),
|
||||
"color", randomOutlineColor(),
|
||||
"layerType", "mask",
|
||||
"locked", "true"});
|
||||
convertFrames(depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, timelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes);
|
||||
convertFrames(swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, timelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes);
|
||||
writer.writeEndElement();
|
||||
|
||||
|
||||
int parentIndex = index;
|
||||
index++;
|
||||
|
||||
|
||||
for (int fx = clipFrame; fx <= lastFrame; fx++) {
|
||||
for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) {
|
||||
if (!depthToFramesList.containsKey(nd) || !depthToFramesList.get(nd).contains(fx)) {
|
||||
@@ -3914,13 +3896,13 @@ public class XFLConverter {
|
||||
if (frameToDepthToClips.containsKey(fx)
|
||||
&& frameToDepthToClips.get(fx).containsKey(nd)) {
|
||||
List<PlaceObjectTypeTag> clips = frameToDepthToClips.get(fx).get(nd);
|
||||
if (clips.size() > 1) {
|
||||
if (clips.size() > 1) {
|
||||
PlaceObjectTypeTag po2 = clips.get(1);
|
||||
if (handledClips.contains(po2)) {
|
||||
continue;
|
||||
}
|
||||
handledClips.add(po2);
|
||||
|
||||
|
||||
MultiLevelClip mlc = placeToMaskedSymbol.get(po2);
|
||||
|
||||
writer.writeStartElement("DOMLayer", new String[]{
|
||||
@@ -3946,11 +3928,9 @@ public class XFLConverter {
|
||||
"keyMode", "" + KEY_MODE_NORMAL
|
||||
});
|
||||
writer.writeEmptyElement("elements");
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
|
||||
|
||||
writer.writeStartElement("DOMFrame", new String[]{
|
||||
"index", "" + clipFrame2,
|
||||
"duration", "" + mlc.numFrames,
|
||||
@@ -3975,24 +3955,23 @@ public class XFLConverter {
|
||||
writer.writeEndElement(); //elements
|
||||
writer.writeEndElement(); //DOMFrame
|
||||
|
||||
|
||||
writer.writeEndElement(); //frames
|
||||
|
||||
writer.writeEndElement();
|
||||
index++;
|
||||
|
||||
index++;
|
||||
|
||||
for (int nd2 = po2.getDepth(); nd2 <= po2.getClipDepth(); nd2++) {
|
||||
for (int i = clipFrame2; i < clipFrame2 + mlc.numFrames; i++) {
|
||||
depthToFramesList.get(nd2).remove((Integer) i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) {
|
||||
boolean nonEmpty = writeLayer(index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes);
|
||||
}
|
||||
|
||||
for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) {
|
||||
boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes);
|
||||
for (int i = clipFrame; i <= lastFrame; i++) {
|
||||
depthToFramesList.get(nd).remove((Integer) i);
|
||||
}
|
||||
@@ -4006,7 +3985,7 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
|
||||
boolean nonEmpty = writeLayer(index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes);
|
||||
boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes);
|
||||
if (nonEmpty) {
|
||||
index++;
|
||||
}
|
||||
@@ -4044,7 +4023,7 @@ public class XFLConverter {
|
||||
writer.writeEndElement(); //DOMLayer
|
||||
}
|
||||
|
||||
private boolean writeLayer(int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, FLAVersion flaVersion, HashMap<String, byte[]> files, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
private boolean writeLayer(SWF swf, int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, FLAVersion flaVersion, HashMap<String, byte[]> files, List<Integer> multiUsageMorphShapes) throws XMLStreamException {
|
||||
XFLXmlWriter layerPrev = new XFLXmlWriter();
|
||||
layerPrev.writeStartElement("DOMLayer", new String[]{
|
||||
"name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + d + ")" : ""),
|
||||
@@ -4056,12 +4035,12 @@ public class XFLConverter {
|
||||
}
|
||||
if (parentLayer != -1) {
|
||||
layerPrev.writeAttribute("parentLayerIndex", parentLayer);
|
||||
layerPrev.writeAttribute("locked", true);
|
||||
layerPrev.writeAttribute("locked", true);
|
||||
}
|
||||
layerPrev.writeCharacters(""); // todo honfika: hack to close start tag
|
||||
String layerAfter = "</DOMLayer>";
|
||||
int prevLength = writer.length();
|
||||
convertFrames(onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer, multiUsageMorphShapes);
|
||||
convertFrames(swf, onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer, multiUsageMorphShapes);
|
||||
return writer.length() != prevLength;
|
||||
}
|
||||
|
||||
@@ -4549,7 +4528,7 @@ public class XFLConverter {
|
||||
final HashMap<String, byte[]> datfiles = new HashMap<>();
|
||||
HashMap<Integer, CharacterTag> characters = getCharacters(swf.getTags());
|
||||
List<Integer> multiUsageMorphShapes = getMultiUsageMorphShapes(swf.getTags(), characters);
|
||||
List<Integer> nonLibraryShapes = getNonLibraryShapes(swf.getTags(), characters);
|
||||
List<Integer> nonLibraryShapes = getNonLibraryShapes(swf, swf.getTags(), characters);
|
||||
Map<Integer, String> characterClasses = getCharacterClasses(swf.getTags());
|
||||
Map<Integer, ScriptPack> characterScriptPacks = getCharacterScriptPacks(swf, characterClasses);
|
||||
Map<Integer, String> characterVariables = getCharacterVariables(swf.getTags());
|
||||
@@ -4594,13 +4573,13 @@ public class XFLConverter {
|
||||
}
|
||||
|
||||
Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol = new HashMap<>();
|
||||
|
||||
convertFonts(characterClasses, swf.getTags(), domDocument);
|
||||
|
||||
convertFonts(characterClasses, swf.getTags(), domDocument);
|
||||
convertLibrary(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
|
||||
//domDocument.writeStartElement("timelines");
|
||||
ScriptPack documentScriptPack = characterScriptPacks.containsKey(0) ? characterScriptPacks.get(0) : null;
|
||||
convertTimeline(swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
convertTimeline(swf, swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes);
|
||||
//domDocument.writeEndElement();
|
||||
|
||||
if (hasAmfMetadata) {
|
||||
@@ -4618,6 +4597,8 @@ public class XFLConverter {
|
||||
domDocument.writeEndElement();
|
||||
} catch (XMLStreamException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
} catch (Throwable ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
String domDocumentStr = prettyFormatXML(domDocument.toString());
|
||||
@@ -5377,8 +5358,9 @@ public class XFLConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class MultiLevelClip {
|
||||
|
||||
public PlaceObjectTypeTag startClipPlaceTag;
|
||||
public int symbol;
|
||||
public int numFrames;
|
||||
@@ -5387,6 +5369,6 @@ public class XFLConverter {
|
||||
this.startClipPlaceTag = startClipPlaceTag;
|
||||
this.symbol = symbol;
|
||||
this.numFrames = numFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,46 +17,111 @@
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.math.BezierEdge;
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
|
||||
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 com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ShapeFixer {
|
||||
|
||||
final boolean DEBUG_PRINT = false;
|
||||
|
||||
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 isOnRight(Point2D a, Point2D b, Point2D c) {
|
||||
return ((b.getX() - a.getX()) * (c.getY() - a.getY()) - (b.getY() - a.getY()) * (c.getX() - a.getX())) > 0;
|
||||
}
|
||||
|
||||
private boolean fixSingleEdge(List<ShapeRecordAdvanced> records) {
|
||||
Map<Point2D, List<Edge>> edgeMap = new LinkedHashMap<>();
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
private class BezierPair {
|
||||
|
||||
BezierEdge be1;
|
||||
BezierEdge be2;
|
||||
|
||||
public BezierPair(BezierEdge be1, BezierEdge be2) {
|
||||
this.be1 = be1;
|
||||
this.be2 = be2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.be1) + Objects.hashCode(this.be2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final BezierPair other = (BezierPair) obj;
|
||||
if ((Objects.equals(this.be1, other.be1) && Objects.equals(this.be2, other.be2))
|
||||
|| (Objects.equals(this.be1, other.be2) && Objects.equals(this.be2, other.be1))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<ShapeRecordAdvanced> fix(
|
||||
List<SHAPERECORD> records
|
||||
) {
|
||||
|
||||
List<List<BezierEdge>> shapes = new ArrayList<>();
|
||||
List<BezierEdge> currentShape = new ArrayList<>();
|
||||
List<Integer> fillStyles0 = new ArrayList<>();
|
||||
List<Integer> fillStyles1 = new ArrayList<>();
|
||||
List<Integer> lineStyles = new ArrayList<>();
|
||||
List<Integer> layers = new ArrayList<>();
|
||||
List<FILLSTYLEARRAY> fillStyleLayers = new ArrayList<>();
|
||||
List<LINESTYLEARRAY> lineStyleLayers = new ArrayList<>();
|
||||
|
||||
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;
|
||||
int lineStyle = 0;
|
||||
int layer = -1;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
for (SHAPERECORD rec : records) {
|
||||
if (rec instanceof StyleChangeRecord) {
|
||||
StyleChangeRecord scr = (StyleChangeRecord) rec;
|
||||
if (scr.stateMoveTo
|
||||
|| scr.stateNewStyles
|
||||
|| scr.stateFillStyle0
|
||||
|| scr.stateFillStyle1
|
||||
|| scr.stateLineStyle) {
|
||||
if (!currentShape.isEmpty()) {
|
||||
shapes.add(currentShape);
|
||||
fillStyles0.add(fillStyle0);
|
||||
fillStyles1.add(fillStyle1);
|
||||
lineStyles.add(lineStyle);
|
||||
layers.add(layer);
|
||||
currentShape = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
if (scr.stateNewStyles) {
|
||||
layer++;
|
||||
fillStyle0 = 0;
|
||||
fillStyle1 = 0;
|
||||
lineStyle = 0;
|
||||
fillStyleLayers.add(scr.fillStyles);
|
||||
lineStyleLayers.add(scr.lineStyles);
|
||||
}
|
||||
if (scr.stateFillStyle0) {
|
||||
fillStyle0 = scr.fillStyle0;
|
||||
@@ -64,297 +129,298 @@ public class ShapeFixer {
|
||||
if (scr.stateFillStyle1) {
|
||||
fillStyle1 = scr.fillStyle1;
|
||||
}
|
||||
if (scr.stateLineStyle) {
|
||||
lineStyle = scr.lineStyle;
|
||||
}
|
||||
}
|
||||
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 StraightEdgeRecord) {
|
||||
int x2 = rec.changeX(x);
|
||||
int y2 = rec.changeY(y);
|
||||
BezierEdge be = new BezierEdge(x, y, x2, y2);
|
||||
currentShape.add(be);
|
||||
}
|
||||
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
|
||||
));
|
||||
if (rec instanceof CurvedEdgeRecord) {
|
||||
CurvedEdgeRecord cer = (CurvedEdgeRecord) rec;
|
||||
int cx = x + cer.controlDeltaX;
|
||||
int cy = y + cer.controlDeltaY;
|
||||
int ax = cx + cer.anchorDeltaX;
|
||||
int ay = cy + cer.anchorDeltaY;
|
||||
BezierEdge be = new BezierEdge(x, y, cx, cy, ax, ay);
|
||||
currentShape.add(be);
|
||||
}
|
||||
if (rec instanceof EndShapeRecord) {
|
||||
if (!currentShape.isEmpty()) {
|
||||
shapes.add(currentShape);
|
||||
fillStyles0.add(fillStyle0);
|
||||
fillStyles1.add(fillStyle1);
|
||||
lineStyles.add(lineStyle);
|
||||
layers.add(layer);
|
||||
currentShape = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
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<>();
|
||||
List<BezierPair> splittedPairs = new ArrayList<>();
|
||||
|
||||
if (edge1.intersection(edge2, newEdges1, newEdges2)) {
|
||||
loopi1:
|
||||
for (int i1 = 0; i1 < shapes.size(); i1++) {
|
||||
layer = layers.get(i1);
|
||||
loopj1:
|
||||
for (int j1 = 0; j1 < shapes.get(i1).size(); j1++) {
|
||||
BezierEdge be1 = shapes.get(i1).get(j1);
|
||||
for (int i2 = 0; i2 < shapes.size(); i2++) {
|
||||
if (layers.get(i2) == layer) {
|
||||
loopj2:
|
||||
for (int j2 = 0; j2 < shapes.get(i2).size(); j2++) {
|
||||
BezierEdge be2 = shapes.get(i2).get(j2);
|
||||
|
||||
/*System.out.println("---------------------------------------");
|
||||
System.out.println("intersection edge "+edge1 + " and " +edge2);
|
||||
System.out.println("newEdges1:");
|
||||
for(Edge e:newEdges1) {
|
||||
System.out.println("..."+e);
|
||||
if (i1 == i2 && j1 == j2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (be1.isEmpty()) {
|
||||
shapes.get(i1).remove(j1);
|
||||
j1--;
|
||||
continue loopj1;
|
||||
}
|
||||
if (be2.isEmpty()) {
|
||||
shapes.get(i2).remove(j2);
|
||||
j2--;
|
||||
continue loopj2;
|
||||
}
|
||||
|
||||
//duplicated edge
|
||||
if (be1.equals(be2) || be1.equals(be2.reverse())) {
|
||||
shapes.get(i2).remove(j2);
|
||||
j2--;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("removing duplicate " + be1.toSvg() + " and " + be2.toSvg());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
BezierPair pair = new BezierPair(be1, be2);
|
||||
if (splittedPairs.contains(pair)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Double> t1Ref = new ArrayList<>();
|
||||
List<Double> t2Ref = new ArrayList<>();
|
||||
List<Point2D> intPoints = new ArrayList<>();
|
||||
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.print("checking shape[" + i1 + "][" + j1 + "] to shape[" + i2 + "][" + j2 + "] : " + be1.toSvg() + " and " + be2.toSvg());
|
||||
}
|
||||
|
||||
boolean isint = be1.intersects(be2, t1Ref, t2Ref, intPoints);
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println(" " + isint);
|
||||
if (isint) {
|
||||
System.err.println("xxx");
|
||||
}
|
||||
}
|
||||
//List<Point2D> inters = be.getIntersections(be2);
|
||||
|
||||
if (!t1Ref.isEmpty()) {
|
||||
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("be1 " + be1);
|
||||
System.err.println("be2 " + be2);
|
||||
System.err.println("intersects " + be1.toSvg() + " " + be2.toSvg());
|
||||
System.err.println(" fillstyle0: " + fillStyles0.get(i1) + " , " + fillStyles0.get(i2));
|
||||
System.err.println(" fillstyle1: " + fillStyles1.get(i1) + " , " + fillStyles1.get(i2));
|
||||
System.err.println(" linestyle: " + lineStyles.get(i1) + " , " + lineStyles.get(i2));
|
||||
|
||||
for (int n = 0; n < t1Ref.size(); n++) {
|
||||
System.err.println("- " + t1Ref.get(n) + " , " + t2Ref.get(n) + " : " + intPoints.get(n));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ((be1.getBeginPoint().equals(be2.getBeginPoint())
|
||||
|| be1.getBeginPoint().equals(be2.getEndPoint())
|
||||
|| be1.getEndPoint().equals(be2.getBeginPoint())
|
||||
|| be1.getEndPoint().equals(be2.getEndPoint())) && (t1Ref.size() == 1)) {
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("Same begin/end - continued");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((t1Ref.size() == 2) && !((t1Ref.get(0) == 0 || t1Ref.get(0) == 1)
|
||||
&& (t2Ref.get(0) == 0 || t2Ref.get(0) == 1))) {
|
||||
t1Ref.add(0, t1Ref.remove(1));
|
||||
t2Ref.add(0, t2Ref.remove(1));
|
||||
intPoints.add(0, intPoints.remove(1));
|
||||
}
|
||||
|
||||
int splitPointIndex = 0;
|
||||
if (intPoints.size() > 1) {
|
||||
splitPointIndex = 1;
|
||||
}
|
||||
|
||||
splittedPairs.add(pair);
|
||||
|
||||
Reference<BezierEdge> be1LRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be1RRef = new Reference<>(null);
|
||||
be1.split(t1Ref.get(splitPointIndex), be1LRef, be1RRef);
|
||||
Reference<BezierEdge> be2LRef = new Reference<>(null);
|
||||
Reference<BezierEdge> be2RRef = new Reference<>(null);
|
||||
be2.split(t2Ref.get(splitPointIndex), be2LRef, be2RRef);
|
||||
|
||||
BezierEdge be1L = be1LRef.getVal();
|
||||
BezierEdge be1R = be1RRef.getVal();
|
||||
BezierEdge be2L = be2LRef.getVal();
|
||||
BezierEdge be2R = be2RRef.getVal();
|
||||
|
||||
Point2D intP = intPoints.get(splitPointIndex);
|
||||
|
||||
be1L.setEndPoint(intP);
|
||||
be1R.setBeginPoint(intP);
|
||||
be2L.setEndPoint(intP);
|
||||
be2R.setBeginPoint(intP);
|
||||
|
||||
be1L.roundHalf();
|
||||
be1R.roundHalf();
|
||||
be2L.roundHalf();
|
||||
be2R.roundHalf();
|
||||
|
||||
if (i1 == i2 && j1 < j2) {
|
||||
shapes.get(i1).remove(j2);
|
||||
shapes.get(i2).remove(j1);
|
||||
j2--;
|
||||
} else {
|
||||
shapes.get(i1).remove(j1);
|
||||
shapes.get(i2).remove(j2);
|
||||
if (i1 == i2) {
|
||||
j1--;
|
||||
}
|
||||
}
|
||||
|
||||
int n1 = j1;
|
||||
int n2 = j2;
|
||||
|
||||
if (!be1L.isEmpty()) {
|
||||
shapes.get(i1).add(n1, be1L);
|
||||
if (i1 == i2 && n2 > n1) {
|
||||
n2++;
|
||||
}
|
||||
n1++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be1L.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
if (!be1R.isEmpty()) {
|
||||
shapes.get(i1).add(n1, be1R);
|
||||
if (i1 == i2 && n2 > n1) {
|
||||
n2++;
|
||||
}
|
||||
n1++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be1R.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
if (!be2L.isEmpty()) {
|
||||
shapes.get(i2).add(n2, be2L);
|
||||
if (i1 == i2 && n1 > n2) {
|
||||
n1++;
|
||||
}
|
||||
n2++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be2L.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
if (!be2R.isEmpty()) {
|
||||
shapes.get(i2).add(n2, be2R);
|
||||
if (i1 == i2 && n1 > n2) {
|
||||
n1++;
|
||||
}
|
||||
n2++;
|
||||
if (DEBUG_PRINT) {
|
||||
System.err.println("added " + be2R.toSvg());
|
||||
}
|
||||
}
|
||||
|
||||
j1--;
|
||||
continue loopj1;
|
||||
}
|
||||
}
|
||||
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();
|
||||
List<ShapeRecordAdvanced> ret = new ArrayList<>();
|
||||
|
||||
layer = -1;
|
||||
double dx = 0;
|
||||
double dy = 0;
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> bes = shapes.get(i);
|
||||
if (bes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
StyleChangeRecordAdvanced scr = new StyleChangeRecordAdvanced();
|
||||
scr.stateMoveTo = true;
|
||||
dx = scr.moveDeltaX = bes.get(0).points.get(0).getX();
|
||||
dy = scr.moveDeltaY = bes.get(0).points.get(0).getY();
|
||||
|
||||
int newLayer = layers.get(i);
|
||||
if (newLayer != layer) {
|
||||
scr.stateNewStyles = true;
|
||||
scr.fillStyles = fillStyleLayers.get(newLayer);
|
||||
scr.lineStyles = lineStyleLayers.get(newLayer);
|
||||
}
|
||||
layer = newLayer;
|
||||
|
||||
scr.stateFillStyle0 = true;
|
||||
scr.fillStyle0 = fillStyles0.get(i);
|
||||
scr.stateFillStyle1 = true;
|
||||
scr.fillStyle1 = fillStyles1.get(i);
|
||||
scr.stateLineStyle = true;
|
||||
scr.lineStyle = lineStyles.get(i);
|
||||
ret.add(scr);
|
||||
for (BezierEdge be : bes) {
|
||||
if (!be.getBeginPoint().equals(new Point2D.Double(dx, dy))) {
|
||||
StyleChangeRecordAdvanced sm = new StyleChangeRecordAdvanced();
|
||||
sm.stateMoveTo = true;
|
||||
sm.moveDeltaX = be.getBeginPoint().getX();
|
||||
sm.moveDeltaY = be.getBeginPoint().getY();
|
||||
ret.add(sm);
|
||||
}
|
||||
dx = be.getEndPoint().getX();
|
||||
dy = be.getEndPoint().getY();
|
||||
|
||||
ShapeRecordAdvanced sra = bezierToAdvancedRecord(be);
|
||||
ret.add(sra);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
private ShapeRecordAdvanced bezierToAdvancedRecord(BezierEdge be) {
|
||||
if (be.points.size() == 2) {
|
||||
StraightEdgeRecordAdvanced ser = new StraightEdgeRecordAdvanced();
|
||||
ser.deltaX = points.get(1).getX() - points.get(0).getX();
|
||||
ser.deltaY = points.get(1).getY() - points.get(0).getY();
|
||||
ser.deltaX = be.points.get(1).getX() - be.points.get(0).getX();
|
||||
ser.deltaY = be.points.get(1).getY() - be.points.get(0).getY();
|
||||
return ser;
|
||||
}
|
||||
if (be.points.size() == 3) {
|
||||
CurvedEdgeRecordAdvanced cer = new CurvedEdgeRecordAdvanced();
|
||||
cer.controlDeltaX = be.points.get(1).getX() - be.points.get(0).getX();
|
||||
cer.controlDeltaY = be.points.get(1).getY() - be.points.get(0).getY();
|
||||
cer.anchorDeltaX = be.points.get(2).getX() - be.points.get(1).getX();
|
||||
cer.anchorDeltaY = be.points.get(2).getY() - be.points.get(1).getY();
|
||||
return cer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user