[PathKit] Write more complete docs and clean up API to be consistent
Breaking Changes (should be minor, as it's mostly just things for testing): - PathKit.ApplyPathOp should have returned a new SkPath, but didn't. It now does and is named "MakeFromOp", which makes the convention of "Have 'make' in name, needs delete" more consistent. - PathKit.FromCmds(arr) now only needs to take the JS Array and will handle the TypedArrays under the hood. If clients want to deal with TypedArrays themselves, they can use _FromCmds(ptr, len) directly. - PathKit.MakeLTRBRect is now just PathKit.LTRBRect. The thing returned is a normal JS Object and doesn't need delete(). As per custom with v0 apps, we are updating the minor version to v0.3.0 to account for breaking changes. Docs-Preview: https://skia.org/?cl=147960 Bug: skia:8216 Change-Id: Ia3626e69f3e97698fc62a6aee876af005e29ffca Reviewed-on: https://skia-review.googlesource.com/147960 Reviewed-by: Mike Reed <reed@google.com> Reviewed-by: Heather Miller <hcm@google.com>
This commit is contained in:
parent
1118cfdbdc
commit
d993648fa4
@ -13,7 +13,7 @@ npm: npm-test npm-debug
|
||||
|
||||
publish:
|
||||
cd npm-wasm; npm publish
|
||||
cd ../npm-asmjs; npm publish
|
||||
cd npm-asmjs; npm publish
|
||||
|
||||
update-major:
|
||||
cd npm-wasm; npm version major
|
||||
@ -22,12 +22,12 @@ update-major:
|
||||
|
||||
update-minor:
|
||||
cd npm-wasm; npm version minor
|
||||
cd ../npm-asmjs; npm version minor
|
||||
cd npm-asmjs; npm version minor
|
||||
echo "Don't forget to publish."
|
||||
|
||||
update-patch:
|
||||
cd npm-wasm; npm version patch
|
||||
cd ../npm-asmjs; npm version patch
|
||||
cd npm-asmjs; npm version patch
|
||||
echo "Don't forget to publish."
|
||||
|
||||
# Build the library and run the tests. If developing locally, test-continuous is better
|
||||
|
@ -115,13 +115,13 @@
|
||||
return null;
|
||||
};
|
||||
|
||||
PathKit.SkPath.prototype.quadraticCurveTo = function(x1, y1, x2, y2) {
|
||||
this._quadTo(x1, y1, x2, y2);
|
||||
PathKit.SkPath.prototype.quadraticCurveTo = function(cpx, cpy, x, y) {
|
||||
this._quadTo(cpx, cpy, x, y);
|
||||
return this;
|
||||
};
|
||||
|
||||
PathKit.SkPath.prototype.quadTo = function(x1, y1, x2, y2) {
|
||||
this._quadTo(x1, y1, x2, y2);
|
||||
PathKit.SkPath.prototype.quadTo = function(cpx, cpy, x, y) {
|
||||
this._quadTo(cpx, cpy, x, y);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -24,6 +24,9 @@ var PathKit = {
|
||||
SkBits2FloatUnsigned: function(num) {},
|
||||
_malloc: function(size) {},
|
||||
onRuntimeInitialized: function() {},
|
||||
_FromCmds: function(ptr, size) {},
|
||||
loadCmdsTypedArray: function(arr) {},
|
||||
FromCmds: function(arr) {},
|
||||
|
||||
HEAPF32: {},
|
||||
|
||||
@ -40,7 +43,7 @@ var PathKit = {
|
||||
_lineTo: function(x1, y1) {},
|
||||
_moveTo: function(x1, y1) {},
|
||||
_op: function(otherPath, op) {},
|
||||
_quadTo: function(x1, y1, x2, y2) {},
|
||||
_quadTo: function(cpx, cpy, x, y) {},
|
||||
_rect: function(x, y, w, h) {},
|
||||
_simplify: function() {},
|
||||
_stroke: function(opts) {},
|
||||
|
@ -7,9 +7,9 @@
|
||||
var Float32ArrayCache = {};
|
||||
|
||||
// Takes a 2D array of commands and puts them into the WASM heap
|
||||
// as a 1D array. This allowing them to referenced from the C++ code.
|
||||
// as a 1D array. This allows them to referenced from the C++ code.
|
||||
// Returns a 2 element array, with the first item being essentially a
|
||||
// pointer to the array and the second item being the lengh of
|
||||
// pointer to the array and the second item being the length of
|
||||
// the new 1D array.
|
||||
//
|
||||
// Example usage:
|
||||
@ -26,7 +26,7 @@
|
||||
//
|
||||
// If arguments at index 1... in each cmd row are strings, they will be
|
||||
// parsed as hex, and then converted to floats using SkBits2FloatUnsigned
|
||||
PathKit['loadCmdsTypedArray'] = function(arr) {
|
||||
PathKit.loadCmdsTypedArray = function(arr) {
|
||||
var len = 0;
|
||||
for (var r = 0; r < arr.length; r++) {
|
||||
len += arr[r].length;
|
||||
@ -57,5 +57,13 @@
|
||||
PathKit.HEAPF32.set(ta, ptr / ta.BYTES_PER_ELEMENT);
|
||||
return [ptr, len];
|
||||
}
|
||||
|
||||
// Experimentation has shown that using TypedArrays to pass arrays from
|
||||
// JS to C++ is faster than passing the JS Arrays across.
|
||||
// See above for example of cmds.
|
||||
PathKit.FromCmds = function(cmds) {
|
||||
var ptrLen = PathKit.loadCmdsTypedArray(cmds);
|
||||
return PathKit._FromCmds(ptrLen[0], ptrLen[1]);
|
||||
}
|
||||
}(Module)); // When this file is loaded in, the high level object is "Module";
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
<canvas class=big id=canvas8></canvas>
|
||||
<canvas class=big id=canvas9></canvas>
|
||||
<canvas class=big id=canvas10></canvas>
|
||||
<canvas class=big id=canvas11></canvas>
|
||||
<canvas class=big id=canvasTransform></canvas>
|
||||
|
||||
<h2> Supports fill-rules of nonzero and evenodd </h2>
|
||||
@ -193,58 +194,74 @@
|
||||
// no-op
|
||||
(path) => path,
|
||||
// dash
|
||||
(path) => path.dash(10, 3, 0),
|
||||
(path, counter) => path.dash(10, 3, counter/5),
|
||||
// trim (takes optional 3rd param for returning the trimmed part
|
||||
// or the complement)
|
||||
(path) => path.trim(0.25, 0.8, false),
|
||||
(path, counter) => path.trim((counter/100) % 1, 0.8, false),
|
||||
// simplify
|
||||
(path) => path.simplify(),
|
||||
// stroke
|
||||
(path) => path.stroke({
|
||||
width: 15,
|
||||
(path, counter) => path.stroke({
|
||||
width: 10 * (Math.sin(counter/30) + 1),
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
miter_limit: 1,
|
||||
}),
|
||||
// "offset effect", that is, making a border around the shape.
|
||||
(path) => {
|
||||
(path, counter) => {
|
||||
let orig = path.copy();
|
||||
path.stroke({
|
||||
width: 10,
|
||||
width: 10 + (counter / 4) % 50,
|
||||
join: PathKit.StrokeJoin.ROUND,
|
||||
cap: PathKit.StrokeCap.SQUARE,
|
||||
})
|
||||
.op(orig, PathKit.PathOp.DIFFERENCE);
|
||||
orig.delete();
|
||||
},
|
||||
(path, counter) => {
|
||||
let simplified = path.simplify().copy();
|
||||
path.stroke({
|
||||
width: 2 + (counter / 2) % 100,
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
})
|
||||
.op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
|
||||
simplified.delete();
|
||||
}
|
||||
];
|
||||
|
||||
let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Offset"];
|
||||
let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
|
||||
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
let path = PathKit.NewPath();
|
||||
drawStar(path);
|
||||
let counter = 0;
|
||||
function frame() {
|
||||
counter++;
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
let path = PathKit.NewPath();
|
||||
drawStar(path);
|
||||
|
||||
// The transforms apply directly to the path.
|
||||
effects[i](path);
|
||||
// The transforms apply directly to the path.
|
||||
effects[i](path, counter);
|
||||
|
||||
let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
ctx.fillStyle = '#3c597a';
|
||||
if (i === 4 || i === 5) {
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
} else {
|
||||
ctx.stroke(path.toPath2D());
|
||||
let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
ctx.fillStyle = '#3c597a';
|
||||
if (i >=4 ) {
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
} else {
|
||||
ctx.stroke(path.toPath2D());
|
||||
}
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
|
||||
let x = 150-ctx.measureText(names[i]).width/2;
|
||||
ctx.strokeText(names[i], x, 290);
|
||||
|
||||
path.delete();
|
||||
}
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
|
||||
let x = 150-ctx.measureText(names[i]).width/2;
|
||||
ctx.strokeText(names[i], x, 290);
|
||||
|
||||
path.delete();
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function MatrixTransformExample(PathKit) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "experimental-pathkit-asmjs",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "A asm.js version of Skia's PathOps toolkit",
|
||||
"main": "bin/pathkit.js",
|
||||
"homepage": "https://github.com/google/skia/tree/master/experimental/pathkit",
|
||||
|
@ -37,6 +37,7 @@
|
||||
<canvas class=big id=canvas8></canvas>
|
||||
<canvas class=big id=canvas9></canvas>
|
||||
<canvas class=big id=canvas10></canvas>
|
||||
<canvas class=big id=canvas11></canvas>
|
||||
<canvas class=big id=canvasTransform></canvas>
|
||||
|
||||
<h2> Supports fill-rules of nonzero and evenodd </h2>
|
||||
@ -193,58 +194,74 @@
|
||||
// no-op
|
||||
(path) => path,
|
||||
// dash
|
||||
(path) => path.dash(10, 3, 0),
|
||||
(path, counter) => path.dash(10, 3, counter/5),
|
||||
// trim (takes optional 3rd param for returning the trimmed part
|
||||
// or the complement)
|
||||
(path) => path.trim(0.25, 0.8, false),
|
||||
(path, counter) => path.trim((counter/100) % 1, 0.8, false),
|
||||
// simplify
|
||||
(path) => path.simplify(),
|
||||
// stroke
|
||||
(path) => path.stroke({
|
||||
width: 15,
|
||||
(path, counter) => path.stroke({
|
||||
width: 10 * (Math.sin(counter/30) + 1),
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
miter_limit: 1,
|
||||
}),
|
||||
// "offset effect", that is, making a border around the shape.
|
||||
(path) => {
|
||||
(path, counter) => {
|
||||
let orig = path.copy();
|
||||
path.stroke({
|
||||
width: 10,
|
||||
width: 10 + (counter / 4) % 50,
|
||||
join: PathKit.StrokeJoin.ROUND,
|
||||
cap: PathKit.StrokeCap.SQUARE,
|
||||
})
|
||||
.op(orig, PathKit.PathOp.DIFFERENCE);
|
||||
orig.delete();
|
||||
},
|
||||
(path, counter) => {
|
||||
let simplified = path.simplify().copy();
|
||||
path.stroke({
|
||||
width: 2 + (counter / 2) % 100,
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
})
|
||||
.op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
|
||||
simplified.delete();
|
||||
}
|
||||
];
|
||||
|
||||
let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Offset"];
|
||||
let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
|
||||
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
let path = PathKit.NewPath();
|
||||
drawStar(path);
|
||||
let counter = 0;
|
||||
function frame() {
|
||||
counter++;
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
let path = PathKit.NewPath();
|
||||
drawStar(path);
|
||||
|
||||
// The transforms apply directly to the path.
|
||||
effects[i](path);
|
||||
// The transforms apply directly to the path.
|
||||
effects[i](path, counter);
|
||||
|
||||
let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
ctx.fillStyle = '#3c597a';
|
||||
if (i === 4 || i === 5) {
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
} else {
|
||||
ctx.stroke(path.toPath2D());
|
||||
let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
ctx.fillStyle = '#3c597a';
|
||||
if (i >=4 ) {
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
} else {
|
||||
ctx.stroke(path.toPath2D());
|
||||
}
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
|
||||
let x = 150-ctx.measureText(names[i]).width/2;
|
||||
ctx.strokeText(names[i], x, 290);
|
||||
|
||||
path.delete();
|
||||
}
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
|
||||
let x = 150-ctx.measureText(names[i]).width/2;
|
||||
ctx.strokeText(names[i], x, 290);
|
||||
|
||||
path.delete();
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function MatrixTransformExample(PathKit) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "experimental-pathkit-wasm",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "A WASM version of Skia's PathOps toolkit",
|
||||
"main": "bin/pathkit.js",
|
||||
"homepage": "https://github.com/google/skia/tree/master/experimental/pathkit",
|
||||
|
@ -134,7 +134,7 @@ SkPathOrNull EMSCRIPTEN_KEEPALIVE FromCmds(uintptr_t /* float* */ cptr, int numC
|
||||
CHECK_NUM_ARGS(5);
|
||||
x1 = cmds[i++], y1 = cmds[i++];
|
||||
x2 = cmds[i++], y2 = cmds[i++];
|
||||
x3 = cmds[i++]; // width
|
||||
x3 = cmds[i++]; // weight
|
||||
path.conicTo(x1, y1, x2, y2, x3);
|
||||
break;
|
||||
case CUBIC:
|
||||
@ -249,6 +249,14 @@ bool EMSCRIPTEN_KEEPALIVE ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, Sk
|
||||
return Op(pathOne, pathTwo, op, &pathOne);
|
||||
}
|
||||
|
||||
SkPathOrNull EMSCRIPTEN_KEEPALIVE MakeFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
|
||||
SkPath out;
|
||||
if (Op(pathOne, pathTwo, op, &out)) {
|
||||
return emscripten::val(out);
|
||||
}
|
||||
return emscripten::val::null();
|
||||
}
|
||||
|
||||
SkPathOrNull EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) {
|
||||
SkPath path;
|
||||
if (builder.resolve(&path)) {
|
||||
@ -481,9 +489,9 @@ EMSCRIPTEN_BINDINGS(skia) {
|
||||
.function("_arcTo", &ApplyArcTo)
|
||||
//"bezierCurveTo" alias handled in JS bindings
|
||||
.function("_close", &ApplyClose)
|
||||
//"closePath" alias handled in JS bindings
|
||||
.function("_conicTo", &ApplyConicTo)
|
||||
.function("_cubicTo", &ApplyCubicTo)
|
||||
//"closePath" alias handled in JS bindings
|
||||
|
||||
.function("_ellipse", &ApplyEllipse)
|
||||
.function("_lineTo", &ApplyLineTo)
|
||||
@ -530,6 +538,7 @@ EMSCRIPTEN_BINDINGS(skia) {
|
||||
.constructor<>()
|
||||
|
||||
.function("add", &SkOpBuilder::add)
|
||||
.function("make", &ResolveBuilder)
|
||||
.function("resolve", &ResolveBuilder);
|
||||
|
||||
// Without these function() bindings, the function would be exposed but oblivious to
|
||||
@ -537,13 +546,14 @@ EMSCRIPTEN_BINDINGS(skia) {
|
||||
|
||||
// Import
|
||||
function("FromSVGString", &FromSVGString);
|
||||
function("FromCmds", &FromCmds);
|
||||
function("NewPath", &NewPath);
|
||||
function("NewPath", &CopyPath);
|
||||
// FromCmds is defined in helper.js to make use of TypedArrays transparent.
|
||||
function("_FromCmds", &FromCmds);
|
||||
// Path2D is opaque, so we can't read in from it.
|
||||
|
||||
// PathOps
|
||||
function("ApplyPathOp", &ApplyPathOp);
|
||||
function("MakeFromOp", &MakeFromOp);
|
||||
|
||||
enum_<SkPathOp>("PathOp")
|
||||
.value("DIFFERENCE", SkPathOp::kDifference_SkPathOp)
|
||||
@ -574,7 +584,7 @@ EMSCRIPTEN_BINDINGS(skia) {
|
||||
.field("fRight", &SkRect::fRight)
|
||||
.field("fBottom", &SkRect::fBottom);
|
||||
|
||||
function("MakeLTRBRect", &SkRect::MakeLTRB);
|
||||
function("LTRBRect", &SkRect::MakeLTRB);
|
||||
|
||||
// Stroke
|
||||
enum_<SkPaint::Join>("StrokeJoin")
|
||||
|
@ -112,13 +112,13 @@ describe('PathKit\'s Path Behavior', function() {
|
||||
LoadPathKit.then(() => {
|
||||
// Based on test_bounds_crbug_513799
|
||||
let path = PathKit.NewPath();
|
||||
expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(0, 0, 0, 0));
|
||||
expect(path.getBounds()).toEqual(PathKit.LTRBRect(0, 0, 0, 0));
|
||||
path.moveTo(-5, -8);
|
||||
expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(-5, -8, -5, -8));
|
||||
expect(path.getBounds()).toEqual(PathKit.LTRBRect(-5, -8, -5, -8));
|
||||
path.rect(1, 2, 2, 2);
|
||||
expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(-5, -8, 3, 4));
|
||||
expect(path.getBounds()).toEqual(PathKit.LTRBRect(-5, -8, 3, 4));
|
||||
path.moveTo(1, 2);
|
||||
expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(-5, -8, 3, 4));
|
||||
expect(path.getBounds()).toEqual(PathKit.LTRBRect(-5, -8, 3, 4));
|
||||
path.delete();
|
||||
done();
|
||||
});
|
||||
@ -130,9 +130,9 @@ describe('PathKit\'s Path Behavior', function() {
|
||||
let path = PathKit.NewPath();
|
||||
path.moveTo(1, 1);
|
||||
path.quadraticCurveTo(4, 3, 2, 2);
|
||||
expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(1, 1, 4, 3));
|
||||
expect(path.getBounds()).toEqual(PathKit.LTRBRect(1, 1, 4, 3));
|
||||
ExpectRectsToBeEqual(path.computeTightBounds(),
|
||||
PathKit.MakeLTRBRect(1, 1,
|
||||
PathKit.LTRBRect(1, 1,
|
||||
bits2float("0x40333334"), // 2.8
|
||||
bits2float("0x40155556"))); // 2.3333333
|
||||
path.delete();
|
||||
|
@ -117,11 +117,6 @@ describe('PathKit\'s PathOps Behavior', function() {
|
||||
return e;
|
||||
}
|
||||
|
||||
function fromCmds(cmds) {
|
||||
let [ptr, len] = PathKit.loadCmdsTypedArray(cmds);
|
||||
return PathKit.FromCmds(ptr, len);
|
||||
}
|
||||
|
||||
it('combines two paths with .op() and matches what we see from C++', function(done) {
|
||||
LoadPathKit.then(() => {
|
||||
// Test JSON created with:
|
||||
@ -135,11 +130,11 @@ describe('PathKit\'s PathOps Behavior', function() {
|
||||
for (testName of testNames) {
|
||||
let test = json[testName];
|
||||
|
||||
let path1 = fromCmds(test.p1);
|
||||
let path1 = PathKit.FromCmds(test.p1);
|
||||
expect(path1).not.toBeNull(`path1 error when loading cmds '${test.p1}'`);
|
||||
path1.setFillType(getFillType(test.fillType1));
|
||||
|
||||
let path2 = fromCmds(test.p2);
|
||||
let path2 = PathKit.FromCmds(test.p2);
|
||||
expect(path2).not.toBeNull(`path2 error when loading cmds '${test.p2}'`);
|
||||
path2.setFillType(getFillType(test.fillType2));
|
||||
|
||||
@ -149,7 +144,7 @@ describe('PathKit\'s PathOps Behavior', function() {
|
||||
expect(combined).toBeNull(`Test ${testName} should have not created output, but did`);
|
||||
} else {
|
||||
expect(combined).not.toBeNull();
|
||||
let expected = fromCmds(test.out);
|
||||
let expected = PathKit.FromCmds(test.out);
|
||||
// Do a tolerant match.
|
||||
let diff = diffPaths(expected, combined);
|
||||
if (test.expectMatch === 'yes'){
|
||||
@ -192,7 +187,7 @@ describe('PathKit\'s PathOps Behavior', function() {
|
||||
for (testName of testNames) {
|
||||
let test = json[testName];
|
||||
|
||||
let path = fromCmds(test.path);
|
||||
let path = PathKit.FromCmds(test.path);
|
||||
expect(path).not.toBeNull(`path1 error when loading cmds '${test.path}'`);
|
||||
path.setFillType(getFillType(test.fillType));
|
||||
|
||||
@ -202,7 +197,7 @@ describe('PathKit\'s PathOps Behavior', function() {
|
||||
expect(simplified).toBeNull(`Test ${testName} should have not created output, but did`);
|
||||
} else {
|
||||
expect(simplified).not.toBeNull();
|
||||
let expected = fromCmds(test.out);
|
||||
let expected = PathKit.FromCmds(test.out);
|
||||
// Do a tolerant match.
|
||||
let diff = diffPaths(expected, simplified);
|
||||
if (test.expectMatch === 'yes'){
|
||||
|
@ -45,8 +45,7 @@ describe('PathKit\'s SVG Behavior', function() {
|
||||
[PathKit.LINE_VERB, 5, 295],
|
||||
[PathKit.LINE_VERB, 205, 5],
|
||||
[PathKit.CLOSE_VERB]];
|
||||
let [ptr, len] = PathKit.loadCmdsTypedArray(cmds);
|
||||
let path = PathKit.FromCmds(ptr, len);
|
||||
let path = PathKit.FromCmds(cmds);
|
||||
|
||||
let svgStr = path.toSVGString();
|
||||
// We output it in terse form, which is different than Wikipedia's version
|
||||
@ -64,8 +63,7 @@ describe('PathKit\'s SVG Behavior', function() {
|
||||
[PathKit.LINE_VERB, 5, 295],
|
||||
[PathKit.LINE_VERB, "0x15e80300", "0x400004dc"], // 9.37088e-26f, 2.0003f
|
||||
[PathKit.CLOSE_VERB]];
|
||||
let [ptr, len] = PathKit.loadCmdsTypedArray(cmds);
|
||||
let path = PathKit.FromCmds(ptr, len);
|
||||
let path = PathKit.FromCmds(cmds);
|
||||
|
||||
let svgStr = path.toSVGString();
|
||||
expect(svgStr).toEqual('M9.37088e-26 2.0003L795 5L595 295L5 295L9.37088e-26 2.0003Z');
|
||||
|
@ -7,23 +7,774 @@ available to JS clients (e.g. Web Browsers) using WebAssembly and asm.js.
|
||||
Download the library
|
||||
--------------------
|
||||
|
||||
See [the npm page](https://www.npmjs.com/package/experimental-pathkit-wasm) for details on downloading
|
||||
and getting started.
|
||||
See the the npm page for either the [WebAssembly](https://www.npmjs.com/package/experimental-pathkit-wasm) version
|
||||
or the [asm.js](https://www.npmjs.com/package/experimental-pathkit-asmjs) version
|
||||
for details on downloading and getting started.
|
||||
|
||||
WebAssembly has faster load times and better overall performance but is
|
||||
currently supported by only Chrome and Firefox (with a flag).
|
||||
The asm.js version should run anywhere JavaScript does.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
PathKit is still under rapid development, so the exact API is still changing.
|
||||
PathKit is still under rapid development, so the exact API is subject to change.
|
||||
|
||||
The primary features are:
|
||||
|
||||
- API compatibility (e.g. drop-in replacement) with [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D)
|
||||
- Can output to SVG / Canvas / Path2D
|
||||
- Exposes a variety of path effects: <img width=800 src="./PathKit_effects.png"/>
|
||||
- Exposes a variety of path effects:
|
||||
|
||||
<style>
|
||||
canvas.patheffect {
|
||||
border: 1px dashed #AAA;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id=effects>
|
||||
<canvas class=patheffect id=canvas1 title="Plain: A drawn star with overlapping solid lines"></canvas>
|
||||
<canvas class=patheffect id=canvas2 title="Dash: A drawn star with overlapping dashed lines"></canvas>
|
||||
<canvas class=patheffect id=canvas3 title="Trim: A portion of a drawn star with overlapping solid lines"></canvas>
|
||||
<canvas class=patheffect id=canvas4 title="Simplify: A drawn star with non-overlapping solid lines."></canvas>
|
||||
<canvas class=patheffect id=canvas5 title="Stroke: A drawn star with non-overlapping solid lines stroked at various thicknesses and with square edges"></canvas>
|
||||
<canvas class=patheffect id=canvas6 title="Grow: A drawn star's expanding outline"></canvas>
|
||||
<canvas class=patheffect id=canvas7 title="Shrink: A solid drawn star shrunk down"></canvas>
|
||||
<canvas class=patheffect id=canvasTransform title="Transform: A drawn star moved and rotated by an Affine Matrix"></canvas>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/experimental-pathkit-asmjs@0.3.0/bin/pathkit.js"></script>
|
||||
<script>
|
||||
try {
|
||||
PathKitInit({
|
||||
locateFile: (file) => 'https://unpkg.com/experimental-pathkit-asmjs@0.3.0/bin/'+file,
|
||||
}).then((PathKit) => {
|
||||
// Code goes here using PathKit
|
||||
PathEffectsExample(PathKit);
|
||||
MatrixTransformExample(PathKit);
|
||||
});
|
||||
|
||||
}
|
||||
catch(error) {
|
||||
console.warn(error, 'falling back to image');
|
||||
docment.getElementById('effects').innerHTML = '<img width=800 src="./PathKit_effects.png"/>'
|
||||
}
|
||||
|
||||
function setCanvasSize(ctx, width, height) {
|
||||
ctx.canvas.width = width;
|
||||
ctx.canvas.height = height;
|
||||
}
|
||||
|
||||
function drawStar(path) {
|
||||
let R = 115.2, C = 128.0;
|
||||
path.moveTo(C + R + 22, C);
|
||||
for (let i = 1; i < 8; i++) {
|
||||
let a = 2.6927937 * i;
|
||||
path.lineTo(C + R * Math.cos(a) + 22, C + R * Math.sin(a));
|
||||
}
|
||||
path.closePath();
|
||||
return path;
|
||||
}
|
||||
|
||||
function PathEffectsExample(PathKit) {
|
||||
let effects = [
|
||||
// no-op
|
||||
(path) => path,
|
||||
// dash
|
||||
(path, counter) => path.dash(10, 3, counter/5),
|
||||
// trim (takes optional 3rd param for returning the trimmed part
|
||||
// or the complement)
|
||||
(path, counter) => path.trim((counter/100) % 1, 0.8, false),
|
||||
// simplify
|
||||
(path) => path.simplify(),
|
||||
// stroke
|
||||
(path, counter) => path.stroke({
|
||||
width: 10 * (Math.sin(counter/30) + 1),
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
miter_limit: 1,
|
||||
}),
|
||||
// "offset effect", that is, making a border around the shape.
|
||||
(path, counter) => {
|
||||
let orig = path.copy();
|
||||
path.stroke({
|
||||
width: 10 + (counter / 4) % 50,
|
||||
join: PathKit.StrokeJoin.ROUND,
|
||||
cap: PathKit.StrokeCap.SQUARE,
|
||||
})
|
||||
.op(orig, PathKit.PathOp.DIFFERENCE);
|
||||
orig.delete();
|
||||
},
|
||||
(path, counter) => {
|
||||
let simplified = path.simplify().copy();
|
||||
path.stroke({
|
||||
width: 2 + (counter / 2) % 100,
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
})
|
||||
.op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
|
||||
simplified.delete();
|
||||
}
|
||||
];
|
||||
|
||||
let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
|
||||
|
||||
let counter = 0;
|
||||
function frame() {
|
||||
counter++;
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
let path = PathKit.NewPath();
|
||||
drawStar(path);
|
||||
|
||||
// The transforms apply directly to the path.
|
||||
effects[i](path, counter);
|
||||
|
||||
let ctx = document.getElementById(`canvas${i+1}`).getContext('2d');
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
ctx.fillStyle = '#3c597a';
|
||||
if (i >=4 ) {
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
} else {
|
||||
ctx.stroke(path.toPath2D());
|
||||
}
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
|
||||
let x = 150-ctx.measureText(names[i]).width/2;
|
||||
ctx.strokeText(names[i], x, 290);
|
||||
|
||||
path.delete();
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function MatrixTransformExample(PathKit) {
|
||||
// Creates an animated star that twists and moves.
|
||||
let ctx = document.getElementById('canvasTransform').getContext('2d');
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
|
||||
let path = drawStar(PathKit.NewPath());
|
||||
// TODO(kjlubick): Perhaps expose some matrix helper functions to allow
|
||||
// clients to build their own matrices like this?
|
||||
// These matrices represent a 2 degree rotation and a 1% scale factor.
|
||||
let scaleUp = [1.0094, -0.0352, 3.1041,
|
||||
0.0352, 1.0094, -6.4885,
|
||||
0 , 0 , 1];
|
||||
|
||||
let scaleDown = [ 0.9895, 0.0346, -2.8473,
|
||||
-0.0346, 0.9895, 6.5276,
|
||||
0 , 0 , 1];
|
||||
|
||||
let i = 0;
|
||||
function frame(){
|
||||
i++;
|
||||
if (Math.round(i/100) % 2) {
|
||||
path.transform(scaleDown);
|
||||
} else {
|
||||
path.transform(scaleUp);
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, 300, 300);
|
||||
ctx.stroke(path.toPath2D());
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
let x = 150-ctx.measureText('Transform').width/2;
|
||||
ctx.strokeText('Transform', x, 290);
|
||||
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Example code
|
||||
Example Code
|
||||
------------
|
||||
The best place to look for examples on how to use PathKit would be in the
|
||||
[example.html](https://github.com/google/skia/blob/master/experimental/pathkit/npm-wasm/example.html#L45)
|
||||
which comes in the npm package.
|
||||
[example.html](https://github.com/google/skia/blob/master/experimental/pathkit/npm-wasm/example.html#L45),
|
||||
which comes in the npm package.
|
||||
|
||||
|
||||
API
|
||||
----
|
||||
|
||||
The primary feature of the library is the `SkPath` object. It can be created:
|
||||
|
||||
- From the SVG string of a path `PathKit.FromSVGString(str)`
|
||||
- From a 2D array of verbs and arguments `PathKit.FromCmds(cmds)`
|
||||
- From `PathKit.NewPath()` (It will be blank)
|
||||
- As a copy of an existing `SkPath` with `path.copy()` or `PathKit.NewPath(path)`
|
||||
|
||||
It can be exported as:
|
||||
|
||||
- An SVG string `path.toSVGString()`
|
||||
- A [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) object `path.toPath2D()`
|
||||
- Directly to a canvas 2D context `path.toCanvas(ctx)`
|
||||
- A 2D array of verbs and arguments `path.toCmds()`
|
||||
|
||||
Once an SkPath object has been made, it can be interacted with in the following ways:
|
||||
|
||||
- expanded by any of the Path2D operations (`moveTo`, `lineTo`, `rect`, `arc`, etc)
|
||||
- combined with other paths using `op` or `PathKit.MakeFromOp(p1, p2, op)`. For example, `path1.op(path2, PathKit.PathOp.INTERSECT)` will set path1 to be the area represented by where path1 and path2 overlap (intersect). `PathKit.MakeFromOp(path1, path2, PathKit.PathOp.INTERSECT)` will do the same but returned as a new `SkPath` object.
|
||||
- adjusted with some of the effects (`trim`, `dash`, `stroke`, etc)
|
||||
|
||||
|
||||
**Important**: Any objects (`SkPath`, `SkOpBuilder`, etc) that are created must be cleaned up with `path.delete()` when they
|
||||
leave the scope to avoid leaking the memory in the WASM heap. This includes any of the constructors, `copy()`,
|
||||
or any function prefixed with "make".
|
||||
|
||||
|
||||
### PathKit ###
|
||||
|
||||
#### `FromSVGString(str)` ####
|
||||
**str** - `String` representing an [SVGPath](https://www.w3schools.com/graphics/svg_path.asp)
|
||||
|
||||
Returns an `SkPath` with the same verbs and arguments as the SVG string, or `null` on a failure.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.FromSVGString('M150 0 L75 200 L225 200 Z');
|
||||
// path represents a triangle
|
||||
// don't forget to do path.delete() when it goes out of scope.
|
||||
|
||||
#### `FromCmds(cmds)` ####
|
||||
**cmds** - `Array<Array<Number>>`, a 2D array of commands, where a command is a verb
|
||||
followed by its arguments.
|
||||
|
||||
Returns an `SkPath` with the verbs and arguments from the list or `null` on a failure.
|
||||
|
||||
This can be faster than calling `.moveTo()`, `.lineTo()`, etc many times.
|
||||
|
||||
Example:
|
||||
|
||||
let cmds = [
|
||||
[PathKit.MOVE_VERB, 0, 10],
|
||||
[PathKit.LINE_VERB, 30, 40],
|
||||
[PathKit.QUAD_VERB, 20, 50, 45, 60],
|
||||
];
|
||||
let path = PathKit.FromCmds(cmds);
|
||||
// path is the same as if a user had done
|
||||
// let path = PathKit.NewPath().moveTo(0, 10).lineTo(30, 40).quadTo(20, 50, 45, 60);
|
||||
// don't forget to do path.delete() when it goes out of scope.
|
||||
|
||||
#### `NewPath()` ####
|
||||
|
||||
Returns an empty `SkPath` object.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.NewPath();
|
||||
path.moveTo(0, 10)
|
||||
.lineTo(30, 40)
|
||||
.quadTo(20, 50, 45, 60);
|
||||
// don't forget to do path.delete() when it goes out of scope.
|
||||
// Users can also do let path = new PathKit.SkPath();
|
||||
|
||||
#### `NewPath(pathToCopy)` ####
|
||||
**pathToCopy** - SkPath, a path to make a copy of.
|
||||
|
||||
Returns a `SkPath` that is a copy of the passed in `SkPath`.
|
||||
|
||||
Example:
|
||||
|
||||
let otherPath = ...;
|
||||
let clone = PathKit.NewPath(otherPath);
|
||||
clone.simplify();
|
||||
// don't forget to do clone.delete() when it goes out of scope.
|
||||
// Users can also do let clone = new PathKit.SkPath(otherPath);
|
||||
// or let clone = otherPath.copy();
|
||||
|
||||
#### `MakeFromOp(pathOne, pathTwo, op)` ####
|
||||
**pathOne** - `SkPath`, a path. <br>
|
||||
**pathTwo** - `SkPath`, a path. <br>
|
||||
**op** - `PathOp`, an op to apply
|
||||
|
||||
Returns a new `SkPath` that is the result of applying the given PathOp to the first and second
|
||||
path (order matters).
|
||||
|
||||
Example:
|
||||
|
||||
let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
|
||||
let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
|
||||
let mountains = PathKit.MakeFromOp(pathOne, pathTwo, PathKit.PathOp.UNION);
|
||||
// don't forget to do mountains.delete() when it goes out of scope.
|
||||
// Users can also do pathOne.op(pathTwo, PathKit.PathOp.UNION);
|
||||
// to have the resulting path be stored to pathOne and avoid allocating another object.
|
||||
|
||||
### SkPath (object) ###
|
||||
|
||||
#### `addPath(otherPath)` ####
|
||||
**otherPath** - `SkPath`, a path to append to this path
|
||||
|
||||
Adds the given path to `this` and then returns `this` for chaining purposes.
|
||||
|
||||
#### `addPath(otherPath, transform)` ####
|
||||
**otherPath** - `SkPath`, a path to append to this path. <br>
|
||||
**transform** - [SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix),
|
||||
a transform to apply to otherPath before appending it.
|
||||
|
||||
Adds the given path to `this` after applying the transform and then returns `this` for
|
||||
chaining purposes. See [Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
|
||||
for more details.
|
||||
|
||||
#### `addPath(otherPath, a, b, c, d, e, f)` ####
|
||||
**otherPath** - `SkPath`, a path to append to this path. <br>
|
||||
**a, b, c, d, e, f** - `Number`, the six components of an
|
||||
[SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix),
|
||||
which define the transform to apply to otherPath before appending it.
|
||||
|
||||
Adds the given path to `this` after applying the transform and then returns `this` for
|
||||
chaining purposes. See [Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
|
||||
for more details.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let moreBoxes = PathKit.NewPath();
|
||||
// add box un-transformed (i.e. at 0, 0)
|
||||
moreBoxes.addPath(box)
|
||||
// the params fill out a 2d matrix like:
|
||||
// a c e
|
||||
// b d f
|
||||
// 0 0 1
|
||||
// add box 300 points to the right
|
||||
.addPath(box, 1, 0, 0, 1, 300, 0)
|
||||
// add a box shrunk by 50% in both directions
|
||||
.addPath(box, 0.5, 0, 0, 0.5, 0, 0);
|
||||
// moreBoxes now has 3 paths appended to it
|
||||
|
||||
#### `addPath(otherPath, scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)` ####
|
||||
**otherPath** - `SkPath`, a path to append to this path. <br>
|
||||
**scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2** -
|
||||
`Number`, the nine components of an
|
||||
[Affine Matrix](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations),
|
||||
which define the transform to apply to otherPath before appending it.
|
||||
|
||||
Adds the given path to `this` after applying the transform and then returns `this` for
|
||||
chaining purposes.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let moreBoxes = PathKit.NewPath();
|
||||
// add box un-transformed (i.e. at 0, 0)
|
||||
moreBoxes.addPath(box)
|
||||
// add box 300 points to the right
|
||||
.addPath(box, 1, 0, 0,
|
||||
0, 1, 300,
|
||||
0, 0 ,1)
|
||||
// add a box shrunk by 50% in both directions
|
||||
.addPath(box, 0.5, 0, 0,
|
||||
0, 0.5, 0,
|
||||
0, 0, 1)
|
||||
// moreBoxes now has 3 paths appended to it
|
||||
|
||||
#### `arc(x, y, radius, startAngle, endAngle, ccw=false)` ####
|
||||
**x, y** - `Number`, The coordinates of the arc's center. <br>
|
||||
**radius** - `Number`, The radius of the arc. <br>
|
||||
**startAngle, endAngle** - `Number`, the start and end of the angle, measured
|
||||
clockwise from the positive x axis and in radians. <br>
|
||||
**ccw** - `Boolean`, optional argument specifying if the arc should be drawn
|
||||
counter-clockwise between **startAngle** and **endAngle** instead of
|
||||
clockwise, the default.
|
||||
|
||||
Adds the described arc to `this` then returns `this` for
|
||||
chaining purposes. See [Path2D.arc()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc)
|
||||
for more details.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.NewPath();
|
||||
path.moveTo(20, 120);
|
||||
.arc(20, 120, 18, 0, 1.75 * Math.PI);
|
||||
.lineTo(20, 120);
|
||||
// path looks like a pie with a 1/8th slice removed.
|
||||
|
||||
#### `arcTo(x1, y1, x2, y2, radius)` ####
|
||||
**x1, y1, x2, y2** - `Number`, The coordinates defining the control points. <br>
|
||||
**radius** - `Number`, The radius of the arc.
|
||||
|
||||
Adds the described arc to `this` (appending a line, if needed) then returns `this` for
|
||||
chaining purposes. See [Path2D.arcTo()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo)
|
||||
for more details.
|
||||
|
||||
#### `close()` or `closePath()` ####
|
||||
Returns the pen to the start of the current sub-path, then returns `this` for
|
||||
chaining purposes. See [Path2D.closePath()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/closePath)
|
||||
for more details.
|
||||
|
||||
#### `computeTightBounds()` ####
|
||||
|
||||
Returns an `SkRect` that represents the minimum and maximum area of
|
||||
`this` path. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_computeTightBounds)
|
||||
for more details.
|
||||
|
||||
#### `conicTo(x1, y1, x2, y2, w)` ####
|
||||
**x1, y1, x2, y2** - `Number`, The coordinates defining the control point and the end point. <br>
|
||||
**w** - `Number`, The weight of the conic.
|
||||
|
||||
Adds the described conic line to `this` (appending a line, if needed) then returns `this` for
|
||||
chaining purposes. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_conicTo)
|
||||
for more details.
|
||||
|
||||
#### `copy()` ####
|
||||
|
||||
Return a copy of `this` path.
|
||||
|
||||
#### `cubicTo(cp1x, cp1y, cp2x, cp2y, x, y)` or `bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)` ####
|
||||
**cp1x, cp1y, cp2x, cp2y** - `Number`, The coordinates defining the control points. <br>
|
||||
**x,y** - `Number`, The coordinates defining the end point
|
||||
|
||||
Adds the described cubic line to `this` (appending a line, if needed) then returns `this` for
|
||||
chaining purposes. See [Path2D.bezierCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo)
|
||||
for more details.
|
||||
|
||||
#### `dash(on, off, phase)` ####
|
||||
**on, off** - `Number`, The number of pixels the dash should be on (drawn) and off (blank). <br>
|
||||
**phase** - `Number`, The number of pixels the on/off should be offset (mod **on** + **off**)
|
||||
|
||||
Applies a dashed path effect to `this` then returns `this` for chaining purposes.
|
||||
See the "Dash" effect above for a visual example.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
box.dash(20, 10, 3);
|
||||
// box is now a dashed rectangle that will draw for 20 pixels, then
|
||||
// stop for 10 pixels. Since phase is 3, the first line won't start
|
||||
// at (0, 0), but 3 pixels around the path (3, 0)
|
||||
|
||||
#### `ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw=false)` ####
|
||||
**x, y** - `Number`, The coordinates of the center of the ellipse. <br>
|
||||
**radiusX, radiusY** - `Number`, The radii in the X and Y directions. <br>
|
||||
**rotation** - `Number`, The rotation in radians of this ellipse. <br>
|
||||
**startAngle, endAngle** - `Number`, the starting and ending angles of which to draw,
|
||||
measured in radians from the positive x axis. <br>
|
||||
**ccw** - `Boolean`, optional argument specifying if the ellipse should be drawn
|
||||
counter-clockwise between **startAngle** and **endAngle** instead of
|
||||
clockwise, the default.
|
||||
|
||||
Adds the described ellipse to `this` then returns `this` for chaining purposes.
|
||||
See [Path2D.ellipse](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/ellipse)
|
||||
for more details.
|
||||
|
||||
#### `equals(otherPath)` ####
|
||||
**otherPath** - `SkPath`, the path to compare to.
|
||||
|
||||
Returns a `Boolean` value based on if `this` path is equal
|
||||
to **otherPath**.
|
||||
|
||||
#### `getBounds()` ####
|
||||
|
||||
Returns an `SkRect` that represents the minimum and maximum area of
|
||||
`this` path. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_getBounds)
|
||||
for more details.
|
||||
|
||||
#### `getFillType()` ####
|
||||
|
||||
Returns a `FillType` based on what this path is. This defaults to
|
||||
`PathKit.FillType.WINDING`, but may change with `op()` or `simplify()`.
|
||||
|
||||
Clients will typically want `getFillTypeString()` because that value
|
||||
can be passed directly to an SVG or Canvas.
|
||||
|
||||
#### `getFillTypeString()` ####
|
||||
|
||||
Returns a `String` representing the fillType of `this` path.
|
||||
The values are either "nonzero" or "evenodd".
|
||||
|
||||
Example:
|
||||
|
||||
let path = ...;
|
||||
let ctx = document.getElementById('canvas1').getContext('2d');
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
|
||||
#### `moveTo(x, y)` ####
|
||||
**x, y** - `Number`, The coordinates of where the pen should be moved to.
|
||||
|
||||
Moves the pen (without drawing) to the given coordinates then returns `this` for chaining purposes.
|
||||
See [Path2D.moveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo)
|
||||
for more details.
|
||||
|
||||
#### `lineTo(x, y)` ####
|
||||
**x, y** - `Number`, The coordinates of where the pen should be moved to.
|
||||
|
||||
Draws a straight line to the given coordinates then returns `this` for chaining purposes.
|
||||
See [Path2D.lineTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo)
|
||||
for more details.
|
||||
|
||||
#### `op(otherPath, operation)` ####
|
||||
**otherPath** - `SkPath`, The other path to be combined with `this`. <br>
|
||||
**operation** - `PathOp`, The operation to apply to the two paths.
|
||||
|
||||
Combines otherPath into `this` path with the given operation and returns `this`
|
||||
for chaining purposes.
|
||||
|
||||
Example:
|
||||
|
||||
let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
|
||||
let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
|
||||
// Combine the two triangles to look like two mountains
|
||||
let mountains = pathOne.copy().op(pathOne, pathTwo, PathKit.PathOp.UNION);
|
||||
// set pathOne to be the small triangle where pathOne and pathTwo overlap
|
||||
pathOne.op(pathOne, pathTwo, PathKit.PathOp.INTERSECT);
|
||||
// since copy() was called, don't forget to call delete() on mountains.
|
||||
|
||||
#### `quadTo(cpx, cpy, x, y)` or `quadraticCurveTo(cpx, cpy, x, y)` ####
|
||||
**cpx, cpy** - `Number`, The coordinates for the control point. <br>
|
||||
**x, y** - `Number`, The coordinates for the end point.
|
||||
|
||||
Draws a quadratic Bézier curve with the given coordinates then returns `this` for chaining purposes.
|
||||
See [Path2D.quadraticCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/quadraticCurveTo)
|
||||
for more details.
|
||||
|
||||
#### `rect(x, y, w, h)` ####
|
||||
**x, y** - `Number`, The coordinates of the upper-left corner of the rectangle. <br>
|
||||
**w, h** - `Number`, The width and height of the rectangle
|
||||
|
||||
Draws a rectangle on `this`, then returns `this` for chaining purposes.
|
||||
See [Path2D.rect](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rect)
|
||||
for more details.
|
||||
|
||||
#### `setFillType(fillType)` ####
|
||||
**fillType** - `FillType`, the new fillType.
|
||||
|
||||
Set the fillType of the path. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_FillType)
|
||||
for more details.
|
||||
|
||||
#### `simplify()` ####
|
||||
Set `this` path to a set of *non-overlapping* contours that describe the same area
|
||||
as the original path. See the "Simplify" effect above for a visual example.
|
||||
|
||||
#### `stroke(opts)` ####
|
||||
**opts** - `StrokeOpts`, contains the options for stroking.
|
||||
|
||||
|
||||
Strokes `this` path out with the given options. This can be used for a variety of
|
||||
effects. See the "Stroke", "Grow", and "Shrink" effects above for visual examples.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
// Stroke the path with width 10 and rounded corners
|
||||
let rounded = box.copy().stroke({width: 10, join: PathKit.StrokeJoin.ROUND});
|
||||
// Grow effect, that is, a 20 pixel expansion around the box.
|
||||
let grow = box.copy().stroke({width: 20}).op(box, PathKit.PathOp.DIFFERENCE);
|
||||
// Shrink effect, in which we subtract away from the original
|
||||
let simplified = box.copy().simplify(); // sometimes required for complicated paths
|
||||
let shrink = box.copy().stroke({width: 15, cap: PathKit.StrokeCap.BUTT})
|
||||
.op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
|
||||
// Don't forget to call delete() on each of the copies!
|
||||
|
||||
#### `toCanvas(ctx)` ####
|
||||
**ctx** - `Canvas2DContext`, Canvas on which to draw the path.
|
||||
|
||||
Draws `this` path on the passed in
|
||||
[Canvas Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D).
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let ctx = document.getElementById('canvas1').getContext('2d');
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.beginPath();
|
||||
box.toCanvas(ctx);
|
||||
ctx.stroke(); // could also ctx.fill()
|
||||
|
||||
#### `toCmds()` ####
|
||||
|
||||
Returns a 2D Array of verbs and args. See `PathKit.FromCmds()` for
|
||||
more details.
|
||||
|
||||
#### `toPath2D()` ####
|
||||
|
||||
Returns a [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) object
|
||||
that has the same operations as `this` path.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let ctx = document.getElementById('canvas1').getContext('2d');
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.stroke(box.toPath2D());
|
||||
|
||||
#### `toSVGString()` ####
|
||||
|
||||
Returns a `String` representing an [SVGPath](https://www.w3schools.com/graphics/svg_path.asp) based on `this` path.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let svg = document.getElementById('svg1');
|
||||
let newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
newPath.setAttribute('stroke', 'green');
|
||||
newPath.setAttribute('fill', 'white');
|
||||
newPath.setAttribute('d', box.toSVGString());
|
||||
svg.appendChild(newPath);
|
||||
|
||||
#### `transform(matr)` ####
|
||||
**matr** - `SkMatrix`, i.e. an `Array<Number>` of the nine numbers of an Affine Transform Matrix.
|
||||
|
||||
Applies the specified [transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
|
||||
to `this` and then returns `this` for chaining purposes.
|
||||
|
||||
#### `transform(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)` ####
|
||||
**scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2** -
|
||||
`Number`, the nine numbers of an Affine Transform Matrix.
|
||||
|
||||
Applies the specified [transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
|
||||
to `this` and then returns `this` for chaining purposes.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
// scale up the path by 5x
|
||||
path.transform([5, 0, 0,
|
||||
0, 5, 0,
|
||||
0, 0, 1]);
|
||||
// move the path 75 px to the right.
|
||||
path.transform(1, 0, 75,
|
||||
0, 1, 0,
|
||||
0, 0, 1);
|
||||
|
||||
#### `trim(startT, stopT, isComplement=false)` ####
|
||||
**startT, stopT** - `Number`, values in [0, 1] that indicate the start and stop
|
||||
"percentages" of the path to draw <br>
|
||||
**isComplement** - `Boolean`, If the complement of the trimmed section should
|
||||
be drawn instead of the areas between **startT** and **stopT**.
|
||||
|
||||
Sets `this` path to be a subset of the original path, then returns `this` for chaining purposes.
|
||||
See the "Trim" effect above for a visual example.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
box.trim(0.25, 1.0);
|
||||
// box is now the 3 segments that look like a U
|
||||
// (the top segment has been removed).
|
||||
|
||||
|
||||
### SkOpBuilder (object) ###
|
||||
This object enables chaining multiple PathOps together.
|
||||
Create one with `let builder = new PathKit.SkOpBuilder();`
|
||||
When created, the internal state is "empty path".
|
||||
Don't forget to call `delete()` on both the builder and the result
|
||||
of `resolve()`
|
||||
|
||||
#### `add(path, operation)` ####
|
||||
**path** - `SkPath`, The path to be combined with the given rule. <br>
|
||||
**operation** - `PathOp`, The operation to apply to the two paths.
|
||||
|
||||
Adds a path and the operand to the builder.
|
||||
|
||||
#### `make()` or `resolve()` ####
|
||||
|
||||
Creates and returns a new `SkPath` based on all the given paths
|
||||
and operands.
|
||||
|
||||
Don't forget to call `.delete()` on the returned path when it goes out of scope.
|
||||
|
||||
|
||||
### SkMatrix (struct) ###
|
||||
`SkMatrix` translates between a C++ struct and a JS Array.
|
||||
It basically takes a nine element 1D Array and turns it into a
|
||||
3x3 2D Affine Matrix.
|
||||
|
||||
### SkRect (struct) ###
|
||||
|
||||
`SkRect` translates between a C++ struct and a JS Object with
|
||||
the following keys (all values are `Number`:
|
||||
|
||||
- **fLeft**: x coordinate of top-left corner
|
||||
- **fTop**: y coordinate of top-left corner
|
||||
- **fRight**: x coordinate of bottom-right corner
|
||||
- **fBottom**: y coordinate of bottom-rightcorner
|
||||
|
||||
### StrokeOpts (struct) ###
|
||||
`StrokeOpts` translates between a C++ struct and a JS Object with
|
||||
the following keys:
|
||||
|
||||
- **width**, `Number` the width of the lines of the path. Default 1.
|
||||
- **miter_limit**, `Number`, the miter limit. Defautl 4. See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Miter_Limit) for more details.
|
||||
- **join**, `StrokeJoin`, the join to use. Default `PathKit.StrokeJoin.MITER`.
|
||||
See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#SkPaint_Join) for more details.
|
||||
- **cap**, `StrokeCap`, the cap to use. Default `PathKit.StrokeCap.BUTT`.
|
||||
See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap) for more details.
|
||||
|
||||
### PathOp (enum) ###
|
||||
The following enum values are exposed. They are essentially constant
|
||||
objects, differentiated by thier `.value` property.
|
||||
|
||||
- `PathKit.PathOp.DIFFERENCE`
|
||||
- `PathKit.PathOp.INTERSECT`
|
||||
- `PathKit.PathOp.REVERSE_DIFFERENCE`
|
||||
- `PathKit.PathOp.UNION`
|
||||
- `PathKit.PathOp.XOR`
|
||||
|
||||
These are used in `PathKit.MakeFromOp()` and `SkPath.op()`.
|
||||
|
||||
### FillType (enum) ###
|
||||
The following enum values are exposed. They are essentially constant
|
||||
objects, differentiated by thier `.value` property.
|
||||
|
||||
- `PathKit.FillType.WINDING` (also known as nonzero)
|
||||
- `PathKit.FillType.EVENODD`
|
||||
- `PathKit.FillType.INVERSE_WINDING`
|
||||
- `PathKit.FillType.INVERSE_EVENODD`
|
||||
|
||||
These are used by `SkPath.getFillType()` and `SkPath.setFillType()`, but
|
||||
generally clients will want `SkPath.getFillTypeString()`.
|
||||
|
||||
### StrokeJoin (enum) ###
|
||||
The following enum values are exposed. They are essentially constant
|
||||
objects, differentiated by thier `.value` property.
|
||||
|
||||
- `PathKit.StrokeJoin.MITER`
|
||||
- `PathKit.StrokeJoin.ROUND`
|
||||
- `PathKit.StrokeJoin.BEVEL`
|
||||
|
||||
See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#SkPaint_Join) for more details.
|
||||
|
||||
### StrokeCap (enum) ###
|
||||
The following enum values are exposed. They are essentially constant
|
||||
objects, differentiated by thier `.value` property.
|
||||
|
||||
- `PathKit.StrokeCap.BUTT`
|
||||
- `PathKit.StrokeCap.ROUND`
|
||||
- `PathKit.StrokeCap.SQUARE`
|
||||
|
||||
See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap) for more details.
|
||||
|
||||
### Constants ###
|
||||
The following constants are exposed:
|
||||
|
||||
- `PathKit.MOVE_VERB` = 0
|
||||
- `PathKit.LINE_VERB` = 1
|
||||
- `PathKit.QUAD_VERB` = 2
|
||||
- `PathKit.CONIC_VERB` = 3
|
||||
- `PathKit.CUBIC_VERB` = 4
|
||||
- `PathKit.CLOSE_VERB` = 5
|
||||
|
||||
These are only needed for `PathKit.FromCmds()`.
|
||||
|
||||
### Functions for testing only ###
|
||||
|
||||
#### `PathKit.LTRBRect(left, top, right, bottom)` ####
|
||||
**left** - `Number`, x coordinate of top-left corner of the `SkRect`. <br>
|
||||
**top** - `Number`, y coordinate of top-left corner of the `SkRect`. <br>
|
||||
**right** - `Number`, x coordinate of bottom-right corner of the `SkRect`. <br>
|
||||
**bottom** - `Number`, y coordinate of bottom-right corner of the `SkRect`.
|
||||
|
||||
Returns an `SkRect` object with the given params.
|
||||
|
||||
#### `SkPath.dump()` ####
|
||||
|
||||
Prints all the verbs and arguments to the console.
|
||||
Only available on Debug and Test builds.
|
||||
|
Loading…
Reference in New Issue
Block a user