#490: Fixed roundtrip tests and tuned collinearity threshold.

This commit is contained in:
Harald Kuhr 2020-01-09 21:23:20 +01:00
parent 51ace4ca7f
commit 5c1f51f3ca
2 changed files with 33 additions and 33 deletions

View File

@ -53,6 +53,9 @@ import static com.twelvemonkeys.lang.Validate.notNull;
*/
public final class AdobePathWriter {
// TODO: Might need to get hold of more real Photoshop samples to tune this threshold...
private static final double COLLINEARITY_THRESHOLD = 0.00000001;
private final List<AdobePathSegment> segments;
/**
@ -97,9 +100,8 @@ public final class AdobePathWriter {
System.err.println("coords: " + Arrays.toString(coords));
}
// TODO: We need to support unlinked segments!
boolean collinear;
// We write collinear points as linked segments
boolean collinear = isCollinear(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
switch (segmentType) {
case PathIterator.SEG_MOVETO:
@ -110,22 +112,16 @@ public final class AdobePathWriter {
break;
case PathIterator.SEG_LINETO:
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
System.out.println("isCollinear? " + collinear);
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[1], coords[0], coords[1], coords[0], 0, 0);
break;
case PathIterator.SEG_QUADTO:
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
System.out.println("isCollinear? " + collinear);
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[3], coords[2], 0, 0);
break;
case PathIterator.SEG_CUBICTO:
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
System.out.println("isCollinear? " + collinear);
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[5], coords[4], 0, 0);
break;
@ -139,8 +135,7 @@ public final class AdobePathWriter {
throw new AssertionError("Not a closed path");
}
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply);
System.out.println("isCollinear? " + collinear);
collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply);
subpath.set(0, new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, initial.apy, initial.apx, initial.cply, initial.cplx));
// Add to full path
@ -158,20 +153,12 @@ public final class AdobePathWriter {
return segments;
}
private static final double COLLINEARITY_THRESHOLD = 0.035;
private static boolean isCollinearAndSameDistance(double x1, double y1, double x2, double y2, double x3, double y3) {
// return (y3 - y2) * (x2 - x1) == (y2 - y1) * (x3 - x2); // Collinear Slope
// return x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2) == 0; // Collinear (Double) Area
// return Math.abs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) <= 0.0005; // With some slack...
// return Math.abs(Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) - Math.sqrt(Math.pow(x3 - x2, 2) + Math.pow(y3 - y2, 2))) <= 0.01;
// TODO: Get hold of a real Photoshop sample... The current data may be wrong.
// TODO: If correct, PS writes linked if all points are the same...
private static boolean isCollinear(double x1, double y1, double x2, double y2, double x3, double y3) {
// PS seems to write as linked if all points are the same....
return (x1 == x2 && x2 == x3 && y1 == y2 && y2 == y3) ||
(x1 != x2 || y1 != y2) && (x2 != x3 || y2 != y3) && Math.abs((x2 - x1) - (x3 - x2)) <= COLLINEARITY_THRESHOLD && Math.abs((y2 - y1) - (y3 - y2)) <= COLLINEARITY_THRESHOLD;
(x1 != x2 || y1 != y2) && (x2 != x3 || y2 != y3) &&
Math.abs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) <= COLLINEARITY_THRESHOLD; // With some slack...
}
void writePathResource(final DataOutput output) throws IOException {

View File

@ -245,13 +245,25 @@ public class AdobePathWriterTest {
assertEquals(data.length, bytes.length);
// TODO: We currently write all points as linked, this is probably wrong
// Also... Photoshop does write "something" undocumented in the filler bytes for the length records, which may or may not be important...
// assertEquals(formatSegments(data), formatSegments(bytes));
// assertArrayEquals(data, bytes);
// Path segment 3 contains some unknown bits in the filler bytes, we'll ignore those...
cleanLengthRecords(data);
assertEquals(formatSegments(data), formatSegments(bytes));
assertArrayEquals(data, bytes);
}
static String formatSegments(byte[] data) {
private static void cleanLengthRecords(byte[] data) {
for (int i = 0; i < data.length; i += 26) {
if (data[i + 1] == CLOSED_SUBPATH_LENGTH_RECORD) {
// Clean everything after record type and length field
for (int j = 4; j < 26; j++) {
data[i + j] = 0;
}
}
}
}
private static String formatSegments(byte[] data) {
StringBuilder builder = new StringBuilder(data.length * 5);
for (int i = 0; i < data.length; i += 26) {
@ -282,9 +294,10 @@ public class AdobePathWriterTest {
assertEquals(data.length, bytes.length);
// TODO: We currently write all points as linked, this is probably wrong
// Also... Photoshop does write "something" undocumented in the filler bytes for the length records, which may or may not be important...
// assertEquals(formatSegments(data), formatSegments(bytes));
// assertArrayEquals(data, bytes);
// Path segment 3 and 48 contains some unknown bits in the filler bytes, we'll ignore that:
cleanLengthRecords(data);
assertEquals(formatSegments(data), formatSegments(bytes));
assertArrayEquals(data, bytes);
}
}