mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-28 13:26:16 +00:00
FLA export - fixers refactoring
This commit is contained in:
@@ -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.MorphShapeFixer;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeFixer;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.ShapeRecordAdvanced;
|
||||
import com.jpexs.decompiler.flash.xfl.shapefixer.StraightEdgeRecordAdvanced;
|
||||
@@ -612,8 +613,8 @@ public class XFLConverter {
|
||||
return false;
|
||||
}
|
||||
|
||||
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, boolean useFixer) throws XMLStreamException {
|
||||
List<String> layers = getShapeLayers(swf, characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, morphshape, useFixer);
|
||||
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));
|
||||
@@ -805,25 +806,16 @@ public class XFLConverter {
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static List<String> getShapeLayers(SWF swf, HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape, boolean useFixer) 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;
|
||||
useFixer = true;
|
||||
if (useFixer) {
|
||||
ShapeFixer fixer = new ShapeFixer();
|
||||
shapeRecordsAdvanced = fixer.fix(shapeRecords, morphshape, shapeNum, fillStyles, lineStyles);
|
||||
} else {
|
||||
shapeRecordsAdvanced = new ArrayList<>();
|
||||
for (SHAPERECORD rec : shapeRecords) {
|
||||
ShapeRecordAdvanced arec = ShapeRecordAdvanced.createFromSHAPERECORD(rec);
|
||||
if (arec != null) {
|
||||
shapeRecordsAdvanced.add(arec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShapeFixer fixer = morphshape ? new MorphShapeFixer() : new ShapeFixer();
|
||||
shapeRecordsAdvanced = fixer.fix(shapeRecords, shapeNum, fillStyles, lineStyles);
|
||||
|
||||
|
||||
List<ShapeRecordAdvanced> edges = new ArrayList<>();
|
||||
int lineStyleCount = 0;
|
||||
@@ -1850,7 +1842,7 @@ public class XFLConverter {
|
||||
int characterId = character.getCharacterId();
|
||||
if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId))) {
|
||||
ShapeTag shape = (ShapeTag) character;
|
||||
convertShape(swf, characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, recCharWriter, true);
|
||||
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) {
|
||||
@@ -1912,7 +1904,7 @@ public class XFLConverter {
|
||||
symbolStr.writeStartElement("layers");
|
||||
SHAPEWITHSTYLE shapeWithStyle = shape.getShapes();
|
||||
if (shapeWithStyle != null) {
|
||||
convertShape(swf, characters, null, shape.getShapeNum(), shapeWithStyle.shapeRecords, shapeWithStyle.fillStyles, shapeWithStyle.lineStyles, false, true, symbolStr, true);
|
||||
convertShape(swf, characters, null, shape.getShapeNum(), shapeWithStyle.shapeRecords, shapeWithStyle.fillStyles, shapeWithStyle.lineStyles, false, true, symbolStr);
|
||||
}
|
||||
|
||||
symbolStr.writeEndElement(); // layers
|
||||
@@ -2672,7 +2664,7 @@ public class XFLConverter {
|
||||
MorphShapeTag m = shapeTweener;
|
||||
XFLXmlWriter addLastWriter = new XFLXmlWriter();
|
||||
SHAPEWITHSTYLE endShape = m.getShapeAtRatio(65535); //lastTweenRatio);
|
||||
convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter, false);
|
||||
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;
|
||||
@@ -2688,7 +2680,7 @@ public class XFLConverter {
|
||||
standaloneShapeTweener = null;
|
||||
} else if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId))) { // || shapeTweener != null)) {
|
||||
ShapeTag shape = (ShapeTag) character;
|
||||
convertShape(swf, characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, elementsWriter, true);
|
||||
convertShape(swf, characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, elementsWriter);
|
||||
|
||||
shapeTween = false;
|
||||
shapeTweener = null;
|
||||
@@ -2706,7 +2698,7 @@ public class XFLConverter {
|
||||
elementsWriter.writeCharactersRaw(lastElements);
|
||||
} else {
|
||||
statusStack.pushStatus(m.toString());
|
||||
convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false, elementsWriter, false);
|
||||
convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false, elementsWriter);
|
||||
statusStack.popStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* 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.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.math.BezierEdge;
|
||||
import com.jpexs.decompiler.flash.math.Distances;
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLE;
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLE;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLE2;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* Morphshape fixer. Works as ShapeFixer but also removes duplicated paths,
|
||||
* calculates holes.
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class MorphShapeFixer extends ShapeFixer {
|
||||
|
||||
/**
|
||||
* Hook from base ShapeFixer
|
||||
*
|
||||
* @param shapeNum
|
||||
* @param shapes
|
||||
* @param fillStyles0
|
||||
* @param fillStyles1
|
||||
* @param lineStyles
|
||||
* @param layers
|
||||
* @param baseFillStyles
|
||||
* @param baseLineStyles
|
||||
* @param fillStyleLayers
|
||||
* @param lineStyleLayers
|
||||
*/
|
||||
@Override
|
||||
protected void beforeHandle(int shapeNum, List<List<BezierEdge>> shapes, List<Integer> fillStyles0, List<Integer> fillStyles1, List<Integer> lineStyles, List<Integer> layers, FILLSTYLEARRAY baseFillStyles, LINESTYLEARRAY baseLineStyles, List<FILLSTYLEARRAY> fillStyleLayers, List<LINESTYLEARRAY> lineStyleLayers) {
|
||||
removeEmptyEdges(shapes);
|
||||
mergeSimilar(shapeNum, shapes, fillStyles0, lineStyles, layers, baseFillStyles, baseLineStyles, fillStyleLayers, lineStyleLayers);
|
||||
mergeWithSamePrefix(shapes, fillStyles0, fillStyles1, lineStyles);
|
||||
clearDuplicatePathsNextToEachOther(shapes, fillStyles0, lineStyles);
|
||||
fixHolesAndAntiClockwise(shapes, fillStyles0, fillStyles1, lineStyles, layers);
|
||||
}
|
||||
|
||||
private boolean isEmptyPath(List<BezierEdge> path) {
|
||||
for (BezierEdge be : path) {
|
||||
if (!be.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void fixHolesAndAntiClockwise(
|
||||
List<List<BezierEdge>> shapes,
|
||||
List<Integer> fillStyles0,
|
||||
List<Integer> fillStyles1,
|
||||
List<Integer> lineStyles,
|
||||
List<Integer> layers
|
||||
) {
|
||||
List<List<BezierEdge>> closedShapes = new ArrayList<>();
|
||||
List<Integer> closedFillStyle = new ArrayList<>();
|
||||
List<Integer> closedShapesI = new ArrayList<>();
|
||||
List<Integer> closedShapesJ = new ArrayList<>();
|
||||
|
||||
Set<Integer> closedHolesI = new LinkedHashSet<>();
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Point2D lastMoveTo = null;
|
||||
BezierEdge lastEdge = null;
|
||||
int moveToIndex = 0;
|
||||
List<BezierEdge> batch = new ArrayList<>();
|
||||
for (int j = 0; j < shapes.get(i).size(); j++) {
|
||||
BezierEdge be = shapes.get(i).get(j);
|
||||
batch.add(be);
|
||||
if (lastMoveTo != null && be.getEndPoint().equals(lastMoveTo)) {
|
||||
closedFillStyle.add(fillStyles0.get(i));
|
||||
closedShapes.add(batch);
|
||||
closedShapesI.add(i);
|
||||
closedShapesJ.add(moveToIndex);
|
||||
moveToIndex = j + 1;
|
||||
lastMoveTo = be.getEndPoint();
|
||||
batch = new ArrayList<>();
|
||||
}
|
||||
if (lastEdge != null) {
|
||||
if (!lastEdge.getEndPoint().equals(be.getBeginPoint())) {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
moveToIndex = j;
|
||||
}
|
||||
} else {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
}
|
||||
lastEdge = be;
|
||||
}
|
||||
}
|
||||
|
||||
//reversing anti-clockwise
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
List<BezierEdge> list = closedShapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<Point2D> points = new ArrayList<>();
|
||||
points.add(list.get(0).getBeginPoint());
|
||||
for (BezierEdge be : list) {
|
||||
if (be.points.size() == 3) {
|
||||
points.add(be.points.get(1));
|
||||
}
|
||||
points.add(be.getEndPoint());
|
||||
}
|
||||
double sum = 0;
|
||||
for (int j = 0; j < points.size(); j++) {
|
||||
Point2D p1 = points.get(j);
|
||||
Point2D p2 = points.get((j + 1) % points.size());
|
||||
sum += (p1.getX() * p2.getY() - p2.getX() * p1.getY());
|
||||
}
|
||||
|
||||
if (sum < 0) { //anti clockwise
|
||||
//reverse the list
|
||||
List<BezierEdge> rev = new ArrayList<>();
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
rev.add(list.get(list.size() - 1 - j).reverse());
|
||||
}
|
||||
|
||||
int shapeI = closedShapesI.get(i);
|
||||
int shapeJ = closedShapesJ.get(i);
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
shapes.get(shapeI).set(shapeJ + j, rev.get(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<Integer, List<Integer>> fillStyleToClosed = new LinkedHashMap<>();
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
int fs = closedFillStyle.get(i);
|
||||
if (fs != 0) {
|
||||
if (!fillStyleToClosed.containsKey(fs)) {
|
||||
fillStyleToClosed.put(fs, new ArrayList<>());
|
||||
}
|
||||
fillStyleToClosed.get(fs).add(i);
|
||||
}
|
||||
}
|
||||
for (int fs : fillStyleToClosed.keySet()) {
|
||||
GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
Map<Integer, GeneralPath> closedPaths = new LinkedHashMap<>();
|
||||
for (int i : fillStyleToClosed.get(fs)) {
|
||||
List<BezierEdge> closed = closedShapes.get(i);
|
||||
if (closed.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
GeneralPath closedPath = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
|
||||
closedPath.moveTo(closed.get(0).getBeginPoint().getX(), closed.get(0).getBeginPoint().getY());
|
||||
path.moveTo(closed.get(0).getBeginPoint().getX(), closed.get(0).getBeginPoint().getY());
|
||||
boolean isEmpty = true;
|
||||
for (BezierEdge be : closed) {
|
||||
if (be.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
isEmpty = false;
|
||||
if (be.points.size() == 3) {
|
||||
closedPath.quadTo(be.points.get(1).getX(), be.points.get(1).getY(), be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
path.quadTo(be.points.get(1).getX(), be.points.get(1).getY(), be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
} else {
|
||||
closedPath.lineTo(be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
path.lineTo(be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
}
|
||||
}
|
||||
closedPath.closePath();
|
||||
path.closePath();
|
||||
if (!isEmpty) {
|
||||
closedPaths.put(i, closedPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i : closedPaths.keySet()) {
|
||||
GeneralPath region = closedPaths.get(i);
|
||||
Rectangle r = region.getBounds();
|
||||
double px;
|
||||
double py;
|
||||
do {
|
||||
px = r.getX() + r.getWidth() * Math.random();
|
||||
py = r.getY() + r.getHeight() * Math.random();
|
||||
} while (!region.contains(px, py));
|
||||
|
||||
if (!path.contains(px, py)) {
|
||||
closedHolesI.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = closedShapes.size() - 1; i >= 0; i--) {
|
||||
if (closedHolesI.contains(i)) {
|
||||
int to = closedShapesJ.get(i) + closedShapes.get(i).size() - 1;
|
||||
int from = closedShapesJ.get(i);
|
||||
List<BezierEdge> list = shapes.get(closedShapesI.get(i));
|
||||
|
||||
//System.err.println("removing hole["+closedShapesI.get(i)+"]["+from+" to "+to+"]");
|
||||
for (int j = to; j >= from; j--) {
|
||||
list.remove(j);
|
||||
}
|
||||
|
||||
int shapeI = closedShapesI.get(i);
|
||||
|
||||
//add this path as new with fillstyle1 instead of fillstyle0
|
||||
shapes.add(shapeI + 1, closedShapes.get(i));
|
||||
closedShapes.set(i, new ArrayList<>());
|
||||
fillStyles0.add(shapeI + 1, 0);
|
||||
fillStyles1.add(shapeI + 1, fillStyles0.get(shapeI));
|
||||
lineStyles.add(shapeI + 1, lineStyles.get(shapeI));
|
||||
layers.add(shapeI + 1, layers.get(shapeI));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearDuplicatePathsNextToEachOther(
|
||||
List<List<BezierEdge>> shapes,
|
||||
List<Integer> fillStyles0,
|
||||
List<Integer> lineStyles
|
||||
) {
|
||||
List<BezierEdge> prevList = null;
|
||||
int prevI = -1;
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (prevList != null) {
|
||||
if (fillStyles0.get(i) == fillStyles0.get(prevI)
|
||||
&& lineStyles.get(i) == lineStyles.get(prevI)) {
|
||||
if (list.equals(prevList)) {
|
||||
prevList.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
prevI = i;
|
||||
prevList = list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will remove a stroked path with no fill which has same stroke as
|
||||
* subsequent path (or is its prefix). This happens in the morphshape edges.
|
||||
* This needs to be cleaned up before exporting to FLA.
|
||||
*/
|
||||
private void mergeWithSamePrefix(
|
||||
List<List<BezierEdge>> shapes,
|
||||
List<Integer> fillStyles0,
|
||||
List<Integer> fillStyles1,
|
||||
List<Integer> lineStyles
|
||||
) {
|
||||
List<BezierEdge> prevList = null;
|
||||
int prevI = -1;
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (prevList != null) {
|
||||
|
||||
int prevFillStyle0 = fillStyles0.get(i - 1);
|
||||
int prevFillStyle1 = fillStyles1.get(i - 1);
|
||||
int prevLineStyle = lineStyles.get(i - 1);
|
||||
int lineStyle = lineStyles.get(i);
|
||||
int fillStyle0 = fillStyles0.get(i);
|
||||
int fillStyle1 = fillStyles1.get(i);
|
||||
|
||||
if (fillStyle0 == 0 && fillStyle1 == 0 && lineStyle != 0 && lineStyle == prevLineStyle) {
|
||||
if (prevList.size() >= list.size()) {
|
||||
boolean isPrefix = true;
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
if (!prevList.get(j).equals(list.get(j))) {
|
||||
isPrefix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPrefix) {
|
||||
shapes.get(i).clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (prevFillStyle0 == 0 && prevFillStyle1 == 0 && prevLineStyle != 0 && lineStyle == prevLineStyle) {
|
||||
//list startswitch prevList
|
||||
if (list.size() >= prevList.size()) {
|
||||
boolean isPrefix = true;
|
||||
for (int j = 0; j < prevList.size(); j++) {
|
||||
if (!prevList.get(j).equals(list.get(j))) {
|
||||
isPrefix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPrefix) {
|
||||
shapes.get(prevI).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prevI = i;
|
||||
prevList = list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges similar paths. This happens in morphshapes when one shape is
|
||||
* transformed into multiple shapes.
|
||||
*
|
||||
* @param shapeNum
|
||||
* @param shapes
|
||||
* @param fillStyles0
|
||||
* @param lineStyles
|
||||
* @param layers
|
||||
* @param baseFillStyles
|
||||
* @param baseLineStyles
|
||||
* @param fillStyleLayers
|
||||
* @param lineStyleLayers
|
||||
*/
|
||||
private void mergeSimilar(
|
||||
int shapeNum,
|
||||
List<List<BezierEdge>> shapes,
|
||||
List<Integer> fillStyles0,
|
||||
List<Integer> lineStyles,
|
||||
List<Integer> layers,
|
||||
FILLSTYLEARRAY baseFillStyles,
|
||||
LINESTYLEARRAY baseLineStyles,
|
||||
List<FILLSTYLEARRAY> fillStyleLayers,
|
||||
List<LINESTYLEARRAY> lineStyleLayers
|
||||
) {
|
||||
List<List<BezierEdge>> closedShapes = new ArrayList<>();
|
||||
List<Integer> closedFillStyle = new ArrayList<>();
|
||||
List<Integer> closedLineStyle = new ArrayList<>();
|
||||
List<Integer> closedLayers = new ArrayList<>();
|
||||
List<Integer> closedShapesI = new ArrayList<>();
|
||||
List<Integer> closedShapesJ = new ArrayList<>();
|
||||
|
||||
Map<Integer, Integer> replacements = new LinkedHashMap<>();
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Point2D lastMoveTo = null;
|
||||
BezierEdge lastEdge = null;
|
||||
int moveToIndex = 0;
|
||||
List<BezierEdge> batch = new ArrayList<>();
|
||||
for (int j = 0; j < shapes.get(i).size(); j++) {
|
||||
BezierEdge be = shapes.get(i).get(j);
|
||||
batch.add(be);
|
||||
if (lastMoveTo != null && be.getEndPoint().equals(lastMoveTo)) {
|
||||
closedFillStyle.add(fillStyles0.get(i));
|
||||
closedLineStyle.add(lineStyles.get(i));
|
||||
closedShapes.add(batch);
|
||||
closedLayers.add(layers.get(i));
|
||||
closedShapesI.add(i);
|
||||
closedShapesJ.add(moveToIndex);
|
||||
moveToIndex = j + 1;
|
||||
lastMoveTo = be.getEndPoint();
|
||||
batch = new ArrayList<>();
|
||||
}
|
||||
if (lastEdge != null) {
|
||||
if (!lastEdge.getEndPoint().equals(be.getBeginPoint())) {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
moveToIndex = j;
|
||||
}
|
||||
} else {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
}
|
||||
lastEdge = be;
|
||||
}
|
||||
}
|
||||
|
||||
Set<Integer> removedShapes = new HashSet<>();
|
||||
for (int i1 = 0; i1 < closedShapes.size(); i1++) {
|
||||
if (replacements.containsKey(i1)) {
|
||||
continue;
|
||||
}
|
||||
for (int i2 = 0; i2 < closedShapes.size(); i2++) {
|
||||
if (i1 == i2) {
|
||||
continue;
|
||||
}
|
||||
if (replacements.containsKey(i2)) {
|
||||
continue;
|
||||
}
|
||||
if (closedLayers.get(i1) != closedLayers.get(i2)) {
|
||||
continue;
|
||||
}
|
||||
if (closedFillStyle.get(i1) > 0 && closedFillStyle.get(i2) > 0) {
|
||||
if (closedFillStyle.get(i1) != closedFillStyle.get(i2)) {
|
||||
FILLSTYLEARRAY fa = closedLayers.get(i1) == -1 ? baseFillStyles : fillStyleLayers.get(closedLayers.get(i1));
|
||||
FILLSTYLE fs1 = fa.fillStyles[closedFillStyle.get(i1) - 1];
|
||||
FILLSTYLE fs2 = fa.fillStyles[closedFillStyle.get(i2) - 1];
|
||||
if (!fs1.equals(fs2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closedLineStyle.get(i1) > 0 && closedLineStyle.get(i2) > 0) {
|
||||
if (closedLineStyle.get(i1) != closedLineStyle.get(i2)) {
|
||||
LINESTYLEARRAY lsa = closedLayers.get(i1) == -1 ? baseLineStyles : lineStyleLayers.get(closedLayers.get(i1));
|
||||
if (shapeNum <= 3) {
|
||||
LINESTYLE ls1 = lsa.lineStyles[closedLineStyle.get(i1) - 1];
|
||||
LINESTYLE ls2 = lsa.lineStyles[closedLineStyle.get(i2) - 1];
|
||||
if (!ls1.equals(ls2)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
LINESTYLE2 ls1 = lsa.lineStyles2[closedLineStyle.get(i1) - 1];
|
||||
LINESTYLE2 ls2 = lsa.lineStyles2[closedLineStyle.get(i2) - 1];
|
||||
if (!ls1.equals(ls2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closedShapes.get(i1).size() <= 1 || closedShapes.get(i2).size() <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (closedLineStyle.get(i1) > 0 && closedLineStyle.get(i2) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isEmptyPath(closedShapes.get(i1)) || isEmptyPath(closedShapes.get(i2))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double dist = Distances.getBatchDistance(closedShapes.get(i1), closedShapes.get(i2));
|
||||
|
||||
if (dist <= 10) { //magic
|
||||
/*System.err.println("dist = " + dist);
|
||||
System.err.println("removed");
|
||||
System.err.println("removed shape["+closedShapesI.get(i2)+"]["+closedShapesJ.get(i2)+"], fs "+ closedFillStyle.get(i2) + ", ls " + closedLineStyle.get(i2));
|
||||
System.err.println("left shape["+closedShapesI.get(i1)+"]["+closedShapesJ.get(i1)+"], fs "+ closedFillStyle.get(i1)+ ", ls " + closedLineStyle.get(i1));
|
||||
*/
|
||||
replacements.put(i2, i1);
|
||||
removedShapes.add(i2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
int repI = replacements.containsKey(i) ? replacements.get(i) : i;
|
||||
List<BezierEdge> listI = closedShapes.get(repI);
|
||||
|
||||
for (int j = i + 1; j < closedShapes.size(); j++) {
|
||||
/*if (i == j) {
|
||||
break;
|
||||
}*/
|
||||
if (removedShapes.contains(j) && !replacements.containsKey(j)) {
|
||||
continue;
|
||||
}
|
||||
int repJ = replacements.containsKey(j) ? replacements.get(j) : j;
|
||||
List<BezierEdge> listJ = closedShapes.get(repJ);
|
||||
|
||||
if (closedFillStyle.get(i) != closedFillStyle.get(j)
|
||||
|| closedLineStyle.get(i) != closedLineStyle.get(j)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (listI.equals(listJ)) {
|
||||
replacements.remove(j);
|
||||
removedShapes.add(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = closedShapes.size() - 1; i >= 0; i--) {
|
||||
int to = closedShapesJ.get(i) + closedShapes.get(i).size() - 1;
|
||||
int from = closedShapesJ.get(i);
|
||||
List<BezierEdge> list = shapes.get(closedShapesI.get(i));
|
||||
|
||||
if (removedShapes.contains(i)) {
|
||||
//System.err.println("removing shape["+closedShapesI.get(i)+"]["+from+" to "+to+"]");
|
||||
for (int j = to; j >= from; j--) {
|
||||
list.remove(j);
|
||||
}
|
||||
}
|
||||
if (replacements.containsKey(i)) {
|
||||
list.addAll(from, closedShapes.get(replacements.get(i)));
|
||||
}
|
||||
|
||||
if (!removedShapes.contains(i)) {
|
||||
for (int j = to; j >= from; j--) {
|
||||
if (list.get(j).isEmpty()) {
|
||||
list.remove(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param shapes
|
||||
*/
|
||||
private void removeEmptyEdges(List<List<BezierEdge>> shapes) {
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
if (list.get(j).isEmpty()) {
|
||||
list.remove(j);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,7 @@
|
||||
package com.jpexs.decompiler.flash.xfl.shapefixer;
|
||||
|
||||
import com.jpexs.decompiler.flash.math.BezierEdge;
|
||||
import com.jpexs.decompiler.flash.math.Distances;
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLE;
|
||||
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLE;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLE2;
|
||||
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
|
||||
@@ -29,21 +25,14 @@ 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.Reference;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Shape fixer. This will walk a shape and split crossed edges so FLA editor can
|
||||
* properly load it. It also fixes morphshape - removes duplicated paths, calculate holes.
|
||||
* properly load it.
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
@@ -92,19 +81,23 @@ public class ShapeFixer {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean isEmptyBatch(List<BezierEdge> batch) {
|
||||
for (BezierEdge be : batch) {
|
||||
if (!be.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
protected void beforeHandle(
|
||||
int shapeNum,
|
||||
List<List<BezierEdge>> shapes,
|
||||
List<Integer> fillStyles0,
|
||||
List<Integer> fillStyles1,
|
||||
List<Integer> lineStyles,
|
||||
List<Integer> layers,
|
||||
FILLSTYLEARRAY baseFillStyles,
|
||||
LINESTYLEARRAY baseLineStyles,
|
||||
List<FILLSTYLEARRAY> fillStyleLayers,
|
||||
List<LINESTYLEARRAY> lineStyleLayers
|
||||
) {
|
||||
}
|
||||
|
||||
public List<ShapeRecordAdvanced> fix(
|
||||
List<SHAPERECORD> records,
|
||||
boolean morphshape,
|
||||
int shapeNum,
|
||||
FILLSTYLEARRAY baseFillStyles,
|
||||
LINESTYLEARRAY baseLineStyles
|
||||
@@ -187,415 +180,8 @@ public class ShapeFixer {
|
||||
x = rec.changeX(x);
|
||||
y = rec.changeY(y);
|
||||
}
|
||||
|
||||
if (morphshape) {
|
||||
|
||||
//Remove empty edges
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
if (list.get(j).isEmpty()) {
|
||||
list.remove(j);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<List<BezierEdge>> closedShapes = new ArrayList<>();
|
||||
List<Integer> closedFillStyle = new ArrayList<>();
|
||||
List<Integer> closedLineStyle = new ArrayList<>();
|
||||
List<Integer> closedLayers = new ArrayList<>();
|
||||
List<Integer> closedShapesI = new ArrayList<>();
|
||||
List<Integer> closedShapesJ = new ArrayList<>();
|
||||
|
||||
Map<Integer, Integer> replacements = new LinkedHashMap<>();
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Point2D lastMoveTo = null;
|
||||
BezierEdge lastEdge = null;
|
||||
int moveToIndex = 0;
|
||||
List<BezierEdge> batch = new ArrayList<>();
|
||||
for (int j = 0; j < shapes.get(i).size(); j++) {
|
||||
BezierEdge be = shapes.get(i).get(j);
|
||||
batch.add(be);
|
||||
if (lastMoveTo != null && be.getEndPoint().equals(lastMoveTo)) {
|
||||
closedFillStyle.add(fillStyles0.get(i));
|
||||
closedLineStyle.add(lineStyles.get(i));
|
||||
closedShapes.add(batch);
|
||||
closedLayers.add(layers.get(i));
|
||||
closedShapesI.add(i);
|
||||
closedShapesJ.add(moveToIndex);
|
||||
moveToIndex = j + 1;
|
||||
lastMoveTo = be.getEndPoint();
|
||||
batch = new ArrayList<>();
|
||||
}
|
||||
if (lastEdge != null) {
|
||||
if (!lastEdge.getEndPoint().equals(be.getBeginPoint())) {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
moveToIndex = j;
|
||||
}
|
||||
} else {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
}
|
||||
lastEdge = be;
|
||||
}
|
||||
}
|
||||
|
||||
Set<Integer> removedShapes = new HashSet<>();
|
||||
for (int i1 = 0; i1 < closedShapes.size(); i1++) {
|
||||
if (replacements.containsKey(i1)) {
|
||||
continue;
|
||||
}
|
||||
for (int i2 = 0; i2 < closedShapes.size(); i2++) {
|
||||
if (i1 == i2) {
|
||||
continue;
|
||||
}
|
||||
if (replacements.containsKey(i2)) {
|
||||
continue;
|
||||
}
|
||||
if (closedLayers.get(i1) != closedLayers.get(i2)) {
|
||||
continue;
|
||||
}
|
||||
if (closedFillStyle.get(i1) > 0 && closedFillStyle.get(i2) > 0) {
|
||||
if (closedFillStyle.get(i1) != closedFillStyle.get(i2)) {
|
||||
FILLSTYLEARRAY fa = closedLayers.get(i1) == -1 ? baseFillStyles : fillStyleLayers.get(closedLayers.get(i1));
|
||||
FILLSTYLE fs1 = fa.fillStyles[closedFillStyle.get(i1) - 1];
|
||||
FILLSTYLE fs2 = fa.fillStyles[closedFillStyle.get(i2) - 1];
|
||||
if (!fs1.equals(fs2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closedLineStyle.get(i1) > 0 && closedLineStyle.get(i2) > 0) {
|
||||
if (closedLineStyle.get(i1) != closedLineStyle.get(i2)) {
|
||||
LINESTYLEARRAY lsa = closedLayers.get(i1) == -1 ? baseLineStyles : lineStyleLayers.get(closedLayers.get(i1));
|
||||
if (shapeNum <= 3) {
|
||||
LINESTYLE ls1 = lsa.lineStyles[closedLineStyle.get(i1) - 1];
|
||||
LINESTYLE ls2 = lsa.lineStyles[closedLineStyle.get(i2) - 1];
|
||||
if (!ls1.equals(ls2)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
LINESTYLE2 ls1 = lsa.lineStyles2[closedLineStyle.get(i1) - 1];
|
||||
LINESTYLE2 ls2 = lsa.lineStyles2[closedLineStyle.get(i2) - 1];
|
||||
if (!ls1.equals(ls2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closedShapes.get(i1).size() <= 1 || closedShapes.get(i2).size() <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (closedLineStyle.get(i1) > 0 && closedLineStyle.get(i2) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isEmptyBatch(closedShapes.get(i1)) || isEmptyBatch(closedShapes.get(i2))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double dist = Distances.getBatchDistance(closedShapes.get(i1), closedShapes.get(i2));
|
||||
|
||||
if (dist <= 10) { //magic
|
||||
/*System.err.println("dist = " + dist);
|
||||
System.err.println("removed");
|
||||
System.err.println("removed shape["+closedShapesI.get(i2)+"]["+closedShapesJ.get(i2)+"], fs "+ closedFillStyle.get(i2) + ", ls " + closedLineStyle.get(i2));
|
||||
System.err.println("left shape["+closedShapesI.get(i1)+"]["+closedShapesJ.get(i1)+"], fs "+ closedFillStyle.get(i1)+ ", ls " + closedLineStyle.get(i1));
|
||||
*/
|
||||
replacements.put(i2, i1);
|
||||
removedShapes.add(i2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
int repI = replacements.containsKey(i) ? replacements.get(i) : i;
|
||||
List<BezierEdge> listI = closedShapes.get(repI);
|
||||
|
||||
for (int j = i + 1; j < closedShapes.size(); j++) {
|
||||
/*if (i == j) {
|
||||
break;
|
||||
}*/
|
||||
if (removedShapes.contains(j) && !replacements.containsKey(j)) {
|
||||
continue;
|
||||
}
|
||||
int repJ = replacements.containsKey(j) ? replacements.get(j) : j;
|
||||
List<BezierEdge> listJ = closedShapes.get(repJ);
|
||||
|
||||
if (closedFillStyle.get(i) != closedFillStyle.get(j)
|
||||
|| closedLineStyle.get(i) != closedLineStyle.get(j)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (listI.equals(listJ)) {
|
||||
replacements.remove(j);
|
||||
removedShapes.add(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = closedShapes.size() - 1; i >= 0; i--) {
|
||||
int to = closedShapesJ.get(i) + closedShapes.get(i).size() - 1;
|
||||
int from = closedShapesJ.get(i);
|
||||
List<BezierEdge> list = shapes.get(closedShapesI.get(i));
|
||||
|
||||
if (removedShapes.contains(i)) {
|
||||
//System.err.println("removing shape["+closedShapesI.get(i)+"]["+from+" to "+to+"]");
|
||||
for (int j = to; j >= from; j--) {
|
||||
list.remove(j);
|
||||
}
|
||||
}
|
||||
if (replacements.containsKey(i)) {
|
||||
list.addAll(from, closedShapes.get(replacements.get(i)));
|
||||
}
|
||||
|
||||
if (!removedShapes.contains(i)) {
|
||||
for (int j = to; j >= from; j--) {
|
||||
if (list.get(j).isEmpty()) {
|
||||
list.remove(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This will remove a stroked path with no fill which has same
|
||||
* stroke as subsequent path (or is its prefix). This happens in the
|
||||
* morphshape edges. This needs to be cleaned up before exporting to FLA.
|
||||
*/
|
||||
List<BezierEdge> prevList = null;
|
||||
int prevI = -1;
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (prevList != null) {
|
||||
|
||||
int prevFillStyle0 = fillStyles0.get(i - 1);
|
||||
int prevFillStyle1 = fillStyles1.get(i - 1);
|
||||
int prevLineStyle = lineStyles.get(i - 1);
|
||||
lineStyle = lineStyles.get(i);
|
||||
fillStyle0 = fillStyles0.get(i);
|
||||
fillStyle1 = fillStyles1.get(i);
|
||||
|
||||
if (fillStyle0 == 0 && fillStyle1 == 0 && lineStyle != 0 && lineStyle == prevLineStyle) {
|
||||
if (prevList.size() >= list.size()) {
|
||||
boolean isPrefix = true;
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
if (!prevList.get(j).equals(list.get(j))) {
|
||||
isPrefix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPrefix) {
|
||||
shapes.get(i).clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (prevFillStyle0 == 0 && prevFillStyle1 == 0 && prevLineStyle != 0 && lineStyle == prevLineStyle) {
|
||||
//list startswitch prevList
|
||||
if (list.size() >= prevList.size()) {
|
||||
boolean isPrefix = true;
|
||||
for (int j = 0; j < prevList.size(); j++) {
|
||||
if (!prevList.get(j).equals(list.get(j))) {
|
||||
isPrefix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPrefix) {
|
||||
shapes.get(prevI).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prevI = i;
|
||||
prevList = list;
|
||||
}
|
||||
|
||||
//Clear obvious duplicates
|
||||
//System.err.println("Clear obvious duplicates...");
|
||||
prevList = null;
|
||||
prevI = -1;
|
||||
//if (false)
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
List<BezierEdge> list = shapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (prevList != null) {
|
||||
if (fillStyles0.get(i) == fillStyles0.get(prevI)
|
||||
&& lineStyles.get(i) == lineStyles.get(prevI)) {
|
||||
if (list.equals(prevList)) {
|
||||
System.err.println("clearing " + i);
|
||||
prevList.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
prevI = i;
|
||||
prevList = list;
|
||||
}
|
||||
|
||||
//----------------------------FIND "holes" = apply wind even odd -------------
|
||||
closedShapes = new ArrayList<>();
|
||||
closedFillStyle = new ArrayList<>();
|
||||
closedLineStyle = new ArrayList<>();
|
||||
closedLayers = new ArrayList<>();
|
||||
closedShapesI = new ArrayList<>();
|
||||
closedShapesJ = new ArrayList<>();
|
||||
Set<Integer> closedHolesI = new LinkedHashSet<>();
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Point2D lastMoveTo = null;
|
||||
BezierEdge lastEdge = null;
|
||||
int moveToIndex = 0;
|
||||
List<BezierEdge> batch = new ArrayList<>();
|
||||
for (int j = 0; j < shapes.get(i).size(); j++) {
|
||||
BezierEdge be = shapes.get(i).get(j);
|
||||
batch.add(be);
|
||||
if (lastMoveTo != null && be.getEndPoint().equals(lastMoveTo)) {
|
||||
closedFillStyle.add(fillStyles0.get(i));
|
||||
closedLineStyle.add(lineStyles.get(i));
|
||||
closedShapes.add(batch);
|
||||
closedLayers.add(layers.get(i));
|
||||
closedShapesI.add(i);
|
||||
closedShapesJ.add(moveToIndex);
|
||||
moveToIndex = j + 1;
|
||||
lastMoveTo = be.getEndPoint();
|
||||
batch = new ArrayList<>();
|
||||
}
|
||||
if (lastEdge != null) {
|
||||
if (!lastEdge.getEndPoint().equals(be.getBeginPoint())) {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
moveToIndex = j;
|
||||
}
|
||||
} else {
|
||||
lastMoveTo = be.getBeginPoint();
|
||||
}
|
||||
lastEdge = be;
|
||||
}
|
||||
}
|
||||
|
||||
//reversing anti-clockwise
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
List<BezierEdge> list = closedShapes.get(i);
|
||||
if (list.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<Point2D> points = new ArrayList<>();
|
||||
points.add(list.get(0).getBeginPoint());
|
||||
for (BezierEdge be : list) {
|
||||
if (be.points.size() == 3) {
|
||||
points.add(be.points.get(1));
|
||||
}
|
||||
points.add(be.getEndPoint());
|
||||
}
|
||||
double sum = 0;
|
||||
for (int j = 0; j < points.size(); j++) {
|
||||
Point2D p1 = points.get(j);
|
||||
Point2D p2 = points.get((j + 1) % points.size());
|
||||
sum += (p1.getX() * p2.getY() - p2.getX() * p1.getY());
|
||||
}
|
||||
|
||||
if (sum < 0) { //anti clockwise
|
||||
//reverse the list
|
||||
List<BezierEdge> rev = new ArrayList<>();
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
rev.add(list.get(list.size() - 1 - j).reverse());
|
||||
}
|
||||
|
||||
int shapeI = closedShapesI.get(i);
|
||||
int shapeJ = closedShapesJ.get(i);
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
shapes.get(shapeI).set(shapeJ + j, rev.get(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<Integer, List<Integer>> fillStyleToClosed = new LinkedHashMap<>();
|
||||
for (int i = 0; i < closedShapes.size(); i++) {
|
||||
int fs = closedFillStyle.get(i);
|
||||
if (fs != 0) {
|
||||
if (!fillStyleToClosed.containsKey(fs)) {
|
||||
fillStyleToClosed.put(fs, new ArrayList<>());
|
||||
}
|
||||
fillStyleToClosed.get(fs).add(i);
|
||||
}
|
||||
}
|
||||
for (int fs : fillStyleToClosed.keySet()) {
|
||||
GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
Map<Integer, GeneralPath> closedPaths = new LinkedHashMap<>();
|
||||
for (int i : fillStyleToClosed.get(fs)) {
|
||||
List<BezierEdge> closed = closedShapes.get(i);
|
||||
if (closed.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
GeneralPath closedPath = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
|
||||
closedPath.moveTo(closed.get(0).getBeginPoint().getX(), closed.get(0).getBeginPoint().getY());
|
||||
path.moveTo(closed.get(0).getBeginPoint().getX(), closed.get(0).getBeginPoint().getY());
|
||||
boolean isEmpty = true;
|
||||
for (BezierEdge be : closed) {
|
||||
if (be.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
isEmpty = false;
|
||||
if (be.points.size() == 3) {
|
||||
closedPath.quadTo(be.points.get(1).getX(), be.points.get(1).getY(), be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
path.quadTo(be.points.get(1).getX(), be.points.get(1).getY(), be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
} else {
|
||||
closedPath.lineTo(be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
path.lineTo(be.getEndPoint().getX(), be.getEndPoint().getY());
|
||||
}
|
||||
}
|
||||
closedPath.closePath();
|
||||
path.closePath();
|
||||
if (!isEmpty) {
|
||||
closedPaths.put(i, closedPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i : closedPaths.keySet()) {
|
||||
GeneralPath region = closedPaths.get(i);
|
||||
Rectangle r = region.getBounds();
|
||||
double px;
|
||||
double py;
|
||||
do {
|
||||
px = r.getX() + r.getWidth() * Math.random();
|
||||
py = r.getY() + r.getHeight() * Math.random();
|
||||
} while (!region.contains(px, py));
|
||||
|
||||
if (!path.contains(px, py)) {
|
||||
closedHolesI.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = closedShapes.size() - 1; i >= 0; i--) {
|
||||
if (closedHolesI.contains(i)) {
|
||||
int to = closedShapesJ.get(i) + closedShapes.get(i).size() - 1;
|
||||
int from = closedShapesJ.get(i);
|
||||
List<BezierEdge> list = shapes.get(closedShapesI.get(i));
|
||||
|
||||
//System.err.println("removing hole["+closedShapesI.get(i)+"]["+from+" to "+to+"]");
|
||||
for (int j = to; j >= from; j--) {
|
||||
list.remove(j);
|
||||
}
|
||||
|
||||
int shapeI = closedShapesI.get(i);
|
||||
shapes.add(shapeI + 1, closedShapes.get(i));
|
||||
closedShapes.set(i, new ArrayList<>());
|
||||
fillStyles0.add(shapeI + 1, 0);
|
||||
fillStyles1.add(shapeI + 1, fillStyles0.get(shapeI));
|
||||
lineStyles.add(shapeI + 1, lineStyles.get(shapeI));
|
||||
layers.add(shapeI + 1, layers.get(shapeI));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
beforeHandle(shapeNum, shapes, fillStyles0, fillStyles1, lineStyles, layers, baseFillStyles, baseLineStyles, fillStyleLayers, lineStyleLayers);
|
||||
|
||||
//------------------- detecting overlapping edges --------------------
|
||||
List<BezierPair> splittedPairs = new ArrayList<>();
|
||||
|
||||
Reference in New Issue
Block a user