From 2559d948d36b70b34d04c4d7ece70e8c38ca112a Mon Sep 17 00:00:00 2001 From: poni Date: Fri, 20 Nov 2020 10:43:40 +0100 Subject: [PATCH] trying mapping positions --- node_modules/path-data-parser/LICENSE | 21 + node_modules/path-data-parser/README.md | 72 +++ .../path-data-parser/lib/absolutize.d.ts | 2 + .../path-data-parser/lib/absolutize.js | 110 +++++ node_modules/path-data-parser/lib/index.d.ts | 3 + node_modules/path-data-parser/lib/index.js | 3 + .../path-data-parser/lib/normalize.d.ts | 2 + .../path-data-parser/lib/normalize.js | 219 +++++++++ node_modules/path-data-parser/lib/parser.d.ts | 6 + node_modules/path-data-parser/lib/parser.js | 107 ++++ node_modules/path-data-parser/package.json | 63 +++ .../path-data-parser/src/absolutize.ts | 112 +++++ node_modules/path-data-parser/src/index.ts | 3 + .../path-data-parser/src/normalize.ts | 242 +++++++++ node_modules/path-data-parser/src/parser.ts | 113 +++++ node_modules/path-data-parser/tsconfig.json | 23 + node_modules/path-data-parser/tslint.json | 61 +++ node_modules/points-on-curve/LICENSE | 21 + node_modules/points-on-curve/README.md | 90 ++++ .../points-on-curve/lib/curve-to-bezier.d.ts | 2 + .../points-on-curve/lib/curve-to-bezier.js | 35 ++ node_modules/points-on-curve/lib/index.d.ts | 3 + node_modules/points-on-curve/lib/index.js | 123 +++++ node_modules/points-on-curve/package.json | 59 +++ .../points-on-curve/src/curve-to-bezier.ts | 42 ++ node_modules/points-on-curve/src/index.ts | 137 ++++++ node_modules/points-on-curve/tsconfig.json | 23 + node_modules/points-on-curve/tslint.json | 61 +++ node_modules/points-on-path/LICENSE | 21 + node_modules/points-on-path/README.md | 55 +++ node_modules/points-on-path/lib/index.d.ts | 3 + node_modules/points-on-path/lib/index.js | 61 +++ node_modules/points-on-path/package.json | 62 +++ node_modules/points-on-path/src/index.ts | 70 +++ node_modules/points-on-path/tsconfig.json | 23 + node_modules/points-on-path/tslint.json | 61 +++ node_modules/roughjs/CHANGELOG.md | 33 ++ node_modules/roughjs/LICENSE | 21 + node_modules/roughjs/README.md | 127 +++++ node_modules/roughjs/bin/canvas.d.ts | 23 + node_modules/roughjs/bin/canvas.js | 131 +++++ node_modules/roughjs/bin/core.d.ts | 82 ++++ node_modules/roughjs/bin/core.js | 1 + .../roughjs/bin/fillers/dashed-filler.d.ts | 9 + .../roughjs/bin/fillers/dashed-filler.js | 36 ++ .../roughjs/bin/fillers/dot-filler.d.ts | 9 + .../roughjs/bin/fillers/dot-filler.js | 41 ++ .../roughjs/bin/fillers/filler-interface.d.ts | 11 + .../roughjs/bin/fillers/filler-interface.js | 0 node_modules/roughjs/bin/fillers/filler.d.ts | 3 + node_modules/roughjs/bin/fillers/filler.js | 47 ++ .../roughjs/bin/fillers/hachure-filler.d.ts | 13 + .../roughjs/bin/fillers/hachure-filler.js | 100 ++++ .../roughjs/bin/fillers/hatch-filler.d.ts | 6 + .../roughjs/bin/fillers/hatch-filler.js | 10 + .../bin/fillers/scan-line-hachure.d.ts | 3 + .../roughjs/bin/fillers/scan-line-hachure.js | 114 +++++ .../roughjs/bin/fillers/zigzag-filler.d.ts | 6 + .../roughjs/bin/fillers/zigzag-filler.js | 6 + .../bin/fillers/zigzag-line-filler.d.ts | 9 + .../roughjs/bin/fillers/zigzag-line-filler.js | 38 ++ node_modules/roughjs/bin/generator.d.ts | 22 + node_modules/roughjs/bin/generator.js | 259 ++++++++++ node_modules/roughjs/bin/geometry.d.ts | 14 + node_modules/roughjs/bin/geometry.js | 100 ++++ node_modules/roughjs/bin/math.d.ts | 6 + node_modules/roughjs/bin/math.js | 16 + node_modules/roughjs/bin/renderer.d.ts | 28 ++ node_modules/roughjs/bin/renderer.js | 431 ++++++++++++++++ node_modules/roughjs/bin/rough.d.ts | 11 + node_modules/roughjs/bin/rough.js | 17 + node_modules/roughjs/bin/svg.d.ts | 22 + node_modules/roughjs/bin/svg.js | 115 +++++ node_modules/roughjs/bundled/rough.cjs.js | 15 + node_modules/roughjs/bundled/rough.esm.js | 1 + node_modules/roughjs/bundled/rough.js | 1 + node_modules/roughjs/package.json | 74 +++ node_modules/roughjs/tsconfig.json | 24 + package-lock.json | 35 ++ web/index.html | 7 +- web/p5.scribble.js | 458 ++++++++++++++++++ web/rough.js | 1 + web/script.js | 23 + web/style.css | 4 +- 84 files changed, 4673 insertions(+), 4 deletions(-) create mode 100644 node_modules/path-data-parser/LICENSE create mode 100644 node_modules/path-data-parser/README.md create mode 100644 node_modules/path-data-parser/lib/absolutize.d.ts create mode 100644 node_modules/path-data-parser/lib/absolutize.js create mode 100644 node_modules/path-data-parser/lib/index.d.ts create mode 100644 node_modules/path-data-parser/lib/index.js create mode 100644 node_modules/path-data-parser/lib/normalize.d.ts create mode 100644 node_modules/path-data-parser/lib/normalize.js create mode 100644 node_modules/path-data-parser/lib/parser.d.ts create mode 100644 node_modules/path-data-parser/lib/parser.js create mode 100644 node_modules/path-data-parser/package.json create mode 100644 node_modules/path-data-parser/src/absolutize.ts create mode 100644 node_modules/path-data-parser/src/index.ts create mode 100644 node_modules/path-data-parser/src/normalize.ts create mode 100644 node_modules/path-data-parser/src/parser.ts create mode 100644 node_modules/path-data-parser/tsconfig.json create mode 100644 node_modules/path-data-parser/tslint.json create mode 100644 node_modules/points-on-curve/LICENSE create mode 100644 node_modules/points-on-curve/README.md create mode 100644 node_modules/points-on-curve/lib/curve-to-bezier.d.ts create mode 100644 node_modules/points-on-curve/lib/curve-to-bezier.js create mode 100644 node_modules/points-on-curve/lib/index.d.ts create mode 100644 node_modules/points-on-curve/lib/index.js create mode 100644 node_modules/points-on-curve/package.json create mode 100644 node_modules/points-on-curve/src/curve-to-bezier.ts create mode 100644 node_modules/points-on-curve/src/index.ts create mode 100644 node_modules/points-on-curve/tsconfig.json create mode 100644 node_modules/points-on-curve/tslint.json create mode 100644 node_modules/points-on-path/LICENSE create mode 100644 node_modules/points-on-path/README.md create mode 100644 node_modules/points-on-path/lib/index.d.ts create mode 100644 node_modules/points-on-path/lib/index.js create mode 100644 node_modules/points-on-path/package.json create mode 100644 node_modules/points-on-path/src/index.ts create mode 100644 node_modules/points-on-path/tsconfig.json create mode 100644 node_modules/points-on-path/tslint.json create mode 100644 node_modules/roughjs/CHANGELOG.md create mode 100644 node_modules/roughjs/LICENSE create mode 100644 node_modules/roughjs/README.md create mode 100644 node_modules/roughjs/bin/canvas.d.ts create mode 100644 node_modules/roughjs/bin/canvas.js create mode 100644 node_modules/roughjs/bin/core.d.ts create mode 100644 node_modules/roughjs/bin/core.js create mode 100644 node_modules/roughjs/bin/fillers/dashed-filler.d.ts create mode 100644 node_modules/roughjs/bin/fillers/dashed-filler.js create mode 100644 node_modules/roughjs/bin/fillers/dot-filler.d.ts create mode 100644 node_modules/roughjs/bin/fillers/dot-filler.js create mode 100644 node_modules/roughjs/bin/fillers/filler-interface.d.ts create mode 100644 node_modules/roughjs/bin/fillers/filler-interface.js create mode 100644 node_modules/roughjs/bin/fillers/filler.d.ts create mode 100644 node_modules/roughjs/bin/fillers/filler.js create mode 100644 node_modules/roughjs/bin/fillers/hachure-filler.d.ts create mode 100644 node_modules/roughjs/bin/fillers/hachure-filler.js create mode 100644 node_modules/roughjs/bin/fillers/hatch-filler.d.ts create mode 100644 node_modules/roughjs/bin/fillers/hatch-filler.js create mode 100644 node_modules/roughjs/bin/fillers/scan-line-hachure.d.ts create mode 100644 node_modules/roughjs/bin/fillers/scan-line-hachure.js create mode 100644 node_modules/roughjs/bin/fillers/zigzag-filler.d.ts create mode 100644 node_modules/roughjs/bin/fillers/zigzag-filler.js create mode 100644 node_modules/roughjs/bin/fillers/zigzag-line-filler.d.ts create mode 100644 node_modules/roughjs/bin/fillers/zigzag-line-filler.js create mode 100644 node_modules/roughjs/bin/generator.d.ts create mode 100644 node_modules/roughjs/bin/generator.js create mode 100644 node_modules/roughjs/bin/geometry.d.ts create mode 100644 node_modules/roughjs/bin/geometry.js create mode 100644 node_modules/roughjs/bin/math.d.ts create mode 100644 node_modules/roughjs/bin/math.js create mode 100644 node_modules/roughjs/bin/renderer.d.ts create mode 100644 node_modules/roughjs/bin/renderer.js create mode 100644 node_modules/roughjs/bin/rough.d.ts create mode 100644 node_modules/roughjs/bin/rough.js create mode 100644 node_modules/roughjs/bin/svg.d.ts create mode 100644 node_modules/roughjs/bin/svg.js create mode 100644 node_modules/roughjs/bundled/rough.cjs.js create mode 100644 node_modules/roughjs/bundled/rough.esm.js create mode 100644 node_modules/roughjs/bundled/rough.js create mode 100644 node_modules/roughjs/package.json create mode 100644 node_modules/roughjs/tsconfig.json create mode 100644 package-lock.json create mode 100644 web/p5.scribble.js create mode 100644 web/rough.js diff --git a/node_modules/path-data-parser/LICENSE b/node_modules/path-data-parser/LICENSE new file mode 100644 index 0000000..973f15b --- /dev/null +++ b/node_modules/path-data-parser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Preet Shihn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/path-data-parser/README.md b/node_modules/path-data-parser/README.md new file mode 100644 index 0000000..95904c7 --- /dev/null +++ b/node_modules/path-data-parser/README.md @@ -0,0 +1,72 @@ +# path-data-parser + +I know there are a bunch of SVG path parsers out there, but here's one that I have been using for a few years now. It's small, tree-shakable and provides four simple functions that I have needed in several graphics projects. + +## Install + +From npm + +``` +npm install --save path-data-parser +``` + +The code is shipped as ES6 modules. + +## Methods + +The module exposes 4 methods + +### pasrePath(d: string): Segment[] + +This is the main method that parses the SVG path data. You pass in the path string and it returns an array of `Segments`. A segment has a `key` which is the commands, e.g. `M` or `h` or `C`; and `data` which is an array of numbers used by the command + +```javascript +Segment { + key: string; + data: number[]; +} +``` + +example: + +```javascript +import { parsePath } from 'path-data-parser'; +const segments = parsePath('M10 10 h 80 v 80 h -80 C Z'); +``` + +### serialize(segments: Segment[]): string + +This is essentially the opposite of the `parsePath` command. It outputs a path string from an array of `Segment` objects. + +```javascript +import { parsePath, serialize, absolutize } from 'path-data-parser'; + +const segments = parsePath('M10 10 h 80 v 80 h -80 Z'); +const absoluteSegments = absolutize(segments); // Turns relative commands to absolute +const outputPath = serialize(absoluteSegments); +console.log(outputPath); // M 10 10 H 90 V 90 H 10 Z +``` + +### absolutize(segments: Segment[]): Segment[] + +Translates relative commands to absolute commands. i.e. all commands that use relative positions (lower-case ones), turns into absolute position commands (upper-case ones). +See the `serialize` example above. + +### normalize(segments: Segment[]): Segment[] + +Normalize takes a list of _absolute segments_ and outputs a list of segments with only four commands: `M, L, C, Z`. So every segment is described as move, line, or a bezier curve (cubic). + +This is useful when translating SVG paths to non SVG mediums - Canvas, or some other graphics platform. Most such platforms will support lines and bezier curves. It also simplifies the cases to consider when modifying these segments. + +```javascript +import { parsePath, serialize, absolutize, normalize } from 'path-data-parser'; + +const segments = parsePath(' M 10 80 Q 52.5 10, 95 80 T 180 80'); +const absoluteSegments = absolutize(segments); +const normalizedSegments = normalize(absoluteSegments); +// M 10 80 C 38.33 33.33, 66.67 33.33, 95 80 C 123.33 126.67, 151.67 126.67, 180 80 + +``` + +## License +[MIT License](https://github.com/pshihn/path-data-parser/blob/master/LICENSE) diff --git a/node_modules/path-data-parser/lib/absolutize.d.ts b/node_modules/path-data-parser/lib/absolutize.d.ts new file mode 100644 index 0000000..97b950e --- /dev/null +++ b/node_modules/path-data-parser/lib/absolutize.d.ts @@ -0,0 +1,2 @@ +import { Segment } from './parser.js'; +export declare function absolutize(segments: Segment[]): Segment[]; diff --git a/node_modules/path-data-parser/lib/absolutize.js b/node_modules/path-data-parser/lib/absolutize.js new file mode 100644 index 0000000..1afa30f --- /dev/null +++ b/node_modules/path-data-parser/lib/absolutize.js @@ -0,0 +1,110 @@ +// Translate relative commands to absolute commands +export function absolutize(segments) { + let cx = 0, cy = 0; + let subx = 0, suby = 0; + const out = []; + for (const { key, data } of segments) { + switch (key) { + case 'M': + out.push({ key: 'M', data: [...data] }); + [cx, cy] = data; + [subx, suby] = data; + break; + case 'm': + cx += data[0]; + cy += data[1]; + out.push({ key: 'M', data: [cx, cy] }); + subx = cx; + suby = cy; + break; + case 'L': + out.push({ key: 'L', data: [...data] }); + [cx, cy] = data; + break; + case 'l': + cx += data[0]; + cy += data[1]; + out.push({ key: 'L', data: [cx, cy] }); + break; + case 'C': + out.push({ key: 'C', data: [...data] }); + cx = data[4]; + cy = data[5]; + break; + case 'c': { + const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx)); + out.push({ key: 'C', data: newdata }); + cx = newdata[4]; + cy = newdata[5]; + break; + } + case 'Q': + out.push({ key: 'Q', data: [...data] }); + cx = data[2]; + cy = data[3]; + break; + case 'q': { + const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx)); + out.push({ key: 'Q', data: newdata }); + cx = newdata[2]; + cy = newdata[3]; + break; + } + case 'A': + out.push({ key: 'A', data: [...data] }); + cx = data[5]; + cy = data[6]; + break; + case 'a': + cx += data[5]; + cy += data[6]; + out.push({ key: 'A', data: [data[0], data[1], data[2], data[3], data[4], cx, cy] }); + break; + case 'H': + out.push({ key: 'H', data: [...data] }); + cx = data[0]; + break; + case 'h': + cx += data[0]; + out.push({ key: 'H', data: [cx] }); + break; + case 'V': + out.push({ key: 'V', data: [...data] }); + cy = data[0]; + break; + case 'v': + cy += data[0]; + out.push({ key: 'V', data: [cy] }); + break; + case 'S': + out.push({ key: 'S', data: [...data] }); + cx = data[2]; + cy = data[3]; + break; + case 's': { + const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx)); + out.push({ key: 'S', data: newdata }); + cx = newdata[2]; + cy = newdata[3]; + break; + } + case 'T': + out.push({ key: 'T', data: [...data] }); + cx = data[0]; + cy = data[1]; + break; + case 't': + cx += data[0]; + cy += data[1]; + out.push({ key: 'T', data: [cx, cy] }); + break; + case 'Z': + case 'z': + out.push({ key: 'Z', data: [] }); + cx = subx; + cy = suby; + break; + } + } + return out; +} diff --git a/node_modules/path-data-parser/lib/index.d.ts b/node_modules/path-data-parser/lib/index.d.ts new file mode 100644 index 0000000..b981c69 --- /dev/null +++ b/node_modules/path-data-parser/lib/index.d.ts @@ -0,0 +1,3 @@ +export { parsePath, serialize } from './parser.js'; +export { absolutize } from './absolutize.js'; +export { normalize } from './normalize.js'; diff --git a/node_modules/path-data-parser/lib/index.js b/node_modules/path-data-parser/lib/index.js new file mode 100644 index 0000000..b981c69 --- /dev/null +++ b/node_modules/path-data-parser/lib/index.js @@ -0,0 +1,3 @@ +export { parsePath, serialize } from './parser.js'; +export { absolutize } from './absolutize.js'; +export { normalize } from './normalize.js'; diff --git a/node_modules/path-data-parser/lib/normalize.d.ts b/node_modules/path-data-parser/lib/normalize.d.ts new file mode 100644 index 0000000..42ef5d4 --- /dev/null +++ b/node_modules/path-data-parser/lib/normalize.d.ts @@ -0,0 +1,2 @@ +import { Segment } from './parser.js'; +export declare function normalize(segments: Segment[]): Segment[]; diff --git a/node_modules/path-data-parser/lib/normalize.js b/node_modules/path-data-parser/lib/normalize.js new file mode 100644 index 0000000..bf50d4b --- /dev/null +++ b/node_modules/path-data-parser/lib/normalize.js @@ -0,0 +1,219 @@ +// Normalize path to include only M, L, C, and Z commands +export function normalize(segments) { + const out = []; + let lastType = ''; + let cx = 0, cy = 0; + let subx = 0, suby = 0; + let lcx = 0, lcy = 0; + for (const { key, data } of segments) { + switch (key) { + case 'M': + out.push({ key: 'M', data: [...data] }); + [cx, cy] = data; + [subx, suby] = data; + break; + case 'C': + out.push({ key: 'C', data: [...data] }); + cx = data[4]; + cy = data[5]; + lcx = data[2]; + lcy = data[3]; + break; + case 'L': + out.push({ key: 'L', data: [...data] }); + [cx, cy] = data; + break; + case 'H': + cx = data[0]; + out.push({ key: 'L', data: [cx, cy] }); + break; + case 'V': + cy = data[0]; + out.push({ key: 'L', data: [cx, cy] }); + break; + case 'S': { + let cx1 = 0, cy1 = 0; + if (lastType === 'C' || lastType === 'S') { + cx1 = cx + (cx - lcx); + cy1 = cy + (cy - lcy); + } + else { + cx1 = cx; + cy1 = cy; + } + out.push({ key: 'C', data: [cx1, cy1, ...data] }); + lcx = data[0]; + lcy = data[1]; + cx = data[2]; + cy = data[3]; + break; + } + case 'T': { + const [x, y] = data; + let x1 = 0, y1 = 0; + if (lastType === 'Q' || lastType === 'T') { + x1 = cx + (cx - lcx); + y1 = cy + (cy - lcy); + } + else { + x1 = cx; + y1 = cy; + } + const cx1 = cx + 2 * (x1 - cx) / 3; + const cy1 = cy + 2 * (y1 - cy) / 3; + const cx2 = x + 2 * (x1 - x) / 3; + const cy2 = y + 2 * (y1 - y) / 3; + out.push({ key: 'C', data: [cx1, cy1, cx2, cy2, x, y] }); + lcx = x1; + lcy = y1; + cx = x; + cy = y; + break; + } + case 'Q': { + const [x1, y1, x, y] = data; + const cx1 = cx + 2 * (x1 - cx) / 3; + const cy1 = cy + 2 * (y1 - cy) / 3; + const cx2 = x + 2 * (x1 - x) / 3; + const cy2 = y + 2 * (y1 - y) / 3; + out.push({ key: 'C', data: [cx1, cy1, cx2, cy2, x, y] }); + lcx = x1; + lcy = y1; + cx = x; + cy = y; + break; + } + case 'A': { + const r1 = Math.abs(data[0]); + const r2 = Math.abs(data[1]); + const angle = data[2]; + const largeArcFlag = data[3]; + const sweepFlag = data[4]; + const x = data[5]; + const y = data[6]; + if (r1 === 0 || r2 === 0) { + out.push({ key: 'C', data: [cx, cy, x, y, x, y] }); + cx = x; + cy = y; + } + else { + if (cx !== x || cy !== y) { + const curves = arcToCubicCurves(cx, cy, x, y, r1, r2, angle, largeArcFlag, sweepFlag); + curves.forEach(function (curve) { + out.push({ key: 'C', data: curve }); + }); + cx = x; + cy = y; + } + } + break; + } + case 'Z': + out.push({ key: 'Z', data: [] }); + cx = subx; + cy = suby; + break; + } + lastType = key; + } + return out; +} +function degToRad(degrees) { + return (Math.PI * degrees) / 180; +} +function rotate(x, y, angleRad) { + const X = x * Math.cos(angleRad) - y * Math.sin(angleRad); + const Y = x * Math.sin(angleRad) + y * Math.cos(angleRad); + return [X, Y]; +} +function arcToCubicCurves(x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, recursive) { + const angleRad = degToRad(angle); + let params = []; + let f1 = 0, f2 = 0, cx = 0, cy = 0; + if (recursive) { + [f1, f2, cx, cy] = recursive; + } + else { + [x1, y1] = rotate(x1, y1, -angleRad); + [x2, y2] = rotate(x2, y2, -angleRad); + const x = (x1 - x2) / 2; + const y = (y1 - y2) / 2; + let h = (x * x) / (r1 * r1) + (y * y) / (r2 * r2); + if (h > 1) { + h = Math.sqrt(h); + r1 = h * r1; + r2 = h * r2; + } + const sign = (largeArcFlag === sweepFlag) ? -1 : 1; + const r1Pow = r1 * r1; + const r2Pow = r2 * r2; + const left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x; + const right = r1Pow * y * y + r2Pow * x * x; + const k = sign * Math.sqrt(Math.abs(left / right)); + cx = k * r1 * y / r2 + (x1 + x2) / 2; + cy = k * -r2 * x / r1 + (y1 + y2) / 2; + f1 = Math.asin(parseFloat(((y1 - cy) / r2).toFixed(9))); + f2 = Math.asin(parseFloat(((y2 - cy) / r2).toFixed(9))); + if (x1 < cx) { + f1 = Math.PI - f1; + } + if (x2 < cx) { + f2 = Math.PI - f2; + } + if (f1 < 0) { + f1 = Math.PI * 2 + f1; + } + if (f2 < 0) { + f2 = Math.PI * 2 + f2; + } + if (sweepFlag && f1 > f2) { + f1 = f1 - Math.PI * 2; + } + if (!sweepFlag && f2 > f1) { + f2 = f2 - Math.PI * 2; + } + } + let df = f2 - f1; + if (Math.abs(df) > (Math.PI * 120 / 180)) { + const f2old = f2; + const x2old = x2; + const y2old = y2; + if (sweepFlag && f2 > f1) { + f2 = f1 + (Math.PI * 120 / 180) * (1); + } + else { + f2 = f1 + (Math.PI * 120 / 180) * (-1); + } + x2 = cx + r1 * Math.cos(f2); + y2 = cy + r2 * Math.sin(f2); + params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]); + } + df = f2 - f1; + const c1 = Math.cos(f1); + const s1 = Math.sin(f1); + const c2 = Math.cos(f2); + const s2 = Math.sin(f2); + const t = Math.tan(df / 4); + const hx = 4 / 3 * r1 * t; + const hy = 4 / 3 * r2 * t; + const m1 = [x1, y1]; + const m2 = [x1 + hx * s1, y1 - hy * c1]; + const m3 = [x2 + hx * s2, y2 - hy * c2]; + const m4 = [x2, y2]; + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + if (recursive) { + return [m2, m3, m4].concat(params); + } + else { + params = [m2, m3, m4].concat(params); + const curves = []; + for (let i = 0; i < params.length; i += 3) { + const r1 = rotate(params[i][0], params[i][1], angleRad); + const r2 = rotate(params[i + 1][0], params[i + 1][1], angleRad); + const r3 = rotate(params[i + 2][0], params[i + 2][1], angleRad); + curves.push([r1[0], r1[1], r2[0], r2[1], r3[0], r3[1]]); + } + return curves; + } +} diff --git a/node_modules/path-data-parser/lib/parser.d.ts b/node_modules/path-data-parser/lib/parser.d.ts new file mode 100644 index 0000000..63108a8 --- /dev/null +++ b/node_modules/path-data-parser/lib/parser.d.ts @@ -0,0 +1,6 @@ +export interface Segment { + key: string; + data: number[]; +} +export declare function parsePath(d: string): Segment[]; +export declare function serialize(segments: Segment[]): string; diff --git a/node_modules/path-data-parser/lib/parser.js b/node_modules/path-data-parser/lib/parser.js new file mode 100644 index 0000000..1aeffda --- /dev/null +++ b/node_modules/path-data-parser/lib/parser.js @@ -0,0 +1,107 @@ +const COMMAND = 0; +const NUMBER = 1; +const EOD = 2; +const PARAMS = { A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0 }; +function tokenize(d) { + const tokens = new Array(); + while (d !== '') { + if (d.match(/^([ \t\r\n,]+)/)) { + d = d.substr(RegExp.$1.length); + } + else if (d.match(/^([aAcChHlLmMqQsStTvVzZ])/)) { + tokens[tokens.length] = { type: COMMAND, text: RegExp.$1 }; + d = d.substr(RegExp.$1.length); + } + else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) { + tokens[tokens.length] = { type: NUMBER, text: `${parseFloat(RegExp.$1)}` }; + d = d.substr(RegExp.$1.length); + } + else { + return []; + } + } + tokens[tokens.length] = { type: EOD, text: '' }; + return tokens; +} +function isType(token, type) { + return token.type === type; +} +export function parsePath(d) { + const segments = []; + const tokens = tokenize(d); + let mode = 'BOD'; + let index = 0; + let token = tokens[index]; + while (!isType(token, EOD)) { + let paramsCount = 0; + const params = []; + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + paramsCount = PARAMS[token.text]; + mode = token.text; + } + else { + return parsePath('M0,0' + d); + } + } + else if (isType(token, NUMBER)) { + paramsCount = PARAMS[mode]; + } + else { + index++; + paramsCount = PARAMS[token.text]; + mode = token.text; + } + if ((index + paramsCount) < tokens.length) { + for (let i = index; i < index + paramsCount; i++) { + const numbeToken = tokens[i]; + if (isType(numbeToken, NUMBER)) { + params[params.length] = +numbeToken.text; + } + else { + throw new Error('Param not a number: ' + mode + ',' + numbeToken.text); + } + } + if (typeof PARAMS[mode] === 'number') { + const segment = { key: mode, data: params }; + segments.push(segment); + index += paramsCount; + token = tokens[index]; + if (mode === 'M') + mode = 'L'; + if (mode === 'm') + mode = 'l'; + } + else { + throw new Error('Bad segment: ' + mode); + } + } + else { + throw new Error('Path data ended short'); + } + } + return segments; +} +export function serialize(segments) { + const tokens = []; + for (const { key, data } of segments) { + tokens.push(key); + switch (key) { + case 'C': + case 'c': + tokens.push(data[0], `${data[1]},`, data[2], `${data[3]},`, data[4], data[5]); + break; + case 'S': + case 's': + case 'Q': + case 'q': + tokens.push(data[0], `${data[1]},`, data[2], data[3]); + break; + default: + tokens.push(...data); + break; + } + } + return tokens.join(' '); +} diff --git a/node_modules/path-data-parser/package.json b/node_modules/path-data-parser/package.json new file mode 100644 index 0000000..c14161e --- /dev/null +++ b/node_modules/path-data-parser/package.json @@ -0,0 +1,63 @@ +{ + "_from": "path-data-parser@^0.1.0", + "_id": "path-data-parser@0.1.0", + "_inBundle": false, + "_integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "_location": "/path-data-parser", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "path-data-parser@^0.1.0", + "name": "path-data-parser", + "escapedName": "path-data-parser", + "rawSpec": "^0.1.0", + "saveSpec": null, + "fetchSpec": "^0.1.0" + }, + "_requiredBy": [ + "/points-on-path", + "/roughjs" + ], + "_resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "_shasum": "8f5ba5cc70fc7becb3dcefaea08e2659aba60b8c", + "_spec": "path-data-parser@^0.1.0", + "_where": "/Users/1009738/PracticalVision/node_modules/roughjs", + "author": { + "name": "Preet Shihn" + }, + "bugs": { + "url": "https://github.com/pshihn/path-data-parser/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Yet another SVG path parser. This one's tiny", + "devDependencies": { + "tslint": "^6.1.1", + "typescript": "^3.8.3" + }, + "homepage": "https://github.com/pshihn/path-data-parser#readme", + "keywords": [ + "svg path parser", + "SVG", + "path parser", + "SVGPATH", + "pathdata", + "svg parser" + ], + "license": "MIT", + "main": "lib/index.js", + "module": "lib/index.js", + "name": "path-data-parser", + "repository": { + "type": "git", + "url": "git+https://github.com/pshihn/path-data-parser.git" + }, + "scripts": { + "build": "rm -rf lib && tsc", + "lint": "tslint -p tsconfig.json", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "types": "lib/index.d.ts", + "version": "0.1.0" +} diff --git a/node_modules/path-data-parser/src/absolutize.ts b/node_modules/path-data-parser/src/absolutize.ts new file mode 100644 index 0000000..ea55ed2 --- /dev/null +++ b/node_modules/path-data-parser/src/absolutize.ts @@ -0,0 +1,112 @@ +import { Segment } from './parser.js'; + +// Translate relative commands to absolute commands +export function absolutize(segments: Segment[]): Segment[] { + let cx = 0, cy = 0; + let subx = 0, suby = 0; + const out: Segment[] = []; + for (const { key, data } of segments) { + switch (key) { + case 'M': + out.push({ key: 'M', data: [...data] }); + [cx, cy] = data; + [subx, suby] = data; + break; + case 'm': + cx += data[0]; + cy += data[1]; + out.push({ key: 'M', data: [cx, cy] }); + subx = cx; + suby = cy; + break; + case 'L': + out.push({ key: 'L', data: [...data] }); + [cx, cy] = data; + break; + case 'l': + cx += data[0]; + cy += data[1]; + out.push({ key: 'L', data: [cx, cy] }); + break; + case 'C': + out.push({ key: 'C', data: [...data] }); + cx = data[4]; + cy = data[5]; + break; + case 'c': { + const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx)); + out.push({ key: 'C', data: newdata }); + cx = newdata[4]; + cy = newdata[5]; + break; + } + case 'Q': + out.push({ key: 'Q', data: [...data] }); + cx = data[2]; + cy = data[3]; + break; + case 'q': { + const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx)); + out.push({ key: 'Q', data: newdata }); + cx = newdata[2]; + cy = newdata[3]; + break; + } + case 'A': + out.push({ key: 'A', data: [...data] }); + cx = data[5]; + cy = data[6]; + break; + case 'a': + cx += data[5]; + cy += data[6]; + out.push({ key: 'A', data: [data[0], data[1], data[2], data[3], data[4], cx, cy] }); + break; + case 'H': + out.push({ key: 'H', data: [...data] }); + cx = data[0]; + break; + case 'h': + cx += data[0]; + out.push({ key: 'H', data: [cx] }); + break; + case 'V': + out.push({ key: 'V', data: [...data] }); + cy = data[0]; + break; + case 'v': + cy += data[0]; + out.push({ key: 'V', data: [cy] }); + break; + case 'S': + out.push({ key: 'S', data: [...data] }); + cx = data[2]; + cy = data[3]; + break; + case 's': { + const newdata = data.map((d, i) => (i % 2) ? (d + cy) : (d + cx)); + out.push({ key: 'S', data: newdata }); + cx = newdata[2]; + cy = newdata[3]; + break; + } + case 'T': + out.push({ key: 'T', data: [...data] }); + cx = data[0]; + cy = data[1]; + break; + case 't': + cx += data[0]; + cy += data[1]; + out.push({ key: 'T', data: [cx, cy] }); + break; + case 'Z': + case 'z': + out.push({ key: 'Z', data: [] }); + cx = subx; + cy = suby; + break; + } + } + return out; +} \ No newline at end of file diff --git a/node_modules/path-data-parser/src/index.ts b/node_modules/path-data-parser/src/index.ts new file mode 100644 index 0000000..0ba7e59 --- /dev/null +++ b/node_modules/path-data-parser/src/index.ts @@ -0,0 +1,3 @@ +export { parsePath, serialize } from './parser.js'; +export { absolutize } from './absolutize.js'; +export { normalize } from './normalize.js'; \ No newline at end of file diff --git a/node_modules/path-data-parser/src/normalize.ts b/node_modules/path-data-parser/src/normalize.ts new file mode 100644 index 0000000..3f24cd8 --- /dev/null +++ b/node_modules/path-data-parser/src/normalize.ts @@ -0,0 +1,242 @@ +import { Segment } from './parser.js'; + +// Normalize path to include only M, L, C, and Z commands +export function normalize(segments: Segment[]): Segment[] { + const out: Segment[] = []; + + let lastType = ''; + let cx = 0, cy = 0; + let subx = 0, suby = 0; + let lcx = 0, lcy = 0; + + for (const { key, data } of segments) { + switch (key) { + case 'M': + out.push({ key: 'M', data: [...data] }); + [cx, cy] = data; + [subx, suby] = data; + break; + case 'C': + out.push({ key: 'C', data: [...data] }); + cx = data[4]; + cy = data[5]; + lcx = data[2]; + lcy = data[3]; + break; + case 'L': + out.push({ key: 'L', data: [...data] }); + [cx, cy] = data; + break; + case 'H': + cx = data[0]; + out.push({ key: 'L', data: [cx, cy] }); + break; + case 'V': + cy = data[0]; + out.push({ key: 'L', data: [cx, cy] }); + break; + case 'S': { + let cx1 = 0, cy1 = 0; + if (lastType === 'C' || lastType === 'S') { + cx1 = cx + (cx - lcx); + cy1 = cy + (cy - lcy); + } else { + cx1 = cx; + cy1 = cy; + } + out.push({ key: 'C', data: [cx1, cy1, ...data] }); + lcx = data[0]; + lcy = data[1]; + cx = data[2]; + cy = data[3]; + break; + } + case 'T': { + const [x, y] = data; + let x1 = 0, y1 = 0; + if (lastType === 'Q' || lastType === 'T') { + x1 = cx + (cx - lcx); + y1 = cy + (cy - lcy); + } else { + x1 = cx; + y1 = cy; + } + const cx1 = cx + 2 * (x1 - cx) / 3; + const cy1 = cy + 2 * (y1 - cy) / 3; + const cx2 = x + 2 * (x1 - x) / 3; + const cy2 = y + 2 * (y1 - y) / 3; + out.push({ key: 'C', data: [cx1, cy1, cx2, cy2, x, y] }); + lcx = x1; + lcy = y1; + cx = x; + cy = y; + break; + } + case 'Q': { + const [x1, y1, x, y] = data; + const cx1 = cx + 2 * (x1 - cx) / 3; + const cy1 = cy + 2 * (y1 - cy) / 3; + const cx2 = x + 2 * (x1 - x) / 3; + const cy2 = y + 2 * (y1 - y) / 3; + out.push({ key: 'C', data: [cx1, cy1, cx2, cy2, x, y] }); + lcx = x1; + lcy = y1; + cx = x; + cy = y; + break; + } + case 'A': { + const r1 = Math.abs(data[0]); + const r2 = Math.abs(data[1]); + const angle = data[2]; + const largeArcFlag = data[3]; + const sweepFlag = data[4]; + const x = data[5]; + const y = data[6]; + if (r1 === 0 || r2 === 0) { + out.push({ key: 'C', data: [cx, cy, x, y, x, y] }); + cx = x; + cy = y; + } else { + if (cx !== x || cy !== y) { + const curves: number[][] = arcToCubicCurves(cx, cy, x, y, r1, r2, angle, largeArcFlag, sweepFlag); + curves.forEach(function (curve) { + out.push({ key: 'C', data: curve }); + }); + cx = x; + cy = y; + } + } + break; + } + case 'Z': + out.push({ key: 'Z', data: [] }); + cx = subx; + cy = suby; + break; + } + lastType = key; + } + return out; +} + +function degToRad(degrees: number): number { + return (Math.PI * degrees) / 180; +} + +function rotate(x: number, y: number, angleRad: number): [number, number] { + const X = x * Math.cos(angleRad) - y * Math.sin(angleRad); + const Y = x * Math.sin(angleRad) + y * Math.cos(angleRad); + return [X, Y]; +} + +function arcToCubicCurves(x1: number, y1: number, x2: number, y2: number, r1: number, r2: number, angle: number, largeArcFlag: number, sweepFlag: number, recursive?: number[]): number[][] { + const angleRad = degToRad(angle); + let params: number[][] = []; + + let f1 = 0, f2 = 0, cx = 0, cy = 0; + if (recursive) { + [f1, f2, cx, cy] = recursive; + } else { + [x1, y1] = rotate(x1, y1, -angleRad); + [x2, y2] = rotate(x2, y2, -angleRad); + + const x = (x1 - x2) / 2; + const y = (y1 - y2) / 2; + let h = (x * x) / (r1 * r1) + (y * y) / (r2 * r2); + if (h > 1) { + h = Math.sqrt(h); + r1 = h * r1; + r2 = h * r2; + } + + const sign = (largeArcFlag === sweepFlag) ? -1 : 1; + + const r1Pow = r1 * r1; + const r2Pow = r2 * r2; + + const left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x; + const right = r1Pow * y * y + r2Pow * x * x; + + const k = sign * Math.sqrt(Math.abs(left / right)); + + cx = k * r1 * y / r2 + (x1 + x2) / 2; + cy = k * -r2 * x / r1 + (y1 + y2) / 2; + + f1 = Math.asin(parseFloat(((y1 - cy) / r2).toFixed(9))); + f2 = Math.asin(parseFloat(((y2 - cy) / r2).toFixed(9))); + + if (x1 < cx) { + f1 = Math.PI - f1; + } + if (x2 < cx) { + f2 = Math.PI - f2; + } + + if (f1 < 0) { + f1 = Math.PI * 2 + f1; + } + if (f2 < 0) { + f2 = Math.PI * 2 + f2; + } + + if (sweepFlag && f1 > f2) { + f1 = f1 - Math.PI * 2; + } + if (!sweepFlag && f2 > f1) { + f2 = f2 - Math.PI * 2; + } + } + + let df = f2 - f1; + + if (Math.abs(df) > (Math.PI * 120 / 180)) { + const f2old = f2; + const x2old = x2; + const y2old = y2; + + if (sweepFlag && f2 > f1) { + f2 = f1 + (Math.PI * 120 / 180) * (1); + } + else { + f2 = f1 + (Math.PI * 120 / 180) * (-1); + } + + x2 = cx + r1 * Math.cos(f2); + y2 = cy + r2 * Math.sin(f2); + params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]); + } + + df = f2 - f1; + + const c1 = Math.cos(f1); + const s1 = Math.sin(f1); + const c2 = Math.cos(f2); + const s2 = Math.sin(f2); + const t = Math.tan(df / 4); + const hx = 4 / 3 * r1 * t; + const hy = 4 / 3 * r2 * t; + + const m1 = [x1, y1]; + const m2 = [x1 + hx * s1, y1 - hy * c1]; + const m3 = [x2 + hx * s2, y2 - hy * c2]; + const m4 = [x2, y2]; + + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + + if (recursive) { + return [m2, m3, m4].concat(params); + } + else { + params = [m2, m3, m4].concat(params); + const curves = []; + for (let i = 0; i < params.length; i += 3) { + const r1 = rotate(params[i][0], params[i][1], angleRad); + const r2 = rotate(params[i + 1][0], params[i + 1][1], angleRad); + const r3 = rotate(params[i + 2][0], params[i + 2][1], angleRad); + curves.push([r1[0], r1[1], r2[0], r2[1], r3[0], r3[1]]); + } + return curves; + } +} \ No newline at end of file diff --git a/node_modules/path-data-parser/src/parser.ts b/node_modules/path-data-parser/src/parser.ts new file mode 100644 index 0000000..3c335e2 --- /dev/null +++ b/node_modules/path-data-parser/src/parser.ts @@ -0,0 +1,113 @@ +const COMMAND = 0; +const NUMBER = 1; +const EOD = 2; +type TokenType = 0 | 1 | 2; + +interface PathToken { + type: TokenType; + text: string; +} + +export interface Segment { + key: string; + data: number[]; +} + +const PARAMS: { [key: string]: number } = { A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0 }; + +function tokenize(d: string): PathToken[] { + const tokens: PathToken[] = new Array(); + while (d !== '') { + if (d.match(/^([ \t\r\n,]+)/)) { + d = d.substr(RegExp.$1.length); + } else if (d.match(/^([aAcChHlLmMqQsStTvVzZ])/)) { + tokens[tokens.length] = { type: COMMAND, text: RegExp.$1 }; + d = d.substr(RegExp.$1.length); + } else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) { + tokens[tokens.length] = { type: NUMBER, text: `${parseFloat(RegExp.$1)}` }; + d = d.substr(RegExp.$1.length); + } else { + return []; + } + } + tokens[tokens.length] = { type: EOD, text: '' }; + return tokens; +} + +function isType(token: PathToken, type: number) { + return token.type === type; +} + +export function parsePath(d: string): Segment[] { + const segments: Segment[] = []; + const tokens = tokenize(d); + let mode = 'BOD'; + let index = 0; + let token = tokens[index]; + while (!isType(token, EOD)) { + let paramsCount = 0; + const params: number[] = []; + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + paramsCount = PARAMS[token.text]; + mode = token.text; + } else { + return parsePath('M0,0' + d); + } + } else if (isType(token, NUMBER)) { + paramsCount = PARAMS[mode]; + } else { + index++; + paramsCount = PARAMS[token.text]; + mode = token.text; + } + if ((index + paramsCount) < tokens.length) { + for (let i = index; i < index + paramsCount; i++) { + const numbeToken = tokens[i]; + if (isType(numbeToken, NUMBER)) { + params[params.length] = +numbeToken.text; + } + else { + throw new Error('Param not a number: ' + mode + ',' + numbeToken.text); + } + } + if (typeof PARAMS[mode] === 'number') { + const segment: Segment = { key: mode, data: params }; + segments.push(segment); + index += paramsCount; + token = tokens[index]; + if (mode === 'M') mode = 'L'; + if (mode === 'm') mode = 'l'; + } else { + throw new Error('Bad segment: ' + mode); + } + } else { + throw new Error('Path data ended short'); + } + } + return segments; +} + +export function serialize(segments: Segment[]): string { + const tokens: (string | number)[] = []; + for (const { key, data } of segments) { + tokens.push(key); + switch (key) { + case 'C': + case 'c': + tokens.push(data[0], `${data[1]},`, data[2], `${data[3]},`, data[4], data[5]); + break; + case 'S': + case 's': + case 'Q': + case 'q': + tokens.push(data[0], `${data[1]},`, data[2], data[3]); + break; + default: + tokens.push(...data); + break; + } + } + return tokens.join(' '); +} \ No newline at end of file diff --git a/node_modules/path-data-parser/tsconfig.json b/node_modules/path-data-parser/tsconfig.json new file mode 100644 index 0000000..68d5331 --- /dev/null +++ b/node_modules/path-data-parser/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "es2015", + "moduleResolution": "node", + "lib": [ + "es2017" + ], + "declaration": true, + "outDir": "./lib", + "baseUrl": ".", + "strict": true, + "strictNullChecks": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/node_modules/path-data-parser/tslint.json b/node_modules/path-data-parser/tslint.json new file mode 100644 index 0000000..c8e9cb2 --- /dev/null +++ b/node_modules/path-data-parser/tslint.json @@ -0,0 +1,61 @@ +{ + "rules": { + "arrow-parens": true, + "class-name": true, + "indent": [ + true, + "spaces", + 2 + ], + "prefer-const": true, + "no-duplicate-variable": true, + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": false, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "single", + "avoid-escape" + ], + "semicolon": [ + true, + "always" + ], + "trailing-comma": [ + true, + "multiline" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/node_modules/points-on-curve/LICENSE b/node_modules/points-on-curve/LICENSE new file mode 100644 index 0000000..973f15b --- /dev/null +++ b/node_modules/points-on-curve/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Preet Shihn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/points-on-curve/README.md b/node_modules/points-on-curve/README.md new file mode 100644 index 0000000..255164e --- /dev/null +++ b/node_modules/points-on-curve/README.md @@ -0,0 +1,90 @@ +# points-on-curve + +This package calculate the points on a curve with a certain tolerance. It can also simplify the shape to use fewer points. +This can really be useful when estimating lines/polygons for curves in WebGL or for Hit/Collision detections. + +## Install + +From npm + +``` +npm install --save points-on-curve +``` + +The package is distributed as an ES6 module. + +## API + +### pointsOnBezierCurves(points: Point[], tolerance?: number, distance?: number): Point[] + +You pass in the points representing a bezier curve. Each point is an array of two numbers e.g. `[100, 123]`. + +The points can also be a set of continuous curves where the last poing on the `Nth` curve acts as the first point of the next. + +```javascript +import { pointsOnBezierCurves } from 'points-on-curve'; + +const curve = [[70,240],[145,60],[275,90],[300,230]]; +const points = pointsOnBezierCurves(curve); +// plotPoints(points); +``` + +![points on bezier](https://user-images.githubusercontent.com/833927/79051836-45630300-7be7-11ea-8cb6-cba2695a4807.png) + +Same can be rendered with more **tolerance** (default value is 0.15): + +```javascript +const points = pointsOnBezierCurves(curve, 0.7); +``` +![points on bezier with 0.7 tolerance](https://user-images.githubusercontent.com/833927/79051837-45fb9980-7be7-11ea-9583-52cf882e770e.png) + +Note that this method does not accept the number of points to render, but takes in a tolerance level which allows for better distribution of points. + +The value of **tolerance** can be between 0 and 1. It is used to decide how many points are needed in a section of the curve. The algorithm determined the *flatness* of a section of the curve and compares it to the *tolerance* level, if less flat, the segment gets further divided into 2 segments. + + +#### Simplifying path + +Based on the tolerance alone, this algorithm nicely provides enough points to represent a curve. It does not, however, efficiently get rid of unneeded points. The second *optional* argument in function, **distance** helps with that. If a `distance` value is provided, the method uses the [Ramer–Douglas–Peucker algorithm](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) to reduce the points. + +```javascript +const points = pointsOnBezierCurves(curve, 0.2, 0.15); +``` + +Following are the points generated with distance values of `0.15`, `0.75`, `1.5`, and `3.0` + +![points with 0.15d](https://user-images.githubusercontent.com/833927/79051853-53b11f00-7be7-11ea-8970-7cc3f7621142.png) +![points with 0.75d](https://user-images.githubusercontent.com/833927/79051854-5449b580-7be7-11ea-9601-a1dd418b10d8.png) +![points with 1.5d](https://user-images.githubusercontent.com/833927/79051855-5449b580-7be7-11ea-9ab4-139beb0faf11.png) +![points with 3.0d](https://user-images.githubusercontent.com/833927/79051856-54e24c00-7be7-11ea-9f52-34e3ad9c81bd.png) + +### curveToBezier(pointsIn: Point[]): Point[] + +Sometimes it's hard to think of shape as a set of cubic bezier curves, each curve with 2 controls points. It is simple to just think of them as a curve passing through a set of points. + +This method turns those set of points to a set of points representing bezier curves. + +```javascript +import { curveToBezier } from 'points-on-curve/lib/curve-to-bezier.js'; + +const curvePoints = [ + [20, 240], + [95, 69], + [225, 90], + [250, 180], + [290, 220], + [380, 80], +]; +const bcurve = curveToBezier(curvePoints); +// .. Plot bcurve +``` +![Curve through points](https://user-images.githubusercontent.com/833927/79051797-12b90a80-7be7-11ea-92d2-5cb79adcbe30.png) + +Now that we have bezier points, these could be passed to `pointsOnBezierCurves` function to get the points on the curve + +![Curve through points](https://user-images.githubusercontent.com/833927/79051798-1351a100-7be7-11ea-8465-959a22b72371.png) + + +## License +[MIT License](https://github.com/pshihn/bezier-points/blob/master/LICENSE) + diff --git a/node_modules/points-on-curve/lib/curve-to-bezier.d.ts b/node_modules/points-on-curve/lib/curve-to-bezier.d.ts new file mode 100644 index 0000000..9dd4d9b --- /dev/null +++ b/node_modules/points-on-curve/lib/curve-to-bezier.d.ts @@ -0,0 +1,2 @@ +import { Point } from './index.js'; +export declare function curveToBezier(pointsIn: Point[], curveTightness?: number): Point[]; diff --git a/node_modules/points-on-curve/lib/curve-to-bezier.js b/node_modules/points-on-curve/lib/curve-to-bezier.js new file mode 100644 index 0000000..94118e2 --- /dev/null +++ b/node_modules/points-on-curve/lib/curve-to-bezier.js @@ -0,0 +1,35 @@ +function clone(p) { + return [...p]; +} +export function curveToBezier(pointsIn, curveTightness = 0) { + const len = pointsIn.length; + if (len < 3) { + throw new Error('A curve must have at least three points.'); + } + const out = []; + if (len === 3) { + out.push(clone(pointsIn[0]), clone(pointsIn[1]), clone(pointsIn[2]), clone(pointsIn[2])); + } + else { + const points = []; + points.push(pointsIn[0], pointsIn[0]); + for (let i = 1; i < pointsIn.length; i++) { + points.push(pointsIn[i]); + if (i === (pointsIn.length - 1)) { + points.push(pointsIn[i]); + } + } + const b = []; + const s = 1 - curveTightness; + out.push(clone(points[0])); + for (let i = 1; (i + 2) < points.length; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + out.push(b[1], b[2], b[3]); + } + } + return out; +} diff --git a/node_modules/points-on-curve/lib/index.d.ts b/node_modules/points-on-curve/lib/index.d.ts new file mode 100644 index 0000000..cd3f880 --- /dev/null +++ b/node_modules/points-on-curve/lib/index.d.ts @@ -0,0 +1,3 @@ +export declare type Point = [number, number]; +export declare function simplify(points: Point[], distance: number): Point[]; +export declare function pointsOnBezierCurves(points: Point[], tolerance?: number, distance?: number): Point[]; diff --git a/node_modules/points-on-curve/lib/index.js b/node_modules/points-on-curve/lib/index.js new file mode 100644 index 0000000..46bda3b --- /dev/null +++ b/node_modules/points-on-curve/lib/index.js @@ -0,0 +1,123 @@ +// distance between 2 points +function distance(p1, p2) { + return Math.sqrt(distanceSq(p1, p2)); +} +// distance between 2 points squared +function distanceSq(p1, p2) { + return Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2); +} +// Sistance squared from a point p to the line segment vw +function distanceToSegmentSq(p, v, w) { + const l2 = distanceSq(v, w); + if (l2 === 0) { + return distanceSq(p, v); + } + let t = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2; + t = Math.max(0, Math.min(1, t)); + return distanceSq(p, lerp(v, w, t)); +} +function lerp(a, b, t) { + return [ + a[0] + (b[0] - a[0]) * t, + a[1] + (b[1] - a[1]) * t, + ]; +} +// Adapted from https://seant23.wordpress.com/2010/11/12/offset-bezier-curves/ +function flatness(points, offset) { + const p1 = points[offset + 0]; + const p2 = points[offset + 1]; + const p3 = points[offset + 2]; + const p4 = points[offset + 3]; + let ux = 3 * p2[0] - 2 * p1[0] - p4[0]; + ux *= ux; + let uy = 3 * p2[1] - 2 * p1[1] - p4[1]; + uy *= uy; + let vx = 3 * p3[0] - 2 * p4[0] - p1[0]; + vx *= vx; + let vy = 3 * p3[1] - 2 * p4[1] - p1[1]; + vy *= vy; + if (ux < vx) { + ux = vx; + } + if (uy < vy) { + uy = vy; + } + return ux + uy; +} +function getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints) { + const outPoints = newPoints || []; + if (flatness(points, offset) < tolerance) { + const p0 = points[offset + 0]; + if (outPoints.length) { + const d = distance(outPoints[outPoints.length - 1], p0); + if (d > 1) { + outPoints.push(p0); + } + } + else { + outPoints.push(p0); + } + outPoints.push(points[offset + 3]); + } + else { + // subdivide + const t = .5; + const p1 = points[offset + 0]; + const p2 = points[offset + 1]; + const p3 = points[offset + 2]; + const p4 = points[offset + 3]; + const q1 = lerp(p1, p2, t); + const q2 = lerp(p2, p3, t); + const q3 = lerp(p3, p4, t); + const r1 = lerp(q1, q2, t); + const r2 = lerp(q2, q3, t); + const red = lerp(r1, r2, t); + getPointsOnBezierCurveWithSplitting([p1, q1, r1, red], 0, tolerance, outPoints); + getPointsOnBezierCurveWithSplitting([red, r2, q3, p4], 0, tolerance, outPoints); + } + return outPoints; +} +export function simplify(points, distance) { + return simplifyPoints(points, 0, points.length, distance); +} +// Ramer–Douglas–Peucker algorithm +// https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm +function simplifyPoints(points, start, end, epsilon, newPoints) { + const outPoints = newPoints || []; + // find the most distance point from the endpoints + const s = points[start]; + const e = points[end - 1]; + let maxDistSq = 0; + let maxNdx = 1; + for (let i = start + 1; i < end - 1; ++i) { + const distSq = distanceToSegmentSq(points[i], s, e); + if (distSq > maxDistSq) { + maxDistSq = distSq; + maxNdx = i; + } + } + // if that point is too far, split + if (Math.sqrt(maxDistSq) > epsilon) { + simplifyPoints(points, start, maxNdx + 1, epsilon, outPoints); + simplifyPoints(points, maxNdx, end, epsilon, outPoints); + } + else { + if (!outPoints.length) { + outPoints.push(s); + } + outPoints.push(e); + } + return outPoints; +} +export function pointsOnBezierCurves(points, tolerance = 0.15, distance) { + const newPoints = []; + const numSegments = (points.length - 1) / 3; + for (let i = 0; i < numSegments; i++) { + const offset = i * 3; + getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints); + } + if (distance && distance > 0) { + return simplifyPoints(newPoints, 0, newPoints.length, distance); + } + return newPoints; +} diff --git a/node_modules/points-on-curve/package.json b/node_modules/points-on-curve/package.json new file mode 100644 index 0000000..1fa3035 --- /dev/null +++ b/node_modules/points-on-curve/package.json @@ -0,0 +1,59 @@ +{ + "_from": "points-on-curve@^0.2.0", + "_id": "points-on-curve@0.2.0", + "_inBundle": false, + "_integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "_location": "/points-on-curve", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "points-on-curve@^0.2.0", + "name": "points-on-curve", + "escapedName": "points-on-curve", + "rawSpec": "^0.2.0", + "saveSpec": null, + "fetchSpec": "^0.2.0" + }, + "_requiredBy": [ + "/points-on-path", + "/roughjs" + ], + "_resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "_shasum": "7dbb98c43791859434284761330fa893cb81b4d1", + "_spec": "points-on-curve@^0.2.0", + "_where": "/Users/1009738/PracticalVision/node_modules/roughjs", + "author": { + "name": "Preet Shihn" + }, + "bugs": { + "url": "https://github.com/pshihn/bezier-points/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Estimate points on a bezier curve or a set of connexted bezier curves", + "devDependencies": { + "tslint": "^6.1.1", + "typescript": "^3.8.3" + }, + "homepage": "https://github.com/pshihn/bezier-points#readme", + "keywords": [ + "Bezier", + "graphics" + ], + "license": "MIT", + "main": "lib/index.js", + "module": "lib/index.js", + "name": "points-on-curve", + "repository": { + "type": "git", + "url": "git+https://github.com/pshihn/bezier-points.git" + }, + "scripts": { + "build": "rm -rf lib && tsc", + "lint": "tslint -p tsconfig.json", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "types": "lib/index.d.ts", + "version": "0.2.0" +} diff --git a/node_modules/points-on-curve/src/curve-to-bezier.ts b/node_modules/points-on-curve/src/curve-to-bezier.ts new file mode 100644 index 0000000..0feb25e --- /dev/null +++ b/node_modules/points-on-curve/src/curve-to-bezier.ts @@ -0,0 +1,42 @@ +import { Point } from './index.js'; + +function clone(p: Point): Point { + return [...p] as Point; +} + +export function curveToBezier(pointsIn: Point[], curveTightness = 0): Point[] { + const len = pointsIn.length; + if (len < 3) { + throw new Error('A curve must have at least three points.'); + } + const out: Point[] = []; + if (len === 3) { + out.push( + clone(pointsIn[0]), + clone(pointsIn[1]), + clone(pointsIn[2]), + clone(pointsIn[2]) + ); + } else { + const points: Point[] = []; + points.push(pointsIn[0], pointsIn[0]); + for (let i = 1; i < pointsIn.length; i++) { + points.push(pointsIn[i]); + if (i === (pointsIn.length - 1)) { + points.push(pointsIn[i]); + } + } + const b: Point[] = []; + const s = 1 - curveTightness; + out.push(clone(points[0])); + for (let i = 1; (i + 2) < points.length; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + out.push(b[1], b[2], b[3]); + } + } + return out; +} \ No newline at end of file diff --git a/node_modules/points-on-curve/src/index.ts b/node_modules/points-on-curve/src/index.ts new file mode 100644 index 0000000..f523f55 --- /dev/null +++ b/node_modules/points-on-curve/src/index.ts @@ -0,0 +1,137 @@ +export type Point = [number, number]; + +// distance between 2 points +function distance(p1: Point, p2: Point): number { + return Math.sqrt(distanceSq(p1, p2)); +} + +// distance between 2 points squared +function distanceSq(p1: Point, p2: Point): number { + return Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2); +} + +// Sistance squared from a point p to the line segment vw +function distanceToSegmentSq(p: Point, v: Point, w: Point): number { + const l2 = distanceSq(v, w); + if (l2 === 0) { + return distanceSq(p, v); + } + let t = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2; + t = Math.max(0, Math.min(1, t)); + return distanceSq(p, lerp(v, w, t)); +} + +function lerp(a: Point, b: Point, t: number): Point { + return [ + a[0] + (b[0] - a[0]) * t, + a[1] + (b[1] - a[1]) * t, + ]; +} + +// Adapted from https://seant23.wordpress.com/2010/11/12/offset-bezier-curves/ +function flatness(points: Point[], offset: number): number { + const p1 = points[offset + 0]; + const p2 = points[offset + 1]; + const p3 = points[offset + 2]; + const p4 = points[offset + 3]; + + let ux = 3 * p2[0] - 2 * p1[0] - p4[0]; ux *= ux; + let uy = 3 * p2[1] - 2 * p1[1] - p4[1]; uy *= uy; + let vx = 3 * p3[0] - 2 * p4[0] - p1[0]; vx *= vx; + let vy = 3 * p3[1] - 2 * p4[1] - p1[1]; vy *= vy; + + if (ux < vx) { + ux = vx; + } + + if (uy < vy) { + uy = vy; + } + + return ux + uy; +} + +function getPointsOnBezierCurveWithSplitting(points: Point[], offset: number, tolerance: number, newPoints?: Point[]): Point[] { + const outPoints = newPoints || []; + if (flatness(points, offset) < tolerance) { + const p0 = points[offset + 0]; + if (outPoints.length) { + const d = distance(outPoints[outPoints.length - 1], p0); + if (d > 1) { + outPoints.push(p0); + } + } else { + outPoints.push(p0); + } + outPoints.push(points[offset + 3]); + } else { + // subdivide + const t = .5; + const p1 = points[offset + 0]; + const p2 = points[offset + 1]; + const p3 = points[offset + 2]; + const p4 = points[offset + 3]; + + const q1 = lerp(p1, p2, t); + const q2 = lerp(p2, p3, t); + const q3 = lerp(p3, p4, t); + + const r1 = lerp(q1, q2, t); + const r2 = lerp(q2, q3, t); + + const red = lerp(r1, r2, t); + + getPointsOnBezierCurveWithSplitting([p1, q1, r1, red], 0, tolerance, outPoints); + getPointsOnBezierCurveWithSplitting([red, r2, q3, p4], 0, tolerance, outPoints); + } + return outPoints; +} + +export function simplify(points: Point[], distance: number): Point[] { + return simplifyPoints(points, 0, points.length, distance); +} + +// Ramer–Douglas–Peucker algorithm +// https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm +function simplifyPoints(points: Point[], start: number, end: number, epsilon: number, newPoints?: Point[]): Point[] { + const outPoints = newPoints || []; + + // find the most distance point from the endpoints + const s = points[start]; + const e = points[end - 1]; + let maxDistSq = 0; + let maxNdx = 1; + for (let i = start + 1; i < end - 1; ++i) { + const distSq = distanceToSegmentSq(points[i], s, e); + if (distSq > maxDistSq) { + maxDistSq = distSq; + maxNdx = i; + } + } + + // if that point is too far, split + if (Math.sqrt(maxDistSq) > epsilon) { + simplifyPoints(points, start, maxNdx + 1, epsilon, outPoints); + simplifyPoints(points, maxNdx, end, epsilon, outPoints); + } else { + if (!outPoints.length) { + outPoints.push(s); + } + outPoints.push(e); + } + + return outPoints; +} + +export function pointsOnBezierCurves(points: Point[], tolerance: number = 0.15, distance?: number): Point[] { + const newPoints: Point[] = []; + const numSegments = (points.length - 1) / 3; + for (let i = 0; i < numSegments; i++) { + const offset = i * 3; + getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints); + } + if (distance && distance > 0) { + return simplifyPoints(newPoints, 0, newPoints.length, distance); + } + return newPoints; +} \ No newline at end of file diff --git a/node_modules/points-on-curve/tsconfig.json b/node_modules/points-on-curve/tsconfig.json new file mode 100644 index 0000000..68d5331 --- /dev/null +++ b/node_modules/points-on-curve/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "es2015", + "moduleResolution": "node", + "lib": [ + "es2017" + ], + "declaration": true, + "outDir": "./lib", + "baseUrl": ".", + "strict": true, + "strictNullChecks": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/node_modules/points-on-curve/tslint.json b/node_modules/points-on-curve/tslint.json new file mode 100644 index 0000000..c8e9cb2 --- /dev/null +++ b/node_modules/points-on-curve/tslint.json @@ -0,0 +1,61 @@ +{ + "rules": { + "arrow-parens": true, + "class-name": true, + "indent": [ + true, + "spaces", + 2 + ], + "prefer-const": true, + "no-duplicate-variable": true, + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": false, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "single", + "avoid-escape" + ], + "semicolon": [ + true, + "always" + ], + "trailing-comma": [ + true, + "multiline" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/node_modules/points-on-path/LICENSE b/node_modules/points-on-path/LICENSE new file mode 100644 index 0000000..4fa9f9b --- /dev/null +++ b/node_modules/points-on-path/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Preet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/points-on-path/README.md b/node_modules/points-on-path/README.md new file mode 100644 index 0000000..ca62737 --- /dev/null +++ b/node_modules/points-on-path/README.md @@ -0,0 +1,55 @@ +# points-on-path + +This package calculate the points on a SVG Path with a certain tolerance. It can also simplify the shape to use fewer points. +This can really usefule when estimating lines/polygons for paths in WebGL or for Hit/Cosllision detections. + +This package essentially combines packages [path-data-parser](https://github.com/pshihn/path-data-parser) and [points-on-curve](https://github.com/pshihn/bezier-points) + +## Install + +From npm + +``` +npm install --save points-on-path +``` + +The package is distributed as an ES6 module. + +## Usage + +### pointsOnPath(path: string, tolerance?: number, distance?: number): PathPoints + +Pass in a SVG path string and get back a `PathPoints` object. A `PathPoints` gives you a list of points (each being a an array of 2 numbers `[x, y]`), and a flag telling you if the path is actually composed of multiple disconnected paths. + +```javascript +PathPoints { + points: Point[]; + continuous: boolean; +} +``` + +Take this path for example: + +![points on path](https://user-images.githubusercontent.com/833927/79054782-ba8d0300-7bfc-11ea-8f16-ed36001c56c9.png) + +and estimate the points on the path + +```javascript +import { pointsOnPath } from 'points-on-path'; + +const points = pointsOnPath('M240,100c50,0,0,125,50,100s0,-125,50,-150s175,50,50,100s-175,50,-300,0s0,-125,50,-100s0,125,50,150s0,-100,50,-100'); +// plotPoints(points); +``` + +![points on path](https://user-images.githubusercontent.com/833927/79054650-8d8c2080-7bfb-11ea-93cf-2c070dfe63c5.png) + +The method also accepts two optional values `tolerance` and `distance`. These are described by [points-on-curve](https://github.com/pshihn/bezier-points); to estimate more tolerant and fewer points. + + +![points on path](https://user-images.githubusercontent.com/833927/79054652-8e24b700-7bfb-11ea-8ff8-68dce51a3940.png) + +![points on path](https://user-images.githubusercontent.com/833927/79054653-8ebd4d80-7bfb-11ea-8645-a5a0ed81cf84.png) + +## License +[MIT License](https://github.com/pshihn/points-on-path/blob/master/LICENSE) + diff --git a/node_modules/points-on-path/lib/index.d.ts b/node_modules/points-on-path/lib/index.d.ts new file mode 100644 index 0000000..7f6472d --- /dev/null +++ b/node_modules/points-on-path/lib/index.d.ts @@ -0,0 +1,3 @@ +import { Point } from 'points-on-curve'; +export { Point } from 'points-on-curve'; +export declare function pointsOnPath(path: string, tolerance?: number, distance?: number): Point[][]; diff --git a/node_modules/points-on-path/lib/index.js b/node_modules/points-on-path/lib/index.js new file mode 100644 index 0000000..18cc9ce --- /dev/null +++ b/node_modules/points-on-path/lib/index.js @@ -0,0 +1,61 @@ +import { pointsOnBezierCurves, simplify } from 'points-on-curve'; +import { parsePath, absolutize, normalize } from 'path-data-parser'; +export function pointsOnPath(path, tolerance, distance) { + const segments = parsePath(path); + const normalized = normalize(absolutize(segments)); + const sets = []; + let currentPoints = []; + let start = [0, 0]; + let pendingCurve = []; + const appendPendingCurve = () => { + if (pendingCurve.length >= 4) { + currentPoints.push(...pointsOnBezierCurves(pendingCurve, tolerance)); + } + pendingCurve = []; + }; + const appendPendingPoints = () => { + appendPendingCurve(); + if (currentPoints.length) { + sets.push(currentPoints); + currentPoints = []; + } + }; + for (const { key, data } of normalized) { + switch (key) { + case 'M': + appendPendingPoints(); + start = [data[0], data[1]]; + currentPoints.push(start); + break; + case 'L': + appendPendingCurve(); + currentPoints.push([data[0], data[1]]); + break; + case 'C': + if (!pendingCurve.length) { + const lastPoint = currentPoints.length ? currentPoints[currentPoints.length - 1] : start; + pendingCurve.push([lastPoint[0], lastPoint[1]]); + } + pendingCurve.push([data[0], data[1]]); + pendingCurve.push([data[2], data[3]]); + pendingCurve.push([data[4], data[5]]); + break; + case 'Z': + appendPendingCurve(); + currentPoints.push([start[0], start[1]]); + break; + } + } + appendPendingPoints(); + if (!distance) { + return sets; + } + const out = []; + for (const set of sets) { + const simplifiedSet = simplify(set, distance); + if (simplifiedSet.length) { + out.push(simplifiedSet); + } + } + return out; +} diff --git a/node_modules/points-on-path/package.json b/node_modules/points-on-path/package.json new file mode 100644 index 0000000..854e94a --- /dev/null +++ b/node_modules/points-on-path/package.json @@ -0,0 +1,62 @@ +{ + "_from": "points-on-path@^0.2.1", + "_id": "points-on-path@0.2.1", + "_inBundle": false, + "_integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "_location": "/points-on-path", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "points-on-path@^0.2.1", + "name": "points-on-path", + "escapedName": "points-on-path", + "rawSpec": "^0.2.1", + "saveSpec": null, + "fetchSpec": "^0.2.1" + }, + "_requiredBy": [ + "/roughjs" + ], + "_resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "_shasum": "553202b5424c53bed37135b318858eacff85dd52", + "_spec": "points-on-path@^0.2.1", + "_where": "/Users/1009738/PracticalVision/node_modules/roughjs", + "author": { + "name": "Preet Shihn" + }, + "bugs": { + "url": "https://github.com/pshihn/points-on-path/issues" + }, + "bundleDependencies": false, + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + }, + "deprecated": false, + "description": "Estimate points on a SVG path", + "devDependencies": { + "tslint": "^6.1.1", + "typescript": "^3.8.3" + }, + "homepage": "https://github.com/pshihn/points-on-path#readme", + "keywords": [ + "SVG", + "graphics" + ], + "license": "MIT", + "main": "lib/index.js", + "module": "lib/index.js", + "name": "points-on-path", + "repository": { + "type": "git", + "url": "git+https://github.com/pshihn/points-on-path.git" + }, + "scripts": { + "build": "rm -rf lib && tsc", + "lint": "tslint -p tsconfig.json", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "types": "lib/index.d.ts", + "version": "0.2.1" +} diff --git a/node_modules/points-on-path/src/index.ts b/node_modules/points-on-path/src/index.ts new file mode 100644 index 0000000..5dc0800 --- /dev/null +++ b/node_modules/points-on-path/src/index.ts @@ -0,0 +1,70 @@ +import { Point, pointsOnBezierCurves, simplify } from 'points-on-curve'; +import { parsePath, absolutize, normalize } from 'path-data-parser'; + +export { Point } from 'points-on-curve'; + +export function pointsOnPath(path: string, tolerance?: number, distance?: number): Point[][] { + const segments = parsePath(path); + const normalized = normalize(absolutize(segments)); + + const sets: Point[][] = []; + let currentPoints: Point[] = []; + let start: Point = [0, 0]; + let pendingCurve: Point[] = []; + + const appendPendingCurve = () => { + if (pendingCurve.length >= 4) { + currentPoints.push(...pointsOnBezierCurves(pendingCurve, tolerance)); + } + pendingCurve = []; + }; + + const appendPendingPoints = () => { + appendPendingCurve(); + if (currentPoints.length) { + sets.push(currentPoints); + currentPoints = []; + } + }; + + for (const { key, data } of normalized) { + switch (key) { + case 'M': + appendPendingPoints(); + start = [data[0], data[1]]; + currentPoints.push(start); + break; + case 'L': + appendPendingCurve(); + currentPoints.push([data[0], data[1]]); + break; + case 'C': + if (!pendingCurve.length) { + const lastPoint = currentPoints.length ? currentPoints[currentPoints.length - 1] : start; + pendingCurve.push([lastPoint[0], lastPoint[1]]); + } + pendingCurve.push([data[0], data[1]]); + pendingCurve.push([data[2], data[3]]); + pendingCurve.push([data[4], data[5]]); + break; + case 'Z': + appendPendingCurve(); + currentPoints.push([start[0], start[1]]); + break; + } + } + appendPendingPoints(); + + if (!distance) { + return sets; + } + + const out: Point[][] = []; + for (const set of sets) { + const simplifiedSet = simplify(set, distance); + if (simplifiedSet.length) { + out.push(simplifiedSet); + } + } + return out; +} \ No newline at end of file diff --git a/node_modules/points-on-path/tsconfig.json b/node_modules/points-on-path/tsconfig.json new file mode 100644 index 0000000..68d5331 --- /dev/null +++ b/node_modules/points-on-path/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "es2015", + "moduleResolution": "node", + "lib": [ + "es2017" + ], + "declaration": true, + "outDir": "./lib", + "baseUrl": ".", + "strict": true, + "strictNullChecks": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/node_modules/points-on-path/tslint.json b/node_modules/points-on-path/tslint.json new file mode 100644 index 0000000..c8e9cb2 --- /dev/null +++ b/node_modules/points-on-path/tslint.json @@ -0,0 +1,61 @@ +{ + "rules": { + "arrow-parens": true, + "class-name": true, + "indent": [ + true, + "spaces", + 2 + ], + "prefer-const": true, + "no-duplicate-variable": true, + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": false, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "single", + "avoid-escape" + ], + "semicolon": [ + true, + "always" + ], + "trailing-comma": [ + true, + "multiline" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/node_modules/roughjs/CHANGELOG.md b/node_modules/roughjs/CHANGELOG.md new file mode 100644 index 0000000..2677ba6 --- /dev/null +++ b/node_modules/roughjs/CHANGELOG.md @@ -0,0 +1,33 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +## [4.3.0] - 2020-05-11 + +* Added options to draw dashed lines - *strokeLineDash, strokeLineDashOffset, fillLineDash, fillLineDashOffset* +* Added option to disable double stroking effect - *disableMultiStroke, disableMultiStrokeFill*. +* Bug fixes to solid fill in SVG which was not obeying evenodd rules by default + +## [4.1.0] - 2020-01-13 + +* Added ability to **fill** non-svg curves + +## [4.0.0] - 2020-01-13 + +* Add optional seeding for randomness to ensure shapes generated with same arguments result in same vectors +* Implemented a new algorithm for hachure generation based on scanlines. Smaller in code size, and about 20% faster +* Algorithm update - adjust shape randomness and curve-step-counts based on the size of the shape +* Removed async/worker builds - can be achieved in the app level, so no need to be in the lib +* Support no-stroke sketching. `stroke: "none"` will not generate outline vectors anymore +* Removed `sunburst` fill style - it had a lot of corner cases where it did not work, and not very popular. + +## [3.1.0] - 2019-03-14 + +* Added three new fill styles: **sunburst**, **dashed**, and **zigzag-line** +* Added three new properties in *Options* to support these fill styles: +* **dashOffset** - length of dashes in dashed fill +* **dashGap** - length of gap between dashes in dashed fill +* **zigzagOffset** - width of zigzag triangle when using zigzag-lines fill + + + diff --git a/node_modules/roughjs/LICENSE b/node_modules/roughjs/LICENSE new file mode 100644 index 0000000..8861089 --- /dev/null +++ b/node_modules/roughjs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Preet Shihn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/roughjs/README.md b/node_modules/roughjs/README.md new file mode 100644 index 0000000..7308bc8 --- /dev/null +++ b/node_modules/roughjs/README.md @@ -0,0 +1,127 @@ +# Rough.js + +Rough.js is a small (\<9 kB) graphics library that lets you draw in a _sketchy_, _hand-drawn-like_, style. +The library defines primitives to draw lines, curves, arcs, polygons, circles, and ellipses. It also supports drawing [SVG paths](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths). + +Rough.js works with both [Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) and [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG). + +![Rough.js sample](https://roughjs.com/images/cap_demo.png) + +[@RoughLib](https://twitter.com/RoughLib) on Twitter. + +## Install + +from npm: + +``` +npm install --save roughjs +``` + +Or get the latest using unpkg: https://unpkg.com/roughjs@latest/bundled/rough.js + + +If you are looking for bundled version in different formats, the npm package will have these in the following locations: + +CommonJS: `roughjs/bundled/rough.cjs.js` + +ESM: `roughjs/bundled/rough.esm.js` + +Browser IIFE: `roughjs/bundled/rough.js` + + +## Usage + +![Rough.js rectangle](https://roughjs.com/images/m1.png) + +```js +const rc = rough.canvas(document.getElementById('canvas')); +rc.rectangle(10, 10, 200, 200); // x, y, width, height +``` + +or SVG + +```js +const rc = rough.svg(svg); +let node = rc.rectangle(10, 10, 200, 200); // x, y, width, height +svg.appendChild(node); +``` + +### Lines and Ellipses + +![Rough.js rectangle](https://roughjs.com/images/m2.png) + +```js +rc.circle(80, 120, 50); // centerX, centerY, diameter +rc.ellipse(300, 100, 150, 80); // centerX, centerY, width, height +rc.line(80, 120, 300, 100); // x1, y1, x2, y2 +``` + +### Filling + +![Rough.js rectangle](https://roughjs.com/images/m3.png) + +```js +rc.circle(50, 50, 80, { fill: 'red' }); // fill with red hachure +rc.rectangle(120, 15, 80, 80, { fill: 'red' }); +rc.circle(50, 150, 80, { + fill: "rgb(10,150,10)", + fillWeight: 3 // thicker lines for hachure +}); +rc.rectangle(220, 15, 80, 80, { + fill: 'red', + hachureAngle: 60, // angle of hachure, + hachureGap: 8 +}); +rc.rectangle(120, 105, 80, 80, { + fill: 'rgba(255,0,200,0.2)', + fillStyle: 'solid' // solid fill +}); +``` + +Fill styles can be: **hachure**(default), **solid**, **zigzag**, **cross-hatch**, **dots**, **dashed**, or **zigzag-line** + +![Rough.js fill examples](https://roughjs.com/images/m14.png) + +### Sketching style + +![Rough.js rectangle](https://roughjs.com/images/m4.png) + +```js +rc.rectangle(15, 15, 80, 80, { roughness: 0.5, fill: 'red' }); +rc.rectangle(120, 15, 80, 80, { roughness: 2.8, fill: 'blue' }); +rc.rectangle(220, 15, 80, 80, { bowing: 6, stroke: 'green', strokeWidth: 3 }); +``` + +### SVG Paths + +![Rough.js paths](https://roughjs.com/images/m5.png) + +```js +rc.path('M80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 Z', { fill: 'green' }); +rc.path('M230 80 A 45 45, 0, 1, 0, 275 125 L 275 80 Z', { fill: 'purple' }); +rc.path('M80 230 A 45 45, 0, 0, 1, 125 275 L 125 230 Z', { fill: 'red' }); +rc.path('M230 230 A 45 45, 0, 1, 1, 275 275 L 275 230 Z', { fill: 'blue' }); +``` + +SVG Path with simplification: + +![Rough.js texas map](https://roughjs.com/images/m9.png) ![Rough.js texas map](https://roughjs.com/images/m10.png) + +## Examples + +![Rough.js US map](https://roughjs.com/images/m6.png) + +[View examples here](https://github.com/pshihn/rough/wiki/Examples) + +## API & Documentation + +[Full Rough.js API](https://github.com/pshihn/rough/wiki) + +## Credits + +Some of the core algorithms were adapted from [handy](https://www.gicentre.net/software/#/handy/) processing lib. + +Algorithm to convert SVG arcs to Canvas [described here](https://www.w3.org/TR/SVG/implnote.html) was adapted from [Mozilla codebase](https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887) + +## License +[MIT License](https://github.com/pshihn/rough/blob/master/LICENSE) (c) [Preet Shihn](https://twitter.com/preetster) diff --git a/node_modules/roughjs/bin/canvas.d.ts b/node_modules/roughjs/bin/canvas.d.ts new file mode 100644 index 0000000..6221c6c --- /dev/null +++ b/node_modules/roughjs/bin/canvas.d.ts @@ -0,0 +1,23 @@ +import { Config, Options, ResolvedOptions, Drawable } from './core'; +import { RoughGenerator } from './generator'; +import { Point } from './geometry'; +export declare class RoughCanvas { + private gen; + private canvas; + private ctx; + constructor(canvas: HTMLCanvasElement, config?: Config); + draw(drawable: Drawable): void; + private fillSketch; + private _drawToContext; + get generator(): RoughGenerator; + getDefaultOptions(): ResolvedOptions; + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Drawable; + rectangle(x: number, y: number, width: number, height: number, options?: Options): Drawable; + ellipse(x: number, y: number, width: number, height: number, options?: Options): Drawable; + circle(x: number, y: number, diameter: number, options?: Options): Drawable; + linearPath(points: Point[], options?: Options): Drawable; + polygon(points: Point[], options?: Options): Drawable; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed?: boolean, options?: Options): Drawable; + curve(points: Point[], options?: Options): Drawable; + path(d: string, options?: Options): Drawable; +} diff --git a/node_modules/roughjs/bin/canvas.js b/node_modules/roughjs/bin/canvas.js new file mode 100644 index 0000000..f59363c --- /dev/null +++ b/node_modules/roughjs/bin/canvas.js @@ -0,0 +1,131 @@ +import { RoughGenerator } from './generator'; +export class RoughCanvas { + constructor(canvas, config) { + this.canvas = canvas; + this.ctx = this.canvas.getContext('2d'); + this.gen = new RoughGenerator(config); + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.getDefaultOptions(); + const ctx = this.ctx; + for (const drawing of sets) { + switch (drawing.type) { + case 'path': + ctx.save(); + ctx.strokeStyle = o.stroke === 'none' ? 'transparent' : o.stroke; + ctx.lineWidth = o.strokeWidth; + if (o.strokeLineDash) { + ctx.setLineDash(o.strokeLineDash); + } + if (o.strokeLineDashOffset) { + ctx.lineDashOffset = o.strokeLineDashOffset; + } + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillPath': + ctx.save(); + ctx.fillStyle = o.fill || ''; + const fillRule = (drawable.shape === 'curve' || drawable.shape === 'polygon') ? 'evenodd' : 'nonzero'; + this._drawToContext(ctx, drawing, fillRule); + ctx.restore(); + break; + case 'fillSketch': + this.fillSketch(ctx, drawing, o); + break; + } + } + } + fillSketch(ctx, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + ctx.save(); + if (o.fillLineDash) { + ctx.setLineDash(o.fillLineDash); + } + if (o.fillLineDashOffset) { + ctx.lineDashOffset = o.fillLineDashOffset; + } + ctx.strokeStyle = o.fill || ''; + ctx.lineWidth = fweight; + this._drawToContext(ctx, drawing); + ctx.restore(); + } + _drawToContext(ctx, drawing, rule = 'nonzero') { + ctx.beginPath(); + for (const item of drawing.ops) { + const data = item.data; + switch (item.op) { + case 'move': + ctx.moveTo(data[0], data[1]); + break; + case 'bcurveTo': + ctx.bezierCurveTo(data[0], data[1], data[2], data[3], data[4], data[5]); + break; + case 'lineTo': + ctx.lineTo(data[0], data[1]); + break; + } + } + if (drawing.type === 'fillPath') { + ctx.fill(rule); + } + else { + ctx.stroke(); + } + } + get generator() { + return this.gen; + } + getDefaultOptions() { + return this.gen.defaultOptions; + } + line(x1, y1, x2, y2, options) { + const d = this.gen.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + rectangle(x, y, width, height, options) { + const d = this.gen.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + ellipse(x, y, width, height, options) { + const d = this.gen.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + circle(x, y, diameter, options) { + const d = this.gen.circle(x, y, diameter, options); + this.draw(d); + return d; + } + linearPath(points, options) { + const d = this.gen.linearPath(points, options); + this.draw(d); + return d; + } + polygon(points, options) { + const d = this.gen.polygon(points, options); + this.draw(d); + return d; + } + arc(x, y, width, height, start, stop, closed = false, options) { + const d = this.gen.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + curve(points, options) { + const d = this.gen.curve(points, options); + this.draw(d); + return d; + } + path(d, options) { + const drawing = this.gen.path(d, options); + this.draw(drawing); + return drawing; + } +} diff --git a/node_modules/roughjs/bin/core.d.ts b/node_modules/roughjs/bin/core.d.ts new file mode 100644 index 0000000..96deca7 --- /dev/null +++ b/node_modules/roughjs/bin/core.d.ts @@ -0,0 +1,82 @@ +import { Point } from './geometry'; +import { Random } from './math'; +export declare const SVGNS = "http://www.w3.org/2000/svg"; +export interface Config { + options?: Options; +} +export interface DrawingSurface { + width: number | SVGAnimatedLength; + height: number | SVGAnimatedLength; +} +export interface Options { + maxRandomnessOffset?: number; + roughness?: number; + bowing?: number; + stroke?: string; + strokeWidth?: number; + curveFitting?: number; + curveTightness?: number; + curveStepCount?: number; + fill?: string; + fillStyle?: string; + fillWeight?: number; + hachureAngle?: number; + hachureGap?: number; + simplification?: number; + dashOffset?: number; + dashGap?: number; + zigzagOffset?: number; + seed?: number; + combineNestedSvgPaths?: boolean; + strokeLineDash?: number[]; + strokeLineDashOffset?: number; + fillLineDash?: number[]; + fillLineDashOffset?: number; + disableMultiStroke?: boolean; + disableMultiStrokeFill?: boolean; +} +export interface ResolvedOptions extends Options { + maxRandomnessOffset: number; + roughness: number; + bowing: number; + stroke: string; + strokeWidth: number; + curveFitting: number; + curveTightness: number; + curveStepCount: number; + fillStyle: string; + fillWeight: number; + hachureAngle: number; + hachureGap: number; + dashOffset: number; + dashGap: number; + zigzagOffset: number; + seed: number; + combineNestedSvgPaths: boolean; + randomizer?: Random; + disableMultiStroke: boolean; + disableMultiStrokeFill: boolean; +} +export declare type OpType = 'move' | 'bcurveTo' | 'lineTo'; +export declare type OpSetType = 'path' | 'fillPath' | 'fillSketch'; +export interface Op { + op: OpType; + data: number[]; +} +export interface OpSet { + type: OpSetType; + ops: Op[]; + size?: Point; + path?: string; +} +export interface Drawable { + shape: string; + options: ResolvedOptions; + sets: OpSet[]; +} +export interface PathInfo { + d: string; + stroke: string; + strokeWidth: number; + fill?: string; +} diff --git a/node_modules/roughjs/bin/core.js b/node_modules/roughjs/bin/core.js new file mode 100644 index 0000000..0d14519 --- /dev/null +++ b/node_modules/roughjs/bin/core.js @@ -0,0 +1 @@ +export const SVGNS = 'http://www.w3.org/2000/svg'; diff --git a/node_modules/roughjs/bin/fillers/dashed-filler.d.ts b/node_modules/roughjs/bin/fillers/dashed-filler.d.ts new file mode 100644 index 0000000..df8058e --- /dev/null +++ b/node_modules/roughjs/bin/fillers/dashed-filler.d.ts @@ -0,0 +1,9 @@ +import { PatternFiller, RenderHelper } from './filler-interface'; +import { ResolvedOptions, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class DashedFiller implements PatternFiller { + private helper; + constructor(helper: RenderHelper); + fillPolygon(points: Point[], o: ResolvedOptions): OpSet; + private dashedLine; +} diff --git a/node_modules/roughjs/bin/fillers/dashed-filler.js b/node_modules/roughjs/bin/fillers/dashed-filler.js new file mode 100644 index 0000000..5d746e3 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/dashed-filler.js @@ -0,0 +1,36 @@ +import { lineLength } from '../geometry'; +import { polygonHachureLines } from './scan-line-hachure'; +export class DashedFiller { + constructor(helper) { + this.helper = helper; + } + fillPolygon(points, o) { + const lines = polygonHachureLines(points, o); + return { type: 'fillSketch', ops: this.dashedLine(lines, o) }; + } + dashedLine(lines, o) { + const offset = o.dashOffset < 0 ? (o.hachureGap < 0 ? (o.strokeWidth * 4) : o.hachureGap) : o.dashOffset; + const gap = o.dashGap < 0 ? (o.hachureGap < 0 ? (o.strokeWidth * 4) : o.hachureGap) : o.dashGap; + const ops = []; + lines.forEach((line) => { + const length = lineLength(line); + const count = Math.floor(length / (offset + gap)); + const startOffset = (length + gap - (count * (offset + gap))) / 2; + let p1 = line[0]; + let p2 = line[1]; + if (p1[0] > p2[0]) { + p1 = line[1]; + p2 = line[0]; + } + const alpha = Math.atan((p2[1] - p1[1]) / (p2[0] - p1[0])); + for (let i = 0; i < count; i++) { + const lstart = i * (offset + gap); + const lend = lstart + offset; + const start = [p1[0] + (lstart * Math.cos(alpha)) + (startOffset * Math.cos(alpha)), p1[1] + lstart * Math.sin(alpha) + (startOffset * Math.sin(alpha))]; + const end = [p1[0] + (lend * Math.cos(alpha)) + (startOffset * Math.cos(alpha)), p1[1] + (lend * Math.sin(alpha)) + (startOffset * Math.sin(alpha))]; + ops.push(...this.helper.doubleLineOps(start[0], start[1], end[0], end[1], o)); + } + }); + return ops; + } +} diff --git a/node_modules/roughjs/bin/fillers/dot-filler.d.ts b/node_modules/roughjs/bin/fillers/dot-filler.d.ts new file mode 100644 index 0000000..e682378 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/dot-filler.d.ts @@ -0,0 +1,9 @@ +import { PatternFiller, RenderHelper } from './filler-interface'; +import { ResolvedOptions, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class DotFiller implements PatternFiller { + private helper; + constructor(helper: RenderHelper); + fillPolygon(points: Point[], o: ResolvedOptions): OpSet; + private dotsOnLines; +} diff --git a/node_modules/roughjs/bin/fillers/dot-filler.js b/node_modules/roughjs/bin/fillers/dot-filler.js new file mode 100644 index 0000000..e1ccbae --- /dev/null +++ b/node_modules/roughjs/bin/fillers/dot-filler.js @@ -0,0 +1,41 @@ +import { lineLength } from '../geometry'; +import { polygonHachureLines } from './scan-line-hachure'; +export class DotFiller { + constructor(helper) { + this.helper = helper; + } + fillPolygon(points, o) { + o = Object.assign({}, o, { curveStepCount: 4, hachureAngle: 0, roughness: 1 }); + const lines = polygonHachureLines(points, o); + return this.dotsOnLines(lines, o); + } + dotsOnLines(lines, o) { + const ops = []; + let gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const ro = gap / 4; + for (const line of lines) { + const length = lineLength(line); + const dl = length / gap; + const count = Math.ceil(dl) - 1; + const offset = length - (count * gap); + const x = ((line[0][0] + line[1][0]) / 2) - (gap / 4); + const minY = Math.min(line[0][1], line[1][1]); + for (let i = 0; i < count; i++) { + const y = minY + offset + (i * gap); + const cx = this.helper.randOffsetWithRange(x - ro, x + ro, o); + const cy = this.helper.randOffsetWithRange(y - ro, y + ro, o); + const el = this.helper.ellipse(cx, cy, fweight, fweight, o); + ops.push(...el.ops); + } + } + return { type: 'fillSketch', ops }; + } +} diff --git a/node_modules/roughjs/bin/fillers/filler-interface.d.ts b/node_modules/roughjs/bin/fillers/filler-interface.d.ts new file mode 100644 index 0000000..9499c04 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/filler-interface.d.ts @@ -0,0 +1,11 @@ +import { ResolvedOptions, OpSet, Op } from '../core'; +import { Point } from '../geometry'; +export interface PatternFiller { + fillPolygon(points: Point[], o: ResolvedOptions): OpSet; +} +export interface RenderHelper { + randOffset(x: number, o: ResolvedOptions): number; + randOffsetWithRange(min: number, max: number, o: ResolvedOptions): number; + ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; + doubleLineOps(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[]; +} diff --git a/node_modules/roughjs/bin/fillers/filler-interface.js b/node_modules/roughjs/bin/fillers/filler-interface.js new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/roughjs/bin/fillers/filler.d.ts b/node_modules/roughjs/bin/fillers/filler.d.ts new file mode 100644 index 0000000..5df5ab3 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/filler.d.ts @@ -0,0 +1,3 @@ +import { ResolvedOptions } from '../core'; +import { PatternFiller, RenderHelper } from './filler-interface'; +export declare function getFiller(o: ResolvedOptions, helper: RenderHelper): PatternFiller; diff --git a/node_modules/roughjs/bin/fillers/filler.js b/node_modules/roughjs/bin/fillers/filler.js new file mode 100644 index 0000000..3b7dd75 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/filler.js @@ -0,0 +1,47 @@ +import { HachureFiller } from './hachure-filler'; +import { ZigZagFiller } from './zigzag-filler'; +import { HatchFiller } from './hatch-filler'; +import { DotFiller } from './dot-filler'; +import { DashedFiller } from './dashed-filler'; +import { ZigZagLineFiller } from './zigzag-line-filler'; +const fillers = {}; +export function getFiller(o, helper) { + let fillerName = o.fillStyle || 'hachure'; + if (!fillers[fillerName]) { + switch (fillerName) { + case 'zigzag': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagFiller(helper); + } + break; + case 'cross-hatch': + if (!fillers[fillerName]) { + fillers[fillerName] = new HatchFiller(helper); + } + break; + case 'dots': + if (!fillers[fillerName]) { + fillers[fillerName] = new DotFiller(helper); + } + break; + case 'dashed': + if (!fillers[fillerName]) { + fillers[fillerName] = new DashedFiller(helper); + } + break; + case 'zigzag-line': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagLineFiller(helper); + } + break; + case 'hachure': + default: + fillerName = 'hachure'; + if (!fillers[fillerName]) { + fillers[fillerName] = new HachureFiller(helper); + } + break; + } + } + return fillers[fillerName]; +} diff --git a/node_modules/roughjs/bin/fillers/hachure-filler.d.ts b/node_modules/roughjs/bin/fillers/hachure-filler.d.ts new file mode 100644 index 0000000..f7be3fb --- /dev/null +++ b/node_modules/roughjs/bin/fillers/hachure-filler.d.ts @@ -0,0 +1,13 @@ +import { PatternFiller, RenderHelper } from './filler-interface'; +import { ResolvedOptions, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class HachureFiller implements PatternFiller { + private helper; + constructor(helper: RenderHelper); + fillPolygon(points: Point[], o: ResolvedOptions): OpSet; + protected _fillPolygon(points: Point[], o: ResolvedOptions, connectEnds?: boolean): OpSet; + private renderLines; + private connectingLines; + private midPointInPolygon; + private splitOnIntersections; +} diff --git a/node_modules/roughjs/bin/fillers/hachure-filler.js b/node_modules/roughjs/bin/fillers/hachure-filler.js new file mode 100644 index 0000000..522f616 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/hachure-filler.js @@ -0,0 +1,100 @@ +import { lineLength, lineIntersection, doIntersect, isPointInPolygon } from '../geometry'; +import { polygonHachureLines } from './scan-line-hachure'; +export class HachureFiller { + constructor(helper) { + this.helper = helper; + } + fillPolygon(points, o) { + return this._fillPolygon(points, o); + } + _fillPolygon(points, o, connectEnds = false) { + let lines = polygonHachureLines(points, o); + if (connectEnds) { + const connectingLines = this.connectingLines(points, lines); + lines = lines.concat(connectingLines); + } + const ops = this.renderLines(lines, o); + return { type: 'fillSketch', ops }; + } + renderLines(lines, o) { + const ops = []; + for (const line of lines) { + ops.push(...this.helper.doubleLineOps(line[0][0], line[0][1], line[1][0], line[1][1], o)); + } + return ops; + } + connectingLines(polygon, lines) { + const result = []; + if (lines.length > 1) { + for (let i = 1; i < lines.length; i++) { + const prev = lines[i - 1]; + if (lineLength(prev) < 3) { + continue; + } + const current = lines[i]; + const segment = [current[0], prev[1]]; + if (lineLength(segment) > 3) { + const segSplits = this.splitOnIntersections(polygon, segment); + result.push(...segSplits); + } + } + } + return result; + } + midPointInPolygon(polygon, segment) { + return isPointInPolygon(polygon, (segment[0][0] + segment[1][0]) / 2, (segment[0][1] + segment[1][1]) / 2); + } + splitOnIntersections(polygon, segment) { + const error = Math.max(5, lineLength(segment) * 0.1); + const intersections = []; + for (let i = 0; i < polygon.length; i++) { + const p1 = polygon[i]; + const p2 = polygon[(i + 1) % polygon.length]; + if (doIntersect(p1, p2, ...segment)) { + const ip = lineIntersection(p1, p2, segment[0], segment[1]); + if (ip) { + const d0 = lineLength([ip, segment[0]]); + const d1 = lineLength([ip, segment[1]]); + if (d0 > error && d1 > error) { + intersections.push({ + point: ip, + distance: d0 + }); + } + } + } + } + if (intersections.length > 1) { + const ips = intersections.sort((a, b) => a.distance - b.distance).map((d) => d.point); + if (!isPointInPolygon(polygon, ...segment[0])) { + ips.shift(); + } + if (!isPointInPolygon(polygon, ...segment[1])) { + ips.pop(); + } + if (ips.length <= 1) { + if (this.midPointInPolygon(polygon, segment)) { + return [segment]; + } + else { + return []; + } + } + const spoints = [segment[0], ...ips, segment[1]]; + const slines = []; + for (let i = 0; i < (spoints.length - 1); i += 2) { + const subSegment = [spoints[i], spoints[i + 1]]; + if (this.midPointInPolygon(polygon, subSegment)) { + slines.push(subSegment); + } + } + return slines; + } + else if (this.midPointInPolygon(polygon, segment)) { + return [segment]; + } + else { + return []; + } + } +} diff --git a/node_modules/roughjs/bin/fillers/hatch-filler.d.ts b/node_modules/roughjs/bin/fillers/hatch-filler.d.ts new file mode 100644 index 0000000..2692088 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/hatch-filler.d.ts @@ -0,0 +1,6 @@ +import { HachureFiller } from './hachure-filler'; +import { ResolvedOptions, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class HatchFiller extends HachureFiller { + fillPolygon(points: Point[], o: ResolvedOptions): OpSet; +} diff --git a/node_modules/roughjs/bin/fillers/hatch-filler.js b/node_modules/roughjs/bin/fillers/hatch-filler.js new file mode 100644 index 0000000..16d9d89 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/hatch-filler.js @@ -0,0 +1,10 @@ +import { HachureFiller } from './hachure-filler'; +export class HatchFiller extends HachureFiller { + fillPolygon(points, o) { + const set = this._fillPolygon(points, o); + const o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + const set2 = this._fillPolygon(points, o2); + set.ops = set.ops.concat(set2.ops); + return set; + } +} diff --git a/node_modules/roughjs/bin/fillers/scan-line-hachure.d.ts b/node_modules/roughjs/bin/fillers/scan-line-hachure.d.ts new file mode 100644 index 0000000..ef91f65 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/scan-line-hachure.d.ts @@ -0,0 +1,3 @@ +import { Point, Line } from '../geometry'; +import { ResolvedOptions } from '../core'; +export declare function polygonHachureLines(points: Point[], o: ResolvedOptions): Line[]; diff --git a/node_modules/roughjs/bin/fillers/scan-line-hachure.js b/node_modules/roughjs/bin/fillers/scan-line-hachure.js new file mode 100644 index 0000000..a017085 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/scan-line-hachure.js @@ -0,0 +1,114 @@ +import { rotatePoints, rotateLines } from '../geometry'; +export function polygonHachureLines(points, o) { + const rotationCenter = [0, 0]; + const angle = Math.round(o.hachureAngle + 90); + if (angle) { + rotatePoints(points, rotationCenter, angle); + } + const lines = straightHachureLines(points, o); + if (angle) { + rotatePoints(points, rotationCenter, -angle); + rotateLines(lines, rotationCenter, -angle); + } + return lines; +} +function straightHachureLines(points, o) { + const vertices = [...points]; + if (vertices[0].join(',') !== vertices[vertices.length - 1].join(',')) { + vertices.push([vertices[0][0], vertices[0][1]]); + } + const lines = []; + if (vertices && vertices.length > 2) { + let gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + // Create sorted edges table + const edges = []; + for (let i = 0; i < vertices.length - 1; i++) { + const p1 = vertices[i]; + const p2 = vertices[i + 1]; + if (p1[1] !== p2[1]) { + const ymin = Math.min(p1[1], p2[1]); + edges.push({ + ymin, + ymax: Math.max(p1[1], p2[1]), + x: ymin === p1[1] ? p1[0] : p2[0], + islope: (p2[0] - p1[0]) / (p2[1] - p1[1]) + }); + } + } + edges.sort((e1, e2) => { + if (e1.ymin < e2.ymin) { + return -1; + } + if (e1.ymin > e2.ymin) { + return 1; + } + if (e1.x < e2.x) { + return -1; + } + if (e1.x > e2.x) { + return 1; + } + if (e1.ymax === e2.ymax) { + return 0; + } + return (e1.ymax - e2.ymax) / Math.abs((e1.ymax - e2.ymax)); + }); + if (!edges.length) { + return lines; + } + // Start scanning + let activeEdges = []; + let y = edges[0].ymin; + while (activeEdges.length || edges.length) { + if (edges.length) { + let ix = -1; + for (let i = 0; i < edges.length; i++) { + if (edges[i].ymin > y) { + break; + } + ix = i; + } + const removed = edges.splice(0, ix + 1); + removed.forEach((edge) => { + activeEdges.push({ s: y, edge }); + }); + } + activeEdges = activeEdges.filter((ae) => { + if (ae.edge.ymax <= y) { + return false; + } + return true; + }); + activeEdges.sort((ae1, ae2) => { + if (ae1.edge.x === ae2.edge.x) { + return 0; + } + return (ae1.edge.x - ae2.edge.x) / Math.abs((ae1.edge.x - ae2.edge.x)); + }); + // fill between the edges + if (activeEdges.length > 1) { + for (let i = 0; i < activeEdges.length; i = i + 2) { + const nexti = i + 1; + if (nexti >= activeEdges.length) { + break; + } + const ce = activeEdges[i].edge; + const ne = activeEdges[nexti].edge; + lines.push([ + [Math.round(ce.x), y], + [Math.round(ne.x), y] + ]); + } + } + y += gap; + activeEdges.forEach((ae) => { + ae.edge.x = ae.edge.x + (gap * ae.edge.islope); + }); + } + } + return lines; +} diff --git a/node_modules/roughjs/bin/fillers/zigzag-filler.d.ts b/node_modules/roughjs/bin/fillers/zigzag-filler.d.ts new file mode 100644 index 0000000..1e43c9e --- /dev/null +++ b/node_modules/roughjs/bin/fillers/zigzag-filler.d.ts @@ -0,0 +1,6 @@ +import { HachureFiller } from './hachure-filler'; +import { ResolvedOptions, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class ZigZagFiller extends HachureFiller { + fillPolygon(points: Point[], o: ResolvedOptions): OpSet; +} diff --git a/node_modules/roughjs/bin/fillers/zigzag-filler.js b/node_modules/roughjs/bin/fillers/zigzag-filler.js new file mode 100644 index 0000000..494d917 --- /dev/null +++ b/node_modules/roughjs/bin/fillers/zigzag-filler.js @@ -0,0 +1,6 @@ +import { HachureFiller } from './hachure-filler'; +export class ZigZagFiller extends HachureFiller { + fillPolygon(points, o) { + return this._fillPolygon(points, o, true); + } +} diff --git a/node_modules/roughjs/bin/fillers/zigzag-line-filler.d.ts b/node_modules/roughjs/bin/fillers/zigzag-line-filler.d.ts new file mode 100644 index 0000000..7f4b33a --- /dev/null +++ b/node_modules/roughjs/bin/fillers/zigzag-line-filler.d.ts @@ -0,0 +1,9 @@ +import { PatternFiller, RenderHelper } from './filler-interface'; +import { ResolvedOptions, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class ZigZagLineFiller implements PatternFiller { + private helper; + constructor(helper: RenderHelper); + fillPolygon(points: Point[], o: ResolvedOptions): OpSet; + private zigzagLines; +} diff --git a/node_modules/roughjs/bin/fillers/zigzag-line-filler.js b/node_modules/roughjs/bin/fillers/zigzag-line-filler.js new file mode 100644 index 0000000..04fd08b --- /dev/null +++ b/node_modules/roughjs/bin/fillers/zigzag-line-filler.js @@ -0,0 +1,38 @@ +import { lineLength } from '../geometry'; +import { polygonHachureLines } from './scan-line-hachure'; +export class ZigZagLineFiller { + constructor(helper) { + this.helper = helper; + } + fillPolygon(points, o) { + const gap = o.hachureGap < 0 ? (o.strokeWidth * 4) : o.hachureGap; + const zo = o.zigzagOffset < 0 ? gap : o.zigzagOffset; + o = Object.assign({}, o, { hachureGap: gap + zo }); + const lines = polygonHachureLines(points, o); + return { type: 'fillSketch', ops: this.zigzagLines(lines, zo, o) }; + } + zigzagLines(lines, zo, o) { + const ops = []; + lines.forEach((line) => { + const length = lineLength(line); + const count = Math.round(length / (2 * zo)); + let p1 = line[0]; + let p2 = line[1]; + if (p1[0] > p2[0]) { + p1 = line[1]; + p2 = line[0]; + } + const alpha = Math.atan((p2[1] - p1[1]) / (p2[0] - p1[0])); + for (let i = 0; i < count; i++) { + const lstart = i * 2 * zo; + const lend = (i + 1) * 2 * zo; + const dz = Math.sqrt(2 * Math.pow(zo, 2)); + const start = [p1[0] + (lstart * Math.cos(alpha)), p1[1] + lstart * Math.sin(alpha)]; + const end = [p1[0] + (lend * Math.cos(alpha)), p1[1] + (lend * Math.sin(alpha))]; + const middle = [start[0] + dz * Math.cos(alpha + Math.PI / 4), start[1] + dz * Math.sin(alpha + Math.PI / 4)]; + ops.push(...this.helper.doubleLineOps(start[0], start[1], middle[0], middle[1], o), ...this.helper.doubleLineOps(middle[0], middle[1], end[0], end[1], o)); + } + }); + return ops; + } +} diff --git a/node_modules/roughjs/bin/generator.d.ts b/node_modules/roughjs/bin/generator.d.ts new file mode 100644 index 0000000..fa292d7 --- /dev/null +++ b/node_modules/roughjs/bin/generator.d.ts @@ -0,0 +1,22 @@ +import { Config, Options, Drawable, OpSet, ResolvedOptions, PathInfo } from './core.js'; +import { Point } from './geometry.js'; +export declare class RoughGenerator { + private config; + defaultOptions: ResolvedOptions; + constructor(config?: Config); + static newSeed(): number; + private _o; + private _d; + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Drawable; + rectangle(x: number, y: number, width: number, height: number, options?: Options): Drawable; + ellipse(x: number, y: number, width: number, height: number, options?: Options): Drawable; + circle(x: number, y: number, diameter: number, options?: Options): Drawable; + linearPath(points: Point[], options?: Options): Drawable; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed?: boolean, options?: Options): Drawable; + curve(points: Point[], options?: Options): Drawable; + polygon(points: Point[], options?: Options): Drawable; + path(d: string, options?: Options): Drawable; + opsToPath(drawing: OpSet): string; + toPaths(drawable: Drawable): PathInfo[]; + private fillSketch; +} diff --git a/node_modules/roughjs/bin/generator.js b/node_modules/roughjs/bin/generator.js new file mode 100644 index 0000000..e2c168e --- /dev/null +++ b/node_modules/roughjs/bin/generator.js @@ -0,0 +1,259 @@ +import { line, solidFillPolygon, patternFillPolygon, rectangle, ellipseWithParams, generateEllipseParams, linearPath, arc, patternFillArc, curve, svgPath } from './renderer.js'; +import { randomSeed } from './math.js'; +import { curveToBezier } from 'points-on-curve/lib/curve-to-bezier.js'; +import { pointsOnBezierCurves } from 'points-on-curve'; +import { pointsOnPath } from 'points-on-path'; +const NOS = 'none'; +export class RoughGenerator { + constructor(config) { + this.defaultOptions = { + maxRandomnessOffset: 2, + roughness: 1, + bowing: 1, + stroke: '#000', + strokeWidth: 1, + curveTightness: 0, + curveFitting: 0.95, + curveStepCount: 9, + fillStyle: 'hachure', + fillWeight: -1, + hachureAngle: -41, + hachureGap: -1, + dashOffset: -1, + dashGap: -1, + zigzagOffset: -1, + seed: 0, + combineNestedSvgPaths: false, + disableMultiStroke: false, + disableMultiStrokeFill: false + }; + this.config = config || {}; + if (this.config.options) { + this.defaultOptions = this._o(this.config.options); + } + } + static newSeed() { + return randomSeed(); + } + _o(options) { + return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; + } + _d(shape, sets, options) { + return { shape, sets: sets || [], options: options || this.defaultOptions }; + } + line(x1, y1, x2, y2, options) { + const o = this._o(options); + return this._d('line', [line(x1, y1, x2, y2, o)], o); + } + rectangle(x, y, width, height, options) { + const o = this._o(options); + const paths = []; + const outline = rectangle(x, y, width, height, o); + if (o.fill) { + const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(solidFillPolygon(points, o)); + } + else { + paths.push(patternFillPolygon(points, o)); + } + } + if (o.stroke !== NOS) { + paths.push(outline); + } + return this._d('rectangle', paths, o); + } + ellipse(x, y, width, height, options) { + const o = this._o(options); + const paths = []; + const ellipseParams = generateEllipseParams(width, height, o); + const ellipseResponse = ellipseWithParams(x, y, o, ellipseParams); + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = ellipseWithParams(x, y, o, ellipseParams).opset; + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(patternFillPolygon(ellipseResponse.estimatedPoints, o)); + } + } + if (o.stroke !== NOS) { + paths.push(ellipseResponse.opset); + } + return this._d('ellipse', paths, o); + } + circle(x, y, diameter, options) { + const ret = this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + linearPath(points, options) { + const o = this._o(options); + return this._d('linearPath', [linearPath(points, false, o)], o); + } + arc(x, y, width, height, start, stop, closed = false, options) { + const o = this._o(options); + const paths = []; + const outline = arc(x, y, width, height, start, stop, closed, true, o); + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(patternFillArc(x, y, width, height, start, stop, o)); + } + } + if (o.stroke !== NOS) { + paths.push(outline); + } + return this._d('arc', paths, o); + } + curve(points, options) { + const o = this._o(options); + const paths = []; + const outline = curve(points, o); + if (o.fill && o.fill !== NOS && points.length >= 3) { + const bcurve = curveToBezier(points); + const polyPoints = pointsOnBezierCurves(bcurve, 10, (1 + o.roughness) / 2); + if (o.fillStyle === 'solid') { + paths.push(solidFillPolygon(polyPoints, o)); + } + else { + paths.push(patternFillPolygon(polyPoints, o)); + } + } + if (o.stroke !== NOS) { + paths.push(outline); + } + return this._d('curve', paths, o); + } + polygon(points, options) { + const o = this._o(options); + const paths = []; + const outline = linearPath(points, true, o); + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(solidFillPolygon(points, o)); + } + else { + paths.push(patternFillPolygon(points, o)); + } + } + if (o.stroke !== NOS) { + paths.push(outline); + } + return this._d('polygon', paths, o); + } + path(d, options) { + const o = this._o(options); + const paths = []; + if (!d) { + return this._d('path', paths, o); + } + d = (d || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + const hasFill = o.fill && o.fill !== 'transparent' && o.fill !== NOS; + const hasStroke = o.stroke !== NOS; + const simplified = !!(o.simplification && (o.simplification < 1)); + const distance = simplified ? (4 - 4 * (o.simplification)) : ((1 + o.roughness) / 2); + const sets = pointsOnPath(d, 1, distance); + if (hasFill) { + if (o.combineNestedSvgPaths) { + const combined = []; + sets.forEach((set) => combined.push(...set)); + if (o.fillStyle === 'solid') { + paths.push(solidFillPolygon(combined, o)); + } + else { + paths.push(patternFillPolygon(combined, o)); + } + } + else { + sets.forEach((polyPoints) => { + if (o.fillStyle === 'solid') { + paths.push(solidFillPolygon(polyPoints, o)); + } + else { + paths.push(patternFillPolygon(polyPoints, o)); + } + }); + } + } + if (hasStroke) { + if (simplified) { + sets.forEach((set) => { + paths.push(linearPath(set, false, o)); + }); + } + else { + paths.push(svgPath(d, o)); + } + } + return this._d('path', paths, o); + } + opsToPath(drawing) { + let path = ''; + for (const item of drawing.ops) { + const data = item.data; + switch (item.op) { + case 'move': + path += `M${data[0]} ${data[1]} `; + break; + case 'bcurveTo': + path += `C${data[0]} ${data[1]}, ${data[2]} ${data[3]}, ${data[4]} ${data[5]} `; + break; + case 'lineTo': + path += `L${data[0]} ${data[1]} `; + break; + } + } + return path.trim(); + } + toPaths(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.defaultOptions; + const paths = []; + for (const drawing of sets) { + let path = null; + switch (drawing.type) { + case 'path': + path = { + d: this.opsToPath(drawing), + stroke: o.stroke, + strokeWidth: o.strokeWidth, + fill: NOS + }; + break; + case 'fillPath': + path = { + d: this.opsToPath(drawing), + stroke: NOS, + strokeWidth: 0, + fill: o.fill || NOS + }; + break; + case 'fillSketch': + path = this.fillSketch(drawing, o); + break; + } + if (path) { + paths.push(path); + } + } + return paths; + } + fillSketch(drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + return { + d: this.opsToPath(drawing), + stroke: o.fill || NOS, + strokeWidth: fweight, + fill: NOS + }; + } +} diff --git a/node_modules/roughjs/bin/geometry.d.ts b/node_modules/roughjs/bin/geometry.d.ts new file mode 100644 index 0000000..43a6138 --- /dev/null +++ b/node_modules/roughjs/bin/geometry.d.ts @@ -0,0 +1,14 @@ +export declare type Point = [number, number]; +export declare type Line = [Point, Point]; +export interface Rectangle { + x: number; + y: number; + width: number; + height: number; +} +export declare function rotatePoints(points: Point[], center: Point, degrees: number): void; +export declare function rotateLines(lines: Line[], center: Point, degrees: number): void; +export declare function lineLength(line: Line): number; +export declare function lineIntersection(a: Point, b: Point, c: Point, d: Point): Point | null; +export declare function isPointInPolygon(points: Point[], x: number, y: number): boolean; +export declare function doIntersect(p1: Point, q1: Point, p2: Point, q2: Point): boolean; diff --git a/node_modules/roughjs/bin/geometry.js b/node_modules/roughjs/bin/geometry.js new file mode 100644 index 0000000..19c46e7 --- /dev/null +++ b/node_modules/roughjs/bin/geometry.js @@ -0,0 +1,100 @@ +export function rotatePoints(points, center, degrees) { + if (points && points.length) { + const [cx, cy] = center; + const angle = (Math.PI / 180) * degrees; + const cos = Math.cos(angle); + const sin = Math.sin(angle); + points.forEach((p) => { + const [x, y] = p; + p[0] = ((x - cx) * cos) - ((y - cy) * sin) + cx; + p[1] = ((x - cx) * sin) + ((y - cy) * cos) + cy; + }); + } +} +export function rotateLines(lines, center, degrees) { + const points = []; + lines.forEach((line) => points.push(...line)); + rotatePoints(points, center, degrees); +} +export function lineLength(line) { + const p1 = line[0]; + const p2 = line[1]; + return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); +} +export function lineIntersection(a, b, c, d) { + const a1 = b[1] - a[1]; + const b1 = a[0] - b[0]; + const c1 = a1 * (a[0]) + b1 * (a[1]); + const a2 = d[1] - c[1]; + const b2 = c[0] - d[0]; + const c2 = a2 * (c[0]) + b2 * (c[1]); + const determinant = a1 * b2 - a2 * b1; + return determinant ? [(b2 * c1 - b1 * c2) / determinant, (a1 * c2 - a2 * c1) / determinant] : null; +} +export function isPointInPolygon(points, x, y) { + const vertices = points.length; + // There must be at least 3 vertices in polygon + if (vertices < 3) { + return false; + } + const extreme = [Number.MAX_SAFE_INTEGER, y]; + const p = [x, y]; + let count = 0; + for (let i = 0; i < vertices; i++) { + const current = points[i]; + const next = points[(i + 1) % vertices]; + if (doIntersect(current, next, p, extreme)) { + if (orientation(current, p, next) === 0) { + return onSegment(current, p, next); + } + count++; + } + } + // true if count is off + return count % 2 === 1; +} +// Check if q lies on the line segment pr +function onSegment(p, q, r) { + return (q[0] <= Math.max(p[0], r[0]) && + q[0] >= Math.min(p[0], r[0]) && + q[1] <= Math.max(p[1], r[1]) && + q[1] >= Math.min(p[1], r[1])); +} +// For the ordered points p, q, r, return +// 0 if p, q, r are collinear +// 1 if Clockwise +// 2 if counterclickwise +function orientation(p, q, r) { + const val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]); + if (val === 0) { + return 0; + } + return val > 0 ? 1 : 2; +} +// Check is p1q1 intersects with p2q2 +export function doIntersect(p1, q1, p2, q2) { + const o1 = orientation(p1, q1, p2); + const o2 = orientation(p1, q1, q2); + const o3 = orientation(p2, q2, p1); + const o4 = orientation(p2, q2, q1); + if (o1 !== o2 && o3 !== o4) { + return true; + } + // p1, q1 and p2 are colinear and p2 lies on segment p1q1 + if (o1 === 0 && onSegment(p1, p2, q1)) { + return true; + } + // p1, q1 and p2 are colinear and q2 lies on segment p1q1 + if (o2 === 0 && onSegment(p1, q2, q1)) { + return true; + } + // p2, q2 and p1 are colinear and p1 lies on segment p2q2 + if (o3 === 0 && onSegment(p2, p1, q2)) { + return true; + } + // p2, q2 and q1 are colinear and q1 lies on segment p2q2 + if (o4 === 0 && onSegment(p2, q1, q2)) { + return true; + } + return false; +} diff --git a/node_modules/roughjs/bin/math.d.ts b/node_modules/roughjs/bin/math.d.ts new file mode 100644 index 0000000..5d745a7 --- /dev/null +++ b/node_modules/roughjs/bin/math.d.ts @@ -0,0 +1,6 @@ +export declare function randomSeed(): number; +export declare class Random { + private seed; + constructor(seed: number); + next(): number; +} diff --git a/node_modules/roughjs/bin/math.js b/node_modules/roughjs/bin/math.js new file mode 100644 index 0000000..b2bac33 --- /dev/null +++ b/node_modules/roughjs/bin/math.js @@ -0,0 +1,16 @@ +export function randomSeed() { + return Math.floor(Math.random() * 2 ** 31); +} +export class Random { + constructor(seed) { + this.seed = seed; + } + next() { + if (this.seed) { + return ((2 ** 31 - 1) & (this.seed = Math.imul(48271, this.seed))) / 2 ** 31; + } + else { + return Math.random(); + } + } +} diff --git a/node_modules/roughjs/bin/renderer.d.ts b/node_modules/roughjs/bin/renderer.d.ts new file mode 100644 index 0000000..339e0c8 --- /dev/null +++ b/node_modules/roughjs/bin/renderer.d.ts @@ -0,0 +1,28 @@ +import { ResolvedOptions, Op, OpSet } from './core.js'; +import { Point } from './geometry.js'; +interface EllipseParams { + rx: number; + ry: number; + increment: number; +} +export declare function line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet; +export declare function linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet; +export declare function polygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function curve(points: Point[], o: ResolvedOptions): OpSet; +export interface EllipseResult { + opset: OpSet; + estimatedPoints: Point[]; +} +export declare function ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function generateEllipseParams(width: number, height: number, o: ResolvedOptions): EllipseParams; +export declare function ellipseWithParams(x: number, y: number, o: ResolvedOptions, ellipseParams: EllipseParams): EllipseResult; +export declare function arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet; +export declare function svgPath(path: string, o: ResolvedOptions): OpSet; +export declare function solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet; +export declare function randOffset(x: number, o: ResolvedOptions): number; +export declare function randOffsetWithRange(min: number, max: number, o: ResolvedOptions): number; +export declare function doubleLineFillOps(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[]; +export {}; diff --git a/node_modules/roughjs/bin/renderer.js b/node_modules/roughjs/bin/renderer.js new file mode 100644 index 0000000..4f9547c --- /dev/null +++ b/node_modules/roughjs/bin/renderer.js @@ -0,0 +1,431 @@ +import { getFiller } from './fillers/filler.js'; +import { Random } from './math.js'; +import { parsePath, normalize, absolutize } from 'path-data-parser'; +const helper = { + randOffset, + randOffsetWithRange, + ellipse, + doubleLineOps: doubleLineFillOps +}; +export function line(x1, y1, x2, y2, o) { + return { type: 'path', ops: _doubleLine(x1, y1, x2, y2, o) }; +} +export function linearPath(points, close, o) { + const len = (points || []).length; + if (len > 2) { + const ops = []; + for (let i = 0; i < (len - 1); i++) { + ops.push(..._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops.push(..._doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); + } + return { type: 'path', ops }; + } + else if (len === 2) { + return line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; +} +export function polygon(points, o) { + return linearPath(points, true, o); +} +export function rectangle(x, y, width, height, o) { + const points = [ + [x, y], + [x + width, y], + [x + width, y + height], + [x, y + height] + ]; + return polygon(points, o); +} +export function curve(points, o) { + let o1 = _curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + if (!o.disableMultiStroke) { + const o2 = _curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), cloneOptionsAlterSeed(o)); + o1 = o1.concat(o2); + } + return { type: 'path', ops: o1 }; +} +export function ellipse(x, y, width, height, o) { + const params = generateEllipseParams(width, height, o); + return ellipseWithParams(x, y, o, params).opset; +} +export function generateEllipseParams(width, height, o) { + const psq = Math.sqrt(Math.PI * 2 * Math.sqrt((Math.pow(width / 2, 2) + Math.pow(height / 2, 2)) / 2)); + const stepCount = Math.max(o.curveStepCount, (o.curveStepCount / Math.sqrt(200)) * psq); + const increment = (Math.PI * 2) / stepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + const curveFitRandomness = 1 - o.curveFitting; + rx += _offsetOpt(rx * curveFitRandomness, o); + ry += _offsetOpt(ry * curveFitRandomness, o); + return { increment, rx, ry }; +} +export function ellipseWithParams(x, y, o, ellipseParams) { + const [ap1, cp1] = _computeEllipsePoints(ellipseParams.increment, x, y, ellipseParams.rx, ellipseParams.ry, 1, ellipseParams.increment * _offset(0.1, _offset(0.4, 1, o), o), o); + let o1 = _curve(ap1, null, o); + if (!o.disableMultiStroke) { + const [ap2] = _computeEllipsePoints(ellipseParams.increment, x, y, ellipseParams.rx, ellipseParams.ry, 1.5, 0, o); + const o2 = _curve(ap2, null, o); + o1 = o1.concat(o2); + } + return { + estimatedPoints: cp1, + opset: { type: 'path', ops: o1 } + }; +} +export function arc(x, y, width, height, start, stop, closed, roughClosure, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const ellipseInc = (Math.PI * 2) / o.curveStepCount; + const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + const ops = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + if (!o.disableMultiStroke) { + const o2 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + ops.push(...o2); + } + if (closed) { + if (roughClosure) { + ops.push(..._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o), ..._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } + else { + ops.push({ op: 'lineTo', data: [cx, cy] }, { op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; +} +export function svgPath(path, o) { + const segments = normalize(absolutize(parsePath(path))); + const ops = []; + let first = [0, 0]; + let current = [0, 0]; + for (const { key, data } of segments) { + switch (key) { + case 'M': { + const ro = 1 * (o.maxRandomnessOffset || 0); + ops.push({ op: 'move', data: data.map((d) => d + _offsetOpt(ro, o)) }); + current = [data[0], data[1]]; + first = [data[0], data[1]]; + break; + } + case 'L': + ops.push(..._doubleLine(current[0], current[1], data[0], data[1], o)); + current = [data[0], data[1]]; + break; + case 'C': { + const [x1, y1, x2, y2, x, y] = data; + ops.push(..._bezierTo(x1, y1, x2, y2, x, y, current, o)); + current = [x, y]; + break; + } + case 'Z': + ops.push(..._doubleLine(current[0], current[1], first[0], first[1], o)); + current = [first[0], first[1]]; + break; + } + } + return { type: 'path', ops }; +} +// Fills +export function solidFillPolygon(points, o) { + const ops = []; + if (points.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + _offsetOpt(offset, o), points[0][1] + _offsetOpt(offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + _offsetOpt(offset, o), points[i][1] + _offsetOpt(offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; +} +export function patternFillPolygon(points, o) { + return getFiller(o, helper).fillPolygon(points, o); +} +export function patternFillArc(x, y, width, height, start, stop, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const increment = (stp - strt) / o.curveStepCount; + const points = []; + for (let angle = strt; angle <= stp; angle = angle + increment) { + points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); + } + points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); + points.push([cx, cy]); + return patternFillPolygon(points, o); +} +export function randOffset(x, o) { + return _offsetOpt(x, o); +} +export function randOffsetWithRange(min, max, o) { + return _offset(min, max, o); +} +export function doubleLineFillOps(x1, y1, x2, y2, o) { + return _doubleLine(x1, y1, x2, y2, o, true); +} +// Private helpers +function cloneOptionsAlterSeed(ops) { + const result = Object.assign({}, ops); + result.randomizer = undefined; + if (ops.seed) { + result.seed = ops.seed + 1; + } + return result; +} +function random(ops) { + if (!ops.randomizer) { + ops.randomizer = new Random(ops.seed || 0); + } + return ops.randomizer.next(); +} +function _offset(min, max, ops, roughnessGain = 1) { + return ops.roughness * roughnessGain * ((random(ops) * (max - min)) + min); +} +function _offsetOpt(x, ops, roughnessGain = 1) { + return _offset(-x, x, ops, roughnessGain); +} +function _doubleLine(x1, y1, x2, y2, o, filling = false) { + const singleStroke = filling ? o.disableMultiStrokeFill : o.disableMultiStroke; + const o1 = _line(x1, y1, x2, y2, o, true, false); + if (singleStroke) { + return o1; + } + const o2 = _line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); +} +function _line(x1, y1, x2, y2, o, move, overlay) { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + const length = Math.sqrt(lengthSq); + let roughnessGain = 1; + if (length < 200) { + roughnessGain = 1; + } + else if (length > 500) { + roughnessGain = 0.4; + } + else { + roughnessGain = (-0.0016668) * length + 1.233334; + } + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = length / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + random(o) * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = _offsetOpt(midDispX, o, roughnessGain); + midDispY = _offsetOpt(midDispY, o, roughnessGain); + const ops = []; + const randomHalf = () => _offsetOpt(halfOffset, o, roughnessGain); + const randomFull = () => _offsetOpt(offset, o, roughnessGain); + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + randomHalf(), + y1 + randomHalf() + ] + }); + } + else { + ops.push({ + op: 'move', data: [ + x1 + _offsetOpt(offset, o, roughnessGain), + y1 + _offsetOpt(offset, o, roughnessGain) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + (y2 - y1) * divergePoint + randomHalf(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomHalf(), + x2 + randomHalf(), + y2 + randomHalf() + ] + }); + } + else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + (y2 - y1) * divergePoint + randomFull(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomFull(), + x2 + randomFull(), + y2 + randomFull() + ] + }); + } + return ops; +} +function _curveWithOffset(points, offset, o) { + const ps = []; + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), + ]); + } + } + return _curve(ps, null, o); +} +function _curve(points, closePoint, o) { + const len = points.length; + const ops = []; + if (len > 3) { + const b = []; + const s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (let i = 1; (i + 2) < len; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); + } + if (closePoint && closePoint.length === 2) { + const ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + _offsetOpt(ro, o), closePoint[1] + _offsetOpt(ro, o)] }); + } + } + else if (len === 3) { + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + ops.push({ + op: 'bcurveTo', data: [ + points[1][0], points[1][1], + points[2][0], points[2][1], + points[2][0], points[2][1] + ] + }); + } + else if (len === 2) { + ops.push(..._doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; +} +function _computeEllipsePoints(increment, cx, cy, rx, ry, offset, overlap, o) { + const corePoints = []; + const allPoints = []; + const radOffset = _offsetOpt(0.5, o) - (Math.PI / 2); + allPoints.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + const p = [ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]; + corePoints.push(p); + allPoints.push(p); + } + allPoints.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + _offsetOpt(offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + allPoints.push([ + _offsetOpt(offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + _offsetOpt(offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + allPoints.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return [allPoints, corePoints]; +} +function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + const radOffset = strt + _offsetOpt(0.1, o); + const points = []; + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + return _curve(points, null, o); +} +function _bezierTo(x1, y1, x2, y2, x, y, current, o) { + const ops = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.3]; + let f = [0, 0]; + const iterations = o.disableMultiStroke ? 1 : 2; + for (let i = 0; i < iterations; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [current[0], current[1]] }); + } + else { + ops.push({ op: 'move', data: [current[0] + _offsetOpt(ros[0], o), current[1] + _offsetOpt(ros[0], o)] }); + } + f = [x + _offsetOpt(ros[i], o), y + _offsetOpt(ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + _offsetOpt(ros[i], o), y1 + _offsetOpt(ros[i], o), + x2 + _offsetOpt(ros[i], o), y2 + _offsetOpt(ros[i], o), + f[0], f[1] + ] + }); + } + return ops; +} diff --git a/node_modules/roughjs/bin/rough.d.ts b/node_modules/roughjs/bin/rough.d.ts new file mode 100644 index 0000000..e8e33b2 --- /dev/null +++ b/node_modules/roughjs/bin/rough.d.ts @@ -0,0 +1,11 @@ +import { Config } from './core'; +import { RoughCanvas } from './canvas'; +import { RoughGenerator } from './generator'; +import { RoughSVG } from './svg'; +declare const _default: { + canvas(canvas: HTMLCanvasElement, config?: Config | undefined): RoughCanvas; + svg(svg: SVGSVGElement, config?: Config | undefined): RoughSVG; + generator(config?: Config | undefined): RoughGenerator; + newSeed(): number; +}; +export default _default; diff --git a/node_modules/roughjs/bin/rough.js b/node_modules/roughjs/bin/rough.js new file mode 100644 index 0000000..11c4c43 --- /dev/null +++ b/node_modules/roughjs/bin/rough.js @@ -0,0 +1,17 @@ +import { RoughCanvas } from './canvas'; +import { RoughGenerator } from './generator'; +import { RoughSVG } from './svg'; +export default { + canvas(canvas, config) { + return new RoughCanvas(canvas, config); + }, + svg(svg, config) { + return new RoughSVG(svg, config); + }, + generator(config) { + return new RoughGenerator(config); + }, + newSeed() { + return RoughGenerator.newSeed(); + } +}; diff --git a/node_modules/roughjs/bin/svg.d.ts b/node_modules/roughjs/bin/svg.d.ts new file mode 100644 index 0000000..18d27cb --- /dev/null +++ b/node_modules/roughjs/bin/svg.d.ts @@ -0,0 +1,22 @@ +import { Config, Options, OpSet, ResolvedOptions, Drawable } from './core'; +import { RoughGenerator } from './generator'; +import { Point } from './geometry'; +export declare class RoughSVG { + private gen; + private svg; + constructor(svg: SVGSVGElement, config?: Config); + draw(drawable: Drawable): SVGGElement; + private fillSketch; + get generator(): RoughGenerator; + getDefaultOptions(): ResolvedOptions; + opsToPath(drawing: OpSet): string; + line(x1: number, y1: number, x2: number, y2: number, options?: Options): SVGGElement; + rectangle(x: number, y: number, width: number, height: number, options?: Options): SVGGElement; + ellipse(x: number, y: number, width: number, height: number, options?: Options): SVGGElement; + circle(x: number, y: number, diameter: number, options?: Options): SVGGElement; + linearPath(points: Point[], options?: Options): SVGGElement; + polygon(points: Point[], options?: Options): SVGGElement; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed?: boolean, options?: Options): SVGGElement; + curve(points: Point[], options?: Options): SVGGElement; + path(d: string, options?: Options): SVGGElement; +} diff --git a/node_modules/roughjs/bin/svg.js b/node_modules/roughjs/bin/svg.js new file mode 100644 index 0000000..1e55ca9 --- /dev/null +++ b/node_modules/roughjs/bin/svg.js @@ -0,0 +1,115 @@ +import { SVGNS } from './core'; +import { RoughGenerator } from './generator'; +export class RoughSVG { + constructor(svg, config) { + this.svg = svg; + this.gen = new RoughGenerator(config); + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.getDefaultOptions(); + const doc = this.svg.ownerDocument || window.document; + const g = doc.createElementNS(SVGNS, 'g'); + for (const drawing of sets) { + let path = null; + switch (drawing.type) { + case 'path': { + path = doc.createElementNS(SVGNS, 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.setAttribute('stroke', o.stroke); + path.setAttribute('stroke-width', o.strokeWidth + ''); + path.setAttribute('fill', 'none'); + if (o.strokeLineDash) { + path.setAttribute('stroke-dasharray', o.strokeLineDash.join(' ').trim()); + } + if (o.strokeLineDashOffset) { + path.setAttribute('stroke-dashoffset', `${o.strokeLineDashOffset}`); + } + break; + } + case 'fillPath': { + path = doc.createElementNS(SVGNS, 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.setAttribute('stroke', 'none'); + path.setAttribute('stroke-width', '0'); + path.setAttribute('fill', o.fill || ''); + if (drawable.shape === 'curve' || drawable.shape === 'polygon') { + path.setAttribute('fill-rule', 'evenodd'); + } + break; + } + case 'fillSketch': { + path = this.fillSketch(doc, drawing, o); + break; + } + } + if (path) { + g.appendChild(path); + } + } + return g; + } + fillSketch(doc, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const path = doc.createElementNS(SVGNS, 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.setAttribute('stroke', o.fill || ''); + path.setAttribute('stroke-width', fweight + ''); + path.setAttribute('fill', 'none'); + if (o.fillLineDash) { + path.setAttribute('stroke-dasharray', o.fillLineDash.join(' ').trim()); + } + if (o.fillLineDashOffset) { + path.setAttribute('stroke-dashoffset', `${o.fillLineDashOffset}`); + } + return path; + } + get generator() { + return this.gen; + } + getDefaultOptions() { + return this.gen.defaultOptions; + } + opsToPath(drawing) { + return this.gen.opsToPath(drawing); + } + line(x1, y1, x2, y2, options) { + const d = this.gen.line(x1, y1, x2, y2, options); + return this.draw(d); + } + rectangle(x, y, width, height, options) { + const d = this.gen.rectangle(x, y, width, height, options); + return this.draw(d); + } + ellipse(x, y, width, height, options) { + const d = this.gen.ellipse(x, y, width, height, options); + return this.draw(d); + } + circle(x, y, diameter, options) { + const d = this.gen.circle(x, y, diameter, options); + return this.draw(d); + } + linearPath(points, options) { + const d = this.gen.linearPath(points, options); + return this.draw(d); + } + polygon(points, options) { + const d = this.gen.polygon(points, options); + return this.draw(d); + } + arc(x, y, width, height, start, stop, closed = false, options) { + const d = this.gen.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + curve(points, options) { + const d = this.gen.curve(points, options); + return this.draw(d); + } + path(d, options) { + const drawing = this.gen.path(d, options); + return this.draw(drawing); + } +} diff --git a/node_modules/roughjs/bundled/rough.cjs.js b/node_modules/roughjs/bundled/rough.cjs.js new file mode 100644 index 0000000..0f27836 --- /dev/null +++ b/node_modules/roughjs/bundled/rough.cjs.js @@ -0,0 +1,15 @@ +"use strict"; +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};function e(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}var n=function(){return(n=Object.assign||function(t){for(var e,n=1,r=arguments.length;n=Math.min(t[0],n[0])&&e[1]<=Math.max(t[1],n[1])&&e[1]>=Math.min(t[1],n[1])}function h(t,e,n){var r=(e[1]-t[1])*(n[0]-e[0])-(e[0]-t[0])*(n[1]-e[1]);return 0===r?0:r>0?1:2}function u(t,e,n,r){var a=h(t,e,n),o=h(t,e,r),s=h(n,r,t),u=h(n,r,e);return a!==o&&s!==u||(!(0!==a||!i(t,n,e))||(!(0!==o||!i(t,r,e))||(!(0!==s||!i(n,t,r))||!(0!==u||!i(n,e,r)))))}function p(t,e){var n=[0,0],o=Math.round(e.hachureAngle+90);o&&a(t,n,o);var s=function(t,e){var n=r(t);n[0].join(",")!==n[n.length-1].join(",")&&n.push([n[0][0],n[0][1]]);var a=[];if(n&&n.length>2){var o=e.hachureGap;o<0&&(o=4*e.strokeWidth),o=Math.max(o,.1);for(var s=[],i=0;ie.ymin?1:t.xe.x?1:t.ymax===e.ymax?0:(t.ymax-e.ymax)/Math.abs(t.ymax-e.ymax)})),!s.length)return a;for(var l=[],c=s[0].ymin;l.length||s.length;){if(s.length){var f=-1;for(i=0;ic);i++)f=i;s.splice(0,f+1).forEach((function(t){l.push({s:c,edge:t})}))}if((l=l.filter((function(t){return!(t.edge.ymax<=c)}))).sort((function(t,e){return t.edge.x===e.edge.x?0:(t.edge.x-e.edge.x)/Math.abs(t.edge.x-e.edge.x)})),l.length>1)for(i=0;i=l.length)break;var v=l[i].edge,g=l[d].edge;a.push([[Math.round(v.x),c],[Math.round(g.x),c]])}c+=o,l.forEach((function(t){t.edge.x=t.edge.x+o*t.edge.islope}))}}return a}(t,e);return o&&(a(t,n,-o),function(t,e,n){var r=[];t.forEach((function(t){return r.push.apply(r,t)})),a(r,e,n)}(s,n,-o)),s}var l=function(){function t(t){this.helper=t}return t.prototype.fillPolygon=function(t,e){return this._fillPolygon(t,e)},t.prototype._fillPolygon=function(t,e,n){void 0===n&&(n=!1);var r=p(t,e);if(n){var a=this.connectingLines(t,r);r=r.concat(a)}return{type:"fillSketch",ops:this.renderLines(r,e)}},t.prototype.renderLines=function(t,e){for(var n=[],r=0,a=t;r1)for(var r=1;r3){var i=this.splitOnIntersections(t,s);n.push.apply(n,i)}}}return n},t.prototype.midPointInPolygon=function(t,e){return s(t,(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2)},t.prototype.splitOnIntersections=function(t,e){for(var n,a,i,h,p,l,c,f,d,v,g,y=Math.max(5,.1*o(e)),M=[],k=0;ky&&x>y&&M.push({point:w,distance:P})}}}if(M.length>1){var O=M.sort((function(t,e){return t.distance-e.distance})).map((function(t){return t.point}));if(s.apply(void 0,r([t],e[0]))||O.shift(),s.apply(void 0,r([t],e[1]))||O.pop(),O.length<=1)return this.midPointInPolygon(t,e)?[e]:[];var S=r([e[0]],O,[e[1]]),L=[];for(k=0;kl[0]&&(p=t[1],l=t[0]);for(var c=Math.atan((l[1]-p[1])/(l[0]-p[0])),f=0;fp[0]&&(u=t[1],p=t[0]);for(var l=Math.atan((p[1]-u[1])/(p[0]-u[0])),c=0;cr%2?t+n:t+e);o.push({key:"C",data:t}),e=t[4],n=t[5];break}case"Q":o.push({key:"Q",data:[...i]}),e=i[2],n=i[3];break;case"q":{const t=i.map((t,r)=>r%2?t+n:t+e);o.push({key:"Q",data:t}),e=t[2],n=t[3];break}case"A":o.push({key:"A",data:[...i]}),e=i[5],n=i[6];break;case"a":e+=i[5],n+=i[6],o.push({key:"A",data:[i[0],i[1],i[2],i[3],i[4],e,n]});break;case"H":o.push({key:"H",data:[...i]}),e=i[0];break;case"h":e+=i[0],o.push({key:"H",data:[e]});break;case"V":o.push({key:"V",data:[...i]}),n=i[0];break;case"v":n+=i[0],o.push({key:"V",data:[n]});break;case"S":o.push({key:"S",data:[...i]}),e=i[2],n=i[3];break;case"s":{const t=i.map((t,r)=>r%2?t+n:t+e);o.push({key:"S",data:t}),e=t[2],n=t[3];break}case"T":o.push({key:"T",data:[...i]}),e=i[0],n=i[1];break;case"t":e+=i[0],n+=i[1],o.push({key:"T",data:[e,n]});break;case"Z":case"z":o.push({key:"Z",data:[]}),e=r,n=a}return o}function P(t){const e=[];let n="",r=0,a=0,o=0,s=0,i=0,h=0;for(const{key:u,data:p}of t){switch(u){case"M":e.push({key:"M",data:[...p]}),[r,a]=p,[o,s]=p;break;case"C":e.push({key:"C",data:[...p]}),r=p[4],a=p[5],i=p[2],h=p[3];break;case"L":e.push({key:"L",data:[...p]}),[r,a]=p;break;case"H":r=p[0],e.push({key:"L",data:[r,a]});break;case"V":a=p[0],e.push({key:"L",data:[r,a]});break;case"S":{let t=0,o=0;"C"===n||"S"===n?(t=r+(r-i),o=a+(a-h)):(t=r,o=a),e.push({key:"C",data:[t,o,...p]}),i=p[0],h=p[1],r=p[2],a=p[3];break}case"T":{const[t,o]=p;let s=0,u=0;"Q"===n||"T"===n?(s=r+(r-i),u=a+(a-h)):(s=r,u=a);const l=r+2*(s-r)/3,c=a+2*(u-a)/3,f=t+2*(s-t)/3,d=o+2*(u-o)/3;e.push({key:"C",data:[l,c,f,d,t,o]}),i=s,h=u,r=t,a=o;break}case"Q":{const[t,n,o,s]=p,u=r+2*(t-r)/3,l=a+2*(n-a)/3,c=o+2*(t-o)/3,f=s+2*(n-s)/3;e.push({key:"C",data:[u,l,c,f,o,s]}),i=t,h=n,r=o,a=s;break}case"A":{const t=Math.abs(p[0]),n=Math.abs(p[1]),o=p[2],s=p[3],i=p[4],h=p[5],u=p[6];if(0===t||0===n)e.push({key:"C",data:[r,a,h,u,h,u]}),r=h,a=u;else if(r!==h||a!==u){O(r,a,h,u,t,n,o,s,i).forEach((function(t){e.push({key:"C",data:t})})),r=h,a=u}break}case"Z":e.push({key:"Z",data:[]}),r=o,a=s}n=u}return e}function x(t,e,n){return[t*Math.cos(n)-e*Math.sin(n),t*Math.sin(n)+e*Math.cos(n)]}function O(t,e,n,r,a,o,s,i,h,u){const p=(l=s,Math.PI*l/180);var l;let c=[],f=0,d=0,v=0,g=0;if(u)[f,d,v,g]=u;else{[t,e]=x(t,e,-p),[n,r]=x(n,r,-p);const s=(t-n)/2,u=(e-r)/2;let l=s*s/(a*a)+u*u/(o*o);l>1&&(l=Math.sqrt(l),a*=l,o*=l);const c=a*a,y=o*o,M=c*y-c*u*u-y*s*s,k=c*u*u+y*s*s,b=(i===h?-1:1)*Math.sqrt(Math.abs(M/k));v=b*a*u/o+(t+n)/2,g=b*-o*s/a+(e+r)/2,f=Math.asin(parseFloat(((e-g)/o).toFixed(9))),d=Math.asin(parseFloat(((r-g)/o).toFixed(9))),td&&(f-=2*Math.PI),!h&&d>f&&(d-=2*Math.PI)}let y=d-f;if(Math.abs(y)>120*Math.PI/180){const t=d,e=n,i=r;d=h&&d>f?f+120*Math.PI/180*1:f+120*Math.PI/180*-1,c=O(n=v+a*Math.cos(d),r=g+o*Math.sin(d),e,i,a,o,s,0,h,[d,t,v,g])}y=d-f;const M=Math.cos(f),k=Math.sin(f),b=Math.cos(d),m=Math.sin(d),w=Math.tan(y/4),P=4/3*a*w,S=4/3*o*w,L=[t,e],T=[t+P*k,e-S*M],_=[n+P*m,r-S*b],I=[n,r];if(T[0]=2*L[0]-T[0],T[1]=2*L[1]-T[1],u)return[T,_,I].concat(c);{c=[T,_,I].concat(c);const t=[];for(let e=0;e2){for(var a=[],o=0;o2*Math.PI&&(d=0,v=2*Math.PI);var g=2*Math.PI/u.curveStepCount,y=Math.min(g/2,(v-d)/2),M=H(y,p,l,c,f,d,v,1,u);if(!u.disableMultiStroke){var k=H(y,p,l,c,f,d,v,1.5,u);M.push.apply(M,k)}return i&&(h?M.push.apply(M,r(q(p,l,p+c*Math.cos(d),l+f*Math.sin(d),u),q(p,l,p+c*Math.cos(v),l+f*Math.sin(v),u))):M.push({op:"lineTo",data:[p,l]},{op:"lineTo",data:[p+c*Math.cos(d),l+f*Math.sin(d)]})),{type:"path",ops:M}}function E(t,e){for(var n=P(w(m(t))),r=[],a=[0,0],o=[0,0],s=function(t,n){switch(t){case"M":var s=1*(e.maxRandomnessOffset||0);r.push({op:"move",data:n.map((function(t){return t+G(s,e)}))}),o=[n[0],n[1]],a=[n[0],n[1]];break;case"L":r.push.apply(r,q(o[0],o[1],n[0],n[1],e)),o=[n[0],n[1]];break;case"C":var i=n[0],h=n[1],u=n[2],p=n[3],l=n[4],c=n[5];r.push.apply(r,function(t,e,n,r,a,o,s,i){for(var h=[],u=[i.maxRandomnessOffset||1,(i.maxRandomnessOffset||1)+.3],p=[0,0],l=i.disableMultiStroke?1:2,c=0;c2){n.push({op:"move",data:[t[0][0]+G(r,e),t[0][1]+G(r,e)]});for(var o=1;o500?.4:-.0016668*h+1.233334;var p=a.maxRandomnessOffset||0;p*p*100>i&&(p=h/10);var l=p/2,c=.2+.2*R(a),f=a.bowing*a.maxRandomnessOffset*(r-e)/200,d=a.bowing*a.maxRandomnessOffset*(t-n)/200;f=G(f,a,u),d=G(d,a,u);var v=[],g=function(){return G(l,a,u)},y=function(){return G(p,a,u)};return o&&(s?v.push({op:"move",data:[t+g(),e+g()]}):v.push({op:"move",data:[t+G(p,a,u),e+G(p,a,u)]})),s?v.push({op:"bcurveTo",data:[f+t+(n-t)*c+g(),d+e+(r-e)*c+g(),f+t+2*(n-t)*c+g(),d+e+2*(r-e)*c+g(),n+g(),r+g()]}):v.push({op:"bcurveTo",data:[f+t+(n-t)*c+y(),d+e+(r-e)*c+y(),f+t+2*(n-t)*c+y(),d+e+2*(r-e)*c+y(),n+y(),r+y()]}),v}function N(t,e,n){var r=[];r.push([t[0][0]+G(e,n),t[0][1]+G(e,n)]),r.push([t[0][0]+G(e,n),t[0][1]+G(e,n)]);for(var a=1;a3){var o=[],s=1-n.curveTightness;a.push({op:"move",data:[t[1][0],t[1][1]]});for(var i=1;i+21&&a.push(n)}else a.push(n);a.push(t[e+3])}else{const r=.5,o=t[e+0],s=t[e+1],i=t[e+2],h=t[e+3],u=X(o,s,r),p=X(s,i,r),l=X(i,h,r),c=X(u,p,r),f=X(p,l,r),d=X(c,f,r);J([o,u,c,d],0,n,a),J([d,f,l,h],0,n,a)}var o,s;return a}function K(t,e){return U(t,0,t.length,e)}function U(t,e,n,r,a){const o=a||[],s=t[e],i=t[n-1];let h=0,u=1;for(let r=e+1;rh&&(h=e,u=r)}return Math.sqrt(h)>r?(U(t,e,u+1,r,o),U(t,u,n,r,o)):(o.length||o.push(s),o.push(i)),o}function Y(t,e=.15,n){const r=[],a=(t.length-1)/3;for(let n=0;n0?U(r,0,r.length,n):r}var tt="none",et=function(){function t(t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveFitting:.95,curveStepCount:9,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1,dashOffset:-1,dashGap:-1,zigzagOffset:-1,seed:0,combineNestedSvgPaths:!1,disableMultiStroke:!1,disableMultiStrokeFill:!1},this.config=t||{},this.config.options&&(this.defaultOptions=this._o(this.config.options))}return t.newSeed=function(){return Math.floor(Math.random()*Math.pow(2,31))},t.prototype._o=function(t){return t?Object.assign({},this.defaultOptions,t):this.defaultOptions},t.prototype._d=function(t,e,n){return{shape:t,sets:e||[],options:n||this.defaultOptions}},t.prototype.line=function(t,e,n,r,a){var o=this._o(a);return this._d("line",[L(t,e,n,r,o)],o)},t.prototype.rectangle=function(t,e,n,r,a){var o=this._o(a),s=[],i=_(t,e,n,r,o);if(o.fill){var h=[[t,e],[t+n,e],[t+n,e+r],[t,e+r]];"solid"===o.fillStyle?s.push(W(h,o)):s.push(z(h,o))}return o.stroke!==tt&&s.push(i),this._d("rectangle",s,o)},t.prototype.ellipse=function(t,e,n,r,a){var o=this._o(a),s=[],i=A(n,r,o),h=C(t,e,o,i);if(o.fill)if("solid"===o.fillStyle){var u=C(t,e,o,i).opset;u.type="fillPath",s.push(u)}else s.push(z(h.estimatedPoints,o));return o.stroke!==tt&&s.push(h.opset),this._d("ellipse",s,o)},t.prototype.circle=function(t,e,n,r){var a=this.ellipse(t,e,n,n,r);return a.shape="circle",a},t.prototype.linearPath=function(t,e){var n=this._o(e);return this._d("linearPath",[T(t,!1,n)],n)},t.prototype.arc=function(t,e,n,r,a,o,s,i){void 0===s&&(s=!1);var h=this._o(i),u=[],p=D(t,e,n,r,a,o,s,!0,h);if(s&&h.fill)if("solid"===h.fillStyle){var l=D(t,e,n,r,a,o,!0,!1,h);l.type="fillPath",u.push(l)}else u.push(function(t,e,n,r,a,o,s){var i=t,h=e,u=Math.abs(n/2),p=Math.abs(r/2);u+=G(.01*u,s),p+=G(.01*p,s);for(var l=a,c=o;l<0;)l+=2*Math.PI,c+=2*Math.PI;c-l>2*Math.PI&&(l=0,c=2*Math.PI);for(var f=(c-l)/s.curveStepCount,d=[],v=l;v<=c;v+=f)d.push([i+u*Math.cos(v),h+p*Math.sin(v)]);return d.push([i+u*Math.cos(c),h+p*Math.sin(c)]),d.push([i,h]),z(d,s)}(t,e,n,r,a,o,h));return h.stroke!==tt&&u.push(p),this._d("arc",u,h)},t.prototype.curve=function(t,e){var n=this._o(e),r=[],a=I(t,n);if(n.fill&&n.fill!==tt&&t.length>=3){var o=Y(function(t,e=0){const n=t.length;if(n<3)throw new Error("A curve must have at least three points.");const r=[];if(3===n)r.push(V(t[0]),V(t[1]),V(t[2]),V(t[2]));else{const n=[];n.push(t[0],t[0]);for(let e=1;e{i.length>=4&&o.push(...Y(i,e)),i=[]},u=()=>{h(),o.length&&(a.push(o),o=[])};for(const{key:t,data:e}of r)switch(t){case"M":u(),s=[e[0],e[1]],o.push(s);break;case"L":h(),o.push([e[0],e[1]]);break;case"C":if(!i.length){const t=o.length?o[o.length-1]:s;i.push([t[0],t[1]])}i.push([e[0],e[1]]),i.push([e[2],e[3]]),i.push([e[4],e[5]]);break;case"Z":h(),o.push([s[0],s[1]])}if(u(),!n)return a;const p=[];for(const t of a){const e=K(t,n);e.length&&p.push(e)}return p}(t,1,s?4-4*n.simplification:(1+n.roughness)/2);if(a)if(n.combineNestedSvgPaths){var h=[];i.forEach((function(t){return h.push.apply(h,t)})),"solid"===n.fillStyle?r.push(W(h,n)):r.push(z(h,n))}else i.forEach((function(t){"solid"===n.fillStyle?r.push(W(t,n)):r.push(z(t,n))}));return o&&(s?i.forEach((function(t){r.push(T(t,!1,n))})):r.push(E(t,n))),this._d("path",r,n)},t.prototype.opsToPath=function(t){for(var e="",n=0,r=t.ops;n{const[e,s]=t;t[0]=(e-n)*r-(s-o)*h+n,t[1]=(e-n)*h+(s-o)*r+o})}}function e(t){const e=t[0],s=t[1];return Math.sqrt(Math.pow(e[0]-s[0],2)+Math.pow(e[1]-s[1],2))}function s(t,e,s,n){const o=e[1]-t[1],a=t[0]-e[0],r=o*t[0]+a*t[1],h=n[1]-s[1],i=s[0]-n[0],c=h*s[0]+i*s[1],l=o*i-h*a;return l?[(i*r-a*c)/l,(o*c-h*r)/l]:null}function n(t,e,s){const n=t.length;if(n<3)return!1;const h=[Number.MAX_SAFE_INTEGER,s],i=[e,s];let c=0;for(let e=0;e=Math.min(t[0],s[0])&&e[1]<=Math.max(t[1],s[1])&&e[1]>=Math.min(t[1],s[1])}function a(t,e,s){const n=(e[1]-t[1])*(s[0]-e[0])-(e[0]-t[0])*(s[1]-e[1]);return 0===n?0:n>0?1:2}function r(t,e,s,n){const r=a(t,e,s),h=a(t,e,n),i=a(s,n,t),c=a(s,n,e);return r!==h&&i!==c||(!(0!==r||!o(t,s,e))||(!(0!==h||!o(t,n,e))||(!(0!==i||!o(s,t,n))||!(0!==c||!o(s,e,n)))))}function h(e,s){const n=[0,0],o=Math.round(s.hachureAngle+90);o&&t(e,n,o);const a=function(t,e){const s=[...t];s[0].join(",")!==s[s.length-1].join(",")&&s.push([s[0][0],s[0][1]]);const n=[];if(s&&s.length>2){let t=e.hachureGap;t<0&&(t=4*e.strokeWidth),t=Math.max(t,.1);const o=[];for(let t=0;tt.ymine.ymin?1:t.xe.x?1:t.ymax===e.ymax?0:(t.ymax-e.ymax)/Math.abs(t.ymax-e.ymax)),!o.length)return n;let a=[],r=o[0].ymin;for(;a.length||o.length;){if(o.length){let t=-1;for(let e=0;er);e++)t=e;o.splice(0,t+1).forEach(t=>{a.push({s:r,edge:t})})}if(a=a.filter(t=>!(t.edge.ymax<=r)),a.sort((t,e)=>t.edge.x===e.edge.x?0:(t.edge.x-e.edge.x)/Math.abs(t.edge.x-e.edge.x)),a.length>1)for(let t=0;t=a.length)break;const s=a[t].edge,o=a[e].edge;n.push([[Math.round(s.x),r],[Math.round(o.x),r]])}r+=t,a.forEach(e=>{e.edge.x=e.edge.x+t*e.edge.islope})}}return n}(e,s);return o&&(t(e,n,-o),function(e,s,n){const o=[];e.forEach(t=>o.push(...t)),t(o,s,n)}(a,n,-o)),a}class i{constructor(t){this.helper=t}fillPolygon(t,e){return this._fillPolygon(t,e)}_fillPolygon(t,e,s=!1){let n=h(t,e);if(s){const e=this.connectingLines(t,n);n=n.concat(e)}return{type:"fillSketch",ops:this.renderLines(n,e)}}renderLines(t,e){const s=[];for(const n of t)s.push(...this.helper.doubleLineOps(n[0][0],n[0][1],n[1][0],n[1][1],e));return s}connectingLines(t,s){const n=[];if(s.length>1)for(let o=1;o3){const e=this.splitOnIntersections(t,r);n.push(...e)}}return n}midPointInPolygon(t,e){return n(t,(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2)}splitOnIntersections(t,o){const a=Math.max(5,.1*e(o)),h=[];for(let n=0;na&&n>a&&h.push({point:t,distance:s})}}}if(h.length>1){const e=h.sort((t,e)=>t.distance-e.distance).map(t=>t.point);if(n(t,...o[0])||e.shift(),n(t,...o[1])||e.pop(),e.length<=1)return this.midPointInPolygon(t,o)?[o]:[];const s=[o[0],...e,o[1]],a=[];for(let e=0;e{const r=e(t),h=Math.floor(r/(n+o)),i=(r+o-h*(n+o))/2;let c=t[0],l=t[1];c[0]>l[0]&&(c=t[1],l=t[0]);const u=Math.atan((l[1]-c[1])/(l[0]-c[0]));for(let t=0;t{const a=e(t),r=Math.round(a/(2*s));let h=t[0],i=t[1];h[0]>i[0]&&(h=t[1],i=t[0]);const c=Math.atan((i[1]-h[1])/(i[0]-h[0]));for(let t=0;tn%2?t+s:t+e);a.push({key:"C",data:t}),e=t[4],s=t[5];break}case"Q":a.push({key:"Q",data:[...h]}),e=h[2],s=h[3];break;case"q":{const t=h.map((t,n)=>n%2?t+s:t+e);a.push({key:"Q",data:t}),e=t[2],s=t[3];break}case"A":a.push({key:"A",data:[...h]}),e=h[5],s=h[6];break;case"a":e+=h[5],s+=h[6],a.push({key:"A",data:[h[0],h[1],h[2],h[3],h[4],e,s]});break;case"H":a.push({key:"H",data:[...h]}),e=h[0];break;case"h":e+=h[0],a.push({key:"H",data:[e]});break;case"V":a.push({key:"V",data:[...h]}),s=h[0];break;case"v":s+=h[0],a.push({key:"V",data:[s]});break;case"S":a.push({key:"S",data:[...h]}),e=h[2],s=h[3];break;case"s":{const t=h.map((t,n)=>n%2?t+s:t+e);a.push({key:"S",data:t}),e=t[2],s=t[3];break}case"T":a.push({key:"T",data:[...h]}),e=h[0],s=h[1];break;case"t":e+=h[0],s+=h[1],a.push({key:"T",data:[e,s]});break;case"Z":case"z":a.push({key:"Z",data:[]}),e=n,s=o}return a}function m(t){const e=[];let s="",n=0,o=0,a=0,r=0,h=0,i=0;for(const{key:c,data:l}of t){switch(c){case"M":e.push({key:"M",data:[...l]}),[n,o]=l,[a,r]=l;break;case"C":e.push({key:"C",data:[...l]}),n=l[4],o=l[5],h=l[2],i=l[3];break;case"L":e.push({key:"L",data:[...l]}),[n,o]=l;break;case"H":n=l[0],e.push({key:"L",data:[n,o]});break;case"V":o=l[0],e.push({key:"L",data:[n,o]});break;case"S":{let t=0,a=0;"C"===s||"S"===s?(t=n+(n-h),a=o+(o-i)):(t=n,a=o),e.push({key:"C",data:[t,a,...l]}),h=l[0],i=l[1],n=l[2],o=l[3];break}case"T":{const[t,a]=l;let r=0,c=0;"Q"===s||"T"===s?(r=n+(n-h),c=o+(o-i)):(r=n,c=o);const u=n+2*(r-n)/3,f=o+2*(c-o)/3,p=t+2*(r-t)/3,d=a+2*(c-a)/3;e.push({key:"C",data:[u,f,p,d,t,a]}),h=r,i=c,n=t,o=a;break}case"Q":{const[t,s,a,r]=l,c=n+2*(t-n)/3,u=o+2*(s-o)/3,f=a+2*(t-a)/3,p=r+2*(s-r)/3;e.push({key:"C",data:[c,u,f,p,a,r]}),h=t,i=s,n=a,o=r;break}case"A":{const t=Math.abs(l[0]),s=Math.abs(l[1]),a=l[2],r=l[3],h=l[4],i=l[5],c=l[6];if(0===t||0===s)e.push({key:"C",data:[n,o,i,c,i,c]}),n=i,o=c;else if(n!==i||o!==c){P(n,o,i,c,t,s,a,r,h).forEach((function(t){e.push({key:"C",data:t})})),n=i,o=c}break}case"Z":e.push({key:"Z",data:[]}),n=a,o=r}s=c}return e}function w(t,e,s){return[t*Math.cos(s)-e*Math.sin(s),t*Math.sin(s)+e*Math.cos(s)]}function P(t,e,s,n,o,a,r,h,i,c){const l=(u=r,Math.PI*u/180);var u;let f=[],p=0,d=0,g=0,M=0;if(c)[p,d,g,M]=c;else{[t,e]=w(t,e,-l),[s,n]=w(s,n,-l);const r=(t-s)/2,c=(e-n)/2;let u=r*r/(o*o)+c*c/(a*a);u>1&&(u=Math.sqrt(u),o*=u,a*=u);const f=o*o,k=a*a,b=f*k-f*c*c-k*r*r,y=f*c*c+k*r*r,m=(h===i?-1:1)*Math.sqrt(Math.abs(b/y));g=m*o*c/a+(t+s)/2,M=m*-a*r/o+(e+n)/2,p=Math.asin(parseFloat(((e-M)/a).toFixed(9))),d=Math.asin(parseFloat(((n-M)/a).toFixed(9))),td&&(p-=2*Math.PI),!i&&d>p&&(d-=2*Math.PI)}let k=d-p;if(Math.abs(k)>120*Math.PI/180){const t=d,e=s,h=n;d=i&&d>p?p+120*Math.PI/180*1:p+120*Math.PI/180*-1,f=P(s=g+o*Math.cos(d),n=M+a*Math.sin(d),e,h,o,a,r,0,i,[d,t,g,M])}k=d-p;const b=Math.cos(p),y=Math.sin(p),m=Math.cos(d),x=Math.sin(d),v=Math.tan(k/4),O=4/3*o*v,S=4/3*a*v,L=[t,e],T=[t+O*y,e-S*b],I=[s+O*x,n-S*m],A=[s,n];if(T[0]=2*L[0]-T[0],T[1]=2*L[1]-T[1],c)return[T,I,A].concat(f);{f=[T,I,A].concat(f);const t=[];for(let e=0;e2){const o=[];for(let e=0;e2*Math.PI&&(p=0,d=2*Math.PI);const g=2*Math.PI/i.curveStepCount,M=Math.min(g/2,(d-p)/2),k=F(M,c,l,u,f,p,d,1,i);if(!i.disableMultiStroke){const t=F(M,c,l,u,f,p,d,1.5,i);k.push(...t)}return r&&(h?k.push(...z(c,l,c+u*Math.cos(p),l+f*Math.sin(p),i),...z(c,l,c+u*Math.cos(d),l+f*Math.sin(d),i)):k.push({op:"lineTo",data:[c,l]},{op:"lineTo",data:[c+u*Math.cos(p),l+f*Math.sin(p)]})),{type:"path",ops:k}}function _(t,e){const s=[];if(t.length){const n=e.maxRandomnessOffset||0,o=t.length;if(o>2){s.push({op:"move",data:[t[0][0]+W(n,e),t[0][1]+W(n,e)]});for(let a=1;a500?.4:-.0016668*i+1.233334;let l=o.maxRandomnessOffset||0;l*l*100>h&&(l=i/10);const u=l/2,f=.2+.2*D(o);let p=o.bowing*o.maxRandomnessOffset*(n-e)/200,d=o.bowing*o.maxRandomnessOffset*(t-s)/200;p=W(p,o,c),d=W(d,o,c);const g=[],M=()=>W(u,o,c),k=()=>W(l,o,c);return a&&(r?g.push({op:"move",data:[t+M(),e+M()]}):g.push({op:"move",data:[t+W(l,o,c),e+W(l,o,c)]})),r?g.push({op:"bcurveTo",data:[p+t+(s-t)*f+M(),d+e+(n-e)*f+M(),p+t+2*(s-t)*f+M(),d+e+2*(n-e)*f+M(),s+M(),n+M()]}):g.push({op:"bcurveTo",data:[p+t+(s-t)*f+k(),d+e+(n-e)*f+k(),p+t+2*(s-t)*f+k(),d+e+2*(n-e)*f+k(),s+k(),n+k()]}),g}function $(t,e,s){const n=[];n.push([t[0][0]+W(e,s),t[0][1]+W(e,s)]),n.push([t[0][0]+W(e,s),t[0][1]+W(e,s)]);for(let o=1;o3){const a=[],r=1-s.curveTightness;o.push({op:"move",data:[t[1][0],t[1][1]]});for(let e=1;e+21&&o.push(s)}else o.push(s);o.push(t[e+3])}else{const n=.5,a=t[e+0],r=t[e+1],h=t[e+2],i=t[e+3],c=H(a,r,n),l=H(r,h,n),u=H(h,i,n),f=H(c,l,n),p=H(l,u,n),d=H(f,p,n);V([a,c,f,d],0,s,o),V([d,p,u,i],0,s,o)}var a,r;return o}function B(t,e){return X(t,0,t.length,e)}function X(t,e,s,n,o){const a=o||[],r=t[e],h=t[s-1];let i=0,c=1;for(let n=e+1;ni&&(i=e,c=n)}return Math.sqrt(i)>n?(X(t,e,c+1,n,a),X(t,c,s,n,a)):(a.length||a.push(r),a.push(h)),a}function J(t,e=.15,s){const n=[],o=(t.length-1)/3;for(let s=0;s0?X(n,0,n.length,s):n}const K="none";class U{constructor(t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveFitting:.95,curveStepCount:9,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1,dashOffset:-1,dashGap:-1,zigzagOffset:-1,seed:0,combineNestedSvgPaths:!1,disableMultiStroke:!1,disableMultiStrokeFill:!1},this.config=t||{},this.config.options&&(this.defaultOptions=this._o(this.config.options))}static newSeed(){return Math.floor(Math.random()*2**31)}_o(t){return t?Object.assign({},this.defaultOptions,t):this.defaultOptions}_d(t,e,s){return{shape:t,sets:e||[],options:s||this.defaultOptions}}line(t,e,s,n,o){const a=this._o(o);return this._d("line",[v(t,e,s,n,a)],a)}rectangle(t,e,s,n,o){const a=this._o(o),r=[],h=S(t,e,s,n,a);if(a.fill){const o=[[t,e],[t+s,e],[t+s,e+n],[t,e+n]];"solid"===a.fillStyle?r.push(_(o,a)):r.push(C(o,a))}return a.stroke!==K&&r.push(h),this._d("rectangle",r,a)}ellipse(t,e,s,n,o){const a=this._o(o),r=[],h=T(s,n,a),i=I(t,e,a,h);if(a.fill)if("solid"===a.fillStyle){const s=I(t,e,a,h).opset;s.type="fillPath",r.push(s)}else r.push(C(i.estimatedPoints,a));return a.stroke!==K&&r.push(i.opset),this._d("ellipse",r,a)}circle(t,e,s,n){const o=this.ellipse(t,e,s,s,n);return o.shape="circle",o}linearPath(t,e){const s=this._o(e);return this._d("linearPath",[O(t,!1,s)],s)}arc(t,e,s,n,o,a,r=!1,h){const i=this._o(h),c=[],l=A(t,e,s,n,o,a,r,!0,i);if(r&&i.fill)if("solid"===i.fillStyle){const r=A(t,e,s,n,o,a,!0,!1,i);r.type="fillPath",c.push(r)}else c.push(function(t,e,s,n,o,a,r){const h=t,i=e;let c=Math.abs(s/2),l=Math.abs(n/2);c+=W(.01*c,r),l+=W(.01*l,r);let u=o,f=a;for(;u<0;)u+=2*Math.PI,f+=2*Math.PI;f-u>2*Math.PI&&(u=0,f=2*Math.PI);const p=(f-u)/r.curveStepCount,d=[];for(let t=u;t<=f;t+=p)d.push([h+c*Math.cos(t),i+l*Math.sin(t)]);return d.push([h+c*Math.cos(f),i+l*Math.sin(f)]),d.push([h,i]),C(d,r)}(t,e,s,n,o,a,i));return i.stroke!==K&&c.push(l),this._d("arc",c,i)}curve(t,e){const s=this._o(e),n=[],o=L(t,s);if(s.fill&&s.fill!==K&&t.length>=3){const e=J(function(t,e=0){const s=t.length;if(s<3)throw new Error("A curve must have at least three points.");const n=[];if(3===s)n.push(N(t[0]),N(t[1]),N(t[2]),N(t[2]));else{const s=[];s.push(t[0],t[0]);for(let e=1;e{h.length>=4&&a.push(...J(h,e)),h=[]},c=()=>{i(),a.length&&(o.push(a),a=[])};for(const{key:t,data:e}of n)switch(t){case"M":c(),r=[e[0],e[1]],a.push(r);break;case"L":i(),a.push([e[0],e[1]]);break;case"C":if(!h.length){const t=a.length?a[a.length-1]:r;h.push([t[0],t[1]])}h.push([e[0],e[1]]),h.push([e[2],e[3]]),h.push([e[4],e[5]]);break;case"Z":i(),a.push([r[0],r[1]])}if(c(),!s)return o;const l=[];for(const t of o){const e=B(t,s);e.length&&l.push(e)}return l}(t,1,r?4-4*s.simplification:(1+s.roughness)/2);if(o)if(s.combineNestedSvgPaths){const t=[];h.forEach(e=>t.push(...e)),"solid"===s.fillStyle?n.push(_(t,s)):n.push(C(t,s))}else h.forEach(t=>{"solid"===s.fillStyle?n.push(_(t,s)):n.push(C(t,s))});return a&&(r?h.forEach(t=>{n.push(O(t,!1,s))}):n.push(function(t,e){const s=m(y(b(t))),n=[];let o=[0,0],a=[0,0];for(const{key:t,data:r}of s)switch(t){case"M":{const t=1*(e.maxRandomnessOffset||0);n.push({op:"move",data:r.map(s=>s+W(t,e))}),a=[r[0],r[1]],o=[r[0],r[1]];break}case"L":n.push(...z(a[0],a[1],r[0],r[1],e)),a=[r[0],r[1]];break;case"C":{const[t,s,o,h,i,c]=r;n.push(...j(t,s,o,h,i,c,a,e)),a=[i,c];break}case"Z":n.push(...z(a[0],a[1],o[0],o[1],e)),a=[o[0],o[1]]}return{type:"path",ops:n}}(t,s))),this._d("path",n,s)}opsToPath(t){let e="";for(const s of t.ops){const t=s.data;switch(s.op){case"move":e+=`M${t[0]} ${t[1]} `;break;case"bcurveTo":e+=`C${t[0]} ${t[1]}, ${t[2]} ${t[3]}, ${t[4]} ${t[5]} `;break;case"lineTo":e+=`L${t[0]} ${t[1]} `}}return e.trim()}toPaths(t){const e=t.sets||[],s=t.options||this.defaultOptions,n=[];for(const t of e){let e=null;switch(t.type){case"path":e={d:this.opsToPath(t),stroke:s.stroke,strokeWidth:s.strokeWidth,fill:K};break;case"fillPath":e={d:this.opsToPath(t),stroke:K,strokeWidth:0,fill:s.fill||K};break;case"fillSketch":e=this.fillSketch(t,s)}e&&n.push(e)}return n}fillSketch(t,e){let s=e.fillWeight;return s<0&&(s=e.strokeWidth/2),{d:this.opsToPath(t),stroke:e.fill||K,strokeWidth:s,fill:K}}}class Y{constructor(t,e){this.canvas=t,this.ctx=this.canvas.getContext("2d"),this.gen=new U(e)}draw(t){const e=t.sets||[],s=t.options||this.getDefaultOptions(),n=this.ctx;for(const o of e)switch(o.type){case"path":n.save(),n.strokeStyle="none"===s.stroke?"transparent":s.stroke,n.lineWidth=s.strokeWidth,s.strokeLineDash&&n.setLineDash(s.strokeLineDash),s.strokeLineDashOffset&&(n.lineDashOffset=s.strokeLineDashOffset),this._drawToContext(n,o),n.restore();break;case"fillPath":n.save(),n.fillStyle=s.fill||"";const e="curve"===t.shape||"polygon"===t.shape?"evenodd":"nonzero";this._drawToContext(n,o,e),n.restore();break;case"fillSketch":this.fillSketch(n,o,s)}}fillSketch(t,e,s){let n=s.fillWeight;n<0&&(n=s.strokeWidth/2),t.save(),s.fillLineDash&&t.setLineDash(s.fillLineDash),s.fillLineDashOffset&&(t.lineDashOffset=s.fillLineDashOffset),t.strokeStyle=s.fill||"",t.lineWidth=n,this._drawToContext(t,e),t.restore()}_drawToContext(t,e,s="nonzero"){t.beginPath();for(const s of e.ops){const e=s.data;switch(s.op){case"move":t.moveTo(e[0],e[1]);break;case"bcurveTo":t.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5]);break;case"lineTo":t.lineTo(e[0],e[1])}}"fillPath"===e.type?t.fill(s):t.stroke()}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}line(t,e,s,n,o){const a=this.gen.line(t,e,s,n,o);return this.draw(a),a}rectangle(t,e,s,n,o){const a=this.gen.rectangle(t,e,s,n,o);return this.draw(a),a}ellipse(t,e,s,n,o){const a=this.gen.ellipse(t,e,s,n,o);return this.draw(a),a}circle(t,e,s,n){const o=this.gen.circle(t,e,s,n);return this.draw(o),o}linearPath(t,e){const s=this.gen.linearPath(t,e);return this.draw(s),s}polygon(t,e){const s=this.gen.polygon(t,e);return this.draw(s),s}arc(t,e,s,n,o,a,r=!1,h){const i=this.gen.arc(t,e,s,n,o,a,r,h);return this.draw(i),i}curve(t,e){const s=this.gen.curve(t,e);return this.draw(s),s}path(t,e){const s=this.gen.path(t,e);return this.draw(s),s}}const tt="http://www.w3.org/2000/svg";class et{constructor(t,e){this.svg=t,this.gen=new U(e)}draw(t){const e=t.sets||[],s=t.options||this.getDefaultOptions(),n=this.svg.ownerDocument||window.document,o=n.createElementNS(tt,"g");for(const a of e){let e=null;switch(a.type){case"path":e=n.createElementNS(tt,"path"),e.setAttribute("d",this.opsToPath(a)),e.setAttribute("stroke",s.stroke),e.setAttribute("stroke-width",s.strokeWidth+""),e.setAttribute("fill","none"),s.strokeLineDash&&e.setAttribute("stroke-dasharray",s.strokeLineDash.join(" ").trim()),s.strokeLineDashOffset&&e.setAttribute("stroke-dashoffset",""+s.strokeLineDashOffset);break;case"fillPath":e=n.createElementNS(tt,"path"),e.setAttribute("d",this.opsToPath(a)),e.setAttribute("stroke","none"),e.setAttribute("stroke-width","0"),e.setAttribute("fill",s.fill||""),"curve"!==t.shape&&"polygon"!==t.shape||e.setAttribute("fill-rule","evenodd");break;case"fillSketch":e=this.fillSketch(n,a,s)}e&&o.appendChild(e)}return o}fillSketch(t,e,s){let n=s.fillWeight;n<0&&(n=s.strokeWidth/2);const o=t.createElementNS(tt,"path");return o.setAttribute("d",this.opsToPath(e)),o.setAttribute("stroke",s.fill||""),o.setAttribute("stroke-width",n+""),o.setAttribute("fill","none"),s.fillLineDash&&o.setAttribute("stroke-dasharray",s.fillLineDash.join(" ").trim()),s.fillLineDashOffset&&o.setAttribute("stroke-dashoffset",""+s.fillLineDashOffset),o}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}opsToPath(t){return this.gen.opsToPath(t)}line(t,e,s,n,o){const a=this.gen.line(t,e,s,n,o);return this.draw(a)}rectangle(t,e,s,n,o){const a=this.gen.rectangle(t,e,s,n,o);return this.draw(a)}ellipse(t,e,s,n,o){const a=this.gen.ellipse(t,e,s,n,o);return this.draw(a)}circle(t,e,s,n){const o=this.gen.circle(t,e,s,n);return this.draw(o)}linearPath(t,e){const s=this.gen.linearPath(t,e);return this.draw(s)}polygon(t,e){const s=this.gen.polygon(t,e);return this.draw(s)}arc(t,e,s,n,o,a,r=!1,h){const i=this.gen.arc(t,e,s,n,o,a,r,h);return this.draw(i)}curve(t,e){const s=this.gen.curve(t,e);return this.draw(s)}path(t,e){const s=this.gen.path(t,e);return this.draw(s)}}var st={canvas:(t,e)=>new Y(t,e),svg:(t,e)=>new et(t,e),generator:t=>new U(t),newSeed:()=>U.newSeed()};export default st; diff --git a/node_modules/roughjs/bundled/rough.js b/node_modules/roughjs/bundled/rough.js new file mode 100644 index 0000000..4e09601 --- /dev/null +++ b/node_modules/roughjs/bundled/rough.js @@ -0,0 +1 @@ +var rough=function(){"use strict";function t(t,e,s){if(t&&t.length){const[n,o]=e,a=Math.PI/180*s,r=Math.cos(a),h=Math.sin(a);t.forEach(t=>{const[e,s]=t;t[0]=(e-n)*r-(s-o)*h+n,t[1]=(e-n)*h+(s-o)*r+o})}}function e(t){const e=t[0],s=t[1];return Math.sqrt(Math.pow(e[0]-s[0],2)+Math.pow(e[1]-s[1],2))}function s(t,e,s,n){const o=e[1]-t[1],a=t[0]-e[0],r=o*t[0]+a*t[1],h=n[1]-s[1],i=s[0]-n[0],c=h*s[0]+i*s[1],l=o*i-h*a;return l?[(i*r-a*c)/l,(o*c-h*r)/l]:null}function n(t,e,s){const n=t.length;if(n<3)return!1;const h=[Number.MAX_SAFE_INTEGER,s],i=[e,s];let c=0;for(let e=0;e=Math.min(t[0],s[0])&&e[1]<=Math.max(t[1],s[1])&&e[1]>=Math.min(t[1],s[1])}function a(t,e,s){const n=(e[1]-t[1])*(s[0]-e[0])-(e[0]-t[0])*(s[1]-e[1]);return 0===n?0:n>0?1:2}function r(t,e,s,n){const r=a(t,e,s),h=a(t,e,n),i=a(s,n,t),c=a(s,n,e);return r!==h&&i!==c||(!(0!==r||!o(t,s,e))||(!(0!==h||!o(t,n,e))||(!(0!==i||!o(s,t,n))||!(0!==c||!o(s,e,n)))))}function h(e,s){const n=[0,0],o=Math.round(s.hachureAngle+90);o&&t(e,n,o);const a=function(t,e){const s=[...t];s[0].join(",")!==s[s.length-1].join(",")&&s.push([s[0][0],s[0][1]]);const n=[];if(s&&s.length>2){let t=e.hachureGap;t<0&&(t=4*e.strokeWidth),t=Math.max(t,.1);const o=[];for(let t=0;tt.ymine.ymin?1:t.xe.x?1:t.ymax===e.ymax?0:(t.ymax-e.ymax)/Math.abs(t.ymax-e.ymax)),!o.length)return n;let a=[],r=o[0].ymin;for(;a.length||o.length;){if(o.length){let t=-1;for(let e=0;er);e++)t=e;o.splice(0,t+1).forEach(t=>{a.push({s:r,edge:t})})}if(a=a.filter(t=>!(t.edge.ymax<=r)),a.sort((t,e)=>t.edge.x===e.edge.x?0:(t.edge.x-e.edge.x)/Math.abs(t.edge.x-e.edge.x)),a.length>1)for(let t=0;t=a.length)break;const s=a[t].edge,o=a[e].edge;n.push([[Math.round(s.x),r],[Math.round(o.x),r]])}r+=t,a.forEach(e=>{e.edge.x=e.edge.x+t*e.edge.islope})}}return n}(e,s);return o&&(t(e,n,-o),function(e,s,n){const o=[];e.forEach(t=>o.push(...t)),t(o,s,n)}(a,n,-o)),a}class i{constructor(t){this.helper=t}fillPolygon(t,e){return this._fillPolygon(t,e)}_fillPolygon(t,e,s=!1){let n=h(t,e);if(s){const e=this.connectingLines(t,n);n=n.concat(e)}return{type:"fillSketch",ops:this.renderLines(n,e)}}renderLines(t,e){const s=[];for(const n of t)s.push(...this.helper.doubleLineOps(n[0][0],n[0][1],n[1][0],n[1][1],e));return s}connectingLines(t,s){const n=[];if(s.length>1)for(let o=1;o3){const e=this.splitOnIntersections(t,r);n.push(...e)}}return n}midPointInPolygon(t,e){return n(t,(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2)}splitOnIntersections(t,o){const a=Math.max(5,.1*e(o)),h=[];for(let n=0;na&&n>a&&h.push({point:t,distance:s})}}}if(h.length>1){const e=h.sort((t,e)=>t.distance-e.distance).map(t=>t.point);if(n(t,...o[0])||e.shift(),n(t,...o[1])||e.pop(),e.length<=1)return this.midPointInPolygon(t,o)?[o]:[];const s=[o[0],...e,o[1]],a=[];for(let e=0;e{const r=e(t),h=Math.floor(r/(n+o)),i=(r+o-h*(n+o))/2;let c=t[0],l=t[1];c[0]>l[0]&&(c=t[1],l=t[0]);const u=Math.atan((l[1]-c[1])/(l[0]-c[0]));for(let t=0;t{const a=e(t),r=Math.round(a/(2*s));let h=t[0],i=t[1];h[0]>i[0]&&(h=t[1],i=t[0]);const c=Math.atan((i[1]-h[1])/(i[0]-h[0]));for(let t=0;tn%2?t+s:t+e);a.push({key:"C",data:t}),e=t[4],s=t[5];break}case"Q":a.push({key:"Q",data:[...h]}),e=h[2],s=h[3];break;case"q":{const t=h.map((t,n)=>n%2?t+s:t+e);a.push({key:"Q",data:t}),e=t[2],s=t[3];break}case"A":a.push({key:"A",data:[...h]}),e=h[5],s=h[6];break;case"a":e+=h[5],s+=h[6],a.push({key:"A",data:[h[0],h[1],h[2],h[3],h[4],e,s]});break;case"H":a.push({key:"H",data:[...h]}),e=h[0];break;case"h":e+=h[0],a.push({key:"H",data:[e]});break;case"V":a.push({key:"V",data:[...h]}),s=h[0];break;case"v":s+=h[0],a.push({key:"V",data:[s]});break;case"S":a.push({key:"S",data:[...h]}),e=h[2],s=h[3];break;case"s":{const t=h.map((t,n)=>n%2?t+s:t+e);a.push({key:"S",data:t}),e=t[2],s=t[3];break}case"T":a.push({key:"T",data:[...h]}),e=h[0],s=h[1];break;case"t":e+=h[0],s+=h[1],a.push({key:"T",data:[e,s]});break;case"Z":case"z":a.push({key:"Z",data:[]}),e=n,s=o}return a}function m(t){const e=[];let s="",n=0,o=0,a=0,r=0,h=0,i=0;for(const{key:c,data:l}of t){switch(c){case"M":e.push({key:"M",data:[...l]}),[n,o]=l,[a,r]=l;break;case"C":e.push({key:"C",data:[...l]}),n=l[4],o=l[5],h=l[2],i=l[3];break;case"L":e.push({key:"L",data:[...l]}),[n,o]=l;break;case"H":n=l[0],e.push({key:"L",data:[n,o]});break;case"V":o=l[0],e.push({key:"L",data:[n,o]});break;case"S":{let t=0,a=0;"C"===s||"S"===s?(t=n+(n-h),a=o+(o-i)):(t=n,a=o),e.push({key:"C",data:[t,a,...l]}),h=l[0],i=l[1],n=l[2],o=l[3];break}case"T":{const[t,a]=l;let r=0,c=0;"Q"===s||"T"===s?(r=n+(n-h),c=o+(o-i)):(r=n,c=o);const u=n+2*(r-n)/3,f=o+2*(c-o)/3,p=t+2*(r-t)/3,d=a+2*(c-a)/3;e.push({key:"C",data:[u,f,p,d,t,a]}),h=r,i=c,n=t,o=a;break}case"Q":{const[t,s,a,r]=l,c=n+2*(t-n)/3,u=o+2*(s-o)/3,f=a+2*(t-a)/3,p=r+2*(s-r)/3;e.push({key:"C",data:[c,u,f,p,a,r]}),h=t,i=s,n=a,o=r;break}case"A":{const t=Math.abs(l[0]),s=Math.abs(l[1]),a=l[2],r=l[3],h=l[4],i=l[5],c=l[6];if(0===t||0===s)e.push({key:"C",data:[n,o,i,c,i,c]}),n=i,o=c;else if(n!==i||o!==c){P(n,o,i,c,t,s,a,r,h).forEach((function(t){e.push({key:"C",data:t})})),n=i,o=c}break}case"Z":e.push({key:"Z",data:[]}),n=a,o=r}s=c}return e}function w(t,e,s){return[t*Math.cos(s)-e*Math.sin(s),t*Math.sin(s)+e*Math.cos(s)]}function P(t,e,s,n,o,a,r,h,i,c){const l=(u=r,Math.PI*u/180);var u;let f=[],p=0,d=0,g=0,M=0;if(c)[p,d,g,M]=c;else{[t,e]=w(t,e,-l),[s,n]=w(s,n,-l);const r=(t-s)/2,c=(e-n)/2;let u=r*r/(o*o)+c*c/(a*a);u>1&&(u=Math.sqrt(u),o*=u,a*=u);const f=o*o,k=a*a,b=f*k-f*c*c-k*r*r,y=f*c*c+k*r*r,m=(h===i?-1:1)*Math.sqrt(Math.abs(b/y));g=m*o*c/a+(t+s)/2,M=m*-a*r/o+(e+n)/2,p=Math.asin(parseFloat(((e-M)/a).toFixed(9))),d=Math.asin(parseFloat(((n-M)/a).toFixed(9))),td&&(p-=2*Math.PI),!i&&d>p&&(d-=2*Math.PI)}let k=d-p;if(Math.abs(k)>120*Math.PI/180){const t=d,e=s,h=n;d=i&&d>p?p+120*Math.PI/180*1:p+120*Math.PI/180*-1,f=P(s=g+o*Math.cos(d),n=M+a*Math.sin(d),e,h,o,a,r,0,i,[d,t,g,M])}k=d-p;const b=Math.cos(p),y=Math.sin(p),m=Math.cos(d),x=Math.sin(d),v=Math.tan(k/4),O=4/3*o*v,S=4/3*a*v,L=[t,e],T=[t+O*y,e-S*b],I=[s+O*x,n-S*m],A=[s,n];if(T[0]=2*L[0]-T[0],T[1]=2*L[1]-T[1],c)return[T,I,A].concat(f);{f=[T,I,A].concat(f);const t=[];for(let e=0;e2){const o=[];for(let e=0;e2*Math.PI&&(p=0,d=2*Math.PI);const g=2*Math.PI/i.curveStepCount,M=Math.min(g/2,(d-p)/2),k=F(M,c,l,u,f,p,d,1,i);if(!i.disableMultiStroke){const t=F(M,c,l,u,f,p,d,1.5,i);k.push(...t)}return r&&(h?k.push(...z(c,l,c+u*Math.cos(p),l+f*Math.sin(p),i),...z(c,l,c+u*Math.cos(d),l+f*Math.sin(d),i)):k.push({op:"lineTo",data:[c,l]},{op:"lineTo",data:[c+u*Math.cos(p),l+f*Math.sin(p)]})),{type:"path",ops:k}}function _(t,e){const s=[];if(t.length){const n=e.maxRandomnessOffset||0,o=t.length;if(o>2){s.push({op:"move",data:[t[0][0]+W(n,e),t[0][1]+W(n,e)]});for(let a=1;a500?.4:-.0016668*i+1.233334;let l=o.maxRandomnessOffset||0;l*l*100>h&&(l=i/10);const u=l/2,f=.2+.2*D(o);let p=o.bowing*o.maxRandomnessOffset*(n-e)/200,d=o.bowing*o.maxRandomnessOffset*(t-s)/200;p=W(p,o,c),d=W(d,o,c);const g=[],M=()=>W(u,o,c),k=()=>W(l,o,c);return a&&(r?g.push({op:"move",data:[t+M(),e+M()]}):g.push({op:"move",data:[t+W(l,o,c),e+W(l,o,c)]})),r?g.push({op:"bcurveTo",data:[p+t+(s-t)*f+M(),d+e+(n-e)*f+M(),p+t+2*(s-t)*f+M(),d+e+2*(n-e)*f+M(),s+M(),n+M()]}):g.push({op:"bcurveTo",data:[p+t+(s-t)*f+k(),d+e+(n-e)*f+k(),p+t+2*(s-t)*f+k(),d+e+2*(n-e)*f+k(),s+k(),n+k()]}),g}function $(t,e,s){const n=[];n.push([t[0][0]+W(e,s),t[0][1]+W(e,s)]),n.push([t[0][0]+W(e,s),t[0][1]+W(e,s)]);for(let o=1;o3){const a=[],r=1-s.curveTightness;o.push({op:"move",data:[t[1][0],t[1][1]]});for(let e=1;e+21&&o.push(s)}else o.push(s);o.push(t[e+3])}else{const n=.5,a=t[e+0],r=t[e+1],h=t[e+2],i=t[e+3],c=H(a,r,n),l=H(r,h,n),u=H(h,i,n),f=H(c,l,n),p=H(l,u,n),d=H(f,p,n);V([a,c,f,d],0,s,o),V([d,p,u,i],0,s,o)}var a,r;return o}function B(t,e){return X(t,0,t.length,e)}function X(t,e,s,n,o){const a=o||[],r=t[e],h=t[s-1];let i=0,c=1;for(let n=e+1;ni&&(i=e,c=n)}return Math.sqrt(i)>n?(X(t,e,c+1,n,a),X(t,c,s,n,a)):(a.length||a.push(r),a.push(h)),a}function J(t,e=.15,s){const n=[],o=(t.length-1)/3;for(let s=0;s0?X(n,0,n.length,s):n}const K="none";class U{constructor(t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveFitting:.95,curveStepCount:9,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1,dashOffset:-1,dashGap:-1,zigzagOffset:-1,seed:0,combineNestedSvgPaths:!1,disableMultiStroke:!1,disableMultiStrokeFill:!1},this.config=t||{},this.config.options&&(this.defaultOptions=this._o(this.config.options))}static newSeed(){return Math.floor(Math.random()*2**31)}_o(t){return t?Object.assign({},this.defaultOptions,t):this.defaultOptions}_d(t,e,s){return{shape:t,sets:e||[],options:s||this.defaultOptions}}line(t,e,s,n,o){const a=this._o(o);return this._d("line",[v(t,e,s,n,a)],a)}rectangle(t,e,s,n,o){const a=this._o(o),r=[],h=S(t,e,s,n,a);if(a.fill){const o=[[t,e],[t+s,e],[t+s,e+n],[t,e+n]];"solid"===a.fillStyle?r.push(_(o,a)):r.push(C(o,a))}return a.stroke!==K&&r.push(h),this._d("rectangle",r,a)}ellipse(t,e,s,n,o){const a=this._o(o),r=[],h=T(s,n,a),i=I(t,e,a,h);if(a.fill)if("solid"===a.fillStyle){const s=I(t,e,a,h).opset;s.type="fillPath",r.push(s)}else r.push(C(i.estimatedPoints,a));return a.stroke!==K&&r.push(i.opset),this._d("ellipse",r,a)}circle(t,e,s,n){const o=this.ellipse(t,e,s,s,n);return o.shape="circle",o}linearPath(t,e){const s=this._o(e);return this._d("linearPath",[O(t,!1,s)],s)}arc(t,e,s,n,o,a,r=!1,h){const i=this._o(h),c=[],l=A(t,e,s,n,o,a,r,!0,i);if(r&&i.fill)if("solid"===i.fillStyle){const r=A(t,e,s,n,o,a,!0,!1,i);r.type="fillPath",c.push(r)}else c.push(function(t,e,s,n,o,a,r){const h=t,i=e;let c=Math.abs(s/2),l=Math.abs(n/2);c+=W(.01*c,r),l+=W(.01*l,r);let u=o,f=a;for(;u<0;)u+=2*Math.PI,f+=2*Math.PI;f-u>2*Math.PI&&(u=0,f=2*Math.PI);const p=(f-u)/r.curveStepCount,d=[];for(let t=u;t<=f;t+=p)d.push([h+c*Math.cos(t),i+l*Math.sin(t)]);return d.push([h+c*Math.cos(f),i+l*Math.sin(f)]),d.push([h,i]),C(d,r)}(t,e,s,n,o,a,i));return i.stroke!==K&&c.push(l),this._d("arc",c,i)}curve(t,e){const s=this._o(e),n=[],o=L(t,s);if(s.fill&&s.fill!==K&&t.length>=3){const e=J(function(t,e=0){const s=t.length;if(s<3)throw new Error("A curve must have at least three points.");const n=[];if(3===s)n.push(N(t[0]),N(t[1]),N(t[2]),N(t[2]));else{const s=[];s.push(t[0],t[0]);for(let e=1;e{h.length>=4&&a.push(...J(h,e)),h=[]},c=()=>{i(),a.length&&(o.push(a),a=[])};for(const{key:t,data:e}of n)switch(t){case"M":c(),r=[e[0],e[1]],a.push(r);break;case"L":i(),a.push([e[0],e[1]]);break;case"C":if(!h.length){const t=a.length?a[a.length-1]:r;h.push([t[0],t[1]])}h.push([e[0],e[1]]),h.push([e[2],e[3]]),h.push([e[4],e[5]]);break;case"Z":i(),a.push([r[0],r[1]])}if(c(),!s)return o;const l=[];for(const t of o){const e=B(t,s);e.length&&l.push(e)}return l}(t,1,r?4-4*s.simplification:(1+s.roughness)/2);if(o)if(s.combineNestedSvgPaths){const t=[];h.forEach(e=>t.push(...e)),"solid"===s.fillStyle?n.push(_(t,s)):n.push(C(t,s))}else h.forEach(t=>{"solid"===s.fillStyle?n.push(_(t,s)):n.push(C(t,s))});return a&&(r?h.forEach(t=>{n.push(O(t,!1,s))}):n.push(function(t,e){const s=m(y(b(t))),n=[];let o=[0,0],a=[0,0];for(const{key:t,data:r}of s)switch(t){case"M":{const t=1*(e.maxRandomnessOffset||0);n.push({op:"move",data:r.map(s=>s+W(t,e))}),a=[r[0],r[1]],o=[r[0],r[1]];break}case"L":n.push(...z(a[0],a[1],r[0],r[1],e)),a=[r[0],r[1]];break;case"C":{const[t,s,o,h,i,c]=r;n.push(...j(t,s,o,h,i,c,a,e)),a=[i,c];break}case"Z":n.push(...z(a[0],a[1],o[0],o[1],e)),a=[o[0],o[1]]}return{type:"path",ops:n}}(t,s))),this._d("path",n,s)}opsToPath(t){let e="";for(const s of t.ops){const t=s.data;switch(s.op){case"move":e+=`M${t[0]} ${t[1]} `;break;case"bcurveTo":e+=`C${t[0]} ${t[1]}, ${t[2]} ${t[3]}, ${t[4]} ${t[5]} `;break;case"lineTo":e+=`L${t[0]} ${t[1]} `}}return e.trim()}toPaths(t){const e=t.sets||[],s=t.options||this.defaultOptions,n=[];for(const t of e){let e=null;switch(t.type){case"path":e={d:this.opsToPath(t),stroke:s.stroke,strokeWidth:s.strokeWidth,fill:K};break;case"fillPath":e={d:this.opsToPath(t),stroke:K,strokeWidth:0,fill:s.fill||K};break;case"fillSketch":e=this.fillSketch(t,s)}e&&n.push(e)}return n}fillSketch(t,e){let s=e.fillWeight;return s<0&&(s=e.strokeWidth/2),{d:this.opsToPath(t),stroke:e.fill||K,strokeWidth:s,fill:K}}}class Y{constructor(t,e){this.canvas=t,this.ctx=this.canvas.getContext("2d"),this.gen=new U(e)}draw(t){const e=t.sets||[],s=t.options||this.getDefaultOptions(),n=this.ctx;for(const o of e)switch(o.type){case"path":n.save(),n.strokeStyle="none"===s.stroke?"transparent":s.stroke,n.lineWidth=s.strokeWidth,s.strokeLineDash&&n.setLineDash(s.strokeLineDash),s.strokeLineDashOffset&&(n.lineDashOffset=s.strokeLineDashOffset),this._drawToContext(n,o),n.restore();break;case"fillPath":n.save(),n.fillStyle=s.fill||"";const e="curve"===t.shape||"polygon"===t.shape?"evenodd":"nonzero";this._drawToContext(n,o,e),n.restore();break;case"fillSketch":this.fillSketch(n,o,s)}}fillSketch(t,e,s){let n=s.fillWeight;n<0&&(n=s.strokeWidth/2),t.save(),s.fillLineDash&&t.setLineDash(s.fillLineDash),s.fillLineDashOffset&&(t.lineDashOffset=s.fillLineDashOffset),t.strokeStyle=s.fill||"",t.lineWidth=n,this._drawToContext(t,e),t.restore()}_drawToContext(t,e,s="nonzero"){t.beginPath();for(const s of e.ops){const e=s.data;switch(s.op){case"move":t.moveTo(e[0],e[1]);break;case"bcurveTo":t.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5]);break;case"lineTo":t.lineTo(e[0],e[1])}}"fillPath"===e.type?t.fill(s):t.stroke()}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}line(t,e,s,n,o){const a=this.gen.line(t,e,s,n,o);return this.draw(a),a}rectangle(t,e,s,n,o){const a=this.gen.rectangle(t,e,s,n,o);return this.draw(a),a}ellipse(t,e,s,n,o){const a=this.gen.ellipse(t,e,s,n,o);return this.draw(a),a}circle(t,e,s,n){const o=this.gen.circle(t,e,s,n);return this.draw(o),o}linearPath(t,e){const s=this.gen.linearPath(t,e);return this.draw(s),s}polygon(t,e){const s=this.gen.polygon(t,e);return this.draw(s),s}arc(t,e,s,n,o,a,r=!1,h){const i=this.gen.arc(t,e,s,n,o,a,r,h);return this.draw(i),i}curve(t,e){const s=this.gen.curve(t,e);return this.draw(s),s}path(t,e){const s=this.gen.path(t,e);return this.draw(s),s}}const tt="http://www.w3.org/2000/svg";class et{constructor(t,e){this.svg=t,this.gen=new U(e)}draw(t){const e=t.sets||[],s=t.options||this.getDefaultOptions(),n=this.svg.ownerDocument||window.document,o=n.createElementNS(tt,"g");for(const a of e){let e=null;switch(a.type){case"path":e=n.createElementNS(tt,"path"),e.setAttribute("d",this.opsToPath(a)),e.setAttribute("stroke",s.stroke),e.setAttribute("stroke-width",s.strokeWidth+""),e.setAttribute("fill","none"),s.strokeLineDash&&e.setAttribute("stroke-dasharray",s.strokeLineDash.join(" ").trim()),s.strokeLineDashOffset&&e.setAttribute("stroke-dashoffset",""+s.strokeLineDashOffset);break;case"fillPath":e=n.createElementNS(tt,"path"),e.setAttribute("d",this.opsToPath(a)),e.setAttribute("stroke","none"),e.setAttribute("stroke-width","0"),e.setAttribute("fill",s.fill||""),"curve"!==t.shape&&"polygon"!==t.shape||e.setAttribute("fill-rule","evenodd");break;case"fillSketch":e=this.fillSketch(n,a,s)}e&&o.appendChild(e)}return o}fillSketch(t,e,s){let n=s.fillWeight;n<0&&(n=s.strokeWidth/2);const o=t.createElementNS(tt,"path");return o.setAttribute("d",this.opsToPath(e)),o.setAttribute("stroke",s.fill||""),o.setAttribute("stroke-width",n+""),o.setAttribute("fill","none"),s.fillLineDash&&o.setAttribute("stroke-dasharray",s.fillLineDash.join(" ").trim()),s.fillLineDashOffset&&o.setAttribute("stroke-dashoffset",""+s.fillLineDashOffset),o}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}opsToPath(t){return this.gen.opsToPath(t)}line(t,e,s,n,o){const a=this.gen.line(t,e,s,n,o);return this.draw(a)}rectangle(t,e,s,n,o){const a=this.gen.rectangle(t,e,s,n,o);return this.draw(a)}ellipse(t,e,s,n,o){const a=this.gen.ellipse(t,e,s,n,o);return this.draw(a)}circle(t,e,s,n){const o=this.gen.circle(t,e,s,n);return this.draw(o)}linearPath(t,e){const s=this.gen.linearPath(t,e);return this.draw(s)}polygon(t,e){const s=this.gen.polygon(t,e);return this.draw(s)}arc(t,e,s,n,o,a,r=!1,h){const i=this.gen.arc(t,e,s,n,o,a,r,h);return this.draw(i)}curve(t,e){const s=this.gen.curve(t,e);return this.draw(s)}path(t,e){const s=this.gen.path(t,e);return this.draw(s)}}return{canvas:(t,e)=>new Y(t,e),svg:(t,e)=>new et(t,e),generator:t=>new U(t),newSeed:()=>U.newSeed()}}(); diff --git a/node_modules/roughjs/package.json b/node_modules/roughjs/package.json new file mode 100644 index 0000000..58fc28f --- /dev/null +++ b/node_modules/roughjs/package.json @@ -0,0 +1,74 @@ +{ + "_from": "roughjs", + "_id": "roughjs@4.3.1", + "_inBundle": false, + "_integrity": "sha512-m42+OBaBR7x5UhIKyjBCnWqqkaEkBKLkXvHv4pOWJXPofvMnQY4ZcFEQlqf3coKKyZN2lfWMyx7QXSg2GD7SGA==", + "_location": "/roughjs", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "roughjs", + "name": "roughjs", + "escapedName": "roughjs", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.3.1.tgz", + "_shasum": "b7af0b205c94bc3b79ee5a7eae1e09d5063bc3fe", + "_spec": "roughjs", + "_where": "/Users/1009738/PracticalVision", + "author": { + "name": "Preet Shihn", + "email": "preetshihn@gmail.com" + }, + "browser": "bundled/rough.js", + "bugs": { + "url": "https://github.com/pshihn/rough/issues" + }, + "bundleDependencies": false, + "dependencies": { + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + }, + "deprecated": false, + "description": "Create graphics using HTML Canvas or SVG with a hand-drawn, sketchy, appearance.", + "devDependencies": { + "@rollup/plugin-typescript": "^2.1.0", + "rollup": "^1.32.1", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-terser": "^5.3.0", + "tslint": "^5.20.1", + "typescript": "^3.8.3" + }, + "homepage": "https://roughjs.com", + "keywords": [ + "canvas", + "svg", + "graphics", + "sketchy", + "hand drawn", + "hand-drawn" + ], + "license": "MIT", + "main": "bundled/rough.cjs.js", + "module": "bundled/rough.esm.js", + "name": "roughjs", + "repository": { + "type": "git", + "url": "git+https://github.com/pshihn/rough.git" + }, + "scripts": { + "build": "rm -rf bin && tsc && rollup -c", + "lint": "tslint -p tsconfig.json", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "types": "bin/rough.d.ts", + "version": "4.3.1" +} diff --git a/node_modules/roughjs/tsconfig.json b/node_modules/roughjs/tsconfig.json new file mode 100644 index 0000000..747687a --- /dev/null +++ b/node_modules/roughjs/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "es2015", + "moduleResolution": "node", + "lib": [ + "es2017", + "dom" + ], + "declaration": true, + "outDir": "./bin", + "baseUrl": ".", + "strict": true, + "strictNullChecks": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5289ca0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,35 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==" + }, + "points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==" + }, + "points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "requires": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "roughjs": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.3.1.tgz", + "integrity": "sha512-m42+OBaBR7x5UhIKyjBCnWqqkaEkBKLkXvHv4pOWJXPofvMnQY4ZcFEQlqf3coKKyZN2lfWMyx7QXSg2GD7SGA==", + "requires": { + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + } + } +} diff --git a/web/index.html b/web/index.html index 6fa1984..b5cf098 100644 --- a/web/index.html +++ b/web/index.html @@ -7,10 +7,13 @@ Practical Vision - + + + + - +

