2018-08-09 17:58:04 +00:00
|
|
|
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)}'>
|
2018-08-22 13:35:32 +00:00
|
|
|
<path stroke=black fill=white stroke-width=0.01 d="${expectedPath.toSVGString()}"></path>
|
2018-08-09 17:58:04 +00:00
|
|
|
</svg>
|
|
|
|
|
|
|
|
<svg class='actual' viewBox='${getViewBox(actualPath)}'>
|
2018-08-22 13:35:32 +00:00
|
|
|
<path stroke=black fill=white stroke-width=0.01 d="${actualPath.toSVGString()}"></path>
|
2018-08-09 17:58:04 +00:00
|
|
|
</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 = {};
|
2019-03-12 13:20:32 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
2018-08-09 17:58:04 +00:00
|
|
|
}
|
2019-03-12 13:20:32 +00:00
|
|
|
}
|
2018-08-09 17:58:04 +00:00
|
|
|
|
|
|
|
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) {
|
2018-11-05 12:51:40 +00:00
|
|
|
LoadPathKit.then(catchException(done, () => {
|
2019-03-12 13:20:32 +00:00
|
|
|
init();
|
2018-08-09 17:58:04 +00:00
|
|
|
// Test JSON created with:
|
2018-08-31 14:03:23 +00:00
|
|
|
// ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsOp.json -m PathOpsOp$
|
2018-08-09 17:58:04 +00:00
|
|
|
fetch('/base/tests/PathOpsOp.json').then((r) => {
|
2018-11-05 12:51:40 +00:00
|
|
|
r.json().then((json) => {
|
2018-08-09 17:58:04 +00:00
|
|
|
expect(json).toBeTruthy();
|
|
|
|
let testNames = Object.keys(json);
|
2018-08-29 12:26:21 +00:00
|
|
|
// Assert we loaded a non-zero amount of tests, i.e. the JSON is valid.
|
|
|
|
expect(testNames.length > 0).toBeTruthy();
|
2018-08-09 17:58:04 +00:00
|
|
|
testNames.sort();
|
|
|
|
for (testName of testNames) {
|
|
|
|
let test = json[testName];
|
|
|
|
|
2018-08-24 14:44:16 +00:00
|
|
|
let path1 = PathKit.FromCmds(test.p1);
|
2018-08-09 17:58:04 +00:00
|
|
|
expect(path1).not.toBeNull(`path1 error when loading cmds '${test.p1}'`);
|
|
|
|
path1.setFillType(getFillType(test.fillType1));
|
|
|
|
|
2018-08-24 14:44:16 +00:00
|
|
|
let path2 = PathKit.FromCmds(test.p2);
|
2018-08-09 17:58:04 +00:00
|
|
|
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();
|
2018-08-24 14:44:16 +00:00
|
|
|
let expected = PathKit.FromCmds(test.out);
|
2018-08-09 17:58:04 +00:00
|
|
|
// Do a tolerant match.
|
|
|
|
let diff = diffPaths(expected, combined);
|
|
|
|
if (test.expectMatch === 'yes'){
|
2018-08-10 19:53:16 +00:00
|
|
|
// Check fill type
|
|
|
|
expect(combined.getFillType().value).toEqual(getFillType(test.fillTypeOut).value);
|
|
|
|
// diff should be null if the paths are identical (modulo rounding)
|
2018-08-09 17:58:04 +00:00
|
|
|
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();
|
|
|
|
}
|
[PathKit] Rework API to avoid extra copies unless explicitly called for.
Breaking Changes:
- All method calls that mutate a path now return the same JS path
object to allow chaining (moveTo, lineTo, trim, op, simplify, etc).
Pre-existing code likely will need to have some delete() methods
removed because the path will be deleted multiple times. See
chaining.js for this code (basically, we wrote our own binding code
since the default code wasn't quite flexible enough)
- GetCanvasFillType -> GetFillTypeString (Was in https://skia-review.googlesource.com/c/skia/+/147209)
Since Canvas and SVG use the same strings, it seemed logical to make
them share.
- stroke() now takes a single object instead of 3 params. This object
currently can have up to 4 params, cap, join, width, miter_limit.
This object can be expanded on in future versions as more configuration
options are added.
As per custom with v0 software, we bump the minor version to 0.2.X
to indicate breaking changes in a pre-release software package.
Other changes of note:
- Simple tests added for effects (see effects.specs.js) A follow up
CL will handle the Gold (correctness tests)
- Simple tests added for equals and copy constructors (from https://skia-review.googlesource.com/c/skia/+/147209)
- Added transform() to allow for arbitrary matrix transforms
- Added SimpleMatrix as a value_array, which means users can
provide a 9 element array which will be converted to SimpleMatrix
and then SkMatrix on the C++ side.
- Renamed helpers_externs.js to externs.js and expanded it greatly.
This was necessitated by the code written in chaining.js
- Fixed a few bugs in previous tests (svg gold test race condition,
uncaught exception in svg reporting)
See also https://skia-review.googlesource.com/c/skia/+/147209 which
allows .moveTo .lineTo, etc to chain on the C++ SkPath.
Bug: skia:8216
Change-Id: I7450cd8b7b5377cf15c962b02d161677b62d7e15
Reviewed-on: https://skia-review.googlesource.com/147115
Reviewed-by: Mike Reed <reed@google.com>
2018-08-17 17:52:56 +00:00
|
|
|
// combined === path1, so we only have to delete one.
|
2018-08-09 17:58:04 +00:00
|
|
|
path1.delete();
|
|
|
|
path2.delete();
|
|
|
|
}
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2018-11-05 12:51:40 +00:00
|
|
|
}));
|
2018-08-09 17:58:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('simplifies a path with .simplify() and matches what we see from C++', function(done) {
|
2018-11-05 12:51:40 +00:00
|
|
|
LoadPathKit.then(catchException(done, () => {
|
2019-03-12 13:20:32 +00:00
|
|
|
init();
|
2018-08-09 17:58:04 +00:00
|
|
|
// Test JSON created with:
|
2018-08-31 14:03:23 +00:00
|
|
|
// ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsSimplify.json -m PathOpsSimplify$
|
2018-08-09 17:58:04 +00:00
|
|
|
fetch('/base/tests/PathOpsSimplify.json').then((r) => {
|
2018-11-05 12:51:40 +00:00
|
|
|
r.json().then((json) => {
|
2018-08-09 17:58:04 +00:00
|
|
|
expect(json).toBeTruthy();
|
|
|
|
let testNames = Object.keys(json);
|
2018-08-29 12:26:21 +00:00
|
|
|
// Assert we loaded a non-zero amount of tests, i.e. the JSON is valid.
|
|
|
|
expect(testNames.length > 0).toBeTruthy();
|
2018-08-09 17:58:04 +00:00
|
|
|
testNames.sort();
|
|
|
|
for (testName of testNames) {
|
|
|
|
let test = json[testName];
|
|
|
|
|
2018-08-24 14:44:16 +00:00
|
|
|
let path = PathKit.FromCmds(test.path);
|
2018-08-09 17:58:04 +00:00
|
|
|
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();
|
2018-08-24 14:44:16 +00:00
|
|
|
let expected = PathKit.FromCmds(test.out);
|
2018-08-09 17:58:04 +00:00
|
|
|
// Do a tolerant match.
|
|
|
|
let diff = diffPaths(expected, simplified);
|
|
|
|
if (test.expectMatch === 'yes'){
|
2018-08-10 19:53:16 +00:00
|
|
|
// Check fill type
|
|
|
|
expect(simplified.getFillType().value).toEqual(getFillType(test.fillTypeOut).value);
|
|
|
|
// diff should be null if the paths are identical (modulo rounding)
|
2018-08-09 17:58:04 +00:00
|
|
|
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();
|
|
|
|
}
|
[PathKit] Rework API to avoid extra copies unless explicitly called for.
Breaking Changes:
- All method calls that mutate a path now return the same JS path
object to allow chaining (moveTo, lineTo, trim, op, simplify, etc).
Pre-existing code likely will need to have some delete() methods
removed because the path will be deleted multiple times. See
chaining.js for this code (basically, we wrote our own binding code
since the default code wasn't quite flexible enough)
- GetCanvasFillType -> GetFillTypeString (Was in https://skia-review.googlesource.com/c/skia/+/147209)
Since Canvas and SVG use the same strings, it seemed logical to make
them share.
- stroke() now takes a single object instead of 3 params. This object
currently can have up to 4 params, cap, join, width, miter_limit.
This object can be expanded on in future versions as more configuration
options are added.
As per custom with v0 software, we bump the minor version to 0.2.X
to indicate breaking changes in a pre-release software package.
Other changes of note:
- Simple tests added for effects (see effects.specs.js) A follow up
CL will handle the Gold (correctness tests)
- Simple tests added for equals and copy constructors (from https://skia-review.googlesource.com/c/skia/+/147209)
- Added transform() to allow for arbitrary matrix transforms
- Added SimpleMatrix as a value_array, which means users can
provide a 9 element array which will be converted to SimpleMatrix
and then SkMatrix on the C++ side.
- Renamed helpers_externs.js to externs.js and expanded it greatly.
This was necessitated by the code written in chaining.js
- Fixed a few bugs in previous tests (svg gold test race condition,
uncaught exception in svg reporting)
See also https://skia-review.googlesource.com/c/skia/+/147209 which
allows .moveTo .lineTo, etc to chain on the C++ SkPath.
Bug: skia:8216
Change-Id: I7450cd8b7b5377cf15c962b02d161677b62d7e15
Reviewed-on: https://skia-review.googlesource.com/147115
Reviewed-by: Mike Reed <reed@google.com>
2018-08-17 17:52:56 +00:00
|
|
|
// simplified === path, so we only have to delete one.
|
2018-08-09 17:58:04 +00:00
|
|
|
path.delete();
|
|
|
|
}
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2018-11-05 12:51:40 +00:00
|
|
|
}));
|
2018-08-09 17:58:04 +00:00
|
|
|
});
|
2018-08-10 19:53:16 +00:00
|
|
|
});
|