mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 20:45:29 -04:00
#490: Fixed roundtrip tests and tuned collinearity threshold.
This commit is contained in:
parent
51ace4ca7f
commit
5c1f51f3ca
@ -53,6 +53,9 @@ import static com.twelvemonkeys.lang.Validate.notNull;
|
|||||||
*/
|
*/
|
||||||
public final class AdobePathWriter {
|
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;
|
private final List<AdobePathSegment> segments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,9 +100,8 @@ public final class AdobePathWriter {
|
|||||||
System.err.println("coords: " + Arrays.toString(coords));
|
System.err.println("coords: " + Arrays.toString(coords));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We need to support unlinked segments!
|
// We write collinear points as linked segments
|
||||||
|
boolean collinear = isCollinear(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
|
||||||
boolean collinear;
|
|
||||||
|
|
||||||
switch (segmentType) {
|
switch (segmentType) {
|
||||||
case PathIterator.SEG_MOVETO:
|
case PathIterator.SEG_MOVETO:
|
||||||
@ -110,22 +112,16 @@ public final class AdobePathWriter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PathIterator.SEG_LINETO:
|
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]));
|
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);
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[1], coords[0], coords[1], coords[0], 0, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PathIterator.SEG_QUADTO:
|
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]));
|
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);
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[3], coords[2], 0, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PathIterator.SEG_CUBICTO:
|
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]));
|
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);
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[5], coords[4], 0, 0);
|
||||||
break;
|
break;
|
||||||
@ -139,8 +135,7 @@ public final class AdobePathWriter {
|
|||||||
throw new AssertionError("Not a closed path");
|
throw new AssertionError("Not a closed path");
|
||||||
}
|
}
|
||||||
|
|
||||||
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply);
|
collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply);
|
||||||
System.out.println("isCollinear? " + collinear);
|
|
||||||
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));
|
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
|
// Add to full path
|
||||||
@ -158,20 +153,12 @@ public final class AdobePathWriter {
|
|||||||
return segments;
|
return segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final double COLLINEARITY_THRESHOLD = 0.035;
|
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....
|
||||||
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...
|
|
||||||
return (x1 == x2 && x2 == x3 && y1 == y2 && y2 == y3) ||
|
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 {
|
void writePathResource(final DataOutput output) throws IOException {
|
||||||
|
@ -245,13 +245,25 @@ public class AdobePathWriterTest {
|
|||||||
|
|
||||||
assertEquals(data.length, bytes.length);
|
assertEquals(data.length, bytes.length);
|
||||||
|
|
||||||
// TODO: We currently write all points as linked, this is probably wrong
|
// Path segment 3 contains some unknown bits in the filler bytes, we'll ignore those...
|
||||||
// Also... Photoshop does write "something" undocumented in the filler bytes for the length records, which may or may not be important...
|
cleanLengthRecords(data);
|
||||||
// assertEquals(formatSegments(data), formatSegments(bytes));
|
|
||||||
// assertArrayEquals(data, bytes);
|
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);
|
StringBuilder builder = new StringBuilder(data.length * 5);
|
||||||
|
|
||||||
for (int i = 0; i < data.length; i += 26) {
|
for (int i = 0; i < data.length; i += 26) {
|
||||||
@ -282,9 +294,10 @@ public class AdobePathWriterTest {
|
|||||||
|
|
||||||
assertEquals(data.length, bytes.length);
|
assertEquals(data.length, bytes.length);
|
||||||
|
|
||||||
// TODO: We currently write all points as linked, this is probably wrong
|
// Path segment 3 and 48 contains some unknown bits in the filler bytes, we'll ignore that:
|
||||||
// Also... Photoshop does write "something" undocumented in the filler bytes for the length records, which may or may not be important...
|
cleanLengthRecords(data);
|
||||||
// assertEquals(formatSegments(data), formatSegments(bytes));
|
|
||||||
// assertArrayEquals(data, bytes);
|
assertEquals(formatSegments(data), formatSegments(bytes));
|
||||||
|
assertArrayEquals(data, bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user