You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
9.7 KiB
222 lines
9.7 KiB
4 months ago
|
var dumpErrors = false;
|
||
|
var container;
|
||
|
|
||
|
function getViewBox(path) {
|
||
|
let bounds = path.getBounds();
|
||
|
return `${(bounds.fLeft-2)*.95} ${(bounds.fTop-2)*.95} ${(bounds.fRight+2)*1.05} ${(bounds.fBottom+2)*1.05}`;
|
||
|
}
|
||
|
|
||
|
function addSVG(testName, expectedPath, actualPath, message) {
|
||
|
if (!dumpErrors) {
|
||
|
return;
|
||
|
}
|
||
|
if (!container) {
|
||
|
let styleEl = document.createElement('style');
|
||
|
document.head.appendChild(styleEl);
|
||
|
let sheet = styleEl.sheet;
|
||
|
sheet.insertRule(`svg {
|
||
|
border: 1px solid #DDD;
|
||
|
max-width: 45%;
|
||
|
vertical-align: top;
|
||
|
}`, 0);
|
||
|
|
||
|
container = document.createElement('div');
|
||
|
document.body.appendChild(container);
|
||
|
|
||
|
}
|
||
|
|
||
|
let thisTest = document.createElement('div');
|
||
|
thisTest.innerHTML = `
|
||
|
<h2>Failed test ${testName}</h2>
|
||
|
|
||
|
<div>${message}</div>
|
||
|
|
||
|
<svg class='expected' viewBox='${getViewBox(expectedPath)}'>
|
||
|
<path stroke=black fill=white stroke-width=0.01 d="${expectedPath.toSVGString()}"></path>
|
||
|
</svg>
|
||
|
|
||
|
<svg class='actual' viewBox='${getViewBox(actualPath)}'>
|
||
|
<path stroke=black fill=white stroke-width=0.01 d="${actualPath.toSVGString()}"></path>
|
||
|
</svg>
|
||
|
`;
|
||
|
container.appendChild(thisTest);
|
||
|
|
||
|
}
|
||
|
|
||
|
const TOLERANCE = 0.0001;
|
||
|
|
||
|
function diffPaths(expected, actual) {
|
||
|
// Look through commands and see if they are within tolerance.
|
||
|
let eCmds = expected.toCmds(), aCmds = actual.toCmds();
|
||
|
if (eCmds.length !== aCmds.length) {
|
||
|
//console.log(`Expected: ${JSON.stringify(eCmds)} and Actual: ${JSON.stringify(aCmds)}`);
|
||
|
return `Different amount of verbs. Expected had ${eCmds.length}, Actual had ${aCmds.length}`;
|
||
|
}
|
||
|
for(let idx = 0; idx < eCmds.length; idx++){
|
||
|
let eCmd = eCmds[idx], aCmd = aCmds[idx];
|
||
|
if (eCmd.length !== aCmd.length) {
|
||
|
// Should never happen, means WASM code is returning bad ops.
|
||
|
return `Command index ${idx} differs in num arguments. Expected had ${eCmd.length}, Actual had ${aCmd.length}`;
|
||
|
}
|
||
|
let eVerb = eCmd[0], aVerb = aCmd[0];
|
||
|
if (eVerb !== aVerb) {
|
||
|
return `Command index ${idx} differs. Expected had ${eVerb}, Actual had ${aVerb}`;
|
||
|
}
|
||
|
for (let arg = 1; arg < eCmd.length; arg++) {
|
||
|
if (Math.abs(eCmd[arg] - aCmd[arg]) > TOLERANCE) {
|
||
|
return `Command index ${idx} has different argument for verb ${eVerb} at position ${arg}. Expected had ${eCmd[arg]}, Actual had ${aCmd[arg]}`
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
describe('PathKit\'s PathOps Behavior', function() {
|
||
|
var PATHOP_MAP = {};
|
||
|
var FILLTYPE_MAP = {};
|
||
|
|
||
|
function init() {
|
||
|
if (PathKit && !PATHOP_MAP['kIntersect_SkPathOp']) {
|
||
|
PATHOP_MAP = {
|
||
|
'kIntersect_SkPathOp': PathKit.PathOp.INTERSECT,
|
||
|
'kDifference_SkPathOp': PathKit.PathOp.DIFFERENCE,
|
||
|
'kUnion_SkPathOp': PathKit.PathOp.UNION,
|
||
|
'kXOR_SkPathOp': PathKit.PathOp.XOR,
|
||
|
'kXOR_PathOp': PathKit.PathOp.XOR,
|
||
|
'kReverseDifference_SkPathOp': PathKit.PathOp.REVERSE_DIFFERENCE,
|
||
|
};
|
||
|
FILLTYPE_MAP = {
|
||
|
'kWinding_FillType': PathKit.FillType.WINDING,
|
||
|
'kEvenOdd_FillType': PathKit.FillType.EVENODD,
|
||
|
'kInverseWinding_FillType': PathKit.FillType.INVERSE_WINDING,
|
||
|
'kInverseEvenOdd_FillType': PathKit.FillType.INVERSE_EVENODD,
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getFillType(str) {
|
||
|
let e = FILLTYPE_MAP[str];
|
||
|
expect(e).toBeTruthy(`Could not find FillType Enum for ${str}`);
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
function getPathOp(str) {
|
||
|
let e = PATHOP_MAP[str];
|
||
|
expect(e).toBeTruthy(`Could not find PathOp Enum for ${str}`);
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
it('combines two paths with .op() and matches what we see from C++', function(done) {
|
||
|
LoadPathKit.then(catchException(done, () => {
|
||
|
init();
|
||
|
// Test JSON created with:
|
||
|
// ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsOp.json -m PathOpsOp$
|
||
|
fetch('/base/tests/PathOpsOp.json').then((r) => {
|
||
|
r.json().then((json) => {
|
||
|
expect(json).toBeTruthy();
|
||
|
let testNames = Object.keys(json);
|
||
|
// Assert we loaded a non-zero amount of tests, i.e. the JSON is valid.
|
||
|
expect(testNames.length > 0).toBeTruthy();
|
||
|
testNames.sort();
|
||
|
for (testName of testNames) {
|
||
|
let test = json[testName];
|
||
|
|
||
|
let path1 = PathKit.FromCmds(test.p1);
|
||
|
expect(path1).not.toBeNull(`path1 error when loading cmds '${test.p1}'`);
|
||
|
path1.setFillType(getFillType(test.fillType1));
|
||
|
|
||
|
let path2 = PathKit.FromCmds(test.p2);
|
||
|
expect(path2).not.toBeNull(`path2 error when loading cmds '${test.p2}'`);
|
||
|
path2.setFillType(getFillType(test.fillType2));
|
||
|
|
||
|
let combined = path1.op(path2, getPathOp(test.op));
|
||
|
|
||
|
if (test.expectSuccess === 'no') {
|
||
|
expect(combined).toBeNull(`Test ${testName} should have not created output, but did`);
|
||
|
} else {
|
||
|
expect(combined).not.toBeNull();
|
||
|
let expected = PathKit.FromCmds(test.out);
|
||
|
// Do a tolerant match.
|
||
|
let diff = diffPaths(expected, combined);
|
||
|
if (test.expectMatch === 'yes'){
|
||
|
// Check fill type
|
||
|
expect(combined.getFillType().value).toEqual(getFillType(test.fillTypeOut).value);
|
||
|
// diff should be null if the paths are identical (modulo rounding)
|
||
|
if (diff) {
|
||
|
expect(`[${testName}] ${diff}`).toBe('');
|
||
|
addSVG('[PathOps] ' + testName, expected, combined, diff);
|
||
|
}
|
||
|
} else if (test.expectMatch === 'flaky') {
|
||
|
// Don't worry about it, at least it didn't crash.
|
||
|
} else {
|
||
|
if (!diff) {
|
||
|
expect(`[${testName}] was expected to have paths that differed`).not.toBe('');
|
||
|
}
|
||
|
}
|
||
|
expected.delete();
|
||
|
}
|
||
|
// combined === path1, so we only have to delete one.
|
||
|
path1.delete();
|
||
|
path2.delete();
|
||
|
}
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
}));
|
||
|
});
|
||
|
|
||
|
it('simplifies a path with .simplify() and matches what we see from C++', function(done) {
|
||
|
LoadPathKit.then(catchException(done, () => {
|
||
|
init();
|
||
|
// Test JSON created with:
|
||
|
// ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsSimplify.json -m PathOpsSimplify$
|
||
|
fetch('/base/tests/PathOpsSimplify.json').then((r) => {
|
||
|
r.json().then((json) => {
|
||
|
expect(json).toBeTruthy();
|
||
|
let testNames = Object.keys(json);
|
||
|
// Assert we loaded a non-zero amount of tests, i.e. the JSON is valid.
|
||
|
expect(testNames.length > 0).toBeTruthy();
|
||
|
testNames.sort();
|
||
|
for (testName of testNames) {
|
||
|
let test = json[testName];
|
||
|
|
||
|
let path = PathKit.FromCmds(test.path);
|
||
|
expect(path).not.toBeNull(`path1 error when loading cmds '${test.path}'`);
|
||
|
path.setFillType(getFillType(test.fillType));
|
||
|
|
||
|
let simplified = path.simplify();
|
||
|
|
||
|
if (test.expectSuccess === 'no') {
|
||
|
expect(simplified).toBeNull(`Test ${testName} should have not created output, but did`);
|
||
|
} else {
|
||
|
expect(simplified).not.toBeNull();
|
||
|
let expected = PathKit.FromCmds(test.out);
|
||
|
// Do a tolerant match.
|
||
|
let diff = diffPaths(expected, simplified);
|
||
|
if (test.expectMatch === 'yes'){
|
||
|
// Check fill type
|
||
|
expect(simplified.getFillType().value).toEqual(getFillType(test.fillTypeOut).value);
|
||
|
// diff should be null if the paths are identical (modulo rounding)
|
||
|
if (diff) {
|
||
|
expect(`[${testName}] ${diff}`).toBe('');
|
||
|
addSVG('[Simplify] ' + testName, expected, simplified, diff);
|
||
|
}
|
||
|
} else if (test.expectMatch === 'flaky') {
|
||
|
// Don't worry about it, at least it didn't crash.
|
||
|
} else {
|
||
|
if (!diff) {
|
||
|
expect(`[${testName}] was expected to not match output`).not.toBe('');
|
||
|
}
|
||
|
}
|
||
|
expected.delete();
|
||
|
}
|
||
|
// simplified === path, so we only have to delete one.
|
||
|
path.delete();
|
||
|
}
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
}));
|
||
|
});
|
||
|
});
|