PRACTICAL
VISIONS

diff --git a/web/p5.scribble.js b/web/p5.scribble.js new file mode 100644 index 0000000..949fff5 --- /dev/null +++ b/web/p5.scribble.js @@ -0,0 +1,458 @@ +/* +This file contains functions for drawing 2d primitives with a handy sketchy look in p5.js. + +Author: Janneck Wullschleger in 07/2016 +Web: http://itsjw.de +Mail: jw@itsjw.de + +Updated: 24.02.2017 to use with a reference to the p5 instance. +Just put it in as param to the constructor. + +Much of the source code is taken from the handy library for processing, +written by Jo Wood, giCentre, City University London based on an idea by Nikolaus Gradwohl. +The handy library is licensed under the GNU Lesser General Public License: http://www.gnu.org/licenses/. +*/ + +function Scribble(p) { + this.sketch = p || window; + this.bowing = 1; + this.roughness = 1; + this.maxOffset = 2; + this.numEllipseSteps = 9; + this.ellipseInc = (Math.PI*2)/this.numEllipseSteps; + + this.getOffset = function( minVal, maxVal ) { + return this.roughness*(this.sketch.random()*(maxVal-minVal)+minVal); + } + + this.buildEllipse = function( cx, cy, rx, ry, offset, overlap ) { + var radialOffset = this.getOffset( -0.5, 0.5 )-Math.PI/2; + + this.sketch.beginShape(); + this.sketch.curveVertex( this.getOffset( -offset, offset )+cx+0.9*rx*Math.cos( radialOffset-this.ellipseInc ), + this.getOffset( -offset, offset )+cy+0.9*ry*Math.sin( radialOffset-this.ellipseInc ) ); + + for ( var theta = radialOffset; theta < Math.PI*2+radialOffset-0.01; theta+=this.ellipseInc ) { + this.sketch.curveVertex( this.getOffset( -offset, offset )+cx+rx*Math.cos( theta ), + this.getOffset( -offset, offset )+cy+ry*Math.sin( theta ) ); + } + + this.sketch.curveVertex( this.getOffset( -offset, offset )+cx+rx*Math.cos( radialOffset+Math.PI*2+overlap*0.5 ), + this.getOffset( -offset, offset )+cy+ry*Math.sin( radialOffset+Math.PI*2+overlap*0.5 ) ); + + this.sketch.curveVertex( this.getOffset( -offset, offset )+cx+0.98*rx*Math.cos( radialOffset+overlap ), + this.getOffset( -offset, offset )+cy+0.98*ry*Math.sin( radialOffset+overlap ) ); + + this.sketch.curveVertex( this.getOffset( -offset, offset )+cx+0.9*rx*Math.cos( radialOffset+overlap*0.5 ), + this.getOffset( -offset, offset )+cy+0.9*ry*Math.sin( radialOffset+overlap*0.5 ) ); + this.sketch.endShape(); + } + + this.getIntersectingLines = function( lineCoords, xCoords, yCoords ) { + var intersections = []; + var s1 = new Segment( lineCoords[0], lineCoords[1], lineCoords[2], lineCoords[3] ); + + for ( var i = 0; i < xCoords.length; i++ ) { + var s2 = new Segment( xCoords[i], yCoords[i], xCoords[(i+1)%xCoords.length], yCoords[(i+1)%xCoords.length] ); + + if ( s1.compare(s2) == Relation.INTERSECTS ) { + intersections.push( [s1.getIntersectionX(), s1.getIntersectionY()] ); + } + } + return intersections; + } + + this.scribbleLine = function( x1, y1, x2, y2 ) { + var lenSq = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2); + var offset = this.maxOffset; + + if ( this.maxOffset*this.maxOffset*100 > lenSq ) { + offset = Math.sqrt( lenSq )/10; + } + + var halfOffset = offset/2; + var divergePoint = 0.2 + this.sketch.random()*0.2; + var midDispX = this.bowing*this.maxOffset*(y2-y1)/200; + var midDispY = this.bowing*this.maxOffset*(x1-x2)/200; + midDispX = this.getOffset( -midDispX, midDispX ); + midDispY = this.getOffset( -midDispY, midDispY ); + + this.sketch.noFill(); + + this.sketch.beginShape(); + this.sketch.vertex( x1 + this.getOffset( -offset, offset ), y1 + this.getOffset( -offset, offset ) ); + this.sketch.curveVertex(x1 + this.getOffset( -offset, offset ), y1 + this.getOffset( -offset, offset ) ); + this.sketch.curveVertex(midDispX+x1+(x2 -x1)*divergePoint + this.getOffset( -offset, offset ), midDispY+y1 + (y2-y1)*divergePoint + this.getOffset( -offset, offset ) ); + this.sketch.curveVertex(midDispX+x1+2*(x2-x1)*divergePoint + this.getOffset( -offset, offset ), midDispY+y1+ 2*(y2-y1)*divergePoint + this.getOffset( -offset,offset ) ); + this.sketch.curveVertex(x2 + this.getOffset( -offset, offset ), y2 + this.getOffset( -offset, offset ) ); + this.sketch.vertex( x2 + this.getOffset( -offset, offset ), y2 + this.getOffset( -offset, offset ) ); + this.sketch.endShape(); + + this.sketch.beginShape(); + this.sketch.vertex( x1 + this.getOffset( -halfOffset, halfOffset ), y1 + this.getOffset( -halfOffset, halfOffset ) ); + this.sketch.curveVertex(x1 + this.getOffset( -halfOffset, halfOffset ), y1 + this.getOffset( -halfOffset, halfOffset ) ); + this.sketch.curveVertex(midDispX+x1+(x2 -x1)*divergePoint + this.getOffset( -halfOffset, halfOffset ), midDispY+y1 + (y2-y1)*divergePoint + this.getOffset( -halfOffset, halfOffset ) ); + this.sketch.curveVertex(midDispX+x1+2*(x2-x1)*divergePoint + this.getOffset( -halfOffset, halfOffset ), midDispY+y1+ 2*(y2-y1)*divergePoint + this.getOffset( -halfOffset, halfOffset ) ); + this.sketch.curveVertex(x2 + this.getOffset( -halfOffset, halfOffset ), y2 + this.getOffset( -halfOffset, halfOffset ) ); + this.sketch.vertex( x2 + this.getOffset( -halfOffset, halfOffset ), y2 + this.getOffset( -halfOffset, halfOffset ) ); + this.sketch.endShape(); + } + + this.scribbleCurve = function( x1, y1, x2, y2, x3, y3, x4, y4 ) { + this.sketch.bezier( x1+this.getOffset( -2, 2 ), y1+this.getOffset( -2, 2 ), + x3+this.getOffset( -4, 4 ), y3+this.getOffset( -3, 3 ), + x4+this.getOffset( -3, 3 ), y4+this.getOffset( -3, 3 ), + x2+this.getOffset( -1, 1 ), y2+this.getOffset( -1, 1 ) ); + + this.sketch.bezier( x1+this.getOffset( -2, 2 ), y1+this.getOffset( -2, 2 ), + x3+this.getOffset( -3, 3 ), y3+this.getOffset( -3, 3 ), + x4+this.getOffset( -3, 3 ), y4+this.getOffset( -4, 4 ), + x2+this.getOffset( -2, 2 ), y2+this.getOffset( -2, 2 ) ); + } + + this.scribbleRect = function( x, y, w, h ) { + var halfWidth = w/2; + var halfHeight = h/2; + var left = Math.min( x-halfWidth, x+halfWidth ); + var right = Math.max( x-halfWidth, x+halfWidth ); + var top = Math.min( y-halfHeight, y+halfHeight ); + var bottom = Math.max( y-halfHeight, y+halfHeight ); + + this.scribbleLine( left, top, right, top ); + this.scribbleLine( right, top, right, bottom ); + this.scribbleLine( right, bottom, left, bottom ); + this.scribbleLine( left, bottom, left, top ); + } + + this.scribbleRoundedRect = function( x, y, w, h, radius ) { + var halfWidth = w/2; + var halfHeight = h/2; + + if ( radius == 0 || radius > halfWidth || radius > halfHeight ) { + this.scribbleRect( x, y, w, h ); + return; + } + + var left = Math.min( x-halfWidth, x+halfWidth ); + var right = Math.max( x-halfWidth, x+halfWidth ); + var top = Math.min( y-halfHeight, y+halfHeight ); + var bottom = Math.max( y-halfHeight, y+halfHeight ); + + this.scribbleLine( left+radius, top, right-radius, top, 1.5 ); + this.scribbleLine( right, top+radius, right, bottom-radius, 1.5 ); + this.scribbleLine( right-radius, bottom, left+radius, bottom, 1.5 ); + this.scribbleLine( left, bottom-radius, left, top+radius, 1.5 ); + + this.scribbleCurve( left+radius, top, left, top+radius, left+radius*0.1, top+radius*0.1, left+radius*0.1, top+radius*0.1 ); + this.scribbleCurve( right-radius, top, right, top+radius, right-radius*0.1, top+radius*0.1, right-radius*0.1, top+radius*0.1 ); + this.scribbleCurve( left+radius, bottom, left, bottom-radius, left+radius*0.1, bottom-radius*0.1, left+radius*0.1, bottom-radius*0.1 ); + this.scribbleCurve( right-radius, bottom, right, bottom-radius, right-radius*0.1, bottom-radius*0.1, right-radius*0.1, bottom-radius*0.1 ); + } + + this.scribbleEllipse = function( x, y, w, h ) { + var rx = Math.abs(w/2); + var ry = Math.abs(h/2); + + rx += this.getOffset( -rx*0.05, rx*0.05 ); + ry += this.getOffset( -ry*0.05, ry*0.05 ); + + this.buildEllipse( x, y, rx, ry, 1, this.ellipseInc*this.getOffset( 0.1, this.getOffset( 0.4, 1 ) ) ); + this.buildEllipse( x, y, rx, ry, 1.5, 0 ); + } + + this.scribbleFilling = function( xCoords, yCoords, gap, angle ) { + if ((xCoords == null) || (yCoords == null) || (xCoords.length == 0) || (yCoords.length == 0)) { + return; + } + + var hachureAngle = this.sketch.radians( angle%180 ); + var cosAngle = Math.cos( hachureAngle ); + var sinAngle = Math.sin( hachureAngle ); + var tanAngle = Math.tan( hachureAngle ); + + var left = xCoords[0]; + var right = xCoords[0]; + var top = yCoords[0]; + var bottom = yCoords[0]; + + for ( var i = 1; i < xCoords.length; i++ ) { + left = Math.min( left, xCoords[i] ); + right = Math.max( right, xCoords[i] ); + top = Math.min( top, yCoords[i] ); + bottom = Math.max( bottom, yCoords[i] ); + } + + var it = new HachureIterator( top-1, bottom+1, left-1, right+1, gap, sinAngle, cosAngle, tanAngle ); + var rectCoords = null; + + while ( (rectCoords = it.getNextLine()) != null ) { + var lines = this.getIntersectingLines( rectCoords, xCoords, yCoords ); + + for ( var i = 0; i < lines.length; i+=2 ) { + if ( i < lines.length-1 ) { + var p1 = lines[i]; + var p2 = lines[i+1]; + this.scribbleLine( p1[0], p1[1], p2[0], p2[1], 2 ); + } + } + } + } +} + + + +function HachureIterator( _top, _bottom, _left, _right, _gap, _sinAngle, _cosAngle, _tanAngle ) { + var sinAngle = _sinAngle; + var tanAngle = _tanAngle; + var top = _top; + var bottom = _bottom; + var left = _left; + var right = _right; + var gap = _gap; + + var pos; + var deltaX, hGap; + var sLeft, sRight; + + if (Math.abs(sinAngle) < 0.0001) { + pos = left+gap; + } else if (Math.abs(sinAngle) > 0.9999) { + pos = top+gap; + } else { + deltaX = (bottom-top)*Math.abs(tanAngle); + pos = left-Math.abs(deltaX); + hGap = Math.abs(gap / _cosAngle); + sLeft = new Segment(left, bottom, left, top); + sRight = new Segment(right, bottom, right, top); + } + + this.getNextLine = function() { + if (Math.abs(sinAngle) < 0.0001) { + if (pos < right) { + var line = [pos, top, pos, bottom]; + pos += gap; + return line; + } + } else if (Math.abs(sinAngle) > 0.9999) { + if (pos right) && (xUpper > right))) { + pos += hGap; + xLower = pos-deltaX/2; + xUpper = pos+deltaX/2; + + if (pos > right+deltaX) { + return null; + } + } + + var s = new Segment(xLower, yLower, xUpper, yUpper); + + if (s.compare(sLeft) == Relation.INTERSECTS) { + xLower = s.getIntersectionX(); + yLower = s.getIntersectionY(); + } + if (s.compare(sRight) == Relation.INTERSECTS) { + xUpper = s.getIntersectionX(); + yUpper = s.getIntersectionY(); + } + if (tanAngle > 0) { + xLower = right-(xLower-left); + xUpper = right-(xUpper-left); + } + + var line = [xLower, yLower, xUpper, yUpper]; + pos += hGap; + return line; + } + } + return null; + } +} + +function Segment( _x1, _y1, _x2, _y2 ) { + var x1 = _x1; + var y1 =_y1; + var x2 = _x2; + var y2 = _y2; + var a, b, c; + var undef; + var xi = Number.MAX_VALUE; + var yi = Number.MAX_VALUE; + + a=y2-y1; + b=x1-x2; + c=x2*y1-x1*y2; + + if ((a==0) && (b==0) && (c==0)) { + undef = true; + } else { + undef = false; + } + + this.compare = function( otherSegment ) { + if ((this.isUndefined()) || (otherSegment.isUndefined())) { + return Relation.UNDEFINED; + } + + var grad1 = Number.MAX_VALUE; + var grad2 = Number.MAX_VALUE; + var int1 = 0; + var int2 = 0; + + if (Math.abs(b) > 0.00001) { + grad1 = -a/b; + int1 = -c/b; + } + + if (Math.abs(otherSegment.getB()) > 0.00001) { + grad2 = -otherSegment.getA()/otherSegment.getB(); + int2 = -otherSegment.getC()/otherSegment.getB(); + } + + if (grad1 == Number.MAX_VALUE) { + if (grad2 == Number.MAX_VALUE) { + if (-c/a != -otherSegment.getC()/otherSegment.getA()) { + return Relation.SEPARATE; + } + + if ((y1 >= Math.min(otherSegment.getPy1(),otherSegment.getPy2())) && + (y1 <= Math.max(otherSegment.getPy1(),otherSegment.getPy2()))) { + xi = x1; + yi = y1; + return Relation.INTERSECTS; + } + + if ((y2 >= Math.min(otherSegment.getPy1(),otherSegment.getPy2())) && + (y2 <= Math.max(otherSegment.getPy1(),otherSegment.getPy2()))) { + xi = x2; + yi = y2; + return Relation.INTERSECTS; + } + + return Relation.SEPARATE; + } + + xi = x1; + yi = grad2*xi+int2; + + if (((y1-yi)*(yi-y2) < -0.00001) || ((otherSegment.getPy1()-yi)*(yi-otherSegment.getPy2()) < -0.00001)) { + return Relation.SEPARATE; + } + + if (Math.abs(otherSegment.getA()) < 0.00001) { + if ((otherSegment.getPx1()-xi)*(xi-otherSegment.getPx2()) < -0.00001) { + return Relation.SEPARATE; + } + return Relation.INTERSECTS; + } + return Relation.INTERSECTS; + } + + if (grad2 == Number.MAX_VALUE) { + xi = otherSegment.getPx1(); + yi = grad1*xi+int1; + + if (((otherSegment.getPy1()-yi)*(yi-otherSegment.getPy2()) < -0.00001) || ((y1-yi)*(yi-y2) < -0.00001)) { + return Relation.SEPARATE; + } + + if (Math.abs(a) < 0.00001) { + if ((x1-xi)*(xi-x2) < -0.00001) { + return Relation.SEPARATE; + } + return Relation.INTERSECTS; + } + return Relation.INTERSECTS; + } + + if (grad1 == grad2) { + if (int1 != int2) { + return Relation.SEPARATE; + } + + if ((x1 >= Math.min(otherSegment.getPx1(),otherSegment.getPx2())) && + (x1 <= Math.max(otherSegment.getPy1(),otherSegment.getPy2()))) { + xi = x1; + yi = y1; + return Relation.INTERSECTS; + } + + if ((x2 >= Math.min(otherSegment.getPx1(),otherSegment.getPx2())) && + (x2 <= Math.max(otherSegment.getPx1(),otherSegment.getPx2()))) { + xi = x2; + yi = y2; + return Relation.INTERSECTS; + } + + return Relation.SEPARATE; + } + + xi = (int2-int1)/(grad1-grad2); + yi = grad1*xi + int1; + + if (((x1-xi)*(xi-x2) < -0.00001) || ((otherSegment.getPx1()-xi)*(xi-otherSegment.getPx2()) < -0.00001)) { + return Relation.SEPARATE; + } + return Relation.INTERSECTS; + } + + this.getPx1 = function() { + return x1; + } + + this.getPy1 = function() { + return y1; + } + + this.getPx2 = function() { + return x2; + } + + this.getPy2 = function() { + return y2; + } + + this.isUndefined = function() { + return undef; + } + + this.getA = function() { + return a; + } + + this.getB = function() { + return b; + } + + this.getC = function() { + return c; + } + + this.getIntersectionX = function() { + return xi; + } + + this.getIntersectionY = function() { + return yi; + } + + this.getLength = function( tx1, ty1, tx2, ty2 ) { + var dx = tx2 - tx1; + var dy = ty2 - ty1; + return Math.sqrt(dx*dx + dy*dy); + } + +} + +var Relation = { LEFT:1, RIGHT:2, INTERSECTS:3, AHEAD:4, BEHIND:5, SEPARATE:6, UNDEFINED:7 }; diff --git a/web/rough.js b/web/rough.js new file mode 100644 index 0000000..4e09601 --- /dev/null +++ b/web/rough.js @@ -0,0 +1 @@ +var rough=function(){"use strict";function t(t,e,s){if(t&&t.length){const[n,o]=e,a=Math.PI/180*s,r=Math.cos(a),h=Math.sin(a);t.forEach(t=>{const[e,s]=t;t[0]=(e-n)*r-(s-o)*h+n,t[1]=(e-n)*h+(s-o)*r+o})}}function e(t){const e=t[0],s=t[1];return Math.sqrt(Math.pow(e[0]-s[0],2)+Math.pow(e[1]-s[1],2))}function s(t,e,s,n){const o=e[1]-t[1],a=t[0]-e[0],r=o*t[0]+a*t[1],h=n[1]-s[1],i=s[0]-n[0],c=h*s[0]+i*s[1],l=o*i-h*a;return l?[(i*r-a*c)/l,(o*c-h*r)/l]:null}function n(t,e,s){const n=t.length;if(n<3)return!1;const h=[Number.MAX_SAFE_INTEGER,s],i=[e,s];let c=0;for(let e=0;e=Math.min(t[0],s[0])&&e[1]<=Math.max(t[1],s[1])&&e[1]>=Math.min(t[1],s[1])}function a(t,e,s){const n=(e[1]-t[1])*(s[0]-e[0])-(e[0]-t[0])*(s[1]-e[1]);return 0===n?0:n>0?1:2}function r(t,e,s,n){const r=a(t,e,s),h=a(t,e,n),i=a(s,n,t),c=a(s,n,e);return r!==h&&i!==c||(!(0!==r||!o(t,s,e))||(!(0!==h||!o(t,n,e))||(!(0!==i||!o(s,t,n))||!(0!==c||!o(s,e,n)))))}function h(e,s){const n=[0,0],o=Math.round(s.hachureAngle+90);o&&t(e,n,o);const a=function(t,e){const s=[...t];s[0].join(",")!==s[s.length-1].join(",")&&s.push([s[0][0],s[0][1]]);const n=[];if(s&&s.length>2){let t=e.hachureGap;t<0&&(t=4*e.strokeWidth),t=Math.max(t,.1);const o=[];for(let t=0;tt.ymine.ymin?1:t.xe.x?1:t.ymax===e.ymax?0:(t.ymax-e.ymax)/Math.abs(t.ymax-e.ymax)),!o.length)return n;let a=[],r=o[0].ymin;for(;a.length||o.length;){if(o.length){let t=-1;for(let e=0;er);e++)t=e;o.splice(0,t+1).forEach(t=>{a.push({s:r,edge:t})})}if(a=a.filter(t=>!(t.edge.ymax<=r)),a.sort((t,e)=>t.edge.x===e.edge.x?0:(t.edge.x-e.edge.x)/Math.abs(t.edge.x-e.edge.x)),a.length>1)for(let t=0;t=a.length)break;const s=a[t].edge,o=a[e].edge;n.push([[Math.round(s.x),r],[Math.round(o.x),r]])}r+=t,a.forEach(e=>{e.edge.x=e.edge.x+t*e.edge.islope})}}return n}(e,s);return o&&(t(e,n,-o),function(e,s,n){const o=[];e.forEach(t=>o.push(...t)),t(o,s,n)}(a,n,-o)),a}class i{constructor(t){this.helper=t}fillPolygon(t,e){return this._fillPolygon(t,e)}_fillPolygon(t,e,s=!1){let n=h(t,e);if(s){const e=this.connectingLines(t,n);n=n.concat(e)}return{type:"fillSketch",ops:this.renderLines(n,e)}}renderLines(t,e){const s=[];for(const n of t)s.push(...this.helper.doubleLineOps(n[0][0],n[0][1],n[1][0],n[1][1],e));return s}connectingLines(t,s){const n=[];if(s.length>1)for(let o=1;o3){const e=this.splitOnIntersections(t,r);n.push(...e)}}return n}midPointInPolygon(t,e){return n(t,(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2)}splitOnIntersections(t,o){const a=Math.max(5,.1*e(o)),h=[];for(let n=0;na&&n>a&&h.push({point:t,distance:s})}}}if(h.length>1){const e=h.sort((t,e)=>t.distance-e.distance).map(t=>t.point);if(n(t,...o[0])||e.shift(),n(t,...o[1])||e.pop(),e.length<=1)return this.midPointInPolygon(t,o)?[o]:[];const s=[o[0],...e,o[1]],a=[];for(let e=0;e{const r=e(t),h=Math.floor(r/(n+o)),i=(r+o-h*(n+o))/2;let c=t[0],l=t[1];c[0]>l[0]&&(c=t[1],l=t[0]);const u=Math.atan((l[1]-c[1])/(l[0]-c[0]));for(let t=0;t{const a=e(t),r=Math.round(a/(2*s));let h=t[0],i=t[1];h[0]>i[0]&&(h=t[1],i=t[0]);const c=Math.atan((i[1]-h[1])/(i[0]-h[0]));for(let t=0;tn%2?t+s:t+e);a.push({key:"C",data:t}),e=t[4],s=t[5];break}case"Q":a.push({key:"Q",data:[...h]}),e=h[2],s=h[3];break;case"q":{const t=h.map((t,n)=>n%2?t+s:t+e);a.push({key:"Q",data:t}),e=t[2],s=t[3];break}case"A":a.push({key:"A",data:[...h]}),e=h[5],s=h[6];break;case"a":e+=h[5],s+=h[6],a.push({key:"A",data:[h[0],h[1],h[2],h[3],h[4],e,s]});break;case"H":a.push({key:"H",data:[...h]}),e=h[0];break;case"h":e+=h[0],a.push({key:"H",data:[e]});break;case"V":a.push({key:"V",data:[...h]}),s=h[0];break;case"v":s+=h[0],a.push({key:"V",data:[s]});break;case"S":a.push({key:"S",data:[...h]}),e=h[2],s=h[3];break;case"s":{const t=h.map((t,n)=>n%2?t+s:t+e);a.push({key:"S",data:t}),e=t[2],s=t[3];break}case"T":a.push({key:"T",data:[...h]}),e=h[0],s=h[1];break;case"t":e+=h[0],s+=h[1],a.push({key:"T",data:[e,s]});break;case"Z":case"z":a.push({key:"Z",data:[]}),e=n,s=o}return a}function m(t){const e=[];let s="",n=0,o=0,a=0,r=0,h=0,i=0;for(const{key:c,data:l}of t){switch(c){case"M":e.push({key:"M",data:[...l]}),[n,o]=l,[a,r]=l;break;case"C":e.push({key:"C",data:[...l]}),n=l[4],o=l[5],h=l[2],i=l[3];break;case"L":e.push({key:"L",data:[...l]}),[n,o]=l;break;case"H":n=l[0],e.push({key:"L",data:[n,o]});break;case"V":o=l[0],e.push({key:"L",data:[n,o]});break;case"S":{let t=0,a=0;"C"===s||"S"===s?(t=n+(n-h),a=o+(o-i)):(t=n,a=o),e.push({key:"C",data:[t,a,...l]}),h=l[0],i=l[1],n=l[2],o=l[3];break}case"T":{const[t,a]=l;let r=0,c=0;"Q"===s||"T"===s?(r=n+(n-h),c=o+(o-i)):(r=n,c=o);const u=n+2*(r-n)/3,f=o+2*(c-o)/3,p=t+2*(r-t)/3,d=a+2*(c-a)/3;e.push({key:"C",data:[u,f,p,d,t,a]}),h=r,i=c,n=t,o=a;break}case"Q":{const[t,s,a,r]=l,c=n+2*(t-n)/3,u=o+2*(s-o)/3,f=a+2*(t-a)/3,p=r+2*(s-r)/3;e.push({key:"C",data:[c,u,f,p,a,r]}),h=t,i=s,n=a,o=r;break}case"A":{const t=Math.abs(l[0]),s=Math.abs(l[1]),a=l[2],r=l[3],h=l[4],i=l[5],c=l[6];if(0===t||0===s)e.push({key:"C",data:[n,o,i,c,i,c]}),n=i,o=c;else if(n!==i||o!==c){P(n,o,i,c,t,s,a,r,h).forEach((function(t){e.push({key:"C",data:t})})),n=i,o=c}break}case"Z":e.push({key:"Z",data:[]}),n=a,o=r}s=c}return e}function w(t,e,s){return[t*Math.cos(s)-e*Math.sin(s),t*Math.sin(s)+e*Math.cos(s)]}function P(t,e,s,n,o,a,r,h,i,c){const l=(u=r,Math.PI*u/180);var u;let f=[],p=0,d=0,g=0,M=0;if(c)[p,d,g,M]=c;else{[t,e]=w(t,e,-l),[s,n]=w(s,n,-l);const r=(t-s)/2,c=(e-n)/2;let u=r*r/(o*o)+c*c/(a*a);u>1&&(u=Math.sqrt(u),o*=u,a*=u);const f=o*o,k=a*a,b=f*k-f*c*c-k*r*r,y=f*c*c+k*r*r,m=(h===i?-1:1)*Math.sqrt(Math.abs(b/y));g=m*o*c/a+(t+s)/2,M=m*-a*r/o+(e+n)/2,p=Math.asin(parseFloat(((e-M)/a).toFixed(9))),d=Math.asin(parseFloat(((n-M)/a).toFixed(9))),td&&(p-=2*Math.PI),!i&&d>p&&(d-=2*Math.PI)}let k=d-p;if(Math.abs(k)>120*Math.PI/180){const t=d,e=s,h=n;d=i&&d>p?p+120*Math.PI/180*1:p+120*Math.PI/180*-1,f=P(s=g+o*Math.cos(d),n=M+a*Math.sin(d),e,h,o,a,r,0,i,[d,t,g,M])}k=d-p;const b=Math.cos(p),y=Math.sin(p),m=Math.cos(d),x=Math.sin(d),v=Math.tan(k/4),O=4/3*o*v,S=4/3*a*v,L=[t,e],T=[t+O*y,e-S*b],I=[s+O*x,n-S*m],A=[s,n];if(T[0]=2*L[0]-T[0],T[1]=2*L[1]-T[1],c)return[T,I,A].concat(f);{f=[T,I,A].concat(f);const t=[];for(let e=0;e2){const o=[];for(let e=0;e2*Math.PI&&(p=0,d=2*Math.PI);const g=2*Math.PI/i.curveStepCount,M=Math.min(g/2,(d-p)/2),k=F(M,c,l,u,f,p,d,1,i);if(!i.disableMultiStroke){const t=F(M,c,l,u,f,p,d,1.5,i);k.push(...t)}return r&&(h?k.push(...z(c,l,c+u*Math.cos(p),l+f*Math.sin(p),i),...z(c,l,c+u*Math.cos(d),l+f*Math.sin(d),i)):k.push({op:"lineTo",data:[c,l]},{op:"lineTo",data:[c+u*Math.cos(p),l+f*Math.sin(p)]})),{type:"path",ops:k}}function _(t,e){const s=[];if(t.length){const n=e.maxRandomnessOffset||0,o=t.length;if(o>2){s.push({op:"move",data:[t[0][0]+W(n,e),t[0][1]+W(n,e)]});for(let a=1;a500?.4:-.0016668*i+1.233334;let l=o.maxRandomnessOffset||0;l*l*100>h&&(l=i/10);const u=l/2,f=.2+.2*D(o);let p=o.bowing*o.maxRandomnessOffset*(n-e)/200,d=o.bowing*o.maxRandomnessOffset*(t-s)/200;p=W(p,o,c),d=W(d,o,c);const g=[],M=()=>W(u,o,c),k=()=>W(l,o,c);return a&&(r?g.push({op:"move",data:[t+M(),e+M()]}):g.push({op:"move",data:[t+W(l,o,c),e+W(l,o,c)]})),r?g.push({op:"bcurveTo",data:[p+t+(s-t)*f+M(),d+e+(n-e)*f+M(),p+t+2*(s-t)*f+M(),d+e+2*(n-e)*f+M(),s+M(),n+M()]}):g.push({op:"bcurveTo",data:[p+t+(s-t)*f+k(),d+e+(n-e)*f+k(),p+t+2*(s-t)*f+k(),d+e+2*(n-e)*f+k(),s+k(),n+k()]}),g}function $(t,e,s){const n=[];n.push([t[0][0]+W(e,s),t[0][1]+W(e,s)]),n.push([t[0][0]+W(e,s),t[0][1]+W(e,s)]);for(let o=1;o3){const a=[],r=1-s.curveTightness;o.push({op:"move",data:[t[1][0],t[1][1]]});for(let e=1;e+21&&o.push(s)}else o.push(s);o.push(t[e+3])}else{const n=.5,a=t[e+0],r=t[e+1],h=t[e+2],i=t[e+3],c=H(a,r,n),l=H(r,h,n),u=H(h,i,n),f=H(c,l,n),p=H(l,u,n),d=H(f,p,n);V([a,c,f,d],0,s,o),V([d,p,u,i],0,s,o)}var a,r;return o}function B(t,e){return X(t,0,t.length,e)}function X(t,e,s,n,o){const a=o||[],r=t[e],h=t[s-1];let i=0,c=1;for(let n=e+1;ni&&(i=e,c=n)}return Math.sqrt(i)>n?(X(t,e,c+1,n,a),X(t,c,s,n,a)):(a.length||a.push(r),a.push(h)),a}function J(t,e=.15,s){const n=[],o=(t.length-1)/3;for(let s=0;s0?X(n,0,n.length,s):n}const K="none";class U{constructor(t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveFitting:.95,curveStepCount:9,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1,dashOffset:-1,dashGap:-1,zigzagOffset:-1,seed:0,combineNestedSvgPaths:!1,disableMultiStroke:!1,disableMultiStrokeFill:!1},this.config=t||{},this.config.options&&(this.defaultOptions=this._o(this.config.options))}static newSeed(){return Math.floor(Math.random()*2**31)}_o(t){return t?Object.assign({},this.defaultOptions,t):this.defaultOptions}_d(t,e,s){return{shape:t,sets:e||[],options:s||this.defaultOptions}}line(t,e,s,n,o){const a=this._o(o);return this._d("line",[v(t,e,s,n,a)],a)}rectangle(t,e,s,n,o){const a=this._o(o),r=[],h=S(t,e,s,n,a);if(a.fill){const o=[[t,e],[t+s,e],[t+s,e+n],[t,e+n]];"solid"===a.fillStyle?r.push(_(o,a)):r.push(C(o,a))}return a.stroke!==K&&r.push(h),this._d("rectangle",r,a)}ellipse(t,e,s,n,o){const a=this._o(o),r=[],h=T(s,n,a),i=I(t,e,a,h);if(a.fill)if("solid"===a.fillStyle){const s=I(t,e,a,h).opset;s.type="fillPath",r.push(s)}else r.push(C(i.estimatedPoints,a));return a.stroke!==K&&r.push(i.opset),this._d("ellipse",r,a)}circle(t,e,s,n){const o=this.ellipse(t,e,s,s,n);return o.shape="circle",o}linearPath(t,e){const s=this._o(e);return this._d("linearPath",[O(t,!1,s)],s)}arc(t,e,s,n,o,a,r=!1,h){const i=this._o(h),c=[],l=A(t,e,s,n,o,a,r,!0,i);if(r&&i.fill)if("solid"===i.fillStyle){const r=A(t,e,s,n,o,a,!0,!1,i);r.type="fillPath",c.push(r)}else c.push(function(t,e,s,n,o,a,r){const h=t,i=e;let c=Math.abs(s/2),l=Math.abs(n/2);c+=W(.01*c,r),l+=W(.01*l,r);let u=o,f=a;for(;u<0;)u+=2*Math.PI,f+=2*Math.PI;f-u>2*Math.PI&&(u=0,f=2*Math.PI);const p=(f-u)/r.curveStepCount,d=[];for(let t=u;t<=f;t+=p)d.push([h+c*Math.cos(t),i+l*Math.sin(t)]);return d.push([h+c*Math.cos(f),i+l*Math.sin(f)]),d.push([h,i]),C(d,r)}(t,e,s,n,o,a,i));return i.stroke!==K&&c.push(l),this._d("arc",c,i)}curve(t,e){const s=this._o(e),n=[],o=L(t,s);if(s.fill&&s.fill!==K&&t.length>=3){const e=J(function(t,e=0){const s=t.length;if(s<3)throw new Error("A curve must have at least three points.");const n=[];if(3===s)n.push(N(t[0]),N(t[1]),N(t[2]),N(t[2]));else{const s=[];s.push(t[0],t[0]);for(let e=1;e{h.length>=4&&a.push(...J(h,e)),h=[]},c=()=>{i(),a.length&&(o.push(a),a=[])};for(const{key:t,data:e}of n)switch(t){case"M":c(),r=[e[0],e[1]],a.push(r);break;case"L":i(),a.push([e[0],e[1]]);break;case"C":if(!h.length){const t=a.length?a[a.length-1]:r;h.push([t[0],t[1]])}h.push([e[0],e[1]]),h.push([e[2],e[3]]),h.push([e[4],e[5]]);break;case"Z":i(),a.push([r[0],r[1]])}if(c(),!s)return o;const l=[];for(const t of o){const e=B(t,s);e.length&&l.push(e)}return l}(t,1,r?4-4*s.simplification:(1+s.roughness)/2);if(o)if(s.combineNestedSvgPaths){const t=[];h.forEach(e=>t.push(...e)),"solid"===s.fillStyle?n.push(_(t,s)):n.push(C(t,s))}else h.forEach(t=>{"solid"===s.fillStyle?n.push(_(t,s)):n.push(C(t,s))});return a&&(r?h.forEach(t=>{n.push(O(t,!1,s))}):n.push(function(t,e){const s=m(y(b(t))),n=[];let o=[0,0],a=[0,0];for(const{key:t,data:r}of s)switch(t){case"M":{const t=1*(e.maxRandomnessOffset||0);n.push({op:"move",data:r.map(s=>s+W(t,e))}),a=[r[0],r[1]],o=[r[0],r[1]];break}case"L":n.push(...z(a[0],a[1],r[0],r[1],e)),a=[r[0],r[1]];break;case"C":{const[t,s,o,h,i,c]=r;n.push(...j(t,s,o,h,i,c,a,e)),a=[i,c];break}case"Z":n.push(...z(a[0],a[1],o[0],o[1],e)),a=[o[0],o[1]]}return{type:"path",ops:n}}(t,s))),this._d("path",n,s)}opsToPath(t){let e="";for(const s of t.ops){const t=s.data;switch(s.op){case"move":e+=`M${t[0]} ${t[1]} `;break;case"bcurveTo":e+=`C${t[0]} ${t[1]}, ${t[2]} ${t[3]}, ${t[4]} ${t[5]} `;break;case"lineTo":e+=`L${t[0]} ${t[1]} `}}return e.trim()}toPaths(t){const e=t.sets||[],s=t.options||this.defaultOptions,n=[];for(const t of e){let e=null;switch(t.type){case"path":e={d:this.opsToPath(t),stroke:s.stroke,strokeWidth:s.strokeWidth,fill:K};break;case"fillPath":e={d:this.opsToPath(t),stroke:K,strokeWidth:0,fill:s.fill||K};break;case"fillSketch":e=this.fillSketch(t,s)}e&&n.push(e)}return n}fillSketch(t,e){let s=e.fillWeight;return s<0&&(s=e.strokeWidth/2),{d:this.opsToPath(t),stroke:e.fill||K,strokeWidth:s,fill:K}}}class Y{constructor(t,e){this.canvas=t,this.ctx=this.canvas.getContext("2d"),this.gen=new U(e)}draw(t){const e=t.sets||[],s=t.options||this.getDefaultOptions(),n=this.ctx;for(const o of e)switch(o.type){case"path":n.save(),n.strokeStyle="none"===s.stroke?"transparent":s.stroke,n.lineWidth=s.strokeWidth,s.strokeLineDash&&n.setLineDash(s.strokeLineDash),s.strokeLineDashOffset&&(n.lineDashOffset=s.strokeLineDashOffset),this._drawToContext(n,o),n.restore();break;case"fillPath":n.save(),n.fillStyle=s.fill||"";const e="curve"===t.shape||"polygon"===t.shape?"evenodd":"nonzero";this._drawToContext(n,o,e),n.restore();break;case"fillSketch":this.fillSketch(n,o,s)}}fillSketch(t,e,s){let n=s.fillWeight;n<0&&(n=s.strokeWidth/2),t.save(),s.fillLineDash&&t.setLineDash(s.fillLineDash),s.fillLineDashOffset&&(t.lineDashOffset=s.fillLineDashOffset),t.strokeStyle=s.fill||"",t.lineWidth=n,this._drawToContext(t,e),t.restore()}_drawToContext(t,e,s="nonzero"){t.beginPath();for(const s of e.ops){const e=s.data;switch(s.op){case"move":t.moveTo(e[0],e[1]);break;case"bcurveTo":t.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5]);break;case"lineTo":t.lineTo(e[0],e[1])}}"fillPath"===e.type?t.fill(s):t.stroke()}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}line(t,e,s,n,o){const a=this.gen.line(t,e,s,n,o);return this.draw(a),a}rectangle(t,e,s,n,o){const a=this.gen.rectangle(t,e,s,n,o);return this.draw(a),a}ellipse(t,e,s,n,o){const a=this.gen.ellipse(t,e,s,n,o);return this.draw(a),a}circle(t,e,s,n){const o=this.gen.circle(t,e,s,n);return this.draw(o),o}linearPath(t,e){const s=this.gen.linearPath(t,e);return this.draw(s),s}polygon(t,e){const s=this.gen.polygon(t,e);return this.draw(s),s}arc(t,e,s,n,o,a,r=!1,h){const i=this.gen.arc(t,e,s,n,o,a,r,h);return this.draw(i),i}curve(t,e){const s=this.gen.curve(t,e);return this.draw(s),s}path(t,e){const s=this.gen.path(t,e);return this.draw(s),s}}const tt="http://www.w3.org/2000/svg";class et{constructor(t,e){this.svg=t,this.gen=new U(e)}draw(t){const e=t.sets||[],s=t.options||this.getDefaultOptions(),n=this.svg.ownerDocument||window.document,o=n.createElementNS(tt,"g");for(const a of e){let e=null;switch(a.type){case"path":e=n.createElementNS(tt,"path"),e.setAttribute("d",this.opsToPath(a)),e.setAttribute("stroke",s.stroke),e.setAttribute("stroke-width",s.strokeWidth+""),e.setAttribute("fill","none"),s.strokeLineDash&&e.setAttribute("stroke-dasharray",s.strokeLineDash.join(" ").trim()),s.strokeLineDashOffset&&e.setAttribute("stroke-dashoffset",""+s.strokeLineDashOffset);break;case"fillPath":e=n.createElementNS(tt,"path"),e.setAttribute("d",this.opsToPath(a)),e.setAttribute("stroke","none"),e.setAttribute("stroke-width","0"),e.setAttribute("fill",s.fill||""),"curve"!==t.shape&&"polygon"!==t.shape||e.setAttribute("fill-rule","evenodd");break;case"fillSketch":e=this.fillSketch(n,a,s)}e&&o.appendChild(e)}return o}fillSketch(t,e,s){let n=s.fillWeight;n<0&&(n=s.strokeWidth/2);const o=t.createElementNS(tt,"path");return o.setAttribute("d",this.opsToPath(e)),o.setAttribute("stroke",s.fill||""),o.setAttribute("stroke-width",n+""),o.setAttribute("fill","none"),s.fillLineDash&&o.setAttribute("stroke-dasharray",s.fillLineDash.join(" ").trim()),s.fillLineDashOffset&&o.setAttribute("stroke-dashoffset",""+s.fillLineDashOffset),o}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}opsToPath(t){return this.gen.opsToPath(t)}line(t,e,s,n,o){const a=this.gen.line(t,e,s,n,o);return this.draw(a)}rectangle(t,e,s,n,o){const a=this.gen.rectangle(t,e,s,n,o);return this.draw(a)}ellipse(t,e,s,n,o){const a=this.gen.ellipse(t,e,s,n,o);return this.draw(a)}circle(t,e,s,n){const o=this.gen.circle(t,e,s,n);return this.draw(o)}linearPath(t,e){const s=this.gen.linearPath(t,e);return this.draw(s)}polygon(t,e){const s=this.gen.polygon(t,e);return this.draw(s)}arc(t,e,s,n,o,a,r=!1,h){const i=this.gen.arc(t,e,s,n,o,a,r,h);return this.draw(i)}curve(t,e){const s=this.gen.curve(t,e);return this.draw(s)}path(t,e){const s=this.gen.path(t,e);return this.draw(s)}}return{canvas:(t,e)=>new Y(t,e),svg:(t,e)=>new et(t,e),generator:t=>new U(t),newSeed:()=>U.newSeed()}}(); diff --git a/web/script.js b/web/script.js index e69de29..48f9648 100644 --- a/web/script.js +++ b/web/script.js @@ -0,0 +1,23 @@ +import rough from 'roughjs'; +const rc = require('roughjs') +const rc = rough.canvas(document.getElementById('canvas')); +rc.rectangle(100, 100, 200, 200); // x, y, width, height + +// function getOffset( el ) { +// var _x = 0; +// var _y = 0; +// while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) { +// _x += el.offsetLeft - el.scrollLeft; +// _y += el.offsetTop - el.scrollTop; +// el = el.offsetParent; +// } +// return { top: _y, left: _x }; +// } +// var x = getOffset( document.getElementById('complexities') ).left; +// var y = getOffset( document.getElementById('complexities') ).top; + + + + +// var scribble = new Scribble(); +// scribble.scribbleEllipse(200,300,300,200) \ No newline at end of file diff --git a/web/style.css b/web/style.css index 8211bb5..d566941 100644 --- a/web/style.css +++ b/web/style.css @@ -83,8 +83,8 @@ nav{ } .contents{ - display: none; - z-index: 90; + /* display: none; */ + z-index: 190; background-color: white; box-shadow: 1px 3px 90px 20px rgba(4, 14, 70, 0.76) ; border: blue 0.5vw;