From 766ed189c0e1a9d1e0d2a9af6cdb207d695aba86 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Thu, 7 Jun 2018 16:27:45 -0700 Subject: [PATCH 01/16] ts code port - initial draft - in progress --- .gitignore | 3 +- package-lock.json | 824 ++++++++++++++++++-------------- package.json | 8 +- srcts/core.ts | 34 ++ srcts/fillers/filler.ts | 6 + srcts/fillers/hachure-filler.ts | 0 srcts/geometry.ts | 8 + srcts/path.ts | 470 ++++++++++++++++++ srcts/renderer.ts | 566 ++++++++++++++++++++++ srcts/utils/liang-barsky.ts | 39 ++ tsconfig.json | 26 + tslint.json | 61 +++ 12 files changed, 1689 insertions(+), 356 deletions(-) create mode 100644 srcts/core.ts create mode 100644 srcts/fillers/filler.ts create mode 100644 srcts/fillers/hachure-filler.ts create mode 100644 srcts/geometry.ts create mode 100644 srcts/path.ts create mode 100644 srcts/renderer.ts create mode 100644 srcts/utils/liang-barsky.ts create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.gitignore b/.gitignore index 35fa2b8..4e5d4b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .cache .DS_Store z -node_modules \ No newline at end of file +node_modules +bin \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e24d326..29394b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,27 @@ { "name": "roughjs", - "version": "2.1.2", + "version": "2.1.3", "lockfileVersion": 1, "requires": true, "dependencies": { + "@comandeer/babel-plugin-banner": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@comandeer/babel-plugin-banner/-/babel-plugin-banner-2.0.2.tgz", + "integrity": "sha512-5/GMOcgqBy/+cMYfOTFhqrioolOP+g7ADjl4jo1nYONwVGvMpzKPdweuVaJsC/Ol2PH+GmwlF2WrNP3xjWoSiw==", + "dev": true + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/node": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.1.tgz", + "integrity": "sha512-IsX9aDHDzJohkm3VCDB8tkzl5RQ34E/PFA29TQk6uDGb7Oc869ZBtmdKVDBzY3+h9GnXB8ssrRXEPVZrlIOPOw==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -16,42 +34,51 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.1", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.1", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.5", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.8", - "slash": "1.0.0", - "source-map": "0.5.7" + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" } }, "babel-generator": { @@ -60,26 +87,26 @@ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" } }, "babel-helper-evaluate-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.3.0.tgz", - "integrity": "sha512-dRFlMTqUJRGzx5a2smKxmptDdNCXKSkPcXWzKLwAV72hvIZumrd/0z9RcewHkr7PmAEq+ETtpD1GK6wZ6ZUXzw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.4.3.tgz", + "integrity": "sha1-ComvcCwGshcCf6NxkI3UmJ0+Yz8=", "dev": true }, "babel-helper-flip-expressions": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.3.0.tgz", - "integrity": "sha512-kNGohWmtAG3b7tN1xocRQ5rsKkH/hpvZsMiGOJ1VwGJKhnwzR5KlB3rvKBaBPl5/IGHcopB2JN+r1SUEX1iMAw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz", + "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=", "dev": true }, "babel-helper-is-nodes-equiv": { @@ -89,27 +116,27 @@ "dev": true }, "babel-helper-is-void-0": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.3.0.tgz", - "integrity": "sha512-JVqdX8y7Rf/x4NwbqtUI7mdQjL9HWoDnoAEQ8Gv8oxzjvbJv+n75f7l36m9Y8C7sCUltX3V5edndrp7Hp1oSXQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz", + "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=", "dev": true }, "babel-helper-mark-eval-scopes": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.3.0.tgz", - "integrity": "sha512-nrho5Dg4vl0VUgURVpGpEGiwbst5JX7efIyDHFxmkCx/ocQFnrPt8ze9Kxl6TKjR29bJ7D/XKY1NMlSxOQJRbQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz", + "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=", "dev": true }, "babel-helper-remove-or-void": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.3.0.tgz", - "integrity": "sha512-D68W1M3ibCcbg0ysh3ww4/O0g10X1CXK720oOuR8kpfY7w0yP4tVcpK7zDmI1JecynycTQYAZ1rhLJo9aVtIKQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz", + "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=", "dev": true }, "babel-helper-to-multiple-sequence-expressions": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.3.0.tgz", - "integrity": "sha512-1uCrBD+EAaMnAYh7hc944n8Ga19y3daEnoXWPYDvFVsxMCc1l8aDjksApaCEaNSSuewq8BEcff47Cy1PbLg2Gw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.4.3.tgz", + "integrity": "sha1-W1GLESf0ezA4dzOGoVYaK0jmMrY=", "dev": true }, "babel-helpers": { @@ -118,8 +145,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-messages": { @@ -128,219 +155,205 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-minify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-minify/-/babel-minify-0.3.0.tgz", - "integrity": "sha512-LrNEcK+vUkMC+2UERDI+03khMM10Oz60Q422nL6DLhJ9H5/0UJAhCfp3elVciCdaj507AEkiXLtkF585Oe9NFg==", - "dev": true, - "requires": { - "babel-core": "6.26.0", - "babel-preset-minify": "0.3.0", - "fs-readdir-recursive": "1.1.0", - "mkdirp": "0.5.1", - "util.promisify": "1.0.0", - "yargs-parser": "8.1.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-minify-builtins": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.3.0.tgz", - "integrity": "sha512-MqhSHlxkmgURqj3144qPksbZ/qof1JWdumcbucc4tysFcf3P3V3z3munTevQgKEFNMd8F5/ECGnwb63xogLjAg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.4.3.tgz", + "integrity": "sha1-nqPVn0rEp7uVjXEtKVVqH4b3+B4=", "dev": true, "requires": { - "babel-helper-evaluate-path": "0.3.0" + "babel-helper-evaluate-path": "^0.4.3" } }, "babel-plugin-minify-constant-folding": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.3.0.tgz", - "integrity": "sha512-1XeRpx+aY1BuNY6QU/cm6P+FtEi3ar3XceYbmC+4q4W+2Ewq5pL7V68oHg1hKXkBIE0Z4/FjSoHz6vosZLOe/A==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.4.3.tgz", + "integrity": "sha1-MA+d6N2ghEoXaxk2U5YOJK0z4ZE=", "dev": true, "requires": { - "babel-helper-evaluate-path": "0.3.0" + "babel-helper-evaluate-path": "^0.4.3" } }, "babel-plugin-minify-dead-code-elimination": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.3.0.tgz", - "integrity": "sha512-SjM2Fzg85YZz+q/PNJ/HU4O3W98FKFOiP9K5z3sfonlamGOzvZw3Eup2OTiEBsbbqTeY8yzNCAv3qpJRYCgGmw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.4.3.tgz", + "integrity": "sha1-c2KCZYZPkAjQAnUG9Yq+s8HQLZg=", "dev": true, "requires": { - "babel-helper-evaluate-path": "0.3.0", - "babel-helper-mark-eval-scopes": "0.3.0", - "babel-helper-remove-or-void": "0.3.0", - "lodash.some": "4.6.0" + "babel-helper-evaluate-path": "^0.4.3", + "babel-helper-mark-eval-scopes": "^0.4.3", + "babel-helper-remove-or-void": "^0.4.3", + "lodash.some": "^4.6.0" } }, "babel-plugin-minify-flip-comparisons": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.3.0.tgz", - "integrity": "sha512-B8lK+ekcpSNVH7PZpWDe5nC5zxjRiiT4nTsa6h3QkF3Kk6y9qooIFLemdGlqBq6j0zALEnebvCpw8v7gAdpgnw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz", + "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=", "dev": true, "requires": { - "babel-helper-is-void-0": "0.3.0" + "babel-helper-is-void-0": "^0.4.3" } }, "babel-plugin-minify-guarded-expressions": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.3.0.tgz", - "integrity": "sha512-O+6CvF5/Ttsth3LMg4/BhyvVZ82GImeKMXGdVRQGK/8jFiP15EjRpdgFlxv3cnqRjqdYxLCS6r28VfLpb9C/kA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.3.tgz", + "integrity": "sha1-zHCbRFP9IbHzAod0RMifiEJ845c=", "dev": true, "requires": { - "babel-helper-flip-expressions": "0.3.0" + "babel-helper-flip-expressions": "^0.4.3" } }, "babel-plugin-minify-infinity": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.3.0.tgz", - "integrity": "sha512-Sj8ia3/w9158DWieUxU6/VvnYVy59geeFEkVgLZYBE8EBP+sN48tHtBM/jSgz0ejEdBlcfqJ6TnvPmVXTzR2BQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz", + "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=", "dev": true }, "babel-plugin-minify-mangle-names": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.3.0.tgz", - "integrity": "sha512-PYTonhFWURsfAN8achDwvR5Xgy6EeTClLz+fSgGRqjAIXb0OyFm3/xfccbQviVi1qDXmlSnt6oJhBg8KE4Fn7Q==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.4.3.tgz", + "integrity": "sha1-FvG/90t6fJPfwkHngx3V+0sCPvc=", "dev": true, "requires": { - "babel-helper-mark-eval-scopes": "0.3.0" + "babel-helper-mark-eval-scopes": "^0.4.3" } }, "babel-plugin-minify-numeric-literals": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.3.0.tgz", - "integrity": "sha512-TgZj6ay8zDw74AS3yiIfoQ8vRSNJisYO/Du60S8nPV7EW7JM6fDMx5Sar6yVHlVuuwNgvDUBh191K33bVrAhpg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz", + "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=", "dev": true }, "babel-plugin-minify-replace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.3.0.tgz", - "integrity": "sha512-VR6tTg2Lt0TicHIOw04fsUtpPw7RaRP8PC8YzSFwEixnzvguZjZJoL7TgG7ZyEWQD1cJ96UezswECmFNa815bg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.4.3.tgz", + "integrity": "sha1-nSifS6FdTmAR6HmfpfG6d+yBIZ0=", "dev": true }, "babel-plugin-minify-simplify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.3.0.tgz", - "integrity": "sha512-2M16ytQOCqBi7bYMu4DCWn8e6KyFCA108F6+tVrBJxOmm5u2sOmTFEa8s94tR9RHRRNYmcUf+rgidfnzL3ik9Q==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.4.3.tgz", + "integrity": "sha1-N3VthcYURktLCSfytOQXGR1Vc4o=", "dev": true, "requires": { - "babel-helper-flip-expressions": "0.3.0", - "babel-helper-is-nodes-equiv": "0.0.1", - "babel-helper-to-multiple-sequence-expressions": "0.3.0" + "babel-helper-flip-expressions": "^0.4.3", + "babel-helper-is-nodes-equiv": "^0.0.1", + "babel-helper-to-multiple-sequence-expressions": "^0.4.3" } }, "babel-plugin-minify-type-constructors": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.3.0.tgz", - "integrity": "sha512-XRXpvsUCPeVw9YEUw+9vSiugcSZfow81oIJT0yR9s8H4W7yJ6FHbImi5DJHoL8KcDUjYnL9wYASXk/fOkbyR6Q==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz", + "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=", "dev": true, "requires": { - "babel-helper-is-void-0": "0.3.0" + "babel-helper-is-void-0": "^0.4.3" } }, "babel-plugin-transform-inline-consecutive-adds": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.3.0.tgz", - "integrity": "sha512-iZsYAIjYLLfLK0yN5WVT7Xf7Y3wQ9Z75j9A8q/0IglQSpUt2ppTdHlwl/GeaXnxdaSmsxBu861klbTBbv2n+RA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz", + "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=", "dev": true }, "babel-plugin-transform-member-expression-literals": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.0.tgz", - "integrity": "sha512-bxtac+8w755ctVeDs4vU98RhWY49eW1wO02HAN+eirZYSKk/dVrKONIznXbHmxWKxT4UX1rpTKOCyezuzLpbTw==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz", + "integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=", "dev": true }, "babel-plugin-transform-merge-sibling-variables": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.0.tgz", - "integrity": "sha512-9G1URVEEKoQLDqe0GwqYudECN7kE/q0OCNo5TiD1iwWnnaKi97xY915l5r2KKUvNflXEm9c3faNWknSXYQ7h6Q==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz", + "integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=", "dev": true }, "babel-plugin-transform-minify-booleans": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.0.tgz", - "integrity": "sha512-JtpyTRyF+wF/r7GSxpRbNCrVve5M/aCC8xoGcnFItaPUDqjxKmFYvBzMc9u+g0lgo8NWjuZLc16MYaIwkHKD/A==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz", + "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=", "dev": true }, "babel-plugin-transform-property-literals": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.0.tgz", - "integrity": "sha512-B8s+71+4DPye9+pmZiPGgLPy3YqcmIuvE/9UcZLczPlwL5ALwF6qRUdLC3Fk17NhL6jxp4u33ZVZ8R4kvASPzw==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz", + "integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=", "dev": true, "requires": { - "esutils": "2.0.2" + "esutils": "^2.0.2" } }, "babel-plugin-transform-regexp-constructors": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.3.0.tgz", - "integrity": "sha512-h92YHzyl042rb0naKO8frTHntpRFwRgKkfWD8602kFHoQingjJNtbvZzvxqHncJ6XmKVyYvfrBpDOSkCTDIIxw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz", + "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=", "dev": true }, "babel-plugin-transform-remove-console": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.0.tgz", - "integrity": "sha512-mck9//yGTwObqqqDzY/sISO88/5/XfIB3ILb4uJLXk2xq124NT4yQVjFSRgVSbLcNq8OyBAn2acxKUqg4W/okQ==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=", "dev": true }, "babel-plugin-transform-remove-debugger": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.0.tgz", - "integrity": "sha512-i/HWGjsmL2d1N2dl+eIzf44XpSP5v7hi1/GXB0xzom9kjrU8js3T8Kadizn95ZxfHK592Vg8P4JJWP/fvimEWw==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz", + "integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=", "dev": true }, "babel-plugin-transform-remove-undefined": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.3.0.tgz", - "integrity": "sha512-TYGQucc8iP3LJwN3kDZLEz5aa/2KuFrqpT+s8f8NnHsBU1sAgR3y8Opns0xhC+smyDYWscqFCKM1gbkWQOhhnw==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.4.3.tgz", + "integrity": "sha1-1AsNp/kcCMBsxyt2dHTAHEiU3gI=", "dev": true, "requires": { - "babel-helper-evaluate-path": "0.3.0" + "babel-helper-evaluate-path": "^0.4.3" } }, "babel-plugin-transform-simplify-comparison-operators": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.0.tgz", - "integrity": "sha512-EJyfYeph0CSekwQuwWVwJqy2go/bETkR95iaWQ/HTUis7tkCGNYmXngaFzuIXdmoPXfvmXYCvAXR4/93hqHVjw==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", + "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=", "dev": true }, "babel-plugin-transform-undefined-to-void": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.0.tgz", - "integrity": "sha512-AVDVEmp0S9mbF1O8zekWbsOOmqnR08PZah5NRZJqSvJnFgiL0ep4Lwo4EymH8OieJR2QgQdR3q71TNW+wiVn4g==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz", + "integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=", "dev": true }, "babel-preset-minify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.3.0.tgz", - "integrity": "sha512-+VV2GWEyak3eDOmzT1DDMuqHrw3VbE9nBNkx2LLVs4pH/Me32ND8DRpVDd8IRvk1xX5p75nygyRPtkMh6GIAbQ==", - "dev": true, - "requires": { - "babel-plugin-minify-builtins": "0.3.0", - "babel-plugin-minify-constant-folding": "0.3.0", - "babel-plugin-minify-dead-code-elimination": "0.3.0", - "babel-plugin-minify-flip-comparisons": "0.3.0", - "babel-plugin-minify-guarded-expressions": "0.3.0", - "babel-plugin-minify-infinity": "0.3.0", - "babel-plugin-minify-mangle-names": "0.3.0", - "babel-plugin-minify-numeric-literals": "0.3.0", - "babel-plugin-minify-replace": "0.3.0", - "babel-plugin-minify-simplify": "0.3.0", - "babel-plugin-minify-type-constructors": "0.3.0", - "babel-plugin-transform-inline-consecutive-adds": "0.3.0", - "babel-plugin-transform-member-expression-literals": "6.9.0", - "babel-plugin-transform-merge-sibling-variables": "6.9.0", - "babel-plugin-transform-minify-booleans": "6.9.0", - "babel-plugin-transform-property-literals": "6.9.0", - "babel-plugin-transform-regexp-constructors": "0.3.0", - "babel-plugin-transform-remove-console": "6.9.0", - "babel-plugin-transform-remove-debugger": "6.9.0", - "babel-plugin-transform-remove-undefined": "0.3.0", - "babel-plugin-transform-simplify-comparison-operators": "6.9.0", - "babel-plugin-transform-undefined-to-void": "6.9.0", - "lodash.isplainobject": "4.0.6" + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.4.3.tgz", + "integrity": "sha1-spw91pGJBThFmPCSuVUVLiah/g8=", + "dev": true, + "requires": { + "babel-plugin-minify-builtins": "^0.4.3", + "babel-plugin-minify-constant-folding": "^0.4.3", + "babel-plugin-minify-dead-code-elimination": "^0.4.3", + "babel-plugin-minify-flip-comparisons": "^0.4.3", + "babel-plugin-minify-guarded-expressions": "^0.4.3", + "babel-plugin-minify-infinity": "^0.4.3", + "babel-plugin-minify-mangle-names": "^0.4.3", + "babel-plugin-minify-numeric-literals": "^0.4.3", + "babel-plugin-minify-replace": "^0.4.3", + "babel-plugin-minify-simplify": "^0.4.3", + "babel-plugin-minify-type-constructors": "^0.4.3", + "babel-plugin-transform-inline-consecutive-adds": "^0.4.3", + "babel-plugin-transform-member-expression-literals": "^6.9.4", + "babel-plugin-transform-merge-sibling-variables": "^6.9.4", + "babel-plugin-transform-minify-booleans": "^6.9.4", + "babel-plugin-transform-property-literals": "^6.9.4", + "babel-plugin-transform-regexp-constructors": "^0.4.3", + "babel-plugin-transform-remove-console": "^6.9.4", + "babel-plugin-transform-remove-debugger": "^6.9.4", + "babel-plugin-transform-remove-undefined": "^0.4.3", + "babel-plugin-transform-simplify-comparison-operators": "^6.9.4", + "babel-plugin-transform-undefined-to-void": "^6.9.4", + "lodash.isplainobject": "^4.0.6" } }, "babel-register": { @@ -349,13 +362,13 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.3", - "home-or-tmp": "2.0.0", - "lodash": "4.17.5", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" } }, "babel-runtime": { @@ -364,8 +377,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.3", - "regenerator-runtime": "0.11.1" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, "babel-template": { @@ -374,11 +387,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.5" + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" } }, "babel-traverse": { @@ -387,15 +400,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.3", - "lodash": "4.17.5" + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" } }, "babel-types": { @@ -404,10 +417,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" } }, "babylon": { @@ -428,14 +441,14 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "chalk": { @@ -444,13 +457,34 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -464,9 +498,9 @@ "dev": true }, "core-js": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", - "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=", + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", "dev": true }, "debug": { @@ -478,48 +512,20 @@ "ms": "2.0.0" } }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "dev": true, - "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" - } - }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, - "es-abstract": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", - "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", - "dev": true, - "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.1", - "is-callable": "1.1.3", - "is-regex": "1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" - } + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true }, "escape-string-regexp": { "version": "1.0.5", @@ -527,29 +533,37 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } }, "globals": { "version": "9.18.0", @@ -557,54 +571,55 @@ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" } }, - "invariant": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.3.tgz", - "integrity": "sha512-7Z5PPegwDTyjbaeCnV0efcyS6vdKAU51kpEmS7QFib3P4822l8ICYyMn7qvJnc+WzLoDsuI9gPMKbJ8pCu8XtA==", + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "loose-envify": "1.3.1" + "once": "^1.3.0", + "wrappy": "1" } }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } }, "is-finite": { "version": "1.0.2", @@ -612,30 +627,25 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "1.0.1" - } - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", @@ -649,9 +659,9 @@ "dev": true }, "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, "lodash.isplainobject": { @@ -672,7 +682,16 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0" + } + }, + "magic-string": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.24.1.tgz", + "integrity": "sha512-YBfNxbJiixMzxW40XqJEIldzHyh5f7CZKalo1uZffevyrPEX8Qgo9s0dmcORLHdV47UyvJg8/zD+6hQG3qvJrA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.1" } }, "minimatch": { @@ -681,7 +700,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -711,20 +730,13 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", - "dev": true - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.10.0" + "wrappy": "1" } }, "os-homedir": { @@ -745,6 +757,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -763,13 +781,44 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.2" + "is-finite": "^1.0.0" + } + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" } }, "rollup": { - "version": "0.56.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.56.5.tgz", - "integrity": "sha512-IGPk5vdWrsc4vkiW9XMeXr5QMtxmvATTttTi59w2jBQWe9G/MMQtn8teIBAj+DdK51TrpVT6P0aQUaQUlUYCJA==", + "version": "0.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.60.1.tgz", + "integrity": "sha512-LujiS7PH8DwKAphB2ldaSEF1EX9hWY9w+mct2b4DczC8tvn7qwmr9ZFLtM9IT7gPFYlmS8O1JdiLT/aEiBEcsA==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "*" + } + }, + "rollup-plugin-babel-minify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-babel-minify/-/rollup-plugin-babel-minify-5.0.0.tgz", + "integrity": "sha512-/XMozvFf1wm0O04FEybfEGl05jq9RQ408Xx/GjCfCau9ETzKYlEkfdi19pj2EvmpbSO/r0yspMKmwnUHNycdsw==", + "dev": true, + "requires": { + "@comandeer/babel-plugin-banner": "^2.0.2", + "babel-core": "^6.26.0", + "babel-preset-minify": "^0.4.0", + "magic-string": "^0.24.0" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "slash": { @@ -790,16 +839,28 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "^0.5.6" } }, + "sourcemap-codec": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz", + "integrity": "sha512-hX1eNBNuilj8yfFnECh0DzLgwKpBLMIvmhgEhixXNui8lMLBInTI8Kyxt++RwJnMNu7cAUo635L2+N1TxMJCzA==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "supports-color": { @@ -820,24 +881,83 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "object.getownpropertydescriptors": "2.0.3" - } - }, - "yargs-parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", - "dev": true, - "requires": { - "camelcase": "4.1.0" - } + "tslib": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", + "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==", + "dev": true + }, + "tslint": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz", + "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.12.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "tsutils": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz", + "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz", + "integrity": "sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true } } } diff --git a/package.json b/package.json index 4a4f268..39129aa 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,9 @@ }, "homepage": "https://roughjs.com", "devDependencies": { - "babel-minify": "^0.3.0", - "rollup": "^0.56.5" + "rollup": "^0.60.1", + "rollup-plugin-babel-minify": "^5.0.0", + "tslint": "^5.10.0", + "typescript": "^2.9.1" } -} \ No newline at end of file +} diff --git a/srcts/core.ts b/srcts/core.ts new file mode 100644 index 0000000..ee9bd13 --- /dev/null +++ b/srcts/core.ts @@ -0,0 +1,34 @@ +export interface Options { + maxRandomnessOffset: number; + roughness: number; + bowing: number; + stroke: string; + strokeWidth: number; + curveTightness: number; + curveStepCount: number; + fill: string | null; + fillStyle: string; + fillWeight: number; + hachureAngle: number; + hachureGap: number; + simplification?: number; +} + +export declare type OpType = 'move' | 'bcurveTo' | 'lineTo' | 'qcurveTo'; +export declare type OpSetType = 'path' | 'fillPath' | 'fillSketch'; + +export interface Op { + op: OpType; + data: number[]; +} + +export interface OpSet { + type: OpSetType; + ops: Op[]; +} + +export interface Drawable { + shape: string; + options: Options; + sets: OpSet[]; +} \ No newline at end of file diff --git a/srcts/fillers/filler.ts b/srcts/fillers/filler.ts new file mode 100644 index 0000000..8af3a07 --- /dev/null +++ b/srcts/fillers/filler.ts @@ -0,0 +1,6 @@ +import { Options, OpSet } from '../core'; +import { Rectangle } from '../geometry'; + +export interface PatternFiller { + fill(box: Rectangle, o: Options): OpSet; +} \ No newline at end of file diff --git a/srcts/fillers/hachure-filler.ts b/srcts/fillers/hachure-filler.ts new file mode 100644 index 0000000..e69de29 diff --git a/srcts/geometry.ts b/srcts/geometry.ts new file mode 100644 index 0000000..0fbddaf --- /dev/null +++ b/srcts/geometry.ts @@ -0,0 +1,8 @@ +export declare type Point = [number, number]; + +export interface Rectangle { + x: number; + y: number; + width: number; + height: number; +} \ No newline at end of file diff --git a/srcts/path.ts b/srcts/path.ts new file mode 100644 index 0000000..5e5a1d0 --- /dev/null +++ b/srcts/path.ts @@ -0,0 +1,470 @@ +import { Point } from './geometry'; + +interface PathToken { + type: number; + text: string; +} + +function isType(token: PathToken, type: number) { + return token.type === type; +} + +export interface Segment { + key: string; + data: number[]; + point?: Point; +} + +const PARAMS: { [key: string]: { length: number } } = { + A: { length: 7 }, + a: { length: 7 }, + C: { length: 6 }, + c: { length: 6 }, + H: { length: 1 }, + h: { length: 1 }, + L: { length: 2 }, + l: { length: 2 }, + M: { length: 2 }, + m: { length: 2 }, + Q: { length: 4 }, + q: { length: 4 }, + S: { length: 4 }, + s: { length: 4 }, + T: { length: 4 }, + t: { length: 2 }, + V: { length: 1 }, + v: { length: 1 }, + Z: { length: 0 }, + z: { length: 0 } +}; + +class ParsedPath { + private COMMAND = 0; + private NUMBER = 1; + private EOD = 2; + private _closed?: boolean; + segments: Segment[] = []; + + constructor(d: string) { + this.parseData(d); + this.processPoints(); + } + + private 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: this.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: this.NUMBER, text: `parseFloat(RegExp.$1)` }; + d = d.substr(RegExp.$1.length); + } else { + console.error('Unrecognized segment command: ' + d); + return []; + } + } + tokens[tokens.length] = { type: this.EOD, text: '' }; + return tokens; + } + + private parseData(d: string) { + const tokens = this.tokenize(d); + let index = 0; + let token = tokens[index]; + let mode: string = 'BOD'; + this.segments = new Array(); + while (!isType(token, this.EOD)) { + let param_length: number; + const params: number[] = new Array(); + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } else { + this.parseData('M0,0' + d); + return; + } + } else { + if (isType(token, this.NUMBER)) { + param_length = PARAMS[mode].length; + } else { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + } + if ((index + param_length) < tokens.length) { + for (let i = index; i < index + param_length; i++) { + const numbeToken = tokens[i]; + if (isType(numbeToken, this.NUMBER)) { + params[params.length] = +numbeToken.text; + } + else { + console.error('Parameter type is not a number: ' + mode + ',' + numbeToken.text); + return; + } + } + if (PARAMS[mode]) { + const segment: Segment = { key: mode, data: params }; + this.segments.push(segment); + index += param_length; + token = tokens[index]; + if (mode === 'M') mode = 'L'; + if (mode === 'm') mode = 'l'; + } else { + console.error('Unsupported segment type: ' + mode); + return; + } + } else { + console.error('Path data ended before all parameters were found'); + } + } + } + + get closed() { + if (typeof this._closed === 'undefined') { + this._closed = false; + for (const s of this.segments) { + if (s.key.toLowerCase() === 'z') { + this._closed = true; + } + } + } + return this._closed; + } + + processPoints() { + let first: Point | null = null; + let currentPoint: Point = [0, 0]; + for (let i = 0; i < this.segments.length; i++) { + const s = this.segments[i]; + switch (s.key) { + case 'M': + case 'L': + case 'T': + s.point = [s.data[0], s.data[1]]; + break; + case 'm': + case 'l': + case 't': + s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; + break; + case 'H': + s.point = [s.data[0], currentPoint[1]]; + break; + case 'h': + s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; + break; + case 'V': + s.point = [currentPoint[0], s.data[0]]; + break; + case 'v': + s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; + break; + case 'z': + case 'Z': + if (first) { + s.point = [first[0], first[1]]; + } + break; + case 'C': + s.point = [s.data[4], s.data[5]]; + break; + case 'c': + s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; + break; + case 'S': + s.point = [s.data[2], s.data[3]]; + break; + case 's': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'Q': + s.point = [s.data[2], s.data[3]]; + break; + case 'q': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'A': + s.point = [s.data[5], s.data[6]]; + break; + case 'a': + s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; + break; + } + if (s.key === 'm' || s.key === 'M') { + first = null; + } + if (s.point) { + currentPoint = s.point; + if (!first) { + first = s.point; + } + } + if (s.key === 'z' || s.key === 'Z') { + first = null; + } + } + } +} + +export class RoughPath { + private parsed: ParsedPath; + private _position: Point = [0, 0]; + private _first: Point | null = null; + private _linearPoints?: Point[][]; + bezierReflectionPoint: Point | null = null; + quadReflectionPoint: Point | null = null; + + constructor(d: string) { + this.parsed = new ParsedPath(d); + } + + get segments(): Segment[] { + return this.parsed.segments; + } + + get closed(): boolean { + return this.parsed.closed; + } + + get linearPoints(): Point[][] { + if (!this._linearPoints) { + const lp: Point[][] = []; + let points: Point[] = []; + for (const s of this.parsed.segments) { + const key = s.key.toLowerCase(); + if (key === 'm' || key === 'z') { + if (points.length) { + lp.push(points); + points = []; + } + if (key === 'z') { + continue; + } + } + if (s.point) { + points.push(s.point); + } + } + if (points.length) { + lp.push(points); + points = []; + } + this._linearPoints = lp; + } + return this._linearPoints; + } + + get first(): Point | null { + return this._first; + } + + set first(v: Point | null) { + this._first = v; + } + + setPosition(x: number, y: number) { + this._position = [x, y]; + if (!this._first) { + this._first = [x, y]; + } + } + + get position(): Point { + return this._position; + } + + get x(): number { + return this._position[0]; + } + + get y(): number { + return this._position[1]; + } +} + + +export interface RoughArcSegment { + cp1: Point; + cp2: Point; + to: Point; +} + +// Algorithm as described in https://www.w3.org/TR/SVG/implnote.html +// Code adapted from nsSVGPathDataParser.cpp in Mozilla +// https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 +export class RoughArcConverter { + private _segIndex = 0; + private _numSegs = 0; + private _rx = 0; + private _ry = 0; + private _sinPhi = 0; + private _cosPhi = 0; + private _C: Point = [0, 0]; + private _theta = 0; + private _delta = 0; + private _T = 0; + private _from: Point; + + constructor(from: Point, to: Point, radii: Point, angle: number, largeArcFlag: boolean, sweepFlag: boolean) { + this._from = from; + if (from[0] === to[0] && from[1] === to[1]) { + return; + } + const radPerDeg = Math.PI / 180; + this._rx = Math.abs(radii[0]); + this._ry = Math.abs(radii[1]); + this._sinPhi = Math.sin(angle * radPerDeg); + this._cosPhi = Math.cos(angle * radPerDeg); + const x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; + const y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; + let root = 0; + const numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; + if (numerator < 0) { + const s = Math.sqrt(1 - (numerator / (this._rx * this._rx * this._ry * this._ry))); + this._rx = this._rx * s; + this._ry = this._ry * s; + root = 0; + } else { + root = (largeArcFlag === sweepFlag ? -1.0 : 1.0) * + Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); + } + const cxdash = root * this._rx * y1dash / this._ry; + const cydash = -root * this._ry * x1dash / this._rx; + this._C = [0, 0]; + this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; + this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; + this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); + let dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); + if ((!sweepFlag) && (dtheta > 0)) { + dtheta -= 2 * Math.PI; + } else if (sweepFlag && (dtheta < 0)) { + dtheta += 2 * Math.PI; + } + this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); + this._delta = dtheta / this._numSegs; + this._T = (8 / 3) * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); + } + + getNextSegment(): RoughArcSegment | null { + if (this._segIndex === this._numSegs) { + return null; + } + const cosTheta1 = Math.cos(this._theta); + const sinTheta1 = Math.sin(this._theta); + const theta2 = this._theta + this._delta; + const cosTheta2 = Math.cos(theta2); + const sinTheta2 = Math.sin(theta2); + + const to: Point = [ + this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], + this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1] + ]; + const cp1: Point = [ + this._from[0] + this._T * (- this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), + this._from[1] + this._T * (- this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1) + ]; + const cp2: Point = [ + to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), + to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2) + ]; + + this._theta = theta2; + this._from = [to[0], to[1]]; + this._segIndex++; + + return { + cp1: cp1, + cp2: cp2, + to: to + }; + } + + calculateVectorAngle(ux: number, uy: number, vx: number, vy: number): number { + const ta = Math.atan2(uy, ux); + const tb = Math.atan2(vy, vx); + if (tb >= ta) + return tb - ta; + return 2 * Math.PI - (ta - tb); + } +} + +export class PathFitter { + sets: Point[][]; + closed: boolean; + + constructor(sets: Point[][], closed: boolean) { + this.sets = sets; + this.closed = closed; + } + + fit(simplification: number): string { + const outSets: Point[][] = []; + for (const set of this.sets) { + const length = set.length; + let estLength = Math.floor(simplification * length); + if (estLength < 5) { + if (length <= 5) { + continue; + } + estLength = 5; + } + outSets.push(this.reduce(set, estLength)); + } + + let d = ''; + for (const set of outSets) { + for (let i = 0; i < set.length; i++) { + const point = set[i]; + if (i === 0) { + d += 'M' + point[0] + ',' + point[1]; + } else { + d += 'L' + point[0] + ',' + point[1]; + } + } + if (this.closed) { + d += 'z '; + } + } + return d; + } + + distance(p1: Point, p2: Point): number { + return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); + } + + reduce(set: Point[], count: number): Point[] { + if (set.length <= count) { + return set; + } + const points: Point[] = set.slice(0); + while (points.length > count) { + const areas = []; + let minArea = -1; + let minIndex = -1; + for (let i = 1; i < (points.length - 1); i++) { + const a = this.distance(points[i - 1], points[i]); + const b = this.distance(points[i], points[i + 1]); + const c = this.distance(points[i - 1], points[i + 1]); + const s = (a + b + c) / 2.0; + const area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); + areas.push(area); + if ((minArea < 0) || (area < minArea)) { + minArea = area; + minIndex = i; + } + } + if (minIndex > 0) { + points.splice(minIndex, 1); + } else { + break; + } + } + return points; + } +} \ No newline at end of file diff --git a/srcts/renderer.ts b/srcts/renderer.ts new file mode 100644 index 0000000..d315126 --- /dev/null +++ b/srcts/renderer.ts @@ -0,0 +1,566 @@ +import { Options, OpSet, Op } from './core'; +import { RoughPath, RoughArcConverter, PathFitter, Segment } from './path.js'; +import { Point } from './geometry'; + +export class RoughRenderer { + line(x1: number, y1: number, x2: number, y2: number, o: Options): OpSet { + const ops = this._doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops }; + } + + linearPath(points: Point[], close: boolean, o: Options): OpSet { + const len = (points || []).length; + if (len > 2) { + let ops: Op[] = []; + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(this._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this._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 this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + + polygon(points: Point[], o: Options): OpSet { + return this.linearPath(points, true, o); + } + + rectangle(x: number, y: number, width: number, height: number, o: Options): OpSet { + const points: Point[] = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return this.polygon(points, o); + } + + curve(points: Point[], o: Options): OpSet { + const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + + ellipse(x: number, y: number, width: number, height: number, o: Options): OpSet { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this._getOffset(-rx * 0.05, rx * 0.05, o); + ry += this._getOffset(-ry * 0.05, ry * 0.05, o); + const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this._getOffset(0.1, this._getOffset(0.4, 1, o), o), o); + const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: Options): OpSet { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this._getOffset(-rx * 0.01, rx * 0.01, o); + ry += this._getOffset(-ry * 0.01, 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 o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; + } + + svgPath(path: string, o: Options): OpSet { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops: Op[] = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; + } + + /// + + private _getOffset(min: number, max: number, ops: Options): number { + return ops.roughness * ((Math.random() * (max - min)) + min); + } + + private _doubleLine(x1: number, y1: number, x2: number, y2: number, o: Options) { + const o1 = this._line(x1, y1, x2, y2, o, true, false); + const o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + + private _line(x1: number, y1: number, x2: number, y2: number, o: Options, move: boolean, overlay: boolean): Op[] { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this._getOffset(-midDispX, midDispX, o); + midDispY = this._getOffset(-midDispY, midDispY, o); + const ops: Op[] = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + this._getOffset(-halfOffset, halfOffset, o), + y1 + this._getOffset(-halfOffset, halfOffset, o) + ] + }); + } else { + ops.push({ + op: 'move', data: [ + x1 + this._getOffset(-offset, offset, o), + y1 + this._getOffset(-offset, offset, o) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), + x2 + this._getOffset(-halfOffset, halfOffset, o), + y2 + this._getOffset(-halfOffset, halfOffset, o) + ] + }); + } else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), + x2 + this._getOffset(-offset, offset, o), + y2 + this._getOffset(-offset, offset, o) + ] + }); + } + return ops; + } + + private _curve(points: Point[], closePoint: Point | null, o: Options): Op[] { + const len = points.length; + let ops: Op[] = []; + 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] + this._getOffset(-ro, ro, o), closePoint[1] + + this._getOffset(-ro, 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 = ops.concat(this._doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + + private _ellipse(increment: number, cx: number, cy: number, rx: number, ry: number, offset: number, overlap: number, o: Options): Op[] { + const radOffset = this._getOffset(-0.5, 0.5, o) - (Math.PI / 2); + const points: Point[] = []; + points.push([ + this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + this._getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + this._getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + this._getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + this._getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return this._curve(points, null, o); + } + + private _curveWithOffset(points: Point[], offset: number, o: Options): Op[] { + const ps: Point[] = []; + ps.push([ + points[0][0] + this._getOffset(-offset, offset, o), + points[0][1] + this._getOffset(-offset, offset, o), + ]); + ps.push([ + points[0][0] + this._getOffset(-offset, offset, o), + points[0][1] + this._getOffset(-offset, offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + this._getOffset(-offset, offset, o), + points[i][1] + this._getOffset(-offset, offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + this._getOffset(-offset, offset, o), + points[i][1] + this._getOffset(-offset, offset, o), + ]); + } + } + return this._curve(ps, null, o); + } + + private _arc(increment: number, cx: number, cy: number, rx: number, ry: number, strt: number, stp: number, offset: number, o: Options) { + const radOffset = strt + this._getOffset(-0.1, 0.1, o); + const points: Point[] = []; + points.push([ + this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this._getOffset(-offset, 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 this._curve(points, null, o); + } + + private _bezierTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number, path: RoughPath, o: Options): Op[] { + const ops: Op[] = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f: Point = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } else { + ops.push({ op: 'move', data: [path.x + this._getOffset(-ros[0], ros[0], o), path.y + this._getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this._getOffset(-ros[i], ros[i], o), y + this._getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + this._getOffset(-ros[i], ros[i], o), y1 + this._getOffset(-ros[i], ros[i], o), + x2 + this._getOffset(-ros[i], ros[i], o), y2 + this._getOffset(-ros[i], ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + + private _processSegment(path: RoughPath, seg: Segment, prevSeg: Segment | null, o: Options): Op[] { + let ops: Op[] = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this._getOffset(-ro, ro, o); + y = y + this._getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(this._doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(this._doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(this._doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref: Point | null = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); + let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); + f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref: Point | null = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); + let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); + f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter( + [path.x, path.y], + [x, y], + [rx, ry], + angle, + largeArcFlag ? true : false, + sweepFlag ? true : false + ); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } +} \ No newline at end of file diff --git a/srcts/utils/liang-barsky.ts b/srcts/utils/liang-barsky.ts new file mode 100644 index 0000000..db3f933 --- /dev/null +++ b/srcts/utils/liang-barsky.ts @@ -0,0 +1,39 @@ +import { Point, Rectangle } from '../geometry'; + +export function clip(p1: Point, p2: Point, box: Rectangle): [Point, Point] | null { + const xmin = box.x; + const xmax = box.x + box.width; + const ymin = box.y; + const ymax = box.y + box.height; + + let t0 = 0; + let t1 = 1; + const dx = p2[0] - p1[0]; + const dy = p2[1] - p1[0]; + let p = 0; + let q = 0; + let r = 0; + + for (let edge = 0; edge < 4; edge++) { + if (edge === 0) { p = -dx; q = -(xmin - p1[0]); } + if (edge === 1) { p = dx; q = (xmax - p1[0]); } + if (edge === 2) { p = -dy; q = -(ymin - p1[1]); } + if (edge === 3) { p = dy; q = (ymax - p1[1]); } + r = q / p; + if (p === 0 && q < 0) { + return null; + } + if (p < 0) { + if (r > t1) return null; + else if (r > t0) t0 = r; + } else if (p > 0) { + if (r < t0) return null; + else if (r < t1) t1 = r; + } + } + + return [ + [p1[0] + t0 * dx, p1[1] + t0 * dy], + [p1[0] + t1 * dx, p1[1] + t1 * dy] + ]; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f1098d0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "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, + "experimentalDecorators": true, + "emitDecoratorMetadata": true + }, + "include": [ + "srcts/**/*.ts" + ] +} \ No newline at end of file diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..c8e9cb2 --- /dev/null +++ b/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 From ae2a5a1dde62adc9bff79337d64ca3b0f113f09d Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Sat, 16 Jun 2018 10:44:48 -0700 Subject: [PATCH 02/16] work in progress --- srcts/canvas.ts | 84 ++++++++++++++ srcts/core.ts | 16 +++ srcts/fillers/filler-interface.ts | 12 ++ srcts/fillers/filler.ts | 23 +++- srcts/fillers/hachure-filler.ts | 118 ++++++++++++++++++++ srcts/generator.ts | 70 ++++++++++++ srcts/geometry.ts | 118 ++++++++++++++++++++ srcts/renderer.ts | 178 +++++++++++++++++------------- srcts/rough.ts | 25 +++++ srcts/utils/hachure.ts | 86 +++++++++++++++ 10 files changed, 647 insertions(+), 83 deletions(-) create mode 100644 srcts/canvas.ts create mode 100644 srcts/fillers/filler-interface.ts create mode 100644 srcts/generator.ts create mode 100644 srcts/rough.ts create mode 100644 srcts/utils/hachure.ts diff --git a/srcts/canvas.ts b/srcts/canvas.ts new file mode 100644 index 0000000..5c87bb2 --- /dev/null +++ b/srcts/canvas.ts @@ -0,0 +1,84 @@ +import { Config, Options, Drawable, OpSet } from './core'; +import { RoughGenerator } from './generator'; +import { RoughRenderer } from './renderer'; + +export class RoughCanvas { + private canvas: HTMLCanvasElement; + private ctx: CanvasRenderingContext2D; + private gen: RoughGenerator; + + constructor(canvas: HTMLCanvasElement, config?: Config) { + this.canvas = canvas; + this.ctx = this.canvas.getContext('2d')!; + this.gen = new RoughGenerator(config || null, this.canvas); + } + + get generator(): RoughGenerator { + return this.gen; + } + + static createRenderer(): RoughRenderer { + return new RoughRenderer(); + } + + line(x1: number, y1: number, x2: number, y2: number, options?: Options) { + const d = this.gen.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + + rectangle(x: number, y: number, width: number, height: number, options: Options) { + const d = this.gen.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + + draw(drawable: Drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const ctx = this.ctx; + for (const drawing of sets) { + switch (drawing.type) { + case 'path': + ctx.save(); + ctx.strokeStyle = o.stroke; + ctx.lineWidth = o.strokeWidth; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillPath': + ctx.save(); + ctx.fillStyle = o.fill || ''; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + } + } + } + + private _drawToContext(ctx: CanvasRenderingContext2D, drawing: OpSet) { + 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 'qcurveTo': + ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); + break; + case 'lineTo': + ctx.lineTo(data[0], data[1]); + break; + } + } + if (drawing.type === 'fillPath') { + ctx.fill(); + } else { + ctx.stroke(); + } + } +} \ No newline at end of file diff --git a/srcts/core.ts b/srcts/core.ts index ee9bd13..a804911 100644 --- a/srcts/core.ts +++ b/srcts/core.ts @@ -1,3 +1,17 @@ +import { Point } from './geometry'; + +export interface Config { + async?: boolean; + options?: Options; + noWorker?: boolean; + worklyURL?: string; +} + +export interface DrawingSurface { + width: number; + height: number; +} + export interface Options { maxRandomnessOffset: number; roughness: number; @@ -25,6 +39,8 @@ export interface Op { export interface OpSet { type: OpSetType; ops: Op[]; + size?: Point; + path?: string; } export interface Drawable { diff --git a/srcts/fillers/filler-interface.ts b/srcts/fillers/filler-interface.ts new file mode 100644 index 0000000..84366f5 --- /dev/null +++ b/srcts/fillers/filler-interface.ts @@ -0,0 +1,12 @@ +import { Options, OpSet, Op } from '../core'; +import { Point } from '../geometry'; + +export interface PatternFiller { + fillPolygon(points: Point[], o: Options): OpSet; + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet; +} + +export interface RenderHelper { + doubleLine(x1: number, y1: number, x2: number, y2: number, o: Options): Op[]; + getOffset(min: number, max: number, ops: Options): number; +} \ No newline at end of file diff --git a/srcts/fillers/filler.ts b/srcts/fillers/filler.ts index 8af3a07..fd7ea28 100644 --- a/srcts/fillers/filler.ts +++ b/srcts/fillers/filler.ts @@ -1,6 +1,21 @@ -import { Options, OpSet } from '../core'; -import { Rectangle } from '../geometry'; +import { Options } from '../core'; +import { PatternFiller, RenderHelper } from './filler-interface'; +import { HachureFiller } from './hachure-filler'; -export interface PatternFiller { - fill(box: Rectangle, o: Options): OpSet; +const fillers: { [name: string]: PatternFiller } = {}; + +export function getFiller(renderer: RenderHelper, o: Options): PatternFiller { + let fillerName = o.fillStyle || 'hachure'; + if (!fillers[fillerName]) { + switch (fillerName) { + case 'hachure': + default: + fillerName = 'hachure'; + if (!fillers[fillerName]) { + fillers[fillerName] = new HachureFiller(renderer); + } + break; + } + } + return fillers[fillerName]; } \ No newline at end of file diff --git a/srcts/fillers/hachure-filler.ts b/srcts/fillers/hachure-filler.ts index e69de29..5a0127e 100644 --- a/srcts/fillers/hachure-filler.ts +++ b/srcts/fillers/hachure-filler.ts @@ -0,0 +1,118 @@ +import { PatternFiller, RenderHelper } from './filler-interface'; +import { Options, OpSet, Op } from '../core'; +import { Point, Segment } from '../geometry'; +import { HachureIterator } from '../utils/hachure'; + +export class HachureFiller implements PatternFiller { + renderer: RenderHelper; + + constructor(renderer: RenderHelper) { + this.renderer = renderer; + } + + fillPolygon(points: Point[], o: Options): OpSet { + return this._fillPolygon(points, o); + } + + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet { + return this._fillEllipse(cx, cy, width, height, o); + } + + protected _fillPolygon(points: Point[], o: Options): OpSet { + let ops: Op[] = []; + if (points && points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const cosAngle = Math.cos(hachureAngle); + const sinAngle = Math.sin(hachureAngle); + const tanAngle = Math.tan(hachureAngle); + const it = new HachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); + let rect: number[] | null; + while ((rect = it.nextLine()) != null) { + const lines = this.getIntersectingLines(rect, points); + for (let i = 0; i < lines.length; i++) { + if (i < (lines.length - 1)) { + const p1 = lines[i]; + const p2 = lines[i + 1]; + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + } + } + } + } + return { type: 'fillSketch', ops }; + } + + protected _fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet { + let ops: Op[] = []; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.renderer.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.renderer.getOffset(-ry * 0.05, ry * 0.05, o); + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap <= 0) { + gap = o.strokeWidth * 4; + } + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const tanAngle = Math.tan(hachureAngle); + const aspectRatio = ry / rx; + const hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); + const sinAnglePrime = aspectRatio * tanAngle / hyp; + const cosAnglePrime = 1 / hyp; + const gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); + let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); + for (let xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { + halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); + const p1 = this.affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + const p2 = this.affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + } + return { type: 'fillSketch', ops }; + } + + private getIntersectingLines(line: number[], points: Point[]): Point[] { + const intersections: Point[] = []; + const s1 = new Segment([line[0], line[1]], [line[2], line[3]]); + for (let i = 0; i < points.length; i++) { + const s2 = new Segment(points[i], points[(i + 1) % points.length]); + if (s1.intersects(s2)) { + intersections.push([s1.xi, s1.yi]); + } + } + return intersections; + } + + private affine(x: number, y: number, cx: number, cy: number, sinAnglePrime: number, cosAnglePrime: number, R: number): Point { + const A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; + const B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; + const C = cosAnglePrime; + const D = sinAnglePrime; + const E = -R * sinAnglePrime; + const F = R * cosAnglePrime; + return [ + A + C * x + D * y, + B + E * x + F * y + ]; + } +} \ No newline at end of file diff --git a/srcts/generator.ts b/srcts/generator.ts new file mode 100644 index 0000000..7233c79 --- /dev/null +++ b/srcts/generator.ts @@ -0,0 +1,70 @@ +import { RoughRenderer } from './renderer.js'; +import { Config, DrawingSurface, Options, Drawable, OpSet } from './core'; +import { Point } from './geometry.js'; + +export class RoughGenerator { + private config: Config; + private surface: DrawingSurface; + private renderer?: RoughRenderer; + defaultOptions: Options = { + maxRandomnessOffset: 2, + roughness: 1, + bowing: 1, + stroke: '#000', + strokeWidth: 1, + curveTightness: 0, + curveStepCount: 9, + fill: null, + fillStyle: 'hachure', + fillWeight: -1, + hachureAngle: -41, + hachureGap: -1 + }; + + constructor(config: Config | null, surface: DrawingSurface) { + this.config = config || {}; + this.surface = surface; + if (this.config.options) { + this.defaultOptions = this._options(this.config.options); + } + } + + private _options(options?: Options): Options { + return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; + } + + private _drawable(shape: string, sets: OpSet[], options: Options): Drawable { + return { shape, sets: sets || [], options: options || this.defaultOptions }; + } + + private get lib(): RoughRenderer { + if (!this.renderer) { + this.renderer = new RoughRenderer(); + } + return this.renderer; + } + + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Drawable { + const o = this._options(options); + return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + } + + rectangle(x: number, y: number, width: number, height: number, options?: Options): Drawable { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points: Point[] = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + // if (o.fillStyle === 'solid') { + // paths.push(this.lib.solidFillShape(xc, yc, o)); + // } else { + // paths.push(this.lib.hachureFillShape(xc, yc, o)); + // } + } + console.log(this.surface); + paths.push(this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } +} \ No newline at end of file diff --git a/srcts/geometry.ts b/srcts/geometry.ts index 0fbddaf..c8e34dd 100644 --- a/srcts/geometry.ts +++ b/srcts/geometry.ts @@ -5,4 +5,122 @@ export interface Rectangle { y: number; width: number; height: number; +} + +export class Segment { + px1: number; + px2: number; + py1: number; + py2: number; + xi = Number.MAX_VALUE; + yi = Number.MAX_VALUE; + a: number; + b: number; + c: number; + _undefined: boolean; + + constructor(p1: Point, p2: Point) { + this.px1 = p1[0]; + this.py1 = p1[1]; + this.px2 = p2[0]; + this.py2 = p2[1]; + this.a = this.py2 - this.py1; + this.b = this.px1 - this.px2; + this.c = this.px2 * this.py1 - this.px1 * this.py2; + this._undefined = ((this.a === 0) && (this.b === 0) && (this.c === 0)); + } + + isUndefined() { + return this._undefined; + } + + intersects(otherSegment: Segment): boolean { + if (this.isUndefined() || otherSegment.isUndefined()) { + return false; + } + let grad1 = Number.MAX_VALUE; + let grad2 = Number.MAX_VALUE; + let int1 = 0, int2 = 0; + const a = this.a, b = this.b, c = this.c; + + if (Math.abs(b) > 0.00001) { + grad1 = -a / b; + int1 = -c / b; + } + if (Math.abs(otherSegment.b) > 0.00001) { + grad2 = -otherSegment.a / otherSegment.b; + int2 = -otherSegment.c / otherSegment.b; + } + + if (grad1 === Number.MAX_VALUE) { + if (grad2 === Number.MAX_VALUE) { + if ((-c / a) !== (-otherSegment.c / otherSegment.a)) { + return false; + } + if ((this.py1 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.py2 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py2 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = this.px1; + this.yi = (grad2 * this.xi + int2); + if (((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) || ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001)) { + return false; + } + if (Math.abs(otherSegment.a) < 0.00001) { + if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + + if (grad2 === Number.MAX_VALUE) { + this.xi = otherSegment.px1; + this.yi = grad1 * this.xi + int1; + if (((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) || ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001)) { + return false; + } + if (Math.abs(a) < 0.00001) { + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + + if (grad1 === grad2) { + if (int1 !== int2) { + return false; + } + if ((this.px1 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.px2 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px2 <= Math.max(otherSegment.px1, otherSegment.px2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + + this.xi = ((int2 - int1) / (grad1 - grad2)); + this.yi = (grad1 * this.xi + int1); + + if (((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) || ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001)) { + return false; + } + return true; + } } \ No newline at end of file diff --git a/srcts/renderer.ts b/srcts/renderer.ts index d315126..6756cc1 100644 --- a/srcts/renderer.ts +++ b/srcts/renderer.ts @@ -4,7 +4,7 @@ import { Point } from './geometry'; export class RoughRenderer { line(x1: number, y1: number, x2: number, y2: number, o: Options): OpSet { - const ops = this._doubleLine(x1, y1, x2, y2, o); + const ops = this.doubleLine(x1, y1, x2, y2, o); return { type: 'path', ops }; } @@ -13,10 +13,10 @@ export class RoughRenderer { if (len > 2) { let ops: Op[] = []; for (let i = 0; i < (len - 1); i++) { - ops = ops.concat(this._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); } if (close) { - ops = ops.concat(this._doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); + ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); } return { type: 'path', ops }; } else if (len === 2) { @@ -46,9 +46,9 @@ export class RoughRenderer { const increment = (Math.PI * 2) / o.curveStepCount; let rx = Math.abs(width / 2); let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.05, rx * 0.05, o); - ry += this._getOffset(-ry * 0.05, ry * 0.05, o); - const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this._getOffset(0.1, this._getOffset(0.4, 1, o), o), o); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); return { type: 'path', ops: o1.concat(o2) }; } @@ -58,8 +58,8 @@ export class RoughRenderer { const cy = y; let rx = Math.abs(width / 2); let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.01, rx * 0.01, o); - ry += this._getOffset(-ry * 0.01, ry * 0.01, o); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); let strt = start; let stp = stop; while (strt < 0) { @@ -77,8 +77,8 @@ export class RoughRenderer { let ops = o1.concat(o2); if (closed) { if (roughClosure) { - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); } else { ops.push({ op: 'lineTo', data: [cx, cy] }); ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); @@ -108,13 +108,33 @@ export class RoughRenderer { return { type: 'path', ops }; } + solidFillPolygon(points: Point[], o: Options): OpSet { + const ops: Op[] = []; + if (PointerEvent.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; + } + + // fillPatern(box: Rectangle, o: Options): OpSet { + // const filler = getFiller(this, o); + // return filler.fill(box, o); + // } + /// - private _getOffset(min: number, max: number, ops: Options): number { + getOffset(min: number, max: number, ops: Options): number { return ops.roughness * ((Math.random() * (max - min)) + min); } - private _doubleLine(x1: number, y1: number, x2: number, y2: number, o: Options) { + doubleLine(x1: number, y1: number, x2: number, y2: number, o: Options): Op[] { const o1 = this._line(x1, y1, x2, y2, o, true, false); const o2 = this._line(x1, y1, x2, y2, o, true, true); return o1.concat(o2); @@ -130,22 +150,22 @@ export class RoughRenderer { const divergePoint = 0.2 + Math.random() * 0.2; let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; - midDispX = this._getOffset(-midDispX, midDispX, o); - midDispY = this._getOffset(-midDispY, midDispY, o); + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); const ops: Op[] = []; if (move) { if (overlay) { ops.push({ op: 'move', data: [ - x1 + this._getOffset(-halfOffset, halfOffset, o), - y1 + this._getOffset(-halfOffset, halfOffset, o) + x1 + this.getOffset(-halfOffset, halfOffset, o), + y1 + this.getOffset(-halfOffset, halfOffset, o) ] }); } else { ops.push({ op: 'move', data: [ - x1 + this._getOffset(-offset, offset, o), - y1 + this._getOffset(-offset, offset, o) + x1 + this.getOffset(-offset, offset, o), + y1 + this.getOffset(-offset, offset, o) ] }); } @@ -153,23 +173,23 @@ export class RoughRenderer { if (overlay) { ops.push({ op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - x2 + this._getOffset(-halfOffset, halfOffset, o), - y2 + this._getOffset(-halfOffset, halfOffset, o) + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + x2 + this.getOffset(-halfOffset, halfOffset, o), + y2 + this.getOffset(-halfOffset, halfOffset, o) ] }); } else { ops.push({ op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - x2 + this._getOffset(-offset, offset, o), - y2 + this._getOffset(-offset, offset, o) + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + x2 + this.getOffset(-offset, offset, o), + y2 + this.getOffset(-offset, offset, o) ] }); } @@ -193,7 +213,7 @@ export class RoughRenderer { } if (closePoint && closePoint.length === 2) { const ro = o.maxRandomnessOffset; - ops.push({ op: 'lineTo', data: [closePoint[0] + this._getOffset(-ro, ro, o), closePoint[1] + + this._getOffset(-ro, ro, o)] }); + ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + + this.getOffset(-ro, ro, o)] }); } } else if (len === 3) { ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); @@ -204,35 +224,35 @@ export class RoughRenderer { points[2][0], points[2][1]] }); } else if (len === 2) { - ops = ops.concat(this._doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + ops = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); } return ops; } private _ellipse(increment: number, cx: number, cy: number, rx: number, ry: number, offset: number, overlap: number, o: Options): Op[] { - const radOffset = this._getOffset(-0.5, 0.5, o) - (Math.PI / 2); + const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); const points: Point[] = []; points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) ]); for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) ]); } points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) ]); points.push([ - this._getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), - this._getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) ]); points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) ]); return this._curve(points, null, o); } @@ -240,22 +260,22 @@ export class RoughRenderer { private _curveWithOffset(points: Point[], offset: number, o: Options): Op[] { const ps: Point[] = []; ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), ]); ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), ]); for (let i = 1; i < points.length; i++) { ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), ]); if (i === (points.length - 1)) { ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), ]); } } @@ -263,16 +283,16 @@ export class RoughRenderer { } private _arc(increment: number, cx: number, cy: number, rx: number, ry: number, strt: number, stp: number, offset: number, o: Options) { - const radOffset = strt + this._getOffset(-0.1, 0.1, o); + const radOffset = strt + this.getOffset(-0.1, 0.1, o); const points: Point[] = []; points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) ]); for (let angle = radOffset; angle <= stp; angle = angle + increment) { points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) ]); } points.push([ @@ -294,13 +314,13 @@ export class RoughRenderer { if (i === 0) { ops.push({ op: 'move', data: [path.x, path.y] }); } else { - ops.push({ op: 'move', data: [path.x + this._getOffset(-ros[0], ros[0], o), path.y + this._getOffset(-ros[0], ros[0], o)] }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); } - f = [x + this._getOffset(-ros[i], ros[i], o), y + this._getOffset(-ros[i], ros[i], o)]; + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; ops.push({ op: 'bcurveTo', data: [ - x1 + this._getOffset(-ros[i], ros[i], o), y1 + this._getOffset(-ros[i], ros[i], o), - x2 + this._getOffset(-ros[i], ros[i], o), y2 + this._getOffset(-ros[i], ros[i], o), + x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), + x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), f[0], f[1] ] }); @@ -323,8 +343,8 @@ export class RoughRenderer { y += path.y; } const ro = 1 * (o.maxRandomnessOffset || 0); - x = x + this._getOffset(-ro, ro, o); - y = y + this._getOffset(-ro, ro, o); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); path.setPosition(x, y); ops.push({ op: 'move', data: [x, y] }); } @@ -340,7 +360,7 @@ export class RoughRenderer { x += path.x; y += path.y; } - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); path.setPosition(x, y); } break; @@ -353,7 +373,7 @@ export class RoughRenderer { if (delta) { x += path.x; } - ops = ops.concat(this._doubleLine(path.x, path.y, x, path.y, o)); + ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); path.setPosition(x, path.y); } break; @@ -366,7 +386,7 @@ export class RoughRenderer { if (delta) { y += path.y; } - ops = ops.concat(this._doubleLine(path.x, path.y, path.x, y, o)); + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); path.setPosition(path.x, y); } break; @@ -374,7 +394,7 @@ export class RoughRenderer { case 'Z': case 'z': { if (path.first) { - ops = ops.concat(this._doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); path.setPosition(path.first[0], path.first[1]); path.first = null; } @@ -451,19 +471,19 @@ export class RoughRenderer { } const offset1 = 1 * (1 + o.roughness * 0.2); const offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; ops.push({ op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), f[0], f[1] ] }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; ops.push({ op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), f[0], f[1] ] }); @@ -495,19 +515,19 @@ export class RoughRenderer { } const offset1 = 1 * (1 + o.roughness * 0.2); const offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; ops.push({ op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), f[0], f[1] ] }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; ops.push({ op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), f[0], f[1] ] }); @@ -535,7 +555,7 @@ export class RoughRenderer { break; } if (rx === 0 || ry === 0) { - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); path.setPosition(x, y); } else { for (let i = 0; i < 1; i++) { diff --git a/srcts/rough.ts b/srcts/rough.ts new file mode 100644 index 0000000..e9458d1 --- /dev/null +++ b/srcts/rough.ts @@ -0,0 +1,25 @@ +import { Config, DrawingSurface } from './core'; +import { RoughCanvas } from './canvas'; +import { RoughRenderer } from './renderer'; +import { RoughGenerator } from './generator'; + +export default { + canvas(canvas: HTMLCanvasElement, config?: Config): RoughCanvas { + if (config && config.async) { + // TODO: + } + return new RoughCanvas(canvas, config); + }, + + createRenderer(): RoughRenderer { + return RoughCanvas.createRenderer(); + }, + + generator(config: Config | null, surface: DrawingSurface) { + // if (config && config.async) { + // return new RoughGeneratorAsync(config, size); + // } + // return new RoughGenerator(config, size); + return new RoughGenerator(config, surface); + } +}; \ No newline at end of file diff --git a/srcts/utils/hachure.ts b/srcts/utils/hachure.ts new file mode 100644 index 0000000..c55fd40 --- /dev/null +++ b/srcts/utils/hachure.ts @@ -0,0 +1,86 @@ +import { Segment } from '../geometry'; + +export class HachureIterator { + top: number; + bottom: number; + left: number; + right: number; + gap: number; + sinAngle: number; + tanAngle: number; + pos: number; + deltaX: number = 0; + hGap: number = 0; + sLeft?: Segment; + sRight?: Segment; + + constructor(top: number, bottom: number, left: number, right: number, gap: number, sinAngle: number, cosAngle: number, tanAngle: number) { + this.top = top; + this.bottom = bottom; + this.left = left; + this.right = right; + this.gap = gap; + this.sinAngle = sinAngle; + this.tanAngle = tanAngle; + + if (Math.abs(sinAngle) < 0.0001) { + this.pos = left + gap; + } else if (Math.abs(sinAngle) > 0.9999) { + this.pos = top + gap; + } else { + this.deltaX = (bottom - top) * Math.abs(tanAngle); + this.pos = left - Math.abs(this.deltaX); + this.hGap = Math.abs(gap / cosAngle); + this.sLeft = new Segment([left, bottom], [left, top]); + this.sRight = new Segment([right, bottom], [right, top]); + } + } + + nextLine(): number[] | null { + if (Math.abs(this.sinAngle) < 0.0001) { + if (this.pos < this.right) { + const line = [this.pos, this.top, this.pos, this.bottom]; + this.pos += this.gap; + return line; + } + } else if (Math.abs(this.sinAngle) > 0.9999) { + if (this.pos < this.bottom) { + const line = [this.left, this.pos, this.right, this.pos]; + this.pos += this.gap; + return line; + } + } else { + let xLower = this.pos - this.deltaX / 2; + let xUpper = this.pos + this.deltaX / 2; + let yLower = this.bottom; + let yUpper = this.top; + if (this.pos < (this.right + this.deltaX)) { + while (((xLower < this.left) && (xUpper < this.left)) || ((xLower > this.right) && (xUpper > this.right))) { + this.pos += this.hGap; + xLower = this.pos - this.deltaX / 2; + xUpper = this.pos + this.deltaX / 2; + if (this.pos > (this.right + this.deltaX)) { + return null; + } + } + const s = new Segment([xLower, yLower], [xUpper, yUpper]); + if (this.sLeft && s.intersects(this.sLeft)) { + xLower = s.xi; + yLower = s.yi; + } + if (this.sRight && s.intersects(this.sRight)) { + xUpper = s.xi; + yUpper = s.yi; + } + if (this.tanAngle > 0) { + xLower = this.right - (xLower - this.left); + xUpper = this.right - (xUpper - this.left); + } + const line = [xLower, yLower, xUpper, yUpper]; + this.pos += this.hGap; + return line; + } + } + return null; + } +} \ No newline at end of file From 5e85f5f4395575b32645ef126e1bd5425d0133bd Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Sat, 16 Jun 2018 18:25:24 -0700 Subject: [PATCH 03/16] added other shapes --- srcts/canvas.ts | 60 +++++++++++++++++++++++++++++++- srcts/generator.ts | 85 ++++++++++++++++++++++++++++++++++++++++++---- srcts/renderer.ts | 41 +++++++++++++++++++--- 3 files changed, 175 insertions(+), 11 deletions(-) diff --git a/srcts/canvas.ts b/srcts/canvas.ts index 5c87bb2..eff8aea 100644 --- a/srcts/canvas.ts +++ b/srcts/canvas.ts @@ -1,6 +1,7 @@ import { Config, Options, Drawable, OpSet } from './core'; import { RoughGenerator } from './generator'; import { RoughRenderer } from './renderer'; +import { Point } from './geometry'; export class RoughCanvas { private canvas: HTMLCanvasElement; @@ -27,12 +28,54 @@ export class RoughCanvas { return d; } - rectangle(x: number, y: number, width: number, height: number, options: Options) { + rectangle(x: number, y: number, width: number, height: number, options?: Options) { const d = this.gen.rectangle(x, y, width, height, options); this.draw(d); return d; } + ellipse(x: number, y: number, width: number, height: number, options?: Options) { + const d = this.gen.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + + circle(x: number, y: number, diameter: number, options?: Options) { + const d = this.gen.circle(x, y, diameter, options); + this.draw(d); + return d; + } + + linearPath(points: Point[], options?: Options) { + const d = this.gen.linearPath(points, options); + this.draw(d); + return d; + } + + polygon(points: Point[], options?: Options) { + const d = this.gen.polygon(points, options); + this.draw(d); + return d; + } + + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options) { + const d = this.gen.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + + curve(points: Point[], options?: Options) { + const d = this.gen.curve(points, options); + this.draw(d); + return d; + } + + // path(d: string, options?: Options) { + // const drawing = this.gen.path(d, options); + // this.draw(drawing); + // return drawing; + // } + draw(drawable: Drawable) { const sets = drawable.sets || []; const o = drawable.options || this.gen.defaultOptions; @@ -52,10 +95,25 @@ export class RoughCanvas { this._drawToContext(ctx, drawing); ctx.restore(); break; + case 'fillSketch': + this.fillSketch(ctx, drawing, o); + break; } } } + private fillSketch(ctx: CanvasRenderingContext2D, drawing: OpSet, o: Options) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + ctx.save(); + ctx.strokeStyle = o.fill || ''; + ctx.lineWidth = fweight; + this._drawToContext(ctx, drawing); + ctx.restore(); + } + private _drawToContext(ctx: CanvasRenderingContext2D, drawing: OpSet) { ctx.beginPath(); for (const item of drawing.ops) { diff --git a/srcts/generator.ts b/srcts/generator.ts index 7233c79..4ca8a34 100644 --- a/srcts/generator.ts +++ b/srcts/generator.ts @@ -44,6 +44,21 @@ export class RoughGenerator { return this.renderer; } + protected getCanvasSize(): Point { + const val = (w: any): number => { + if (w && typeof w === 'object') { + if (w.baseVal && w.baseVal.value) { + return w.baseVal.value; + } + } + return w || 100; + }; + if (this.surface) { + return [val(this.surface.width), val(this.surface.height)]; + } + return [100, 100]; + } + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Drawable { const o = this._options(options); return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); @@ -56,15 +71,73 @@ export class RoughGenerator { const points: Point[] = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; if (o.fillStyle === 'solid') { paths.push(this.lib.solidFillPolygon(points, o)); + } else { + paths.push(this.lib.patternFillPolygon(points, o)); } - // if (o.fillStyle === 'solid') { - // paths.push(this.lib.solidFillShape(xc, yc, o)); - // } else { - // paths.push(this.lib.hachureFillShape(xc, yc, o)); - // } } - console.log(this.surface); paths.push(this.lib.rectangle(x, y, width, height, o)); return this._drawable('rectangle', paths, o); } + + ellipse(x: number, y: number, width: number, height: number, options?: Options): Drawable { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(this.lib.ellipse(x, y, width, height, o)); + return this._drawable('ellipse', paths, o); + } + + circle(x: number, y: number, diameter: number, options?: Options): Drawable { + const ret = this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + + linearPath(points: Point[], options?: Options): Drawable { + const o = this._options(options); + return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + } + + polygon(points: Point[], options?: Options): Drawable { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): Drawable { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + + curve(points: Point[], options?: Options): Drawable { + const o = this._options(options); + return this._drawable('curve', [this.lib.curve(points, o)], o); + } } \ No newline at end of file diff --git a/srcts/renderer.ts b/srcts/renderer.ts index 6756cc1..4e10c41 100644 --- a/srcts/renderer.ts +++ b/srcts/renderer.ts @@ -1,6 +1,7 @@ import { Options, OpSet, Op } from './core'; import { RoughPath, RoughArcConverter, PathFitter, Segment } from './path.js'; import { Point } from './geometry'; +import { getFiller } from './fillers/filler'; export class RoughRenderer { line(x1: number, y1: number, x2: number, y2: number, o: Options): OpSet { @@ -123,10 +124,42 @@ export class RoughRenderer { return { type: 'fillPath', ops }; } - // fillPatern(box: Rectangle, o: Options): OpSet { - // const filler = getFiller(this, o); - // return filler.fill(box, o); - // } + patternFillPolygon(points: Point[], o: Options): OpSet { + const filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + + patternFillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet { + const filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + + patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: Options): OpSet { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, 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: Point[] = []; + 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 this.patternFillPolygon(points, o); + } /// From 5264d6a1a81be66bfa4b3167227ee69605ea0e22 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Sat, 16 Jun 2018 20:16:58 -0700 Subject: [PATCH 04/16] path parsing logic port --- srcts/canvas.ts | 10 +-- srcts/core.ts | 20 +++++- srcts/generator.ts | 157 ++++++++++++++++++++++++++++++++++++++++++++- srcts/path.ts | 2 +- 4 files changed, 180 insertions(+), 9 deletions(-) diff --git a/srcts/canvas.ts b/srcts/canvas.ts index eff8aea..b8fe003 100644 --- a/srcts/canvas.ts +++ b/srcts/canvas.ts @@ -70,11 +70,11 @@ export class RoughCanvas { return d; } - // path(d: string, options?: Options) { - // const drawing = this.gen.path(d, options); - // this.draw(drawing); - // return drawing; - // } + path(d: string, options?: Options) { + const drawing = this.gen.path(d, options); + this.draw(drawing); + return drawing; + } draw(drawable: Drawable) { const sets = drawable.sets || []; diff --git a/srcts/core.ts b/srcts/core.ts index a804911..a534418 100644 --- a/srcts/core.ts +++ b/srcts/core.ts @@ -29,7 +29,7 @@ export interface Options { } export declare type OpType = 'move' | 'bcurveTo' | 'lineTo' | 'qcurveTo'; -export declare type OpSetType = 'path' | 'fillPath' | 'fillSketch'; +export declare type OpSetType = 'path' | 'fillPath' | 'fillSketch' | 'path2Dfill' | 'path2Dpattern'; export interface Op { op: OpType; @@ -47,4 +47,22 @@ export interface Drawable { shape: string; options: Options; sets: OpSet[]; +} + +export interface PathInfo { + d: string; + stroke: string; + strokeWidth: number; + fill?: string; + pattern?: PatternInfo; +} + +export interface PatternInfo { + x: number; + y: number; + width: number; + height: number; + viewBox: string; + patternUnits: string; + path: PathInfo; } \ No newline at end of file diff --git a/srcts/generator.ts b/srcts/generator.ts index 4ca8a34..a38f699 100644 --- a/srcts/generator.ts +++ b/srcts/generator.ts @@ -1,7 +1,9 @@ import { RoughRenderer } from './renderer.js'; -import { Config, DrawingSurface, Options, Drawable, OpSet } from './core'; +import { Config, DrawingSurface, Options, Drawable, OpSet, PathInfo, PatternInfo } from './core'; import { Point } from './geometry.js'; +const hasSelf = typeof self !== 'undefined'; + export class RoughGenerator { private config: Config; private surface: DrawingSurface; @@ -44,7 +46,7 @@ export class RoughGenerator { return this.renderer; } - protected getCanvasSize(): Point { + private getCanvasSize(): Point { const val = (w: any): number => { if (w && typeof w === 'object') { if (w.baseVal && w.baseVal.value) { @@ -59,6 +61,35 @@ export class RoughGenerator { return [100, 100]; } + private computePathSize(d: string): Point { + let size: Point = [0, 0]; + if (hasSelf && self.document) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = self.document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + self.document.body.appendChild(svg); + const bb = pathNode.getBBox(); + if (bb) { + size[0] = bb.width || 0; + size[1] = bb.height || 0; + } + self.document.body.removeChild(svg); + } catch (err) { } + } + const canvasSize = this.getCanvasSize(); + if (!(size[0] * size[1])) { + size = canvasSize; + } + size[0] = Math.min(size[0], canvasSize[0]); + size[1] = Math.min(size[1], canvasSize[1]); + return size; + } + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Drawable { const o = this._options(options); return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); @@ -140,4 +171,126 @@ export class RoughGenerator { const o = this._options(options); return this._drawable('curve', [this.lib.curve(points, o)], o); } + + path(d: string, options?: Options): Drawable { + const o = this._options(options); + const paths: OpSet[] = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape: OpSet = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } else { + const size = this.computePathSize(d); + const points: Point[] = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } + + toPaths(drawable: Drawable): PathInfo[] { + const sets = drawable.sets || []; + const o = drawable.options || this.defaultOptions; + const paths: PathInfo[] = []; + for (const drawing of sets) { + let path: PathInfo | null = null; + switch (drawing.type) { + case 'path': + path = { + d: this.opsToPath(drawing), + stroke: o.stroke, + strokeWidth: o.strokeWidth, + fill: 'none' + }; + break; + case 'fillPath': + path = { + d: this.opsToPath(drawing), + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'fillSketch': + path = this.fillSketch(drawing, o); + break; + case 'path2Dfill': + path = { + d: drawing.path || '', + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'path2Dpattern': { + const size = drawing.size!; + const pattern: PatternInfo = { + x: 0, y: 0, width: 1, height: 1, + viewBox: `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`, + patternUnits: 'objectBoundingBox', + path: this.fillSketch(drawing, o) + }; + path = { + d: drawing.path!, + stroke: 'none', + strokeWidth: 0, + pattern: pattern + }; + break; + } + } + if (path) { + paths.push(path); + } + } + return paths; + } + + private fillSketch(drawing: OpSet, o: Options): PathInfo { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + return { + d: this.opsToPath(drawing), + stroke: o.fill || 'none', + strokeWidth: fweight, + fill: 'none' + }; + } + + private opsToPath(drawing: OpSet): string { + 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 'qcurveTo': + path += `Q${data[0]} ${data[1]}, ${data[2]} ${data[3]} `; + break; + case 'lineTo': + path += `L${data[0]} ${data[1]} `; + break; + } + } + return path.trim(); + } } \ No newline at end of file diff --git a/srcts/path.ts b/srcts/path.ts index 5e5a1d0..5800d73 100644 --- a/srcts/path.ts +++ b/srcts/path.ts @@ -59,7 +59,7 @@ class ParsedPath { tokens[tokens.length] = { type: this.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: this.NUMBER, text: `parseFloat(RegExp.$1)` }; + tokens[tokens.length] = { type: this.NUMBER, text: `${parseFloat(RegExp.$1)}` }; d = d.substr(RegExp.$1.length); } else { console.error('Unrecognized segment command: ' + d); From 89fbef0e057bb3a386a169dc96d7563327bec275 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Sun, 17 Jun 2018 00:26:42 -0700 Subject: [PATCH 05/16] path fills --- srcts/canvas.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/srcts/canvas.ts b/srcts/canvas.ts index b8fe003..dacf473 100644 --- a/srcts/canvas.ts +++ b/srcts/canvas.ts @@ -3,6 +3,8 @@ import { RoughGenerator } from './generator'; import { RoughRenderer } from './renderer'; import { Point } from './geometry'; +const hasDocument = typeof document !== 'undefined'; + export class RoughCanvas { private canvas: HTMLCanvasElement; private ctx: CanvasRenderingContext2D; @@ -98,10 +100,60 @@ export class RoughCanvas { case 'fillSketch': this.fillSketch(ctx, drawing, o); break; + case 'path2Dfill': { + this.ctx.save(); + this.ctx.fillStyle = o.fill || ''; + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + break; + } + case 'path2Dpattern': { + if (hasDocument) { + const size = drawing.size!; + const hcanvas = document.createElement('canvas'); + const hcontext = hcanvas.getContext('2d')!; + const bbox = this.computeBBox(drawing.path!); + if (bbox && (bbox.width || bbox.height)) { + hcanvas.width = this.canvas.width; + hcanvas.height = this.canvas.height; + hcontext.translate(bbox.x || 0, bbox.y || 0); + } else { + hcanvas.width = size[0]; + hcanvas.height = size[1]; + } + this.fillSketch(hcontext, drawing, o); + this.ctx.save(); + this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + } + break; + } } } } + private computeBBox(d: string): SVGRect | null { + if (hasDocument) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + document.body.appendChild(svg); + const bbox = pathNode.getBBox(); + document.body.removeChild(svg); + return bbox; + } catch (err) { } + } + return null; + } + private fillSketch(ctx: CanvasRenderingContext2D, drawing: OpSet, o: Options) { let fweight = o.fillWeight; if (fweight < 0) { From d0660149f30e155cf2b58c32c98b3be6ea649e29 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Sun, 17 Jun 2018 01:01:56 -0700 Subject: [PATCH 06/16] async version of canvas --- srcts/canvas-async.ts | 76 ++++++++++++++++++++++++ srcts/canvas.ts | 4 +- srcts/generator-async.ts | 125 +++++++++++++++++++++++++++++++++++++++ srcts/generator.ts | 8 +-- srcts/rough.ts | 13 ++-- 5 files changed, 214 insertions(+), 12 deletions(-) create mode 100644 srcts/canvas-async.ts create mode 100644 srcts/generator-async.ts diff --git a/srcts/canvas-async.ts b/srcts/canvas-async.ts new file mode 100644 index 0000000..7277d81 --- /dev/null +++ b/srcts/canvas-async.ts @@ -0,0 +1,76 @@ +import { RoughCanvas } from './canvas'; +import { Config, Options, Drawable } from './core'; +import { RoughGeneratorAsync } from './generator-async'; +import { Point } from './geometry'; + +export class RoughCanvasAsync extends RoughCanvas { + private genAsync: RoughGeneratorAsync; + + constructor(canvas: HTMLCanvasElement, config?: Config) { + super(canvas, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.canvas); + } + + // @ts-ignore + async line(x1: number, y1: number, x2: number, y2: number, options?: Options): Promise { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + + // @ts-ignore + async rectangle(x: number, y: number, width: number, height: number, options?: Options): Promise { + const d = await this.genAsync.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + + // @ts-ignore + async ellipse(x: number, y: number, width: number, height: number, options?: Options): Promise { + const d = await this.genAsync.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + + // @ts-ignore + async circle(x: number, y: number, diameter: number, options?: Options): Promise { + const d = await this.genAsync.circle(x, y, diameter, options); + this.draw(d); + return d; + } + + // @ts-ignore + async linearPath(points: Point[], options?: Options): Promise { + const d = await this.genAsync.linearPath(points, options); + this.draw(d); + return d; + } + + // @ts-ignore + async polygon(points: Point[], options?: Options): Promise { + const d = await this.genAsync.polygon(points, options); + this.draw(d); + return d; + } + + // @ts-ignore + async arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): Promise { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + + // @ts-ignore + async curve(points: Point[], options?: Options): Promise { + const d = await this.genAsync.curve(points, options); + this.draw(d); + return d; + } + + // @ts-ignore + async path(d: string, options?: Options): Promise { + const drawing = await this.genAsync.path(d, options); + this.draw(drawing); + return drawing; + } +} \ No newline at end of file diff --git a/srcts/canvas.ts b/srcts/canvas.ts index dacf473..9b2a3e5 100644 --- a/srcts/canvas.ts +++ b/srcts/canvas.ts @@ -6,8 +6,8 @@ import { Point } from './geometry'; const hasDocument = typeof document !== 'undefined'; export class RoughCanvas { - private canvas: HTMLCanvasElement; - private ctx: CanvasRenderingContext2D; + protected canvas: HTMLCanvasElement; + protected ctx: CanvasRenderingContext2D; private gen: RoughGenerator; constructor(canvas: HTMLCanvasElement, config?: Config) { diff --git a/srcts/generator-async.ts b/srcts/generator-async.ts new file mode 100644 index 0000000..1dc92a9 --- /dev/null +++ b/srcts/generator-async.ts @@ -0,0 +1,125 @@ +import { RoughGenerator } from './generator'; +import { Options, Drawable, OpSet } from './core'; +import { Point } from './geometry.js'; + +export class RoughGeneratorAsync extends RoughGenerator { + // @ts-ignore + async line(x1: number, y1: number, x2: number, y2: number, options?: Options): Promise { + const o = this._options(options); + return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); + } + + // @ts-ignore + async rectangle(x: number, y: number, width: number, height: number, options?: Options): Promise { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points: Point[] = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + + // @ts-ignore + async ellipse(x: number, y: number, width: number, height: number, options?: Options): Promise { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(await this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(await this.lib.ellipse(x, y, width, height, o)); + return this._drawable('ellipse', paths, o); + } + + // @ts-ignore + async circle(x: number, y: number, diameter: number, options?: Options): Promise { + const ret = await this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + + // @ts-ignore + async linearPath(points: Point[], options?: Options): Promise { + const o = this._options(options); + return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); + } + + // @ts-ignore + async polygon(points: Point[], options?: Options): Promise { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + + // @ts-ignore + async arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): Promise { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(await this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(await this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + + // @ts-ignore + async curve(points: Point[], options?: Options): Promise { + const o = this._options(options); + return this._drawable('curve', [await this.lib.curve(points, o)], o); + } + + // @ts-ignore + async path(d: string, options?: Options): Promise { + const o = this._options(options); + const paths: OpSet[] = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape: OpSet = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } else { + const size = this.computePathSize(d); + const points: Point[] = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(await this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } +} \ No newline at end of file diff --git a/srcts/generator.ts b/srcts/generator.ts index a38f699..0254a34 100644 --- a/srcts/generator.ts +++ b/srcts/generator.ts @@ -31,15 +31,15 @@ export class RoughGenerator { } } - private _options(options?: Options): Options { + protected _options(options?: Options): Options { return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; } - private _drawable(shape: string, sets: OpSet[], options: Options): Drawable { + protected _drawable(shape: string, sets: OpSet[], options: Options): Drawable { return { shape, sets: sets || [], options: options || this.defaultOptions }; } - private get lib(): RoughRenderer { + protected get lib(): RoughRenderer { if (!this.renderer) { this.renderer = new RoughRenderer(); } @@ -61,7 +61,7 @@ export class RoughGenerator { return [100, 100]; } - private computePathSize(d: string): Point { + protected computePathSize(d: string): Point { let size: Point = [0, 0]; if (hasSelf && self.document) { try { diff --git a/srcts/rough.ts b/srcts/rough.ts index e9458d1..e64abba 100644 --- a/srcts/rough.ts +++ b/srcts/rough.ts @@ -2,11 +2,13 @@ import { Config, DrawingSurface } from './core'; import { RoughCanvas } from './canvas'; import { RoughRenderer } from './renderer'; import { RoughGenerator } from './generator'; +import { RoughGeneratorAsync } from './generator-async'; +import { RoughCanvasAsync } from './canvas-async'; export default { - canvas(canvas: HTMLCanvasElement, config?: Config): RoughCanvas { + canvas(canvas: HTMLCanvasElement, config?: Config) { if (config && config.async) { - // TODO: + return new RoughCanvasAsync(canvas, config); } return new RoughCanvas(canvas, config); }, @@ -16,10 +18,9 @@ export default { }, generator(config: Config | null, surface: DrawingSurface) { - // if (config && config.async) { - // return new RoughGeneratorAsync(config, size); - // } - // return new RoughGenerator(config, size); + if (config && config.async) { + return new RoughGeneratorAsync(config, surface); + } return new RoughGenerator(config, surface); } }; \ No newline at end of file From 8373cc7a78feb4611686973457bb9803a4a14dc2 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Sun, 17 Jun 2018 02:10:21 -0700 Subject: [PATCH 07/16] svg rendering --- srcts/canvas-async.ts | 5 ++ srcts/core.ts | 4 +- srcts/generator.ts | 2 +- srcts/rough.ts | 9 +++ srcts/svg-async.ts | 72 ++++++++++++++++++ srcts/svg.ts | 170 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 srcts/svg-async.ts create mode 100644 srcts/svg.ts diff --git a/srcts/canvas-async.ts b/srcts/canvas-async.ts index 7277d81..bc77097 100644 --- a/srcts/canvas-async.ts +++ b/srcts/canvas-async.ts @@ -11,6 +11,11 @@ export class RoughCanvasAsync extends RoughCanvas { this.genAsync = new RoughGeneratorAsync(config || null, this.canvas); } + // @ts-ignore + get generator(): RoughGeneratorAsync { + return this.genAsync; + } + // @ts-ignore async line(x1: number, y1: number, x2: number, y2: number, options?: Options): Promise { const d = await this.genAsync.line(x1, y1, x2, y2, options); diff --git a/srcts/core.ts b/srcts/core.ts index a534418..e58dbbe 100644 --- a/srcts/core.ts +++ b/srcts/core.ts @@ -8,8 +8,8 @@ export interface Config { } export interface DrawingSurface { - width: number; - height: number; + width: number | SVGAnimatedLength; + height: number | SVGAnimatedLength; } export interface Options { diff --git a/srcts/generator.ts b/srcts/generator.ts index 0254a34..d1771b2 100644 --- a/srcts/generator.ts +++ b/srcts/generator.ts @@ -272,7 +272,7 @@ export class RoughGenerator { }; } - private opsToPath(drawing: OpSet): string { + opsToPath(drawing: OpSet): string { let path = ''; for (const item of drawing.ops) { const data = item.data; diff --git a/srcts/rough.ts b/srcts/rough.ts index e64abba..339a768 100644 --- a/srcts/rough.ts +++ b/srcts/rough.ts @@ -4,6 +4,8 @@ import { RoughRenderer } from './renderer'; import { RoughGenerator } from './generator'; import { RoughGeneratorAsync } from './generator-async'; import { RoughCanvasAsync } from './canvas-async'; +import { RoughSVG } from './svg'; +import { RoughSVGAsync } from './svg-async'; export default { canvas(canvas: HTMLCanvasElement, config?: Config) { @@ -13,6 +15,13 @@ export default { return new RoughCanvas(canvas, config); }, + svg(svg: SVGSVGElement, config?: Config) { + if (config && config.async) { + return new RoughSVGAsync(svg, config); + } + return new RoughSVG(svg, config); + }, + createRenderer(): RoughRenderer { return RoughCanvas.createRenderer(); }, diff --git a/srcts/svg-async.ts b/srcts/svg-async.ts new file mode 100644 index 0000000..97ec6cd --- /dev/null +++ b/srcts/svg-async.ts @@ -0,0 +1,72 @@ +import { RoughSVG } from './svg'; +import { Config, Options } from './core'; +import { RoughGeneratorAsync } from './generator-async'; +import { Point } from './geometry'; + +export class RoughSVGAsync extends RoughSVG { + private genAsync: RoughGeneratorAsync; + + constructor(svg: SVGSVGElement, config?: Config) { + super(svg, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.svg); + } + + // @ts-ignore + get generator(): RoughGeneratorAsync { + return this.genAsync; + } + + // @ts-ignore + async line(x1: number, y1: number, x2: number, y2: number, options?: Options): Promise { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + return this.draw(d); + } + + // @ts-ignore + async rectangle(x: number, y: number, width: number, height: number, options?: Options): Promise { + const d = await this.genAsync.rectangle(x, y, width, height, options); + return this.draw(d); + } + + // @ts-ignore + async ellipse(x: number, y: number, width: number, height: number, options?: Options): Promise { + const d = await this.genAsync.ellipse(x, y, width, height, options); + return this.draw(d); + } + + // @ts-ignore + async circle(x: number, y: number, diameter: number, options?: Options): Promise { + const d = await this.genAsync.circle(x, y, diameter, options); + return this.draw(d); + } + + // @ts-ignore + async linearPath(points: Point[], options?: Options): Promise { + const d = await this.genAsync.linearPath(points, options); + return this.draw(d); + } + + // @ts-ignore + async polygon(points: Point[], options?: Options): Promise { + const d = await this.genAsync.polygon(points, options); + return this.draw(d); + } + + // @ts-ignore + async arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): Promise { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + + // @ts-ignore + async curve(points: Point[], options?: Options): Promise { + const d = await this.genAsync.curve(points, options); + return this.draw(d); + } + + // @ts-ignore + async path(d: string, options?: Options): Promise { + const drawing = await this.genAsync.path(d, options); + return this.draw(drawing); + } +} \ No newline at end of file diff --git a/srcts/svg.ts b/srcts/svg.ts new file mode 100644 index 0000000..3692749 --- /dev/null +++ b/srcts/svg.ts @@ -0,0 +1,170 @@ +import { Config, Options, Drawable, OpSet } from './core'; +import { RoughGenerator } from './generator'; +import { RoughRenderer } from './renderer'; +import { Point } from './geometry'; + +const hasDocument = typeof document !== 'undefined'; + +export class RoughSVG { + protected svg: SVGSVGElement; + private gen: RoughGenerator; + protected _defs?: SVGDefsElement; + + constructor(svg: SVGSVGElement, config?: Config) { + this.svg = svg; + this.gen = new RoughGenerator(config || null, this.svg); + } + + get generator(): RoughGenerator { + return this.gen; + } + + static createRenderer(): RoughRenderer { + return new RoughRenderer(); + } + + get defs(): SVGDefsElement | null { + if (hasDocument) { + if (!this._defs) { + const doc = this.svg.ownerDocument || document; + const dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); + if (this.svg.firstChild) { + this.svg.insertBefore(dnode, this.svg.firstChild); + } else { + this.svg.appendChild(dnode); + } + this._defs = dnode; + } + } + return this._defs || null; + } + + line(x1: number, y1: number, x2: number, y2: number, options?: Options) { + const d = this.gen.line(x1, y1, x2, y2, options); + return this.draw(d); + } + + rectangle(x: number, y: number, width: number, height: number, options?: Options) { + const d = this.gen.rectangle(x, y, width, height, options); + return this.draw(d); + } + + ellipse(x: number, y: number, width: number, height: number, options?: Options) { + const d = this.gen.ellipse(x, y, width, height, options); + return this.draw(d); + } + + circle(x: number, y: number, diameter: number, options?: Options) { + const d = this.gen.circle(x, y, diameter, options); + return this.draw(d); + } + + linearPath(points: Point[], options?: Options) { + const d = this.gen.linearPath(points, options); + return this.draw(d); + } + + polygon(points: Point[], options?: Options) { + const d = this.gen.polygon(points, options); + return this.draw(d); + } + + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options) { + const d = this.gen.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + + curve(points: Point[], options?: Options) { + const d = this.gen.curve(points, options); + return this.draw(d); + } + + path(d: string, options?: Options) { + const drawing = this.gen.path(d, options); + return this.draw(drawing); + } + + draw(drawable: Drawable): SVGGElement { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const doc = this.svg.ownerDocument || (hasDocument && document); + const g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); + for (const drawing of sets) { + let path = null; + switch (drawing.type) { + case 'path': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.stroke; + path.style.strokeWidth = o.strokeWidth + ''; + path.style.fill = 'none'; + break; + } + case 'fillPath': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'fillSketch': { + path = this.fillSketch(doc, drawing, o); + break; + } + case 'path2Dfill': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'path2Dpattern': { + const size = drawing.size!; + const pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); + const id = `rough-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999))}`; + pattern.setAttribute('id', id); + pattern.setAttribute('x', '0'); + pattern.setAttribute('y', '0'); + pattern.setAttribute('width', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('viewBox', `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`); + pattern.setAttribute('patternUnits', 'objectBoundingBox'); + const patternPath = this.fillSketch(doc, drawing, o); + pattern.appendChild(patternPath); + this.defs!.appendChild(pattern); + + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = `url(#${id})`; + break; + } + } + if (path) { + g.appendChild(path); + } + } + return g; + } + + private opsToPath(drawing: OpSet) { + return this.gen.opsToPath(drawing); + } + + private fillSketch(doc: Document, drawing: OpSet, o: Options): SVGPathElement { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.fill; + path.style.strokeWidth = fweight + ''; + path.style.fill = 'none'; + return path; + } +} \ No newline at end of file From b68e9085c10fa126e42e4221110b7b4efdb65afb Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Mon, 18 Jun 2018 11:28:59 -0700 Subject: [PATCH 08/16] custom fillers --- srcts/fillers/filler.ts | 12 ++++++++++++ srcts/fillers/hachure-filler.ts | 14 ++++++++++++-- srcts/fillers/hatch-filler.ts | 21 +++++++++++++++++++++ srcts/fillers/zigzag-filler.ts | 13 +++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 srcts/fillers/hatch-filler.ts create mode 100644 srcts/fillers/zigzag-filler.ts diff --git a/srcts/fillers/filler.ts b/srcts/fillers/filler.ts index fd7ea28..b85469d 100644 --- a/srcts/fillers/filler.ts +++ b/srcts/fillers/filler.ts @@ -1,6 +1,8 @@ import { Options } from '../core'; import { PatternFiller, RenderHelper } from './filler-interface'; import { HachureFiller } from './hachure-filler'; +import { ZigZagFiller } from './zigzag-filler'; +import { HatchFiller } from './hatch-filler'; const fillers: { [name: string]: PatternFiller } = {}; @@ -8,6 +10,16 @@ export function getFiller(renderer: RenderHelper, o: Options): PatternFiller { let fillerName = o.fillStyle || 'hachure'; if (!fillers[fillerName]) { switch (fillerName) { + case 'zigzag': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagFiller(renderer); + } + break; + case 'cross-hatch': + if (!fillers[fillerName]) { + fillers[fillerName] = new HatchFiller(renderer); + } + break; case 'hachure': default: fillerName = 'hachure'; diff --git a/srcts/fillers/hachure-filler.ts b/srcts/fillers/hachure-filler.ts index 5a0127e..c55751c 100644 --- a/srcts/fillers/hachure-filler.ts +++ b/srcts/fillers/hachure-filler.ts @@ -18,7 +18,7 @@ export class HachureFiller implements PatternFiller { return this._fillEllipse(cx, cy, width, height, o); } - protected _fillPolygon(points: Point[], o: Options): OpSet { + protected _fillPolygon(points: Point[], o: Options, connectEnds: boolean = false): OpSet { let ops: Op[] = []; if (points && points.length) { let left = points[0][0]; @@ -44,6 +44,7 @@ export class HachureFiller implements PatternFiller { const tanAngle = Math.tan(hachureAngle); const it = new HachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); let rect: number[] | null; + let prevPoint: Point | null = null; while ((rect = it.nextLine()) != null) { const lines = this.getIntersectingLines(rect, points); for (let i = 0; i < lines.length; i++) { @@ -51,6 +52,10 @@ export class HachureFiller implements PatternFiller { const p1 = lines[i]; const p2 = lines[i + 1]; ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; } } } @@ -58,7 +63,7 @@ export class HachureFiller implements PatternFiller { return { type: 'fillSketch', ops }; } - protected _fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet { + protected _fillEllipse(cx: number, cy: number, width: number, height: number, o: Options, connectEnds: boolean = false): OpSet { let ops: Op[] = []; let rx = Math.abs(width / 2); let ry = Math.abs(height / 2); @@ -82,11 +87,16 @@ export class HachureFiller implements PatternFiller { const cosAnglePrime = 1 / hyp; const gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); + let prevPoint: Point | null = null; for (let xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); const p1 = this.affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); const p2 = this.affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; } return { type: 'fillSketch', ops }; } diff --git a/srcts/fillers/hatch-filler.ts b/srcts/fillers/hatch-filler.ts new file mode 100644 index 0000000..012707e --- /dev/null +++ b/srcts/fillers/hatch-filler.ts @@ -0,0 +1,21 @@ +import { HachureFiller } from './hachure-filler'; +import { Options, OpSet } from '../core'; +import { Point } from '../geometry'; + +export class HatchFiller extends HachureFiller { + fillPolygon(points: Point[], o: Options): OpSet { + 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; + } + + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet { + const set = this._fillEllipse(cx, cy, width, height, o); + const o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + const set2 = this._fillEllipse(cx, cy, width, height, o2); + set.ops = set.ops.concat(set2.ops); + return set; + } +} \ No newline at end of file diff --git a/srcts/fillers/zigzag-filler.ts b/srcts/fillers/zigzag-filler.ts new file mode 100644 index 0000000..c8f1a05 --- /dev/null +++ b/srcts/fillers/zigzag-filler.ts @@ -0,0 +1,13 @@ +import { HachureFiller } from './hachure-filler'; +import { Options, OpSet } from '../core'; +import { Point } from '../geometry'; + +export class ZigZagFiller extends HachureFiller { + fillPolygon(points: Point[], o: Options): OpSet { + return this._fillPolygon(points, o, true); + } + + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet { + return this._fillEllipse(cx, cy, width, height, o, true); + } +} \ No newline at end of file From 670a48e253099569277cf487199d11cefa2a45a9 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Mon, 18 Jun 2018 11:29:46 -0700 Subject: [PATCH 09/16] version updates --- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29394b7..d006043 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,9 +17,9 @@ "dev": true }, "@types/node": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.1.tgz", - "integrity": "sha512-IsX9aDHDzJohkm3VCDB8tkzl5RQ34E/PFA29TQk6uDGb7Oc869ZBtmdKVDBzY3+h9GnXB8ssrRXEPVZrlIOPOw==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.3.tgz", + "integrity": "sha512-/gwCgiI2e9RzzZTKbl+am3vgNqOt7a9fJ/uxv4SqYKxenoEDNVU3KZEadlpusWhQI0A0dOrZ0T68JYKVjzmgdQ==", "dev": true }, "ansi-regex": { @@ -794,9 +794,9 @@ } }, "rollup": { - "version": "0.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.60.1.tgz", - "integrity": "sha512-LujiS7PH8DwKAphB2ldaSEF1EX9hWY9w+mct2b4DczC8tvn7qwmr9ZFLtM9IT7gPFYlmS8O1JdiLT/aEiBEcsA==", + "version": "0.60.7", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.60.7.tgz", + "integrity": "sha512-Uj5I1A2PnDgA79P+v1dsNs1IHVydNgeJdKWRfoEJJdNMmyx07TRYqUtPUINaZ/gDusncFy1SZsT3lJnBBI8CGw==", "dev": true, "requires": { "@types/estree": "0.0.39", @@ -948,9 +948,9 @@ } }, "typescript": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz", - "integrity": "sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "dev": true }, "wrappy": { diff --git a/package.json b/package.json index 39129aa..36c66e7 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,9 @@ }, "homepage": "https://roughjs.com", "devDependencies": { - "rollup": "^0.60.1", + "rollup": "^0.60.7", "rollup-plugin-babel-minify": "^5.0.0", "tslint": "^5.10.0", - "typescript": "^2.9.1" + "typescript": "^2.9.2" } } From c29aac6b2957d3702791fba90545a7262adc429c Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Mon, 18 Jun 2018 11:39:48 -0700 Subject: [PATCH 10/16] new build scripts --- dist/rough.js | 4414 ++++++++++++++++++++-------------------- dist/rough.min.js | 2 +- dist/rough.umd.js | 4416 ++++++++++++++++++++--------------------- dist/rough.umd.min.js | 2 +- package.json | 8 +- rollup.config.js | 40 + 6 files changed, 4433 insertions(+), 4449 deletions(-) create mode 100644 rollup.config.js diff --git a/dist/rough.js b/dist/rough.js index cd318ab..66eaf05 100644 --- a/dist/rough.js +++ b/dist/rough.js @@ -1,2260 +1,2234 @@ var rough = (function () { -'use strict'; - -function RoughSegmentRelation() { - return { - LEFT: 0, - RIGHT: 1, - INTERSECTS: 2, - AHEAD: 3, - BEHIND: 4, - SEPARATE: 5, - UNDEFINED: 6 - }; -} - -class RoughSegment { - constructor(px1, py1, px2, py2) { - this.RoughSegmentRelationConst = RoughSegmentRelation(); - this.px1 = px1; - this.py1 = py1; - this.px2 = px2; - this.py2 = py2; - this.xi = Number.MAX_VALUE; - this.yi = Number.MAX_VALUE; - this.a = py2 - py1; - this.b = px1 - px2; - this.c = px2 * py1 - px1 * py2; - this._undefined = ((this.a == 0) && (this.b == 0) && (this.c == 0)); - } - - isUndefined() { - return this._undefined; - } - - compare(otherSegment) { - if (this.isUndefined() || otherSegment.isUndefined()) { - return this.RoughSegmentRelationConst.UNDEFINED; - } - var grad1 = Number.MAX_VALUE; - var grad2 = Number.MAX_VALUE; - var int1 = 0, int2 = 0; - var a = this.a, b = this.b, c = this.c; - - if (Math.abs(b) > 0.00001) { - grad1 = -a / b; - int1 = -c / b; - } - if (Math.abs(otherSegment.b) > 0.00001) { - grad2 = -otherSegment.a / otherSegment.b; - int2 = -otherSegment.c / otherSegment.b; - } - - if (grad1 == Number.MAX_VALUE) { - if (grad2 == Number.MAX_VALUE) { - if ((-c / a) != (-otherSegment.c / otherSegment.a)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if ((this.py1 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py1 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px1; - this.yi = this.py1; - return this.RoughSegmentRelationConst.INTERSECTS; - } - if ((this.py2 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py2 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px2; - this.yi = this.py2; - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.SEPARATE; - } - this.xi = this.px1; - this.yi = (grad2 * this.xi + int2); - if (((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) || ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if (Math.abs(otherSegment.a) < 0.00001) { - if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - if (grad2 == Number.MAX_VALUE) { - this.xi = otherSegment.px1; - this.yi = grad1 * this.xi + int1; - if (((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) || ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if (Math.abs(a) < 0.00001) { - if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - if (grad1 == grad2) { - if (int1 != int2) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if ((this.px1 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px1 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px1; - this.yi = this.py1; - return this.RoughSegmentRelationConst.INTERSECTS; - } - if ((this.px2 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px2 <= Math.max(otherSegment.px1, otherSegment.px2))) { - this.xi = this.px2; - this.yi = this.py2; - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.SEPARATE; - } - - this.xi = ((int2 - int1) / (grad1 - grad2)); - this.yi = (grad1 * this.xi + int1); - - if (((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) || ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - getLength() { - return this._getLength(this.px1, this.py1, this.px2, this.py2); - } - - _getLength(x1, y1, x2, y2) { - var dx = x2 - x1; - var dy = y2 - y1; - return Math.sqrt(dx * dx + dy * dy); - } -} - -class RoughHachureIterator { - constructor(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { - this.top = top; - this.bottom = bottom; - this.left = left; - this.right = right; - this.gap = gap; - this.sinAngle = sinAngle; - this.tanAngle = tanAngle; - - if (Math.abs(sinAngle) < 0.0001) { - this.pos = left + gap; - } else if (Math.abs(sinAngle) > 0.9999) { - this.pos = top + gap; - } else { - this.deltaX = (bottom - top) * Math.abs(tanAngle); - this.pos = left - Math.abs(this.deltaX); - this.hGap = Math.abs(gap / cosAngle); - this.sLeft = new RoughSegment(left, bottom, left, top); - this.sRight = new RoughSegment(right, bottom, right, top); - } - } - - getNextLine() { - if (Math.abs(this.sinAngle) < 0.0001) { - if (this.pos < this.right) { - let line = [this.pos, this.top, this.pos, this.bottom]; - this.pos += this.gap; - return line; - } - } else if (Math.abs(this.sinAngle) > 0.9999) { - if (this.pos < this.bottom) { - let line = [this.left, this.pos, this.right, this.pos]; - this.pos += this.gap; - return line; - } - } else { - let xLower = this.pos - this.deltaX / 2; - let xUpper = this.pos + this.deltaX / 2; - let yLower = this.bottom; - let yUpper = this.top; - if (this.pos < (this.right + this.deltaX)) { - while (((xLower < this.left) && (xUpper < this.left)) || ((xLower > this.right) && (xUpper > this.right))) { - this.pos += this.hGap; - xLower = this.pos - this.deltaX / 2; - xUpper = this.pos + this.deltaX / 2; - if (this.pos > (this.right + this.deltaX)) { - return null; - } + 'use strict'; + + function isType(token, type) { + return token.type === type; + } + const PARAMS = { + A: { length: 7 }, + a: { length: 7 }, + C: { length: 6 }, + c: { length: 6 }, + H: { length: 1 }, + h: { length: 1 }, + L: { length: 2 }, + l: { length: 2 }, + M: { length: 2 }, + m: { length: 2 }, + Q: { length: 4 }, + q: { length: 4 }, + S: { length: 4 }, + s: { length: 4 }, + T: { length: 4 }, + t: { length: 2 }, + V: { length: 1 }, + v: { length: 1 }, + Z: { length: 0 }, + z: { length: 0 } + }; + class ParsedPath { + constructor(d) { + this.COMMAND = 0; + this.NUMBER = 1; + this.EOD = 2; + this.segments = []; + this.parseData(d); + this.processPoints(); } - let s = new RoughSegment(xLower, yLower, xUpper, yUpper); - if (s.compare(this.sLeft) == RoughSegmentRelation().INTERSECTS) { - xLower = s.xi; - yLower = s.yi; + 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: this.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: this.NUMBER, text: `${parseFloat(RegExp.$1)}` }; + d = d.substr(RegExp.$1.length); + } + else { + console.error('Unrecognized segment command: ' + d); + return []; + } + } + tokens[tokens.length] = { type: this.EOD, text: '' }; + return tokens; } - if (s.compare(this.sRight) == RoughSegmentRelation().INTERSECTS) { - xUpper = s.xi; - yUpper = s.yi; + parseData(d) { + const tokens = this.tokenize(d); + let index = 0; + let token = tokens[index]; + let mode = 'BOD'; + this.segments = new Array(); + while (!isType(token, this.EOD)) { + let param_length; + const params = new Array(); + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + else { + this.parseData('M0,0' + d); + return; + } + } + else { + if (isType(token, this.NUMBER)) { + param_length = PARAMS[mode].length; + } + else { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + } + if ((index + param_length) < tokens.length) { + for (let i = index; i < index + param_length; i++) { + const numbeToken = tokens[i]; + if (isType(numbeToken, this.NUMBER)) { + params[params.length] = +numbeToken.text; + } + else { + console.error('Parameter type is not a number: ' + mode + ',' + numbeToken.text); + return; + } + } + if (PARAMS[mode]) { + const segment = { key: mode, data: params }; + this.segments.push(segment); + index += param_length; + token = tokens[index]; + if (mode === 'M') + mode = 'L'; + if (mode === 'm') + mode = 'l'; + } + else { + console.error('Unsupported segment type: ' + mode); + return; + } + } + else { + console.error('Path data ended before all parameters were found'); + } + } } - if (this.tanAngle > 0) { - xLower = this.right - (xLower - this.left); - xUpper = this.right - (xUpper - this.left); + get closed() { + if (typeof this._closed === 'undefined') { + this._closed = false; + for (const s of this.segments) { + if (s.key.toLowerCase() === 'z') { + this._closed = true; + } + } + } + return this._closed; + } + processPoints() { + let first = null; + let currentPoint = [0, 0]; + for (let i = 0; i < this.segments.length; i++) { + const s = this.segments[i]; + switch (s.key) { + case 'M': + case 'L': + case 'T': + s.point = [s.data[0], s.data[1]]; + break; + case 'm': + case 'l': + case 't': + s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; + break; + case 'H': + s.point = [s.data[0], currentPoint[1]]; + break; + case 'h': + s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; + break; + case 'V': + s.point = [currentPoint[0], s.data[0]]; + break; + case 'v': + s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; + break; + case 'z': + case 'Z': + if (first) { + s.point = [first[0], first[1]]; + } + break; + case 'C': + s.point = [s.data[4], s.data[5]]; + break; + case 'c': + s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; + break; + case 'S': + s.point = [s.data[2], s.data[3]]; + break; + case 's': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'Q': + s.point = [s.data[2], s.data[3]]; + break; + case 'q': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'A': + s.point = [s.data[5], s.data[6]]; + break; + case 'a': + s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; + break; + } + if (s.key === 'm' || s.key === 'M') { + first = null; + } + if (s.point) { + currentPoint = s.point; + if (!first) { + first = s.point; + } + } + if (s.key === 'z' || s.key === 'Z') { + first = null; + } + } } - let line = [xLower, yLower, xUpper, yUpper]; - this.pos += this.hGap; - return line; - } - } - return null; - } -} - -class PathToken { - constructor(type, text) { - this.type = type; - this.text = text; - } - isType(type) { - return this.type === type; - } -} - -class ParsedPath { - constructor(d) { - this.PARAMS = { - A: ["rx", "ry", "x-axis-rotation", "large-arc-flag", "sweep-flag", "x", "y"], - a: ["rx", "ry", "x-axis-rotation", "large-arc-flag", "sweep-flag", "x", "y"], - C: ["x1", "y1", "x2", "y2", "x", "y"], - c: ["x1", "y1", "x2", "y2", "x", "y"], - H: ["x"], - h: ["x"], - L: ["x", "y"], - l: ["x", "y"], - M: ["x", "y"], - m: ["x", "y"], - Q: ["x1", "y1", "x", "y"], - q: ["x1", "y1", "x", "y"], - S: ["x2", "y2", "x", "y"], - s: ["x2", "y2", "x", "y"], - T: ["x", "y"], - t: ["x", "y"], - V: ["y"], - v: ["y"], - Z: [], - z: [] - }; - this.COMMAND = 0; - this.NUMBER = 1; - this.EOD = 2; - this.segments = []; - this.d = d || ""; - this.parseData(d); - this.processPoints(); - } - - loadFromSegments(segments) { - this.segments = segments; - this.processPoints(); - } - - processPoints() { - let first = null, currentPoint = [0, 0]; - for (let i = 0; i < this.segments.length; i++) { - let s = this.segments[i]; - switch (s.key) { - case 'M': - case 'L': - case 'T': - s.point = [s.data[0], s.data[1]]; - break; - case 'm': - case 'l': - case 't': - s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; - break; - case 'H': - s.point = [s.data[0], currentPoint[1]]; - break; - case 'h': - s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; - break; - case 'V': - s.point = [currentPoint[0], s.data[0]]; - break; - case 'v': - s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; - break; - case 'z': - case 'Z': - if (first) { - s.point = [first[0], first[1]]; - } - break; - case 'C': - s.point = [s.data[4], s.data[5]]; - break; - case 'c': - s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; - break; - case 'S': - s.point = [s.data[2], s.data[3]]; - break; - case 's': - s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; - break; - case 'Q': - s.point = [s.data[2], s.data[3]]; - break; - case 'q': - s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; - break; - case 'A': - s.point = [s.data[5], s.data[6]]; - break; - case 'a': - s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; - break; - } - if (s.key === 'm' || s.key === 'M') { - first = null; - } - if (s.point) { - currentPoint = s.point; - if (!first) { - first = s.point; - } - } - if (s.key === 'z' || s.key === 'Z') { - first = null; - } - } - } - - get closed() { - if (typeof this._closed === 'undefined') { - this._closed = false; - for (let s of this.segments) { - if (s.key.toLowerCase() === 'z') { - this._closed = true; - } - } - } - return this._closed; - } - - parseData(d) { - var tokens = this.tokenize(d); - var index = 0; - var token = tokens[index]; - var mode = "BOD"; - this.segments = new Array(); - while (!token.isType(this.EOD)) { - var param_length; - var params = new Array(); - if (mode == "BOD") { - if (token.text == "M" || token.text == "m") { - index++; - param_length = this.PARAMS[token.text].length; - mode = token.text; - } else { - return this.parseData('M0,0' + d); - } - } else { - if (token.isType(this.NUMBER)) { - param_length = this.PARAMS[mode].length; - } else { - index++; - param_length = this.PARAMS[token.text].length; - mode = token.text; - } - } - - if ((index + param_length) < tokens.length) { - for (var i = index; i < index + param_length; i++) { - var number = tokens[i]; - if (number.isType(this.NUMBER)) { - params[params.length] = number.text; - } - else { - console.error("Parameter type is not a number: " + mode + "," + number.text); - return; - } - } - var segment; - if (this.PARAMS[mode]) { - segment = { key: mode, data: params }; - } else { - console.error("Unsupported segment type: " + mode); - return; - } - this.segments.push(segment); - index += param_length; - token = tokens[index]; - if (mode == "M") mode = "L"; - if (mode == "m") mode = "l"; - } else { - console.error("Path data ended before all parameters were found"); - } - } - } - - tokenize(d) { - var 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] = new PathToken(this.COMMAND, RegExp.$1); - d = d.substr(RegExp.$1.length); - } else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) { - tokens[tokens.length] = new PathToken(this.NUMBER, parseFloat(RegExp.$1)); - d = d.substr(RegExp.$1.length); - } else { - console.error("Unrecognized segment command: " + d); - return null; - } - } - tokens[tokens.length] = new PathToken(this.EOD, null); - return tokens; - } -} - -class RoughPath { - constructor(d) { - this.d = d; - this.parsed = new ParsedPath(d); - this._position = [0, 0]; - this.bezierReflectionPoint = null; - this.quadReflectionPoint = null; - this._first = null; - } - - get segments() { - return this.parsed.segments; - } - - get closed() { - return this.parsed.closed; - } - - get linearPoints() { - if (!this._linearPoints) { - const lp = []; - let points = []; - for (let s of this.parsed.segments) { - let key = s.key.toLowerCase(); - if (key === 'm' || key === 'z') { - if (points.length) { - lp.push(points); - points = []; - } - if (key === 'z') { - continue; - } - } - if (s.point) { - points.push(s.point); - } - } - if (points.length) { - lp.push(points); - points = []; - } - this._linearPoints = lp; - } - return this._linearPoints; - } - - get first() { - return this._first; - } - - set first(v) { - this._first = v; - } - - setPosition(x, y) { - this._position = [x, y]; - if (!this._first) { - this._first = [x, y]; - } - } - - get position() { - return this._position; - } - - get x() { - return this._position[0]; - } - - get y() { - return this._position[1]; - } -} - -class RoughArcConverter { - // Algorithm as described in https://www.w3.org/TR/SVG/implnote.html - // Code adapted from nsSVGPathDataParser.cpp in Mozilla - // https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 - constructor(from, to, radii, angle, largeArcFlag, sweepFlag) { - const radPerDeg = Math.PI / 180; - this._segIndex = 0; - this._numSegs = 0; - if (from[0] == to[0] && from[1] == to[1]) { - return; - } - this._rx = Math.abs(radii[0]); - this._ry = Math.abs(radii[1]); - this._sinPhi = Math.sin(angle * radPerDeg); - this._cosPhi = Math.cos(angle * radPerDeg); - var x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; - var y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; - var root; - var numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; - if (numerator < 0) { - let s = Math.sqrt(1 - (numerator / (this._rx * this._rx * this._ry * this._ry))); - this._rx = this._rx * s; - this._ry = this._ry * s; - root = 0; - } else { - root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) * - Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); - } - let cxdash = root * this._rx * y1dash / this._ry; - let cydash = -root * this._ry * x1dash / this._rx; - this._C = [0, 0]; - this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; - this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; - this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); - let dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); - if ((!sweepFlag) && (dtheta > 0)) { - dtheta -= 2 * Math.PI; - } else if (sweepFlag && (dtheta < 0)) { - dtheta += 2 * Math.PI; - } - this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); - this._delta = dtheta / this._numSegs; - this._T = (8 / 3) * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); - this._from = from; - } - - getNextSegment() { - var cp1, cp2, to; - if (this._segIndex == this._numSegs) { - return null; - } - let cosTheta1 = Math.cos(this._theta); - let sinTheta1 = Math.sin(this._theta); - let theta2 = this._theta + this._delta; - let cosTheta2 = Math.cos(theta2); - let sinTheta2 = Math.sin(theta2); - - to = [ - this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], - this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1] - ]; - cp1 = [ - this._from[0] + this._T * (- this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), - this._from[1] + this._T * (- this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1) - ]; - cp2 = [ - to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), - to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2) - ]; - - this._theta = theta2; - this._from = [to[0], to[1]]; - this._segIndex++; - - return { - cp1: cp1, - cp2: cp2, - to: to - }; - } - - calculateVectorAngle(ux, uy, vx, vy) { - let ta = Math.atan2(uy, ux); - let tb = Math.atan2(vy, vx); - if (tb >= ta) - return tb - ta; - return 2 * Math.PI - (ta - tb); - } -} - -class PathFitter { - constructor(sets, closed) { - this.sets = sets; - this.closed = closed; - } - - fit(simplification) { - let outSets = []; - for (const set of this.sets) { - let length = set.length; - let estLength = Math.floor(simplification * length); - if (estLength < 5) { - if (length <= 5) { - continue; - } - estLength = 5; - } - outSets.push(this.reduce(set, estLength)); - } - - let d = ''; - for (const set of outSets) { - for (let i = 0; i < set.length; i++) { - let point = set[i]; - if (i === 0) { - d += 'M' + point[0] + "," + point[1]; - } else { - d += 'L' + point[0] + "," + point[1]; - } - } - if (this.closed) { - d += 'z '; - } - } - return d; - } - - distance(p1, p2) { - return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); - } - - reduce(set, count) { - if (set.length <= count) { - return set; - } - let points = set.slice(0); - while (points.length > count) { - let minArea = -1; - let minIndex = -1; - for (let i = 1; i < (points.length - 1); i++) { - let a = this.distance(points[i - 1], points[i]); - let b = this.distance(points[i], points[i + 1]); - let c = this.distance(points[i - 1], points[i + 1]); - let s = (a + b + c) / 2.0; - let area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); - if ((minArea < 0) || (area < minArea)) { - minArea = area; - minIndex = i; - } - } - if (minIndex > 0) { - points.splice(minIndex, 1); - } else { - break; - } - } - return points; - } -} - -class RoughRenderer { - line(x1, y1, x2, y2, o) { - let ops = this._doubleLine(x1, y1, x2, y2, o); - return { type: 'path', ops }; - } - - linearPath(points, close, o) { - const len = (points || []).length; - if (len > 2) { - let ops = []; - for (let i = 0; i < (len - 1); i++) { - ops = ops.concat(this._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); - } - if (close) { - ops = ops.concat(this._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 this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); - } - } - - polygon(points, o) { - return this.linearPath(points, true, o); - } - - rectangle(x, y, width, height, o) { - let points = [ - [x, y], [x + width, y], [x + width, y + height], [x, y + height] - ]; - return this.polygon(points, o); - } - - curve(points, o) { - let o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); - let o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); - return { type: 'path', ops: o1.concat(o2) }; - } - - ellipse(x, y, width, height, o) { - const increment = (Math.PI * 2) / o.curveStepCount; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.05, rx * 0.05, o); - ry += this._getOffset(-ry * 0.05, ry * 0.05, o); - let o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this._getOffset(0.1, this._getOffset(0.4, 1, o), o), o); - let o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); - return { type: 'path', ops: o1.concat(o2) }; - } - - arc(x, y, width, height, start, stop, closed, roughClosure, o) { - let cx = x; - let cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.01, rx * 0.01, o); - ry += this._getOffset(-ry * 0.01, 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; - } - let ellipseInc = (Math.PI * 2) / o.curveStepCount; - let arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); - let o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); - let o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); - let ops = o1.concat(o2); - if (closed) { - if (roughClosure) { - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); - } else { - ops.push({ op: 'lineTo', data: [cx, cy] }); - ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); - } - } - return { type: 'path', ops }; - } - - hachureFillArc(x, y, width, height, start, stop, o) { - let cx = x; - let cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.01, rx * 0.01, o); - ry += this._getOffset(-ry * 0.01, 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; - } - let increment = (stp - strt) / o.curveStepCount; - let xc = [], yc = []; - for (let angle = strt; angle <= stp; angle = angle + increment) { - xc.push(cx + rx * Math.cos(angle)); - yc.push(cy + ry * Math.sin(angle)); - } - xc.push(cx + rx * Math.cos(stp)); - yc.push(cy + ry * Math.sin(stp)); - xc.push(cx); - yc.push(cy); - return this.hachureFillShape(xc, yc, o); - } - - solidFillShape(xCoords, yCoords, o) { - let ops = []; - if (xCoords && yCoords && xCoords.length && yCoords.length && xCoords.length === yCoords.length) { - let offset = o.maxRandomnessOffset || 0; - const len = xCoords.length; - if (len > 2) { - ops.push({ op: 'move', data: [xCoords[0] + this._getOffset(-offset, offset, o), yCoords[0] + this._getOffset(-offset, offset, o)] }); - for (var i = 1; i < len; i++) { - ops.push({ op: 'lineTo', data: [xCoords[i] + this._getOffset(-offset, offset, o), yCoords[i] + this._getOffset(-offset, offset, o)] }); - } - } - } - return { type: 'fillPath', ops }; - } - - hachureFillShape(xCoords, yCoords, o) { - let ops = []; - if (xCoords && yCoords && xCoords.length && yCoords.length) { - let left = xCoords[0]; - let right = xCoords[0]; - let top = yCoords[0]; - let bottom = yCoords[0]; - for (let 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]); - } - const angle = o.hachureAngle; - let gap = o.hachureGap; - if (gap < 0) { - gap = o.strokeWidth * 4; - } - gap = Math.max(gap, 0.1); - - const radPerDeg = Math.PI / 180; - const hachureAngle = (angle % 180) * radPerDeg; - const cosAngle = Math.cos(hachureAngle); - const sinAngle = Math.sin(hachureAngle); - const tanAngle = Math.tan(hachureAngle); - - const it = new RoughHachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); - let rectCoords; - while ((rectCoords = it.getNextLine()) != null) { - let lines = this._getIntersectingLines(rectCoords, xCoords, yCoords); - for (let i = 0; i < lines.length; i++) { - if (i < (lines.length - 1)) { - let p1 = lines[i]; - let p2 = lines[i + 1]; - ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o)); - } - } - } - } - return { type: 'fillSketch', ops }; - } - - hachureFillEllipse(cx, cy, width, height, o) { - let ops = []; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.05, rx * 0.05, o); - ry += this._getOffset(-ry * 0.05, ry * 0.05, o); - let angle = o.hachureAngle; - let gap = o.hachureGap; - if (gap <= 0) { - gap = o.strokeWidth * 4; - } - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - const radPerDeg = Math.PI / 180; - let hachureAngle = (angle % 180) * radPerDeg; - let tanAngle = Math.tan(hachureAngle); - let aspectRatio = ry / rx; - let hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); - let sinAnglePrime = aspectRatio * tanAngle / hyp; - let cosAnglePrime = 1 / hyp; - let gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); - let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); - for (var xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { - halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); - let p1 = this._affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); - let p2 = this._affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); - ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o)); - } - return { type: 'fillSketch', ops }; - } - - svgPath(path, o) { - path = (path || '').replace(/\n/g, " ").replace(/(-\s)/g, "-").replace("/(\s\s)/g", " "); - let p = new RoughPath(path); - if (o.simplification) { - let fitter = new PathFitter(p.linearPoints, p.closed); - let d = fitter.fit(o.simplification); - p = new RoughPath(d); - } - let ops = []; - let segments = p.segments || []; - for (let i = 0; i < segments.length; i++) { - let s = segments[i]; - let prev = i > 0 ? segments[i - 1] : null; - let opList = this._processSegment(p, s, prev, o); - if (opList && opList.length) { - ops = ops.concat(opList); - } - } - return { type: 'path', ops }; - } - - // privates - - _bezierTo(x1, y1, x2, y2, x, y, path, o) { - let ops = []; - let ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; - let f = null; - for (let i = 0; i < 2; i++) { - if (i === 0) { - ops.push({ op: 'move', data: [path.x, path.y] }); - } else { - ops.push({ op: 'move', data: [path.x + this._getOffset(-ros[0], ros[0], o), path.y + this._getOffset(-ros[0], ros[0], o)] }); - } - f = [x + this._getOffset(-ros[i], ros[i], o), y + this._getOffset(-ros[i], ros[i], o)]; - ops.push({ - op: 'bcurveTo', data: [ - x1 + this._getOffset(-ros[i], ros[i], o), y1 + this._getOffset(-ros[i], ros[i], o), - x2 + this._getOffset(-ros[i], ros[i], o), y2 + this._getOffset(-ros[i], ros[i], o), - f[0], f[1] - ] - }); - } - path.setPosition(f[0], f[1]); - return ops; - } - - _processSegment(path, seg, prevSeg, o) { - let ops = []; - switch (seg.key) { - case 'M': - case 'm': { - let delta = seg.key === 'm'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let ro = 1 * (o.maxRandomnessOffset || 0); - x = x + this._getOffset(-ro, ro, o); - y = y + this._getOffset(-ro, ro, o); - path.setPosition(x, y); - ops.push({ op: 'move', data: [x, y] }); - } - break; - } - case 'L': - case 'l': { - let delta = seg.key === 'l'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } - break; - } - case 'H': - case 'h': { - const delta = seg.key === 'h'; - if (seg.data.length) { - let x = +seg.data[0]; - if (delta) { - x += path.x; - } - ops = ops.concat(this._doubleLine(path.x, path.y, x, path.y, o)); - path.setPosition(x, path.y); - } - break; - } - case 'V': - case 'v': { - const delta = seg.key === 'v'; - if (seg.data.length) { - let y = +seg.data[0]; - if (delta) { - y += path.y; - } - ops = ops.concat(this._doubleLine(path.x, path.y, path.x, y, o)); - path.setPosition(path.x, y); - } - break; - } - case 'Z': - case 'z': { - if (path.first) { - ops = ops.concat(this._doubleLine(path.x, path.y, path.first[0], path.first[1], o)); - path.setPosition(path.first[0], path.first[1]); - path.first = null; - } - break; - } - case 'C': - case 'c': { - const delta = seg.key === 'c'; - if (seg.data.length >= 6) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x2 = +seg.data[2]; - let y2 = +seg.data[3]; - let x = +seg.data[4]; - let y = +seg.data[5]; - if (delta) { - x1 += path.x; - x2 += path.x; - x += path.x; - y1 += path.y; - y2 += path.y; - y += path.y; - } - let ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'S': - case 's': { - const delta = seg.key === 's'; - if (seg.data.length >= 4) { - let x2 = +seg.data[0]; - let y2 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x2 += path.x; - x += path.x; - y2 += path.y; - y += path.y; - } - let x1 = x2; - let y1 = y2; - let prevKey = prevSeg ? prevSeg.key : ""; - var ref = null; - if (prevKey == 'c' || prevKey == 'C' || prevKey == 's' || prevKey == 'S') { - ref = path.bezierReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - let ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'Q': - case 'q': { - const delta = seg.key === 'q'; - if (seg.data.length >= 4) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x1 += path.x; - x += path.x; - y1 += path.y; - y += path.y; - } - let offset1 = 1 * (1 + o.roughness * 0.2); - let offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'T': - case 't': { - const delta = seg.key === 't'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let x1 = x; - let y1 = y; - let prevKey = prevSeg ? prevSeg.key : ""; - var ref = null; - if (prevKey == 'q' || prevKey == 'Q' || prevKey == 't' || prevKey == 'T') { - ref = path.quadReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - let offset1 = 1 * (1 + o.roughness * 0.2); - let offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'A': - case 'a': { - const delta = seg.key === 'a'; - if (seg.data.length >= 7) { - let rx = +seg.data[0]; - let ry = +seg.data[1]; - let angle = +seg.data[2]; - let largeArcFlag = +seg.data[3]; - let sweepFlag = +seg.data[4]; - let x = +seg.data[5]; - let y = +seg.data[6]; - if (delta) { - x += path.x; - y += path.y; - } - if (x == path.x && y == path.y) { - break; - } - if (rx == 0 || ry == 0) { - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } else { - let ro = o.maxRandomnessOffset || 0; - for (let i = 0; i < 1; i++) { - let arcConverter = new RoughArcConverter( - [path.x, path.y], - [x, y], - [rx, ry], - angle, - largeArcFlag ? true : false, - sweepFlag ? true : false - ); - let segment = arcConverter.getNextSegment(); - while (segment) { - let ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); - ops = ops.concat(ob); - segment = arcConverter.getNextSegment(); - } - } - } - } - break; - } - default: - break; - } - return ops; - } - - _getOffset(min, max, ops) { - return ops.roughness * ((Math.random() * (max - min)) + min); - } - - _affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { - var A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; - var B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; - var C = cosAnglePrime; - var D = sinAnglePrime; - var E = -R * sinAnglePrime; - var F = R * cosAnglePrime; - return [ - A + C * x + D * y, - B + E * x + F * y - ]; - } - - _doubleLine(x1, y1, x2, y2, o) { - const o1 = this._line(x1, y1, x2, y2, o, true, false); - const o2 = this._line(x1, y1, x2, y2, o, true, true); - return o1.concat(o2); - } - - _line(x1, y1, x2, y2, o, move, overlay) { - const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); - let offset = o.maxRandomnessOffset || 0; - if ((offset * offset * 100) > lengthSq) { - offset = Math.sqrt(lengthSq) / 10; - } - const halfOffset = offset / 2; - const divergePoint = 0.2 + Math.random() * 0.2; - let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; - let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; - midDispX = this._getOffset(-midDispX, midDispX, o); - midDispY = this._getOffset(-midDispY, midDispY, o); - let ops = []; - if (move) { - if (overlay) { - ops.push({ - op: 'move', data: [ - x1 + this._getOffset(-halfOffset, halfOffset, o), - y1 + this._getOffset(-halfOffset, halfOffset, o) - ] - }); - } else { - ops.push({ - op: 'move', data: [ - x1 + this._getOffset(-offset, offset, o), - y1 + this._getOffset(-offset, offset, o) - ] - }); - } - } - if (overlay) { - ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - x2 + this._getOffset(-halfOffset, halfOffset, o), - y2 + this._getOffset(-halfOffset, halfOffset, o) - ] - }); - } else { - ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - x2 + this._getOffset(-offset, offset, o), - y2 + this._getOffset(-offset, offset, o) - ] - }); } - return ops; - } - - _curve(points, closePoint, o) { - const len = points.length; - let 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) { - let ro = o.maxRandomnessOffset; - // TODO: more roughness here? - ops.push({ ops: 'lineTo', data: [closePoint[0] + this._getOffset(-ro, ro, o), closePoint[1] + + this._getOffset(-ro, 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 = ops.concat(this._doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + class RoughPath { + constructor(d) { + this._position = [0, 0]; + this._first = null; + this.bezierReflectionPoint = null; + this.quadReflectionPoint = null; + this.parsed = new ParsedPath(d); + } + get segments() { + return this.parsed.segments; + } + get closed() { + return this.parsed.closed; + } + get linearPoints() { + if (!this._linearPoints) { + const lp = []; + let points = []; + for (const s of this.parsed.segments) { + const key = s.key.toLowerCase(); + if (key === 'm' || key === 'z') { + if (points.length) { + lp.push(points); + points = []; + } + if (key === 'z') { + continue; + } + } + if (s.point) { + points.push(s.point); + } + } + if (points.length) { + lp.push(points); + points = []; + } + this._linearPoints = lp; + } + return this._linearPoints; + } + get first() { + return this._first; + } + set first(v) { + this._first = v; + } + setPosition(x, y) { + this._position = [x, y]; + if (!this._first) { + this._first = [x, y]; + } + } + get position() { + return this._position; + } + get x() { + return this._position[0]; + } + get y() { + return this._position[1]; + } } - return ops; - } - - _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { - const radOffset = this._getOffset(-0.5, 0.5, o) - (Math.PI / 2); - const points = []; - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) - ]); + // Algorithm as described in https://www.w3.org/TR/SVG/implnote.html + // Code adapted from nsSVGPathDataParser.cpp in Mozilla + // https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 + class RoughArcConverter { + constructor(from, to, radii, angle, largeArcFlag, sweepFlag) { + this._segIndex = 0; + this._numSegs = 0; + this._rx = 0; + this._ry = 0; + this._sinPhi = 0; + this._cosPhi = 0; + this._C = [0, 0]; + this._theta = 0; + this._delta = 0; + this._T = 0; + this._from = from; + if (from[0] === to[0] && from[1] === to[1]) { + return; + } + const radPerDeg = Math.PI / 180; + this._rx = Math.abs(radii[0]); + this._ry = Math.abs(radii[1]); + this._sinPhi = Math.sin(angle * radPerDeg); + this._cosPhi = Math.cos(angle * radPerDeg); + const x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; + const y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; + let root = 0; + const numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; + if (numerator < 0) { + const s = Math.sqrt(1 - (numerator / (this._rx * this._rx * this._ry * this._ry))); + this._rx = this._rx * s; + this._ry = this._ry * s; + root = 0; + } + else { + root = (largeArcFlag === sweepFlag ? -1.0 : 1.0) * + Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); + } + const cxdash = root * this._rx * y1dash / this._ry; + const cydash = -root * this._ry * x1dash / this._rx; + this._C = [0, 0]; + this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; + this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; + this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); + let dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); + if ((!sweepFlag) && (dtheta > 0)) { + dtheta -= 2 * Math.PI; + } + else if (sweepFlag && (dtheta < 0)) { + dtheta += 2 * Math.PI; + } + this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); + this._delta = dtheta / this._numSegs; + this._T = (8 / 3) * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); + } + getNextSegment() { + if (this._segIndex === this._numSegs) { + return null; + } + const cosTheta1 = Math.cos(this._theta); + const sinTheta1 = Math.sin(this._theta); + const theta2 = this._theta + this._delta; + const cosTheta2 = Math.cos(theta2); + const sinTheta2 = Math.sin(theta2); + const to = [ + this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], + this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1] + ]; + const cp1 = [ + this._from[0] + this._T * (-this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), + this._from[1] + this._T * (-this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1) + ]; + const cp2 = [ + to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), + to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2) + ]; + this._theta = theta2; + this._from = [to[0], to[1]]; + this._segIndex++; + return { + cp1: cp1, + cp2: cp2, + to: to + }; + } + calculateVectorAngle(ux, uy, vx, vy) { + const ta = Math.atan2(uy, ux); + const tb = Math.atan2(vy, vx); + if (tb >= ta) + return tb - ta; + return 2 * Math.PI - (ta - tb); + } } - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) - ]); - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), - this._getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) - ]); - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) - ]); - return this._curve(points, null, o); - } - - _curveWithOffset(points, offset, o) { - const ps = []; - ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), - ]); - ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), - ]); - for (let i = 1; i < points.length; i++) { - ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), - ]); - if (i === (points.length - 1)) { - ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), - ]); - } + class PathFitter { + constructor(sets, closed) { + this.sets = sets; + this.closed = closed; + } + fit(simplification) { + const outSets = []; + for (const set of this.sets) { + const length = set.length; + let estLength = Math.floor(simplification * length); + if (estLength < 5) { + if (length <= 5) { + continue; + } + estLength = 5; + } + outSets.push(this.reduce(set, estLength)); + } + let d = ''; + for (const set of outSets) { + for (let i = 0; i < set.length; i++) { + const point = set[i]; + if (i === 0) { + d += 'M' + point[0] + ',' + point[1]; + } + else { + d += 'L' + point[0] + ',' + point[1]; + } + } + if (this.closed) { + d += 'z '; + } + } + return d; + } + distance(p1, p2) { + return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); + } + reduce(set, count) { + if (set.length <= count) { + return set; + } + const points = set.slice(0); + while (points.length > count) { + let minArea = -1; + let minIndex = -1; + for (let i = 1; i < (points.length - 1); i++) { + const a = this.distance(points[i - 1], points[i]); + const b = this.distance(points[i], points[i + 1]); + const c = this.distance(points[i - 1], points[i + 1]); + const s = (a + b + c) / 2.0; + const area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); + if ((minArea < 0) || (area < minArea)) { + minArea = area; + minIndex = i; + } + } + if (minIndex > 0) { + points.splice(minIndex, 1); + } + else { + break; + } + } + return points; + } } - return this._curve(ps, null, o); - } - _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { - const radOffset = strt + this._getOffset(-0.1, 0.1, o); - const points = []; - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle <= stp; angle = angle + increment) { - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) - ]); + class Segment { + constructor(p1, p2) { + this.xi = Number.MAX_VALUE; + this.yi = Number.MAX_VALUE; + this.px1 = p1[0]; + this.py1 = p1[1]; + this.px2 = p2[0]; + this.py2 = p2[1]; + this.a = this.py2 - this.py1; + this.b = this.px1 - this.px2; + this.c = this.px2 * this.py1 - this.px1 * this.py2; + this._undefined = ((this.a === 0) && (this.b === 0) && (this.c === 0)); + } + isUndefined() { + return this._undefined; + } + intersects(otherSegment) { + if (this.isUndefined() || otherSegment.isUndefined()) { + return false; + } + let grad1 = Number.MAX_VALUE; + let grad2 = Number.MAX_VALUE; + let int1 = 0, int2 = 0; + const a = this.a, b = this.b, c = this.c; + if (Math.abs(b) > 0.00001) { + grad1 = -a / b; + int1 = -c / b; + } + if (Math.abs(otherSegment.b) > 0.00001) { + grad2 = -otherSegment.a / otherSegment.b; + int2 = -otherSegment.c / otherSegment.b; + } + if (grad1 === Number.MAX_VALUE) { + if (grad2 === Number.MAX_VALUE) { + if ((-c / a) !== (-otherSegment.c / otherSegment.a)) { + return false; + } + if ((this.py1 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.py2 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py2 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = this.px1; + this.yi = (grad2 * this.xi + int2); + if (((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) || ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001)) { + return false; + } + if (Math.abs(otherSegment.a) < 0.00001) { + if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad2 === Number.MAX_VALUE) { + this.xi = otherSegment.px1; + this.yi = grad1 * this.xi + int1; + if (((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) || ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001)) { + return false; + } + if (Math.abs(a) < 0.00001) { + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad1 === grad2) { + if (int1 !== int2) { + return false; + } + if ((this.px1 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.px2 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px2 <= Math.max(otherSegment.px1, otherSegment.px2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = ((int2 - int1) / (grad1 - grad2)); + this.yi = (grad1 * this.xi + int1); + if (((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) || ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001)) { + return false; + } + return true; + } } - 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 this._curve(points, null, o); - } - _getIntersectingLines(lineCoords, xCoords, yCoords) { - let intersections = []; - var s1 = new RoughSegment(lineCoords[0], lineCoords[1], lineCoords[2], lineCoords[3]); - for (var i = 0; i < xCoords.length; i++) { - let s2 = new RoughSegment(xCoords[i], yCoords[i], xCoords[(i + 1) % xCoords.length], yCoords[(i + 1) % xCoords.length]); - if (s1.compare(s2) == RoughSegmentRelation().INTERSECTS) { - intersections.push([s1.xi, s1.yi]); - } + class HachureIterator { + constructor(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { + this.deltaX = 0; + this.hGap = 0; + this.top = top; + this.bottom = bottom; + this.left = left; + this.right = right; + this.gap = gap; + this.sinAngle = sinAngle; + this.tanAngle = tanAngle; + if (Math.abs(sinAngle) < 0.0001) { + this.pos = left + gap; + } + else if (Math.abs(sinAngle) > 0.9999) { + this.pos = top + gap; + } + else { + this.deltaX = (bottom - top) * Math.abs(tanAngle); + this.pos = left - Math.abs(this.deltaX); + this.hGap = Math.abs(gap / cosAngle); + this.sLeft = new Segment([left, bottom], [left, top]); + this.sRight = new Segment([right, bottom], [right, top]); + } + } + nextLine() { + if (Math.abs(this.sinAngle) < 0.0001) { + if (this.pos < this.right) { + const line = [this.pos, this.top, this.pos, this.bottom]; + this.pos += this.gap; + return line; + } + } + else if (Math.abs(this.sinAngle) > 0.9999) { + if (this.pos < this.bottom) { + const line = [this.left, this.pos, this.right, this.pos]; + this.pos += this.gap; + return line; + } + } + else { + let xLower = this.pos - this.deltaX / 2; + let xUpper = this.pos + this.deltaX / 2; + let yLower = this.bottom; + let yUpper = this.top; + if (this.pos < (this.right + this.deltaX)) { + while (((xLower < this.left) && (xUpper < this.left)) || ((xLower > this.right) && (xUpper > this.right))) { + this.pos += this.hGap; + xLower = this.pos - this.deltaX / 2; + xUpper = this.pos + this.deltaX / 2; + if (this.pos > (this.right + this.deltaX)) { + return null; + } + } + const s = new Segment([xLower, yLower], [xUpper, yUpper]); + if (this.sLeft && s.intersects(this.sLeft)) { + xLower = s.xi; + yLower = s.yi; + } + if (this.sRight && s.intersects(this.sRight)) { + xUpper = s.xi; + yUpper = s.yi; + } + if (this.tanAngle > 0) { + xLower = this.right - (xLower - this.left); + xUpper = this.right - (xUpper - this.left); + } + const line = [xLower, yLower, xUpper, yUpper]; + this.pos += this.hGap; + return line; + } + } + return null; + } } - return intersections; - } -} -self._roughScript = self.document && self.document.currentScript && self.document.currentScript.src; - -class RoughGenerator { - constructor(config, canvas) { - this.config = config || {}; - this.canvas = canvas; - this.defaultOptions = { - maxRandomnessOffset: 2, - roughness: 1, - bowing: 1, - stroke: '#000', - strokeWidth: 1, - curveTightness: 0, - curveStepCount: 9, - fill: null, - fillStyle: 'hachure', - fillWeight: -1, - hachureAngle: -41, - hachureGap: -1 - }; - if (this.config.options) { - this.defaultOptions = this._options(this.config.options); + class HachureFiller { + constructor(renderer) { + this.renderer = renderer; + } + fillPolygon(points, o) { + return this._fillPolygon(points, o); + } + fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o); + } + _fillPolygon(points, o, connectEnds = false) { + let ops = []; + if (points && points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const cosAngle = Math.cos(hachureAngle); + const sinAngle = Math.sin(hachureAngle); + const tanAngle = Math.tan(hachureAngle); + const it = new HachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); + let rect; + let prevPoint = null; + while ((rect = it.nextLine()) != null) { + const lines = this.getIntersectingLines(rect, points); + for (let i = 0; i < lines.length; i++) { + if (i < (lines.length - 1)) { + const p1 = lines[i]; + const p2 = lines[i + 1]; + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + } + } + } + return { type: 'fillSketch', ops }; + } + _fillEllipse(cx, cy, width, height, o, connectEnds = false) { + let ops = []; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.renderer.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.renderer.getOffset(-ry * 0.05, ry * 0.05, o); + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap <= 0) { + gap = o.strokeWidth * 4; + } + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const tanAngle = Math.tan(hachureAngle); + const aspectRatio = ry / rx; + const hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); + const sinAnglePrime = aspectRatio * tanAngle / hyp; + const cosAnglePrime = 1 / hyp; + const gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); + let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); + let prevPoint = null; + for (let xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { + halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); + const p1 = this.affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + const p2 = this.affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + return { type: 'fillSketch', ops }; + } + getIntersectingLines(line, points) { + const intersections = []; + const s1 = new Segment([line[0], line[1]], [line[2], line[3]]); + for (let i = 0; i < points.length; i++) { + const s2 = new Segment(points[i], points[(i + 1) % points.length]); + if (s1.intersects(s2)) { + intersections.push([s1.xi, s1.yi]); + } + } + return intersections; + } + affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { + const A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; + const B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; + const C = cosAnglePrime; + const D = sinAnglePrime; + const E = -R * sinAnglePrime; + const F = R * cosAnglePrime; + return [ + A + C * x + D * y, + B + E * x + F * y + ]; + } } - } - - _options(options) { - return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; - } - - _drawable(shape, sets, options) { - return { shape, sets: sets || [], options: options || this.defaultOptions }; - } - get lib() { - if (!this._renderer) { - if (self && self.workly && this.config.async && (!this.config.noWorker)) { - const tos = Function.prototype.toString; - const worklySource = this.config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; - const rendererSource = this.config.roughURL || self._roughScript; - if (rendererSource && worklySource) { - let code = `importScripts('${worklySource}', '${rendererSource}');\nworkly.expose(self.rough.createRenderer());`; - let ourl = URL.createObjectURL(new Blob([code])); - this._renderer = workly.proxy(ourl); - } else { - this._renderer = new RoughRenderer(); - } - } else { - this._renderer = new RoughRenderer(); - } + class ZigZagFiller extends HachureFiller { + fillPolygon(points, o) { + return this._fillPolygon(points, o, true); + } + fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o, true); + } } - return this._renderer; - } - - line(x1, y1, x2, y2, options) { - const o = this._options(options); - return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); - } - rectangle(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - const xc = [x, x + width, x + width, x]; - const yc = [y, y, y + height, y + height]; - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(this.lib.hachureFillShape(xc, yc, o)); - } + 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; + } + fillEllipse(cx, cy, width, height, o) { + const set = this._fillEllipse(cx, cy, width, height, o); + const o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + const set2 = this._fillEllipse(cx, cy, width, height, o2); + set.ops = set.ops.concat(set2.ops); + return set; + } } - paths.push(this.lib.rectangle(x, y, width, height, o)); - return this._drawable('rectangle', paths, o); - } - ellipse(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - const shape = this.lib.ellipse(x, y, width, height, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(this.lib.hachureFillEllipse(x, y, width, height, o)); - } + const fillers = {}; + function getFiller(renderer, o) { + let fillerName = o.fillStyle || 'hachure'; + if (!fillers[fillerName]) { + switch (fillerName) { + case 'zigzag': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagFiller(renderer); + } + break; + case 'cross-hatch': + if (!fillers[fillerName]) { + fillers[fillerName] = new HatchFiller(renderer); + } + break; + case 'hachure': + default: + fillerName = 'hachure'; + if (!fillers[fillerName]) { + fillers[fillerName] = new HachureFiller(renderer); + } + break; + } + } + return fillers[fillerName]; } - paths.push(this.lib.ellipse(x, y, width, height, o)); - return this._drawable('ellipse', paths, o); - } - - circle(x, y, diameter, options) { - let ret = this.ellipse(x, y, diameter, diameter, options); - ret.shape = 'circle'; - return ret; - } - - linearPath(points, options) { - const o = this._options(options); - return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); - } - polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - let xc = [], yc = []; - for (let p of points) { - xc.push(p[0]); - yc.push(p[1]); - } - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(this.lib.hachureFillShape(xc, yc, o)); - } + class RoughRenderer { + line(x1, y1, x2, y2, o) { + const ops = this.doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops }; + } + linearPath(points, close, o) { + const len = (points || []).length; + if (len > 2) { + let ops = []; + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this.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 this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + polygon(points, o) { + return this.linearPath(points, true, o); + } + rectangle(x, y, width, height, o) { + const points = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return this.polygon(points, o); + } + curve(points, o) { + const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + ellipse(x, y, width, height, o) { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); + const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + 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 += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, 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 o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } + else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; + } + svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; + } + solidFillPolygon(points, o) { + const ops = []; + if (PointerEvent.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; + } + patternFillPolygon(points, o) { + const filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + patternFillEllipse(cx, cy, width, height, o) { + const filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + 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 += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, 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 this.patternFillPolygon(points, o); + } + /// + getOffset(min, max, ops) { + return ops.roughness * ((Math.random() * (max - min)) + min); + } + doubleLine(x1, y1, x2, y2, o) { + const o1 = this._line(x1, y1, x2, y2, o, true, false); + const o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + _line(x1, y1, x2, y2, o, move, overlay) { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); + const ops = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-halfOffset, halfOffset, o), + y1 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-offset, offset, o), + y1 + this.getOffset(-offset, offset, o) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + x2 + this.getOffset(-halfOffset, halfOffset, o), + y2 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + x2 + this.getOffset(-offset, offset, o), + y2 + this.getOffset(-offset, offset, o) + ] + }); + } + return ops; + } + _curve(points, closePoint, o) { + const len = points.length; + let 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] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, 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 = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return this._curve(points, null, o); + } + _curveWithOffset(points, offset, o) { + const ps = []; + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + } + } + return this._curve(ps, null, o); + } + _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + const radOffset = strt + this.getOffset(-0.1, 0.1, o); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, 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 this._curve(points, null, o); + } + _bezierTo(x1, y1, x2, y2, x, y, path, o) { + const ops = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } + else { + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), + x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + _processSegment(path, seg, prevSeg, o) { + let ops = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } } - paths.push(this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - arc(x, y, width, height, start, stop, closed, options) { - const o = this._options(options); - const paths = []; - if (closed && o.fill) { - if (o.fillStyle === 'solid') { - let shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(this.lib.hachureFillArc(x, y, width, height, start, stop, o)); - } + const hasSelf = typeof self !== 'undefined'; + class RoughGenerator { + constructor(config, surface) { + this.defaultOptions = { + maxRandomnessOffset: 2, + roughness: 1, + bowing: 1, + stroke: '#000', + strokeWidth: 1, + curveTightness: 0, + curveStepCount: 9, + fill: null, + fillStyle: 'hachure', + fillWeight: -1, + hachureAngle: -41, + hachureGap: -1 + }; + this.config = config || {}; + this.surface = surface; + if (this.config.options) { + this.defaultOptions = this._options(this.config.options); + } + } + _options(options) { + return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; + } + _drawable(shape, sets, options) { + return { shape, sets: sets || [], options: options || this.defaultOptions }; + } + get lib() { + if (!this.renderer) { + this.renderer = new RoughRenderer(); + } + return this.renderer; + } + getCanvasSize() { + const val = (w) => { + if (w && typeof w === 'object') { + if (w.baseVal && w.baseVal.value) { + return w.baseVal.value; + } + } + return w || 100; + }; + if (this.surface) { + return [val(this.surface.width), val(this.surface.height)]; + } + return [100, 100]; + } + computePathSize(d) { + let size = [0, 0]; + if (hasSelf && self.document) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = self.document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + self.document.body.appendChild(svg); + const bb = pathNode.getBBox(); + if (bb) { + size[0] = bb.width || 0; + size[1] = bb.height || 0; + } + self.document.body.removeChild(svg); + } + catch (err) { } + } + const canvasSize = this.getCanvasSize(); + if (!(size[0] * size[1])) { + size = canvasSize; + } + size[0] = Math.min(size[0], canvasSize[0]); + size[1] = Math.min(size[1], canvasSize[1]); + return size; + } + line(x1, y1, x2, y2, options) { + const o = this._options(options); + return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + } + rectangle(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + ellipse(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(this.lib.ellipse(x, y, width, height, o)); + return this._drawable('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._options(options); + return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + } + polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + arc(x, y, width, height, start, stop, closed = false, options) { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + curve(points, options) { + const o = this._options(options); + return this._drawable('curve', [this.lib.curve(points, o)], o); + } + path(d, options) { + const o = this._options(options); + const paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } + else { + const size = this.computePathSize(d); + const points = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } + 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: 'none' + }; + break; + case 'fillPath': + path = { + d: this.opsToPath(drawing), + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'fillSketch': + path = this.fillSketch(drawing, o); + break; + case 'path2Dfill': + path = { + d: drawing.path || '', + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'path2Dpattern': { + const size = drawing.size; + const pattern = { + x: 0, y: 0, width: 1, height: 1, + viewBox: `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`, + patternUnits: 'objectBoundingBox', + path: this.fillSketch(drawing, o) + }; + path = { + d: drawing.path, + stroke: 'none', + strokeWidth: 0, + pattern: pattern + }; + 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 || 'none', + strokeWidth: fweight, + fill: 'none' + }; + } + 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 'qcurveTo': + path += `Q${data[0]} ${data[1]}, ${data[2]} ${data[3]} `; + break; + case 'lineTo': + path += `L${data[0]} ${data[1]} `; + break; + } + } + return path.trim(); + } } - paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); - return this._drawable('arc', paths, o); - } - curve(points, options) { - const o = this._options(options); - return this._drawable('curve', [this.lib.curve(points, o)], o); - } - - path(d, options) { - const o = this._options(options); - const paths = []; - if (!d) { - return this._drawable('path', paths, o); - } - if (o.fill) { - if (o.fillStyle === 'solid') { - let shape = { type: 'path2Dfill', path: d }; - paths.push(shape); - } else { - const size = this._computePathSize(d); - let xc = [0, size[0], size[0], 0]; - let yc = [0, 0, size[1], size[1]]; - let shape = this.lib.hachureFillShape(xc, yc, o); - shape.type = 'path2Dpattern'; - shape.size = size; - shape.path = d; - paths.push(shape); - } + const hasDocument = typeof document !== 'undefined'; + class RoughCanvas { + constructor(canvas, config) { + this.canvas = canvas; + this.ctx = this.canvas.getContext('2d'); + this.gen = new RoughGenerator(config || null, this.canvas); + } + get generator() { + return this.gen; + } + static createRenderer() { + return new RoughRenderer(); + } + 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; + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const ctx = this.ctx; + for (const drawing of sets) { + switch (drawing.type) { + case 'path': + ctx.save(); + ctx.strokeStyle = o.stroke; + ctx.lineWidth = o.strokeWidth; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillPath': + ctx.save(); + ctx.fillStyle = o.fill || ''; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillSketch': + this.fillSketch(ctx, drawing, o); + break; + case 'path2Dfill': { + this.ctx.save(); + this.ctx.fillStyle = o.fill || ''; + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + break; + } + case 'path2Dpattern': { + if (hasDocument) { + const size = drawing.size; + const hcanvas = document.createElement('canvas'); + const hcontext = hcanvas.getContext('2d'); + const bbox = this.computeBBox(drawing.path); + if (bbox && (bbox.width || bbox.height)) { + hcanvas.width = this.canvas.width; + hcanvas.height = this.canvas.height; + hcontext.translate(bbox.x || 0, bbox.y || 0); + } + else { + hcanvas.width = size[0]; + hcanvas.height = size[1]; + } + this.fillSketch(hcontext, drawing, o); + this.ctx.save(); + this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + } + break; + } + } + } + } + computeBBox(d) { + if (hasDocument) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + document.body.appendChild(svg); + const bbox = pathNode.getBBox(); + document.body.removeChild(svg); + return bbox; + } + catch (err) { } + } + return null; + } + fillSketch(ctx, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + ctx.save(); + ctx.strokeStyle = o.fill || ''; + ctx.lineWidth = fweight; + this._drawToContext(ctx, drawing); + ctx.restore(); + } + _drawToContext(ctx, drawing) { + 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 'qcurveTo': + ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); + break; + case 'lineTo': + ctx.lineTo(data[0], data[1]); + break; + } + } + if (drawing.type === 'fillPath') { + ctx.fill(); + } + else { + ctx.stroke(); + } + } } - paths.push(this.lib.svgPath(d, o)); - return this._drawable('path', paths, o); - } - 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: 'none' - }; - break; - case 'fillPath': - path = { - d: this.opsToPath(drawing), - stroke: 'none', - strokeWidth: 0, - fill: o.fill - }; - break; - case 'fillSketch': - path = this._fillSketch(drawing, o); - break; - case 'path2Dfill': - path = { - d: drawing.path, - stroke: 'none', - strokeWidth: 0, - fill: o.fill - }; - break; - case 'path2Dpattern': { - const size = drawing.size; - const pattern = { - x: 0, y: 0, width: 1, height: 1, - viewBox: `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`, - patternUnits: 'objectBoundingBox', - path: this._fillSketch(drawing, o) - }; - path = { - d: drawing.path, - stroke: 'none', - strokeWidth: 0, - pattern: pattern - }; - break; - } - } - if (path) { - paths.push(path); - } + class RoughGeneratorAsync extends RoughGenerator { + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const o = this._options(options); + return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(await this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(await this.lib.ellipse(x, y, width, height, o)); + return this._drawable('ellipse', paths, o); + } + // @ts-ignore + async circle(x, y, diameter, options) { + const ret = await this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + // @ts-ignore + async linearPath(points, options) { + const o = this._options(options); + return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); + } + // @ts-ignore + async polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(await this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(await this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + // @ts-ignore + async curve(points, options) { + const o = this._options(options); + return this._drawable('curve', [await this.lib.curve(points, o)], o); + } + // @ts-ignore + async path(d, options) { + const o = this._options(options); + const paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } + else { + const size = this.computePathSize(d); + const points = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(await this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } } - return paths; - } - _fillSketch(drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; + class RoughCanvasAsync extends RoughCanvas { + constructor(canvas, config) { + super(canvas, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.canvas); + } + // @ts-ignore + get generator() { + return this.genAsync; + } + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const d = await this.genAsync.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const d = await this.genAsync.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + async circle(x, y, diameter, options) { + const d = await this.genAsync.circle(x, y, diameter, options); + this.draw(d); + return d; + } + // @ts-ignore + async linearPath(points, options) { + const d = await this.genAsync.linearPath(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async polygon(points, options) { + const d = await this.genAsync.polygon(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + // @ts-ignore + async curve(points, options) { + const d = await this.genAsync.curve(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async path(d, options) { + const drawing = await this.genAsync.path(d, options); + this.draw(drawing); + return drawing; + } } - return { - d: this.opsToPath(drawing), - stroke: o.fill, - strokeWidth: fweight, - fill: 'none' - }; - } - opsToPath(drawing) { - let path = ''; - for (let 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 'qcurveTo': - path += `Q${data[0]} ${data[1]}, ${data[2]} ${data[3]} `; - break; - case 'lineTo': - path += `L${data[0]} ${data[1]} `; - break; - } + const hasDocument$1 = typeof document !== 'undefined'; + class RoughSVG { + constructor(svg, config) { + this.svg = svg; + this.gen = new RoughGenerator(config || null, this.svg); + } + get generator() { + return this.gen; + } + static createRenderer() { + return new RoughRenderer(); + } + get defs() { + if (hasDocument$1) { + if (!this._defs) { + const doc = this.svg.ownerDocument || document; + const dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); + if (this.svg.firstChild) { + this.svg.insertBefore(dnode, this.svg.firstChild); + } + else { + this.svg.appendChild(dnode); + } + this._defs = dnode; + } + } + return this._defs || null; + } + 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); + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const doc = this.svg.ownerDocument || (hasDocument$1 && document); + const g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); + for (const drawing of sets) { + let path = null; + switch (drawing.type) { + case 'path': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.stroke; + path.style.strokeWidth = o.strokeWidth + ''; + path.style.fill = 'none'; + break; + } + case 'fillPath': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'fillSketch': { + path = this.fillSketch(doc, drawing, o); + break; + } + case 'path2Dfill': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'path2Dpattern': { + const size = drawing.size; + const pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); + const id = `rough-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999))}`; + pattern.setAttribute('id', id); + pattern.setAttribute('x', '0'); + pattern.setAttribute('y', '0'); + pattern.setAttribute('width', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('viewBox', `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`); + pattern.setAttribute('patternUnits', 'objectBoundingBox'); + const patternPath = this.fillSketch(doc, drawing, o); + pattern.appendChild(patternPath); + this.defs.appendChild(pattern); + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = `url(#${id})`; + break; + } + } + if (path) { + g.appendChild(path); + } + } + return g; + } + opsToPath(drawing) { + return this.gen.opsToPath(drawing); + } + fillSketch(doc, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.fill; + path.style.strokeWidth = fweight + ''; + path.style.fill = 'none'; + return path; + } } - return path.trim(); - } - _computePathSize(d) { - let size = [0, 0]; - if (self.document) { - try { - const ns = "http://www.w3.org/2000/svg"; - let svg = self.document.createElementNS(ns, "svg"); - svg.setAttribute("width", "0"); - svg.setAttribute("height", "0"); - let pathNode = self.document.createElementNS(ns, "path"); - pathNode.setAttribute('d', d); - svg.appendChild(pathNode); - self.document.body.appendChild(svg); - let bb = pathNode.getBBox(); - if (bb) { - size[0] = bb.width || 0; - size[1] = bb.height || 0; - } - self.document.body.removeChild(svg); - } catch (err) { } - } - const canvasSize = this._canvasSize(); - if (!(size[0] * size[1])) { - size = canvasSize; + class RoughSVGAsync extends RoughSVG { + constructor(svg, config) { + super(svg, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.svg); + } + // @ts-ignore + get generator() { + return this.genAsync; + } + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + return this.draw(d); + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const d = await this.genAsync.rectangle(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const d = await this.genAsync.ellipse(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + async circle(x, y, diameter, options) { + const d = await this.genAsync.circle(x, y, diameter, options); + return this.draw(d); + } + // @ts-ignore + async linearPath(points, options) { + const d = await this.genAsync.linearPath(points, options); + return this.draw(d); + } + // @ts-ignore + async polygon(points, options) { + const d = await this.genAsync.polygon(points, options); + return this.draw(d); + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + // @ts-ignore + async curve(points, options) { + const d = await this.genAsync.curve(points, options); + return this.draw(d); + } + // @ts-ignore + async path(d, options) { + const drawing = await this.genAsync.path(d, options); + return this.draw(drawing); + } } - size[0] = Math.min(size[0], canvasSize[0]); - size[1] = Math.min(size[1], canvasSize[1]); - return size; - } - _canvasSize() { - const val = w => { - if (w) { - if (typeof w === 'object') { - if (w.baseVal && w.baseVal.value) { - return w.baseVal.value; - } - } - } - return w || 100; + var rough = { + canvas(canvas, config) { + if (config && config.async) { + return new RoughCanvasAsync(canvas, config); + } + return new RoughCanvas(canvas, config); + }, + svg(svg, config) { + if (config && config.async) { + return new RoughSVGAsync(svg, config); + } + return new RoughSVG(svg, config); + }, + createRenderer() { + return RoughCanvas.createRenderer(); + }, + generator(config, surface) { + if (config && config.async) { + return new RoughGeneratorAsync(config, surface); + } + return new RoughGenerator(config, surface); + } }; - return this.canvas ? [val(this.canvas.width), val(this.canvas.height)] : [100, 100]; - } -} - -class RoughGeneratorAsync extends RoughGenerator { - async line(x1, y1, x2, y2, options) { - const o = this._options(options); - return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); - } - - async rectangle(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - const xc = [x, x + width, x + width, x]; - const yc = [y, y, y + height, y + height]; - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(await this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(await this.lib.rectangle(x, y, width, height, o)); - return this._drawable('rectangle', paths, o); - } - - async ellipse(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - const shape = await this.lib.ellipse(x, y, width, height, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(await this.lib.hachureFillEllipse(x, y, width, height, o)); - } - } - paths.push(await this.lib.ellipse(x, y, width, height, o)); - return this._drawable('ellipse', paths, o); - } - - async circle(x, y, diameter, options) { - let ret = await this.ellipse(x, y, diameter, diameter, options); - ret.shape = 'circle'; - return ret; - } - - async linearPath(points, options) { - const o = this._options(options); - return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); - } - - async polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - let xc = [], yc = []; - for (let p of points) { - xc.push(p[0]); - yc.push(p[1]); - } - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(await this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(await this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - - async arc(x, y, width, height, start, stop, closed, options) { - const o = this._options(options); - const paths = []; - if (closed && o.fill) { - if (o.fillStyle === 'solid') { - let shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(await this.lib.hachureFillArc(x, y, width, height, start, stop, o)); - } - } - paths.push(await this.lib.arc(x, y, width, height, start, stop, closed, true, o)); - return this._drawable('arc', paths, o); - } - - async curve(points, options) { - const o = this._options(options); - return this._drawable('curve', [await this.lib.curve(points, o)], o); - } - - async path(d, options) { - const o = this._options(options); - const paths = []; - if (!d) { - return this._drawable('path', paths, o); - } - if (o.fill) { - if (o.fillStyle === 'solid') { - let shape = { type: 'path2Dfill', path: d }; - paths.push(shape); - } else { - const size = this._computePathSize(d); - let xc = [0, size[0], size[0], 0]; - let yc = [0, 0, size[1], size[1]]; - let shape = await this.lib.hachureFillShape(xc, yc, o); - shape.type = 'path2Dpattern'; - shape.size = size; - shape.path = d; - paths.push(shape); - } - } - paths.push(await this.lib.svgPath(d, o)); - return this._drawable('path', paths, o); - } -} - -class RoughCanvas { - constructor(canvas, config) { - this.canvas = canvas; - this.ctx = this.canvas.getContext("2d"); - this._init(config); - } - - _init(config) { - this.gen = new RoughGenerator(config, this.canvas); - } - - get generator() { - return this.gen; - } - - static createRenderer() { - return new RoughRenderer(); - } - - line(x1, y1, x2, y2, options) { - let d = this.gen.line(x1, y1, x2, y2, options); - this.draw(d); - return d; - } - - rectangle(x, y, width, height, options) { - let d = this.gen.rectangle(x, y, width, height, options); - this.draw(d); - return d; - } - - ellipse(x, y, width, height, options) { - let d = this.gen.ellipse(x, y, width, height, options); - this.draw(d); - return d; - } - - circle(x, y, diameter, options) { - let d = this.gen.circle(x, y, diameter, options); - this.draw(d); - return d; - } - - linearPath(points, options) { - let d = this.gen.linearPath(points, options); - this.draw(d); - return d; - } - - polygon(points, options) { - let d = this.gen.polygon(points, options); - this.draw(d); - return d; - } - - arc(x, y, width, height, start, stop, closed, options) { - let d = this.gen.arc(x, y, width, height, start, stop, closed, options); - this.draw(d); - return d; - } - - curve(points, options) { - let d = this.gen.curve(points, options); - this.draw(d); - return d; - } - - path(d, options) { - let drawing = this.gen.path(d, options); - this.draw(drawing); - return drawing; - } - - draw(drawable) { - let sets = drawable.sets || []; - let o = drawable.options || this.gen.defaultOptions; - let ctx = this.ctx; - for (let drawing of sets) { - switch (drawing.type) { - case 'path': - ctx.save(); - ctx.strokeStyle = o.stroke; - ctx.lineWidth = o.strokeWidth; - this._drawToContext(ctx, drawing); - ctx.restore(); - break; - case 'fillPath': - ctx.save(); - ctx.fillStyle = o.fill; - this._drawToContext(ctx, drawing, o); - ctx.restore(); - break; - case 'fillSketch': - this._fillSketch(ctx, drawing, o); - break; - case 'path2Dfill': { - this.ctx.save(); - this.ctx.fillStyle = o.fill; - let p2d = new Path2D(drawing.path); - this.ctx.fill(p2d); - this.ctx.restore(); - break; - } - case 'path2Dpattern': { - let size = drawing.size; - const hcanvas = document.createElement('canvas'); - const hcontext = hcanvas.getContext("2d"); - let bbox = this._computeBBox(drawing.path); - if (bbox && (bbox.width || bbox.height)) { - hcanvas.width = this.canvas.width; - hcanvas.height = this.canvas.height; - hcontext.translate(bbox.x || 0, bbox.y || 0); - } else { - hcanvas.width = size[0]; - hcanvas.height = size[1]; - } - this._fillSketch(hcontext, drawing, o); - this.ctx.save(); - this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); - let p2d = new Path2D(drawing.path); - this.ctx.fill(p2d); - this.ctx.restore(); - break; - } - } - } - } - - _computeBBox(d) { - if (self.document) { - try { - const ns = "http://www.w3.org/2000/svg"; - let svg = self.document.createElementNS(ns, "svg"); - svg.setAttribute("width", "0"); - svg.setAttribute("height", "0"); - let pathNode = self.document.createElementNS(ns, "path"); - pathNode.setAttribute('d', d); - svg.appendChild(pathNode); - self.document.body.appendChild(svg); - let bbox = pathNode.getBBox(); - self.document.body.removeChild(svg); - return bbox; - } catch (err) { } - } - return null; - } - - _fillSketch(ctx, drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - ctx.save(); - ctx.strokeStyle = o.fill; - ctx.lineWidth = fweight; - this._drawToContext(ctx, drawing); - ctx.restore(); - } - - _drawToContext(ctx, drawing) { - ctx.beginPath(); - for (let 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 'qcurveTo': - ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); - break; - case 'lineTo': - ctx.lineTo(data[0], data[1]); - break; - } - } - if (drawing.type === 'fillPath') { - ctx.fill(); - } else { - ctx.stroke(); - } - } -} - -class RoughCanvasAsync extends RoughCanvas { - _init(config) { - this.gen = new RoughGeneratorAsync(config, this.canvas); - } - - async line(x1, y1, x2, y2, options) { - let d = await this.gen.line(x1, y1, x2, y2, options); - this.draw(d); - return d; - } - - async rectangle(x, y, width, height, options) { - let d = await this.gen.rectangle(x, y, width, height, options); - this.draw(d); - return d; - } - - async ellipse(x, y, width, height, options) { - let d = await this.gen.ellipse(x, y, width, height, options); - this.draw(d); - return d; - } - - async circle(x, y, diameter, options) { - let d = await this.gen.circle(x, y, diameter, options); - this.draw(d); - return d; - } - - async linearPath(points, options) { - let d = await this.gen.linearPath(points, options); - this.draw(d); - return d; - } - - async polygon(points, options) { - let d = await this.gen.polygon(points, options); - this.draw(d); - return d; - } - - async arc(x, y, width, height, start, stop, closed, options) { - let d = await this.gen.arc(x, y, width, height, start, stop, closed, options); - this.draw(d); - return d; - } - - async curve(points, options) { - let d = await this.gen.curve(points, options); - this.draw(d); - return d; - } - - async path(d, options) { - let drawing = await this.gen.path(d, options); - this.draw(drawing); - return drawing; - } -} - -class RoughSVG { - constructor(svg, config) { - this.svg = svg; - this._init(config); - } - - _init(config) { - this.gen = new RoughGenerator(config, this.svg); - } - - get generator() { - return this.gen; - } - - get defs() { - if (!this._defs) { - let doc = this.svg.ownerDocument || document; - let dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); - if (this.svg.firstChild) { - this.svg.insertBefore(dnode, this.svg.firstChild); - } else { - this.svg.appendChild(dnode); - } - this._defs = dnode; - } - return this._defs; - } - - line(x1, y1, x2, y2, options) { - let d = this.gen.line(x1, y1, x2, y2, options); - return this.draw(d); - } - - rectangle(x, y, width, height, options) { - let d = this.gen.rectangle(x, y, width, height, options); - return this.draw(d); - } - - ellipse(x, y, width, height, options) { - let d = this.gen.ellipse(x, y, width, height, options); - return this.draw(d); - } - - circle(x, y, diameter, options) { - let d = this.gen.circle(x, y, diameter, options); - return this.draw(d); - } - - linearPath(points, options) { - let d = this.gen.linearPath(points, options); - return this.draw(d); - } - - polygon(points, options) { - let d = this.gen.polygon(points, options); - return this.draw(d); - } - - arc(x, y, width, height, start, stop, closed, options) { - let d = this.gen.arc(x, y, width, height, start, stop, closed, options); - return this.draw(d); - } - - curve(points, options) { - let d = this.gen.curve(points, options); - return this.draw(d); - } - - path(d, options) { - let drawing = this.gen.path(d, options); - return this.draw(drawing); - } - - draw(drawable) { - let sets = drawable.sets || []; - let o = drawable.options || this.gen.defaultOptions; - let doc = this.svg.ownerDocument || document; - let g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); - for (let drawing of sets) { - let path = null; - switch (drawing.type) { - case 'path': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = o.stroke; - path.style.strokeWidth = o.strokeWidth; - path.style.fill = 'none'; - break; - } - case 'fillPath': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = o.fill; - break; - } - case 'fillSketch': { - path = this._fillSketch(doc, drawing, o); - break; - } - case 'path2Dfill': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', drawing.path); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = o.fill; - break; - } - case 'path2Dpattern': { - const size = drawing.size; - const pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); - const id = `rough-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999))}`; - pattern.setAttribute('id', id); - pattern.setAttribute('x', 0); - pattern.setAttribute('y', 0); - pattern.setAttribute('width', 1); - pattern.setAttribute('height', 1); - pattern.setAttribute('height', 1); - pattern.setAttribute('viewBox', `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`); - pattern.setAttribute('patternUnits', 'objectBoundingBox'); - const patternPath = this._fillSketch(doc, drawing, o); - pattern.appendChild(patternPath); - this.defs.appendChild(pattern); - - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', drawing.path); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = `url(#${id})`; - break; - } - } - if (path) { - g.appendChild(path); - } - } - return g; - } - - _fillSketch(doc, drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - let path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = o.fill; - path.style.strokeWidth = fweight; - path.style.fill = 'none'; - return path; - } - - _opsToPath(drawing) { - return this.gen.opsToPath(drawing); - } -} - -class RoughSVGAsync extends RoughSVG { - _init(config) { - this.gen = new RoughGeneratorAsync(config, this.svg); - } - - async line(x1, y1, x2, y2, options) { - let d = await this.gen.line(x1, y1, x2, y2, options); - return this.draw(d); - } - - async rectangle(x, y, width, height, options) { - let d = await this.gen.rectangle(x, y, width, height, options); - return this.draw(d); - } - - async ellipse(x, y, width, height, options) { - let d = await this.gen.ellipse(x, y, width, height, options); - return this.draw(d); - } - - async circle(x, y, diameter, options) { - let d = await this.gen.circle(x, y, diameter, options); - return this.draw(d); - } - - async linearPath(points, options) { - let d = await this.gen.linearPath(points, options); - return this.draw(d); - } - - async polygon(points, options) { - let d = await this.gen.polygon(points, options); - return this.draw(d); - } - - async arc(x, y, width, height, start, stop, closed, options) { - let d = await this.gen.arc(x, y, width, height, start, stop, closed, options); - return this.draw(d); - } - - async curve(points, options) { - let d = await this.gen.curve(points, options); - return this.draw(d); - } - - async path(d, options) { - let drawing = await this.gen.path(d, options); - return this.draw(drawing); - } -} - -var index = { - canvas(canvas, config) { - if (config && config.async) { - return new RoughCanvasAsync(canvas, config); - } - return new RoughCanvas(canvas, config); - }, - svg(svg, config) { - if (config && config.async) { - return new RoughSVGAsync(svg, config); - } - return new RoughSVG(svg, config); - }, - createRenderer() { - return RoughCanvas.createRenderer(); - }, - generator(config, size) { - if (config && config.async) { - return new RoughGeneratorAsync(config, size); - } - return new RoughGenerator(config, size); - } -}; -return index; + return rough; }()); diff --git a/dist/rough.min.js b/dist/rough.min.js index 2c2119a..94163d8 100644 --- a/dist/rough.min.js +++ b/dist/rough.min.js @@ -1 +1 @@ -var rough=function(){"use strict";function a(){return{LEFT:0,RIGHT:1,INTERSECTS:2,AHEAD:3,BEHIND:4,SEPARATE:5,UNDEFINED:6}}var b=Math.round,c=Math.tan,d=Math.pow,e=Math.floor,f=Math.cos,g=Math.sin,h=Math.PI,j=Math.sqrt,k=Math.max,l=Math.min,i=Math.abs,m=Number.MAX_VALUE;class n{constructor(b,c,d,e){this.RoughSegmentRelationConst=a(),this.px1=b,this.py1=c,this.px2=d,this.py2=e,this.xi=m,this.yi=m,this.a=e-c,this.b=b-d,this.c=d*c-b*e,this._undefined=0==this.a&&0==this.b&&0==this.c}isUndefined(){return this._undefined}compare(d){if(this.isUndefined()||d.isUndefined())return this.RoughSegmentRelationConst.UNDEFINED;var e=m,f=m,g=0,h=0,j=this.a,n=this.b,b=this.c;return(1e-5=l(d.py1,d.py2)&&this.py1<=k(d.py1,d.py2)?(this.xi=this.px1,this.yi=this.py1,this.RoughSegmentRelationConst.INTERSECTS):this.py2>=l(d.py1,d.py2)&&this.py2<=k(d.py1,d.py2)?(this.xi=this.px2,this.yi=this.py2,this.RoughSegmentRelationConst.INTERSECTS):this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.SEPARATE:(this.xi=this.px1,this.yi=f*this.xi+h,-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(d.py1-this.yi)*(this.yi-d.py2)?this.RoughSegmentRelationConst.SEPARATE:1e-5>i(d.a)?-1e-5>(d.px1-this.xi)*(this.xi-d.px2)?this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.INTERSECTS:this.RoughSegmentRelationConst.INTERSECTS):f==m?(this.xi=d.px1,this.yi=e*this.xi+g,-1e-5>(d.py1-this.yi)*(this.yi-d.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2)?this.RoughSegmentRelationConst.SEPARATE:1e-5>i(j)?-1e-5>(this.px1-this.xi)*(this.xi-this.px2)?this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.INTERSECTS:this.RoughSegmentRelationConst.INTERSECTS):e==f?g==h?this.px1>=l(d.px1,d.px2)&&this.px1<=k(d.py1,d.py2)?(this.xi=this.px1,this.yi=this.py1,this.RoughSegmentRelationConst.INTERSECTS):this.px2>=l(d.px1,d.px2)&&this.px2<=k(d.px1,d.px2)?(this.xi=this.px2,this.yi=this.py2,this.RoughSegmentRelationConst.INTERSECTS):this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.SEPARATE:(this.xi=(h-g)/(e-f),this.yi=e*this.xi+g,-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(d.px1-this.xi)*(this.xi-d.px2)?this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.INTERSECTS)}getLength(){return this._getLength(this.px1,this.py1,this.px2,this.py2)}_getLength(a,b,c,d){var e=c-a,f=d-b;return j(e*e+f*f)}}class p{constructor(a,b,c,d,e,f,g,h){this.top=a,this.bottom=b,this.left=c,this.right=d,this.gap=e,this.sinAngle=f,this.tanAngle=h,1e-4>i(f)?this.pos=c+e:.9999i(this.sinAngle)){if(this.posthis.right&&c>this.right;)if(this.pos+=this.hGap,b=this.pos-this.deltaX/2,c=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;let f=new n(b,d,c,e);f.compare(this.sLeft)==a().INTERSECTS&&(b=f.xi,d=f.yi),f.compare(this.sRight)==a().INTERSECTS&&(c=f.xi,e=f.yi),0p){let a=j(1-p/(this._rx*this._rx*this._ry*this._ry));this._rx*=a,this._ry*=a,m=0}else m=(e==k?-1:1)*j(p/(this._rx*this._rx*o*o+this._ry*this._ry*n*n));let q=m*this._rx*o/this._ry,r=-m*this._ry*n/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*q-this._sinPhi*r+(a[0]+b[0])/2,this._C[1]=this._sinPhi*q+this._cosPhi*r+(a[1]+b[1])/2,this._theta=this.calculateVectorAngle(1,0,(n-q)/this._rx,(o-r)/this._ry);let s=this.calculateVectorAngle((n-q)/this._rx,(o-r)/this._ry,(-n-q)/this._rx,(-o-r)/this._ry);!k&&0s&&(s+=2*h),this._numSegs=Math.ceil(i(s/(h/2))),this._delta=s/this._numSegs,this._T=8/3*g(this._delta/4)*g(this._delta/4)/g(this._delta/2),this._from=a}getNextSegment(){var a,b,c;if(this._segIndex==this._numSegs)return null;let d=f(this._theta),e=g(this._theta),h=this._theta+this._delta,i=f(h),j=g(h);return c=[this._cosPhi*this._rx*i-this._sinPhi*this._ry*j+this._C[0],this._sinPhi*this._rx*i+this._cosPhi*this._ry*j+this._C[1]],a=[this._from[0]+this._T*(-this._cosPhi*this._rx*e-this._sinPhi*this._ry*d),this._from[1]+this._T*(-this._sinPhi*this._rx*e+this._cosPhi*this._ry*d)],b=[c[0]+this._T*(this._cosPhi*this._rx*j+this._sinPhi*this._ry*i),c[1]+this._T*(this._sinPhi*this._rx*j-this._cosPhi*this._ry*i)],this._theta=h,this._from=[c[0],c[1]],this._segIndex++,{cp1:a,cp2:b,to:c}}calculateVectorAngle(a,b,c,d){var e=Math.atan2;let f=e(b,a),g=e(d,c);return g>=f?g-f:2*h-(f-g)}}class t{constructor(a,b){this.sets=a,this.closed=b}fit(a){let b=[];for(const c of this.sets){let d=c.length,f=e(a*d);if(5>f){if(5>=d)continue;f=5}b.push(this.reduce(c,f))}let c="";for(const d of b){for(let a,b=0;bb;){let e=-1,f=-1;for(let g=1;ge||is;)s+=2*h,t+=2*h;t-s>2*h&&(s=0,t=2*h);let u=2*h/n.curveStepCount,v=l(u/2,(t-s)/2),w=this._arc(v,o,p,q,r,s,t,1,n),x=this._arc(v,o,p,q,r,s,t,1.5,n),y=w.concat(x);return k&&(m?(y=y.concat(this._doubleLine(o,p,o+q*f(s),p+r*g(s),n)),y=y.concat(this._doubleLine(o,p,o+q*f(t),p+r*g(t),n))):(y.push({op:"lineTo",data:[o,p]}),y.push({op:"lineTo",data:[o+q*f(s),p+r*g(s)]}))),{type:"path",ops:y}}hachureFillArc(a,b,c,d,e,j,k){let l=a,m=b,n=i(c/2),o=i(d/2);n+=this._getOffset(.01*-n,.01*n,k),o+=this._getOffset(.01*-o,.01*o,k);let p=e,q=j;for(;0>p;)p+=2*h,q+=2*h;q-p>2*h&&(p=0,q=2*h);let r=(q-p)/k.curveStepCount,s=[],t=[];for(let h=p;h<=q;h+=r)s.push(l+n*f(h)),t.push(m+o*g(h));return s.push(l+n*f(q)),t.push(m+o*g(q)),s.push(l),t.push(m),this.hachureFillShape(s,t,k)}solidFillShape(a,b,c){let d=[];if(a&&b&&a.length&&b.length&&a.length===b.length){let f=c.maxRandomnessOffset||0;const g=a.length;if(2q&&(q=4*d.strokeWidth),q=k(q,.1);const r=i%180*(h/180),s=f(r),t=g(r),u=c(r),v=new p(n-1,o+1,j-1,m+1,q,t,s,u);for(let c;null!=(c=v.getNextLine());){let f=this._getIntersectingLines(c,a,b);for(let a=0;a=n&&(n=4*f.strokeWidth);let o=f.fillWeight;0>o&&(o=f.strokeWidth/2);let p=c(m%180*(h/180)),q=l/k,r=j(q*p*q*p+1),s=q*p/r,t=1/r,u=n/(k*l/j(l*t*(l*t)+k*s*(k*s))/k),v=j(k*k-(a-k+u)*(a-k+u));for(var w=a-k+u;wf;f++)0===f?k.push({op:"move",data:[h.x,h.y]}):k.push({op:"move",data:[h.x+this._getOffset(-l[0],l[0],j),h.y+this._getOffset(-l[0],l[0],j)]}),m=[e+this._getOffset(-l[f],l[f],j),g+this._getOffset(-l[f],l[f],j)],k.push({op:"bcurveTo",data:[a+this._getOffset(-l[f],l[f],j),b+this._getOffset(-l[f],l[f],j),c+this._getOffset(-l[f],l[f],j),d+this._getOffset(-l[f],l[f],j),m[0],m[1]]});return h.setPosition(m[0],m[1]),k}_processSegment(a,b,c,d){let e=[];switch(b.key){case"M":case"m":{let c="m"===b.key;if(2<=b.data.length){let f=+b.data[0],g=+b.data[1];c&&(f+=a.x,g+=a.y);let h=1*(d.maxRandomnessOffset||0);f+=this._getOffset(-h,h,d),g+=this._getOffset(-h,h,d),a.setPosition(f,g),e.push({op:"move",data:[f,g]})}break}case"L":case"l":{let c="l"===b.key;if(2<=b.data.length){let f=+b.data[0],g=+b.data[1];c&&(f+=a.x,g+=a.y),e=e.concat(this._doubleLine(a.x,a.y,f,g,d)),a.setPosition(f,g)}break}case"H":case"h":{const c="h"===b.key;if(b.data.length){let f=+b.data[0];c&&(f+=a.x),e=e.concat(this._doubleLine(a.x,a.y,f,a.y,d)),a.setPosition(f,a.y)}break}case"V":case"v":{const c="v"===b.key;if(b.data.length){let f=+b.data[0];c&&(f+=a.y),e=e.concat(this._doubleLine(a.x,a.y,a.x,f,d)),a.setPosition(a.x,f)}break}case"Z":case"z":{a.first&&(e=e.concat(this._doubleLine(a.x,a.y,a.first[0],a.first[1],d)),a.setPosition(a.first[0],a.first[1]),a.first=null);break}case"C":case"c":{const c="c"===b.key;if(6<=b.data.length){let f=+b.data[0],g=+b.data[1],h=+b.data[2],i=+b.data[3],j=+b.data[4],k=+b.data[5];c&&(f+=a.x,h+=a.x,j+=a.x,g+=a.y,i+=a.y,k+=a.y);let l=this._bezierTo(f,g,h,i,j,k,a,d);e=e.concat(l),a.bezierReflectionPoint=[j+(j-h),k+(k-i)]}break}case"S":case"s":{const f="s"===b.key;if(4<=b.data.length){let h=+b.data[0],i=+b.data[1],j=+b.data[2],k=+b.data[3];f&&(h+=a.x,j+=a.x,i+=a.y,k+=a.y);let l=h,m=i,n=c?c.key:"";var g=null;("c"==n||"C"==n||"s"==n||"S"==n)&&(g=a.bezierReflectionPoint),g&&(l=g[0],m=g[1]);let o=this._bezierTo(l,m,h,i,j,k,a,d);e=e.concat(o),a.bezierReflectionPoint=[j+(j-h),k+(k-i)]}break}case"Q":case"q":{const c="q"===b.key;if(4<=b.data.length){let g=+b.data[0],h=+b.data[1],i=+b.data[2],j=+b.data[3];c&&(g+=a.x,i+=a.x,h+=a.y,j+=a.y);let k=1*(1+.2*d.roughness),l=1.5*(1+.22*d.roughness);e.push({op:"move",data:[a.x+this._getOffset(-k,k,d),a.y+this._getOffset(-k,k,d)]});let m=[i+this._getOffset(-k,k,d),j+this._getOffset(-k,k,d)];e.push({op:"qcurveTo",data:[g+this._getOffset(-k,k,d),h+this._getOffset(-k,k,d),m[0],m[1]]}),e.push({op:"move",data:[a.x+this._getOffset(-l,l,d),a.y+this._getOffset(-l,l,d)]}),m=[i+this._getOffset(-l,l,d),j+this._getOffset(-l,l,d)],e.push({op:"qcurveTo",data:[g+this._getOffset(-l,l,d),h+this._getOffset(-l,l,d),m[0],m[1]]}),a.setPosition(m[0],m[1]),a.quadReflectionPoint=[i+(i-g),j+(j-h)]}break}case"T":case"t":{const h="t"===b.key;if(2<=b.data.length){let i=+b.data[0],j=+b.data[1];h&&(i+=a.x,j+=a.y);let k=i,l=j,m=c?c.key:"";var g=null;("q"==m||"Q"==m||"t"==m||"T"==m)&&(g=a.quadReflectionPoint),g&&(k=g[0],l=g[1]);let n=1*(1+.2*d.roughness),o=1.5*(1+.22*d.roughness);e.push({op:"move",data:[a.x+this._getOffset(-n,n,d),a.y+this._getOffset(-n,n,d)]});let p=[i+this._getOffset(-n,n,d),j+this._getOffset(-n,n,d)];e.push({op:"qcurveTo",data:[k+this._getOffset(-n,n,d),l+this._getOffset(-n,n,d),p[0],p[1]]}),e.push({op:"move",data:[a.x+this._getOffset(-o,o,d),a.y+this._getOffset(-o,o,d)]}),p=[i+this._getOffset(-o,o,d),j+this._getOffset(-o,o,d)],e.push({op:"qcurveTo",data:[k+this._getOffset(-o,o,d),l+this._getOffset(-o,o,d),p[0],p[1]]}),a.setPosition(p[0],p[1]),a.quadReflectionPoint=[i+(i-k),j+(j-l)]}break}case"A":case"a":{const c="a"===b.key;if(7<=b.data.length){let f=+b.data[0],g=+b.data[1],h=+b.data[2],i=+b.data[3],j=+b.data[4],k=+b.data[5],l=+b.data[6];if(c&&(k+=a.x,l+=a.y),k==a.x&&l==a.y)break;if(0==f||0==g)e=e.concat(this._doubleLine(a.x,a.y,k,l,d)),a.setPosition(k,l);else{d.maxRandomnessOffset||0;for(let b=0;1>b;b++){let b=new s([a.x,a.y],[k,l],[f,g],h,!!i,!!j),c=b.getNextSegment();for(;c;){let f=this._bezierTo(c.cp1[0],c.cp1[1],c.cp2[0],c.cp2[1],c.to[0],c.to[1],a,d);e=e.concat(f),c=b.getNextSegment()}}}}break}default:}return e}_getOffset(a,b,c){return c.roughness*(Math.random()*(b-a)+a)}_affine(a,b,c,d,e,f,g){return[-c*f-d*e+c+f*a+e*b,g*(c*e-d*f)+d+-g*e*a+g*f*b]}_doubleLine(a,b,c,d,e){const f=this._line(a,b,c,d,e,!0,!1),g=this._line(a,b,c,d,e,!0,!0);return f.concat(g)}_line(a,b,c,e,f,g,h){const i=d(a-c,2)+d(b-e,2);let k=f.maxRandomnessOffset||0;100*(k*k)>i&&(k=j(i)/10);const l=k/2,m=.2+.2*Math.random();let n=f.bowing*f.maxRandomnessOffset*(e-b)/200,o=f.bowing*f.maxRandomnessOffset*(a-c)/200;n=this._getOffset(-n,n,f),o=this._getOffset(-o,o,f);let p=[];return g&&(h?p.push({op:"move",data:[a+this._getOffset(-l,l,f),b+this._getOffset(-l,l,f)]}):p.push({op:"move",data:[a+this._getOffset(-k,k,f),b+this._getOffset(-k,k,f)]})),h?p.push({op:"bcurveTo",data:[n+a+(c-a)*m+this._getOffset(-l,l,f),o+b+(e-b)*m+this._getOffset(-l,l,f),n+a+2*(c-a)*m+this._getOffset(-l,l,f),o+b+2*(e-b)*m+this._getOffset(-l,l,f),c+this._getOffset(-l,l,f),e+this._getOffset(-l,l,f)]}):p.push({op:"bcurveTo",data:[n+a+(c-a)*m+this._getOffset(-k,k,f),o+b+(e-b)*m+this._getOffset(-k,k,f),n+a+2*(c-a)*m+this._getOffset(-k,k,f),o+b+2*(e-b)*m+this._getOffset(-k,k,f),c+this._getOffset(-k,k,f),e+this._getOffset(-k,k,f)]}),p}_curve(a,c,d){const e=a.length;let f=[];if(3c&&(c=b.strokeWidth/2),{d:this.opsToPath(a),stroke:b.fill,strokeWidth:c,fill:"none"}}opsToPath(a){let b="";for(let c of a.ops){const a=c.data;switch(c.op){case"move":b+=`M${a[0]} ${a[1]} `;break;case"bcurveTo":b+=`C${a[0]} ${a[1]}, ${a[2]} ${a[3]}, ${a[4]} ${a[5]} `;break;case"qcurveTo":b+=`Q${a[0]} ${a[1]}, ${a[2]} ${a[3]} `;break;case"lineTo":b+=`L${a[0]} ${a[1]} `;}}return b.trim()}_computePathSize(a){let b=[0,0];if(self.document)try{const c="http://www.w3.org/2000/svg";let d=self.document.createElementNS(c,"svg");d.setAttribute("width","0"),d.setAttribute("height","0");let e=self.document.createElementNS(c,"path");e.setAttribute("d",a),d.appendChild(e),self.document.body.appendChild(d);let f=e.getBBox();f&&(b[0]=f.width||0,b[1]=f.height||0),self.document.body.removeChild(d)}catch(a){}const c=this._canvasSize();return b[0]*b[1]||(b=c),b[0]=l(b[0],c[0]),b[1]=l(b[1],c[1]),b}_canvasSize(){const a=(a)=>a&&"object"==typeof a&&a.baseVal&&a.baseVal.value?a.baseVal.value:a||100;return this.canvas?[a(this.canvas.width),a(this.canvas.height)]:[100,100]}}class w extends v{async line(a,b,c,d,e){const f=this._options(e);return this._drawable("line",[await this.lib.line(a,b,c,d,f)],f)}async rectangle(a,b,c,d,e){const f=this._options(e),g=[];if(f.fill){const e=[a,a+c,a+c,a],h=[b,b,b+d,b+d];"solid"===f.fillStyle?g.push((await this.lib.solidFillShape(e,h,f))):g.push((await this.lib.hachureFillShape(e,h,f)))}return g.push((await this.lib.rectangle(a,b,c,d,f))),this._drawable("rectangle",g,f)}async ellipse(a,b,c,d,e){const f=this._options(e),g=[];if(f.fill)if("solid"===f.fillStyle){const e=await this.lib.ellipse(a,b,c,d,f);e.type="fillPath",g.push(e)}else g.push((await this.lib.hachureFillEllipse(a,b,c,d,f)));return g.push((await this.lib.ellipse(a,b,c,d,f))),this._drawable("ellipse",g,f)}async circle(a,b,c,d){let e=await this.ellipse(a,b,c,c,d);return e.shape="circle",e}async linearPath(a,b){const c=this._options(b);return this._drawable("linearPath",[await this.lib.linearPath(a,!1,c)],c)}async polygon(a,b){const c=this._options(b),d=[];if(c.fill){let b=[],e=[];for(let c of a)b.push(c[0]),e.push(c[1]);"solid"===c.fillStyle?d.push((await this.lib.solidFillShape(b,e,c))):d.push((await this.lib.hachureFillShape(b,e,c)))}return d.push((await this.lib.linearPath(a,!0,c))),this._drawable("polygon",d,c)}async arc(a,b,c,d,e,f,g,h){const i=this._options(h),j=[];if(g&&i.fill)if("solid"===i.fillStyle){let g=await this.lib.arc(a,b,c,d,e,f,!0,!1,i);g.type="fillPath",j.push(g)}else j.push((await this.lib.hachureFillArc(a,b,c,d,e,f,i)));return j.push((await this.lib.arc(a,b,c,d,e,f,g,!0,i))),this._drawable("arc",j,i)}async curve(a,b){const c=this._options(b);return this._drawable("curve",[await this.lib.curve(a,c)],c)}async path(a,b){const c=this._options(b),e=[];if(!a)return this._drawable("path",e,c);if(c.fill)if("solid"===c.fillStyle){e.push({type:"path2Dfill",path:a})}else{const b=this._computePathSize(a);let d=[0,b[0],b[0],0],f=[0,0,b[1],b[1]],g=await this.lib.hachureFillShape(d,f,c);g.type="path2Dpattern",g.size=b,g.path=a,e.push(g)}return e.push((await this.lib.svgPath(a,c))),this._drawable("path",e,c)}}class x{constructor(a,b){this.canvas=a,this.ctx=this.canvas.getContext("2d"),this._init(b)}_init(a){this.gen=new v(a,this.canvas)}get generator(){return this.gen}static createRenderer(){return new u}line(a,b,c,e,f){let g=this.gen.line(a,b,c,e,f);return this.draw(g),g}rectangle(a,b,c,e,f){let g=this.gen.rectangle(a,b,c,e,f);return this.draw(g),g}ellipse(a,b,c,e,f){let g=this.gen.ellipse(a,b,c,e,f);return this.draw(g),g}circle(a,b,c,e){let f=this.gen.circle(a,b,c,e);return this.draw(f),f}linearPath(a,b){let c=this.gen.linearPath(a,b);return this.draw(c),c}polygon(a,b){let c=this.gen.polygon(a,b);return this.draw(c),c}arc(a,b,c,e,f,g,h,i){let j=this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j),j}curve(a,b){let c=this.gen.curve(a,b);return this.draw(c),c}path(a,b){let c=this.gen.path(a,b);return this.draw(c),c}draw(a){let b=a.sets||[],c=a.options||this.gen.defaultOptions,d=this.ctx;for(let e of b)switch(e.type){case"path":d.save(),d.strokeStyle=c.stroke,d.lineWidth=c.strokeWidth,this._drawToContext(d,e),d.restore();break;case"fillPath":d.save(),d.fillStyle=c.fill,this._drawToContext(d,e,c),d.restore();break;case"fillSketch":this._fillSketch(d,e,c);break;case"path2Dfill":{this.ctx.save(),this.ctx.fillStyle=c.fill;let a=new Path2D(e.path);this.ctx.fill(a),this.ctx.restore();break}case"path2Dpattern":{let a=e.size;const b=document.createElement("canvas"),d=b.getContext("2d");let f=this._computeBBox(e.path);f&&(f.width||f.height)?(b.width=this.canvas.width,b.height=this.canvas.height,d.translate(f.x||0,f.y||0)):(b.width=a[0],b.height=a[1]),this._fillSketch(d,e,c),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(b,"repeat");let g=new Path2D(e.path);this.ctx.fill(g),this.ctx.restore();break}}}_computeBBox(a){if(self.document)try{const b="http://www.w3.org/2000/svg";let c=self.document.createElementNS(b,"svg");c.setAttribute("width","0"),c.setAttribute("height","0");let d=self.document.createElementNS(b,"path");d.setAttribute("d",a),c.appendChild(d),self.document.body.appendChild(c);let e=d.getBBox();return self.document.body.removeChild(c),e}catch(a){}return null}_fillSketch(a,b,c){let d=c.fillWeight;0>d&&(d=c.strokeWidth/2),a.save(),a.strokeStyle=c.fill,a.lineWidth=d,this._drawToContext(a,b),a.restore()}_drawToContext(a,b){a.beginPath();for(let c of b.ops){const b=c.data;switch(c.op){case"move":a.moveTo(b[0],b[1]);break;case"bcurveTo":a.bezierCurveTo(b[0],b[1],b[2],b[3],b[4],b[5]);break;case"qcurveTo":a.quadraticCurveTo(b[0],b[1],b[2],b[3]);break;case"lineTo":a.lineTo(b[0],b[1]);}}"fillPath"===b.type?a.fill():a.stroke()}}class y extends x{_init(a){this.gen=new w(a,this.canvas)}async line(a,b,c,e,f){let g=await this.gen.line(a,b,c,e,f);return this.draw(g),g}async rectangle(a,b,c,e,f){let g=await this.gen.rectangle(a,b,c,e,f);return this.draw(g),g}async ellipse(a,b,c,e,f){let g=await this.gen.ellipse(a,b,c,e,f);return this.draw(g),g}async circle(a,b,c,e){let f=await this.gen.circle(a,b,c,e);return this.draw(f),f}async linearPath(a,b){let c=await this.gen.linearPath(a,b);return this.draw(c),c}async polygon(a,b){let c=await this.gen.polygon(a,b);return this.draw(c),c}async arc(a,b,c,e,f,g,h,i){let j=await this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j),j}async curve(a,b){let c=await this.gen.curve(a,b);return this.draw(c),c}async path(a,b){let c=await this.gen.path(a,b);return this.draw(c),c}}class z{constructor(a,b){this.svg=a,this._init(b)}_init(a){this.gen=new v(a,this.svg)}get generator(){return this.gen}get defs(){if(!this._defs){let a=this.svg.ownerDocument||document,b=a.createElementNS("http://www.w3.org/2000/svg","defs");this.svg.firstChild?this.svg.insertBefore(b,this.svg.firstChild):this.svg.appendChild(b),this._defs=b}return this._defs}line(a,b,c,e,f){let g=this.gen.line(a,b,c,e,f);return this.draw(g)}rectangle(a,b,c,e,f){let g=this.gen.rectangle(a,b,c,e,f);return this.draw(g)}ellipse(a,b,c,e,f){let g=this.gen.ellipse(a,b,c,e,f);return this.draw(g)}circle(a,b,c,e){let f=this.gen.circle(a,b,c,e);return this.draw(f)}linearPath(a,b){let c=this.gen.linearPath(a,b);return this.draw(c)}polygon(a,b){let c=this.gen.polygon(a,b);return this.draw(c)}arc(a,b,c,e,f,g,h,i){let j=this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j)}curve(a,b){let c=this.gen.curve(a,b);return this.draw(c)}path(a,b){let c=this.gen.path(a,b);return this.draw(c)}draw(a){let c=a.sets||[],d=a.options||this.gen.defaultOptions,f=this.svg.ownerDocument||document,h=f.createElementNS("http://www.w3.org/2000/svg","g");for(let g of c){let a=null;switch(g.type){case"path":{a=f.createElementNS("http://www.w3.org/2000/svg","path"),a.setAttribute("d",this._opsToPath(g)),a.style.stroke=d.stroke,a.style.strokeWidth=d.strokeWidth,a.style.fill="none";break}case"fillPath":{a=f.createElementNS("http://www.w3.org/2000/svg","path"),a.setAttribute("d",this._opsToPath(g)),a.style.stroke="none",a.style.strokeWidth=0,a.style.fill=d.fill;break}case"fillSketch":{a=this._fillSketch(f,g,d);break}case"path2Dfill":{a=f.createElementNS("http://www.w3.org/2000/svg","path"),a.setAttribute("d",g.path),a.style.stroke="none",a.style.strokeWidth=0,a.style.fill=d.fill;break}case"path2Dpattern":{const c=g.size,h=f.createElementNS("http://www.w3.org/2000/svg","pattern"),i=`rough-${e(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;h.setAttribute("id",i),h.setAttribute("x",0),h.setAttribute("y",0),h.setAttribute("width",1),h.setAttribute("height",1),h.setAttribute("height",1),h.setAttribute("viewBox",`0 0 ${b(c[0])} ${b(c[1])}`),h.setAttribute("patternUnits","objectBoundingBox");const j=this._fillSketch(f,g,d);h.appendChild(j),this.defs.appendChild(h),a=f.createElementNS("http://www.w3.org/2000/svg","path"),a.setAttribute("d",g.path),a.style.stroke="none",a.style.strokeWidth=0,a.style.fill=`url(#${i})`;break}}a&&h.appendChild(a)}return h}_fillSketch(a,b,c){let d=c.fillWeight;0>d&&(d=c.strokeWidth/2);let e=a.createElementNS("http://www.w3.org/2000/svg","path");return e.setAttribute("d",this._opsToPath(b)),e.style.stroke=c.fill,e.style.strokeWidth=d,e.style.fill="none",e}_opsToPath(a){return this.gen.opsToPath(a)}}class A extends z{_init(a){this.gen=new w(a,this.svg)}async line(a,b,c,e,f){let g=await this.gen.line(a,b,c,e,f);return this.draw(g)}async rectangle(a,b,c,e,f){let g=await this.gen.rectangle(a,b,c,e,f);return this.draw(g)}async ellipse(a,b,c,e,f){let g=await this.gen.ellipse(a,b,c,e,f);return this.draw(g)}async circle(a,b,c,e){let f=await this.gen.circle(a,b,c,e);return this.draw(f)}async linearPath(a,b){let c=await this.gen.linearPath(a,b);return this.draw(c)}async polygon(a,b){let c=await this.gen.polygon(a,b);return this.draw(c)}async arc(a,b,c,e,f,g,h,i){let j=await this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j)}async curve(a,b){let c=await this.gen.curve(a,b);return this.draw(c)}async path(a,b){let c=await this.gen.path(a,b);return this.draw(c)}}var B={canvas(a,b){return b&&b.async?new y(a,b):new x(a,b)},svg(a,b){return b&&b.async?new A(a,b):new z(a,b)},createRenderer(){return x.createRenderer()},generator(a,b){return a&&a.async?new w(a,b):new v(a,b)}};return B}(); \ No newline at end of file +var rough=function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!v[i])switch(i){case'zigzag':v[i]||(v[i]=new O(e));break;case'cross-hatch':v[i]||(v[i]=new P(e));break;case'hachure':default:i='hachure',v[i]||(v[i]=new m(e));}return v[i]}var i=Math.round,s=Math.tan,n=Math.max,l=Math.min,o=Number.MAX_VALUE,a=Math.pow,p=Math.floor,r=Math.sqrt,d=Math.cos,f=Math.sin,h=Math.abs,g=Math.PI;const c={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class u{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const s=this.tokenize(t);let n=0,i=s[n],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=c[a].length:(n++,l=c[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)n++,l=c[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(n+lu){const e=r(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,c=0}else c=(n===a?-1:1)*r(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=c*this._rx*p/this._ry,_=-c*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*g),this._numSegs=Math.ceil(h(x/(g/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=d(this._theta),t=f(this._theta),i=this._theta+this._delta,s=d(i),n=f(i),a=[this._cosPhi*this._rx*s-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*s+this._cosPhi*this._ry*n+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*s),a[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*s)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,s){var n=Math.atan2;const a=n(t,e),l=n(s,i);return l>=a?l-a:2*g-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const s=i.length;let n=p(e*s);if(5>n){if(5>=s)continue;n=5}t.push(this.reduce(i,n))}let s='';for(const n of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=l(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=l(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+p,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>h(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+s,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>h(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?s==p&&(this.px1>=l(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=l(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(p-s)/(t-i),this.yi=t*this.xi+s,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class w{constructor(e,t,i,s,n,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=s,this.gap=n,this.sinAngle=a,this.tanAngle=o,1e-4>h(a)?this.pos=i+n:.9999h(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new b([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=n(u,.1);const y=i%180*(g/180),_=d(y),x=f(y),b=s(y),m=new w(h-1,c+1,p-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=m.nextLine());){const n=this.getIntersectingLines(i,e);for(let e=0;e=c&&(c=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(g/180)),_=d/p,x=r(_*y*_*y+1),b=_*y/x,w=1/x,m=c/(p*d/r(d*w*(d*w)+p*b*(p*b))/p);let O=r(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*g,b+=2*g;b-x>2*g&&(x=0,b=2*g);const w=2*g/c.curveStepCount,m=l(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,c),P=this._arc(m,o,u,y,_,x,b,1.5,c);let v=O.concat(P);return p&&(r?(v=v.concat(this.doubleLine(o,u,o+y*d(x),u+_*f(x),c)),v=v.concat(this.doubleLine(o,u,o+y*d(b),u+_*f(b),c))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*d(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new y(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new y(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*g,y+=2*g;y-u>2*g&&(u=0,y=2*g);const _=(y-u)/l.curveStepCount,x=[];for(let h=u;h<=y;h+=_)x.push([o+r*d(h),p+c*f(h)]);return x.push([o+r*d(y),p+c*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,s,n){const a=this._line(e,t,i,s,n,!0,!1),l=this._line(e,t,i,s,n,!0,!0);return a.concat(l)}_line(e,t,i,s,n,l,o){const p=a(e-i,2)+a(t-s,2);let d=n.maxRandomnessOffset||0;100*(d*d)>p&&(d=r(p)/10);const f=d/2,h=.2+.2*Math.random();let g=n.bowing*n.maxRandomnessOffset*(s-t)/200,c=n.bowing*n.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,n),c=this.getOffset(-c,c,n);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):u.push({op:'move',data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,n),c+t+(s-t)*h+this.getOffset(-f,f,n),g+e+2*(i-e)*h+this.getOffset(-f,f,n),c+t+2*(s-t)*h+this.getOffset(-f,f,n),i+this.getOffset(-f,f,n),s+this.getOffset(-f,f,n)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-d,d,n),c+t+(s-t)*h+this.getOffset(-d,d,n),g+e+2*(i-e)*h+this.getOffset(-d,d,n),c+t+2*(s-t)*h+this.getOffset(-d,d,n),i+this.getOffset(-d,d,n),s+this.getOffset(-d,d,n)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,s){let n=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(s.maxRandomnessOffset||0);a+=this.getOffset(-o,o,s),l+=this.getOffset(-o,o,s),e.setPosition(a,l),n.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),n=n.concat(this.doubleLine(e.x,e.y,a,l,s)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),n=n.concat(this.doubleLine(e.x,e.y,a,e.y,s)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,a,s)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],s)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,s);n=n.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,s);n=n.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*s.roughness),d=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-r,r,s),e.y+this.getOffset(-r,r,s)]});let h=[o+this.getOffset(-r,r,s),p+this.getOffset(-r,r,s)];n.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,s),l+this.getOffset(-r,r,s),h[0],h[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-d,d,s),e.y+this.getOffset(-d,d,s)]}),h=[o+this.getOffset(-d,d,s),p+this.getOffset(-d,d,s)],n.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,s),l+this.getOffset(-d,d,s),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*s.roughness),c=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-g,g,s),e.y+this.getOffset(-g,g,s)]});let u=[l+this.getOffset(-g,g,s),o+this.getOffset(-g,g,s)];n.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,s),r+this.getOffset(-g,g,s),u[0],u[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-c,c,s),e.y+this.getOffset(-c,c,s)]}),u=[l+this.getOffset(-c,c,s),o+this.getOffset(-c,c,s)],n.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,s),r+this.getOffset(-c,c,s),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)n=n.concat(this.doubleLine(e.x,e.y,d,f,s)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new _([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,s);n=n.concat(a),i=t.getNextSegment()}}}break}default:}return n}}const A='undefined'!=typeof self;class S{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer||(this.renderer=new k),this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(A&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const s=self.document.createElementNS('http://www.w3.org/2000/svg','path');s.setAttribute('d',e),i.appendChild(s),self.document.body.appendChild(i);const n=s.getBBox();n&&(t[0]=n.width||0,t[1]=n.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=l(t[0],i[0]),t[1]=l(t[1],i[1]),t}line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[this.lib.line(e,t,i,s,a)],a)}rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(n,a)):l.push(this.lib.patternFillPolygon(n,a))}return l.push(this.lib.rectangle(e,t,i,s,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push(this.lib.patternFillEllipse(e,t,i,s,a));return l.push(this.lib.ellipse(e,t,i,s,a)),this._drawable('ellipse',l,a)}circle(e,t,i,s){const n=this.ellipse(e,t,i,i,s);return n.shape='circle',n}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push(this.lib.solidFillPolygon(e,i)):s.push(this.lib.patternFillPolygon(e,i))),s.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',s,i)}arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,s,n,a,r));return o.push(this.lib.arc(e,t,i,s,n,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push(this.lib.svgPath(e,i)),this._drawable('path',s,i)}toPaths(e){const t=e.sets||[],s=e.options||this.defaultOptions,n=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:s.stroke,strokeWidth:s.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,s);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'path2Dpattern':{const t=a.size,n={x:0,y:0,width:1,height:1,viewBox:`0 0 ${i(t[0])} ${i(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,s)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:n};break}}e&&n.push(e)}return n}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const T='undefined'!=typeof document;class C{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new S(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new k}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a),a}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a),a}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a),a}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n),n}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.ctx;for(const n of t)switch(n.type){case'path':s.save(),s.strokeStyle=i.stroke,s.lineWidth=i.strokeWidth,this._drawToContext(s,n),s.restore();break;case'fillPath':s.save(),s.fillStyle=i.fill||'',this._drawToContext(s,n),s.restore();break;case'fillSketch':this.fillSketch(s,n,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(n.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(T){const e=n.size,t=document.createElement('canvas'),s=t.getContext('2d'),a=this.computeBBox(n.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,s.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(s,n,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(n.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(T)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const s=i.getBBox();return document.body.removeChild(t),s}catch(e){}return null}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=s,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class E extends S{async line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[await this.lib.line(e,t,i,s,a)],a)}async rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(n,a))):l.push((await this.lib.patternFillPolygon(n,a)))}return l.push((await this.lib.rectangle(e,t,i,s,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=await this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push((await this.lib.patternFillEllipse(e,t,i,s,a)));return l.push((await this.lib.ellipse(e,t,i,s,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,s){const n=await this.ellipse(e,t,i,i,s);return n.shape='circle',n}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push((await this.lib.solidFillPolygon(e,i))):s.push((await this.lib.patternFillPolygon(e,i)))),s.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',s,i)}async arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,s,n,a,r)));return o.push((await this.lib.arc(e,t,i,s,n,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push((await this.lib.svgPath(e,i))),this._drawable('path',s,i)}}class N extends C{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a),a}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a),a}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a),a}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n),n}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const W='undefined'!=typeof document;class z{constructor(e,t){this.svg=e,this.gen=new S(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new k}get defs(){if(W&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a)}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a)}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a)}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],s=e.options||this.gen.defaultOptions,n=this.svg.ownerDocument||W&&document,a=n.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=s.stroke,e.style.strokeWidth=s.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'fillSketch':{e=this.fillSketch(n,l,s);break}case'path2Dfill':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'path2Dpattern':{const t=l.size,a=n.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${p(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${i(t[0])} ${i(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const r=this.fillSketch(n,l,s);a.appendChild(r),this.defs.appendChild(a),e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2);const n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=i.fill,n.style.strokeWidth=s+'',n.style.fill='none',n}}class L extends z{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a)}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a)}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a)}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var R={canvas(e,t){return t&&t.async?new N(e,t):new C(e,t)},svg(e,t){return t&&t.async?new L(e,t):new z(e,t)},createRenderer(){return C.createRenderer()},generator(e,t){return e&&e.async?new E(e,t):new S(e,t)}};return R}(); diff --git a/dist/rough.umd.js b/dist/rough.umd.js index 748ae73..2d6de2d 100644 --- a/dist/rough.umd.js +++ b/dist/rough.umd.js @@ -1,2263 +1,2237 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.rough = factory()); + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.rough = factory()); }(this, (function () { 'use strict'; -function RoughSegmentRelation() { - return { - LEFT: 0, - RIGHT: 1, - INTERSECTS: 2, - AHEAD: 3, - BEHIND: 4, - SEPARATE: 5, - UNDEFINED: 6 - }; -} - -class RoughSegment { - constructor(px1, py1, px2, py2) { - this.RoughSegmentRelationConst = RoughSegmentRelation(); - this.px1 = px1; - this.py1 = py1; - this.px2 = px2; - this.py2 = py2; - this.xi = Number.MAX_VALUE; - this.yi = Number.MAX_VALUE; - this.a = py2 - py1; - this.b = px1 - px2; - this.c = px2 * py1 - px1 * py2; - this._undefined = ((this.a == 0) && (this.b == 0) && (this.c == 0)); - } - - isUndefined() { - return this._undefined; - } - - compare(otherSegment) { - if (this.isUndefined() || otherSegment.isUndefined()) { - return this.RoughSegmentRelationConst.UNDEFINED; - } - var grad1 = Number.MAX_VALUE; - var grad2 = Number.MAX_VALUE; - var int1 = 0, int2 = 0; - var a = this.a, b = this.b, c = this.c; - - if (Math.abs(b) > 0.00001) { - grad1 = -a / b; - int1 = -c / b; - } - if (Math.abs(otherSegment.b) > 0.00001) { - grad2 = -otherSegment.a / otherSegment.b; - int2 = -otherSegment.c / otherSegment.b; - } - - if (grad1 == Number.MAX_VALUE) { - if (grad2 == Number.MAX_VALUE) { - if ((-c / a) != (-otherSegment.c / otherSegment.a)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if ((this.py1 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py1 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px1; - this.yi = this.py1; - return this.RoughSegmentRelationConst.INTERSECTS; - } - if ((this.py2 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py2 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px2; - this.yi = this.py2; - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.SEPARATE; - } - this.xi = this.px1; - this.yi = (grad2 * this.xi + int2); - if (((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) || ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if (Math.abs(otherSegment.a) < 0.00001) { - if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - if (grad2 == Number.MAX_VALUE) { - this.xi = otherSegment.px1; - this.yi = grad1 * this.xi + int1; - if (((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) || ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if (Math.abs(a) < 0.00001) { - if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - if (grad1 == grad2) { - if (int1 != int2) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if ((this.px1 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px1 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px1; - this.yi = this.py1; - return this.RoughSegmentRelationConst.INTERSECTS; - } - if ((this.px2 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px2 <= Math.max(otherSegment.px1, otherSegment.px2))) { - this.xi = this.px2; - this.yi = this.py2; - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.SEPARATE; - } - - this.xi = ((int2 - int1) / (grad1 - grad2)); - this.yi = (grad1 * this.xi + int1); - - if (((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) || ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - getLength() { - return this._getLength(this.px1, this.py1, this.px2, this.py2); - } - - _getLength(x1, y1, x2, y2) { - var dx = x2 - x1; - var dy = y2 - y1; - return Math.sqrt(dx * dx + dy * dy); - } -} - -class RoughHachureIterator { - constructor(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { - this.top = top; - this.bottom = bottom; - this.left = left; - this.right = right; - this.gap = gap; - this.sinAngle = sinAngle; - this.tanAngle = tanAngle; - - if (Math.abs(sinAngle) < 0.0001) { - this.pos = left + gap; - } else if (Math.abs(sinAngle) > 0.9999) { - this.pos = top + gap; - } else { - this.deltaX = (bottom - top) * Math.abs(tanAngle); - this.pos = left - Math.abs(this.deltaX); - this.hGap = Math.abs(gap / cosAngle); - this.sLeft = new RoughSegment(left, bottom, left, top); - this.sRight = new RoughSegment(right, bottom, right, top); - } - } - - getNextLine() { - if (Math.abs(this.sinAngle) < 0.0001) { - if (this.pos < this.right) { - let line = [this.pos, this.top, this.pos, this.bottom]; - this.pos += this.gap; - return line; - } - } else if (Math.abs(this.sinAngle) > 0.9999) { - if (this.pos < this.bottom) { - let line = [this.left, this.pos, this.right, this.pos]; - this.pos += this.gap; - return line; - } - } else { - let xLower = this.pos - this.deltaX / 2; - let xUpper = this.pos + this.deltaX / 2; - let yLower = this.bottom; - let yUpper = this.top; - if (this.pos < (this.right + this.deltaX)) { - while (((xLower < this.left) && (xUpper < this.left)) || ((xLower > this.right) && (xUpper > this.right))) { - this.pos += this.hGap; - xLower = this.pos - this.deltaX / 2; - xUpper = this.pos + this.deltaX / 2; - if (this.pos > (this.right + this.deltaX)) { - return null; - } + function isType(token, type) { + return token.type === type; + } + const PARAMS = { + A: { length: 7 }, + a: { length: 7 }, + C: { length: 6 }, + c: { length: 6 }, + H: { length: 1 }, + h: { length: 1 }, + L: { length: 2 }, + l: { length: 2 }, + M: { length: 2 }, + m: { length: 2 }, + Q: { length: 4 }, + q: { length: 4 }, + S: { length: 4 }, + s: { length: 4 }, + T: { length: 4 }, + t: { length: 2 }, + V: { length: 1 }, + v: { length: 1 }, + Z: { length: 0 }, + z: { length: 0 } + }; + class ParsedPath { + constructor(d) { + this.COMMAND = 0; + this.NUMBER = 1; + this.EOD = 2; + this.segments = []; + this.parseData(d); + this.processPoints(); } - let s = new RoughSegment(xLower, yLower, xUpper, yUpper); - if (s.compare(this.sLeft) == RoughSegmentRelation().INTERSECTS) { - xLower = s.xi; - yLower = s.yi; + 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: this.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: this.NUMBER, text: `${parseFloat(RegExp.$1)}` }; + d = d.substr(RegExp.$1.length); + } + else { + console.error('Unrecognized segment command: ' + d); + return []; + } + } + tokens[tokens.length] = { type: this.EOD, text: '' }; + return tokens; } - if (s.compare(this.sRight) == RoughSegmentRelation().INTERSECTS) { - xUpper = s.xi; - yUpper = s.yi; + parseData(d) { + const tokens = this.tokenize(d); + let index = 0; + let token = tokens[index]; + let mode = 'BOD'; + this.segments = new Array(); + while (!isType(token, this.EOD)) { + let param_length; + const params = new Array(); + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + else { + this.parseData('M0,0' + d); + return; + } + } + else { + if (isType(token, this.NUMBER)) { + param_length = PARAMS[mode].length; + } + else { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + } + if ((index + param_length) < tokens.length) { + for (let i = index; i < index + param_length; i++) { + const numbeToken = tokens[i]; + if (isType(numbeToken, this.NUMBER)) { + params[params.length] = +numbeToken.text; + } + else { + console.error('Parameter type is not a number: ' + mode + ',' + numbeToken.text); + return; + } + } + if (PARAMS[mode]) { + const segment = { key: mode, data: params }; + this.segments.push(segment); + index += param_length; + token = tokens[index]; + if (mode === 'M') + mode = 'L'; + if (mode === 'm') + mode = 'l'; + } + else { + console.error('Unsupported segment type: ' + mode); + return; + } + } + else { + console.error('Path data ended before all parameters were found'); + } + } } - if (this.tanAngle > 0) { - xLower = this.right - (xLower - this.left); - xUpper = this.right - (xUpper - this.left); + get closed() { + if (typeof this._closed === 'undefined') { + this._closed = false; + for (const s of this.segments) { + if (s.key.toLowerCase() === 'z') { + this._closed = true; + } + } + } + return this._closed; + } + processPoints() { + let first = null; + let currentPoint = [0, 0]; + for (let i = 0; i < this.segments.length; i++) { + const s = this.segments[i]; + switch (s.key) { + case 'M': + case 'L': + case 'T': + s.point = [s.data[0], s.data[1]]; + break; + case 'm': + case 'l': + case 't': + s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; + break; + case 'H': + s.point = [s.data[0], currentPoint[1]]; + break; + case 'h': + s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; + break; + case 'V': + s.point = [currentPoint[0], s.data[0]]; + break; + case 'v': + s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; + break; + case 'z': + case 'Z': + if (first) { + s.point = [first[0], first[1]]; + } + break; + case 'C': + s.point = [s.data[4], s.data[5]]; + break; + case 'c': + s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; + break; + case 'S': + s.point = [s.data[2], s.data[3]]; + break; + case 's': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'Q': + s.point = [s.data[2], s.data[3]]; + break; + case 'q': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'A': + s.point = [s.data[5], s.data[6]]; + break; + case 'a': + s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; + break; + } + if (s.key === 'm' || s.key === 'M') { + first = null; + } + if (s.point) { + currentPoint = s.point; + if (!first) { + first = s.point; + } + } + if (s.key === 'z' || s.key === 'Z') { + first = null; + } + } } - let line = [xLower, yLower, xUpper, yUpper]; - this.pos += this.hGap; - return line; - } - } - return null; - } -} - -class PathToken { - constructor(type, text) { - this.type = type; - this.text = text; - } - isType(type) { - return this.type === type; - } -} - -class ParsedPath { - constructor(d) { - this.PARAMS = { - A: ["rx", "ry", "x-axis-rotation", "large-arc-flag", "sweep-flag", "x", "y"], - a: ["rx", "ry", "x-axis-rotation", "large-arc-flag", "sweep-flag", "x", "y"], - C: ["x1", "y1", "x2", "y2", "x", "y"], - c: ["x1", "y1", "x2", "y2", "x", "y"], - H: ["x"], - h: ["x"], - L: ["x", "y"], - l: ["x", "y"], - M: ["x", "y"], - m: ["x", "y"], - Q: ["x1", "y1", "x", "y"], - q: ["x1", "y1", "x", "y"], - S: ["x2", "y2", "x", "y"], - s: ["x2", "y2", "x", "y"], - T: ["x", "y"], - t: ["x", "y"], - V: ["y"], - v: ["y"], - Z: [], - z: [] - }; - this.COMMAND = 0; - this.NUMBER = 1; - this.EOD = 2; - this.segments = []; - this.d = d || ""; - this.parseData(d); - this.processPoints(); - } - - loadFromSegments(segments) { - this.segments = segments; - this.processPoints(); - } - - processPoints() { - let first = null, currentPoint = [0, 0]; - for (let i = 0; i < this.segments.length; i++) { - let s = this.segments[i]; - switch (s.key) { - case 'M': - case 'L': - case 'T': - s.point = [s.data[0], s.data[1]]; - break; - case 'm': - case 'l': - case 't': - s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; - break; - case 'H': - s.point = [s.data[0], currentPoint[1]]; - break; - case 'h': - s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; - break; - case 'V': - s.point = [currentPoint[0], s.data[0]]; - break; - case 'v': - s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; - break; - case 'z': - case 'Z': - if (first) { - s.point = [first[0], first[1]]; - } - break; - case 'C': - s.point = [s.data[4], s.data[5]]; - break; - case 'c': - s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; - break; - case 'S': - s.point = [s.data[2], s.data[3]]; - break; - case 's': - s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; - break; - case 'Q': - s.point = [s.data[2], s.data[3]]; - break; - case 'q': - s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; - break; - case 'A': - s.point = [s.data[5], s.data[6]]; - break; - case 'a': - s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; - break; - } - if (s.key === 'm' || s.key === 'M') { - first = null; - } - if (s.point) { - currentPoint = s.point; - if (!first) { - first = s.point; - } - } - if (s.key === 'z' || s.key === 'Z') { - first = null; - } - } - } - - get closed() { - if (typeof this._closed === 'undefined') { - this._closed = false; - for (let s of this.segments) { - if (s.key.toLowerCase() === 'z') { - this._closed = true; - } - } - } - return this._closed; - } - - parseData(d) { - var tokens = this.tokenize(d); - var index = 0; - var token = tokens[index]; - var mode = "BOD"; - this.segments = new Array(); - while (!token.isType(this.EOD)) { - var param_length; - var params = new Array(); - if (mode == "BOD") { - if (token.text == "M" || token.text == "m") { - index++; - param_length = this.PARAMS[token.text].length; - mode = token.text; - } else { - return this.parseData('M0,0' + d); - } - } else { - if (token.isType(this.NUMBER)) { - param_length = this.PARAMS[mode].length; - } else { - index++; - param_length = this.PARAMS[token.text].length; - mode = token.text; - } - } - - if ((index + param_length) < tokens.length) { - for (var i = index; i < index + param_length; i++) { - var number = tokens[i]; - if (number.isType(this.NUMBER)) { - params[params.length] = number.text; - } - else { - console.error("Parameter type is not a number: " + mode + "," + number.text); - return; - } - } - var segment; - if (this.PARAMS[mode]) { - segment = { key: mode, data: params }; - } else { - console.error("Unsupported segment type: " + mode); - return; - } - this.segments.push(segment); - index += param_length; - token = tokens[index]; - if (mode == "M") mode = "L"; - if (mode == "m") mode = "l"; - } else { - console.error("Path data ended before all parameters were found"); - } - } - } - - tokenize(d) { - var 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] = new PathToken(this.COMMAND, RegExp.$1); - d = d.substr(RegExp.$1.length); - } else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) { - tokens[tokens.length] = new PathToken(this.NUMBER, parseFloat(RegExp.$1)); - d = d.substr(RegExp.$1.length); - } else { - console.error("Unrecognized segment command: " + d); - return null; - } - } - tokens[tokens.length] = new PathToken(this.EOD, null); - return tokens; - } -} - -class RoughPath { - constructor(d) { - this.d = d; - this.parsed = new ParsedPath(d); - this._position = [0, 0]; - this.bezierReflectionPoint = null; - this.quadReflectionPoint = null; - this._first = null; - } - - get segments() { - return this.parsed.segments; - } - - get closed() { - return this.parsed.closed; - } - - get linearPoints() { - if (!this._linearPoints) { - const lp = []; - let points = []; - for (let s of this.parsed.segments) { - let key = s.key.toLowerCase(); - if (key === 'm' || key === 'z') { - if (points.length) { - lp.push(points); - points = []; - } - if (key === 'z') { - continue; - } - } - if (s.point) { - points.push(s.point); - } - } - if (points.length) { - lp.push(points); - points = []; - } - this._linearPoints = lp; - } - return this._linearPoints; - } - - get first() { - return this._first; - } - - set first(v) { - this._first = v; - } - - setPosition(x, y) { - this._position = [x, y]; - if (!this._first) { - this._first = [x, y]; - } - } - - get position() { - return this._position; - } - - get x() { - return this._position[0]; - } - - get y() { - return this._position[1]; - } -} - -class RoughArcConverter { - // Algorithm as described in https://www.w3.org/TR/SVG/implnote.html - // Code adapted from nsSVGPathDataParser.cpp in Mozilla - // https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 - constructor(from, to, radii, angle, largeArcFlag, sweepFlag) { - const radPerDeg = Math.PI / 180; - this._segIndex = 0; - this._numSegs = 0; - if (from[0] == to[0] && from[1] == to[1]) { - return; - } - this._rx = Math.abs(radii[0]); - this._ry = Math.abs(radii[1]); - this._sinPhi = Math.sin(angle * radPerDeg); - this._cosPhi = Math.cos(angle * radPerDeg); - var x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; - var y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; - var root; - var numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; - if (numerator < 0) { - let s = Math.sqrt(1 - (numerator / (this._rx * this._rx * this._ry * this._ry))); - this._rx = this._rx * s; - this._ry = this._ry * s; - root = 0; - } else { - root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) * - Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); - } - let cxdash = root * this._rx * y1dash / this._ry; - let cydash = -root * this._ry * x1dash / this._rx; - this._C = [0, 0]; - this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; - this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; - this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); - let dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); - if ((!sweepFlag) && (dtheta > 0)) { - dtheta -= 2 * Math.PI; - } else if (sweepFlag && (dtheta < 0)) { - dtheta += 2 * Math.PI; - } - this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); - this._delta = dtheta / this._numSegs; - this._T = (8 / 3) * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); - this._from = from; - } - - getNextSegment() { - var cp1, cp2, to; - if (this._segIndex == this._numSegs) { - return null; - } - let cosTheta1 = Math.cos(this._theta); - let sinTheta1 = Math.sin(this._theta); - let theta2 = this._theta + this._delta; - let cosTheta2 = Math.cos(theta2); - let sinTheta2 = Math.sin(theta2); - - to = [ - this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], - this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1] - ]; - cp1 = [ - this._from[0] + this._T * (- this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), - this._from[1] + this._T * (- this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1) - ]; - cp2 = [ - to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), - to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2) - ]; - - this._theta = theta2; - this._from = [to[0], to[1]]; - this._segIndex++; - - return { - cp1: cp1, - cp2: cp2, - to: to - }; - } - - calculateVectorAngle(ux, uy, vx, vy) { - let ta = Math.atan2(uy, ux); - let tb = Math.atan2(vy, vx); - if (tb >= ta) - return tb - ta; - return 2 * Math.PI - (ta - tb); - } -} - -class PathFitter { - constructor(sets, closed) { - this.sets = sets; - this.closed = closed; - } - - fit(simplification) { - let outSets = []; - for (const set of this.sets) { - let length = set.length; - let estLength = Math.floor(simplification * length); - if (estLength < 5) { - if (length <= 5) { - continue; - } - estLength = 5; - } - outSets.push(this.reduce(set, estLength)); - } - - let d = ''; - for (const set of outSets) { - for (let i = 0; i < set.length; i++) { - let point = set[i]; - if (i === 0) { - d += 'M' + point[0] + "," + point[1]; - } else { - d += 'L' + point[0] + "," + point[1]; - } - } - if (this.closed) { - d += 'z '; - } - } - return d; - } - - distance(p1, p2) { - return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); - } - - reduce(set, count) { - if (set.length <= count) { - return set; - } - let points = set.slice(0); - while (points.length > count) { - let minArea = -1; - let minIndex = -1; - for (let i = 1; i < (points.length - 1); i++) { - let a = this.distance(points[i - 1], points[i]); - let b = this.distance(points[i], points[i + 1]); - let c = this.distance(points[i - 1], points[i + 1]); - let s = (a + b + c) / 2.0; - let area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); - if ((minArea < 0) || (area < minArea)) { - minArea = area; - minIndex = i; - } - } - if (minIndex > 0) { - points.splice(minIndex, 1); - } else { - break; - } - } - return points; - } -} - -class RoughRenderer { - line(x1, y1, x2, y2, o) { - let ops = this._doubleLine(x1, y1, x2, y2, o); - return { type: 'path', ops }; - } - - linearPath(points, close, o) { - const len = (points || []).length; - if (len > 2) { - let ops = []; - for (let i = 0; i < (len - 1); i++) { - ops = ops.concat(this._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); - } - if (close) { - ops = ops.concat(this._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 this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); - } - } - - polygon(points, o) { - return this.linearPath(points, true, o); - } - - rectangle(x, y, width, height, o) { - let points = [ - [x, y], [x + width, y], [x + width, y + height], [x, y + height] - ]; - return this.polygon(points, o); - } - - curve(points, o) { - let o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); - let o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); - return { type: 'path', ops: o1.concat(o2) }; - } - - ellipse(x, y, width, height, o) { - const increment = (Math.PI * 2) / o.curveStepCount; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.05, rx * 0.05, o); - ry += this._getOffset(-ry * 0.05, ry * 0.05, o); - let o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this._getOffset(0.1, this._getOffset(0.4, 1, o), o), o); - let o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); - return { type: 'path', ops: o1.concat(o2) }; - } - - arc(x, y, width, height, start, stop, closed, roughClosure, o) { - let cx = x; - let cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.01, rx * 0.01, o); - ry += this._getOffset(-ry * 0.01, 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; - } - let ellipseInc = (Math.PI * 2) / o.curveStepCount; - let arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); - let o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); - let o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); - let ops = o1.concat(o2); - if (closed) { - if (roughClosure) { - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); - } else { - ops.push({ op: 'lineTo', data: [cx, cy] }); - ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); - } - } - return { type: 'path', ops }; - } - - hachureFillArc(x, y, width, height, start, stop, o) { - let cx = x; - let cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.01, rx * 0.01, o); - ry += this._getOffset(-ry * 0.01, 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; - } - let increment = (stp - strt) / o.curveStepCount; - let xc = [], yc = []; - for (let angle = strt; angle <= stp; angle = angle + increment) { - xc.push(cx + rx * Math.cos(angle)); - yc.push(cy + ry * Math.sin(angle)); - } - xc.push(cx + rx * Math.cos(stp)); - yc.push(cy + ry * Math.sin(stp)); - xc.push(cx); - yc.push(cy); - return this.hachureFillShape(xc, yc, o); - } - - solidFillShape(xCoords, yCoords, o) { - let ops = []; - if (xCoords && yCoords && xCoords.length && yCoords.length && xCoords.length === yCoords.length) { - let offset = o.maxRandomnessOffset || 0; - const len = xCoords.length; - if (len > 2) { - ops.push({ op: 'move', data: [xCoords[0] + this._getOffset(-offset, offset, o), yCoords[0] + this._getOffset(-offset, offset, o)] }); - for (var i = 1; i < len; i++) { - ops.push({ op: 'lineTo', data: [xCoords[i] + this._getOffset(-offset, offset, o), yCoords[i] + this._getOffset(-offset, offset, o)] }); - } - } - } - return { type: 'fillPath', ops }; - } - - hachureFillShape(xCoords, yCoords, o) { - let ops = []; - if (xCoords && yCoords && xCoords.length && yCoords.length) { - let left = xCoords[0]; - let right = xCoords[0]; - let top = yCoords[0]; - let bottom = yCoords[0]; - for (let 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]); - } - const angle = o.hachureAngle; - let gap = o.hachureGap; - if (gap < 0) { - gap = o.strokeWidth * 4; - } - gap = Math.max(gap, 0.1); - - const radPerDeg = Math.PI / 180; - const hachureAngle = (angle % 180) * radPerDeg; - const cosAngle = Math.cos(hachureAngle); - const sinAngle = Math.sin(hachureAngle); - const tanAngle = Math.tan(hachureAngle); - - const it = new RoughHachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); - let rectCoords; - while ((rectCoords = it.getNextLine()) != null) { - let lines = this._getIntersectingLines(rectCoords, xCoords, yCoords); - for (let i = 0; i < lines.length; i++) { - if (i < (lines.length - 1)) { - let p1 = lines[i]; - let p2 = lines[i + 1]; - ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o)); - } - } - } - } - return { type: 'fillSketch', ops }; - } - - hachureFillEllipse(cx, cy, width, height, o) { - let ops = []; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.05, rx * 0.05, o); - ry += this._getOffset(-ry * 0.05, ry * 0.05, o); - let angle = o.hachureAngle; - let gap = o.hachureGap; - if (gap <= 0) { - gap = o.strokeWidth * 4; - } - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - const radPerDeg = Math.PI / 180; - let hachureAngle = (angle % 180) * radPerDeg; - let tanAngle = Math.tan(hachureAngle); - let aspectRatio = ry / rx; - let hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); - let sinAnglePrime = aspectRatio * tanAngle / hyp; - let cosAnglePrime = 1 / hyp; - let gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); - let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); - for (var xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { - halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); - let p1 = this._affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); - let p2 = this._affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); - ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o)); - } - return { type: 'fillSketch', ops }; - } - - svgPath(path, o) { - path = (path || '').replace(/\n/g, " ").replace(/(-\s)/g, "-").replace("/(\s\s)/g", " "); - let p = new RoughPath(path); - if (o.simplification) { - let fitter = new PathFitter(p.linearPoints, p.closed); - let d = fitter.fit(o.simplification); - p = new RoughPath(d); - } - let ops = []; - let segments = p.segments || []; - for (let i = 0; i < segments.length; i++) { - let s = segments[i]; - let prev = i > 0 ? segments[i - 1] : null; - let opList = this._processSegment(p, s, prev, o); - if (opList && opList.length) { - ops = ops.concat(opList); - } - } - return { type: 'path', ops }; - } - - // privates - - _bezierTo(x1, y1, x2, y2, x, y, path, o) { - let ops = []; - let ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; - let f = null; - for (let i = 0; i < 2; i++) { - if (i === 0) { - ops.push({ op: 'move', data: [path.x, path.y] }); - } else { - ops.push({ op: 'move', data: [path.x + this._getOffset(-ros[0], ros[0], o), path.y + this._getOffset(-ros[0], ros[0], o)] }); - } - f = [x + this._getOffset(-ros[i], ros[i], o), y + this._getOffset(-ros[i], ros[i], o)]; - ops.push({ - op: 'bcurveTo', data: [ - x1 + this._getOffset(-ros[i], ros[i], o), y1 + this._getOffset(-ros[i], ros[i], o), - x2 + this._getOffset(-ros[i], ros[i], o), y2 + this._getOffset(-ros[i], ros[i], o), - f[0], f[1] - ] - }); - } - path.setPosition(f[0], f[1]); - return ops; - } - - _processSegment(path, seg, prevSeg, o) { - let ops = []; - switch (seg.key) { - case 'M': - case 'm': { - let delta = seg.key === 'm'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let ro = 1 * (o.maxRandomnessOffset || 0); - x = x + this._getOffset(-ro, ro, o); - y = y + this._getOffset(-ro, ro, o); - path.setPosition(x, y); - ops.push({ op: 'move', data: [x, y] }); - } - break; - } - case 'L': - case 'l': { - let delta = seg.key === 'l'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } - break; - } - case 'H': - case 'h': { - const delta = seg.key === 'h'; - if (seg.data.length) { - let x = +seg.data[0]; - if (delta) { - x += path.x; - } - ops = ops.concat(this._doubleLine(path.x, path.y, x, path.y, o)); - path.setPosition(x, path.y); - } - break; - } - case 'V': - case 'v': { - const delta = seg.key === 'v'; - if (seg.data.length) { - let y = +seg.data[0]; - if (delta) { - y += path.y; - } - ops = ops.concat(this._doubleLine(path.x, path.y, path.x, y, o)); - path.setPosition(path.x, y); - } - break; - } - case 'Z': - case 'z': { - if (path.first) { - ops = ops.concat(this._doubleLine(path.x, path.y, path.first[0], path.first[1], o)); - path.setPosition(path.first[0], path.first[1]); - path.first = null; - } - break; - } - case 'C': - case 'c': { - const delta = seg.key === 'c'; - if (seg.data.length >= 6) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x2 = +seg.data[2]; - let y2 = +seg.data[3]; - let x = +seg.data[4]; - let y = +seg.data[5]; - if (delta) { - x1 += path.x; - x2 += path.x; - x += path.x; - y1 += path.y; - y2 += path.y; - y += path.y; - } - let ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'S': - case 's': { - const delta = seg.key === 's'; - if (seg.data.length >= 4) { - let x2 = +seg.data[0]; - let y2 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x2 += path.x; - x += path.x; - y2 += path.y; - y += path.y; - } - let x1 = x2; - let y1 = y2; - let prevKey = prevSeg ? prevSeg.key : ""; - var ref = null; - if (prevKey == 'c' || prevKey == 'C' || prevKey == 's' || prevKey == 'S') { - ref = path.bezierReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - let ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'Q': - case 'q': { - const delta = seg.key === 'q'; - if (seg.data.length >= 4) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x1 += path.x; - x += path.x; - y1 += path.y; - y += path.y; - } - let offset1 = 1 * (1 + o.roughness * 0.2); - let offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'T': - case 't': { - const delta = seg.key === 't'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let x1 = x; - let y1 = y; - let prevKey = prevSeg ? prevSeg.key : ""; - var ref = null; - if (prevKey == 'q' || prevKey == 'Q' || prevKey == 't' || prevKey == 'T') { - ref = path.quadReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - let offset1 = 1 * (1 + o.roughness * 0.2); - let offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'A': - case 'a': { - const delta = seg.key === 'a'; - if (seg.data.length >= 7) { - let rx = +seg.data[0]; - let ry = +seg.data[1]; - let angle = +seg.data[2]; - let largeArcFlag = +seg.data[3]; - let sweepFlag = +seg.data[4]; - let x = +seg.data[5]; - let y = +seg.data[6]; - if (delta) { - x += path.x; - y += path.y; - } - if (x == path.x && y == path.y) { - break; - } - if (rx == 0 || ry == 0) { - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } else { - let ro = o.maxRandomnessOffset || 0; - for (let i = 0; i < 1; i++) { - let arcConverter = new RoughArcConverter( - [path.x, path.y], - [x, y], - [rx, ry], - angle, - largeArcFlag ? true : false, - sweepFlag ? true : false - ); - let segment = arcConverter.getNextSegment(); - while (segment) { - let ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); - ops = ops.concat(ob); - segment = arcConverter.getNextSegment(); - } - } - } - } - break; - } - default: - break; - } - return ops; - } - - _getOffset(min, max, ops) { - return ops.roughness * ((Math.random() * (max - min)) + min); - } - - _affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { - var A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; - var B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; - var C = cosAnglePrime; - var D = sinAnglePrime; - var E = -R * sinAnglePrime; - var F = R * cosAnglePrime; - return [ - A + C * x + D * y, - B + E * x + F * y - ]; - } - - _doubleLine(x1, y1, x2, y2, o) { - const o1 = this._line(x1, y1, x2, y2, o, true, false); - const o2 = this._line(x1, y1, x2, y2, o, true, true); - return o1.concat(o2); - } - - _line(x1, y1, x2, y2, o, move, overlay) { - const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); - let offset = o.maxRandomnessOffset || 0; - if ((offset * offset * 100) > lengthSq) { - offset = Math.sqrt(lengthSq) / 10; - } - const halfOffset = offset / 2; - const divergePoint = 0.2 + Math.random() * 0.2; - let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; - let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; - midDispX = this._getOffset(-midDispX, midDispX, o); - midDispY = this._getOffset(-midDispY, midDispY, o); - let ops = []; - if (move) { - if (overlay) { - ops.push({ - op: 'move', data: [ - x1 + this._getOffset(-halfOffset, halfOffset, o), - y1 + this._getOffset(-halfOffset, halfOffset, o) - ] - }); - } else { - ops.push({ - op: 'move', data: [ - x1 + this._getOffset(-offset, offset, o), - y1 + this._getOffset(-offset, offset, o) - ] - }); - } - } - if (overlay) { - ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - x2 + this._getOffset(-halfOffset, halfOffset, o), - y2 + this._getOffset(-halfOffset, halfOffset, o) - ] - }); - } else { - ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - x2 + this._getOffset(-offset, offset, o), - y2 + this._getOffset(-offset, offset, o) - ] - }); } - return ops; - } - - _curve(points, closePoint, o) { - const len = points.length; - let 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) { - let ro = o.maxRandomnessOffset; - // TODO: more roughness here? - ops.push({ ops: 'lineTo', data: [closePoint[0] + this._getOffset(-ro, ro, o), closePoint[1] + + this._getOffset(-ro, 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 = ops.concat(this._doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + class RoughPath { + constructor(d) { + this._position = [0, 0]; + this._first = null; + this.bezierReflectionPoint = null; + this.quadReflectionPoint = null; + this.parsed = new ParsedPath(d); + } + get segments() { + return this.parsed.segments; + } + get closed() { + return this.parsed.closed; + } + get linearPoints() { + if (!this._linearPoints) { + const lp = []; + let points = []; + for (const s of this.parsed.segments) { + const key = s.key.toLowerCase(); + if (key === 'm' || key === 'z') { + if (points.length) { + lp.push(points); + points = []; + } + if (key === 'z') { + continue; + } + } + if (s.point) { + points.push(s.point); + } + } + if (points.length) { + lp.push(points); + points = []; + } + this._linearPoints = lp; + } + return this._linearPoints; + } + get first() { + return this._first; + } + set first(v) { + this._first = v; + } + setPosition(x, y) { + this._position = [x, y]; + if (!this._first) { + this._first = [x, y]; + } + } + get position() { + return this._position; + } + get x() { + return this._position[0]; + } + get y() { + return this._position[1]; + } } - return ops; - } - - _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { - const radOffset = this._getOffset(-0.5, 0.5, o) - (Math.PI / 2); - const points = []; - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) - ]); + // Algorithm as described in https://www.w3.org/TR/SVG/implnote.html + // Code adapted from nsSVGPathDataParser.cpp in Mozilla + // https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 + class RoughArcConverter { + constructor(from, to, radii, angle, largeArcFlag, sweepFlag) { + this._segIndex = 0; + this._numSegs = 0; + this._rx = 0; + this._ry = 0; + this._sinPhi = 0; + this._cosPhi = 0; + this._C = [0, 0]; + this._theta = 0; + this._delta = 0; + this._T = 0; + this._from = from; + if (from[0] === to[0] && from[1] === to[1]) { + return; + } + const radPerDeg = Math.PI / 180; + this._rx = Math.abs(radii[0]); + this._ry = Math.abs(radii[1]); + this._sinPhi = Math.sin(angle * radPerDeg); + this._cosPhi = Math.cos(angle * radPerDeg); + const x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; + const y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; + let root = 0; + const numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; + if (numerator < 0) { + const s = Math.sqrt(1 - (numerator / (this._rx * this._rx * this._ry * this._ry))); + this._rx = this._rx * s; + this._ry = this._ry * s; + root = 0; + } + else { + root = (largeArcFlag === sweepFlag ? -1.0 : 1.0) * + Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); + } + const cxdash = root * this._rx * y1dash / this._ry; + const cydash = -root * this._ry * x1dash / this._rx; + this._C = [0, 0]; + this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; + this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; + this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); + let dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); + if ((!sweepFlag) && (dtheta > 0)) { + dtheta -= 2 * Math.PI; + } + else if (sweepFlag && (dtheta < 0)) { + dtheta += 2 * Math.PI; + } + this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); + this._delta = dtheta / this._numSegs; + this._T = (8 / 3) * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); + } + getNextSegment() { + if (this._segIndex === this._numSegs) { + return null; + } + const cosTheta1 = Math.cos(this._theta); + const sinTheta1 = Math.sin(this._theta); + const theta2 = this._theta + this._delta; + const cosTheta2 = Math.cos(theta2); + const sinTheta2 = Math.sin(theta2); + const to = [ + this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], + this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1] + ]; + const cp1 = [ + this._from[0] + this._T * (-this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), + this._from[1] + this._T * (-this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1) + ]; + const cp2 = [ + to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), + to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2) + ]; + this._theta = theta2; + this._from = [to[0], to[1]]; + this._segIndex++; + return { + cp1: cp1, + cp2: cp2, + to: to + }; + } + calculateVectorAngle(ux, uy, vx, vy) { + const ta = Math.atan2(uy, ux); + const tb = Math.atan2(vy, vx); + if (tb >= ta) + return tb - ta; + return 2 * Math.PI - (ta - tb); + } } - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) - ]); - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), - this._getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) - ]); - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) - ]); - return this._curve(points, null, o); - } - - _curveWithOffset(points, offset, o) { - const ps = []; - ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), - ]); - ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), - ]); - for (let i = 1; i < points.length; i++) { - ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), - ]); - if (i === (points.length - 1)) { - ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), - ]); - } + class PathFitter { + constructor(sets, closed) { + this.sets = sets; + this.closed = closed; + } + fit(simplification) { + const outSets = []; + for (const set of this.sets) { + const length = set.length; + let estLength = Math.floor(simplification * length); + if (estLength < 5) { + if (length <= 5) { + continue; + } + estLength = 5; + } + outSets.push(this.reduce(set, estLength)); + } + let d = ''; + for (const set of outSets) { + for (let i = 0; i < set.length; i++) { + const point = set[i]; + if (i === 0) { + d += 'M' + point[0] + ',' + point[1]; + } + else { + d += 'L' + point[0] + ',' + point[1]; + } + } + if (this.closed) { + d += 'z '; + } + } + return d; + } + distance(p1, p2) { + return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); + } + reduce(set, count) { + if (set.length <= count) { + return set; + } + const points = set.slice(0); + while (points.length > count) { + let minArea = -1; + let minIndex = -1; + for (let i = 1; i < (points.length - 1); i++) { + const a = this.distance(points[i - 1], points[i]); + const b = this.distance(points[i], points[i + 1]); + const c = this.distance(points[i - 1], points[i + 1]); + const s = (a + b + c) / 2.0; + const area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); + if ((minArea < 0) || (area < minArea)) { + minArea = area; + minIndex = i; + } + } + if (minIndex > 0) { + points.splice(minIndex, 1); + } + else { + break; + } + } + return points; + } } - return this._curve(ps, null, o); - } - _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { - const radOffset = strt + this._getOffset(-0.1, 0.1, o); - const points = []; - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle <= stp; angle = angle + increment) { - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) - ]); + class Segment { + constructor(p1, p2) { + this.xi = Number.MAX_VALUE; + this.yi = Number.MAX_VALUE; + this.px1 = p1[0]; + this.py1 = p1[1]; + this.px2 = p2[0]; + this.py2 = p2[1]; + this.a = this.py2 - this.py1; + this.b = this.px1 - this.px2; + this.c = this.px2 * this.py1 - this.px1 * this.py2; + this._undefined = ((this.a === 0) && (this.b === 0) && (this.c === 0)); + } + isUndefined() { + return this._undefined; + } + intersects(otherSegment) { + if (this.isUndefined() || otherSegment.isUndefined()) { + return false; + } + let grad1 = Number.MAX_VALUE; + let grad2 = Number.MAX_VALUE; + let int1 = 0, int2 = 0; + const a = this.a, b = this.b, c = this.c; + if (Math.abs(b) > 0.00001) { + grad1 = -a / b; + int1 = -c / b; + } + if (Math.abs(otherSegment.b) > 0.00001) { + grad2 = -otherSegment.a / otherSegment.b; + int2 = -otherSegment.c / otherSegment.b; + } + if (grad1 === Number.MAX_VALUE) { + if (grad2 === Number.MAX_VALUE) { + if ((-c / a) !== (-otherSegment.c / otherSegment.a)) { + return false; + } + if ((this.py1 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.py2 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py2 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = this.px1; + this.yi = (grad2 * this.xi + int2); + if (((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) || ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001)) { + return false; + } + if (Math.abs(otherSegment.a) < 0.00001) { + if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad2 === Number.MAX_VALUE) { + this.xi = otherSegment.px1; + this.yi = grad1 * this.xi + int1; + if (((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) || ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001)) { + return false; + } + if (Math.abs(a) < 0.00001) { + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad1 === grad2) { + if (int1 !== int2) { + return false; + } + if ((this.px1 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.px2 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px2 <= Math.max(otherSegment.px1, otherSegment.px2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = ((int2 - int1) / (grad1 - grad2)); + this.yi = (grad1 * this.xi + int1); + if (((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) || ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001)) { + return false; + } + return true; + } } - 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 this._curve(points, null, o); - } - _getIntersectingLines(lineCoords, xCoords, yCoords) { - let intersections = []; - var s1 = new RoughSegment(lineCoords[0], lineCoords[1], lineCoords[2], lineCoords[3]); - for (var i = 0; i < xCoords.length; i++) { - let s2 = new RoughSegment(xCoords[i], yCoords[i], xCoords[(i + 1) % xCoords.length], yCoords[(i + 1) % xCoords.length]); - if (s1.compare(s2) == RoughSegmentRelation().INTERSECTS) { - intersections.push([s1.xi, s1.yi]); - } + class HachureIterator { + constructor(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { + this.deltaX = 0; + this.hGap = 0; + this.top = top; + this.bottom = bottom; + this.left = left; + this.right = right; + this.gap = gap; + this.sinAngle = sinAngle; + this.tanAngle = tanAngle; + if (Math.abs(sinAngle) < 0.0001) { + this.pos = left + gap; + } + else if (Math.abs(sinAngle) > 0.9999) { + this.pos = top + gap; + } + else { + this.deltaX = (bottom - top) * Math.abs(tanAngle); + this.pos = left - Math.abs(this.deltaX); + this.hGap = Math.abs(gap / cosAngle); + this.sLeft = new Segment([left, bottom], [left, top]); + this.sRight = new Segment([right, bottom], [right, top]); + } + } + nextLine() { + if (Math.abs(this.sinAngle) < 0.0001) { + if (this.pos < this.right) { + const line = [this.pos, this.top, this.pos, this.bottom]; + this.pos += this.gap; + return line; + } + } + else if (Math.abs(this.sinAngle) > 0.9999) { + if (this.pos < this.bottom) { + const line = [this.left, this.pos, this.right, this.pos]; + this.pos += this.gap; + return line; + } + } + else { + let xLower = this.pos - this.deltaX / 2; + let xUpper = this.pos + this.deltaX / 2; + let yLower = this.bottom; + let yUpper = this.top; + if (this.pos < (this.right + this.deltaX)) { + while (((xLower < this.left) && (xUpper < this.left)) || ((xLower > this.right) && (xUpper > this.right))) { + this.pos += this.hGap; + xLower = this.pos - this.deltaX / 2; + xUpper = this.pos + this.deltaX / 2; + if (this.pos > (this.right + this.deltaX)) { + return null; + } + } + const s = new Segment([xLower, yLower], [xUpper, yUpper]); + if (this.sLeft && s.intersects(this.sLeft)) { + xLower = s.xi; + yLower = s.yi; + } + if (this.sRight && s.intersects(this.sRight)) { + xUpper = s.xi; + yUpper = s.yi; + } + if (this.tanAngle > 0) { + xLower = this.right - (xLower - this.left); + xUpper = this.right - (xUpper - this.left); + } + const line = [xLower, yLower, xUpper, yUpper]; + this.pos += this.hGap; + return line; + } + } + return null; + } } - return intersections; - } -} -self._roughScript = self.document && self.document.currentScript && self.document.currentScript.src; - -class RoughGenerator { - constructor(config, canvas) { - this.config = config || {}; - this.canvas = canvas; - this.defaultOptions = { - maxRandomnessOffset: 2, - roughness: 1, - bowing: 1, - stroke: '#000', - strokeWidth: 1, - curveTightness: 0, - curveStepCount: 9, - fill: null, - fillStyle: 'hachure', - fillWeight: -1, - hachureAngle: -41, - hachureGap: -1 - }; - if (this.config.options) { - this.defaultOptions = this._options(this.config.options); + class HachureFiller { + constructor(renderer) { + this.renderer = renderer; + } + fillPolygon(points, o) { + return this._fillPolygon(points, o); + } + fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o); + } + _fillPolygon(points, o, connectEnds = false) { + let ops = []; + if (points && points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const cosAngle = Math.cos(hachureAngle); + const sinAngle = Math.sin(hachureAngle); + const tanAngle = Math.tan(hachureAngle); + const it = new HachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); + let rect; + let prevPoint = null; + while ((rect = it.nextLine()) != null) { + const lines = this.getIntersectingLines(rect, points); + for (let i = 0; i < lines.length; i++) { + if (i < (lines.length - 1)) { + const p1 = lines[i]; + const p2 = lines[i + 1]; + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + } + } + } + return { type: 'fillSketch', ops }; + } + _fillEllipse(cx, cy, width, height, o, connectEnds = false) { + let ops = []; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.renderer.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.renderer.getOffset(-ry * 0.05, ry * 0.05, o); + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap <= 0) { + gap = o.strokeWidth * 4; + } + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const tanAngle = Math.tan(hachureAngle); + const aspectRatio = ry / rx; + const hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); + const sinAnglePrime = aspectRatio * tanAngle / hyp; + const cosAnglePrime = 1 / hyp; + const gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); + let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); + let prevPoint = null; + for (let xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { + halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); + const p1 = this.affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + const p2 = this.affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + return { type: 'fillSketch', ops }; + } + getIntersectingLines(line, points) { + const intersections = []; + const s1 = new Segment([line[0], line[1]], [line[2], line[3]]); + for (let i = 0; i < points.length; i++) { + const s2 = new Segment(points[i], points[(i + 1) % points.length]); + if (s1.intersects(s2)) { + intersections.push([s1.xi, s1.yi]); + } + } + return intersections; + } + affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { + const A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; + const B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; + const C = cosAnglePrime; + const D = sinAnglePrime; + const E = -R * sinAnglePrime; + const F = R * cosAnglePrime; + return [ + A + C * x + D * y, + B + E * x + F * y + ]; + } } - } - - _options(options) { - return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; - } - - _drawable(shape, sets, options) { - return { shape, sets: sets || [], options: options || this.defaultOptions }; - } - get lib() { - if (!this._renderer) { - if (self && self.workly && this.config.async && (!this.config.noWorker)) { - const tos = Function.prototype.toString; - const worklySource = this.config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; - const rendererSource = this.config.roughURL || self._roughScript; - if (rendererSource && worklySource) { - let code = `importScripts('${worklySource}', '${rendererSource}');\nworkly.expose(self.rough.createRenderer());`; - let ourl = URL.createObjectURL(new Blob([code])); - this._renderer = workly.proxy(ourl); - } else { - this._renderer = new RoughRenderer(); - } - } else { - this._renderer = new RoughRenderer(); - } + class ZigZagFiller extends HachureFiller { + fillPolygon(points, o) { + return this._fillPolygon(points, o, true); + } + fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o, true); + } } - return this._renderer; - } - - line(x1, y1, x2, y2, options) { - const o = this._options(options); - return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); - } - rectangle(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - const xc = [x, x + width, x + width, x]; - const yc = [y, y, y + height, y + height]; - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(this.lib.hachureFillShape(xc, yc, o)); - } + 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; + } + fillEllipse(cx, cy, width, height, o) { + const set = this._fillEllipse(cx, cy, width, height, o); + const o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + const set2 = this._fillEllipse(cx, cy, width, height, o2); + set.ops = set.ops.concat(set2.ops); + return set; + } } - paths.push(this.lib.rectangle(x, y, width, height, o)); - return this._drawable('rectangle', paths, o); - } - ellipse(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - const shape = this.lib.ellipse(x, y, width, height, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(this.lib.hachureFillEllipse(x, y, width, height, o)); - } + const fillers = {}; + function getFiller(renderer, o) { + let fillerName = o.fillStyle || 'hachure'; + if (!fillers[fillerName]) { + switch (fillerName) { + case 'zigzag': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagFiller(renderer); + } + break; + case 'cross-hatch': + if (!fillers[fillerName]) { + fillers[fillerName] = new HatchFiller(renderer); + } + break; + case 'hachure': + default: + fillerName = 'hachure'; + if (!fillers[fillerName]) { + fillers[fillerName] = new HachureFiller(renderer); + } + break; + } + } + return fillers[fillerName]; } - paths.push(this.lib.ellipse(x, y, width, height, o)); - return this._drawable('ellipse', paths, o); - } - - circle(x, y, diameter, options) { - let ret = this.ellipse(x, y, diameter, diameter, options); - ret.shape = 'circle'; - return ret; - } - - linearPath(points, options) { - const o = this._options(options); - return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); - } - polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - let xc = [], yc = []; - for (let p of points) { - xc.push(p[0]); - yc.push(p[1]); - } - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(this.lib.hachureFillShape(xc, yc, o)); - } + class RoughRenderer { + line(x1, y1, x2, y2, o) { + const ops = this.doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops }; + } + linearPath(points, close, o) { + const len = (points || []).length; + if (len > 2) { + let ops = []; + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this.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 this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + polygon(points, o) { + return this.linearPath(points, true, o); + } + rectangle(x, y, width, height, o) { + const points = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return this.polygon(points, o); + } + curve(points, o) { + const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + ellipse(x, y, width, height, o) { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); + const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + 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 += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, 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 o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } + else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; + } + svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; + } + solidFillPolygon(points, o) { + const ops = []; + if (PointerEvent.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; + } + patternFillPolygon(points, o) { + const filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + patternFillEllipse(cx, cy, width, height, o) { + const filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + 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 += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, 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 this.patternFillPolygon(points, o); + } + /// + getOffset(min, max, ops) { + return ops.roughness * ((Math.random() * (max - min)) + min); + } + doubleLine(x1, y1, x2, y2, o) { + const o1 = this._line(x1, y1, x2, y2, o, true, false); + const o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + _line(x1, y1, x2, y2, o, move, overlay) { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); + const ops = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-halfOffset, halfOffset, o), + y1 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-offset, offset, o), + y1 + this.getOffset(-offset, offset, o) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + x2 + this.getOffset(-halfOffset, halfOffset, o), + y2 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + x2 + this.getOffset(-offset, offset, o), + y2 + this.getOffset(-offset, offset, o) + ] + }); + } + return ops; + } + _curve(points, closePoint, o) { + const len = points.length; + let 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] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, 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 = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return this._curve(points, null, o); + } + _curveWithOffset(points, offset, o) { + const ps = []; + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + } + } + return this._curve(ps, null, o); + } + _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + const radOffset = strt + this.getOffset(-0.1, 0.1, o); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, 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 this._curve(points, null, o); + } + _bezierTo(x1, y1, x2, y2, x, y, path, o) { + const ops = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } + else { + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), + x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + _processSegment(path, seg, prevSeg, o) { + let ops = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } } - paths.push(this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - arc(x, y, width, height, start, stop, closed, options) { - const o = this._options(options); - const paths = []; - if (closed && o.fill) { - if (o.fillStyle === 'solid') { - let shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(this.lib.hachureFillArc(x, y, width, height, start, stop, o)); - } + const hasSelf = typeof self !== 'undefined'; + class RoughGenerator { + constructor(config, surface) { + this.defaultOptions = { + maxRandomnessOffset: 2, + roughness: 1, + bowing: 1, + stroke: '#000', + strokeWidth: 1, + curveTightness: 0, + curveStepCount: 9, + fill: null, + fillStyle: 'hachure', + fillWeight: -1, + hachureAngle: -41, + hachureGap: -1 + }; + this.config = config || {}; + this.surface = surface; + if (this.config.options) { + this.defaultOptions = this._options(this.config.options); + } + } + _options(options) { + return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; + } + _drawable(shape, sets, options) { + return { shape, sets: sets || [], options: options || this.defaultOptions }; + } + get lib() { + if (!this.renderer) { + this.renderer = new RoughRenderer(); + } + return this.renderer; + } + getCanvasSize() { + const val = (w) => { + if (w && typeof w === 'object') { + if (w.baseVal && w.baseVal.value) { + return w.baseVal.value; + } + } + return w || 100; + }; + if (this.surface) { + return [val(this.surface.width), val(this.surface.height)]; + } + return [100, 100]; + } + computePathSize(d) { + let size = [0, 0]; + if (hasSelf && self.document) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = self.document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + self.document.body.appendChild(svg); + const bb = pathNode.getBBox(); + if (bb) { + size[0] = bb.width || 0; + size[1] = bb.height || 0; + } + self.document.body.removeChild(svg); + } + catch (err) { } + } + const canvasSize = this.getCanvasSize(); + if (!(size[0] * size[1])) { + size = canvasSize; + } + size[0] = Math.min(size[0], canvasSize[0]); + size[1] = Math.min(size[1], canvasSize[1]); + return size; + } + line(x1, y1, x2, y2, options) { + const o = this._options(options); + return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + } + rectangle(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + ellipse(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(this.lib.ellipse(x, y, width, height, o)); + return this._drawable('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._options(options); + return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + } + polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + arc(x, y, width, height, start, stop, closed = false, options) { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + curve(points, options) { + const o = this._options(options); + return this._drawable('curve', [this.lib.curve(points, o)], o); + } + path(d, options) { + const o = this._options(options); + const paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } + else { + const size = this.computePathSize(d); + const points = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } + 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: 'none' + }; + break; + case 'fillPath': + path = { + d: this.opsToPath(drawing), + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'fillSketch': + path = this.fillSketch(drawing, o); + break; + case 'path2Dfill': + path = { + d: drawing.path || '', + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'path2Dpattern': { + const size = drawing.size; + const pattern = { + x: 0, y: 0, width: 1, height: 1, + viewBox: `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`, + patternUnits: 'objectBoundingBox', + path: this.fillSketch(drawing, o) + }; + path = { + d: drawing.path, + stroke: 'none', + strokeWidth: 0, + pattern: pattern + }; + 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 || 'none', + strokeWidth: fweight, + fill: 'none' + }; + } + 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 'qcurveTo': + path += `Q${data[0]} ${data[1]}, ${data[2]} ${data[3]} `; + break; + case 'lineTo': + path += `L${data[0]} ${data[1]} `; + break; + } + } + return path.trim(); + } } - paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); - return this._drawable('arc', paths, o); - } - curve(points, options) { - const o = this._options(options); - return this._drawable('curve', [this.lib.curve(points, o)], o); - } - - path(d, options) { - const o = this._options(options); - const paths = []; - if (!d) { - return this._drawable('path', paths, o); - } - if (o.fill) { - if (o.fillStyle === 'solid') { - let shape = { type: 'path2Dfill', path: d }; - paths.push(shape); - } else { - const size = this._computePathSize(d); - let xc = [0, size[0], size[0], 0]; - let yc = [0, 0, size[1], size[1]]; - let shape = this.lib.hachureFillShape(xc, yc, o); - shape.type = 'path2Dpattern'; - shape.size = size; - shape.path = d; - paths.push(shape); - } + const hasDocument = typeof document !== 'undefined'; + class RoughCanvas { + constructor(canvas, config) { + this.canvas = canvas; + this.ctx = this.canvas.getContext('2d'); + this.gen = new RoughGenerator(config || null, this.canvas); + } + get generator() { + return this.gen; + } + static createRenderer() { + return new RoughRenderer(); + } + 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; + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const ctx = this.ctx; + for (const drawing of sets) { + switch (drawing.type) { + case 'path': + ctx.save(); + ctx.strokeStyle = o.stroke; + ctx.lineWidth = o.strokeWidth; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillPath': + ctx.save(); + ctx.fillStyle = o.fill || ''; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillSketch': + this.fillSketch(ctx, drawing, o); + break; + case 'path2Dfill': { + this.ctx.save(); + this.ctx.fillStyle = o.fill || ''; + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + break; + } + case 'path2Dpattern': { + if (hasDocument) { + const size = drawing.size; + const hcanvas = document.createElement('canvas'); + const hcontext = hcanvas.getContext('2d'); + const bbox = this.computeBBox(drawing.path); + if (bbox && (bbox.width || bbox.height)) { + hcanvas.width = this.canvas.width; + hcanvas.height = this.canvas.height; + hcontext.translate(bbox.x || 0, bbox.y || 0); + } + else { + hcanvas.width = size[0]; + hcanvas.height = size[1]; + } + this.fillSketch(hcontext, drawing, o); + this.ctx.save(); + this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + } + break; + } + } + } + } + computeBBox(d) { + if (hasDocument) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + document.body.appendChild(svg); + const bbox = pathNode.getBBox(); + document.body.removeChild(svg); + return bbox; + } + catch (err) { } + } + return null; + } + fillSketch(ctx, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + ctx.save(); + ctx.strokeStyle = o.fill || ''; + ctx.lineWidth = fweight; + this._drawToContext(ctx, drawing); + ctx.restore(); + } + _drawToContext(ctx, drawing) { + 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 'qcurveTo': + ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); + break; + case 'lineTo': + ctx.lineTo(data[0], data[1]); + break; + } + } + if (drawing.type === 'fillPath') { + ctx.fill(); + } + else { + ctx.stroke(); + } + } } - paths.push(this.lib.svgPath(d, o)); - return this._drawable('path', paths, o); - } - 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: 'none' - }; - break; - case 'fillPath': - path = { - d: this.opsToPath(drawing), - stroke: 'none', - strokeWidth: 0, - fill: o.fill - }; - break; - case 'fillSketch': - path = this._fillSketch(drawing, o); - break; - case 'path2Dfill': - path = { - d: drawing.path, - stroke: 'none', - strokeWidth: 0, - fill: o.fill - }; - break; - case 'path2Dpattern': { - const size = drawing.size; - const pattern = { - x: 0, y: 0, width: 1, height: 1, - viewBox: `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`, - patternUnits: 'objectBoundingBox', - path: this._fillSketch(drawing, o) - }; - path = { - d: drawing.path, - stroke: 'none', - strokeWidth: 0, - pattern: pattern - }; - break; - } - } - if (path) { - paths.push(path); - } + class RoughGeneratorAsync extends RoughGenerator { + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const o = this._options(options); + return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(await this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(await this.lib.ellipse(x, y, width, height, o)); + return this._drawable('ellipse', paths, o); + } + // @ts-ignore + async circle(x, y, diameter, options) { + const ret = await this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + // @ts-ignore + async linearPath(points, options) { + const o = this._options(options); + return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); + } + // @ts-ignore + async polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(await this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(await this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + // @ts-ignore + async curve(points, options) { + const o = this._options(options); + return this._drawable('curve', [await this.lib.curve(points, o)], o); + } + // @ts-ignore + async path(d, options) { + const o = this._options(options); + const paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } + else { + const size = this.computePathSize(d); + const points = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(await this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } } - return paths; - } - _fillSketch(drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; + class RoughCanvasAsync extends RoughCanvas { + constructor(canvas, config) { + super(canvas, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.canvas); + } + // @ts-ignore + get generator() { + return this.genAsync; + } + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const d = await this.genAsync.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const d = await this.genAsync.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + async circle(x, y, diameter, options) { + const d = await this.genAsync.circle(x, y, diameter, options); + this.draw(d); + return d; + } + // @ts-ignore + async linearPath(points, options) { + const d = await this.genAsync.linearPath(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async polygon(points, options) { + const d = await this.genAsync.polygon(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + // @ts-ignore + async curve(points, options) { + const d = await this.genAsync.curve(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async path(d, options) { + const drawing = await this.genAsync.path(d, options); + this.draw(drawing); + return drawing; + } } - return { - d: this.opsToPath(drawing), - stroke: o.fill, - strokeWidth: fweight, - fill: 'none' - }; - } - opsToPath(drawing) { - let path = ''; - for (let 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 'qcurveTo': - path += `Q${data[0]} ${data[1]}, ${data[2]} ${data[3]} `; - break; - case 'lineTo': - path += `L${data[0]} ${data[1]} `; - break; - } + const hasDocument$1 = typeof document !== 'undefined'; + class RoughSVG { + constructor(svg, config) { + this.svg = svg; + this.gen = new RoughGenerator(config || null, this.svg); + } + get generator() { + return this.gen; + } + static createRenderer() { + return new RoughRenderer(); + } + get defs() { + if (hasDocument$1) { + if (!this._defs) { + const doc = this.svg.ownerDocument || document; + const dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); + if (this.svg.firstChild) { + this.svg.insertBefore(dnode, this.svg.firstChild); + } + else { + this.svg.appendChild(dnode); + } + this._defs = dnode; + } + } + return this._defs || null; + } + 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); + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const doc = this.svg.ownerDocument || (hasDocument$1 && document); + const g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); + for (const drawing of sets) { + let path = null; + switch (drawing.type) { + case 'path': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.stroke; + path.style.strokeWidth = o.strokeWidth + ''; + path.style.fill = 'none'; + break; + } + case 'fillPath': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'fillSketch': { + path = this.fillSketch(doc, drawing, o); + break; + } + case 'path2Dfill': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'path2Dpattern': { + const size = drawing.size; + const pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); + const id = `rough-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999))}`; + pattern.setAttribute('id', id); + pattern.setAttribute('x', '0'); + pattern.setAttribute('y', '0'); + pattern.setAttribute('width', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('viewBox', `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`); + pattern.setAttribute('patternUnits', 'objectBoundingBox'); + const patternPath = this.fillSketch(doc, drawing, o); + pattern.appendChild(patternPath); + this.defs.appendChild(pattern); + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = `url(#${id})`; + break; + } + } + if (path) { + g.appendChild(path); + } + } + return g; + } + opsToPath(drawing) { + return this.gen.opsToPath(drawing); + } + fillSketch(doc, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.fill; + path.style.strokeWidth = fweight + ''; + path.style.fill = 'none'; + return path; + } } - return path.trim(); - } - _computePathSize(d) { - let size = [0, 0]; - if (self.document) { - try { - const ns = "http://www.w3.org/2000/svg"; - let svg = self.document.createElementNS(ns, "svg"); - svg.setAttribute("width", "0"); - svg.setAttribute("height", "0"); - let pathNode = self.document.createElementNS(ns, "path"); - pathNode.setAttribute('d', d); - svg.appendChild(pathNode); - self.document.body.appendChild(svg); - let bb = pathNode.getBBox(); - if (bb) { - size[0] = bb.width || 0; - size[1] = bb.height || 0; - } - self.document.body.removeChild(svg); - } catch (err) { } - } - const canvasSize = this._canvasSize(); - if (!(size[0] * size[1])) { - size = canvasSize; + class RoughSVGAsync extends RoughSVG { + constructor(svg, config) { + super(svg, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.svg); + } + // @ts-ignore + get generator() { + return this.genAsync; + } + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + return this.draw(d); + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const d = await this.genAsync.rectangle(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const d = await this.genAsync.ellipse(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + async circle(x, y, diameter, options) { + const d = await this.genAsync.circle(x, y, diameter, options); + return this.draw(d); + } + // @ts-ignore + async linearPath(points, options) { + const d = await this.genAsync.linearPath(points, options); + return this.draw(d); + } + // @ts-ignore + async polygon(points, options) { + const d = await this.genAsync.polygon(points, options); + return this.draw(d); + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + // @ts-ignore + async curve(points, options) { + const d = await this.genAsync.curve(points, options); + return this.draw(d); + } + // @ts-ignore + async path(d, options) { + const drawing = await this.genAsync.path(d, options); + return this.draw(drawing); + } } - size[0] = Math.min(size[0], canvasSize[0]); - size[1] = Math.min(size[1], canvasSize[1]); - return size; - } - _canvasSize() { - const val = w => { - if (w) { - if (typeof w === 'object') { - if (w.baseVal && w.baseVal.value) { - return w.baseVal.value; - } - } - } - return w || 100; + var rough = { + canvas(canvas, config) { + if (config && config.async) { + return new RoughCanvasAsync(canvas, config); + } + return new RoughCanvas(canvas, config); + }, + svg(svg, config) { + if (config && config.async) { + return new RoughSVGAsync(svg, config); + } + return new RoughSVG(svg, config); + }, + createRenderer() { + return RoughCanvas.createRenderer(); + }, + generator(config, surface) { + if (config && config.async) { + return new RoughGeneratorAsync(config, surface); + } + return new RoughGenerator(config, surface); + } }; - return this.canvas ? [val(this.canvas.width), val(this.canvas.height)] : [100, 100]; - } -} - -class RoughGeneratorAsync extends RoughGenerator { - async line(x1, y1, x2, y2, options) { - const o = this._options(options); - return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); - } - - async rectangle(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - const xc = [x, x + width, x + width, x]; - const yc = [y, y, y + height, y + height]; - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(await this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(await this.lib.rectangle(x, y, width, height, o)); - return this._drawable('rectangle', paths, o); - } - - async ellipse(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - const shape = await this.lib.ellipse(x, y, width, height, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(await this.lib.hachureFillEllipse(x, y, width, height, o)); - } - } - paths.push(await this.lib.ellipse(x, y, width, height, o)); - return this._drawable('ellipse', paths, o); - } - - async circle(x, y, diameter, options) { - let ret = await this.ellipse(x, y, diameter, diameter, options); - ret.shape = 'circle'; - return ret; - } - - async linearPath(points, options) { - const o = this._options(options); - return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); - } - - async polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - let xc = [], yc = []; - for (let p of points) { - xc.push(p[0]); - yc.push(p[1]); - } - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(await this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(await this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - - async arc(x, y, width, height, start, stop, closed, options) { - const o = this._options(options); - const paths = []; - if (closed && o.fill) { - if (o.fillStyle === 'solid') { - let shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(await this.lib.hachureFillArc(x, y, width, height, start, stop, o)); - } - } - paths.push(await this.lib.arc(x, y, width, height, start, stop, closed, true, o)); - return this._drawable('arc', paths, o); - } - - async curve(points, options) { - const o = this._options(options); - return this._drawable('curve', [await this.lib.curve(points, o)], o); - } - - async path(d, options) { - const o = this._options(options); - const paths = []; - if (!d) { - return this._drawable('path', paths, o); - } - if (o.fill) { - if (o.fillStyle === 'solid') { - let shape = { type: 'path2Dfill', path: d }; - paths.push(shape); - } else { - const size = this._computePathSize(d); - let xc = [0, size[0], size[0], 0]; - let yc = [0, 0, size[1], size[1]]; - let shape = await this.lib.hachureFillShape(xc, yc, o); - shape.type = 'path2Dpattern'; - shape.size = size; - shape.path = d; - paths.push(shape); - } - } - paths.push(await this.lib.svgPath(d, o)); - return this._drawable('path', paths, o); - } -} - -class RoughCanvas { - constructor(canvas, config) { - this.canvas = canvas; - this.ctx = this.canvas.getContext("2d"); - this._init(config); - } - - _init(config) { - this.gen = new RoughGenerator(config, this.canvas); - } - - get generator() { - return this.gen; - } - - static createRenderer() { - return new RoughRenderer(); - } - - line(x1, y1, x2, y2, options) { - let d = this.gen.line(x1, y1, x2, y2, options); - this.draw(d); - return d; - } - - rectangle(x, y, width, height, options) { - let d = this.gen.rectangle(x, y, width, height, options); - this.draw(d); - return d; - } - - ellipse(x, y, width, height, options) { - let d = this.gen.ellipse(x, y, width, height, options); - this.draw(d); - return d; - } - - circle(x, y, diameter, options) { - let d = this.gen.circle(x, y, diameter, options); - this.draw(d); - return d; - } - - linearPath(points, options) { - let d = this.gen.linearPath(points, options); - this.draw(d); - return d; - } - - polygon(points, options) { - let d = this.gen.polygon(points, options); - this.draw(d); - return d; - } - - arc(x, y, width, height, start, stop, closed, options) { - let d = this.gen.arc(x, y, width, height, start, stop, closed, options); - this.draw(d); - return d; - } - - curve(points, options) { - let d = this.gen.curve(points, options); - this.draw(d); - return d; - } - - path(d, options) { - let drawing = this.gen.path(d, options); - this.draw(drawing); - return drawing; - } - - draw(drawable) { - let sets = drawable.sets || []; - let o = drawable.options || this.gen.defaultOptions; - let ctx = this.ctx; - for (let drawing of sets) { - switch (drawing.type) { - case 'path': - ctx.save(); - ctx.strokeStyle = o.stroke; - ctx.lineWidth = o.strokeWidth; - this._drawToContext(ctx, drawing); - ctx.restore(); - break; - case 'fillPath': - ctx.save(); - ctx.fillStyle = o.fill; - this._drawToContext(ctx, drawing, o); - ctx.restore(); - break; - case 'fillSketch': - this._fillSketch(ctx, drawing, o); - break; - case 'path2Dfill': { - this.ctx.save(); - this.ctx.fillStyle = o.fill; - let p2d = new Path2D(drawing.path); - this.ctx.fill(p2d); - this.ctx.restore(); - break; - } - case 'path2Dpattern': { - let size = drawing.size; - const hcanvas = document.createElement('canvas'); - const hcontext = hcanvas.getContext("2d"); - let bbox = this._computeBBox(drawing.path); - if (bbox && (bbox.width || bbox.height)) { - hcanvas.width = this.canvas.width; - hcanvas.height = this.canvas.height; - hcontext.translate(bbox.x || 0, bbox.y || 0); - } else { - hcanvas.width = size[0]; - hcanvas.height = size[1]; - } - this._fillSketch(hcontext, drawing, o); - this.ctx.save(); - this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); - let p2d = new Path2D(drawing.path); - this.ctx.fill(p2d); - this.ctx.restore(); - break; - } - } - } - } - - _computeBBox(d) { - if (self.document) { - try { - const ns = "http://www.w3.org/2000/svg"; - let svg = self.document.createElementNS(ns, "svg"); - svg.setAttribute("width", "0"); - svg.setAttribute("height", "0"); - let pathNode = self.document.createElementNS(ns, "path"); - pathNode.setAttribute('d', d); - svg.appendChild(pathNode); - self.document.body.appendChild(svg); - let bbox = pathNode.getBBox(); - self.document.body.removeChild(svg); - return bbox; - } catch (err) { } - } - return null; - } - - _fillSketch(ctx, drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - ctx.save(); - ctx.strokeStyle = o.fill; - ctx.lineWidth = fweight; - this._drawToContext(ctx, drawing); - ctx.restore(); - } - - _drawToContext(ctx, drawing) { - ctx.beginPath(); - for (let 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 'qcurveTo': - ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); - break; - case 'lineTo': - ctx.lineTo(data[0], data[1]); - break; - } - } - if (drawing.type === 'fillPath') { - ctx.fill(); - } else { - ctx.stroke(); - } - } -} - -class RoughCanvasAsync extends RoughCanvas { - _init(config) { - this.gen = new RoughGeneratorAsync(config, this.canvas); - } - - async line(x1, y1, x2, y2, options) { - let d = await this.gen.line(x1, y1, x2, y2, options); - this.draw(d); - return d; - } - - async rectangle(x, y, width, height, options) { - let d = await this.gen.rectangle(x, y, width, height, options); - this.draw(d); - return d; - } - - async ellipse(x, y, width, height, options) { - let d = await this.gen.ellipse(x, y, width, height, options); - this.draw(d); - return d; - } - - async circle(x, y, diameter, options) { - let d = await this.gen.circle(x, y, diameter, options); - this.draw(d); - return d; - } - - async linearPath(points, options) { - let d = await this.gen.linearPath(points, options); - this.draw(d); - return d; - } - - async polygon(points, options) { - let d = await this.gen.polygon(points, options); - this.draw(d); - return d; - } - - async arc(x, y, width, height, start, stop, closed, options) { - let d = await this.gen.arc(x, y, width, height, start, stop, closed, options); - this.draw(d); - return d; - } - - async curve(points, options) { - let d = await this.gen.curve(points, options); - this.draw(d); - return d; - } - - async path(d, options) { - let drawing = await this.gen.path(d, options); - this.draw(drawing); - return drawing; - } -} - -class RoughSVG { - constructor(svg, config) { - this.svg = svg; - this._init(config); - } - - _init(config) { - this.gen = new RoughGenerator(config, this.svg); - } - - get generator() { - return this.gen; - } - - get defs() { - if (!this._defs) { - let doc = this.svg.ownerDocument || document; - let dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); - if (this.svg.firstChild) { - this.svg.insertBefore(dnode, this.svg.firstChild); - } else { - this.svg.appendChild(dnode); - } - this._defs = dnode; - } - return this._defs; - } - - line(x1, y1, x2, y2, options) { - let d = this.gen.line(x1, y1, x2, y2, options); - return this.draw(d); - } - - rectangle(x, y, width, height, options) { - let d = this.gen.rectangle(x, y, width, height, options); - return this.draw(d); - } - - ellipse(x, y, width, height, options) { - let d = this.gen.ellipse(x, y, width, height, options); - return this.draw(d); - } - - circle(x, y, diameter, options) { - let d = this.gen.circle(x, y, diameter, options); - return this.draw(d); - } - - linearPath(points, options) { - let d = this.gen.linearPath(points, options); - return this.draw(d); - } - - polygon(points, options) { - let d = this.gen.polygon(points, options); - return this.draw(d); - } - - arc(x, y, width, height, start, stop, closed, options) { - let d = this.gen.arc(x, y, width, height, start, stop, closed, options); - return this.draw(d); - } - - curve(points, options) { - let d = this.gen.curve(points, options); - return this.draw(d); - } - - path(d, options) { - let drawing = this.gen.path(d, options); - return this.draw(drawing); - } - - draw(drawable) { - let sets = drawable.sets || []; - let o = drawable.options || this.gen.defaultOptions; - let doc = this.svg.ownerDocument || document; - let g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); - for (let drawing of sets) { - let path = null; - switch (drawing.type) { - case 'path': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = o.stroke; - path.style.strokeWidth = o.strokeWidth; - path.style.fill = 'none'; - break; - } - case 'fillPath': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = o.fill; - break; - } - case 'fillSketch': { - path = this._fillSketch(doc, drawing, o); - break; - } - case 'path2Dfill': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', drawing.path); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = o.fill; - break; - } - case 'path2Dpattern': { - const size = drawing.size; - const pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); - const id = `rough-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999))}`; - pattern.setAttribute('id', id); - pattern.setAttribute('x', 0); - pattern.setAttribute('y', 0); - pattern.setAttribute('width', 1); - pattern.setAttribute('height', 1); - pattern.setAttribute('height', 1); - pattern.setAttribute('viewBox', `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`); - pattern.setAttribute('patternUnits', 'objectBoundingBox'); - const patternPath = this._fillSketch(doc, drawing, o); - pattern.appendChild(patternPath); - this.defs.appendChild(pattern); - - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', drawing.path); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = `url(#${id})`; - break; - } - } - if (path) { - g.appendChild(path); - } - } - return g; - } - - _fillSketch(doc, drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - let path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = o.fill; - path.style.strokeWidth = fweight; - path.style.fill = 'none'; - return path; - } - - _opsToPath(drawing) { - return this.gen.opsToPath(drawing); - } -} - -class RoughSVGAsync extends RoughSVG { - _init(config) { - this.gen = new RoughGeneratorAsync(config, this.svg); - } - - async line(x1, y1, x2, y2, options) { - let d = await this.gen.line(x1, y1, x2, y2, options); - return this.draw(d); - } - - async rectangle(x, y, width, height, options) { - let d = await this.gen.rectangle(x, y, width, height, options); - return this.draw(d); - } - - async ellipse(x, y, width, height, options) { - let d = await this.gen.ellipse(x, y, width, height, options); - return this.draw(d); - } - - async circle(x, y, diameter, options) { - let d = await this.gen.circle(x, y, diameter, options); - return this.draw(d); - } - - async linearPath(points, options) { - let d = await this.gen.linearPath(points, options); - return this.draw(d); - } - - async polygon(points, options) { - let d = await this.gen.polygon(points, options); - return this.draw(d); - } - - async arc(x, y, width, height, start, stop, closed, options) { - let d = await this.gen.arc(x, y, width, height, start, stop, closed, options); - return this.draw(d); - } - - async curve(points, options) { - let d = await this.gen.curve(points, options); - return this.draw(d); - } - - async path(d, options) { - let drawing = await this.gen.path(d, options); - return this.draw(drawing); - } -} - -var index = { - canvas(canvas, config) { - if (config && config.async) { - return new RoughCanvasAsync(canvas, config); - } - return new RoughCanvas(canvas, config); - }, - svg(svg, config) { - if (config && config.async) { - return new RoughSVGAsync(svg, config); - } - return new RoughSVG(svg, config); - }, - createRenderer() { - return RoughCanvas.createRenderer(); - }, - generator(config, size) { - if (config && config.async) { - return new RoughGeneratorAsync(config, size); - } - return new RoughGenerator(config, size); - } -}; -return index; + return rough; }))); diff --git a/dist/rough.umd.min.js b/dist/rough.umd.min.js index 84efa54..46cd0bf 100644 --- a/dist/rough.umd.min.js +++ b/dist/rough.umd.min.js @@ -1 +1 @@ -(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?module.exports=b():'function'==typeof define&&define.amd?define(b):a.rough=b()})(this,function(){'use strict';function a(){return{LEFT:0,RIGHT:1,INTERSECTS:2,AHEAD:3,BEHIND:4,SEPARATE:5,UNDEFINED:6}}var b=Math.round,c=Math.tan,d=Math.pow,e=Math.floor,f=Math.cos,g=Math.sin,h=Math.PI,j=Math.sqrt,k=Math.max,l=Math.min,i=Math.abs,m=Number.MAX_VALUE;class n{constructor(b,c,d,e){this.RoughSegmentRelationConst=a(),this.px1=b,this.py1=c,this.px2=d,this.py2=e,this.xi=m,this.yi=m,this.a=e-c,this.b=b-d,this.c=d*c-b*e,this._undefined=0==this.a&&0==this.b&&0==this.c}isUndefined(){return this._undefined}compare(d){if(this.isUndefined()||d.isUndefined())return this.RoughSegmentRelationConst.UNDEFINED;var e=m,f=m,g=0,h=0,j=this.a,n=this.b,b=this.c;return(1e-5=l(d.py1,d.py2)&&this.py1<=k(d.py1,d.py2)?(this.xi=this.px1,this.yi=this.py1,this.RoughSegmentRelationConst.INTERSECTS):this.py2>=l(d.py1,d.py2)&&this.py2<=k(d.py1,d.py2)?(this.xi=this.px2,this.yi=this.py2,this.RoughSegmentRelationConst.INTERSECTS):this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.SEPARATE:(this.xi=this.px1,this.yi=f*this.xi+h,-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(d.py1-this.yi)*(this.yi-d.py2)?this.RoughSegmentRelationConst.SEPARATE:1e-5>i(d.a)?-1e-5>(d.px1-this.xi)*(this.xi-d.px2)?this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.INTERSECTS:this.RoughSegmentRelationConst.INTERSECTS):f==m?(this.xi=d.px1,this.yi=e*this.xi+g,-1e-5>(d.py1-this.yi)*(this.yi-d.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2)?this.RoughSegmentRelationConst.SEPARATE:1e-5>i(j)?-1e-5>(this.px1-this.xi)*(this.xi-this.px2)?this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.INTERSECTS:this.RoughSegmentRelationConst.INTERSECTS):e==f?g==h?this.px1>=l(d.px1,d.px2)&&this.px1<=k(d.py1,d.py2)?(this.xi=this.px1,this.yi=this.py1,this.RoughSegmentRelationConst.INTERSECTS):this.px2>=l(d.px1,d.px2)&&this.px2<=k(d.px1,d.px2)?(this.xi=this.px2,this.yi=this.py2,this.RoughSegmentRelationConst.INTERSECTS):this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.SEPARATE:(this.xi=(h-g)/(e-f),this.yi=e*this.xi+g,-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(d.px1-this.xi)*(this.xi-d.px2)?this.RoughSegmentRelationConst.SEPARATE:this.RoughSegmentRelationConst.INTERSECTS)}getLength(){return this._getLength(this.px1,this.py1,this.px2,this.py2)}_getLength(a,b,c,d){var e=c-a,f=d-b;return j(e*e+f*f)}}class p{constructor(a,b,c,d,e,f,g,h){this.top=a,this.bottom=b,this.left=c,this.right=d,this.gap=e,this.sinAngle=f,this.tanAngle=h,1e-4>i(f)?this.pos=c+e:.9999i(this.sinAngle)){if(this.posthis.right&&c>this.right;)if(this.pos+=this.hGap,b=this.pos-this.deltaX/2,c=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;let f=new n(b,d,c,e);f.compare(this.sLeft)==a().INTERSECTS&&(b=f.xi,d=f.yi),f.compare(this.sRight)==a().INTERSECTS&&(c=f.xi,e=f.yi),0p){let a=j(1-p/(this._rx*this._rx*this._ry*this._ry));this._rx*=a,this._ry*=a,m=0}else m=(e==k?-1:1)*j(p/(this._rx*this._rx*o*o+this._ry*this._ry*n*n));let q=m*this._rx*o/this._ry,r=-m*this._ry*n/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*q-this._sinPhi*r+(a[0]+b[0])/2,this._C[1]=this._sinPhi*q+this._cosPhi*r+(a[1]+b[1])/2,this._theta=this.calculateVectorAngle(1,0,(n-q)/this._rx,(o-r)/this._ry);let s=this.calculateVectorAngle((n-q)/this._rx,(o-r)/this._ry,(-n-q)/this._rx,(-o-r)/this._ry);!k&&0s&&(s+=2*h),this._numSegs=Math.ceil(i(s/(h/2))),this._delta=s/this._numSegs,this._T=8/3*g(this._delta/4)*g(this._delta/4)/g(this._delta/2),this._from=a}getNextSegment(){var a,b,c;if(this._segIndex==this._numSegs)return null;let d=f(this._theta),e=g(this._theta),h=this._theta+this._delta,i=f(h),j=g(h);return c=[this._cosPhi*this._rx*i-this._sinPhi*this._ry*j+this._C[0],this._sinPhi*this._rx*i+this._cosPhi*this._ry*j+this._C[1]],a=[this._from[0]+this._T*(-this._cosPhi*this._rx*e-this._sinPhi*this._ry*d),this._from[1]+this._T*(-this._sinPhi*this._rx*e+this._cosPhi*this._ry*d)],b=[c[0]+this._T*(this._cosPhi*this._rx*j+this._sinPhi*this._ry*i),c[1]+this._T*(this._sinPhi*this._rx*j-this._cosPhi*this._ry*i)],this._theta=h,this._from=[c[0],c[1]],this._segIndex++,{cp1:a,cp2:b,to:c}}calculateVectorAngle(a,b,c,d){var e=Math.atan2;let f=e(b,a),g=e(d,c);return g>=f?g-f:2*h-(f-g)}}class t{constructor(a,b){this.sets=a,this.closed=b}fit(a){let b=[];for(const c of this.sets){let d=c.length,f=e(a*d);if(5>f){if(5>=d)continue;f=5}b.push(this.reduce(c,f))}let c='';for(const d of b){for(let a,b=0;bb;){let e=-1,f=-1;for(let g=1;ge||is;)s+=2*h,t+=2*h;t-s>2*h&&(s=0,t=2*h);let u=2*h/n.curveStepCount,v=l(u/2,(t-s)/2),w=this._arc(v,o,p,q,r,s,t,1,n),x=this._arc(v,o,p,q,r,s,t,1.5,n),y=w.concat(x);return k&&(m?(y=y.concat(this._doubleLine(o,p,o+q*f(s),p+r*g(s),n)),y=y.concat(this._doubleLine(o,p,o+q*f(t),p+r*g(t),n))):(y.push({op:'lineTo',data:[o,p]}),y.push({op:'lineTo',data:[o+q*f(s),p+r*g(s)]}))),{type:'path',ops:y}}hachureFillArc(a,b,c,d,e,j,k){let l=a,m=b,n=i(c/2),o=i(d/2);n+=this._getOffset(.01*-n,.01*n,k),o+=this._getOffset(.01*-o,.01*o,k);let p=e,q=j;for(;0>p;)p+=2*h,q+=2*h;q-p>2*h&&(p=0,q=2*h);let r=(q-p)/k.curveStepCount,s=[],t=[];for(let h=p;h<=q;h+=r)s.push(l+n*f(h)),t.push(m+o*g(h));return s.push(l+n*f(q)),t.push(m+o*g(q)),s.push(l),t.push(m),this.hachureFillShape(s,t,k)}solidFillShape(a,b,c){let d=[];if(a&&b&&a.length&&b.length&&a.length===b.length){let f=c.maxRandomnessOffset||0;const g=a.length;if(2q&&(q=4*d.strokeWidth),q=k(q,.1);const r=i%180*(h/180),s=f(r),t=g(r),u=c(r),v=new p(n-1,o+1,j-1,m+1,q,t,s,u);for(let c;null!=(c=v.getNextLine());){let f=this._getIntersectingLines(c,a,b);for(let a=0;a=n&&(n=4*f.strokeWidth);let o=f.fillWeight;0>o&&(o=f.strokeWidth/2);let p=c(m%180*(h/180)),q=l/k,r=j(q*p*q*p+1),s=q*p/r,t=1/r,u=n/(k*l/j(l*t*(l*t)+k*s*(k*s))/k),v=j(k*k-(a-k+u)*(a-k+u));for(var w=a-k+u;wf;f++)0===f?k.push({op:'move',data:[h.x,h.y]}):k.push({op:'move',data:[h.x+this._getOffset(-l[0],l[0],j),h.y+this._getOffset(-l[0],l[0],j)]}),m=[e+this._getOffset(-l[f],l[f],j),g+this._getOffset(-l[f],l[f],j)],k.push({op:'bcurveTo',data:[a+this._getOffset(-l[f],l[f],j),b+this._getOffset(-l[f],l[f],j),c+this._getOffset(-l[f],l[f],j),d+this._getOffset(-l[f],l[f],j),m[0],m[1]]});return h.setPosition(m[0],m[1]),k}_processSegment(a,b,c,d){let e=[];switch(b.key){case'M':case'm':{let c='m'===b.key;if(2<=b.data.length){let f=+b.data[0],g=+b.data[1];c&&(f+=a.x,g+=a.y);let h=1*(d.maxRandomnessOffset||0);f+=this._getOffset(-h,h,d),g+=this._getOffset(-h,h,d),a.setPosition(f,g),e.push({op:'move',data:[f,g]})}break}case'L':case'l':{let c='l'===b.key;if(2<=b.data.length){let f=+b.data[0],g=+b.data[1];c&&(f+=a.x,g+=a.y),e=e.concat(this._doubleLine(a.x,a.y,f,g,d)),a.setPosition(f,g)}break}case'H':case'h':{const c='h'===b.key;if(b.data.length){let f=+b.data[0];c&&(f+=a.x),e=e.concat(this._doubleLine(a.x,a.y,f,a.y,d)),a.setPosition(f,a.y)}break}case'V':case'v':{const c='v'===b.key;if(b.data.length){let f=+b.data[0];c&&(f+=a.y),e=e.concat(this._doubleLine(a.x,a.y,a.x,f,d)),a.setPosition(a.x,f)}break}case'Z':case'z':{a.first&&(e=e.concat(this._doubleLine(a.x,a.y,a.first[0],a.first[1],d)),a.setPosition(a.first[0],a.first[1]),a.first=null);break}case'C':case'c':{const c='c'===b.key;if(6<=b.data.length){let f=+b.data[0],g=+b.data[1],h=+b.data[2],i=+b.data[3],j=+b.data[4],k=+b.data[5];c&&(f+=a.x,h+=a.x,j+=a.x,g+=a.y,i+=a.y,k+=a.y);let l=this._bezierTo(f,g,h,i,j,k,a,d);e=e.concat(l),a.bezierReflectionPoint=[j+(j-h),k+(k-i)]}break}case'S':case's':{const f='s'===b.key;if(4<=b.data.length){let h=+b.data[0],i=+b.data[1],j=+b.data[2],k=+b.data[3];f&&(h+=a.x,j+=a.x,i+=a.y,k+=a.y);let l=h,m=i,n=c?c.key:'';var g=null;('c'==n||'C'==n||'s'==n||'S'==n)&&(g=a.bezierReflectionPoint),g&&(l=g[0],m=g[1]);let o=this._bezierTo(l,m,h,i,j,k,a,d);e=e.concat(o),a.bezierReflectionPoint=[j+(j-h),k+(k-i)]}break}case'Q':case'q':{const c='q'===b.key;if(4<=b.data.length){let g=+b.data[0],h=+b.data[1],i=+b.data[2],j=+b.data[3];c&&(g+=a.x,i+=a.x,h+=a.y,j+=a.y);let k=1*(1+.2*d.roughness),l=1.5*(1+.22*d.roughness);e.push({op:'move',data:[a.x+this._getOffset(-k,k,d),a.y+this._getOffset(-k,k,d)]});let m=[i+this._getOffset(-k,k,d),j+this._getOffset(-k,k,d)];e.push({op:'qcurveTo',data:[g+this._getOffset(-k,k,d),h+this._getOffset(-k,k,d),m[0],m[1]]}),e.push({op:'move',data:[a.x+this._getOffset(-l,l,d),a.y+this._getOffset(-l,l,d)]}),m=[i+this._getOffset(-l,l,d),j+this._getOffset(-l,l,d)],e.push({op:'qcurveTo',data:[g+this._getOffset(-l,l,d),h+this._getOffset(-l,l,d),m[0],m[1]]}),a.setPosition(m[0],m[1]),a.quadReflectionPoint=[i+(i-g),j+(j-h)]}break}case'T':case't':{const h='t'===b.key;if(2<=b.data.length){let i=+b.data[0],j=+b.data[1];h&&(i+=a.x,j+=a.y);let k=i,l=j,m=c?c.key:'';var g=null;('q'==m||'Q'==m||'t'==m||'T'==m)&&(g=a.quadReflectionPoint),g&&(k=g[0],l=g[1]);let n=1*(1+.2*d.roughness),o=1.5*(1+.22*d.roughness);e.push({op:'move',data:[a.x+this._getOffset(-n,n,d),a.y+this._getOffset(-n,n,d)]});let p=[i+this._getOffset(-n,n,d),j+this._getOffset(-n,n,d)];e.push({op:'qcurveTo',data:[k+this._getOffset(-n,n,d),l+this._getOffset(-n,n,d),p[0],p[1]]}),e.push({op:'move',data:[a.x+this._getOffset(-o,o,d),a.y+this._getOffset(-o,o,d)]}),p=[i+this._getOffset(-o,o,d),j+this._getOffset(-o,o,d)],e.push({op:'qcurveTo',data:[k+this._getOffset(-o,o,d),l+this._getOffset(-o,o,d),p[0],p[1]]}),a.setPosition(p[0],p[1]),a.quadReflectionPoint=[i+(i-k),j+(j-l)]}break}case'A':case'a':{const c='a'===b.key;if(7<=b.data.length){let f=+b.data[0],g=+b.data[1],h=+b.data[2],i=+b.data[3],j=+b.data[4],k=+b.data[5],l=+b.data[6];if(c&&(k+=a.x,l+=a.y),k==a.x&&l==a.y)break;if(0==f||0==g)e=e.concat(this._doubleLine(a.x,a.y,k,l,d)),a.setPosition(k,l);else{d.maxRandomnessOffset||0;for(let b=0;1>b;b++){let b=new s([a.x,a.y],[k,l],[f,g],h,!!i,!!j),c=b.getNextSegment();for(;c;){let f=this._bezierTo(c.cp1[0],c.cp1[1],c.cp2[0],c.cp2[1],c.to[0],c.to[1],a,d);e=e.concat(f),c=b.getNextSegment()}}}}break}default:}return e}_getOffset(a,b,c){return c.roughness*(Math.random()*(b-a)+a)}_affine(a,b,c,d,e,f,g){return[-c*f-d*e+c+f*a+e*b,g*(c*e-d*f)+d+-g*e*a+g*f*b]}_doubleLine(a,b,c,d,e){const f=this._line(a,b,c,d,e,!0,!1),g=this._line(a,b,c,d,e,!0,!0);return f.concat(g)}_line(a,b,c,e,f,g,h){const i=d(a-c,2)+d(b-e,2);let k=f.maxRandomnessOffset||0;100*(k*k)>i&&(k=j(i)/10);const l=k/2,m=.2+.2*Math.random();let n=f.bowing*f.maxRandomnessOffset*(e-b)/200,o=f.bowing*f.maxRandomnessOffset*(a-c)/200;n=this._getOffset(-n,n,f),o=this._getOffset(-o,o,f);let p=[];return g&&(h?p.push({op:'move',data:[a+this._getOffset(-l,l,f),b+this._getOffset(-l,l,f)]}):p.push({op:'move',data:[a+this._getOffset(-k,k,f),b+this._getOffset(-k,k,f)]})),h?p.push({op:'bcurveTo',data:[n+a+(c-a)*m+this._getOffset(-l,l,f),o+b+(e-b)*m+this._getOffset(-l,l,f),n+a+2*(c-a)*m+this._getOffset(-l,l,f),o+b+2*(e-b)*m+this._getOffset(-l,l,f),c+this._getOffset(-l,l,f),e+this._getOffset(-l,l,f)]}):p.push({op:'bcurveTo',data:[n+a+(c-a)*m+this._getOffset(-k,k,f),o+b+(e-b)*m+this._getOffset(-k,k,f),n+a+2*(c-a)*m+this._getOffset(-k,k,f),o+b+2*(e-b)*m+this._getOffset(-k,k,f),c+this._getOffset(-k,k,f),e+this._getOffset(-k,k,f)]}),p}_curve(a,c,d){const e=a.length;let f=[];if(3c&&(c=b.strokeWidth/2),{d:this.opsToPath(a),stroke:b.fill,strokeWidth:c,fill:'none'}}opsToPath(a){let b='';for(let c of a.ops){const a=c.data;switch(c.op){case'move':b+=`M${a[0]} ${a[1]} `;break;case'bcurveTo':b+=`C${a[0]} ${a[1]}, ${a[2]} ${a[3]}, ${a[4]} ${a[5]} `;break;case'qcurveTo':b+=`Q${a[0]} ${a[1]}, ${a[2]} ${a[3]} `;break;case'lineTo':b+=`L${a[0]} ${a[1]} `;}}return b.trim()}_computePathSize(a){let b=[0,0];if(self.document)try{const c='http://www.w3.org/2000/svg';let d=self.document.createElementNS(c,'svg');d.setAttribute('width','0'),d.setAttribute('height','0');let e=self.document.createElementNS(c,'path');e.setAttribute('d',a),d.appendChild(e),self.document.body.appendChild(d);let f=e.getBBox();f&&(b[0]=f.width||0,b[1]=f.height||0),self.document.body.removeChild(d)}catch(a){}const c=this._canvasSize();return b[0]*b[1]||(b=c),b[0]=l(b[0],c[0]),b[1]=l(b[1],c[1]),b}_canvasSize(){const a=(a)=>a&&'object'==typeof a&&a.baseVal&&a.baseVal.value?a.baseVal.value:a||100;return this.canvas?[a(this.canvas.width),a(this.canvas.height)]:[100,100]}}class w extends v{async line(a,b,c,d,e){const f=this._options(e);return this._drawable('line',[await this.lib.line(a,b,c,d,f)],f)}async rectangle(a,b,c,d,e){const f=this._options(e),g=[];if(f.fill){const e=[a,a+c,a+c,a],h=[b,b,b+d,b+d];'solid'===f.fillStyle?g.push((await this.lib.solidFillShape(e,h,f))):g.push((await this.lib.hachureFillShape(e,h,f)))}return g.push((await this.lib.rectangle(a,b,c,d,f))),this._drawable('rectangle',g,f)}async ellipse(a,b,c,d,e){const f=this._options(e),g=[];if(f.fill)if('solid'===f.fillStyle){const e=await this.lib.ellipse(a,b,c,d,f);e.type='fillPath',g.push(e)}else g.push((await this.lib.hachureFillEllipse(a,b,c,d,f)));return g.push((await this.lib.ellipse(a,b,c,d,f))),this._drawable('ellipse',g,f)}async circle(a,b,c,d){let e=await this.ellipse(a,b,c,c,d);return e.shape='circle',e}async linearPath(a,b){const c=this._options(b);return this._drawable('linearPath',[await this.lib.linearPath(a,!1,c)],c)}async polygon(a,b){const c=this._options(b),d=[];if(c.fill){let b=[],e=[];for(let c of a)b.push(c[0]),e.push(c[1]);'solid'===c.fillStyle?d.push((await this.lib.solidFillShape(b,e,c))):d.push((await this.lib.hachureFillShape(b,e,c)))}return d.push((await this.lib.linearPath(a,!0,c))),this._drawable('polygon',d,c)}async arc(a,b,c,d,e,f,g,h){const i=this._options(h),j=[];if(g&&i.fill)if('solid'===i.fillStyle){let g=await this.lib.arc(a,b,c,d,e,f,!0,!1,i);g.type='fillPath',j.push(g)}else j.push((await this.lib.hachureFillArc(a,b,c,d,e,f,i)));return j.push((await this.lib.arc(a,b,c,d,e,f,g,!0,i))),this._drawable('arc',j,i)}async curve(a,b){const c=this._options(b);return this._drawable('curve',[await this.lib.curve(a,c)],c)}async path(a,b){const c=this._options(b),e=[];if(!a)return this._drawable('path',e,c);if(c.fill)if('solid'===c.fillStyle){e.push({type:'path2Dfill',path:a})}else{const b=this._computePathSize(a);let d=[0,b[0],b[0],0],f=[0,0,b[1],b[1]],g=await this.lib.hachureFillShape(d,f,c);g.type='path2Dpattern',g.size=b,g.path=a,e.push(g)}return e.push((await this.lib.svgPath(a,c))),this._drawable('path',e,c)}}class x{constructor(a,b){this.canvas=a,this.ctx=this.canvas.getContext('2d'),this._init(b)}_init(a){this.gen=new v(a,this.canvas)}get generator(){return this.gen}static createRenderer(){return new u}line(a,b,c,e,f){let g=this.gen.line(a,b,c,e,f);return this.draw(g),g}rectangle(a,b,c,e,f){let g=this.gen.rectangle(a,b,c,e,f);return this.draw(g),g}ellipse(a,b,c,e,f){let g=this.gen.ellipse(a,b,c,e,f);return this.draw(g),g}circle(a,b,c,e){let f=this.gen.circle(a,b,c,e);return this.draw(f),f}linearPath(a,b){let c=this.gen.linearPath(a,b);return this.draw(c),c}polygon(a,b){let c=this.gen.polygon(a,b);return this.draw(c),c}arc(a,b,c,e,f,g,h,i){let j=this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j),j}curve(a,b){let c=this.gen.curve(a,b);return this.draw(c),c}path(a,b){let c=this.gen.path(a,b);return this.draw(c),c}draw(a){let b=a.sets||[],c=a.options||this.gen.defaultOptions,d=this.ctx;for(let e of b)switch(e.type){case'path':d.save(),d.strokeStyle=c.stroke,d.lineWidth=c.strokeWidth,this._drawToContext(d,e),d.restore();break;case'fillPath':d.save(),d.fillStyle=c.fill,this._drawToContext(d,e,c),d.restore();break;case'fillSketch':this._fillSketch(d,e,c);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=c.fill;let a=new Path2D(e.path);this.ctx.fill(a),this.ctx.restore();break}case'path2Dpattern':{let a=e.size;const b=document.createElement('canvas'),d=b.getContext('2d');let f=this._computeBBox(e.path);f&&(f.width||f.height)?(b.width=this.canvas.width,b.height=this.canvas.height,d.translate(f.x||0,f.y||0)):(b.width=a[0],b.height=a[1]),this._fillSketch(d,e,c),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(b,'repeat');let g=new Path2D(e.path);this.ctx.fill(g),this.ctx.restore();break}}}_computeBBox(a){if(self.document)try{const b='http://www.w3.org/2000/svg';let c=self.document.createElementNS(b,'svg');c.setAttribute('width','0'),c.setAttribute('height','0');let d=self.document.createElementNS(b,'path');d.setAttribute('d',a),c.appendChild(d),self.document.body.appendChild(c);let e=d.getBBox();return self.document.body.removeChild(c),e}catch(a){}return null}_fillSketch(a,b,c){let d=c.fillWeight;0>d&&(d=c.strokeWidth/2),a.save(),a.strokeStyle=c.fill,a.lineWidth=d,this._drawToContext(a,b),a.restore()}_drawToContext(a,b){a.beginPath();for(let c of b.ops){const b=c.data;switch(c.op){case'move':a.moveTo(b[0],b[1]);break;case'bcurveTo':a.bezierCurveTo(b[0],b[1],b[2],b[3],b[4],b[5]);break;case'qcurveTo':a.quadraticCurveTo(b[0],b[1],b[2],b[3]);break;case'lineTo':a.lineTo(b[0],b[1]);}}'fillPath'===b.type?a.fill():a.stroke()}}class y extends x{_init(a){this.gen=new w(a,this.canvas)}async line(a,b,c,e,f){let g=await this.gen.line(a,b,c,e,f);return this.draw(g),g}async rectangle(a,b,c,e,f){let g=await this.gen.rectangle(a,b,c,e,f);return this.draw(g),g}async ellipse(a,b,c,e,f){let g=await this.gen.ellipse(a,b,c,e,f);return this.draw(g),g}async circle(a,b,c,e){let f=await this.gen.circle(a,b,c,e);return this.draw(f),f}async linearPath(a,b){let c=await this.gen.linearPath(a,b);return this.draw(c),c}async polygon(a,b){let c=await this.gen.polygon(a,b);return this.draw(c),c}async arc(a,b,c,e,f,g,h,i){let j=await this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j),j}async curve(a,b){let c=await this.gen.curve(a,b);return this.draw(c),c}async path(a,b){let c=await this.gen.path(a,b);return this.draw(c),c}}class z{constructor(a,b){this.svg=a,this._init(b)}_init(a){this.gen=new v(a,this.svg)}get generator(){return this.gen}get defs(){if(!this._defs){let a=this.svg.ownerDocument||document,b=a.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(b,this.svg.firstChild):this.svg.appendChild(b),this._defs=b}return this._defs}line(a,b,c,e,f){let g=this.gen.line(a,b,c,e,f);return this.draw(g)}rectangle(a,b,c,e,f){let g=this.gen.rectangle(a,b,c,e,f);return this.draw(g)}ellipse(a,b,c,e,f){let g=this.gen.ellipse(a,b,c,e,f);return this.draw(g)}circle(a,b,c,e){let f=this.gen.circle(a,b,c,e);return this.draw(f)}linearPath(a,b){let c=this.gen.linearPath(a,b);return this.draw(c)}polygon(a,b){let c=this.gen.polygon(a,b);return this.draw(c)}arc(a,b,c,e,f,g,h,i){let j=this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j)}curve(a,b){let c=this.gen.curve(a,b);return this.draw(c)}path(a,b){let c=this.gen.path(a,b);return this.draw(c)}draw(a){let c=a.sets||[],d=a.options||this.gen.defaultOptions,f=this.svg.ownerDocument||document,h=f.createElementNS('http://www.w3.org/2000/svg','g');for(let g of c){let a=null;switch(g.type){case'path':{a=f.createElementNS('http://www.w3.org/2000/svg','path'),a.setAttribute('d',this._opsToPath(g)),a.style.stroke=d.stroke,a.style.strokeWidth=d.strokeWidth,a.style.fill='none';break}case'fillPath':{a=f.createElementNS('http://www.w3.org/2000/svg','path'),a.setAttribute('d',this._opsToPath(g)),a.style.stroke='none',a.style.strokeWidth=0,a.style.fill=d.fill;break}case'fillSketch':{a=this._fillSketch(f,g,d);break}case'path2Dfill':{a=f.createElementNS('http://www.w3.org/2000/svg','path'),a.setAttribute('d',g.path),a.style.stroke='none',a.style.strokeWidth=0,a.style.fill=d.fill;break}case'path2Dpattern':{const c=g.size,h=f.createElementNS('http://www.w3.org/2000/svg','pattern'),i=`rough-${e(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;h.setAttribute('id',i),h.setAttribute('x',0),h.setAttribute('y',0),h.setAttribute('width',1),h.setAttribute('height',1),h.setAttribute('height',1),h.setAttribute('viewBox',`0 0 ${b(c[0])} ${b(c[1])}`),h.setAttribute('patternUnits','objectBoundingBox');const j=this._fillSketch(f,g,d);h.appendChild(j),this.defs.appendChild(h),a=f.createElementNS('http://www.w3.org/2000/svg','path'),a.setAttribute('d',g.path),a.style.stroke='none',a.style.strokeWidth=0,a.style.fill=`url(#${i})`;break}}a&&h.appendChild(a)}return h}_fillSketch(a,b,c){let d=c.fillWeight;0>d&&(d=c.strokeWidth/2);let e=a.createElementNS('http://www.w3.org/2000/svg','path');return e.setAttribute('d',this._opsToPath(b)),e.style.stroke=c.fill,e.style.strokeWidth=d,e.style.fill='none',e}_opsToPath(a){return this.gen.opsToPath(a)}}class A extends z{_init(a){this.gen=new w(a,this.svg)}async line(a,b,c,e,f){let g=await this.gen.line(a,b,c,e,f);return this.draw(g)}async rectangle(a,b,c,e,f){let g=await this.gen.rectangle(a,b,c,e,f);return this.draw(g)}async ellipse(a,b,c,e,f){let g=await this.gen.ellipse(a,b,c,e,f);return this.draw(g)}async circle(a,b,c,e){let f=await this.gen.circle(a,b,c,e);return this.draw(f)}async linearPath(a,b){let c=await this.gen.linearPath(a,b);return this.draw(c)}async polygon(a,b){let c=await this.gen.polygon(a,b);return this.draw(c)}async arc(a,b,c,e,f,g,h,i){let j=await this.gen.arc(a,b,c,e,f,g,h,i);return this.draw(j)}async curve(a,b){let c=await this.gen.curve(a,b);return this.draw(c)}async path(a,b){let c=await this.gen.path(a,b);return this.draw(c)}}var B={canvas(a,b){return b&&b.async?new y(a,b):new x(a,b)},svg(a,b){return b&&b.async?new A(a,b):new z(a,b)},createRenderer(){return x.createRenderer()},generator(a,b){return a&&a.async?new w(a,b):new v(a,b)}};return B}); \ No newline at end of file +(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!v[i])switch(i){case'zigzag':v[i]||(v[i]=new O(e));break;case'cross-hatch':v[i]||(v[i]=new P(e));break;case'hachure':default:i='hachure',v[i]||(v[i]=new m(e));}return v[i]}var i=Math.round,s=Math.tan,n=Math.max,l=Math.min,o=Number.MAX_VALUE,a=Math.pow,p=Math.floor,r=Math.sqrt,d=Math.cos,f=Math.sin,h=Math.abs,g=Math.PI;const c={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class u{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const s=this.tokenize(t);let n=0,i=s[n],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=c[a].length:(n++,l=c[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)n++,l=c[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(n+lu){const e=r(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,c=0}else c=(n===a?-1:1)*r(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=c*this._rx*p/this._ry,_=-c*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*g),this._numSegs=Math.ceil(h(x/(g/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=d(this._theta),t=f(this._theta),i=this._theta+this._delta,s=d(i),n=f(i),a=[this._cosPhi*this._rx*s-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*s+this._cosPhi*this._ry*n+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*s),a[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*s)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,s){var n=Math.atan2;const a=n(t,e),l=n(s,i);return l>=a?l-a:2*g-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const s=i.length;let n=p(e*s);if(5>n){if(5>=s)continue;n=5}t.push(this.reduce(i,n))}let s='';for(const n of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=l(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=l(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+p,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>h(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+s,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>h(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?s==p&&(this.px1>=l(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=l(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(p-s)/(t-i),this.yi=t*this.xi+s,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class w{constructor(e,t,i,s,n,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=s,this.gap=n,this.sinAngle=a,this.tanAngle=o,1e-4>h(a)?this.pos=i+n:.9999h(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new b([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=n(u,.1);const y=i%180*(g/180),_=d(y),x=f(y),b=s(y),m=new w(h-1,c+1,p-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=m.nextLine());){const n=this.getIntersectingLines(i,e);for(let e=0;e=c&&(c=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(g/180)),_=d/p,x=r(_*y*_*y+1),b=_*y/x,w=1/x,m=c/(p*d/r(d*w*(d*w)+p*b*(p*b))/p);let O=r(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*g,b+=2*g;b-x>2*g&&(x=0,b=2*g);const w=2*g/c.curveStepCount,m=l(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,c),P=this._arc(m,o,u,y,_,x,b,1.5,c);let v=O.concat(P);return p&&(r?(v=v.concat(this.doubleLine(o,u,o+y*d(x),u+_*f(x),c)),v=v.concat(this.doubleLine(o,u,o+y*d(b),u+_*f(b),c))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*d(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new y(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new y(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*g,y+=2*g;y-u>2*g&&(u=0,y=2*g);const _=(y-u)/l.curveStepCount,x=[];for(let h=u;h<=y;h+=_)x.push([o+r*d(h),p+c*f(h)]);return x.push([o+r*d(y),p+c*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,s,n){const a=this._line(e,t,i,s,n,!0,!1),l=this._line(e,t,i,s,n,!0,!0);return a.concat(l)}_line(e,t,i,s,n,l,o){const p=a(e-i,2)+a(t-s,2);let d=n.maxRandomnessOffset||0;100*(d*d)>p&&(d=r(p)/10);const f=d/2,h=.2+.2*Math.random();let g=n.bowing*n.maxRandomnessOffset*(s-t)/200,c=n.bowing*n.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,n),c=this.getOffset(-c,c,n);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):u.push({op:'move',data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,n),c+t+(s-t)*h+this.getOffset(-f,f,n),g+e+2*(i-e)*h+this.getOffset(-f,f,n),c+t+2*(s-t)*h+this.getOffset(-f,f,n),i+this.getOffset(-f,f,n),s+this.getOffset(-f,f,n)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-d,d,n),c+t+(s-t)*h+this.getOffset(-d,d,n),g+e+2*(i-e)*h+this.getOffset(-d,d,n),c+t+2*(s-t)*h+this.getOffset(-d,d,n),i+this.getOffset(-d,d,n),s+this.getOffset(-d,d,n)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,s){let n=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(s.maxRandomnessOffset||0);a+=this.getOffset(-o,o,s),l+=this.getOffset(-o,o,s),e.setPosition(a,l),n.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),n=n.concat(this.doubleLine(e.x,e.y,a,l,s)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),n=n.concat(this.doubleLine(e.x,e.y,a,e.y,s)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,a,s)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],s)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,s);n=n.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,s);n=n.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*s.roughness),d=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-r,r,s),e.y+this.getOffset(-r,r,s)]});let h=[o+this.getOffset(-r,r,s),p+this.getOffset(-r,r,s)];n.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,s),l+this.getOffset(-r,r,s),h[0],h[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-d,d,s),e.y+this.getOffset(-d,d,s)]}),h=[o+this.getOffset(-d,d,s),p+this.getOffset(-d,d,s)],n.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,s),l+this.getOffset(-d,d,s),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*s.roughness),c=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-g,g,s),e.y+this.getOffset(-g,g,s)]});let u=[l+this.getOffset(-g,g,s),o+this.getOffset(-g,g,s)];n.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,s),r+this.getOffset(-g,g,s),u[0],u[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-c,c,s),e.y+this.getOffset(-c,c,s)]}),u=[l+this.getOffset(-c,c,s),o+this.getOffset(-c,c,s)],n.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,s),r+this.getOffset(-c,c,s),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)n=n.concat(this.doubleLine(e.x,e.y,d,f,s)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new _([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,s);n=n.concat(a),i=t.getNextSegment()}}}break}default:}return n}}const A='undefined'!=typeof self;class S{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer||(this.renderer=new k),this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(A&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const s=self.document.createElementNS('http://www.w3.org/2000/svg','path');s.setAttribute('d',e),i.appendChild(s),self.document.body.appendChild(i);const n=s.getBBox();n&&(t[0]=n.width||0,t[1]=n.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=l(t[0],i[0]),t[1]=l(t[1],i[1]),t}line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[this.lib.line(e,t,i,s,a)],a)}rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(n,a)):l.push(this.lib.patternFillPolygon(n,a))}return l.push(this.lib.rectangle(e,t,i,s,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push(this.lib.patternFillEllipse(e,t,i,s,a));return l.push(this.lib.ellipse(e,t,i,s,a)),this._drawable('ellipse',l,a)}circle(e,t,i,s){const n=this.ellipse(e,t,i,i,s);return n.shape='circle',n}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push(this.lib.solidFillPolygon(e,i)):s.push(this.lib.patternFillPolygon(e,i))),s.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',s,i)}arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,s,n,a,r));return o.push(this.lib.arc(e,t,i,s,n,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push(this.lib.svgPath(e,i)),this._drawable('path',s,i)}toPaths(e){const t=e.sets||[],s=e.options||this.defaultOptions,n=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:s.stroke,strokeWidth:s.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,s);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'path2Dpattern':{const t=a.size,n={x:0,y:0,width:1,height:1,viewBox:`0 0 ${i(t[0])} ${i(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,s)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:n};break}}e&&n.push(e)}return n}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const T='undefined'!=typeof document;class C{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new S(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new k}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a),a}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a),a}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a),a}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n),n}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.ctx;for(const n of t)switch(n.type){case'path':s.save(),s.strokeStyle=i.stroke,s.lineWidth=i.strokeWidth,this._drawToContext(s,n),s.restore();break;case'fillPath':s.save(),s.fillStyle=i.fill||'',this._drawToContext(s,n),s.restore();break;case'fillSketch':this.fillSketch(s,n,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(n.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(T){const e=n.size,t=document.createElement('canvas'),s=t.getContext('2d'),a=this.computeBBox(n.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,s.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(s,n,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(n.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(T)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const s=i.getBBox();return document.body.removeChild(t),s}catch(e){}return null}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=s,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class E extends S{async line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[await this.lib.line(e,t,i,s,a)],a)}async rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(n,a))):l.push((await this.lib.patternFillPolygon(n,a)))}return l.push((await this.lib.rectangle(e,t,i,s,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=await this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push((await this.lib.patternFillEllipse(e,t,i,s,a)));return l.push((await this.lib.ellipse(e,t,i,s,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,s){const n=await this.ellipse(e,t,i,i,s);return n.shape='circle',n}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push((await this.lib.solidFillPolygon(e,i))):s.push((await this.lib.patternFillPolygon(e,i)))),s.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',s,i)}async arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,s,n,a,r)));return o.push((await this.lib.arc(e,t,i,s,n,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push((await this.lib.svgPath(e,i))),this._drawable('path',s,i)}}class N extends C{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a),a}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a),a}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a),a}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n),n}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const W='undefined'!=typeof document;class z{constructor(e,t){this.svg=e,this.gen=new S(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new k}get defs(){if(W&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a)}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a)}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a)}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],s=e.options||this.gen.defaultOptions,n=this.svg.ownerDocument||W&&document,a=n.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=s.stroke,e.style.strokeWidth=s.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'fillSketch':{e=this.fillSketch(n,l,s);break}case'path2Dfill':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'path2Dpattern':{const t=l.size,a=n.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${p(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${i(t[0])} ${i(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const r=this.fillSketch(n,l,s);a.appendChild(r),this.defs.appendChild(a),e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2);const n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=i.fill,n.style.strokeWidth=s+'',n.style.fill='none',n}}class L extends z{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a)}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a)}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a)}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var R={canvas(e,t){return t&&t.async?new N(e,t):new C(e,t)},svg(e,t){return t&&t.async?new L(e,t):new z(e,t)},createRenderer(){return C.createRenderer()},generator(e,t){return e&&e.async?new E(e,t):new S(e,t)}};return R}); diff --git a/package.json b/package.json index 36c66e7..7497d2d 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,7 @@ "main": "dist/rough.umd.js", "jsnext:main": "src/index.js", "scripts": { - "build": "npm run build-d && npm run build-umd", - "build-d": "rollup src/index.js --o dist/rough.js --f iife --name \"rough\" && npm run minify-d", - "minify-d": "babel-minify dist/rough.js -o dist/rough.min.js --mangle", - "build-umd": "rollup src/index.js --o dist/rough.umd.js --f umd --name \"rough\" && npm run minify-umd", - "minify-umd": "babel-minify dist/rough.umd.js -o dist/rough.umd.min.js --mangle", + "build": "tsc && rollup -c", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -36,4 +32,4 @@ "tslint": "^5.10.0", "typescript": "^2.9.2" } -} +} \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..33c7264 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,40 @@ +import minify from 'rollup-plugin-babel-minify'; + +const outFolder = 'dist'; + +export default [ + { + input: 'bin/rough.js', + output: { + file: `${outFolder}/rough.js`, + format: 'iife', + name: 'rough' + } + }, + { + input: 'bin/rough.js', + output: { + file: `${outFolder}/rough.min.js`, + format: 'iife', + name: 'rough' + }, + plugins: [minify({ comments: false })] + }, + { + input: 'bin/rough.js', + output: { + file: `${outFolder}/rough.umd.js`, + format: 'umd', + name: 'rough' + } + }, + { + input: 'bin/rough.js', + output: { + file: `${outFolder}/rough.umd.min.js`, + format: 'umd', + name: 'rough' + }, + plugins: [minify({ comments: false })] + }, +]; \ No newline at end of file From 777e7feb6e85704ff3038003d58cb99fc034ef1e Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Mon, 18 Jun 2018 13:01:26 -0700 Subject: [PATCH 11/16] renderer factory --- dist/rough.js | 20 ++++++++++++++++---- dist/rough.min.js | 2 +- dist/rough.umd.js | 20 ++++++++++++++++---- dist/rough.umd.min.js | 2 +- srcts/generator.ts | 7 +++---- srcts/renderer-factory.ts | 17 +++++++++++++++++ 6 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 srcts/renderer-factory.ts diff --git a/dist/rough.js b/dist/rough.js index 66eaf05..05dae20 100644 --- a/dist/rough.js +++ b/dist/rough.js @@ -1370,6 +1370,20 @@ var rough = (function () { } const hasSelf = typeof self !== 'undefined'; + const roughScript = hasSelf && self && self.document && self.document.currentScript && self.document.currentScript.src; + function createRenderer(config) { + if (hasSelf && roughScript && self && self.workly && config.async && (!config.noWorker)) { + const worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; + if (worklySource) { + const code = `importScripts('${worklySource}', '${roughScript}');\nworkly.expose(self.rough.createRenderer());`; + const ourl = URL.createObjectURL(new Blob([code])); + return self.workly.proxy(ourl); + } + } + return new RoughRenderer(); + } + + const hasSelf$1 = typeof self !== 'undefined'; class RoughGenerator { constructor(config, surface) { this.defaultOptions = { @@ -1388,6 +1402,7 @@ var rough = (function () { }; this.config = config || {}; this.surface = surface; + this.renderer = createRenderer(this.config); if (this.config.options) { this.defaultOptions = this._options(this.config.options); } @@ -1399,9 +1414,6 @@ var rough = (function () { return { shape, sets: sets || [], options: options || this.defaultOptions }; } get lib() { - if (!this.renderer) { - this.renderer = new RoughRenderer(); - } return this.renderer; } getCanvasSize() { @@ -1420,7 +1432,7 @@ var rough = (function () { } computePathSize(d) { let size = [0, 0]; - if (hasSelf && self.document) { + if (hasSelf$1 && self.document) { try { const ns = 'http://www.w3.org/2000/svg'; const svg = self.document.createElementNS(ns, 'svg'); diff --git a/dist/rough.min.js b/dist/rough.min.js index 94163d8..5b1951d 100644 --- a/dist/rough.min.js +++ b/dist/rough.min.js @@ -1 +1 @@ -var rough=function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!v[i])switch(i){case'zigzag':v[i]||(v[i]=new O(e));break;case'cross-hatch':v[i]||(v[i]=new P(e));break;case'hachure':default:i='hachure',v[i]||(v[i]=new m(e));}return v[i]}var i=Math.round,s=Math.tan,n=Math.max,l=Math.min,o=Number.MAX_VALUE,a=Math.pow,p=Math.floor,r=Math.sqrt,d=Math.cos,f=Math.sin,h=Math.abs,g=Math.PI;const c={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class u{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const s=this.tokenize(t);let n=0,i=s[n],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=c[a].length:(n++,l=c[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)n++,l=c[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(n+lu){const e=r(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,c=0}else c=(n===a?-1:1)*r(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=c*this._rx*p/this._ry,_=-c*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*g),this._numSegs=Math.ceil(h(x/(g/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=d(this._theta),t=f(this._theta),i=this._theta+this._delta,s=d(i),n=f(i),a=[this._cosPhi*this._rx*s-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*s+this._cosPhi*this._ry*n+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*s),a[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*s)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,s){var n=Math.atan2;const a=n(t,e),l=n(s,i);return l>=a?l-a:2*g-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const s=i.length;let n=p(e*s);if(5>n){if(5>=s)continue;n=5}t.push(this.reduce(i,n))}let s='';for(const n of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=l(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=l(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+p,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>h(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+s,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>h(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?s==p&&(this.px1>=l(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=l(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(p-s)/(t-i),this.yi=t*this.xi+s,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class w{constructor(e,t,i,s,n,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=s,this.gap=n,this.sinAngle=a,this.tanAngle=o,1e-4>h(a)?this.pos=i+n:.9999h(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new b([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=n(u,.1);const y=i%180*(g/180),_=d(y),x=f(y),b=s(y),m=new w(h-1,c+1,p-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=m.nextLine());){const n=this.getIntersectingLines(i,e);for(let e=0;e=c&&(c=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(g/180)),_=d/p,x=r(_*y*_*y+1),b=_*y/x,w=1/x,m=c/(p*d/r(d*w*(d*w)+p*b*(p*b))/p);let O=r(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*g,b+=2*g;b-x>2*g&&(x=0,b=2*g);const w=2*g/c.curveStepCount,m=l(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,c),P=this._arc(m,o,u,y,_,x,b,1.5,c);let v=O.concat(P);return p&&(r?(v=v.concat(this.doubleLine(o,u,o+y*d(x),u+_*f(x),c)),v=v.concat(this.doubleLine(o,u,o+y*d(b),u+_*f(b),c))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*d(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new y(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new y(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*g,y+=2*g;y-u>2*g&&(u=0,y=2*g);const _=(y-u)/l.curveStepCount,x=[];for(let h=u;h<=y;h+=_)x.push([o+r*d(h),p+c*f(h)]);return x.push([o+r*d(y),p+c*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,s,n){const a=this._line(e,t,i,s,n,!0,!1),l=this._line(e,t,i,s,n,!0,!0);return a.concat(l)}_line(e,t,i,s,n,l,o){const p=a(e-i,2)+a(t-s,2);let d=n.maxRandomnessOffset||0;100*(d*d)>p&&(d=r(p)/10);const f=d/2,h=.2+.2*Math.random();let g=n.bowing*n.maxRandomnessOffset*(s-t)/200,c=n.bowing*n.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,n),c=this.getOffset(-c,c,n);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):u.push({op:'move',data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,n),c+t+(s-t)*h+this.getOffset(-f,f,n),g+e+2*(i-e)*h+this.getOffset(-f,f,n),c+t+2*(s-t)*h+this.getOffset(-f,f,n),i+this.getOffset(-f,f,n),s+this.getOffset(-f,f,n)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-d,d,n),c+t+(s-t)*h+this.getOffset(-d,d,n),g+e+2*(i-e)*h+this.getOffset(-d,d,n),c+t+2*(s-t)*h+this.getOffset(-d,d,n),i+this.getOffset(-d,d,n),s+this.getOffset(-d,d,n)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,s){let n=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(s.maxRandomnessOffset||0);a+=this.getOffset(-o,o,s),l+=this.getOffset(-o,o,s),e.setPosition(a,l),n.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),n=n.concat(this.doubleLine(e.x,e.y,a,l,s)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),n=n.concat(this.doubleLine(e.x,e.y,a,e.y,s)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,a,s)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],s)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,s);n=n.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,s);n=n.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*s.roughness),d=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-r,r,s),e.y+this.getOffset(-r,r,s)]});let h=[o+this.getOffset(-r,r,s),p+this.getOffset(-r,r,s)];n.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,s),l+this.getOffset(-r,r,s),h[0],h[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-d,d,s),e.y+this.getOffset(-d,d,s)]}),h=[o+this.getOffset(-d,d,s),p+this.getOffset(-d,d,s)],n.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,s),l+this.getOffset(-d,d,s),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*s.roughness),c=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-g,g,s),e.y+this.getOffset(-g,g,s)]});let u=[l+this.getOffset(-g,g,s),o+this.getOffset(-g,g,s)];n.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,s),r+this.getOffset(-g,g,s),u[0],u[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-c,c,s),e.y+this.getOffset(-c,c,s)]}),u=[l+this.getOffset(-c,c,s),o+this.getOffset(-c,c,s)],n.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,s),r+this.getOffset(-c,c,s),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)n=n.concat(this.doubleLine(e.x,e.y,d,f,s)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new _([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,s);n=n.concat(a),i=t.getNextSegment()}}}break}default:}return n}}const A='undefined'!=typeof self;class S{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer||(this.renderer=new k),this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(A&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const s=self.document.createElementNS('http://www.w3.org/2000/svg','path');s.setAttribute('d',e),i.appendChild(s),self.document.body.appendChild(i);const n=s.getBBox();n&&(t[0]=n.width||0,t[1]=n.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=l(t[0],i[0]),t[1]=l(t[1],i[1]),t}line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[this.lib.line(e,t,i,s,a)],a)}rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(n,a)):l.push(this.lib.patternFillPolygon(n,a))}return l.push(this.lib.rectangle(e,t,i,s,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push(this.lib.patternFillEllipse(e,t,i,s,a));return l.push(this.lib.ellipse(e,t,i,s,a)),this._drawable('ellipse',l,a)}circle(e,t,i,s){const n=this.ellipse(e,t,i,i,s);return n.shape='circle',n}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push(this.lib.solidFillPolygon(e,i)):s.push(this.lib.patternFillPolygon(e,i))),s.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',s,i)}arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,s,n,a,r));return o.push(this.lib.arc(e,t,i,s,n,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push(this.lib.svgPath(e,i)),this._drawable('path',s,i)}toPaths(e){const t=e.sets||[],s=e.options||this.defaultOptions,n=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:s.stroke,strokeWidth:s.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,s);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'path2Dpattern':{const t=a.size,n={x:0,y:0,width:1,height:1,viewBox:`0 0 ${i(t[0])} ${i(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,s)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:n};break}}e&&n.push(e)}return n}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const T='undefined'!=typeof document;class C{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new S(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new k}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a),a}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a),a}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a),a}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n),n}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.ctx;for(const n of t)switch(n.type){case'path':s.save(),s.strokeStyle=i.stroke,s.lineWidth=i.strokeWidth,this._drawToContext(s,n),s.restore();break;case'fillPath':s.save(),s.fillStyle=i.fill||'',this._drawToContext(s,n),s.restore();break;case'fillSketch':this.fillSketch(s,n,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(n.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(T){const e=n.size,t=document.createElement('canvas'),s=t.getContext('2d'),a=this.computeBBox(n.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,s.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(s,n,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(n.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(T)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const s=i.getBBox();return document.body.removeChild(t),s}catch(e){}return null}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=s,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class E extends S{async line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[await this.lib.line(e,t,i,s,a)],a)}async rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(n,a))):l.push((await this.lib.patternFillPolygon(n,a)))}return l.push((await this.lib.rectangle(e,t,i,s,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=await this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push((await this.lib.patternFillEllipse(e,t,i,s,a)));return l.push((await this.lib.ellipse(e,t,i,s,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,s){const n=await this.ellipse(e,t,i,i,s);return n.shape='circle',n}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push((await this.lib.solidFillPolygon(e,i))):s.push((await this.lib.patternFillPolygon(e,i)))),s.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',s,i)}async arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,s,n,a,r)));return o.push((await this.lib.arc(e,t,i,s,n,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push((await this.lib.svgPath(e,i))),this._drawable('path',s,i)}}class N extends C{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a),a}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a),a}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a),a}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n),n}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const W='undefined'!=typeof document;class z{constructor(e,t){this.svg=e,this.gen=new S(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new k}get defs(){if(W&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a)}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a)}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a)}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],s=e.options||this.gen.defaultOptions,n=this.svg.ownerDocument||W&&document,a=n.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=s.stroke,e.style.strokeWidth=s.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'fillSketch':{e=this.fillSketch(n,l,s);break}case'path2Dfill':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'path2Dpattern':{const t=l.size,a=n.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${p(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${i(t[0])} ${i(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const r=this.fillSketch(n,l,s);a.appendChild(r),this.defs.appendChild(a),e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2);const n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=i.fill,n.style.strokeWidth=s+'',n.style.fill='none',n}}class L extends z{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a)}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a)}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a)}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var R={canvas(e,t){return t&&t.async?new N(e,t):new C(e,t)},svg(e,t){return t&&t.async?new L(e,t):new z(e,t)},createRenderer(){return C.createRenderer()},generator(e,t){return e&&e.async?new E(e,t):new S(e,t)}};return R}(); +var rough=function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var n=Math.round,s=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,f=Math.cos,h=Math.sin,g=Math.abs,c=Math.PI;const u={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const n=this.tokenize(t);let s=0,i=n[s],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a].length:(s++,l=u[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)s++,l=u[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(s+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=f(this._theta),t=h(this._theta),i=this._theta+this._delta,n=f(i),s=h(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=f(y),x=h(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=h&&(h=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=h/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*f(x),u+_*h(x),d)),v=v.concat(this.doubleLine(o,u,o+y*f(b),u+_*h(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*f(x),u+_*h(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*f(g),p+d*h(g)]);return x.push([o+r*f(y),p+d*h(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const f=r/2,h=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,s),t+this.getOffset(-f,f,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,s),c+t+(n-t)*h+this.getOffset(-f,f,s),g+e+2*(i-e)*h+this.getOffset(-f,f,s),c+t+2*(n-t)*h+this.getOffset(-f,f,s),i+this.getOffset(-f,f,s),n+this.getOffset(-f,f,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-r,r,s),c+t+(n-t)*h+this.getOffset(-r,r,s),g+e+2*(i-e)*h+this.getOffset(-r,r,s),c+t+2*(n-t)*h+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,f,n)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class E{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(C&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const n=self.document.createElementNS('http://www.w3.org/2000/svg','path');n.setAttribute('d',e),i.appendChild(n),self.document.body.appendChild(i);const s=n.getBBox();s&&(t[0]=s.width||0,t[1]=s.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=p(t[0],i[0]),t[1]=p(t[1],i[1]),t}line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[this.lib.line(e,t,i,n,a)],a)}rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(s,a)):l.push(this.lib.patternFillPolygon(s,a))}return l.push(this.lib.rectangle(e,t,i,n,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push(this.lib.patternFillEllipse(e,t,i,n,a));return l.push(this.lib.ellipse(e,t,i,n,a)),this._drawable('ellipse',l,a)}circle(e,t,i,n){const s=this.ellipse(e,t,i,i,n);return s.shape='circle',s}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push(this.lib.solidFillPolygon(e,i)):n.push(this.lib.patternFillPolygon(e,i))),n.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',n,i)}arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,n,s,a,r));return o.push(this.lib.arc(e,t,i,n,s,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push(this.lib.svgPath(e,i)),this._drawable('path',n,i)}toPaths(e){const t=e.sets||[],i=e.options||this.defaultOptions,s=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:i.stroke,strokeWidth:i.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,i);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'path2Dpattern':{const t=a.size,s={x:0,y:0,width:1,height:1,viewBox:`0 0 ${n(t[0])} ${n(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,i)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:s};break}}e&&s.push(e)}return s}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const L='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new E(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(L){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(L)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class N extends E{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push((await this.lib.solidFillPolygon(e,i))):n.push((await this.lib.patternFillPolygon(e,i)))),n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class R extends W{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const z='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new E(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(z&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||z&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new R(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new N(e,t):new E(e,t)}};return M}(); diff --git a/dist/rough.umd.js b/dist/rough.umd.js index 2d6de2d..71a7a99 100644 --- a/dist/rough.umd.js +++ b/dist/rough.umd.js @@ -1373,6 +1373,20 @@ } const hasSelf = typeof self !== 'undefined'; + const roughScript = hasSelf && self && self.document && self.document.currentScript && self.document.currentScript.src; + function createRenderer(config) { + if (hasSelf && roughScript && self && self.workly && config.async && (!config.noWorker)) { + const worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; + if (worklySource) { + const code = `importScripts('${worklySource}', '${roughScript}');\nworkly.expose(self.rough.createRenderer());`; + const ourl = URL.createObjectURL(new Blob([code])); + return self.workly.proxy(ourl); + } + } + return new RoughRenderer(); + } + + const hasSelf$1 = typeof self !== 'undefined'; class RoughGenerator { constructor(config, surface) { this.defaultOptions = { @@ -1391,6 +1405,7 @@ }; this.config = config || {}; this.surface = surface; + this.renderer = createRenderer(this.config); if (this.config.options) { this.defaultOptions = this._options(this.config.options); } @@ -1402,9 +1417,6 @@ return { shape, sets: sets || [], options: options || this.defaultOptions }; } get lib() { - if (!this.renderer) { - this.renderer = new RoughRenderer(); - } return this.renderer; } getCanvasSize() { @@ -1423,7 +1435,7 @@ } computePathSize(d) { let size = [0, 0]; - if (hasSelf && self.document) { + if (hasSelf$1 && self.document) { try { const ns = 'http://www.w3.org/2000/svg'; const svg = self.document.createElementNS(ns, 'svg'); diff --git a/dist/rough.umd.min.js b/dist/rough.umd.min.js index 46cd0bf..eb5934d 100644 --- a/dist/rough.umd.min.js +++ b/dist/rough.umd.min.js @@ -1 +1 @@ -(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!v[i])switch(i){case'zigzag':v[i]||(v[i]=new O(e));break;case'cross-hatch':v[i]||(v[i]=new P(e));break;case'hachure':default:i='hachure',v[i]||(v[i]=new m(e));}return v[i]}var i=Math.round,s=Math.tan,n=Math.max,l=Math.min,o=Number.MAX_VALUE,a=Math.pow,p=Math.floor,r=Math.sqrt,d=Math.cos,f=Math.sin,h=Math.abs,g=Math.PI;const c={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class u{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const s=this.tokenize(t);let n=0,i=s[n],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=c[a].length:(n++,l=c[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)n++,l=c[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(n+lu){const e=r(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,c=0}else c=(n===a?-1:1)*r(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=c*this._rx*p/this._ry,_=-c*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*g),this._numSegs=Math.ceil(h(x/(g/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=d(this._theta),t=f(this._theta),i=this._theta+this._delta,s=d(i),n=f(i),a=[this._cosPhi*this._rx*s-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*s+this._cosPhi*this._ry*n+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*s),a[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*s)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,s){var n=Math.atan2;const a=n(t,e),l=n(s,i);return l>=a?l-a:2*g-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const s=i.length;let n=p(e*s);if(5>n){if(5>=s)continue;n=5}t.push(this.reduce(i,n))}let s='';for(const n of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=l(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=l(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+p,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>h(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+s,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>h(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?s==p&&(this.px1>=l(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=l(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(p-s)/(t-i),this.yi=t*this.xi+s,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class w{constructor(e,t,i,s,n,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=s,this.gap=n,this.sinAngle=a,this.tanAngle=o,1e-4>h(a)?this.pos=i+n:.9999h(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new b([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=n(u,.1);const y=i%180*(g/180),_=d(y),x=f(y),b=s(y),m=new w(h-1,c+1,p-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=m.nextLine());){const n=this.getIntersectingLines(i,e);for(let e=0;e=c&&(c=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(g/180)),_=d/p,x=r(_*y*_*y+1),b=_*y/x,w=1/x,m=c/(p*d/r(d*w*(d*w)+p*b*(p*b))/p);let O=r(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*g,b+=2*g;b-x>2*g&&(x=0,b=2*g);const w=2*g/c.curveStepCount,m=l(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,c),P=this._arc(m,o,u,y,_,x,b,1.5,c);let v=O.concat(P);return p&&(r?(v=v.concat(this.doubleLine(o,u,o+y*d(x),u+_*f(x),c)),v=v.concat(this.doubleLine(o,u,o+y*d(b),u+_*f(b),c))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*d(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new y(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new y(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*g,y+=2*g;y-u>2*g&&(u=0,y=2*g);const _=(y-u)/l.curveStepCount,x=[];for(let h=u;h<=y;h+=_)x.push([o+r*d(h),p+c*f(h)]);return x.push([o+r*d(y),p+c*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,s,n){const a=this._line(e,t,i,s,n,!0,!1),l=this._line(e,t,i,s,n,!0,!0);return a.concat(l)}_line(e,t,i,s,n,l,o){const p=a(e-i,2)+a(t-s,2);let d=n.maxRandomnessOffset||0;100*(d*d)>p&&(d=r(p)/10);const f=d/2,h=.2+.2*Math.random();let g=n.bowing*n.maxRandomnessOffset*(s-t)/200,c=n.bowing*n.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,n),c=this.getOffset(-c,c,n);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):u.push({op:'move',data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,n),c+t+(s-t)*h+this.getOffset(-f,f,n),g+e+2*(i-e)*h+this.getOffset(-f,f,n),c+t+2*(s-t)*h+this.getOffset(-f,f,n),i+this.getOffset(-f,f,n),s+this.getOffset(-f,f,n)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-d,d,n),c+t+(s-t)*h+this.getOffset(-d,d,n),g+e+2*(i-e)*h+this.getOffset(-d,d,n),c+t+2*(s-t)*h+this.getOffset(-d,d,n),i+this.getOffset(-d,d,n),s+this.getOffset(-d,d,n)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,s){let n=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(s.maxRandomnessOffset||0);a+=this.getOffset(-o,o,s),l+=this.getOffset(-o,o,s),e.setPosition(a,l),n.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),n=n.concat(this.doubleLine(e.x,e.y,a,l,s)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),n=n.concat(this.doubleLine(e.x,e.y,a,e.y,s)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,a,s)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],s)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,s);n=n.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,s);n=n.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*s.roughness),d=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-r,r,s),e.y+this.getOffset(-r,r,s)]});let h=[o+this.getOffset(-r,r,s),p+this.getOffset(-r,r,s)];n.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,s),l+this.getOffset(-r,r,s),h[0],h[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-d,d,s),e.y+this.getOffset(-d,d,s)]}),h=[o+this.getOffset(-d,d,s),p+this.getOffset(-d,d,s)],n.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,s),l+this.getOffset(-d,d,s),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*s.roughness),c=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-g,g,s),e.y+this.getOffset(-g,g,s)]});let u=[l+this.getOffset(-g,g,s),o+this.getOffset(-g,g,s)];n.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,s),r+this.getOffset(-g,g,s),u[0],u[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-c,c,s),e.y+this.getOffset(-c,c,s)]}),u=[l+this.getOffset(-c,c,s),o+this.getOffset(-c,c,s)],n.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,s),r+this.getOffset(-c,c,s),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)n=n.concat(this.doubleLine(e.x,e.y,d,f,s)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new _([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,s);n=n.concat(a),i=t.getNextSegment()}}}break}default:}return n}}const A='undefined'!=typeof self;class S{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer||(this.renderer=new k),this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(A&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const s=self.document.createElementNS('http://www.w3.org/2000/svg','path');s.setAttribute('d',e),i.appendChild(s),self.document.body.appendChild(i);const n=s.getBBox();n&&(t[0]=n.width||0,t[1]=n.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=l(t[0],i[0]),t[1]=l(t[1],i[1]),t}line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[this.lib.line(e,t,i,s,a)],a)}rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(n,a)):l.push(this.lib.patternFillPolygon(n,a))}return l.push(this.lib.rectangle(e,t,i,s,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push(this.lib.patternFillEllipse(e,t,i,s,a));return l.push(this.lib.ellipse(e,t,i,s,a)),this._drawable('ellipse',l,a)}circle(e,t,i,s){const n=this.ellipse(e,t,i,i,s);return n.shape='circle',n}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push(this.lib.solidFillPolygon(e,i)):s.push(this.lib.patternFillPolygon(e,i))),s.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',s,i)}arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,s,n,a,r));return o.push(this.lib.arc(e,t,i,s,n,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push(this.lib.svgPath(e,i)),this._drawable('path',s,i)}toPaths(e){const t=e.sets||[],s=e.options||this.defaultOptions,n=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:s.stroke,strokeWidth:s.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,s);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:s.fill||'none'};break;case'path2Dpattern':{const t=a.size,n={x:0,y:0,width:1,height:1,viewBox:`0 0 ${i(t[0])} ${i(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,s)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:n};break}}e&&n.push(e)}return n}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const T='undefined'!=typeof document;class C{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new S(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new k}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a),a}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a),a}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a),a}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n),n}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.ctx;for(const n of t)switch(n.type){case'path':s.save(),s.strokeStyle=i.stroke,s.lineWidth=i.strokeWidth,this._drawToContext(s,n),s.restore();break;case'fillPath':s.save(),s.fillStyle=i.fill||'',this._drawToContext(s,n),s.restore();break;case'fillSketch':this.fillSketch(s,n,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(n.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(T){const e=n.size,t=document.createElement('canvas'),s=t.getContext('2d'),a=this.computeBBox(n.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,s.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(s,n,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(n.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(T)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const s=i.getBBox();return document.body.removeChild(t),s}catch(e){}return null}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=s,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class E extends S{async line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[await this.lib.line(e,t,i,s,a)],a)}async rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(n,a))):l.push((await this.lib.patternFillPolygon(n,a)))}return l.push((await this.lib.rectangle(e,t,i,s,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=await this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push((await this.lib.patternFillEllipse(e,t,i,s,a)));return l.push((await this.lib.ellipse(e,t,i,s,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,s){const n=await this.ellipse(e,t,i,i,s);return n.shape='circle',n}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),s=[];return i.fill&&('solid'===i.fillStyle?s.push((await this.lib.solidFillPolygon(e,i))):s.push((await this.lib.patternFillPolygon(e,i)))),s.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',s,i)}async arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,s,n,a,r)));return o.push((await this.lib.arc(e,t,i,s,n,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push((await this.lib.svgPath(e,i))),this._drawable('path',s,i)}}class N extends C{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a),a}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a),a}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a),a}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n),n}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const W='undefined'!=typeof document;class z{constructor(e,t){this.svg=e,this.gen=new S(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new k}get defs(){if(W&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a)}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a)}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a)}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],s=e.options||this.gen.defaultOptions,n=this.svg.ownerDocument||W&&document,a=n.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=s.stroke,e.style.strokeWidth=s.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'fillSketch':{e=this.fillSketch(n,l,s);break}case'path2Dfill':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=s.fill;break}case'path2Dpattern':{const t=l.size,a=n.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${p(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${i(t[0])} ${i(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const r=this.fillSketch(n,l,s);a.appendChild(r),this.defs.appendChild(a),e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2);const n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=i.fill,n.style.strokeWidth=s+'',n.style.fill='none',n}}class L extends z{constructor(e,t){super(e,t),this.genAsync=new E(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a)}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a)}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a)}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var R={canvas(e,t){return t&&t.async?new N(e,t):new C(e,t)},svg(e,t){return t&&t.async?new L(e,t):new z(e,t)},createRenderer(){return C.createRenderer()},generator(e,t){return e&&e.async?new E(e,t):new S(e,t)}};return R}); +(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var n=Math.round,s=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,f=Math.cos,h=Math.sin,g=Math.abs,c=Math.PI;const u={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const n=this.tokenize(t);let s=0,i=n[s],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a].length:(s++,l=u[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)s++,l=u[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(s+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=f(this._theta),t=h(this._theta),i=this._theta+this._delta,n=f(i),s=h(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=f(y),x=h(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=h&&(h=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=h/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*f(x),u+_*h(x),d)),v=v.concat(this.doubleLine(o,u,o+y*f(b),u+_*h(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*f(x),u+_*h(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*f(g),p+d*h(g)]);return x.push([o+r*f(y),p+d*h(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const f=r/2,h=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,s),t+this.getOffset(-f,f,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,s),c+t+(n-t)*h+this.getOffset(-f,f,s),g+e+2*(i-e)*h+this.getOffset(-f,f,s),c+t+2*(n-t)*h+this.getOffset(-f,f,s),i+this.getOffset(-f,f,s),n+this.getOffset(-f,f,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-r,r,s),c+t+(n-t)*h+this.getOffset(-r,r,s),g+e+2*(i-e)*h+this.getOffset(-r,r,s),c+t+2*(n-t)*h+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,f,n)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class E{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(C&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const n=self.document.createElementNS('http://www.w3.org/2000/svg','path');n.setAttribute('d',e),i.appendChild(n),self.document.body.appendChild(i);const s=n.getBBox();s&&(t[0]=s.width||0,t[1]=s.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=p(t[0],i[0]),t[1]=p(t[1],i[1]),t}line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[this.lib.line(e,t,i,n,a)],a)}rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(s,a)):l.push(this.lib.patternFillPolygon(s,a))}return l.push(this.lib.rectangle(e,t,i,n,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push(this.lib.patternFillEllipse(e,t,i,n,a));return l.push(this.lib.ellipse(e,t,i,n,a)),this._drawable('ellipse',l,a)}circle(e,t,i,n){const s=this.ellipse(e,t,i,i,n);return s.shape='circle',s}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push(this.lib.solidFillPolygon(e,i)):n.push(this.lib.patternFillPolygon(e,i))),n.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',n,i)}arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,n,s,a,r));return o.push(this.lib.arc(e,t,i,n,s,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push(this.lib.svgPath(e,i)),this._drawable('path',n,i)}toPaths(e){const t=e.sets||[],i=e.options||this.defaultOptions,s=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:i.stroke,strokeWidth:i.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,i);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'path2Dpattern':{const t=a.size,s={x:0,y:0,width:1,height:1,viewBox:`0 0 ${n(t[0])} ${n(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,i)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:s};break}}e&&s.push(e)}return s}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const L='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new E(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(L){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(L)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class N extends E{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push((await this.lib.solidFillPolygon(e,i))):n.push((await this.lib.patternFillPolygon(e,i)))),n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class R extends W{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const z='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new E(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(z&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||z&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new R(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new N(e,t):new E(e,t)}};return M}); diff --git a/srcts/generator.ts b/srcts/generator.ts index d1771b2..c329350 100644 --- a/srcts/generator.ts +++ b/srcts/generator.ts @@ -1,13 +1,14 @@ import { RoughRenderer } from './renderer.js'; import { Config, DrawingSurface, Options, Drawable, OpSet, PathInfo, PatternInfo } from './core'; import { Point } from './geometry.js'; +import { createRenderer } from './renderer-factory.js'; const hasSelf = typeof self !== 'undefined'; export class RoughGenerator { private config: Config; private surface: DrawingSurface; - private renderer?: RoughRenderer; + private renderer: RoughRenderer; defaultOptions: Options = { maxRandomnessOffset: 2, roughness: 1, @@ -26,6 +27,7 @@ export class RoughGenerator { constructor(config: Config | null, surface: DrawingSurface) { this.config = config || {}; this.surface = surface; + this.renderer = createRenderer(this.config); if (this.config.options) { this.defaultOptions = this._options(this.config.options); } @@ -40,9 +42,6 @@ export class RoughGenerator { } protected get lib(): RoughRenderer { - if (!this.renderer) { - this.renderer = new RoughRenderer(); - } return this.renderer; } diff --git a/srcts/renderer-factory.ts b/srcts/renderer-factory.ts new file mode 100644 index 0000000..8261095 --- /dev/null +++ b/srcts/renderer-factory.ts @@ -0,0 +1,17 @@ +import { Config } from './core'; +import { RoughRenderer } from './renderer'; + +const hasSelf = typeof self !== 'undefined'; +const roughScript = hasSelf && self && self.document && self.document.currentScript && (self.document.currentScript as HTMLScriptElement).src; + +export function createRenderer(config: Config): RoughRenderer { + if (hasSelf && roughScript && self && (self as any).workly && config.async && (!config.noWorker)) { + const worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; + if (worklySource) { + const code = `importScripts('${worklySource}', '${roughScript}');\nworkly.expose(self.rough.createRenderer());`; + const ourl = URL.createObjectURL(new Blob([code])); + return (self as any).workly.proxy(ourl); + } + } + return new RoughRenderer(); +} \ No newline at end of file From 09d28393f6609ba3c1e9e329fdfee89bbc535f9f Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Mon, 18 Jun 2018 13:08:58 -0700 Subject: [PATCH 12/16] removed old code and checking in bin for package --- .gitignore | 3 +- bin/canvas-async.d.ts | 18 + bin/canvas-async.js | 66 ++ bin/canvas.d.ts | 25 + bin/canvas.js | 174 +++++ bin/common/core.d.ts | 29 + bin/common/core.js | 0 bin/common/geometry.d.ts | 7 + bin/common/geometry.js | 0 bin/core.d.ts | 59 ++ bin/core.js | 0 bin/fillers/filler-interface.d.ts | 10 + bin/fillers/filler-interface.js | 0 bin/fillers/filler.d.ts | 3 + bin/fillers/filler.js | 29 + bin/fillers/hachure-filler.d.ts | 13 + bin/fillers/hachure-filler.js | 117 ++++ bin/fillers/hatch-filler.d.ts | 7 + bin/fillers/hatch-filler.js | 17 + bin/fillers/zigzag-filler.d.ts | 7 + bin/fillers/zigzag-filler.js | 9 + bin/generator-async.d.ts | 14 + bin/generator-async.js | 119 ++++ bin/generator.d.ts | 27 + bin/generator.js | 275 ++++++++ bin/geometry.d.ts | 22 + bin/geometry.js | 100 +++ bin/path.d.ts | 52 ++ bin/path.js | 427 ++++++++++++ bin/renderer-factory.d.ts | 3 + bin/renderer-factory.js | 14 + bin/renderer.d.ts | 25 + bin/renderer.js | 597 +++++++++++++++++ bin/rough.d.ts | 15 + bin/rough.js | 29 + bin/svg-async.d.ts | 18 + bin/svg-async.js | 57 ++ bin/svg.d.ts | 25 + bin/svg.js | 147 +++++ bin/utils/hachure.d.ts | 17 + bin/utils/hachure.js | 76 +++ bin/utils/liang-barsky.d.ts | 2 + bin/utils/liang-barsky.js | 51 ++ package.json | 6 +- {srcts => src}/canvas-async.ts | 0 src/canvas.js | 248 ------- {srcts => src}/canvas.ts | 0 {srcts => src}/core.ts | 0 {srcts => src}/fillers/filler-interface.ts | 0 {srcts => src}/fillers/filler.ts | 0 {srcts => src}/fillers/hachure-filler.ts | 0 {srcts => src}/fillers/hatch-filler.ts | 0 {srcts => src}/fillers/zigzag-filler.ts | 0 {srcts => src}/generator-async.ts | 0 src/generator.js | 418 ------------ {srcts => src}/generator.ts | 0 {srcts => src}/geometry.ts | 0 src/hachure.js | 73 --- src/index.js | 27 - src/path.js | 446 ------------- {srcts => src}/path.ts | 0 {srcts => src}/renderer-factory.ts | 0 src/renderer.js | 714 --------------------- {srcts => src}/renderer.ts | 0 {srcts => src}/rough.ts | 0 src/segment.js | 131 ---- {srcts => src}/svg-async.ts | 0 src/svg.js | 210 ------ {srcts => src}/svg.ts | 0 {srcts => src}/utils/hachure.ts | 0 {srcts => src}/utils/liang-barsky.ts | 0 tsconfig.json | 2 +- 72 files changed, 2708 insertions(+), 2272 deletions(-) create mode 100644 bin/canvas-async.d.ts create mode 100644 bin/canvas-async.js create mode 100644 bin/canvas.d.ts create mode 100644 bin/canvas.js create mode 100644 bin/common/core.d.ts create mode 100644 bin/common/core.js create mode 100644 bin/common/geometry.d.ts create mode 100644 bin/common/geometry.js create mode 100644 bin/core.d.ts create mode 100644 bin/core.js create mode 100644 bin/fillers/filler-interface.d.ts create mode 100644 bin/fillers/filler-interface.js create mode 100644 bin/fillers/filler.d.ts create mode 100644 bin/fillers/filler.js create mode 100644 bin/fillers/hachure-filler.d.ts create mode 100644 bin/fillers/hachure-filler.js create mode 100644 bin/fillers/hatch-filler.d.ts create mode 100644 bin/fillers/hatch-filler.js create mode 100644 bin/fillers/zigzag-filler.d.ts create mode 100644 bin/fillers/zigzag-filler.js create mode 100644 bin/generator-async.d.ts create mode 100644 bin/generator-async.js create mode 100644 bin/generator.d.ts create mode 100644 bin/generator.js create mode 100644 bin/geometry.d.ts create mode 100644 bin/geometry.js create mode 100644 bin/path.d.ts create mode 100644 bin/path.js create mode 100644 bin/renderer-factory.d.ts create mode 100644 bin/renderer-factory.js create mode 100644 bin/renderer.d.ts create mode 100644 bin/renderer.js create mode 100644 bin/rough.d.ts create mode 100644 bin/rough.js create mode 100644 bin/svg-async.d.ts create mode 100644 bin/svg-async.js create mode 100644 bin/svg.d.ts create mode 100644 bin/svg.js create mode 100644 bin/utils/hachure.d.ts create mode 100644 bin/utils/hachure.js create mode 100644 bin/utils/liang-barsky.d.ts create mode 100644 bin/utils/liang-barsky.js rename {srcts => src}/canvas-async.ts (100%) delete mode 100644 src/canvas.js rename {srcts => src}/canvas.ts (100%) rename {srcts => src}/core.ts (100%) rename {srcts => src}/fillers/filler-interface.ts (100%) rename {srcts => src}/fillers/filler.ts (100%) rename {srcts => src}/fillers/hachure-filler.ts (100%) rename {srcts => src}/fillers/hatch-filler.ts (100%) rename {srcts => src}/fillers/zigzag-filler.ts (100%) rename {srcts => src}/generator-async.ts (100%) delete mode 100644 src/generator.js rename {srcts => src}/generator.ts (100%) rename {srcts => src}/geometry.ts (100%) delete mode 100644 src/hachure.js delete mode 100644 src/index.js delete mode 100644 src/path.js rename {srcts => src}/path.ts (100%) rename {srcts => src}/renderer-factory.ts (100%) delete mode 100644 src/renderer.js rename {srcts => src}/renderer.ts (100%) rename {srcts => src}/rough.ts (100%) delete mode 100644 src/segment.js rename {srcts => src}/svg-async.ts (100%) delete mode 100644 src/svg.js rename {srcts => src}/svg.ts (100%) rename {srcts => src}/utils/hachure.ts (100%) rename {srcts => src}/utils/liang-barsky.ts (100%) diff --git a/.gitignore b/.gitignore index 4e5d4b3..7a2cbce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .cache .DS_Store -z node_modules -bin \ No newline at end of file +z \ No newline at end of file diff --git a/bin/canvas-async.d.ts b/bin/canvas-async.d.ts new file mode 100644 index 0000000..fa20b0f --- /dev/null +++ b/bin/canvas-async.d.ts @@ -0,0 +1,18 @@ +import { RoughCanvas } from './canvas'; +import { Config, Options, Drawable } from './core'; +import { RoughGeneratorAsync } from './generator-async'; +import { Point } from './geometry'; +export declare class RoughCanvasAsync extends RoughCanvas { + private genAsync; + constructor(canvas: HTMLCanvasElement, config?: Config); + readonly generator: RoughGeneratorAsync; + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Promise; + rectangle(x: number, y: number, width: number, height: number, options?: Options): Promise; + ellipse(x: number, y: number, width: number, height: number, options?: Options): Promise; + circle(x: number, y: number, diameter: number, options?: Options): Promise; + linearPath(points: Point[], options?: Options): Promise; + polygon(points: Point[], options?: Options): Promise; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed?: boolean, options?: Options): Promise; + curve(points: Point[], options?: Options): Promise; + path(d: string, options?: Options): Promise; +} diff --git a/bin/canvas-async.js b/bin/canvas-async.js new file mode 100644 index 0000000..d04f838 --- /dev/null +++ b/bin/canvas-async.js @@ -0,0 +1,66 @@ +import { RoughCanvas } from './canvas'; +import { RoughGeneratorAsync } from './generator-async'; +export class RoughCanvasAsync extends RoughCanvas { + constructor(canvas, config) { + super(canvas, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.canvas); + } + // @ts-ignore + get generator() { + return this.genAsync; + } + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const d = await this.genAsync.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const d = await this.genAsync.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + async circle(x, y, diameter, options) { + const d = await this.genAsync.circle(x, y, diameter, options); + this.draw(d); + return d; + } + // @ts-ignore + async linearPath(points, options) { + const d = await this.genAsync.linearPath(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async polygon(points, options) { + const d = await this.genAsync.polygon(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + // @ts-ignore + async curve(points, options) { + const d = await this.genAsync.curve(points, options); + this.draw(d); + return d; + } + // @ts-ignore + async path(d, options) { + const drawing = await this.genAsync.path(d, options); + this.draw(drawing); + return drawing; + } +} diff --git a/bin/canvas.d.ts b/bin/canvas.d.ts new file mode 100644 index 0000000..bcc4e7e --- /dev/null +++ b/bin/canvas.d.ts @@ -0,0 +1,25 @@ +import { Config, Options, Drawable } from './core'; +import { RoughGenerator } from './generator'; +import { RoughRenderer } from './renderer'; +import { Point } from './geometry'; +export declare class RoughCanvas { + protected canvas: HTMLCanvasElement; + protected ctx: CanvasRenderingContext2D; + private gen; + constructor(canvas: HTMLCanvasElement, config?: Config); + readonly generator: RoughGenerator; + static createRenderer(): RoughRenderer; + 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; + draw(drawable: Drawable): void; + private computeBBox; + private fillSketch; + private _drawToContext; +} diff --git a/bin/canvas.js b/bin/canvas.js new file mode 100644 index 0000000..f7cf87b --- /dev/null +++ b/bin/canvas.js @@ -0,0 +1,174 @@ +import { RoughGenerator } from './generator'; +import { RoughRenderer } from './renderer'; +const hasDocument = typeof document !== 'undefined'; +export class RoughCanvas { + constructor(canvas, config) { + this.canvas = canvas; + this.ctx = this.canvas.getContext('2d'); + this.gen = new RoughGenerator(config || null, this.canvas); + } + get generator() { + return this.gen; + } + static createRenderer() { + return new RoughRenderer(); + } + 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; + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const ctx = this.ctx; + for (const drawing of sets) { + switch (drawing.type) { + case 'path': + ctx.save(); + ctx.strokeStyle = o.stroke; + ctx.lineWidth = o.strokeWidth; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillPath': + ctx.save(); + ctx.fillStyle = o.fill || ''; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillSketch': + this.fillSketch(ctx, drawing, o); + break; + case 'path2Dfill': { + this.ctx.save(); + this.ctx.fillStyle = o.fill || ''; + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + break; + } + case 'path2Dpattern': { + if (hasDocument) { + const size = drawing.size; + const hcanvas = document.createElement('canvas'); + const hcontext = hcanvas.getContext('2d'); + const bbox = this.computeBBox(drawing.path); + if (bbox && (bbox.width || bbox.height)) { + hcanvas.width = this.canvas.width; + hcanvas.height = this.canvas.height; + hcontext.translate(bbox.x || 0, bbox.y || 0); + } + else { + hcanvas.width = size[0]; + hcanvas.height = size[1]; + } + this.fillSketch(hcontext, drawing, o); + this.ctx.save(); + this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); + const p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + } + break; + } + } + } + } + computeBBox(d) { + if (hasDocument) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + document.body.appendChild(svg); + const bbox = pathNode.getBBox(); + document.body.removeChild(svg); + return bbox; + } + catch (err) { } + } + return null; + } + fillSketch(ctx, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + ctx.save(); + ctx.strokeStyle = o.fill || ''; + ctx.lineWidth = fweight; + this._drawToContext(ctx, drawing); + ctx.restore(); + } + _drawToContext(ctx, drawing) { + 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 'qcurveTo': + ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); + break; + case 'lineTo': + ctx.lineTo(data[0], data[1]); + break; + } + } + if (drawing.type === 'fillPath') { + ctx.fill(); + } + else { + ctx.stroke(); + } + } +} diff --git a/bin/common/core.d.ts b/bin/common/core.d.ts new file mode 100644 index 0000000..6b1411b --- /dev/null +++ b/bin/common/core.d.ts @@ -0,0 +1,29 @@ +export interface Options { + maxRandomnessOffset: number; + roughness: number; + bowing: number; + stroke: string; + strokeWidth: number; + curveTightness: number; + curveStepCount: number; + fill: string | null; + fillStyle: string; + fillWeight: number; + hachureAngle: number; + hachureGap: number; +} +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[]; +} +export interface Drawable { + shape: string; + options: Options; + sets: OpSet[]; +} diff --git a/bin/common/core.js b/bin/common/core.js new file mode 100644 index 0000000..e69de29 diff --git a/bin/common/geometry.d.ts b/bin/common/geometry.d.ts new file mode 100644 index 0000000..e708278 --- /dev/null +++ b/bin/common/geometry.d.ts @@ -0,0 +1,7 @@ +export declare type Point = [number, number]; +export interface Rectangle { + x: number; + y: number; + width: number; + height: number; +} diff --git a/bin/common/geometry.js b/bin/common/geometry.js new file mode 100644 index 0000000..e69de29 diff --git a/bin/core.d.ts b/bin/core.d.ts new file mode 100644 index 0000000..d978eda --- /dev/null +++ b/bin/core.d.ts @@ -0,0 +1,59 @@ +import { Point } from './geometry'; +export interface Config { + async?: boolean; + options?: Options; + noWorker?: boolean; + worklyURL?: string; +} +export interface DrawingSurface { + width: number | SVGAnimatedLength; + height: number | SVGAnimatedLength; +} +export interface Options { + maxRandomnessOffset: number; + roughness: number; + bowing: number; + stroke: string; + strokeWidth: number; + curveTightness: number; + curveStepCount: number; + fill: string | null; + fillStyle: string; + fillWeight: number; + hachureAngle: number; + hachureGap: number; + simplification?: number; +} +export declare type OpType = 'move' | 'bcurveTo' | 'lineTo' | 'qcurveTo'; +export declare type OpSetType = 'path' | 'fillPath' | 'fillSketch' | 'path2Dfill' | 'path2Dpattern'; +export interface Op { + op: OpType; + data: number[]; +} +export interface OpSet { + type: OpSetType; + ops: Op[]; + size?: Point; + path?: string; +} +export interface Drawable { + shape: string; + options: Options; + sets: OpSet[]; +} +export interface PathInfo { + d: string; + stroke: string; + strokeWidth: number; + fill?: string; + pattern?: PatternInfo; +} +export interface PatternInfo { + x: number; + y: number; + width: number; + height: number; + viewBox: string; + patternUnits: string; + path: PathInfo; +} diff --git a/bin/core.js b/bin/core.js new file mode 100644 index 0000000..e69de29 diff --git a/bin/fillers/filler-interface.d.ts b/bin/fillers/filler-interface.d.ts new file mode 100644 index 0000000..21e58b4 --- /dev/null +++ b/bin/fillers/filler-interface.d.ts @@ -0,0 +1,10 @@ +import { Options, OpSet, Op } from '../core'; +import { Point } from '../geometry'; +export interface PatternFiller { + fillPolygon(points: Point[], o: Options): OpSet; + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet; +} +export interface RenderHelper { + doubleLine(x1: number, y1: number, x2: number, y2: number, o: Options): Op[]; + getOffset(min: number, max: number, ops: Options): number; +} diff --git a/bin/fillers/filler-interface.js b/bin/fillers/filler-interface.js new file mode 100644 index 0000000..e69de29 diff --git a/bin/fillers/filler.d.ts b/bin/fillers/filler.d.ts new file mode 100644 index 0000000..9421eaa --- /dev/null +++ b/bin/fillers/filler.d.ts @@ -0,0 +1,3 @@ +import { Options } from '../core'; +import { PatternFiller, RenderHelper } from './filler-interface'; +export declare function getFiller(renderer: RenderHelper, o: Options): PatternFiller; diff --git a/bin/fillers/filler.js b/bin/fillers/filler.js new file mode 100644 index 0000000..39752f9 --- /dev/null +++ b/bin/fillers/filler.js @@ -0,0 +1,29 @@ +import { HachureFiller } from './hachure-filler'; +import { ZigZagFiller } from './zigzag-filler'; +import { HatchFiller } from './hatch-filler'; +const fillers = {}; +export function getFiller(renderer, o) { + let fillerName = o.fillStyle || 'hachure'; + if (!fillers[fillerName]) { + switch (fillerName) { + case 'zigzag': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagFiller(renderer); + } + break; + case 'cross-hatch': + if (!fillers[fillerName]) { + fillers[fillerName] = new HatchFiller(renderer); + } + break; + case 'hachure': + default: + fillerName = 'hachure'; + if (!fillers[fillerName]) { + fillers[fillerName] = new HachureFiller(renderer); + } + break; + } + } + return fillers[fillerName]; +} diff --git a/bin/fillers/hachure-filler.d.ts b/bin/fillers/hachure-filler.d.ts new file mode 100644 index 0000000..eb684c2 --- /dev/null +++ b/bin/fillers/hachure-filler.d.ts @@ -0,0 +1,13 @@ +import { PatternFiller, RenderHelper } from './filler-interface'; +import { Options, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class HachureFiller implements PatternFiller { + renderer: RenderHelper; + constructor(renderer: RenderHelper); + fillPolygon(points: Point[], o: Options): OpSet; + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet; + protected _fillPolygon(points: Point[], o: Options, connectEnds?: boolean): OpSet; + protected _fillEllipse(cx: number, cy: number, width: number, height: number, o: Options, connectEnds?: boolean): OpSet; + private getIntersectingLines; + private affine; +} diff --git a/bin/fillers/hachure-filler.js b/bin/fillers/hachure-filler.js new file mode 100644 index 0000000..123c6a1 --- /dev/null +++ b/bin/fillers/hachure-filler.js @@ -0,0 +1,117 @@ +import { Segment } from '../geometry'; +import { HachureIterator } from '../utils/hachure'; +export class HachureFiller { + constructor(renderer) { + this.renderer = renderer; + } + fillPolygon(points, o) { + return this._fillPolygon(points, o); + } + fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o); + } + _fillPolygon(points, o, connectEnds = false) { + let ops = []; + if (points && points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const cosAngle = Math.cos(hachureAngle); + const sinAngle = Math.sin(hachureAngle); + const tanAngle = Math.tan(hachureAngle); + const it = new HachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); + let rect; + let prevPoint = null; + while ((rect = it.nextLine()) != null) { + const lines = this.getIntersectingLines(rect, points); + for (let i = 0; i < lines.length; i++) { + if (i < (lines.length - 1)) { + const p1 = lines[i]; + const p2 = lines[i + 1]; + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + } + } + } + return { type: 'fillSketch', ops }; + } + _fillEllipse(cx, cy, width, height, o, connectEnds = false) { + let ops = []; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.renderer.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.renderer.getOffset(-ry * 0.05, ry * 0.05, o); + const angle = o.hachureAngle; + let gap = o.hachureGap; + if (gap <= 0) { + gap = o.strokeWidth * 4; + } + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const radPerDeg = Math.PI / 180; + const hachureAngle = (angle % 180) * radPerDeg; + const tanAngle = Math.tan(hachureAngle); + const aspectRatio = ry / rx; + const hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); + const sinAnglePrime = aspectRatio * tanAngle / hyp; + const cosAnglePrime = 1 / hyp; + const gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); + let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); + let prevPoint = null; + for (let xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { + halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); + const p1 = this.affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + const p2 = this.affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + return { type: 'fillSketch', ops }; + } + getIntersectingLines(line, points) { + const intersections = []; + const s1 = new Segment([line[0], line[1]], [line[2], line[3]]); + for (let i = 0; i < points.length; i++) { + const s2 = new Segment(points[i], points[(i + 1) % points.length]); + if (s1.intersects(s2)) { + intersections.push([s1.xi, s1.yi]); + } + } + return intersections; + } + affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { + const A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; + const B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; + const C = cosAnglePrime; + const D = sinAnglePrime; + const E = -R * sinAnglePrime; + const F = R * cosAnglePrime; + return [ + A + C * x + D * y, + B + E * x + F * y + ]; + } +} diff --git a/bin/fillers/hatch-filler.d.ts b/bin/fillers/hatch-filler.d.ts new file mode 100644 index 0000000..089743b --- /dev/null +++ b/bin/fillers/hatch-filler.d.ts @@ -0,0 +1,7 @@ +import { HachureFiller } from './hachure-filler'; +import { Options, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class HatchFiller extends HachureFiller { + fillPolygon(points: Point[], o: Options): OpSet; + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet; +} diff --git a/bin/fillers/hatch-filler.js b/bin/fillers/hatch-filler.js new file mode 100644 index 0000000..0f74086 --- /dev/null +++ b/bin/fillers/hatch-filler.js @@ -0,0 +1,17 @@ +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; + } + fillEllipse(cx, cy, width, height, o) { + const set = this._fillEllipse(cx, cy, width, height, o); + const o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + const set2 = this._fillEllipse(cx, cy, width, height, o2); + set.ops = set.ops.concat(set2.ops); + return set; + } +} diff --git a/bin/fillers/zigzag-filler.d.ts b/bin/fillers/zigzag-filler.d.ts new file mode 100644 index 0000000..cd6ba95 --- /dev/null +++ b/bin/fillers/zigzag-filler.d.ts @@ -0,0 +1,7 @@ +import { HachureFiller } from './hachure-filler'; +import { Options, OpSet } from '../core'; +import { Point } from '../geometry'; +export declare class ZigZagFiller extends HachureFiller { + fillPolygon(points: Point[], o: Options): OpSet; + fillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet; +} diff --git a/bin/fillers/zigzag-filler.js b/bin/fillers/zigzag-filler.js new file mode 100644 index 0000000..e8597bf --- /dev/null +++ b/bin/fillers/zigzag-filler.js @@ -0,0 +1,9 @@ +import { HachureFiller } from './hachure-filler'; +export class ZigZagFiller extends HachureFiller { + fillPolygon(points, o) { + return this._fillPolygon(points, o, true); + } + fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o, true); + } +} diff --git a/bin/generator-async.d.ts b/bin/generator-async.d.ts new file mode 100644 index 0000000..bbe56d3 --- /dev/null +++ b/bin/generator-async.d.ts @@ -0,0 +1,14 @@ +import { RoughGenerator } from './generator'; +import { Options, Drawable } from './core'; +import { Point } from './geometry.js'; +export declare class RoughGeneratorAsync extends RoughGenerator { + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Promise; + rectangle(x: number, y: number, width: number, height: number, options?: Options): Promise; + ellipse(x: number, y: number, width: number, height: number, options?: Options): Promise; + circle(x: number, y: number, diameter: number, options?: Options): Promise; + linearPath(points: Point[], options?: Options): Promise; + polygon(points: Point[], options?: Options): Promise; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed?: boolean, options?: Options): Promise; + curve(points: Point[], options?: Options): Promise; + path(d: string, options?: Options): Promise; +} diff --git a/bin/generator-async.js b/bin/generator-async.js new file mode 100644 index 0000000..57b590e --- /dev/null +++ b/bin/generator-async.js @@ -0,0 +1,119 @@ +import { RoughGenerator } from './generator'; +export class RoughGeneratorAsync extends RoughGenerator { + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const o = this._options(options); + return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(await this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(await this.lib.ellipse(x, y, width, height, o)); + return this._drawable('ellipse', paths, o); + } + // @ts-ignore + async circle(x, y, diameter, options) { + const ret = await this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + // @ts-ignore + async linearPath(points, options) { + const o = this._options(options); + return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); + } + // @ts-ignore + async polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(await this.lib.patternFillPolygon(points, o)); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(await this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(await this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + // @ts-ignore + async curve(points, options) { + const o = this._options(options); + return this._drawable('curve', [await this.lib.curve(points, o)], o); + } + // @ts-ignore + async path(d, options) { + const o = this._options(options); + const paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } + else { + const size = this.computePathSize(d); + const points = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(await this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } +} diff --git a/bin/generator.d.ts b/bin/generator.d.ts new file mode 100644 index 0000000..a5dde7b --- /dev/null +++ b/bin/generator.d.ts @@ -0,0 +1,27 @@ +import { RoughRenderer } from './renderer.js'; +import { Config, DrawingSurface, Options, Drawable, OpSet, PathInfo } from './core'; +import { Point } from './geometry.js'; +export declare class RoughGenerator { + private config; + private surface; + private renderer; + defaultOptions: Options; + constructor(config: Config | null, surface: DrawingSurface); + protected _options(options?: Options): Options; + protected _drawable(shape: string, sets: OpSet[], options: Options): Drawable; + protected readonly lib: RoughRenderer; + private getCanvasSize; + protected computePathSize(d: string): Point; + 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; + toPaths(drawable: Drawable): PathInfo[]; + private fillSketch; + opsToPath(drawing: OpSet): string; +} diff --git a/bin/generator.js b/bin/generator.js new file mode 100644 index 0000000..54d0fcf --- /dev/null +++ b/bin/generator.js @@ -0,0 +1,275 @@ +import { createRenderer } from './renderer-factory.js'; +const hasSelf = typeof self !== 'undefined'; +export class RoughGenerator { + constructor(config, surface) { + this.defaultOptions = { + maxRandomnessOffset: 2, + roughness: 1, + bowing: 1, + stroke: '#000', + strokeWidth: 1, + curveTightness: 0, + curveStepCount: 9, + fill: null, + fillStyle: 'hachure', + fillWeight: -1, + hachureAngle: -41, + hachureGap: -1 + }; + this.config = config || {}; + this.surface = surface; + this.renderer = createRenderer(this.config); + if (this.config.options) { + this.defaultOptions = this._options(this.config.options); + } + } + _options(options) { + return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; + } + _drawable(shape, sets, options) { + return { shape, sets: sets || [], options: options || this.defaultOptions }; + } + get lib() { + return this.renderer; + } + getCanvasSize() { + const val = (w) => { + if (w && typeof w === 'object') { + if (w.baseVal && w.baseVal.value) { + return w.baseVal.value; + } + } + return w || 100; + }; + if (this.surface) { + return [val(this.surface.width), val(this.surface.height)]; + } + return [100, 100]; + } + computePathSize(d) { + let size = [0, 0]; + if (hasSelf && self.document) { + try { + const ns = 'http://www.w3.org/2000/svg'; + const svg = self.document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + const pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + self.document.body.appendChild(svg); + const bb = pathNode.getBBox(); + if (bb) { + size[0] = bb.width || 0; + size[1] = bb.height || 0; + } + self.document.body.removeChild(svg); + } + catch (err) { } + } + const canvasSize = this.getCanvasSize(); + if (!(size[0] * size[1])) { + size = canvasSize; + } + size[0] = Math.min(size[0], canvasSize[0]); + size[1] = Math.min(size[1], canvasSize[1]); + return size; + } + line(x1, y1, x2, y2, options) { + const o = this._options(options); + return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + } + rectangle(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + ellipse(x, y, width, height, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(this.lib.ellipse(x, y, width, height, o)); + return this._drawable('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._options(options); + return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + } + polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + arc(x, y, width, height, start, stop, closed = false, options) { + const o = this._options(options); + const paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + const shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } + else { + paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + curve(points, options) { + const o = this._options(options); + return this._drawable('curve', [this.lib.curve(points, o)], o); + } + path(d, options) { + const o = this._options(options); + const paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + const shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } + else { + const size = this.computePathSize(d); + const points = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(points, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = d; + paths.push(shape); + } + } + paths.push(this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } + 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: 'none' + }; + break; + case 'fillPath': + path = { + d: this.opsToPath(drawing), + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'fillSketch': + path = this.fillSketch(drawing, o); + break; + case 'path2Dfill': + path = { + d: drawing.path || '', + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'path2Dpattern': { + const size = drawing.size; + const pattern = { + x: 0, y: 0, width: 1, height: 1, + viewBox: `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`, + patternUnits: 'objectBoundingBox', + path: this.fillSketch(drawing, o) + }; + path = { + d: drawing.path, + stroke: 'none', + strokeWidth: 0, + pattern: pattern + }; + 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 || 'none', + strokeWidth: fweight, + fill: 'none' + }; + } + 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 'qcurveTo': + path += `Q${data[0]} ${data[1]}, ${data[2]} ${data[3]} `; + break; + case 'lineTo': + path += `L${data[0]} ${data[1]} `; + break; + } + } + return path.trim(); + } +} diff --git a/bin/geometry.d.ts b/bin/geometry.d.ts new file mode 100644 index 0000000..c2e4afa --- /dev/null +++ b/bin/geometry.d.ts @@ -0,0 +1,22 @@ +export declare type Point = [number, number]; +export interface Rectangle { + x: number; + y: number; + width: number; + height: number; +} +export declare class Segment { + px1: number; + px2: number; + py1: number; + py2: number; + xi: number; + yi: number; + a: number; + b: number; + c: number; + _undefined: boolean; + constructor(p1: Point, p2: Point); + isUndefined(): boolean; + intersects(otherSegment: Segment): boolean; +} diff --git a/bin/geometry.js b/bin/geometry.js new file mode 100644 index 0000000..7efe288 --- /dev/null +++ b/bin/geometry.js @@ -0,0 +1,100 @@ +export class Segment { + constructor(p1, p2) { + this.xi = Number.MAX_VALUE; + this.yi = Number.MAX_VALUE; + this.px1 = p1[0]; + this.py1 = p1[1]; + this.px2 = p2[0]; + this.py2 = p2[1]; + this.a = this.py2 - this.py1; + this.b = this.px1 - this.px2; + this.c = this.px2 * this.py1 - this.px1 * this.py2; + this._undefined = ((this.a === 0) && (this.b === 0) && (this.c === 0)); + } + isUndefined() { + return this._undefined; + } + intersects(otherSegment) { + if (this.isUndefined() || otherSegment.isUndefined()) { + return false; + } + let grad1 = Number.MAX_VALUE; + let grad2 = Number.MAX_VALUE; + let int1 = 0, int2 = 0; + const a = this.a, b = this.b, c = this.c; + if (Math.abs(b) > 0.00001) { + grad1 = -a / b; + int1 = -c / b; + } + if (Math.abs(otherSegment.b) > 0.00001) { + grad2 = -otherSegment.a / otherSegment.b; + int2 = -otherSegment.c / otherSegment.b; + } + if (grad1 === Number.MAX_VALUE) { + if (grad2 === Number.MAX_VALUE) { + if ((-c / a) !== (-otherSegment.c / otherSegment.a)) { + return false; + } + if ((this.py1 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.py2 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py2 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = this.px1; + this.yi = (grad2 * this.xi + int2); + if (((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) || ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001)) { + return false; + } + if (Math.abs(otherSegment.a) < 0.00001) { + if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad2 === Number.MAX_VALUE) { + this.xi = otherSegment.px1; + this.yi = grad1 * this.xi + int1; + if (((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) || ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001)) { + return false; + } + if (Math.abs(a) < 0.00001) { + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad1 === grad2) { + if (int1 !== int2) { + return false; + } + if ((this.px1 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px1 <= Math.max(otherSegment.py1, otherSegment.py2))) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if ((this.px2 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px2 <= Math.max(otherSegment.px1, otherSegment.px2))) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = ((int2 - int1) / (grad1 - grad2)); + this.yi = (grad1 * this.xi + int1); + if (((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) || ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001)) { + return false; + } + return true; + } +} diff --git a/bin/path.d.ts b/bin/path.d.ts new file mode 100644 index 0000000..8598f94 --- /dev/null +++ b/bin/path.d.ts @@ -0,0 +1,52 @@ +import { Point } from './geometry'; +export interface Segment { + key: string; + data: number[]; + point?: Point; +} +export declare class RoughPath { + private parsed; + private _position; + private _first; + private _linearPoints?; + bezierReflectionPoint: Point | null; + quadReflectionPoint: Point | null; + constructor(d: string); + readonly segments: Segment[]; + readonly closed: boolean; + readonly linearPoints: Point[][]; + first: Point | null; + setPosition(x: number, y: number): void; + readonly position: Point; + readonly x: number; + readonly y: number; +} +export interface RoughArcSegment { + cp1: Point; + cp2: Point; + to: Point; +} +export declare class RoughArcConverter { + private _segIndex; + private _numSegs; + private _rx; + private _ry; + private _sinPhi; + private _cosPhi; + private _C; + private _theta; + private _delta; + private _T; + private _from; + constructor(from: Point, to: Point, radii: Point, angle: number, largeArcFlag: boolean, sweepFlag: boolean); + getNextSegment(): RoughArcSegment | null; + calculateVectorAngle(ux: number, uy: number, vx: number, vy: number): number; +} +export declare class PathFitter { + sets: Point[][]; + closed: boolean; + constructor(sets: Point[][], closed: boolean); + fit(simplification: number): string; + distance(p1: Point, p2: Point): number; + reduce(set: Point[], count: number): Point[]; +} diff --git a/bin/path.js b/bin/path.js new file mode 100644 index 0000000..5ae57c5 --- /dev/null +++ b/bin/path.js @@ -0,0 +1,427 @@ +function isType(token, type) { + return token.type === type; +} +const PARAMS = { + A: { length: 7 }, + a: { length: 7 }, + C: { length: 6 }, + c: { length: 6 }, + H: { length: 1 }, + h: { length: 1 }, + L: { length: 2 }, + l: { length: 2 }, + M: { length: 2 }, + m: { length: 2 }, + Q: { length: 4 }, + q: { length: 4 }, + S: { length: 4 }, + s: { length: 4 }, + T: { length: 4 }, + t: { length: 2 }, + V: { length: 1 }, + v: { length: 1 }, + Z: { length: 0 }, + z: { length: 0 } +}; +class ParsedPath { + constructor(d) { + this.COMMAND = 0; + this.NUMBER = 1; + this.EOD = 2; + this.segments = []; + this.parseData(d); + this.processPoints(); + } + 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: this.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: this.NUMBER, text: `${parseFloat(RegExp.$1)}` }; + d = d.substr(RegExp.$1.length); + } + else { + console.error('Unrecognized segment command: ' + d); + return []; + } + } + tokens[tokens.length] = { type: this.EOD, text: '' }; + return tokens; + } + parseData(d) { + const tokens = this.tokenize(d); + let index = 0; + let token = tokens[index]; + let mode = 'BOD'; + this.segments = new Array(); + while (!isType(token, this.EOD)) { + let param_length; + const params = new Array(); + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + else { + this.parseData('M0,0' + d); + return; + } + } + else { + if (isType(token, this.NUMBER)) { + param_length = PARAMS[mode].length; + } + else { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + } + if ((index + param_length) < tokens.length) { + for (let i = index; i < index + param_length; i++) { + const numbeToken = tokens[i]; + if (isType(numbeToken, this.NUMBER)) { + params[params.length] = +numbeToken.text; + } + else { + console.error('Parameter type is not a number: ' + mode + ',' + numbeToken.text); + return; + } + } + if (PARAMS[mode]) { + const segment = { key: mode, data: params }; + this.segments.push(segment); + index += param_length; + token = tokens[index]; + if (mode === 'M') + mode = 'L'; + if (mode === 'm') + mode = 'l'; + } + else { + console.error('Unsupported segment type: ' + mode); + return; + } + } + else { + console.error('Path data ended before all parameters were found'); + } + } + } + get closed() { + if (typeof this._closed === 'undefined') { + this._closed = false; + for (const s of this.segments) { + if (s.key.toLowerCase() === 'z') { + this._closed = true; + } + } + } + return this._closed; + } + processPoints() { + let first = null; + let currentPoint = [0, 0]; + for (let i = 0; i < this.segments.length; i++) { + const s = this.segments[i]; + switch (s.key) { + case 'M': + case 'L': + case 'T': + s.point = [s.data[0], s.data[1]]; + break; + case 'm': + case 'l': + case 't': + s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; + break; + case 'H': + s.point = [s.data[0], currentPoint[1]]; + break; + case 'h': + s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; + break; + case 'V': + s.point = [currentPoint[0], s.data[0]]; + break; + case 'v': + s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; + break; + case 'z': + case 'Z': + if (first) { + s.point = [first[0], first[1]]; + } + break; + case 'C': + s.point = [s.data[4], s.data[5]]; + break; + case 'c': + s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; + break; + case 'S': + s.point = [s.data[2], s.data[3]]; + break; + case 's': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'Q': + s.point = [s.data[2], s.data[3]]; + break; + case 'q': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'A': + s.point = [s.data[5], s.data[6]]; + break; + case 'a': + s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; + break; + } + if (s.key === 'm' || s.key === 'M') { + first = null; + } + if (s.point) { + currentPoint = s.point; + if (!first) { + first = s.point; + } + } + if (s.key === 'z' || s.key === 'Z') { + first = null; + } + } + } +} +export class RoughPath { + constructor(d) { + this._position = [0, 0]; + this._first = null; + this.bezierReflectionPoint = null; + this.quadReflectionPoint = null; + this.parsed = new ParsedPath(d); + } + get segments() { + return this.parsed.segments; + } + get closed() { + return this.parsed.closed; + } + get linearPoints() { + if (!this._linearPoints) { + const lp = []; + let points = []; + for (const s of this.parsed.segments) { + const key = s.key.toLowerCase(); + if (key === 'm' || key === 'z') { + if (points.length) { + lp.push(points); + points = []; + } + if (key === 'z') { + continue; + } + } + if (s.point) { + points.push(s.point); + } + } + if (points.length) { + lp.push(points); + points = []; + } + this._linearPoints = lp; + } + return this._linearPoints; + } + get first() { + return this._first; + } + set first(v) { + this._first = v; + } + setPosition(x, y) { + this._position = [x, y]; + if (!this._first) { + this._first = [x, y]; + } + } + get position() { + return this._position; + } + get x() { + return this._position[0]; + } + get y() { + return this._position[1]; + } +} +// Algorithm as described in https://www.w3.org/TR/SVG/implnote.html +// Code adapted from nsSVGPathDataParser.cpp in Mozilla +// https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 +export class RoughArcConverter { + constructor(from, to, radii, angle, largeArcFlag, sweepFlag) { + this._segIndex = 0; + this._numSegs = 0; + this._rx = 0; + this._ry = 0; + this._sinPhi = 0; + this._cosPhi = 0; + this._C = [0, 0]; + this._theta = 0; + this._delta = 0; + this._T = 0; + this._from = from; + if (from[0] === to[0] && from[1] === to[1]) { + return; + } + const radPerDeg = Math.PI / 180; + this._rx = Math.abs(radii[0]); + this._ry = Math.abs(radii[1]); + this._sinPhi = Math.sin(angle * radPerDeg); + this._cosPhi = Math.cos(angle * radPerDeg); + const x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; + const y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; + let root = 0; + const numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; + if (numerator < 0) { + const s = Math.sqrt(1 - (numerator / (this._rx * this._rx * this._ry * this._ry))); + this._rx = this._rx * s; + this._ry = this._ry * s; + root = 0; + } + else { + root = (largeArcFlag === sweepFlag ? -1.0 : 1.0) * + Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); + } + const cxdash = root * this._rx * y1dash / this._ry; + const cydash = -root * this._ry * x1dash / this._rx; + this._C = [0, 0]; + this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; + this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; + this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); + let dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); + if ((!sweepFlag) && (dtheta > 0)) { + dtheta -= 2 * Math.PI; + } + else if (sweepFlag && (dtheta < 0)) { + dtheta += 2 * Math.PI; + } + this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); + this._delta = dtheta / this._numSegs; + this._T = (8 / 3) * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); + } + getNextSegment() { + if (this._segIndex === this._numSegs) { + return null; + } + const cosTheta1 = Math.cos(this._theta); + const sinTheta1 = Math.sin(this._theta); + const theta2 = this._theta + this._delta; + const cosTheta2 = Math.cos(theta2); + const sinTheta2 = Math.sin(theta2); + const to = [ + this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], + this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1] + ]; + const cp1 = [ + this._from[0] + this._T * (-this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), + this._from[1] + this._T * (-this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1) + ]; + const cp2 = [ + to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), + to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2) + ]; + this._theta = theta2; + this._from = [to[0], to[1]]; + this._segIndex++; + return { + cp1: cp1, + cp2: cp2, + to: to + }; + } + calculateVectorAngle(ux, uy, vx, vy) { + const ta = Math.atan2(uy, ux); + const tb = Math.atan2(vy, vx); + if (tb >= ta) + return tb - ta; + return 2 * Math.PI - (ta - tb); + } +} +export class PathFitter { + constructor(sets, closed) { + this.sets = sets; + this.closed = closed; + } + fit(simplification) { + const outSets = []; + for (const set of this.sets) { + const length = set.length; + let estLength = Math.floor(simplification * length); + if (estLength < 5) { + if (length <= 5) { + continue; + } + estLength = 5; + } + outSets.push(this.reduce(set, estLength)); + } + let d = ''; + for (const set of outSets) { + for (let i = 0; i < set.length; i++) { + const point = set[i]; + if (i === 0) { + d += 'M' + point[0] + ',' + point[1]; + } + else { + d += 'L' + point[0] + ',' + point[1]; + } + } + if (this.closed) { + d += 'z '; + } + } + return d; + } + distance(p1, p2) { + return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); + } + reduce(set, count) { + if (set.length <= count) { + return set; + } + const points = set.slice(0); + while (points.length > count) { + const areas = []; + let minArea = -1; + let minIndex = -1; + for (let i = 1; i < (points.length - 1); i++) { + const a = this.distance(points[i - 1], points[i]); + const b = this.distance(points[i], points[i + 1]); + const c = this.distance(points[i - 1], points[i + 1]); + const s = (a + b + c) / 2.0; + const area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); + areas.push(area); + if ((minArea < 0) || (area < minArea)) { + minArea = area; + minIndex = i; + } + } + if (minIndex > 0) { + points.splice(minIndex, 1); + } + else { + break; + } + } + return points; + } +} diff --git a/bin/renderer-factory.d.ts b/bin/renderer-factory.d.ts new file mode 100644 index 0000000..241b34d --- /dev/null +++ b/bin/renderer-factory.d.ts @@ -0,0 +1,3 @@ +import { Config } from './core'; +import { RoughRenderer } from './renderer'; +export declare function createRenderer(config: Config): RoughRenderer; diff --git a/bin/renderer-factory.js b/bin/renderer-factory.js new file mode 100644 index 0000000..55759ac --- /dev/null +++ b/bin/renderer-factory.js @@ -0,0 +1,14 @@ +import { RoughRenderer } from './renderer'; +const hasSelf = typeof self !== 'undefined'; +const roughScript = hasSelf && self && self.document && self.document.currentScript && self.document.currentScript.src; +export function createRenderer(config) { + if (hasSelf && roughScript && self && self.workly && config.async && (!config.noWorker)) { + const worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; + if (worklySource) { + const code = `importScripts('${worklySource}', '${roughScript}');\nworkly.expose(self.rough.createRenderer());`; + const ourl = URL.createObjectURL(new Blob([code])); + return self.workly.proxy(ourl); + } + } + return new RoughRenderer(); +} diff --git a/bin/renderer.d.ts b/bin/renderer.d.ts new file mode 100644 index 0000000..1667763 --- /dev/null +++ b/bin/renderer.d.ts @@ -0,0 +1,25 @@ +import { Options, OpSet, Op } from './core'; +import { Point } from './geometry'; +export declare class RoughRenderer { + line(x1: number, y1: number, x2: number, y2: number, o: Options): OpSet; + linearPath(points: Point[], close: boolean, o: Options): OpSet; + polygon(points: Point[], o: Options): OpSet; + rectangle(x: number, y: number, width: number, height: number, o: Options): OpSet; + curve(points: Point[], o: Options): OpSet; + ellipse(x: number, y: number, width: number, height: number, o: Options): OpSet; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: Options): OpSet; + svgPath(path: string, o: Options): OpSet; + solidFillPolygon(points: Point[], o: Options): OpSet; + patternFillPolygon(points: Point[], o: Options): OpSet; + patternFillEllipse(cx: number, cy: number, width: number, height: number, o: Options): OpSet; + patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: Options): OpSet; + getOffset(min: number, max: number, ops: Options): number; + doubleLine(x1: number, y1: number, x2: number, y2: number, o: Options): Op[]; + private _line; + private _curve; + private _ellipse; + private _curveWithOffset; + private _arc; + private _bezierTo; + private _processSegment; +} diff --git a/bin/renderer.js b/bin/renderer.js new file mode 100644 index 0000000..955aea4 --- /dev/null +++ b/bin/renderer.js @@ -0,0 +1,597 @@ +import { RoughPath, RoughArcConverter, PathFitter } from './path.js'; +import { getFiller } from './fillers/filler'; +export class RoughRenderer { + line(x1, y1, x2, y2, o) { + const ops = this.doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops }; + } + linearPath(points, close, o) { + const len = (points || []).length; + if (len > 2) { + let ops = []; + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this.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 this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + polygon(points, o) { + return this.linearPath(points, true, o); + } + rectangle(x, y, width, height, o) { + const points = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return this.polygon(points, o); + } + curve(points, o) { + const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + ellipse(x, y, width, height, o) { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); + const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + 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 += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, 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 o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } + else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; + } + svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; + } + solidFillPolygon(points, o) { + const ops = []; + if (PointerEvent.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; + } + patternFillPolygon(points, o) { + const filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + patternFillEllipse(cx, cy, width, height, o) { + const filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + 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 += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, 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 this.patternFillPolygon(points, o); + } + /// + getOffset(min, max, ops) { + return ops.roughness * ((Math.random() * (max - min)) + min); + } + doubleLine(x1, y1, x2, y2, o) { + const o1 = this._line(x1, y1, x2, y2, o, true, false); + const o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + _line(x1, y1, x2, y2, o, move, overlay) { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); + const ops = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-halfOffset, halfOffset, o), + y1 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-offset, offset, o), + y1 + this.getOffset(-offset, offset, o) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + x2 + this.getOffset(-halfOffset, halfOffset, o), + y2 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + x2 + this.getOffset(-offset, offset, o), + y2 + this.getOffset(-offset, offset, o) + ] + }); + } + return ops; + } + _curve(points, closePoint, o) { + const len = points.length; + let 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] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, 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 = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return this._curve(points, null, o); + } + _curveWithOffset(points, offset, o) { + const ps = []; + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + } + } + return this._curve(ps, null, o); + } + _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + const radOffset = strt + this.getOffset(-0.1, 0.1, o); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, 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 this._curve(points, null, o); + } + _bezierTo(x1, y1, x2, y2, x, y, path, o) { + const ops = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } + else { + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), + x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + _processSegment(path, seg, prevSeg, o) { + let ops = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } +} diff --git a/bin/rough.d.ts b/bin/rough.d.ts new file mode 100644 index 0000000..b9a0af7 --- /dev/null +++ b/bin/rough.d.ts @@ -0,0 +1,15 @@ +import { Config, DrawingSurface } from './core'; +import { RoughCanvas } from './canvas'; +import { RoughRenderer } from './renderer'; +import { RoughGenerator } from './generator'; +import { RoughGeneratorAsync } from './generator-async'; +import { RoughCanvasAsync } from './canvas-async'; +import { RoughSVG } from './svg'; +import { RoughSVGAsync } from './svg-async'; +declare const _default: { + canvas(canvas: HTMLCanvasElement, config?: Config | undefined): RoughCanvas | RoughCanvasAsync; + svg(svg: SVGSVGElement, config?: Config | undefined): RoughSVG | RoughSVGAsync; + createRenderer(): RoughRenderer; + generator(config: Config | null, surface: DrawingSurface): RoughGenerator | RoughGeneratorAsync; +}; +export default _default; diff --git a/bin/rough.js b/bin/rough.js new file mode 100644 index 0000000..47d7f37 --- /dev/null +++ b/bin/rough.js @@ -0,0 +1,29 @@ +import { RoughCanvas } from './canvas'; +import { RoughGenerator } from './generator'; +import { RoughGeneratorAsync } from './generator-async'; +import { RoughCanvasAsync } from './canvas-async'; +import { RoughSVG } from './svg'; +import { RoughSVGAsync } from './svg-async'; +export default { + canvas(canvas, config) { + if (config && config.async) { + return new RoughCanvasAsync(canvas, config); + } + return new RoughCanvas(canvas, config); + }, + svg(svg, config) { + if (config && config.async) { + return new RoughSVGAsync(svg, config); + } + return new RoughSVG(svg, config); + }, + createRenderer() { + return RoughCanvas.createRenderer(); + }, + generator(config, surface) { + if (config && config.async) { + return new RoughGeneratorAsync(config, surface); + } + return new RoughGenerator(config, surface); + } +}; diff --git a/bin/svg-async.d.ts b/bin/svg-async.d.ts new file mode 100644 index 0000000..8a9e54b --- /dev/null +++ b/bin/svg-async.d.ts @@ -0,0 +1,18 @@ +import { RoughSVG } from './svg'; +import { Config, Options } from './core'; +import { RoughGeneratorAsync } from './generator-async'; +import { Point } from './geometry'; +export declare class RoughSVGAsync extends RoughSVG { + private genAsync; + constructor(svg: SVGSVGElement, config?: Config); + readonly generator: RoughGeneratorAsync; + line(x1: number, y1: number, x2: number, y2: number, options?: Options): Promise; + rectangle(x: number, y: number, width: number, height: number, options?: Options): Promise; + ellipse(x: number, y: number, width: number, height: number, options?: Options): Promise; + circle(x: number, y: number, diameter: number, options?: Options): Promise; + linearPath(points: Point[], options?: Options): Promise; + polygon(points: Point[], options?: Options): Promise; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed?: boolean, options?: Options): Promise; + curve(points: Point[], options?: Options): Promise; + path(d: string, options?: Options): Promise; +} diff --git a/bin/svg-async.js b/bin/svg-async.js new file mode 100644 index 0000000..666cde8 --- /dev/null +++ b/bin/svg-async.js @@ -0,0 +1,57 @@ +import { RoughSVG } from './svg'; +import { RoughGeneratorAsync } from './generator-async'; +export class RoughSVGAsync extends RoughSVG { + constructor(svg, config) { + super(svg, config); + this.genAsync = new RoughGeneratorAsync(config || null, this.svg); + } + // @ts-ignore + get generator() { + return this.genAsync; + } + // @ts-ignore + async line(x1, y1, x2, y2, options) { + const d = await this.genAsync.line(x1, y1, x2, y2, options); + return this.draw(d); + } + // @ts-ignore + async rectangle(x, y, width, height, options) { + const d = await this.genAsync.rectangle(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + async ellipse(x, y, width, height, options) { + const d = await this.genAsync.ellipse(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + async circle(x, y, diameter, options) { + const d = await this.genAsync.circle(x, y, diameter, options); + return this.draw(d); + } + // @ts-ignore + async linearPath(points, options) { + const d = await this.genAsync.linearPath(points, options); + return this.draw(d); + } + // @ts-ignore + async polygon(points, options) { + const d = await this.genAsync.polygon(points, options); + return this.draw(d); + } + // @ts-ignore + async arc(x, y, width, height, start, stop, closed = false, options) { + const d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + // @ts-ignore + async curve(points, options) { + const d = await this.genAsync.curve(points, options); + return this.draw(d); + } + // @ts-ignore + async path(d, options) { + const drawing = await this.genAsync.path(d, options); + return this.draw(drawing); + } +} diff --git a/bin/svg.d.ts b/bin/svg.d.ts new file mode 100644 index 0000000..085f25f --- /dev/null +++ b/bin/svg.d.ts @@ -0,0 +1,25 @@ +import { Config, Options, Drawable } from './core'; +import { RoughGenerator } from './generator'; +import { RoughRenderer } from './renderer'; +import { Point } from './geometry'; +export declare class RoughSVG { + protected svg: SVGSVGElement; + private gen; + protected _defs?: SVGDefsElement; + constructor(svg: SVGSVGElement, config?: Config); + readonly generator: RoughGenerator; + static createRenderer(): RoughRenderer; + readonly defs: SVGDefsElement | null; + 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; + draw(drawable: Drawable): SVGGElement; + private opsToPath; + private fillSketch; +} diff --git a/bin/svg.js b/bin/svg.js new file mode 100644 index 0000000..ac17cfd --- /dev/null +++ b/bin/svg.js @@ -0,0 +1,147 @@ +import { RoughGenerator } from './generator'; +import { RoughRenderer } from './renderer'; +const hasDocument = typeof document !== 'undefined'; +export class RoughSVG { + constructor(svg, config) { + this.svg = svg; + this.gen = new RoughGenerator(config || null, this.svg); + } + get generator() { + return this.gen; + } + static createRenderer() { + return new RoughRenderer(); + } + get defs() { + if (hasDocument) { + if (!this._defs) { + const doc = this.svg.ownerDocument || document; + const dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); + if (this.svg.firstChild) { + this.svg.insertBefore(dnode, this.svg.firstChild); + } + else { + this.svg.appendChild(dnode); + } + this._defs = dnode; + } + } + return this._defs || null; + } + 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); + } + draw(drawable) { + const sets = drawable.sets || []; + const o = drawable.options || this.gen.defaultOptions; + const doc = this.svg.ownerDocument || (hasDocument && document); + const g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); + for (const drawing of sets) { + let path = null; + switch (drawing.type) { + case 'path': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.stroke; + path.style.strokeWidth = o.strokeWidth + ''; + path.style.fill = 'none'; + break; + } + case 'fillPath': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'fillSketch': { + path = this.fillSketch(doc, drawing, o); + break; + } + case 'path2Dfill': { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'path2Dpattern': { + const size = drawing.size; + const pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); + const id = `rough-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999))}`; + pattern.setAttribute('id', id); + pattern.setAttribute('x', '0'); + pattern.setAttribute('y', '0'); + pattern.setAttribute('width', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('viewBox', `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`); + pattern.setAttribute('patternUnits', 'objectBoundingBox'); + const patternPath = this.fillSketch(doc, drawing, o); + pattern.appendChild(patternPath); + this.defs.appendChild(pattern); + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = `url(#${id})`; + break; + } + } + if (path) { + g.appendChild(path); + } + } + return g; + } + opsToPath(drawing) { + return this.gen.opsToPath(drawing); + } + fillSketch(doc, drawing, o) { + let fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + const path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.fill; + path.style.strokeWidth = fweight + ''; + path.style.fill = 'none'; + return path; + } +} diff --git a/bin/utils/hachure.d.ts b/bin/utils/hachure.d.ts new file mode 100644 index 0000000..a233ce6 --- /dev/null +++ b/bin/utils/hachure.d.ts @@ -0,0 +1,17 @@ +import { Segment } from '../geometry'; +export declare class HachureIterator { + top: number; + bottom: number; + left: number; + right: number; + gap: number; + sinAngle: number; + tanAngle: number; + pos: number; + deltaX: number; + hGap: number; + sLeft?: Segment; + sRight?: Segment; + constructor(top: number, bottom: number, left: number, right: number, gap: number, sinAngle: number, cosAngle: number, tanAngle: number); + nextLine(): number[] | null; +} diff --git a/bin/utils/hachure.js b/bin/utils/hachure.js new file mode 100644 index 0000000..62a46cd --- /dev/null +++ b/bin/utils/hachure.js @@ -0,0 +1,76 @@ +import { Segment } from '../geometry'; +export class HachureIterator { + constructor(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { + this.deltaX = 0; + this.hGap = 0; + this.top = top; + this.bottom = bottom; + this.left = left; + this.right = right; + this.gap = gap; + this.sinAngle = sinAngle; + this.tanAngle = tanAngle; + if (Math.abs(sinAngle) < 0.0001) { + this.pos = left + gap; + } + else if (Math.abs(sinAngle) > 0.9999) { + this.pos = top + gap; + } + else { + this.deltaX = (bottom - top) * Math.abs(tanAngle); + this.pos = left - Math.abs(this.deltaX); + this.hGap = Math.abs(gap / cosAngle); + this.sLeft = new Segment([left, bottom], [left, top]); + this.sRight = new Segment([right, bottom], [right, top]); + } + } + nextLine() { + if (Math.abs(this.sinAngle) < 0.0001) { + if (this.pos < this.right) { + const line = [this.pos, this.top, this.pos, this.bottom]; + this.pos += this.gap; + return line; + } + } + else if (Math.abs(this.sinAngle) > 0.9999) { + if (this.pos < this.bottom) { + const line = [this.left, this.pos, this.right, this.pos]; + this.pos += this.gap; + return line; + } + } + else { + let xLower = this.pos - this.deltaX / 2; + let xUpper = this.pos + this.deltaX / 2; + let yLower = this.bottom; + let yUpper = this.top; + if (this.pos < (this.right + this.deltaX)) { + while (((xLower < this.left) && (xUpper < this.left)) || ((xLower > this.right) && (xUpper > this.right))) { + this.pos += this.hGap; + xLower = this.pos - this.deltaX / 2; + xUpper = this.pos + this.deltaX / 2; + if (this.pos > (this.right + this.deltaX)) { + return null; + } + } + const s = new Segment([xLower, yLower], [xUpper, yUpper]); + if (this.sLeft && s.intersects(this.sLeft)) { + xLower = s.xi; + yLower = s.yi; + } + if (this.sRight && s.intersects(this.sRight)) { + xUpper = s.xi; + yUpper = s.yi; + } + if (this.tanAngle > 0) { + xLower = this.right - (xLower - this.left); + xUpper = this.right - (xUpper - this.left); + } + const line = [xLower, yLower, xUpper, yUpper]; + this.pos += this.hGap; + return line; + } + } + return null; + } +} diff --git a/bin/utils/liang-barsky.d.ts b/bin/utils/liang-barsky.d.ts new file mode 100644 index 0000000..4908ae6 --- /dev/null +++ b/bin/utils/liang-barsky.d.ts @@ -0,0 +1,2 @@ +import { Point, Rectangle } from '../geometry'; +export declare function clip(p1: Point, p2: Point, box: Rectangle): [Point, Point] | null; diff --git a/bin/utils/liang-barsky.js b/bin/utils/liang-barsky.js new file mode 100644 index 0000000..5a4c0f1 --- /dev/null +++ b/bin/utils/liang-barsky.js @@ -0,0 +1,51 @@ +export function clip(p1, p2, box) { + const xmin = box.x; + const xmax = box.x + box.width; + const ymin = box.y; + const ymax = box.y + box.height; + let t0 = 0; + let t1 = 1; + const dx = p2[0] - p1[0]; + const dy = p2[1] - p1[0]; + let p = 0; + let q = 0; + let r = 0; + for (let edge = 0; edge < 4; edge++) { + if (edge === 0) { + p = -dx; + q = -(xmin - p1[0]); + } + if (edge === 1) { + p = dx; + q = (xmax - p1[0]); + } + if (edge === 2) { + p = -dy; + q = -(ymin - p1[1]); + } + if (edge === 3) { + p = dy; + q = (ymax - p1[1]); + } + r = q / p; + if (p === 0 && q < 0) { + return null; + } + if (p < 0) { + if (r > t1) + return null; + else if (r > t0) + t0 = r; + } + else if (p > 0) { + if (r < t0) + return null; + else if (r < t1) + t1 = r; + } + } + return [ + [p1[0] + t0 * dx, p1[1] + t0 * dy], + [p1[0] + t1 * dx, p1[1] + t1 * dy] + ]; +} diff --git a/package.json b/package.json index 7497d2d..19669dc 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,11 @@ { "name": "roughjs", - "version": "2.1.3", + "version": "2.2.0", "description": "Create graphics using HTML Canvas or SVG with a hand-drawn, sketchy, appearance.", "main": "dist/rough.umd.js", - "jsnext:main": "src/index.js", + "module": "bin/rough.js", + "jsnext:main": "bin/rough.js", + "types": "bin/rough.d.ts", "scripts": { "build": "tsc && rollup -c", "test": "echo \"Error: no test specified\" && exit 1" diff --git a/srcts/canvas-async.ts b/src/canvas-async.ts similarity index 100% rename from srcts/canvas-async.ts rename to src/canvas-async.ts diff --git a/src/canvas.js b/src/canvas.js deleted file mode 100644 index a944bd4..0000000 --- a/src/canvas.js +++ /dev/null @@ -1,248 +0,0 @@ -import { RoughGenerator, RoughGeneratorAsync } from './generator.js' -import { RoughRenderer } from './renderer.js'; - -export class RoughCanvas { - constructor(canvas, config) { - this.canvas = canvas; - this.ctx = this.canvas.getContext("2d"); - this._init(config); - } - - _init(config) { - this.gen = new RoughGenerator(config, this.canvas); - } - - get generator() { - return this.gen; - } - - static createRenderer() { - return new RoughRenderer(); - } - - line(x1, y1, x2, y2, options) { - let d = this.gen.line(x1, y1, x2, y2, options); - this.draw(d); - return d; - } - - rectangle(x, y, width, height, options) { - let d = this.gen.rectangle(x, y, width, height, options); - this.draw(d); - return d; - } - - ellipse(x, y, width, height, options) { - let d = this.gen.ellipse(x, y, width, height, options); - this.draw(d); - return d; - } - - circle(x, y, diameter, options) { - let d = this.gen.circle(x, y, diameter, options); - this.draw(d); - return d; - } - - linearPath(points, options) { - let d = this.gen.linearPath(points, options); - this.draw(d); - return d; - } - - polygon(points, options) { - let d = this.gen.polygon(points, options); - this.draw(d); - return d; - } - - arc(x, y, width, height, start, stop, closed, options) { - let d = this.gen.arc(x, y, width, height, start, stop, closed, options); - this.draw(d); - return d; - } - - curve(points, options) { - let d = this.gen.curve(points, options); - this.draw(d); - return d; - } - - path(d, options) { - let drawing = this.gen.path(d, options); - this.draw(drawing); - return drawing; - } - - draw(drawable) { - let sets = drawable.sets || []; - let o = drawable.options || this.gen.defaultOptions; - let ctx = this.ctx; - for (let drawing of sets) { - switch (drawing.type) { - case 'path': - ctx.save(); - ctx.strokeStyle = o.stroke; - ctx.lineWidth = o.strokeWidth; - this._drawToContext(ctx, drawing); - ctx.restore(); - break; - case 'fillPath': - ctx.save(); - ctx.fillStyle = o.fill; - this._drawToContext(ctx, drawing, o); - ctx.restore(); - break; - case 'fillSketch': - this._fillSketch(ctx, drawing, o); - break; - case 'path2Dfill': { - this.ctx.save(); - this.ctx.fillStyle = o.fill; - let p2d = new Path2D(drawing.path); - this.ctx.fill(p2d); - this.ctx.restore(); - break; - } - case 'path2Dpattern': { - let size = drawing.size; - const hcanvas = document.createElement('canvas'); - const hcontext = hcanvas.getContext("2d"); - let bbox = this._computeBBox(drawing.path); - if (bbox && (bbox.width || bbox.height)) { - hcanvas.width = this.canvas.width; - hcanvas.height = this.canvas.height; - hcontext.translate(bbox.x || 0, bbox.y || 0); - } else { - hcanvas.width = size[0]; - hcanvas.height = size[1]; - } - this._fillSketch(hcontext, drawing, o); - this.ctx.save(); - this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); - let p2d = new Path2D(drawing.path); - this.ctx.fill(p2d); - this.ctx.restore(); - break; - } - } - } - } - - _computeBBox(d) { - if (self.document) { - try { - const ns = "http://www.w3.org/2000/svg"; - let svg = self.document.createElementNS(ns, "svg"); - svg.setAttribute("width", "0"); - svg.setAttribute("height", "0"); - let pathNode = self.document.createElementNS(ns, "path"); - pathNode.setAttribute('d', d); - svg.appendChild(pathNode); - self.document.body.appendChild(svg); - let bbox = pathNode.getBBox(); - self.document.body.removeChild(svg); - return bbox; - } catch (err) { } - } - return null; - } - - _fillSketch(ctx, drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - ctx.save(); - ctx.strokeStyle = o.fill; - ctx.lineWidth = fweight; - this._drawToContext(ctx, drawing); - ctx.restore(); - } - - _drawToContext(ctx, drawing) { - ctx.beginPath(); - for (let 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 'qcurveTo': - ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); - break; - case 'lineTo': - ctx.lineTo(data[0], data[1]); - break; - } - } - if (drawing.type === 'fillPath') { - ctx.fill(); - } else { - ctx.stroke(); - } - } -} - -export class RoughCanvasAsync extends RoughCanvas { - _init(config) { - this.gen = new RoughGeneratorAsync(config, this.canvas); - } - - async line(x1, y1, x2, y2, options) { - let d = await this.gen.line(x1, y1, x2, y2, options); - this.draw(d); - return d; - } - - async rectangle(x, y, width, height, options) { - let d = await this.gen.rectangle(x, y, width, height, options); - this.draw(d); - return d; - } - - async ellipse(x, y, width, height, options) { - let d = await this.gen.ellipse(x, y, width, height, options); - this.draw(d); - return d; - } - - async circle(x, y, diameter, options) { - let d = await this.gen.circle(x, y, diameter, options); - this.draw(d); - return d; - } - - async linearPath(points, options) { - let d = await this.gen.linearPath(points, options); - this.draw(d); - return d; - } - - async polygon(points, options) { - let d = await this.gen.polygon(points, options); - this.draw(d); - return d; - } - - async arc(x, y, width, height, start, stop, closed, options) { - let d = await this.gen.arc(x, y, width, height, start, stop, closed, options); - this.draw(d); - return d; - } - - async curve(points, options) { - let d = await this.gen.curve(points, options); - this.draw(d); - return d; - } - - async path(d, options) { - let drawing = await this.gen.path(d, options); - this.draw(drawing); - return drawing; - } -} \ No newline at end of file diff --git a/srcts/canvas.ts b/src/canvas.ts similarity index 100% rename from srcts/canvas.ts rename to src/canvas.ts diff --git a/srcts/core.ts b/src/core.ts similarity index 100% rename from srcts/core.ts rename to src/core.ts diff --git a/srcts/fillers/filler-interface.ts b/src/fillers/filler-interface.ts similarity index 100% rename from srcts/fillers/filler-interface.ts rename to src/fillers/filler-interface.ts diff --git a/srcts/fillers/filler.ts b/src/fillers/filler.ts similarity index 100% rename from srcts/fillers/filler.ts rename to src/fillers/filler.ts diff --git a/srcts/fillers/hachure-filler.ts b/src/fillers/hachure-filler.ts similarity index 100% rename from srcts/fillers/hachure-filler.ts rename to src/fillers/hachure-filler.ts diff --git a/srcts/fillers/hatch-filler.ts b/src/fillers/hatch-filler.ts similarity index 100% rename from srcts/fillers/hatch-filler.ts rename to src/fillers/hatch-filler.ts diff --git a/srcts/fillers/zigzag-filler.ts b/src/fillers/zigzag-filler.ts similarity index 100% rename from srcts/fillers/zigzag-filler.ts rename to src/fillers/zigzag-filler.ts diff --git a/srcts/generator-async.ts b/src/generator-async.ts similarity index 100% rename from srcts/generator-async.ts rename to src/generator-async.ts diff --git a/src/generator.js b/src/generator.js deleted file mode 100644 index 75b3e4f..0000000 --- a/src/generator.js +++ /dev/null @@ -1,418 +0,0 @@ -import { RoughRenderer } from './renderer.js'; -self._roughScript = self.document && self.document.currentScript && self.document.currentScript.src; - -export class RoughGenerator { - constructor(config, canvas) { - this.config = config || {}; - this.canvas = canvas; - this.defaultOptions = { - maxRandomnessOffset: 2, - roughness: 1, - bowing: 1, - stroke: '#000', - strokeWidth: 1, - curveTightness: 0, - curveStepCount: 9, - fill: null, - fillStyle: 'hachure', - fillWeight: -1, - hachureAngle: -41, - hachureGap: -1 - }; - if (this.config.options) { - this.defaultOptions = this._options(this.config.options); - } - } - - _options(options) { - return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; - } - - _drawable(shape, sets, options) { - return { shape, sets: sets || [], options: options || this.defaultOptions }; - } - - get lib() { - if (!this._renderer) { - if (self && self.workly && this.config.async && (!this.config.noWorker)) { - const tos = Function.prototype.toString; - const worklySource = this.config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; - const rendererSource = this.config.roughURL || self._roughScript; - if (rendererSource && worklySource) { - let code = `importScripts('${worklySource}', '${rendererSource}');\nworkly.expose(self.rough.createRenderer());`; - let ourl = URL.createObjectURL(new Blob([code])); - this._renderer = workly.proxy(ourl); - } else { - this._renderer = new RoughRenderer(); - } - } else { - this._renderer = new RoughRenderer(); - } - } - return this._renderer; - } - - line(x1, y1, x2, y2, options) { - const o = this._options(options); - return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); - } - - rectangle(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - const xc = [x, x + width, x + width, x]; - const yc = [y, y, y + height, y + height]; - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillShape(xc, yc, o)) - } else { - paths.push(this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(this.lib.rectangle(x, y, width, height, o)); - return this._drawable('rectangle', paths, o); - } - - ellipse(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - const shape = this.lib.ellipse(x, y, width, height, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(this.lib.hachureFillEllipse(x, y, width, height, o)); - } - } - paths.push(this.lib.ellipse(x, y, width, height, o)); - return this._drawable('ellipse', paths, o); - } - - circle(x, y, diameter, options) { - let ret = this.ellipse(x, y, diameter, diameter, options); - ret.shape = 'circle'; - return ret; - } - - linearPath(points, options) { - const o = this._options(options); - return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); - } - - polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - let xc = [], yc = []; - for (let p of points) { - xc.push(p[0]); - yc.push(p[1]); - } - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - - arc(x, y, width, height, start, stop, closed, options) { - const o = this._options(options); - const paths = []; - if (closed && o.fill) { - if (o.fillStyle === 'solid') { - let shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(this.lib.hachureFillArc(x, y, width, height, start, stop, o)); - } - } - paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); - return this._drawable('arc', paths, o); - } - - curve(points, options) { - const o = this._options(options); - return this._drawable('curve', [this.lib.curve(points, o)], o); - } - - path(d, options) { - const o = this._options(options); - const paths = []; - if (!d) { - return this._drawable('path', paths, o); - } - if (o.fill) { - if (o.fillStyle === 'solid') { - let shape = { type: 'path2Dfill', path: d }; - paths.push(shape); - } else { - const size = this._computePathSize(d); - let xc = [0, size[0], size[0], 0]; - let yc = [0, 0, size[1], size[1]]; - let shape = this.lib.hachureFillShape(xc, yc, o); - shape.type = 'path2Dpattern'; - shape.size = size; - shape.path = d; - paths.push(shape); - } - } - paths.push(this.lib.svgPath(d, o)); - return this._drawable('path', paths, o); - } - - 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: 'none' - }; - break; - case 'fillPath': - path = { - d: this.opsToPath(drawing), - stroke: 'none', - strokeWidth: 0, - fill: o.fill - }; - break; - case 'fillSketch': - path = this._fillSketch(drawing, o); - break; - case 'path2Dfill': - path = { - d: drawing.path, - stroke: 'none', - strokeWidth: 0, - fill: o.fill - }; - break; - case 'path2Dpattern': { - const size = drawing.size; - const pattern = { - x: 0, y: 0, width: 1, height: 1, - viewBox: `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`, - patternUnits: 'objectBoundingBox', - path: this._fillSketch(drawing, o) - }; - path = { - d: drawing.path, - stroke: 'none', - strokeWidth: 0, - pattern: pattern - }; - 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, - strokeWidth: fweight, - fill: 'none' - }; - } - - opsToPath(drawing) { - let path = ''; - for (let 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 'qcurveTo': - path += `Q${data[0]} ${data[1]}, ${data[2]} ${data[3]} `; - break; - case 'lineTo': - path += `L${data[0]} ${data[1]} `; - break; - } - } - return path.trim(); - } - - _computePathSize(d) { - let size = [0, 0]; - if (self.document) { - try { - const ns = "http://www.w3.org/2000/svg"; - let svg = self.document.createElementNS(ns, "svg"); - svg.setAttribute("width", "0"); - svg.setAttribute("height", "0"); - let pathNode = self.document.createElementNS(ns, "path"); - pathNode.setAttribute('d', d); - svg.appendChild(pathNode); - self.document.body.appendChild(svg); - let bb = pathNode.getBBox() - if (bb) { - size[0] = bb.width || 0; - size[1] = bb.height || 0; - } - self.document.body.removeChild(svg); - } catch (err) { } - } - const canvasSize = this._canvasSize(); - if (!(size[0] * size[1])) { - size = canvasSize; - } - size[0] = Math.min(size[0], canvasSize[0]); - size[1] = Math.min(size[1], canvasSize[1]); - return size; - } - - _canvasSize() { - const val = w => { - if (w) { - if (typeof w === 'object') { - if (w.baseVal && w.baseVal.value) { - return w.baseVal.value; - } - } - } - return w || 100; - }; - return this.canvas ? [val(this.canvas.width), val(this.canvas.height)] : [100, 100]; - } -} - -export class RoughGeneratorAsync extends RoughGenerator { - async line(x1, y1, x2, y2, options) { - const o = this._options(options); - return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); - } - - async rectangle(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - const xc = [x, x + width, x + width, x]; - const yc = [y, y, y + height, y + height]; - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillShape(xc, yc, o)) - } else { - paths.push(await this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(await this.lib.rectangle(x, y, width, height, o)); - return this._drawable('rectangle', paths, o); - } - - async ellipse(x, y, width, height, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - const shape = await this.lib.ellipse(x, y, width, height, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(await this.lib.hachureFillEllipse(x, y, width, height, o)); - } - } - paths.push(await this.lib.ellipse(x, y, width, height, o)); - return this._drawable('ellipse', paths, o); - } - - async circle(x, y, diameter, options) { - let ret = await this.ellipse(x, y, diameter, diameter, options); - ret.shape = 'circle'; - return ret; - } - - async linearPath(points, options) { - const o = this._options(options); - return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); - } - - async polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - let xc = [], yc = []; - for (let p of points) { - xc.push(p[0]); - yc.push(p[1]); - } - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillShape(xc, yc, o)); - } else { - paths.push(await this.lib.hachureFillShape(xc, yc, o)); - } - } - paths.push(await this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - - async arc(x, y, width, height, start, stop, closed, options) { - const o = this._options(options); - const paths = []; - if (closed && o.fill) { - if (o.fillStyle === 'solid') { - let shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); - shape.type = 'fillPath'; - paths.push(shape); - } else { - paths.push(await this.lib.hachureFillArc(x, y, width, height, start, stop, o)); - } - } - paths.push(await this.lib.arc(x, y, width, height, start, stop, closed, true, o)); - return this._drawable('arc', paths, o); - } - - async curve(points, options) { - const o = this._options(options); - return this._drawable('curve', [await this.lib.curve(points, o)], o); - } - - async path(d, options) { - const o = this._options(options); - const paths = []; - if (!d) { - return this._drawable('path', paths, o); - } - if (o.fill) { - if (o.fillStyle === 'solid') { - let shape = { type: 'path2Dfill', path: d }; - paths.push(shape); - } else { - const size = this._computePathSize(d); - let xc = [0, size[0], size[0], 0]; - let yc = [0, 0, size[1], size[1]]; - let shape = await this.lib.hachureFillShape(xc, yc, o); - shape.type = 'path2Dpattern'; - shape.size = size; - shape.path = d; - paths.push(shape); - } - } - paths.push(await this.lib.svgPath(d, o)); - return this._drawable('path', paths, o); - } -} \ No newline at end of file diff --git a/srcts/generator.ts b/src/generator.ts similarity index 100% rename from srcts/generator.ts rename to src/generator.ts diff --git a/srcts/geometry.ts b/src/geometry.ts similarity index 100% rename from srcts/geometry.ts rename to src/geometry.ts diff --git a/src/hachure.js b/src/hachure.js deleted file mode 100644 index f0884ba..0000000 --- a/src/hachure.js +++ /dev/null @@ -1,73 +0,0 @@ -import { RoughSegmentRelation, RoughSegment } from "./segment"; - -export class RoughHachureIterator { - constructor(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { - this.top = top; - this.bottom = bottom; - this.left = left; - this.right = right; - this.gap = gap; - this.sinAngle = sinAngle; - this.tanAngle = tanAngle; - - if (Math.abs(sinAngle) < 0.0001) { - this.pos = left + gap; - } else if (Math.abs(sinAngle) > 0.9999) { - this.pos = top + gap; - } else { - this.deltaX = (bottom - top) * Math.abs(tanAngle); - this.pos = left - Math.abs(this.deltaX); - this.hGap = Math.abs(gap / cosAngle); - this.sLeft = new RoughSegment(left, bottom, left, top); - this.sRight = new RoughSegment(right, bottom, right, top); - } - } - - getNextLine() { - if (Math.abs(this.sinAngle) < 0.0001) { - if (this.pos < this.right) { - let line = [this.pos, this.top, this.pos, this.bottom]; - this.pos += this.gap; - return line; - } - } else if (Math.abs(this.sinAngle) > 0.9999) { - if (this.pos < this.bottom) { - let line = [this.left, this.pos, this.right, this.pos]; - this.pos += this.gap; - return line; - } - } else { - let xLower = this.pos - this.deltaX / 2; - let xUpper = this.pos + this.deltaX / 2; - let yLower = this.bottom; - let yUpper = this.top; - if (this.pos < (this.right + this.deltaX)) { - while (((xLower < this.left) && (xUpper < this.left)) || ((xLower > this.right) && (xUpper > this.right))) { - this.pos += this.hGap; - xLower = this.pos - this.deltaX / 2; - xUpper = this.pos + this.deltaX / 2; - if (this.pos > (this.right + this.deltaX)) { - return null; - } - } - let s = new RoughSegment(xLower, yLower, xUpper, yUpper); - if (s.compare(this.sLeft) == RoughSegmentRelation().INTERSECTS) { - xLower = s.xi; - yLower = s.yi; - } - if (s.compare(this.sRight) == RoughSegmentRelation().INTERSECTS) { - xUpper = s.xi; - yUpper = s.yi; - } - if (this.tanAngle > 0) { - xLower = this.right - (xLower - this.left); - xUpper = this.right - (xUpper - this.left); - } - let line = [xLower, yLower, xUpper, yUpper]; - this.pos += this.hGap; - return line; - } - } - return null; - } -} \ No newline at end of file diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 5650755..0000000 --- a/src/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import { RoughCanvas, RoughCanvasAsync } from './canvas.js'; -import { RoughSVG, RoughSVGAsync } from './svg.js'; -import { RoughGenerator, RoughGeneratorAsync } from './generator.js' - -export default { - canvas(canvas, config) { - if (config && config.async) { - return new RoughCanvasAsync(canvas, config); - } - return new RoughCanvas(canvas, config); - }, - svg(svg, config) { - if (config && config.async) { - return new RoughSVGAsync(svg, config); - } - return new RoughSVG(svg, config); - }, - createRenderer() { - return RoughCanvas.createRenderer(); - }, - generator(config, size) { - if (config && config.async) { - return new RoughGeneratorAsync(config, size); - } - return new RoughGenerator(config, size); - } -}; \ No newline at end of file diff --git a/src/path.js b/src/path.js deleted file mode 100644 index 21b9a30..0000000 --- a/src/path.js +++ /dev/null @@ -1,446 +0,0 @@ -class PathToken { - constructor(type, text) { - this.type = type; - this.text = text; - } - isType(type) { - return this.type === type; - } -} - -class ParsedPath { - constructor(d) { - this.PARAMS = { - A: ["rx", "ry", "x-axis-rotation", "large-arc-flag", "sweep-flag", "x", "y"], - a: ["rx", "ry", "x-axis-rotation", "large-arc-flag", "sweep-flag", "x", "y"], - C: ["x1", "y1", "x2", "y2", "x", "y"], - c: ["x1", "y1", "x2", "y2", "x", "y"], - H: ["x"], - h: ["x"], - L: ["x", "y"], - l: ["x", "y"], - M: ["x", "y"], - m: ["x", "y"], - Q: ["x1", "y1", "x", "y"], - q: ["x1", "y1", "x", "y"], - S: ["x2", "y2", "x", "y"], - s: ["x2", "y2", "x", "y"], - T: ["x", "y"], - t: ["x", "y"], - V: ["y"], - v: ["y"], - Z: [], - z: [] - }; - this.COMMAND = 0; - this.NUMBER = 1; - this.EOD = 2; - this.segments = []; - this.d = d || ""; - this.parseData(d); - this.processPoints(); - } - - loadFromSegments(segments) { - this.segments = segments; - this.processPoints(); - } - - processPoints() { - let first = null, prev = null, currentPoint = [0, 0]; - for (let i = 0; i < this.segments.length; i++) { - let s = this.segments[i]; - switch (s.key) { - case 'M': - case 'L': - case 'T': - s.point = [s.data[0], s.data[1]]; - break; - case 'm': - case 'l': - case 't': - s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; - break; - case 'H': - s.point = [s.data[0], currentPoint[1]]; - break; - case 'h': - s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; - break; - case 'V': - s.point = [currentPoint[0], s.data[0]]; - break; - case 'v': - s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; - break; - case 'z': - case 'Z': - if (first) { - s.point = [first[0], first[1]]; - } - break; - case 'C': - s.point = [s.data[4], s.data[5]]; - break; - case 'c': - s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; - break; - case 'S': - s.point = [s.data[2], s.data[3]]; - break; - case 's': - s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; - break; - case 'Q': - s.point = [s.data[2], s.data[3]]; - break; - case 'q': - s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; - break; - case 'A': - s.point = [s.data[5], s.data[6]]; - break; - case 'a': - s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; - break; - } - if (s.key === 'm' || s.key === 'M') { - first = null; - } - if (s.point) { - currentPoint = s.point; - if (!first) { - first = s.point; - } - } - if (s.key === 'z' || s.key === 'Z') { - first = null; - } - prev = s; - } - } - - get closed() { - if (typeof this._closed === 'undefined') { - this._closed = false; - for (let s of this.segments) { - if (s.key.toLowerCase() === 'z') { - this._closed = true; - } - } - } - return this._closed; - } - - parseData(d) { - var tokens = this.tokenize(d); - var index = 0; - var token = tokens[index]; - var mode = "BOD"; - this.segments = new Array(); - while (!token.isType(this.EOD)) { - var param_length; - var params = new Array(); - if (mode == "BOD") { - if (token.text == "M" || token.text == "m") { - index++; - param_length = this.PARAMS[token.text].length; - mode = token.text; - } else { - return this.parseData('M0,0' + d); - } - } else { - if (token.isType(this.NUMBER)) { - param_length = this.PARAMS[mode].length; - } else { - index++; - param_length = this.PARAMS[token.text].length; - mode = token.text; - } - } - - if ((index + param_length) < tokens.length) { - for (var i = index; i < index + param_length; i++) { - var number = tokens[i]; - if (number.isType(this.NUMBER)) { - params[params.length] = number.text; - } - else { - console.error("Parameter type is not a number: " + mode + "," + number.text); - return; - } - } - var segment; - if (this.PARAMS[mode]) { - segment = { key: mode, data: params }; - } else { - console.error("Unsupported segment type: " + mode); - return; - } - this.segments.push(segment); - index += param_length; - token = tokens[index]; - if (mode == "M") mode = "L"; - if (mode == "m") mode = "l"; - } else { - console.error("Path data ended before all parameters were found"); - } - } - } - - tokenize(d) { - var 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] = new PathToken(this.COMMAND, RegExp.$1); - d = d.substr(RegExp.$1.length); - } else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) { - tokens[tokens.length] = new PathToken(this.NUMBER, parseFloat(RegExp.$1)); - d = d.substr(RegExp.$1.length); - } else { - console.error("Unrecognized segment command: " + d); - return null; - } - } - tokens[tokens.length] = new PathToken(this.EOD, null); - return tokens; - } -} - -export class RoughPath { - constructor(d) { - this.d = d; - this.parsed = new ParsedPath(d); - this._position = [0, 0]; - this.bezierReflectionPoint = null; - this.quadReflectionPoint = null; - this._first = null; - } - - get segments() { - return this.parsed.segments; - } - - get closed() { - return this.parsed.closed; - } - - get linearPoints() { - if (!this._linearPoints) { - const lp = []; - let points = []; - for (let s of this.parsed.segments) { - let key = s.key.toLowerCase(); - if (key === 'm' || key === 'z') { - if (points.length) { - lp.push(points); - points = []; - } - if (key === 'z') { - continue; - } - } - if (s.point) { - points.push(s.point); - } - } - if (points.length) { - lp.push(points); - points = []; - } - this._linearPoints = lp; - } - return this._linearPoints; - } - - get first() { - return this._first; - } - - set first(v) { - this._first = v; - } - - setPosition(x, y) { - this._position = [x, y]; - if (!this._first) { - this._first = [x, y]; - } - } - - get position() { - return this._position; - } - - get x() { - return this._position[0]; - } - - get y() { - return this._position[1]; - } -} - -export class RoughArcConverter { - // Algorithm as described in https://www.w3.org/TR/SVG/implnote.html - // Code adapted from nsSVGPathDataParser.cpp in Mozilla - // https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 - constructor(from, to, radii, angle, largeArcFlag, sweepFlag) { - const radPerDeg = Math.PI / 180; - this._segIndex = 0; - this._numSegs = 0; - if (from[0] == to[0] && from[1] == to[1]) { - return; - } - this._rx = Math.abs(radii[0]); - this._ry = Math.abs(radii[1]); - this._sinPhi = Math.sin(angle * radPerDeg); - this._cosPhi = Math.cos(angle * radPerDeg); - var x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; - var y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; - var root; - var numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; - if (numerator < 0) { - let s = Math.sqrt(1 - (numerator / (this._rx * this._rx * this._ry * this._ry))); - this._rx = this._rx * s; - this._ry = this._ry * s; - root = 0; - } else { - root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) * - Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); - } - let cxdash = root * this._rx * y1dash / this._ry; - let cydash = -root * this._ry * x1dash / this._rx; - this._C = [0, 0]; - this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; - this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; - this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); - let dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); - if ((!sweepFlag) && (dtheta > 0)) { - dtheta -= 2 * Math.PI; - } else if (sweepFlag && (dtheta < 0)) { - dtheta += 2 * Math.PI; - } - this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); - this._delta = dtheta / this._numSegs; - this._T = (8 / 3) * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); - this._from = from; - } - - getNextSegment() { - var cp1, cp2, to; - if (this._segIndex == this._numSegs) { - return null; - } - let cosTheta1 = Math.cos(this._theta); - let sinTheta1 = Math.sin(this._theta); - let theta2 = this._theta + this._delta; - let cosTheta2 = Math.cos(theta2); - let sinTheta2 = Math.sin(theta2); - - to = [ - this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], - this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1] - ]; - cp1 = [ - this._from[0] + this._T * (- this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), - this._from[1] + this._T * (- this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1) - ]; - cp2 = [ - to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), - to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2) - ]; - - this._theta = theta2; - this._from = [to[0], to[1]]; - this._segIndex++; - - return { - cp1: cp1, - cp2: cp2, - to: to - }; - } - - calculateVectorAngle(ux, uy, vx, vy) { - let ta = Math.atan2(uy, ux); - let tb = Math.atan2(vy, vx); - if (tb >= ta) - return tb - ta; - return 2 * Math.PI - (ta - tb); - } -} - -export class PathFitter { - constructor(sets, closed) { - this.sets = sets; - this.closed = closed; - } - - fit(simplification) { - let outSets = []; - for (const set of this.sets) { - let length = set.length; - let estLength = Math.floor(simplification * length); - if (estLength < 5) { - if (length <= 5) { - continue; - } - estLength = 5; - } - outSets.push(this.reduce(set, estLength)); - } - - let d = ''; - for (const set of outSets) { - for (let i = 0; i < set.length; i++) { - let point = set[i]; - if (i === 0) { - d += 'M' + point[0] + "," + point[1]; - } else { - d += 'L' + point[0] + "," + point[1]; - } - } - if (this.closed) { - d += 'z '; - } - } - return d; - } - - distance(p1, p2) { - return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); - } - - reduce(set, count) { - if (set.length <= count) { - return set; - } - let points = set.slice(0); - while (points.length > count) { - let areas = []; - let minArea = -1; - let minIndex = -1; - for (let i = 1; i < (points.length - 1); i++) { - let a = this.distance(points[i - 1], points[i]); - let b = this.distance(points[i], points[i + 1]); - let c = this.distance(points[i - 1], points[i + 1]); - let s = (a + b + c) / 2.0; - let area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); - areas.push(area); - if ((minArea < 0) || (area < minArea)) { - minArea = area; - minIndex = i; - } - } - if (minIndex > 0) { - points.splice(minIndex, 1); - } else { - break; - } - } - return points; - } -} \ No newline at end of file diff --git a/srcts/path.ts b/src/path.ts similarity index 100% rename from srcts/path.ts rename to src/path.ts diff --git a/srcts/renderer-factory.ts b/src/renderer-factory.ts similarity index 100% rename from srcts/renderer-factory.ts rename to src/renderer-factory.ts diff --git a/src/renderer.js b/src/renderer.js deleted file mode 100644 index f758ec6..0000000 --- a/src/renderer.js +++ /dev/null @@ -1,714 +0,0 @@ -import { RoughHachureIterator } from './hachure.js'; -import { RoughSegmentRelation, RoughSegment } from './segment.js'; -import { RoughPath, RoughArcConverter, PathFitter } from './path.js'; - -export class RoughRenderer { - line(x1, y1, x2, y2, o) { - let ops = this._doubleLine(x1, y1, x2, y2, o); - return { type: 'path', ops }; - } - - linearPath(points, close, o) { - const len = (points || []).length; - if (len > 2) { - let ops = []; - for (let i = 0; i < (len - 1); i++) { - ops = ops.concat(this._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); - } - if (close) { - ops = ops.concat(this._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 this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); - } - } - - polygon(points, o) { - return this.linearPath(points, true, o); - } - - rectangle(x, y, width, height, o) { - let points = [ - [x, y], [x + width, y], [x + width, y + height], [x, y + height] - ]; - return this.polygon(points, o); - } - - curve(points, o) { - let o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); - let o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); - return { type: 'path', ops: o1.concat(o2) }; - } - - ellipse(x, y, width, height, o) { - const increment = (Math.PI * 2) / o.curveStepCount; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.05, rx * 0.05, o); - ry += this._getOffset(-ry * 0.05, ry * 0.05, o); - let o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this._getOffset(0.1, this._getOffset(0.4, 1, o), o), o); - let o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); - return { type: 'path', ops: o1.concat(o2) }; - } - - arc(x, y, width, height, start, stop, closed, roughClosure, o) { - let cx = x; - let cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.01, rx * 0.01, o); - ry += this._getOffset(-ry * 0.01, 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; - } - let ellipseInc = (Math.PI * 2) / o.curveStepCount; - let arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); - let o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); - let o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); - let ops = o1.concat(o2); - if (closed) { - if (roughClosure) { - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); - ops = ops.concat(this._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); - } else { - ops.push({ op: 'lineTo', data: [cx, cy] }); - ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); - } - } - return { type: 'path', ops }; - } - - hachureFillArc(x, y, width, height, start, stop, o) { - let cx = x; - let cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.01, rx * 0.01, o); - ry += this._getOffset(-ry * 0.01, 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; - } - let increment = (stp - strt) / o.curveStepCount; - let offset = 1; - let xc = [], yc = []; - for (let angle = strt; angle <= stp; angle = angle + increment) { - xc.push(cx + rx * Math.cos(angle)); - yc.push(cy + ry * Math.sin(angle)); - } - xc.push(cx + rx * Math.cos(stp)); - yc.push(cy + ry * Math.sin(stp)); - xc.push(cx); - yc.push(cy); - return this.hachureFillShape(xc, yc, o); - } - - solidFillShape(xCoords, yCoords, o) { - let ops = []; - if (xCoords && yCoords && xCoords.length && yCoords.length && xCoords.length === yCoords.length) { - let offset = o.maxRandomnessOffset || 0; - const len = xCoords.length; - if (len > 2) { - ops.push({ op: 'move', data: [xCoords[0] + this._getOffset(-offset, offset, o), yCoords[0] + this._getOffset(-offset, offset, o)] }); - for (var i = 1; i < len; i++) { - ops.push({ op: 'lineTo', data: [xCoords[i] + this._getOffset(-offset, offset, o), yCoords[i] + this._getOffset(-offset, offset, o)] }); - } - } - } - return { type: 'fillPath', ops }; - } - - hachureFillShape(xCoords, yCoords, o) { - let ops = []; - if (xCoords && yCoords && xCoords.length && yCoords.length) { - let left = xCoords[0]; - let right = xCoords[0]; - let top = yCoords[0]; - let bottom = yCoords[0]; - for (let 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]); - } - const angle = o.hachureAngle; - let gap = o.hachureGap; - if (gap < 0) { - gap = o.strokeWidth * 4; - } - gap = Math.max(gap, 0.1); - - const radPerDeg = Math.PI / 180; - const hachureAngle = (angle % 180) * radPerDeg; - const cosAngle = Math.cos(hachureAngle); - const sinAngle = Math.sin(hachureAngle); - const tanAngle = Math.tan(hachureAngle); - - const it = new RoughHachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); - let rectCoords; - while ((rectCoords = it.getNextLine()) != null) { - let lines = this._getIntersectingLines(rectCoords, xCoords, yCoords); - for (let i = 0; i < lines.length; i++) { - if (i < (lines.length - 1)) { - let p1 = lines[i]; - let p2 = lines[i + 1]; - ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o)); - } - } - } - } - return { type: 'fillSketch', ops }; - } - - hachureFillEllipse(cx, cy, width, height, o) { - let ops = []; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this._getOffset(-rx * 0.05, rx * 0.05, o); - ry += this._getOffset(-ry * 0.05, ry * 0.05, o); - let angle = o.hachureAngle; - let gap = o.hachureGap; - if (gap <= 0) { - gap = o.strokeWidth * 4; - } - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - const radPerDeg = Math.PI / 180; - let hachureAngle = (angle % 180) * radPerDeg; - let tanAngle = Math.tan(hachureAngle); - let aspectRatio = ry / rx; - let hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); - let sinAnglePrime = aspectRatio * tanAngle / hyp; - let cosAnglePrime = 1 / hyp; - let gapPrime = gap / ((rx * ry / Math.sqrt((ry * cosAnglePrime) * (ry * cosAnglePrime) + (rx * sinAnglePrime) * (rx * sinAnglePrime))) / rx); - let halfLen = Math.sqrt((rx * rx) - (cx - rx + gapPrime) * (cx - rx + gapPrime)); - for (var xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { - halfLen = Math.sqrt((rx * rx) - (cx - xPos) * (cx - xPos)); - let p1 = this._affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); - let p2 = this._affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); - ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o)); - } - return { type: 'fillSketch', ops }; - } - - svgPath(path, o) { - path = (path || '').replace(/\n/g, " ").replace(/(-\s)/g, "-").replace("/(\s\s)/g", " "); - let p = new RoughPath(path); - if (o.simplification) { - let fitter = new PathFitter(p.linearPoints, p.closed); - let d = fitter.fit(o.simplification); - p = new RoughPath(d); - } - let ops = []; - let segments = p.segments || []; - for (let i = 0; i < segments.length; i++) { - let s = segments[i]; - let prev = i > 0 ? segments[i - 1] : null; - let opList = this._processSegment(p, s, prev, o); - if (opList && opList.length) { - ops = ops.concat(opList); - } - } - return { type: 'path', ops }; - } - - // privates - - _bezierTo(x1, y1, x2, y2, x, y, path, o) { - let ops = []; - let ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; - let f = null; - for (let i = 0; i < 2; i++) { - if (i === 0) { - ops.push({ op: 'move', data: [path.x, path.y] }); - } else { - ops.push({ op: 'move', data: [path.x + this._getOffset(-ros[0], ros[0], o), path.y + this._getOffset(-ros[0], ros[0], o)] }); - } - f = [x + this._getOffset(-ros[i], ros[i], o), y + this._getOffset(-ros[i], ros[i], o)]; - ops.push({ - op: 'bcurveTo', data: [ - x1 + this._getOffset(-ros[i], ros[i], o), y1 + this._getOffset(-ros[i], ros[i], o), - x2 + this._getOffset(-ros[i], ros[i], o), y2 + this._getOffset(-ros[i], ros[i], o), - f[0], f[1] - ] - }); - } - path.setPosition(f[0], f[1]); - return ops; - } - - _processSegment(path, seg, prevSeg, o) { - let ops = []; - switch (seg.key) { - case 'M': - case 'm': { - let delta = seg.key === 'm'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let ro = 1 * (o.maxRandomnessOffset || 0); - x = x + this._getOffset(-ro, ro, o); - y = y + this._getOffset(-ro, ro, o); - path.setPosition(x, y); - ops.push({ op: 'move', data: [x, y] }); - } - break; - } - case 'L': - case 'l': { - let delta = seg.key === 'l'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } - break; - } - case 'H': - case 'h': { - const delta = seg.key === 'h'; - if (seg.data.length) { - let x = +seg.data[0]; - if (delta) { - x += path.x; - } - ops = ops.concat(this._doubleLine(path.x, path.y, x, path.y, o)); - path.setPosition(x, path.y); - } - break; - } - case 'V': - case 'v': { - const delta = seg.key === 'v'; - if (seg.data.length) { - let y = +seg.data[0]; - if (delta) { - y += path.y; - } - ops = ops.concat(this._doubleLine(path.x, path.y, path.x, y, o)); - path.setPosition(path.x, y); - } - break; - } - case 'Z': - case 'z': { - if (path.first) { - ops = ops.concat(this._doubleLine(path.x, path.y, path.first[0], path.first[1], o)); - path.setPosition(path.first[0], path.first[1]); - path.first = null; - } - break; - } - case 'C': - case 'c': { - const delta = seg.key === 'c'; - if (seg.data.length >= 6) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x2 = +seg.data[2]; - let y2 = +seg.data[3]; - let x = +seg.data[4]; - let y = +seg.data[5]; - if (delta) { - x1 += path.x; - x2 += path.x; - x += path.x; - y1 += path.y; - y2 += path.y; - y += path.y; - } - let ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'S': - case 's': { - const delta = seg.key === 's'; - if (seg.data.length >= 4) { - let x2 = +seg.data[0]; - let y2 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x2 += path.x; - x += path.x; - y2 += path.y; - y += path.y; - } - let x1 = x2; - let y1 = y2; - let prevKey = prevSeg ? prevSeg.key : ""; - var ref = null; - if (prevKey == 'c' || prevKey == 'C' || prevKey == 's' || prevKey == 'S') { - ref = path.bezierReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - let ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'Q': - case 'q': { - const delta = seg.key === 'q'; - if (seg.data.length >= 4) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x1 += path.x; - x += path.x; - y1 += path.y; - y += path.y; - } - let offset1 = 1 * (1 + o.roughness * 0.2); - let offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'T': - case 't': { - const delta = seg.key === 't'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let x1 = x; - let y1 = y; - let prevKey = prevSeg ? prevSeg.key : ""; - var ref = null; - if (prevKey == 'q' || prevKey == 'Q' || prevKey == 't' || prevKey == 'T') { - ref = path.quadReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - let offset1 = 1 * (1 + o.roughness * 0.2); - let offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset1, offset1, o), path.y + this._getOffset(-offset1, offset1, o)] }); - let f = [x + this._getOffset(-offset1, offset1, o), y + this._getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset1, offset1, o), y1 + this._getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this._getOffset(-offset2, offset2, o), path.y + this._getOffset(-offset2, offset2, o)] }); - f = [x + this._getOffset(-offset2, offset2, o), y + this._getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this._getOffset(-offset2, offset2, o), y1 + this._getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'A': - case 'a': { - const delta = seg.key === 'a'; - if (seg.data.length >= 7) { - let rx = +seg.data[0]; - let ry = +seg.data[1]; - let angle = +seg.data[2]; - let largeArcFlag = +seg.data[3]; - let sweepFlag = +seg.data[4]; - let x = +seg.data[5]; - let y = +seg.data[6]; - if (delta) { - x += path.x; - y += path.y; - } - if (x == path.x && y == path.y) { - break; - } - if (rx == 0 || ry == 0) { - ops = ops.concat(this._doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } else { - let final = null; - let ro = o.maxRandomnessOffset || 0; - for (let i = 0; i < 1; i++) { - let arcConverter = new RoughArcConverter( - [path.x, path.y], - [x, y], - [rx, ry], - angle, - largeArcFlag ? true : false, - sweepFlag ? true : false - ); - let segment = arcConverter.getNextSegment(); - while (segment) { - let ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); - ops = ops.concat(ob); - segment = arcConverter.getNextSegment(); - } - } - } - } - break; - } - default: - break; - } - return ops; - } - - _getOffset(min, max, ops) { - return ops.roughness * ((Math.random() * (max - min)) + min); - } - - _affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { - var A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; - var B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; - var C = cosAnglePrime; - var D = sinAnglePrime; - var E = -R * sinAnglePrime; - var F = R * cosAnglePrime; - return [ - A + C * x + D * y, - B + E * x + F * y - ]; - } - - _doubleLine(x1, y1, x2, y2, o) { - const o1 = this._line(x1, y1, x2, y2, o, true, false); - const o2 = this._line(x1, y1, x2, y2, o, true, true); - return o1.concat(o2); - } - - _line(x1, y1, x2, y2, o, move, overlay) { - const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); - let offset = o.maxRandomnessOffset || 0; - if ((offset * offset * 100) > lengthSq) { - offset = Math.sqrt(lengthSq) / 10; - } - const halfOffset = offset / 2; - const divergePoint = 0.2 + Math.random() * 0.2; - let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; - let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; - midDispX = this._getOffset(-midDispX, midDispX, o); - midDispY = this._getOffset(-midDispY, midDispY, o); - let ops = []; - if (move) { - if (overlay) { - ops.push({ - op: 'move', data: [ - x1 + this._getOffset(-halfOffset, halfOffset, o), - y1 + this._getOffset(-halfOffset, halfOffset, o) - ] - }); - } else { - ops.push({ - op: 'move', data: [ - x1 + this._getOffset(-offset, offset, o), - y1 + this._getOffset(-offset, offset, o) - ] - }); - } - } - if (overlay) { - ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-halfOffset, halfOffset, o), - x2 + this._getOffset(-halfOffset, halfOffset, o), - y2 + this._getOffset(-halfOffset, halfOffset, o) - ] - }); - } else { - ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this._getOffset(-offset, offset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this._getOffset(-offset, offset, o), - x2 + this._getOffset(-offset, offset, o), - y2 + this._getOffset(-offset, offset, o) - ] - }); - } - return ops; - } - - _curve(points, closePoint, o) { - const len = points.length; - let 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) { - let ro = o.maxRandomnessOffset; - // TODO: more roughness here? - ops.push({ ops: 'lineTo', data: [closePoint[0] + this._getOffset(-ro, ro, o), closePoint[1] + + this._getOffset(-ro, 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 = ops.concat(this._doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); - } - return ops; - } - - _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { - const radOffset = this._getOffset(-0.5, 0.5, o) - (Math.PI / 2); - const points = []; - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) - ]); - } - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) - ]); - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), - this._getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) - ]); - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) - ]); - return this._curve(points, null, o); - } - - _curveWithOffset(points, offset, o) { - const ps = []; - ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), - ]); - ps.push([ - points[0][0] + this._getOffset(-offset, offset, o), - points[0][1] + this._getOffset(-offset, offset, o), - ]); - for (let i = 1; i < points.length; i++) { - ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), - ]); - if (i === (points.length - 1)) { - ps.push([ - points[i][0] + this._getOffset(-offset, offset, o), - points[i][1] + this._getOffset(-offset, offset, o), - ]); - } - } - return this._curve(ps, null, o); - } - - _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { - const radOffset = strt + this._getOffset(-0.1, 0.1, o); - const points = []; - points.push([ - this._getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this._getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle <= stp; angle = angle + increment) { - points.push([ - this._getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this._getOffset(-offset, 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 this._curve(points, null, o); - } - - _getIntersectingLines(lineCoords, xCoords, yCoords) { - let intersections = []; - var s1 = new RoughSegment(lineCoords[0], lineCoords[1], lineCoords[2], lineCoords[3]); - for (var i = 0; i < xCoords.length; i++) { - let s2 = new RoughSegment(xCoords[i], yCoords[i], xCoords[(i + 1) % xCoords.length], yCoords[(i + 1) % xCoords.length]); - if (s1.compare(s2) == RoughSegmentRelation().INTERSECTS) { - intersections.push([s1.xi, s1.yi]); - } - } - return intersections; - } -} \ No newline at end of file diff --git a/srcts/renderer.ts b/src/renderer.ts similarity index 100% rename from srcts/renderer.ts rename to src/renderer.ts diff --git a/srcts/rough.ts b/src/rough.ts similarity index 100% rename from srcts/rough.ts rename to src/rough.ts diff --git a/src/segment.js b/src/segment.js deleted file mode 100644 index bb91ad9..0000000 --- a/src/segment.js +++ /dev/null @@ -1,131 +0,0 @@ -export function RoughSegmentRelation() { - return { - LEFT: 0, - RIGHT: 1, - INTERSECTS: 2, - AHEAD: 3, - BEHIND: 4, - SEPARATE: 5, - UNDEFINED: 6 - }; -} - -export class RoughSegment { - constructor(px1, py1, px2, py2) { - this.RoughSegmentRelationConst = RoughSegmentRelation(); - this.px1 = px1; - this.py1 = py1; - this.px2 = px2; - this.py2 = py2; - this.xi = Number.MAX_VALUE; - this.yi = Number.MAX_VALUE; - this.a = py2 - py1; - this.b = px1 - px2; - this.c = px2 * py1 - px1 * py2; - this._undefined = ((this.a == 0) && (this.b == 0) && (this.c == 0)); - } - - isUndefined() { - return this._undefined; - } - - compare(otherSegment) { - if (this.isUndefined() || otherSegment.isUndefined()) { - return this.RoughSegmentRelationConst.UNDEFINED; - } - var grad1 = Number.MAX_VALUE; - var grad2 = Number.MAX_VALUE; - var int1 = 0, int2 = 0; - var a = this.a, b = this.b, c = this.c; - - if (Math.abs(b) > 0.00001) { - grad1 = -a / b; - int1 = -c / b; - } - if (Math.abs(otherSegment.b) > 0.00001) { - grad2 = -otherSegment.a / otherSegment.b; - int2 = -otherSegment.c / otherSegment.b; - } - - if (grad1 == Number.MAX_VALUE) { - if (grad2 == Number.MAX_VALUE) { - if ((-c / a) != (-otherSegment.c / otherSegment.a)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if ((this.py1 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py1 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px1; - this.yi = this.py1; - return this.RoughSegmentRelationConst.INTERSECTS; - } - if ((this.py2 >= Math.min(otherSegment.py1, otherSegment.py2)) && (this.py2 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px2; - this.yi = this.py2; - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.SEPARATE; - } - this.xi = this.px1; - this.yi = (grad2 * this.xi + int2); - if (((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) || ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if (Math.abs(otherSegment.a) < 0.00001) { - if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - if (grad2 == Number.MAX_VALUE) { - this.xi = otherSegment.px1; - this.yi = grad1 * this.xi + int1; - if (((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) || ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if (Math.abs(a) < 0.00001) { - if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - if (grad1 == grad2) { - if (int1 != int2) { - return this.RoughSegmentRelationConst.SEPARATE; - } - if ((this.px1 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px1 <= Math.max(otherSegment.py1, otherSegment.py2))) { - this.xi = this.px1; - this.yi = this.py1; - return this.RoughSegmentRelationConst.INTERSECTS; - } - if ((this.px2 >= Math.min(otherSegment.px1, otherSegment.px2)) && (this.px2 <= Math.max(otherSegment.px1, otherSegment.px2))) { - this.xi = this.px2; - this.yi = this.py2; - return this.RoughSegmentRelationConst.INTERSECTS; - } - return this.RoughSegmentRelationConst.SEPARATE; - } - - this.xi = ((int2 - int1) / (grad1 - grad2)); - this.yi = (grad1 * this.xi + int1); - - if (((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) || ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001)) { - return this.RoughSegmentRelationConst.SEPARATE; - } - return this.RoughSegmentRelationConst.INTERSECTS; - } - - getLength() { - return this._getLength(this.px1, this.py1, this.px2, this.py2); - } - - _getLength(x1, y1, x2, y2) { - var dx = x2 - x1; - var dy = y2 - y1; - return Math.sqrt(dx * dx + dy * dy); - } -} \ No newline at end of file diff --git a/srcts/svg-async.ts b/src/svg-async.ts similarity index 100% rename from srcts/svg-async.ts rename to src/svg-async.ts diff --git a/src/svg.js b/src/svg.js deleted file mode 100644 index 071c4a3..0000000 --- a/src/svg.js +++ /dev/null @@ -1,210 +0,0 @@ -import { RoughGenerator, RoughGeneratorAsync } from './generator.js' - -export class RoughSVG { - constructor(svg, config) { - this.svg = svg; - this._init(config); - } - - _init(config) { - this.gen = new RoughGenerator(config, this.svg); - } - - get generator() { - return this.gen; - } - - get defs() { - if (!this._defs) { - let doc = this.svg.ownerDocument || document; - let dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); - if (this.svg.firstChild) { - this.svg.insertBefore(dnode, this.svg.firstChild); - } else { - this.svg.appendChild(dnode); - } - this._defs = dnode; - } - return this._defs; - } - - line(x1, y1, x2, y2, options) { - let d = this.gen.line(x1, y1, x2, y2, options); - return this.draw(d); - } - - rectangle(x, y, width, height, options) { - let d = this.gen.rectangle(x, y, width, height, options); - return this.draw(d); - } - - ellipse(x, y, width, height, options) { - let d = this.gen.ellipse(x, y, width, height, options); - return this.draw(d); - } - - circle(x, y, diameter, options) { - let d = this.gen.circle(x, y, diameter, options); - return this.draw(d); - } - - linearPath(points, options) { - let d = this.gen.linearPath(points, options); - return this.draw(d); - } - - polygon(points, options) { - let d = this.gen.polygon(points, options); - return this.draw(d); - } - - arc(x, y, width, height, start, stop, closed, options) { - let d = this.gen.arc(x, y, width, height, start, stop, closed, options); - return this.draw(d); - } - - curve(points, options) { - let d = this.gen.curve(points, options); - return this.draw(d); - } - - path(d, options) { - let drawing = this.gen.path(d, options); - return this.draw(drawing); - } - - draw(drawable) { - let sets = drawable.sets || []; - let o = drawable.options || this.gen.defaultOptions; - let doc = this.svg.ownerDocument || document; - let g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); - for (let drawing of sets) { - let path = null; - switch (drawing.type) { - case 'path': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = o.stroke; - path.style.strokeWidth = o.strokeWidth; - path.style.fill = 'none'; - break; - } - case 'fillPath': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = o.fill; - break; - } - case 'fillSketch': { - path = this._fillSketch(doc, drawing, o); - break; - } - case 'path2Dfill': { - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', drawing.path); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = o.fill; - break; - } - case 'path2Dpattern': { - const size = drawing.size; - const pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); - const id = `rough-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999))}`; - pattern.setAttribute('id', id); - pattern.setAttribute('x', 0); - pattern.setAttribute('y', 0); - pattern.setAttribute('width', 1); - pattern.setAttribute('height', 1); - pattern.setAttribute('height', 1); - pattern.setAttribute('viewBox', `0 0 ${Math.round(size[0])} ${Math.round(size[1])}`); - pattern.setAttribute('patternUnits', 'objectBoundingBox'); - const patternPath = this._fillSketch(doc, drawing, o); - pattern.appendChild(patternPath); - this.defs.appendChild(pattern); - - path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', drawing.path); - path.style.stroke = 'none'; - path.style.strokeWidth = 0; - path.style.fill = `url(#${id})`; - break; - } - } - if (path) { - g.appendChild(path); - } - } - return g; - } - - _fillSketch(doc, drawing, o) { - let fweight = o.fillWeight; - if (fweight < 0) { - fweight = o.strokeWidth / 2; - } - let path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', this._opsToPath(drawing)); - path.style.stroke = o.fill; - path.style.strokeWidth = fweight; - path.style.fill = 'none'; - return path; - } - - _opsToPath(drawing) { - return this.gen.opsToPath(drawing); - } -} - -export class RoughSVGAsync extends RoughSVG { - _init(config) { - this.gen = new RoughGeneratorAsync(config, this.svg); - } - - async line(x1, y1, x2, y2, options) { - let d = await this.gen.line(x1, y1, x2, y2, options); - return this.draw(d); - } - - async rectangle(x, y, width, height, options) { - let d = await this.gen.rectangle(x, y, width, height, options); - return this.draw(d); - } - - async ellipse(x, y, width, height, options) { - let d = await this.gen.ellipse(x, y, width, height, options); - return this.draw(d); - } - - async circle(x, y, diameter, options) { - let d = await this.gen.circle(x, y, diameter, options); - return this.draw(d); - } - - async linearPath(points, options) { - let d = await this.gen.linearPath(points, options); - return this.draw(d); - } - - async polygon(points, options) { - let d = await this.gen.polygon(points, options); - return this.draw(d); - } - - async arc(x, y, width, height, start, stop, closed, options) { - let d = await this.gen.arc(x, y, width, height, start, stop, closed, options); - return this.draw(d); - } - - async curve(points, options) { - let d = await this.gen.curve(points, options); - return this.draw(d); - } - - async path(d, options) { - let drawing = await this.gen.path(d, options); - return this.draw(drawing); - } -} \ No newline at end of file diff --git a/srcts/svg.ts b/src/svg.ts similarity index 100% rename from srcts/svg.ts rename to src/svg.ts diff --git a/srcts/utils/hachure.ts b/src/utils/hachure.ts similarity index 100% rename from srcts/utils/hachure.ts rename to src/utils/hachure.ts diff --git a/srcts/utils/liang-barsky.ts b/src/utils/liang-barsky.ts similarity index 100% rename from srcts/utils/liang-barsky.ts rename to src/utils/liang-barsky.ts diff --git a/tsconfig.json b/tsconfig.json index f1098d0..d8b4e1f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,6 @@ "emitDecoratorMetadata": true }, "include": [ - "srcts/**/*.ts" + "src/**/*.ts" ] } \ No newline at end of file From bfe5fea352c733093d37eadf1df2a91b988db9d1 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Mon, 18 Jun 2018 13:36:24 -0700 Subject: [PATCH 13/16] polygons to use pattern fills to deal with concave polygons --- bin/generator-async.d.ts | 2 +- bin/generator-async.js | 41 +++++++++------ bin/generator.d.ts | 4 +- bin/generator.js | 65 ++++++++++++++++++------ dist/rough.js | 106 ++++++++++++++++++++++++++++----------- dist/rough.min.js | 2 +- dist/rough.umd.js | 106 ++++++++++++++++++++++++++++----------- dist/rough.umd.min.js | 2 +- src/generator-async.ts | 41 +++++++++------ src/generator.ts | 67 +++++++++++++++++++------ 10 files changed, 316 insertions(+), 120 deletions(-) diff --git a/bin/generator-async.d.ts b/bin/generator-async.d.ts index bbe56d3..954037e 100644 --- a/bin/generator-async.d.ts +++ b/bin/generator-async.d.ts @@ -7,8 +7,8 @@ export declare class RoughGeneratorAsync extends RoughGenerator { ellipse(x: number, y: number, width: number, height: number, options?: Options): Promise; circle(x: number, y: number, diameter: number, options?: Options): Promise; linearPath(points: Point[], options?: Options): Promise; - polygon(points: Point[], options?: Options): Promise; arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed?: boolean, options?: Options): Promise; curve(points: Point[], options?: Options): Promise; + polygon(points: Point[], options?: Options): Promise; path(d: string, options?: Options): Promise; } diff --git a/bin/generator-async.js b/bin/generator-async.js index 57b590e..27ecfe8 100644 --- a/bin/generator-async.js +++ b/bin/generator-async.js @@ -50,21 +50,6 @@ export class RoughGeneratorAsync extends RoughGenerator { return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); } // @ts-ignore - async polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillPolygon(points, o)); - } - else { - paths.push(await this.lib.patternFillPolygon(points, o)); - } - } - paths.push(await this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - // @ts-ignore async arc(x, y, width, height, start, stop, closed = false, options) { const o = this._options(options); const paths = []; @@ -87,6 +72,32 @@ export class RoughGeneratorAsync extends RoughGenerator { return this._drawable('curve', [await this.lib.curve(points, o)], o); } // @ts-ignore + async polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + const size = this.computePolygonSize(points); + const fillPoints = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + // @ts-ignore async path(d, options) { const o = this._options(options); const paths = []; diff --git a/bin/generator.d.ts b/bin/generator.d.ts index a5dde7b..30bc840 100644 --- a/bin/generator.d.ts +++ b/bin/generator.d.ts @@ -11,15 +11,17 @@ export declare class RoughGenerator { protected _drawable(shape: string, sets: OpSet[], options: Options): Drawable; protected readonly lib: RoughRenderer; private getCanvasSize; + protected computePolygonSize(points: Point[]): Point; + protected polygonPath(points: Point[]): string; protected computePathSize(d: string): Point; 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; + polygon(points: Point[], options?: Options): Drawable; path(d: string, options?: Options): Drawable; toPaths(drawable: Drawable): PathInfo[]; private fillSketch; diff --git a/bin/generator.js b/bin/generator.js index 54d0fcf..30733a5 100644 --- a/bin/generator.js +++ b/bin/generator.js @@ -46,6 +46,32 @@ export class RoughGenerator { } return [100, 100]; } + computePolygonSize(points) { + if (points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + return [(right - left), (bottom - top)]; + } + return [0, 0]; + } + polygonPath(points) { + let d = ''; + if (points.length) { + d = `M${points[0][0]},${points[0][1]}`; + for (let i = 1; i < points.length; i++) { + d = `${d} L${points[i][0]},${points[i][1]}`; + } + } + return d; + } computePathSize(d) { let size = [0, 0]; if (hasSelf && self.document) { @@ -119,20 +145,6 @@ export class RoughGenerator { const o = this._options(options); return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); } - polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); - } - else { - paths.push(this.lib.patternFillPolygon(points, o)); - } - } - paths.push(this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } arc(x, y, width, height, start, stop, closed = false, options) { const o = this._options(options); const paths = []; @@ -153,6 +165,31 @@ export class RoughGenerator { const o = this._options(options); return this._drawable('curve', [this.lib.curve(points, o)], o); } + polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + const size = this.computePolygonSize(points); + const fillPoints = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } path(d, options) { const o = this._options(options); const paths = []; diff --git a/dist/rough.js b/dist/rough.js index 05dae20..dd74f48 100644 --- a/dist/rough.js +++ b/dist/rough.js @@ -1430,6 +1430,32 @@ var rough = (function () { } return [100, 100]; } + computePolygonSize(points) { + if (points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + return [(right - left), (bottom - top)]; + } + return [0, 0]; + } + polygonPath(points) { + let d = ''; + if (points.length) { + d = `M${points[0][0]},${points[0][1]}`; + for (let i = 1; i < points.length; i++) { + d = `${d} L${points[i][0]},${points[i][1]}`; + } + } + return d; + } computePathSize(d) { let size = [0, 0]; if (hasSelf$1 && self.document) { @@ -1503,20 +1529,6 @@ var rough = (function () { const o = this._options(options); return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); } - polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); - } - else { - paths.push(this.lib.patternFillPolygon(points, o)); - } - } - paths.push(this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } arc(x, y, width, height, start, stop, closed = false, options) { const o = this._options(options); const paths = []; @@ -1537,6 +1549,31 @@ var rough = (function () { const o = this._options(options); return this._drawable('curve', [this.lib.curve(points, o)], o); } + polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + const size = this.computePolygonSize(points); + const fillPoints = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } path(d, options) { const o = this._options(options); const paths = []; @@ -1882,21 +1919,6 @@ var rough = (function () { return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); } // @ts-ignore - async polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillPolygon(points, o)); - } - else { - paths.push(await this.lib.patternFillPolygon(points, o)); - } - } - paths.push(await this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - // @ts-ignore async arc(x, y, width, height, start, stop, closed = false, options) { const o = this._options(options); const paths = []; @@ -1919,6 +1941,32 @@ var rough = (function () { return this._drawable('curve', [await this.lib.curve(points, o)], o); } // @ts-ignore + async polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + const size = this.computePolygonSize(points); + const fillPoints = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + // @ts-ignore async path(d, options) { const o = this._options(options); const paths = []; diff --git a/dist/rough.min.js b/dist/rough.min.js index 5b1951d..7c5b94c 100644 --- a/dist/rough.min.js +++ b/dist/rough.min.js @@ -1 +1 @@ -var rough=function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var n=Math.round,s=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,f=Math.cos,h=Math.sin,g=Math.abs,c=Math.PI;const u={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const n=this.tokenize(t);let s=0,i=n[s],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a].length:(s++,l=u[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)s++,l=u[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(s+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=f(this._theta),t=h(this._theta),i=this._theta+this._delta,n=f(i),s=h(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=f(y),x=h(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=h&&(h=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=h/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*f(x),u+_*h(x),d)),v=v.concat(this.doubleLine(o,u,o+y*f(b),u+_*h(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*f(x),u+_*h(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*f(g),p+d*h(g)]);return x.push([o+r*f(y),p+d*h(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const f=r/2,h=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,s),t+this.getOffset(-f,f,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,s),c+t+(n-t)*h+this.getOffset(-f,f,s),g+e+2*(i-e)*h+this.getOffset(-f,f,s),c+t+2*(n-t)*h+this.getOffset(-f,f,s),i+this.getOffset(-f,f,s),n+this.getOffset(-f,f,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-r,r,s),c+t+(n-t)*h+this.getOffset(-r,r,s),g+e+2*(i-e)*h+this.getOffset(-r,r,s),c+t+2*(n-t)*h+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,f,n)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class E{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(C&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const n=self.document.createElementNS('http://www.w3.org/2000/svg','path');n.setAttribute('d',e),i.appendChild(n),self.document.body.appendChild(i);const s=n.getBBox();s&&(t[0]=s.width||0,t[1]=s.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=p(t[0],i[0]),t[1]=p(t[1],i[1]),t}line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[this.lib.line(e,t,i,n,a)],a)}rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(s,a)):l.push(this.lib.patternFillPolygon(s,a))}return l.push(this.lib.rectangle(e,t,i,n,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push(this.lib.patternFillEllipse(e,t,i,n,a));return l.push(this.lib.ellipse(e,t,i,n,a)),this._drawable('ellipse',l,a)}circle(e,t,i,n){const s=this.ellipse(e,t,i,i,n);return s.shape='circle',s}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push(this.lib.solidFillPolygon(e,i)):n.push(this.lib.patternFillPolygon(e,i))),n.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',n,i)}arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,n,s,a,r));return o.push(this.lib.arc(e,t,i,n,s,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push(this.lib.svgPath(e,i)),this._drawable('path',n,i)}toPaths(e){const t=e.sets||[],i=e.options||this.defaultOptions,s=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:i.stroke,strokeWidth:i.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,i);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'path2Dpattern':{const t=a.size,s={x:0,y:0,width:1,height:1,viewBox:`0 0 ${n(t[0])} ${n(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,i)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:s};break}}e&&s.push(e)}return s}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const L='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new E(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(L){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(L)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class N extends E{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push((await this.lib.solidFillPolygon(e,i))):n.push((await this.lib.patternFillPolygon(e,i)))),n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class R extends W{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const z='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new E(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(z&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||z&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new R(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new N(e,t):new E(e,t)}};return M}(); +var rough=function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var n=Math.round,s=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,h=Math.cos,f=Math.sin,g=Math.abs,c=Math.PI;const u={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const n=this.tokenize(t);let s=0,i=n[s],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a].length:(s++,l=u[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)s++,l=u[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(s+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=h(this._theta),t=f(this._theta),i=this._theta+this._delta,n=h(i),s=f(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=h(y),x=f(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=f&&(f=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(h%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=f/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*h(x),u+_*f(x),d)),v=v.concat(this.doubleLine(o,u,o+y*h(b),u+_*f(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*h(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*h(g),p+d*f(g)]);return x.push([o+r*h(y),p+d*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const h=r/2,f=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-h,h,s),t+this.getOffset(-h,h,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-h,h,s),c+t+(n-t)*f+this.getOffset(-h,h,s),g+e+2*(i-e)*f+this.getOffset(-h,h,s),c+t+2*(n-t)*f+this.getOffset(-h,h,s),i+this.getOffset(-h,h,s),n+this.getOffset(-h,h,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-r,r,s),c+t+(n-t)*f+this.getOffset(-r,r,s),g+e+2*(i-e)*f+this.getOffset(-r,r,s),c+t+2*(n-t)*f+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const h=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(h),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,h=o;const f=i?i.key:'';let g=null;('c'===f||'C'===f||'s'===f||'S'===f)&&(g=e.bezierReflectionPoint),g&&(d=g[0],h=g[1]);const c=this._bezierTo(d,h,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],h=+t.data[6];if(i&&(d+=e.x,h+=e.y),d===e.x&&h===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,h,n)),e.setPosition(d,h);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,h],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class z{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePolygonSize(e){if(e.length){let t=e[0][0],n=e[0][0],s=e[0][1],a=e[0][1];for(let o=1;oi&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const E='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new z(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(E){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(E)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class L extends z{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async polygon(e,t){const i=this._options(t),n=[];if(i.fill)if('solid'===i.fillStyle)n.push((await this.lib.solidFillPolygon(e,i)));else{const t=this.computePolygonSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=this.polygonPath(e),n.push(a)}return n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class N extends W{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const R='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new z(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(R&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||R&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new N(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new L(e,t):new z(e,t)}};return M}(); diff --git a/dist/rough.umd.js b/dist/rough.umd.js index 71a7a99..0fb1938 100644 --- a/dist/rough.umd.js +++ b/dist/rough.umd.js @@ -1433,6 +1433,32 @@ } return [100, 100]; } + computePolygonSize(points) { + if (points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + return [(right - left), (bottom - top)]; + } + return [0, 0]; + } + polygonPath(points) { + let d = ''; + if (points.length) { + d = `M${points[0][0]},${points[0][1]}`; + for (let i = 1; i < points.length; i++) { + d = `${d} L${points[i][0]},${points[i][1]}`; + } + } + return d; + } computePathSize(d) { let size = [0, 0]; if (hasSelf$1 && self.document) { @@ -1506,20 +1532,6 @@ const o = this._options(options); return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); } - polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); - } - else { - paths.push(this.lib.patternFillPolygon(points, o)); - } - } - paths.push(this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } arc(x, y, width, height, start, stop, closed = false, options) { const o = this._options(options); const paths = []; @@ -1540,6 +1552,31 @@ const o = this._options(options); return this._drawable('curve', [this.lib.curve(points, o)], o); } + polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } + else { + const size = this.computePolygonSize(points); + const fillPoints = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } path(d, options) { const o = this._options(options); const paths = []; @@ -1885,21 +1922,6 @@ return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); } // @ts-ignore - async polygon(points, options) { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillPolygon(points, o)); - } - else { - paths.push(await this.lib.patternFillPolygon(points, o)); - } - } - paths.push(await this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - // @ts-ignore async arc(x, y, width, height, start, stop, closed = false, options) { const o = this._options(options); const paths = []; @@ -1922,6 +1944,32 @@ return this._drawable('curve', [await this.lib.curve(points, o)], o); } // @ts-ignore + async polygon(points, options) { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } + else { + const size = this.computePolygonSize(points); + const fillPoints = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + // @ts-ignore async path(d, options) { const o = this._options(options); const paths = []; diff --git a/dist/rough.umd.min.js b/dist/rough.umd.min.js index eb5934d..8157213 100644 --- a/dist/rough.umd.min.js +++ b/dist/rough.umd.min.js @@ -1 +1 @@ -(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var n=Math.round,s=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,f=Math.cos,h=Math.sin,g=Math.abs,c=Math.PI;const u={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const n=this.tokenize(t);let s=0,i=n[s],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a].length:(s++,l=u[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)s++,l=u[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(s+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=f(this._theta),t=h(this._theta),i=this._theta+this._delta,n=f(i),s=h(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=f(y),x=h(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=h&&(h=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(f%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=h/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*f(x),u+_*h(x),d)),v=v.concat(this.doubleLine(o,u,o+y*f(b),u+_*h(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*f(x),u+_*h(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*f(g),p+d*h(g)]);return x.push([o+r*f(y),p+d*h(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const f=r/2,h=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,s),t+this.getOffset(-f,f,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,s),c+t+(n-t)*h+this.getOffset(-f,f,s),g+e+2*(i-e)*h+this.getOffset(-f,f,s),c+t+2*(n-t)*h+this.getOffset(-f,f,s),i+this.getOffset(-f,f,s),n+this.getOffset(-f,f,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-r,r,s),c+t+(n-t)*h+this.getOffset(-r,r,s),g+e+2*(i-e)*h+this.getOffset(-r,r,s),c+t+2*(n-t)*h+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,f,n)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class E{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePathSize(e){let t=[0,0];if(C&&self.document)try{const i=self.document.createElementNS('http://www.w3.org/2000/svg','svg');i.setAttribute('width','0'),i.setAttribute('height','0');const n=self.document.createElementNS('http://www.w3.org/2000/svg','path');n.setAttribute('d',e),i.appendChild(n),self.document.body.appendChild(i);const s=n.getBBox();s&&(t[0]=s.width||0,t[1]=s.height||0),self.document.body.removeChild(i)}catch(e){}const i=this.getCanvasSize();return t[0]*t[1]||(t=i),t[0]=p(t[0],i[0]),t[1]=p(t[1],i[1]),t}line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[this.lib.line(e,t,i,n,a)],a)}rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push(this.lib.solidFillPolygon(s,a)):l.push(this.lib.patternFillPolygon(s,a))}return l.push(this.lib.rectangle(e,t,i,n,a)),this._drawable('rectangle',l,a)}ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push(this.lib.patternFillEllipse(e,t,i,n,a));return l.push(this.lib.ellipse(e,t,i,n,a)),this._drawable('ellipse',l,a)}circle(e,t,i,n){const s=this.ellipse(e,t,i,i,n);return s.shape='circle',s}linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[this.lib.linearPath(e,!1,i)],i)}polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push(this.lib.solidFillPolygon(e,i)):n.push(this.lib.patternFillPolygon(e,i))),n.push(this.lib.linearPath(e,!0,i)),this._drawable('polygon',n,i)}arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push(this.lib.patternFillArc(e,t,i,n,s,a,r));return o.push(this.lib.arc(e,t,i,n,s,a,l,!0,r)),this._drawable('arc',o,r)}curve(e,t){const i=this._options(t);return this._drawable('curve',[this.lib.curve(e,i)],i)}path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push(this.lib.svgPath(e,i)),this._drawable('path',n,i)}toPaths(e){const t=e.sets||[],i=e.options||this.defaultOptions,s=[];for(const a of t){let e=null;switch(a.type){case'path':e={d:this.opsToPath(a),stroke:i.stroke,strokeWidth:i.strokeWidth,fill:'none'};break;case'fillPath':e={d:this.opsToPath(a),stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'fillSketch':e=this.fillSketch(a,i);break;case'path2Dfill':e={d:a.path||'',stroke:'none',strokeWidth:0,fill:i.fill||'none'};break;case'path2Dpattern':{const t=a.size,s={x:0,y:0,width:1,height:1,viewBox:`0 0 ${n(t[0])} ${n(t[1])}`,patternUnits:'objectBoundingBox',path:this.fillSketch(a,i)};e={d:a.path,stroke:'none',strokeWidth:0,pattern:s};break}}e&&s.push(e)}return s}fillSketch(e,t){let i=t.fillWeight;return 0>i&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const L='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new E(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(L){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(L)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class N extends E{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async polygon(e,t){const i=this._options(t),n=[];return i.fill&&('solid'===i.fillStyle?n.push((await this.lib.solidFillPolygon(e,i))):n.push((await this.lib.patternFillPolygon(e,i)))),n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class R extends W{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const z='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new E(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(z&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||z&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new N(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new R(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new N(e,t):new E(e,t)}};return M}); +(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var n=Math.round,s=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,h=Math.cos,f=Math.sin,g=Math.abs,c=Math.PI;const u={A:{length:7},a:{length:7},C:{length:6},c:{length:6},H:{length:1},h:{length:1},L:{length:2},l:{length:2},M:{length:2},m:{length:2},Q:{length:4},q:{length:4},S:{length:4},s:{length:4},T:{length:4},t:{length:2},V:{length:1},v:{length:1},Z:{length:0},z:{length:0}};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const n=this.tokenize(t);let s=0,i=n[s],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a].length:(s++,l=u[i.text].length,a=i.text);else if('M'===i.text||'m'===i.text)s++,l=u[i.text].length,a=i.text;else return void this.parseData('M0,0'+t);if(s+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=h(this._theta),t=f(this._theta),i=this._theta+this._delta,n=h(i),s=f(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=h(y),x=f(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=f&&(f=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(h%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=f/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*h(x),u+_*f(x),d)),v=v.concat(this.doubleLine(o,u,o+y*h(b),u+_*f(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*h(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*h(g),p+d*f(g)]);return x.push([o+r*h(y),p+d*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const h=r/2,f=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-h,h,s),t+this.getOffset(-h,h,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-h,h,s),c+t+(n-t)*f+this.getOffset(-h,h,s),g+e+2*(i-e)*f+this.getOffset(-h,h,s),c+t+2*(n-t)*f+this.getOffset(-h,h,s),i+this.getOffset(-h,h,s),n+this.getOffset(-h,h,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-r,r,s),c+t+(n-t)*f+this.getOffset(-r,r,s),g+e+2*(i-e)*f+this.getOffset(-r,r,s),c+t+2*(n-t)*f+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const h=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(h),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,h=o;const f=i?i.key:'';let g=null;('c'===f||'C'===f||'s'===f||'S'===f)&&(g=e.bezierReflectionPoint),g&&(d=g[0],h=g[1]);const c=this._bezierTo(d,h,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],h=+t.data[6];if(i&&(d+=e.x,h+=e.y),d===e.x&&h===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,h,n)),e.setPosition(d,h);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,h],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class z{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePolygonSize(e){if(e.length){let t=e[0][0],n=e[0][0],s=e[0][1],a=e[0][1];for(let o=1;oi&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const E='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new z(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(E){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(E)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class L extends z{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async polygon(e,t){const i=this._options(t),n=[];if(i.fill)if('solid'===i.fillStyle)n.push((await this.lib.solidFillPolygon(e,i)));else{const t=this.computePolygonSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=this.polygonPath(e),n.push(a)}return n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class N extends W{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const R='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new z(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(R&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||R&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new N(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new L(e,t):new z(e,t)}};return M}); diff --git a/src/generator-async.ts b/src/generator-async.ts index 1dc92a9..46a5950 100644 --- a/src/generator-async.ts +++ b/src/generator-async.ts @@ -55,21 +55,6 @@ export class RoughGeneratorAsync extends RoughGenerator { return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); } - // @ts-ignore - async polygon(points: Point[], options?: Options): Promise { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(await this.lib.solidFillPolygon(points, o)); - } else { - paths.push(await this.lib.patternFillPolygon(points, o)); - } - } - paths.push(await this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - // @ts-ignore async arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): Promise { const o = this._options(options); @@ -93,6 +78,32 @@ export class RoughGeneratorAsync extends RoughGenerator { return this._drawable('curve', [await this.lib.curve(points, o)], o); } + // @ts-ignore + async polygon(points: Point[], options?: Options): Promise { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(await this.lib.solidFillPolygon(points, o)); + } else { + const size = this.computePolygonSize(points); + const fillPoints: Point[] = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = await this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(await this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + // @ts-ignore async path(d: string, options?: Options): Promise { const o = this._options(options); diff --git a/src/generator.ts b/src/generator.ts index c329350..588fe56 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -60,6 +60,34 @@ export class RoughGenerator { return [100, 100]; } + protected computePolygonSize(points: Point[]): Point { + if (points.length) { + let left = points[0][0]; + let right = points[0][0]; + let top = points[0][1]; + let bottom = points[0][1]; + for (let i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + return [(right - left), (bottom - top)]; + } + return [0, 0]; + } + + protected polygonPath(points: Point[]): string { + let d = ''; + if (points.length) { + d = `M${points[0][0]},${points[0][1]}`; + for (let i = 1; i < points.length; i++) { + d = `${d} L${points[i][0]},${points[i][1]}`; + } + } + return d; + } + protected computePathSize(d: string): Point { let size: Point = [0, 0]; if (hasSelf && self.document) { @@ -136,20 +164,6 @@ export class RoughGenerator { return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); } - polygon(points: Point[], options?: Options): Drawable { - const o = this._options(options); - const paths = []; - if (o.fill) { - if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); - } else { - paths.push(this.lib.patternFillPolygon(points, o)); - } - } - paths.push(this.lib.linearPath(points, true, o)); - return this._drawable('polygon', paths, o); - } - arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): Drawable { const o = this._options(options); const paths = []; @@ -171,6 +185,31 @@ export class RoughGenerator { return this._drawable('curve', [this.lib.curve(points, o)], o); } + polygon(points: Point[], options?: Options): Drawable { + const o = this._options(options); + const paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } else { + const size = this.computePolygonSize(points); + const fillPoints: Point[] = [ + [0, 0], + [size[0], 0], + [size[0], size[1]], + [0, size[1]] + ]; + const shape = this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + path(d: string, options?: Options): Drawable { const o = this._options(options); const paths: OpSet[] = []; From d249c7c7cf0efd313ad50e30d12a6c15f76fab5a Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Mon, 18 Jun 2018 15:02:58 -0700 Subject: [PATCH 14/16] es5 build --- .babelrc | 8 + dist/rough.es5.js | 2821 +++++++++++++++++++++++++++++++++++++++++ dist/rough.es5.min.js | 1 + package-lock.json | 636 +++++++++- package.json | 8 +- rollup.config.js | 21 + 6 files changed, 3493 insertions(+), 2 deletions(-) create mode 100644 .babelrc create mode 100644 dist/rough.es5.js create mode 100644 dist/rough.es5.min.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..4f6af89 --- /dev/null +++ b/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [ + "es2015" + ], + "plugins": [ + "external-helpers" + ] +} \ No newline at end of file diff --git a/dist/rough.es5.js b/dist/rough.es5.js new file mode 100644 index 0000000..399d4ce --- /dev/null +++ b/dist/rough.es5.js @@ -0,0 +1,2821 @@ +var rough = (function () { + 'use strict'; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; + }; + + var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; + }; + + function isType(token, type) { + return token.type === type; + } + var PARAMS = { + A: { length: 7 }, + a: { length: 7 }, + C: { length: 6 }, + c: { length: 6 }, + H: { length: 1 }, + h: { length: 1 }, + L: { length: 2 }, + l: { length: 2 }, + M: { length: 2 }, + m: { length: 2 }, + Q: { length: 4 }, + q: { length: 4 }, + S: { length: 4 }, + s: { length: 4 }, + T: { length: 4 }, + t: { length: 2 }, + V: { length: 1 }, + v: { length: 1 }, + Z: { length: 0 }, + z: { length: 0 } + }; + + var ParsedPath = function () { + function ParsedPath(d) { + classCallCheck(this, ParsedPath); + + this.COMMAND = 0; + this.NUMBER = 1; + this.EOD = 2; + this.segments = []; + this.parseData(d); + this.processPoints(); + } + + createClass(ParsedPath, [{ + key: 'tokenize', + value: function tokenize(d) { + var 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: this.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: this.NUMBER, text: '' + parseFloat(RegExp.$1) }; + d = d.substr(RegExp.$1.length); + } else { + console.error('Unrecognized segment command: ' + d); + return []; + } + } + tokens[tokens.length] = { type: this.EOD, text: '' }; + return tokens; + } + }, { + key: 'parseData', + value: function parseData(d) { + var tokens = this.tokenize(d); + var index = 0; + var token = tokens[index]; + var mode = 'BOD'; + this.segments = new Array(); + while (!isType(token, this.EOD)) { + var param_length = void 0; + var params = new Array(); + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } else { + this.parseData('M0,0' + d); + return; + } + } else { + if (isType(token, this.NUMBER)) { + param_length = PARAMS[mode].length; + } else { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + } + if (index + param_length < tokens.length) { + for (var i = index; i < index + param_length; i++) { + var numbeToken = tokens[i]; + if (isType(numbeToken, this.NUMBER)) { + params[params.length] = +numbeToken.text; + } else { + console.error('Parameter type is not a number: ' + mode + ',' + numbeToken.text); + return; + } + } + if (PARAMS[mode]) { + var segment = { key: mode, data: params }; + this.segments.push(segment); + index += param_length; + token = tokens[index]; + if (mode === 'M') mode = 'L'; + if (mode === 'm') mode = 'l'; + } else { + console.error('Unsupported segment type: ' + mode); + return; + } + } else { + console.error('Path data ended before all parameters were found'); + } + } + } + }, { + key: 'processPoints', + value: function processPoints() { + var first = null; + var currentPoint = [0, 0]; + for (var i = 0; i < this.segments.length; i++) { + var s = this.segments[i]; + switch (s.key) { + case 'M': + case 'L': + case 'T': + s.point = [s.data[0], s.data[1]]; + break; + case 'm': + case 'l': + case 't': + s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; + break; + case 'H': + s.point = [s.data[0], currentPoint[1]]; + break; + case 'h': + s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; + break; + case 'V': + s.point = [currentPoint[0], s.data[0]]; + break; + case 'v': + s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; + break; + case 'z': + case 'Z': + if (first) { + s.point = [first[0], first[1]]; + } + break; + case 'C': + s.point = [s.data[4], s.data[5]]; + break; + case 'c': + s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; + break; + case 'S': + s.point = [s.data[2], s.data[3]]; + break; + case 's': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'Q': + s.point = [s.data[2], s.data[3]]; + break; + case 'q': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'A': + s.point = [s.data[5], s.data[6]]; + break; + case 'a': + s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; + break; + } + if (s.key === 'm' || s.key === 'M') { + first = null; + } + if (s.point) { + currentPoint = s.point; + if (!first) { + first = s.point; + } + } + if (s.key === 'z' || s.key === 'Z') { + first = null; + } + } + } + }, { + key: 'closed', + get: function get$$1() { + if (typeof this._closed === 'undefined') { + this._closed = false; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = this.segments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var s = _step.value; + + if (s.key.toLowerCase() === 'z') { + this._closed = true; + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + } + return this._closed; + } + }]); + return ParsedPath; + }(); + + var RoughPath = function () { + function RoughPath(d) { + classCallCheck(this, RoughPath); + + this._position = [0, 0]; + this._first = null; + this.bezierReflectionPoint = null; + this.quadReflectionPoint = null; + this.parsed = new ParsedPath(d); + } + + createClass(RoughPath, [{ + key: 'setPosition', + value: function setPosition(x, y) { + this._position = [x, y]; + if (!this._first) { + this._first = [x, y]; + } + } + }, { + key: 'segments', + get: function get$$1() { + return this.parsed.segments; + } + }, { + key: 'closed', + get: function get$$1() { + return this.parsed.closed; + } + }, { + key: 'linearPoints', + get: function get$$1() { + if (!this._linearPoints) { + var lp = []; + var points = []; + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = this.parsed.segments[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var s = _step2.value; + + var key = s.key.toLowerCase(); + if (key === 'm' || key === 'z') { + if (points.length) { + lp.push(points); + points = []; + } + if (key === 'z') { + continue; + } + } + if (s.point) { + points.push(s.point); + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + if (points.length) { + lp.push(points); + points = []; + } + this._linearPoints = lp; + } + return this._linearPoints; + } + }, { + key: 'first', + get: function get$$1() { + return this._first; + }, + set: function set$$1(v) { + this._first = v; + } + }, { + key: 'position', + get: function get$$1() { + return this._position; + } + }, { + key: 'x', + get: function get$$1() { + return this._position[0]; + } + }, { + key: 'y', + get: function get$$1() { + return this._position[1]; + } + }]); + return RoughPath; + }(); + // Algorithm as described in https://www.w3.org/TR/SVG/implnote.html + // Code adapted from nsSVGPathDataParser.cpp in Mozilla + // https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 + var RoughArcConverter = function () { + function RoughArcConverter(from, to, radii, angle, largeArcFlag, sweepFlag) { + classCallCheck(this, RoughArcConverter); + + this._segIndex = 0; + this._numSegs = 0; + this._rx = 0; + this._ry = 0; + this._sinPhi = 0; + this._cosPhi = 0; + this._C = [0, 0]; + this._theta = 0; + this._delta = 0; + this._T = 0; + this._from = from; + if (from[0] === to[0] && from[1] === to[1]) { + return; + } + var radPerDeg = Math.PI / 180; + this._rx = Math.abs(radii[0]); + this._ry = Math.abs(radii[1]); + this._sinPhi = Math.sin(angle * radPerDeg); + this._cosPhi = Math.cos(angle * radPerDeg); + var x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; + var y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; + var root = 0; + var numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; + if (numerator < 0) { + var s = Math.sqrt(1 - numerator / (this._rx * this._rx * this._ry * this._ry)); + this._rx = this._rx * s; + this._ry = this._ry * s; + root = 0; + } else { + root = (largeArcFlag === sweepFlag ? -1.0 : 1.0) * Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); + } + var cxdash = root * this._rx * y1dash / this._ry; + var cydash = -root * this._ry * x1dash / this._rx; + this._C = [0, 0]; + this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; + this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; + this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); + var dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); + if (!sweepFlag && dtheta > 0) { + dtheta -= 2 * Math.PI; + } else if (sweepFlag && dtheta < 0) { + dtheta += 2 * Math.PI; + } + this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); + this._delta = dtheta / this._numSegs; + this._T = 8 / 3 * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); + } + + createClass(RoughArcConverter, [{ + key: 'getNextSegment', + value: function getNextSegment() { + if (this._segIndex === this._numSegs) { + return null; + } + var cosTheta1 = Math.cos(this._theta); + var sinTheta1 = Math.sin(this._theta); + var theta2 = this._theta + this._delta; + var cosTheta2 = Math.cos(theta2); + var sinTheta2 = Math.sin(theta2); + var to = [this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1]]; + var cp1 = [this._from[0] + this._T * (-this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), this._from[1] + this._T * (-this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1)]; + var cp2 = [to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2)]; + this._theta = theta2; + this._from = [to[0], to[1]]; + this._segIndex++; + return { + cp1: cp1, + cp2: cp2, + to: to + }; + } + }, { + key: 'calculateVectorAngle', + value: function calculateVectorAngle(ux, uy, vx, vy) { + var ta = Math.atan2(uy, ux); + var tb = Math.atan2(vy, vx); + if (tb >= ta) return tb - ta; + return 2 * Math.PI - (ta - tb); + } + }]); + return RoughArcConverter; + }(); + var PathFitter = function () { + function PathFitter(sets, closed) { + classCallCheck(this, PathFitter); + + this.sets = sets; + this.closed = closed; + } + + createClass(PathFitter, [{ + key: 'fit', + value: function fit(simplification) { + var outSets = []; + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = this.sets[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + var set$$1 = _step3.value; + + var length = set$$1.length; + var estLength = Math.floor(simplification * length); + if (estLength < 5) { + if (length <= 5) { + continue; + } + estLength = 5; + } + outSets.push(this.reduce(set$$1, estLength)); + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3.return) { + _iterator3.return(); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } + + var d = ''; + var _iteratorNormalCompletion4 = true; + var _didIteratorError4 = false; + var _iteratorError4 = undefined; + + try { + for (var _iterator4 = outSets[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { + var _set = _step4.value; + + for (var i = 0; i < _set.length; i++) { + var point = _set[i]; + if (i === 0) { + d += 'M' + point[0] + ',' + point[1]; + } else { + d += 'L' + point[0] + ',' + point[1]; + } + } + if (this.closed) { + d += 'z '; + } + } + } catch (err) { + _didIteratorError4 = true; + _iteratorError4 = err; + } finally { + try { + if (!_iteratorNormalCompletion4 && _iterator4.return) { + _iterator4.return(); + } + } finally { + if (_didIteratorError4) { + throw _iteratorError4; + } + } + } + + return d; + } + }, { + key: 'distance', + value: function distance(p1, p2) { + return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); + } + }, { + key: 'reduce', + value: function reduce(set$$1, count) { + if (set$$1.length <= count) { + return set$$1; + } + var points = set$$1.slice(0); + while (points.length > count) { + var minArea = -1; + var minIndex = -1; + for (var i = 1; i < points.length - 1; i++) { + var a = this.distance(points[i - 1], points[i]); + var b = this.distance(points[i], points[i + 1]); + var c = this.distance(points[i - 1], points[i + 1]); + var s = (a + b + c) / 2.0; + var area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); + if (minArea < 0 || area < minArea) { + minArea = area; + minIndex = i; + } + } + if (minIndex > 0) { + points.splice(minIndex, 1); + } else { + break; + } + } + return points; + } + }]); + return PathFitter; + }(); + + var Segment = function () { + function Segment(p1, p2) { + classCallCheck(this, Segment); + + this.xi = Number.MAX_VALUE; + this.yi = Number.MAX_VALUE; + this.px1 = p1[0]; + this.py1 = p1[1]; + this.px2 = p2[0]; + this.py2 = p2[1]; + this.a = this.py2 - this.py1; + this.b = this.px1 - this.px2; + this.c = this.px2 * this.py1 - this.px1 * this.py2; + this._undefined = this.a === 0 && this.b === 0 && this.c === 0; + } + + createClass(Segment, [{ + key: "isUndefined", + value: function isUndefined() { + return this._undefined; + } + }, { + key: "intersects", + value: function intersects(otherSegment) { + if (this.isUndefined() || otherSegment.isUndefined()) { + return false; + } + var grad1 = Number.MAX_VALUE; + var grad2 = Number.MAX_VALUE; + var int1 = 0, + int2 = 0; + var a = this.a, + b = this.b, + c = this.c; + if (Math.abs(b) > 0.00001) { + grad1 = -a / b; + int1 = -c / b; + } + if (Math.abs(otherSegment.b) > 0.00001) { + grad2 = -otherSegment.a / otherSegment.b; + int2 = -otherSegment.c / otherSegment.b; + } + if (grad1 === Number.MAX_VALUE) { + if (grad2 === Number.MAX_VALUE) { + if (-c / a !== -otherSegment.c / otherSegment.a) { + return false; + } + if (this.py1 >= Math.min(otherSegment.py1, otherSegment.py2) && this.py1 <= Math.max(otherSegment.py1, otherSegment.py2)) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if (this.py2 >= Math.min(otherSegment.py1, otherSegment.py2) && this.py2 <= Math.max(otherSegment.py1, otherSegment.py2)) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = this.px1; + this.yi = grad2 * this.xi + int2; + if ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001 || (otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) { + return false; + } + if (Math.abs(otherSegment.a) < 0.00001) { + if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad2 === Number.MAX_VALUE) { + this.xi = otherSegment.px1; + this.yi = grad1 * this.xi + int1; + if ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001 || (this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) { + return false; + } + if (Math.abs(a) < 0.00001) { + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad1 === grad2) { + if (int1 !== int2) { + return false; + } + if (this.px1 >= Math.min(otherSegment.px1, otherSegment.px2) && this.px1 <= Math.max(otherSegment.py1, otherSegment.py2)) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if (this.px2 >= Math.min(otherSegment.px1, otherSegment.px2) && this.px2 <= Math.max(otherSegment.px1, otherSegment.px2)) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = (int2 - int1) / (grad1 - grad2); + this.yi = grad1 * this.xi + int1; + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001 || (otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + }]); + return Segment; + }(); + + var HachureIterator = function () { + function HachureIterator(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { + classCallCheck(this, HachureIterator); + + this.deltaX = 0; + this.hGap = 0; + this.top = top; + this.bottom = bottom; + this.left = left; + this.right = right; + this.gap = gap; + this.sinAngle = sinAngle; + this.tanAngle = tanAngle; + if (Math.abs(sinAngle) < 0.0001) { + this.pos = left + gap; + } else if (Math.abs(sinAngle) > 0.9999) { + this.pos = top + gap; + } else { + this.deltaX = (bottom - top) * Math.abs(tanAngle); + this.pos = left - Math.abs(this.deltaX); + this.hGap = Math.abs(gap / cosAngle); + this.sLeft = new Segment([left, bottom], [left, top]); + this.sRight = new Segment([right, bottom], [right, top]); + } + } + + createClass(HachureIterator, [{ + key: 'nextLine', + value: function nextLine() { + if (Math.abs(this.sinAngle) < 0.0001) { + if (this.pos < this.right) { + var line = [this.pos, this.top, this.pos, this.bottom]; + this.pos += this.gap; + return line; + } + } else if (Math.abs(this.sinAngle) > 0.9999) { + if (this.pos < this.bottom) { + var _line = [this.left, this.pos, this.right, this.pos]; + this.pos += this.gap; + return _line; + } + } else { + var xLower = this.pos - this.deltaX / 2; + var xUpper = this.pos + this.deltaX / 2; + var yLower = this.bottom; + var yUpper = this.top; + if (this.pos < this.right + this.deltaX) { + while (xLower < this.left && xUpper < this.left || xLower > this.right && xUpper > this.right) { + this.pos += this.hGap; + xLower = this.pos - this.deltaX / 2; + xUpper = this.pos + this.deltaX / 2; + if (this.pos > this.right + this.deltaX) { + return null; + } + } + var s = new Segment([xLower, yLower], [xUpper, yUpper]); + if (this.sLeft && s.intersects(this.sLeft)) { + xLower = s.xi; + yLower = s.yi; + } + if (this.sRight && s.intersects(this.sRight)) { + xUpper = s.xi; + yUpper = s.yi; + } + if (this.tanAngle > 0) { + xLower = this.right - (xLower - this.left); + xUpper = this.right - (xUpper - this.left); + } + var _line2 = [xLower, yLower, xUpper, yUpper]; + this.pos += this.hGap; + return _line2; + } + } + return null; + } + }]); + return HachureIterator; + }(); + + var HachureFiller = function () { + function HachureFiller(renderer) { + classCallCheck(this, HachureFiller); + + this.renderer = renderer; + } + + createClass(HachureFiller, [{ + key: 'fillPolygon', + value: function fillPolygon(points, o) { + return this._fillPolygon(points, o); + } + }, { + key: 'fillEllipse', + value: function fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o); + } + }, { + key: '_fillPolygon', + value: function _fillPolygon(points, o) { + var connectEnds = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var ops = []; + if (points && points.length) { + var left = points[0][0]; + var right = points[0][0]; + var top = points[0][1]; + var bottom = points[0][1]; + for (var i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + var angle = o.hachureAngle; + var gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + var radPerDeg = Math.PI / 180; + var hachureAngle = angle % 180 * radPerDeg; + var cosAngle = Math.cos(hachureAngle); + var sinAngle = Math.sin(hachureAngle); + var tanAngle = Math.tan(hachureAngle); + var it = new HachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); + var rect = void 0; + var prevPoint = null; + while ((rect = it.nextLine()) != null) { + var lines = this.getIntersectingLines(rect, points); + for (var _i = 0; _i < lines.length; _i++) { + if (_i < lines.length - 1) { + var p1 = lines[_i]; + var p2 = lines[_i + 1]; + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + } + } + } + return { type: 'fillSketch', ops: ops }; + } + }, { + key: '_fillEllipse', + value: function _fillEllipse(cx, cy, width, height, o) { + var connectEnds = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; + + var ops = []; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.renderer.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.renderer.getOffset(-ry * 0.05, ry * 0.05, o); + var angle = o.hachureAngle; + var gap = o.hachureGap; + if (gap <= 0) { + gap = o.strokeWidth * 4; + } + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + var radPerDeg = Math.PI / 180; + var hachureAngle = angle % 180 * radPerDeg; + var tanAngle = Math.tan(hachureAngle); + var aspectRatio = ry / rx; + var hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); + var sinAnglePrime = aspectRatio * tanAngle / hyp; + var cosAnglePrime = 1 / hyp; + var gapPrime = gap / (rx * ry / Math.sqrt(ry * cosAnglePrime * (ry * cosAnglePrime) + rx * sinAnglePrime * (rx * sinAnglePrime)) / rx); + var halfLen = Math.sqrt(rx * rx - (cx - rx + gapPrime) * (cx - rx + gapPrime)); + var prevPoint = null; + for (var xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { + halfLen = Math.sqrt(rx * rx - (cx - xPos) * (cx - xPos)); + var p1 = this.affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + var p2 = this.affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + return { type: 'fillSketch', ops: ops }; + } + }, { + key: 'getIntersectingLines', + value: function getIntersectingLines(line, points) { + var intersections = []; + var s1 = new Segment([line[0], line[1]], [line[2], line[3]]); + for (var i = 0; i < points.length; i++) { + var s2 = new Segment(points[i], points[(i + 1) % points.length]); + if (s1.intersects(s2)) { + intersections.push([s1.xi, s1.yi]); + } + } + return intersections; + } + }, { + key: 'affine', + value: function affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { + var A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; + var B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; + var C = cosAnglePrime; + var D = sinAnglePrime; + var E = -R * sinAnglePrime; + var F = R * cosAnglePrime; + return [A + C * x + D * y, B + E * x + F * y]; + } + }]); + return HachureFiller; + }(); + + var ZigZagFiller = function (_HachureFiller) { + inherits(ZigZagFiller, _HachureFiller); + + function ZigZagFiller() { + classCallCheck(this, ZigZagFiller); + return possibleConstructorReturn(this, (ZigZagFiller.__proto__ || Object.getPrototypeOf(ZigZagFiller)).apply(this, arguments)); + } + + createClass(ZigZagFiller, [{ + key: 'fillPolygon', + value: function fillPolygon(points, o) { + return this._fillPolygon(points, o, true); + } + }, { + key: 'fillEllipse', + value: function fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o, true); + } + }]); + return ZigZagFiller; + }(HachureFiller); + + var HatchFiller = function (_HachureFiller) { + inherits(HatchFiller, _HachureFiller); + + function HatchFiller() { + classCallCheck(this, HatchFiller); + return possibleConstructorReturn(this, (HatchFiller.__proto__ || Object.getPrototypeOf(HatchFiller)).apply(this, arguments)); + } + + createClass(HatchFiller, [{ + key: 'fillPolygon', + value: function fillPolygon(points, o) { + var set$$1 = this._fillPolygon(points, o); + var o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + var set2 = this._fillPolygon(points, o2); + set$$1.ops = set$$1.ops.concat(set2.ops); + return set$$1; + } + }, { + key: 'fillEllipse', + value: function fillEllipse(cx, cy, width, height, o) { + var set$$1 = this._fillEllipse(cx, cy, width, height, o); + var o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + var set2 = this._fillEllipse(cx, cy, width, height, o2); + set$$1.ops = set$$1.ops.concat(set2.ops); + return set$$1; + } + }]); + return HatchFiller; + }(HachureFiller); + + var fillers = {}; + function getFiller(renderer, o) { + var fillerName = o.fillStyle || 'hachure'; + if (!fillers[fillerName]) { + switch (fillerName) { + case 'zigzag': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagFiller(renderer); + } + break; + case 'cross-hatch': + if (!fillers[fillerName]) { + fillers[fillerName] = new HatchFiller(renderer); + } + break; + case 'hachure': + default: + fillerName = 'hachure'; + if (!fillers[fillerName]) { + fillers[fillerName] = new HachureFiller(renderer); + } + break; + } + } + return fillers[fillerName]; + } + + var RoughRenderer = function () { + function RoughRenderer() { + classCallCheck(this, RoughRenderer); + } + + createClass(RoughRenderer, [{ + key: 'line', + value: function line(x1, y1, x2, y2, o) { + var ops = this.doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops: ops }; + } + }, { + key: 'linearPath', + value: function linearPath(points, close, o) { + var len = (points || []).length; + if (len > 2) { + var ops = []; + for (var i = 0; i < len - 1; i++) { + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); + } + return { type: 'path', ops: ops }; + } else if (len === 2) { + return this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + }, { + key: 'polygon', + value: function polygon(points, o) { + return this.linearPath(points, true, o); + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, o) { + var points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + return this.polygon(points, o); + } + }, { + key: 'curve', + value: function curve(points, o) { + var o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + var o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, o) { + var increment = Math.PI * 2 / o.curveStepCount; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + var o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); + var o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop, closed, roughClosure, o) { + var cx = x; + var cy = y; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + var strt = start; + var 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; + } + var ellipseInc = Math.PI * 2 / o.curveStepCount; + var arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + var o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + var o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + var ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops: ops }; + } + }, { + key: 'svgPath', + value: function svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + var p = new RoughPath(path); + if (o.simplification) { + var fitter = new PathFitter(p.linearPoints, p.closed); + var d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + var ops = []; + var segments = p.segments || []; + for (var i = 0; i < segments.length; i++) { + var s = segments[i]; + var prev = i > 0 ? segments[i - 1] : null; + var opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops: ops }; + } + }, { + key: 'solidFillPolygon', + value: function solidFillPolygon(points, o) { + var ops = []; + if (PointerEvent.length) { + var offset = o.maxRandomnessOffset || 0; + var len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (var i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops: ops }; + } + }, { + key: 'patternFillPolygon', + value: function patternFillPolygon(points, o) { + var filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + }, { + key: 'patternFillEllipse', + value: function patternFillEllipse(cx, cy, width, height, o) { + var filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + }, { + key: 'patternFillArc', + value: function patternFillArc(x, y, width, height, start, stop, o) { + var cx = x; + var cy = y; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + var strt = start; + var 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; + } + var increment = (stp - strt) / o.curveStepCount; + var points = []; + for (var 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 this.patternFillPolygon(points, o); + } + /// + + }, { + key: 'getOffset', + value: function getOffset(min, max, ops) { + return ops.roughness * (Math.random() * (max - min) + min); + } + }, { + key: 'doubleLine', + value: function doubleLine(x1, y1, x2, y2, o) { + var o1 = this._line(x1, y1, x2, y2, o, true, false); + var o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + }, { + key: '_line', + value: function _line(x1, y1, x2, y2, o, move, overlay) { + var lengthSq = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2); + var offset = o.maxRandomnessOffset || 0; + if (offset * offset * 100 > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + var halfOffset = offset / 2; + var divergePoint = 0.2 + Math.random() * 0.2; + var midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + var midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); + var ops = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [x1 + this.getOffset(-halfOffset, halfOffset, o), y1 + this.getOffset(-halfOffset, halfOffset, o)] + }); + } else { + ops.push({ + op: 'move', data: [x1 + this.getOffset(-offset, offset, o), y1 + this.getOffset(-offset, offset, o)] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), x2 + this.getOffset(-halfOffset, halfOffset, o), y2 + this.getOffset(-halfOffset, halfOffset, o)] + }); + } else { + ops.push({ + op: 'bcurveTo', data: [midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), x2 + this.getOffset(-offset, offset, o), y2 + this.getOffset(-offset, offset, o)] + }); + } + return ops; + } + }, { + key: '_curve', + value: function _curve(points, closePoint, o) { + var len = points.length; + var ops = []; + if (len > 3) { + var b = []; + var s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (var i = 1; i + 2 < len; i++) { + var 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) { + var ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, 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 = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + }, { + key: '_ellipse', + value: function _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + var radOffset = this.getOffset(-0.5, 0.5, o) - Math.PI / 2; + var points = []; + points.push([this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)]); + for (var angle = radOffset; angle < Math.PI * 2 + radOffset - 0.01; angle = angle + increment) { + points.push([this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle)]); + } + points.push([this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5)]); + points.push([this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap)]); + points.push([this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5)]); + return this._curve(points, null, o); + } + }, { + key: '_curveWithOffset', + value: function _curveWithOffset(points, offset, o) { + var ps = []; + ps.push([points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)]); + ps.push([points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)]); + for (var i = 1; i < points.length; i++) { + ps.push([points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)]); + if (i === points.length - 1) { + ps.push([points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)]); + } + } + return this._curve(ps, null, o); + } + }, { + key: '_arc', + value: function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + var radOffset = strt + this.getOffset(-0.1, 0.1, o); + var points = []; + points.push([this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)]); + for (var angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), this.getOffset(-offset, 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 this._curve(points, null, o); + } + }, { + key: '_bezierTo', + value: function _bezierTo(x1, y1, x2, y2, x, y, path, o) { + var ops = []; + var ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + var f = [0, 0]; + for (var i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } else { + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), f[0], f[1]] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + }, { + key: '_processSegment', + value: function _processSegment(path, seg, prevSeg, o) { + var ops = []; + switch (seg.key) { + case 'M': + case 'm': + { + var delta = seg.key === 'm'; + if (seg.data.length >= 2) { + var x = +seg.data[0]; + var y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + var ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': + { + var _delta = seg.key === 'l'; + if (seg.data.length >= 2) { + var _x = +seg.data[0]; + var _y = +seg.data[1]; + if (_delta) { + _x += path.x; + _y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, _x, _y, o)); + path.setPosition(_x, _y); + } + break; + } + case 'H': + case 'h': + { + var _delta2 = seg.key === 'h'; + if (seg.data.length) { + var _x2 = +seg.data[0]; + if (_delta2) { + _x2 += path.x; + } + ops = ops.concat(this.doubleLine(path.x, path.y, _x2, path.y, o)); + path.setPosition(_x2, path.y); + } + break; + } + case 'V': + case 'v': + { + var _delta3 = seg.key === 'v'; + if (seg.data.length) { + var _y2 = +seg.data[0]; + if (_delta3) { + _y2 += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, _y2, o)); + path.setPosition(path.x, _y2); + } + break; + } + case 'Z': + case 'z': + { + if (path.first) { + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': + { + var _delta4 = seg.key === 'c'; + if (seg.data.length >= 6) { + var x1 = +seg.data[0]; + var y1 = +seg.data[1]; + var x2 = +seg.data[2]; + var y2 = +seg.data[3]; + var _x3 = +seg.data[4]; + var _y3 = +seg.data[5]; + if (_delta4) { + x1 += path.x; + x2 += path.x; + _x3 += path.x; + y1 += path.y; + y2 += path.y; + _y3 += path.y; + } + var ob = this._bezierTo(x1, y1, x2, y2, _x3, _y3, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [_x3 + (_x3 - x2), _y3 + (_y3 - y2)]; + } + break; + } + case 'S': + case 's': + { + var _delta5 = seg.key === 's'; + if (seg.data.length >= 4) { + var _x4 = +seg.data[0]; + var _y4 = +seg.data[1]; + var _x5 = +seg.data[2]; + var _y5 = +seg.data[3]; + if (_delta5) { + _x4 += path.x; + _x5 += path.x; + _y4 += path.y; + _y5 += path.y; + } + var _x6 = _x4; + var _y6 = _y4; + var prevKey = prevSeg ? prevSeg.key : ''; + var ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + _x6 = ref[0]; + _y6 = ref[1]; + } + var _ob = this._bezierTo(_x6, _y6, _x4, _y4, _x5, _y5, path, o); + ops = ops.concat(_ob); + path.bezierReflectionPoint = [_x5 + (_x5 - _x4), _y5 + (_y5 - _y4)]; + } + break; + } + case 'Q': + case 'q': + { + var _delta6 = seg.key === 'q'; + if (seg.data.length >= 4) { + var _x7 = +seg.data[0]; + var _y7 = +seg.data[1]; + var _x8 = +seg.data[2]; + var _y8 = +seg.data[3]; + if (_delta6) { + _x7 += path.x; + _x8 += path.x; + _y7 += path.y; + _y8 += path.y; + } + var offset1 = 1 * (1 + o.roughness * 0.2); + var offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + var f = [_x8 + this.getOffset(-offset1, offset1, o), _y8 + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [_x7 + this.getOffset(-offset1, offset1, o), _y7 + this.getOffset(-offset1, offset1, o), f[0], f[1]] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [_x8 + this.getOffset(-offset2, offset2, o), _y8 + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [_x7 + this.getOffset(-offset2, offset2, o), _y7 + this.getOffset(-offset2, offset2, o), f[0], f[1]] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [_x8 + (_x8 - _x7), _y8 + (_y8 - _y7)]; + } + break; + } + case 'T': + case 't': + { + var _delta7 = seg.key === 't'; + if (seg.data.length >= 2) { + var _x9 = +seg.data[0]; + var _y9 = +seg.data[1]; + if (_delta7) { + _x9 += path.x; + _y9 += path.y; + } + var _x10 = _x9; + var _y10 = _y9; + var _prevKey = prevSeg ? prevSeg.key : ''; + var _ref = null; + if (_prevKey === 'q' || _prevKey === 'Q' || _prevKey === 't' || _prevKey === 'T') { + _ref = path.quadReflectionPoint; + } + if (_ref) { + _x10 = _ref[0]; + _y10 = _ref[1]; + } + var _offset = 1 * (1 + o.roughness * 0.2); + var _offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-_offset, _offset, o), path.y + this.getOffset(-_offset, _offset, o)] }); + var _f = [_x9 + this.getOffset(-_offset, _offset, o), _y9 + this.getOffset(-_offset, _offset, o)]; + ops.push({ + op: 'qcurveTo', data: [_x10 + this.getOffset(-_offset, _offset, o), _y10 + this.getOffset(-_offset, _offset, o), _f[0], _f[1]] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-_offset2, _offset2, o), path.y + this.getOffset(-_offset2, _offset2, o)] }); + _f = [_x9 + this.getOffset(-_offset2, _offset2, o), _y9 + this.getOffset(-_offset2, _offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [_x10 + this.getOffset(-_offset2, _offset2, o), _y10 + this.getOffset(-_offset2, _offset2, o), _f[0], _f[1]] + }); + path.setPosition(_f[0], _f[1]); + path.quadReflectionPoint = [_x9 + (_x9 - _x10), _y9 + (_y9 - _y10)]; + } + break; + } + case 'A': + case 'a': + { + var _delta8 = seg.key === 'a'; + if (seg.data.length >= 7) { + var rx = +seg.data[0]; + var ry = +seg.data[1]; + var angle = +seg.data[2]; + var largeArcFlag = +seg.data[3]; + var sweepFlag = +seg.data[4]; + var _x11 = +seg.data[5]; + var _y11 = +seg.data[6]; + if (_delta8) { + _x11 += path.x; + _y11 += path.y; + } + if (_x11 === path.x && _y11 === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this.doubleLine(path.x, path.y, _x11, _y11, o)); + path.setPosition(_x11, _y11); + } else { + for (var i = 0; i < 1; i++) { + var arcConverter = new RoughArcConverter([path.x, path.y], [_x11, _y11], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + var segment = arcConverter.getNextSegment(); + while (segment) { + var _ob2 = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(_ob2); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } + }]); + return RoughRenderer; + }(); + + var hasSelf = typeof self !== 'undefined'; + var roughScript = hasSelf && self && self.document && self.document.currentScript && self.document.currentScript.src; + function createRenderer(config) { + if (hasSelf && roughScript && self && self.workly && config.async && !config.noWorker) { + var worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; + if (worklySource) { + var code = 'importScripts(\'' + worklySource + '\', \'' + roughScript + '\');\nworkly.expose(self.rough.createRenderer());'; + var ourl = URL.createObjectURL(new Blob([code])); + return self.workly.proxy(ourl); + } + } + return new RoughRenderer(); + } + + var hasSelf$1 = typeof self !== 'undefined'; + var RoughGenerator = function () { + function RoughGenerator(config, surface) { + classCallCheck(this, RoughGenerator); + + this.defaultOptions = { + maxRandomnessOffset: 2, + roughness: 1, + bowing: 1, + stroke: '#000', + strokeWidth: 1, + curveTightness: 0, + curveStepCount: 9, + fill: null, + fillStyle: 'hachure', + fillWeight: -1, + hachureAngle: -41, + hachureGap: -1 + }; + this.config = config || {}; + this.surface = surface; + this.renderer = createRenderer(this.config); + if (this.config.options) { + this.defaultOptions = this._options(this.config.options); + } + } + + createClass(RoughGenerator, [{ + key: '_options', + value: function _options(options) { + return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; + } + }, { + key: '_drawable', + value: function _drawable(shape, sets, options) { + return { shape: shape, sets: sets || [], options: options || this.defaultOptions }; + } + }, { + key: 'getCanvasSize', + value: function getCanvasSize() { + var val = function val(w) { + if (w && (typeof w === 'undefined' ? 'undefined' : _typeof(w)) === 'object') { + if (w.baseVal && w.baseVal.value) { + return w.baseVal.value; + } + } + return w || 100; + }; + if (this.surface) { + return [val(this.surface.width), val(this.surface.height)]; + } + return [100, 100]; + } + }, { + key: 'computePolygonSize', + value: function computePolygonSize(points) { + if (points.length) { + var left = points[0][0]; + var right = points[0][0]; + var top = points[0][1]; + var bottom = points[0][1]; + for (var i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + return [right - left, bottom - top]; + } + return [0, 0]; + } + }, { + key: 'polygonPath', + value: function polygonPath(points) { + var d = ''; + if (points.length) { + d = 'M' + points[0][0] + ',' + points[0][1]; + for (var i = 1; i < points.length; i++) { + d = d + ' L' + points[i][0] + ',' + points[i][1]; + } + } + return d; + } + }, { + key: 'computePathSize', + value: function computePathSize(d) { + var size = [0, 0]; + if (hasSelf$1 && self.document) { + try { + var ns = 'http://www.w3.org/2000/svg'; + var svg = self.document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + var pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + self.document.body.appendChild(svg); + var bb = pathNode.getBBox(); + if (bb) { + size[0] = bb.width || 0; + size[1] = bb.height || 0; + } + self.document.body.removeChild(svg); + } catch (err) {} + } + var canvasSize = this.getCanvasSize(); + if (!(size[0] * size[1])) { + size = canvasSize; + } + size[0] = Math.min(size[0], canvasSize[0]); + size[1] = Math.min(size[1], canvasSize[1]); + return size; + } + }, { + key: 'line', + value: function line(x1, y1, x2, y2, options) { + var o = this._options(options); + return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + var points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(this.lib.ellipse(x, y, width, height, o)); + return this._drawable('ellipse', paths, o); + } + }, { + key: 'circle', + value: function circle(x, y, diameter, options) { + var ret = this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + }, { + key: 'linearPath', + value: function linearPath(points, options) { + var o = this._options(options); + return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var o = this._options(options); + var paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + var shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + }, { + key: 'curve', + value: function curve(points, options) { + var o = this._options(options); + return this._drawable('curve', [this.lib.curve(points, o)], o); + } + }, { + key: 'polygon', + value: function polygon(points, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } else { + var size = this.computePolygonSize(points); + var fillPoints = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var shape = this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + }, { + key: 'path', + value: function path(d, options) { + var o = this._options(options); + var paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } else { + var size = this.computePathSize(d); + var points = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var _shape = this.lib.patternFillPolygon(points, o); + _shape.type = 'path2Dpattern'; + _shape.size = size; + _shape.path = d; + paths.push(_shape); + } + } + paths.push(this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } + }, { + key: 'toPaths', + value: function toPaths(drawable) { + var sets = drawable.sets || []; + var o = drawable.options || this.defaultOptions; + var paths = []; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = sets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var drawing = _step.value; + + var path = null; + switch (drawing.type) { + case 'path': + path = { + d: this.opsToPath(drawing), + stroke: o.stroke, + strokeWidth: o.strokeWidth, + fill: 'none' + }; + break; + case 'fillPath': + path = { + d: this.opsToPath(drawing), + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'fillSketch': + path = this.fillSketch(drawing, o); + break; + case 'path2Dfill': + path = { + d: drawing.path || '', + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'path2Dpattern': + { + var size = drawing.size; + var pattern = { + x: 0, y: 0, width: 1, height: 1, + viewBox: '0 0 ' + Math.round(size[0]) + ' ' + Math.round(size[1]), + patternUnits: 'objectBoundingBox', + path: this.fillSketch(drawing, o) + }; + path = { + d: drawing.path, + stroke: 'none', + strokeWidth: 0, + pattern: pattern + }; + break; + } + } + if (path) { + paths.push(path); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return paths; + } + }, { + key: 'fillSketch', + value: function fillSketch(drawing, o) { + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + return { + d: this.opsToPath(drawing), + stroke: o.fill || 'none', + strokeWidth: fweight, + fill: 'none' + }; + } + }, { + key: 'opsToPath', + value: function opsToPath(drawing) { + var path = ''; + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = drawing.ops[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var item = _step2.value; + + var 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 'qcurveTo': + path += 'Q' + data[0] + ' ' + data[1] + ', ' + data[2] + ' ' + data[3] + ' '; + break; + case 'lineTo': + path += 'L' + data[0] + ' ' + data[1] + ' '; + break; + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + return path.trim(); + } + }, { + key: 'lib', + get: function get$$1() { + return this.renderer; + } + }]); + return RoughGenerator; + }(); + + var hasDocument = typeof document !== 'undefined'; + var RoughCanvas = function () { + function RoughCanvas(canvas, config) { + classCallCheck(this, RoughCanvas); + + this.canvas = canvas; + this.ctx = this.canvas.getContext('2d'); + this.gen = new RoughGenerator(config || null, this.canvas); + } + + createClass(RoughCanvas, [{ + key: 'line', + value: function line(x1, y1, x2, y2, options) { + var d = this.gen.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, options) { + var d = this.gen.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, options) { + var d = this.gen.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + }, { + key: 'circle', + value: function circle(x, y, diameter, options) { + var d = this.gen.circle(x, y, diameter, options); + this.draw(d); + return d; + } + }, { + key: 'linearPath', + value: function linearPath(points, options) { + var d = this.gen.linearPath(points, options); + this.draw(d); + return d; + } + }, { + key: 'polygon', + value: function polygon(points, options) { + var d = this.gen.polygon(points, options); + this.draw(d); + return d; + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = this.gen.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + }, { + key: 'curve', + value: function curve(points, options) { + var d = this.gen.curve(points, options); + this.draw(d); + return d; + } + }, { + key: 'path', + value: function path(d, options) { + var drawing = this.gen.path(d, options); + this.draw(drawing); + return drawing; + } + }, { + key: 'draw', + value: function draw(drawable) { + var sets = drawable.sets || []; + var o = drawable.options || this.gen.defaultOptions; + var ctx = this.ctx; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = sets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var drawing = _step.value; + + switch (drawing.type) { + case 'path': + ctx.save(); + ctx.strokeStyle = o.stroke; + ctx.lineWidth = o.strokeWidth; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillPath': + ctx.save(); + ctx.fillStyle = o.fill || ''; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillSketch': + this.fillSketch(ctx, drawing, o); + break; + case 'path2Dfill': + { + this.ctx.save(); + this.ctx.fillStyle = o.fill || ''; + var p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + break; + } + case 'path2Dpattern': + { + if (hasDocument) { + var size = drawing.size; + var hcanvas = document.createElement('canvas'); + var hcontext = hcanvas.getContext('2d'); + var bbox = this.computeBBox(drawing.path); + if (bbox && (bbox.width || bbox.height)) { + hcanvas.width = this.canvas.width; + hcanvas.height = this.canvas.height; + hcontext.translate(bbox.x || 0, bbox.y || 0); + } else { + hcanvas.width = size[0]; + hcanvas.height = size[1]; + } + this.fillSketch(hcontext, drawing, o); + this.ctx.save(); + this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); + var _p2d = new Path2D(drawing.path); + this.ctx.fill(_p2d); + this.ctx.restore(); + } + break; + } + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + } + }, { + key: 'computeBBox', + value: function computeBBox(d) { + if (hasDocument) { + try { + var ns = 'http://www.w3.org/2000/svg'; + var svg = document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + var pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + document.body.appendChild(svg); + var bbox = pathNode.getBBox(); + document.body.removeChild(svg); + return bbox; + } catch (err) {} + } + return null; + } + }, { + key: 'fillSketch', + value: function fillSketch(ctx, drawing, o) { + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + ctx.save(); + ctx.strokeStyle = o.fill || ''; + ctx.lineWidth = fweight; + this._drawToContext(ctx, drawing); + ctx.restore(); + } + }, { + key: '_drawToContext', + value: function _drawToContext(ctx, drawing) { + ctx.beginPath(); + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = drawing.ops[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var item = _step2.value; + + var 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 'qcurveTo': + ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); + break; + case 'lineTo': + ctx.lineTo(data[0], data[1]); + break; + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + if (drawing.type === 'fillPath') { + ctx.fill(); + } else { + ctx.stroke(); + } + } + }, { + key: 'generator', + get: function get$$1() { + return this.gen; + } + }], [{ + key: 'createRenderer', + value: function createRenderer() { + return new RoughRenderer(); + } + }]); + return RoughCanvas; + }(); + + var RoughGeneratorAsync = function (_RoughGenerator) { + inherits(RoughGeneratorAsync, _RoughGenerator); + + function RoughGeneratorAsync() { + classCallCheck(this, RoughGeneratorAsync); + return possibleConstructorReturn(this, (RoughGeneratorAsync.__proto__ || Object.getPrototypeOf(RoughGeneratorAsync)).apply(this, arguments)); + } + + createClass(RoughGeneratorAsync, [{ + key: 'line', + + // @ts-ignore + value: async function line(x1, y1, x2, y2, options) { + var o = this._options(options); + return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); + } + // @ts-ignore + + }, { + key: 'rectangle', + value: async function rectangle(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + var points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push((await this.lib.solidFillPolygon(points, o))); + } else { + paths.push((await this.lib.patternFillPolygon(points, o))); + } + } + paths.push((await this.lib.rectangle(x, y, width, height, o))); + return this._drawable('rectangle', paths, o); + } + // @ts-ignore + + }, { + key: 'ellipse', + value: async function ellipse(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = await this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push((await this.lib.patternFillEllipse(x, y, width, height, o))); + } + } + paths.push((await this.lib.ellipse(x, y, width, height, o))); + return this._drawable('ellipse', paths, o); + } + // @ts-ignore + + }, { + key: 'circle', + value: async function circle(x, y, diameter, options) { + var ret = await this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + // @ts-ignore + + }, { + key: 'linearPath', + value: async function linearPath(points, options) { + var o = this._options(options); + return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); + } + // @ts-ignore + + }, { + key: 'arc', + value: async function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var o = this._options(options); + var paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + var shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push((await this.lib.patternFillArc(x, y, width, height, start, stop, o))); + } + } + paths.push((await this.lib.arc(x, y, width, height, start, stop, closed, true, o))); + return this._drawable('arc', paths, o); + } + // @ts-ignore + + }, { + key: 'curve', + value: async function curve(points, options) { + var o = this._options(options); + return this._drawable('curve', [await this.lib.curve(points, o)], o); + } + // @ts-ignore + + }, { + key: 'polygon', + value: async function polygon(points, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push((await this.lib.solidFillPolygon(points, o))); + } else { + var size = this.computePolygonSize(points); + var fillPoints = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var shape = await this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push((await this.lib.linearPath(points, true, o))); + return this._drawable('polygon', paths, o); + } + // @ts-ignore + + }, { + key: 'path', + value: async function path(d, options) { + var o = this._options(options); + var paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } else { + var size = this.computePathSize(d); + var points = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var _shape = await this.lib.patternFillPolygon(points, o); + _shape.type = 'path2Dpattern'; + _shape.size = size; + _shape.path = d; + paths.push(_shape); + } + } + paths.push((await this.lib.svgPath(d, o))); + return this._drawable('path', paths, o); + } + }]); + return RoughGeneratorAsync; + }(RoughGenerator); + + var RoughCanvasAsync = function (_RoughCanvas) { + inherits(RoughCanvasAsync, _RoughCanvas); + + function RoughCanvasAsync(canvas, config) { + classCallCheck(this, RoughCanvasAsync); + + var _this = possibleConstructorReturn(this, (RoughCanvasAsync.__proto__ || Object.getPrototypeOf(RoughCanvasAsync)).call(this, canvas, config)); + + _this.genAsync = new RoughGeneratorAsync(config || null, _this.canvas); + return _this; + } + // @ts-ignore + + + createClass(RoughCanvasAsync, [{ + key: 'line', + + // @ts-ignore + value: async function line(x1, y1, x2, y2, options) { + var d = await this.genAsync.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'rectangle', + value: async function rectangle(x, y, width, height, options) { + var d = await this.genAsync.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'ellipse', + value: async function ellipse(x, y, width, height, options) { + var d = await this.genAsync.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'circle', + value: async function circle(x, y, diameter, options) { + var d = await this.genAsync.circle(x, y, diameter, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'linearPath', + value: async function linearPath(points, options) { + var d = await this.genAsync.linearPath(points, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'polygon', + value: async function polygon(points, options) { + var d = await this.genAsync.polygon(points, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'arc', + value: async function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'curve', + value: async function curve(points, options) { + var d = await this.genAsync.curve(points, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'path', + value: async function path(d, options) { + var drawing = await this.genAsync.path(d, options); + this.draw(drawing); + return drawing; + } + }, { + key: 'generator', + get: function get$$1() { + return this.genAsync; + } + }]); + return RoughCanvasAsync; + }(RoughCanvas); + + var hasDocument$1 = typeof document !== 'undefined'; + var RoughSVG = function () { + function RoughSVG(svg, config) { + classCallCheck(this, RoughSVG); + + this.svg = svg; + this.gen = new RoughGenerator(config || null, this.svg); + } + + createClass(RoughSVG, [{ + key: 'line', + value: function line(x1, y1, x2, y2, options) { + var d = this.gen.line(x1, y1, x2, y2, options); + return this.draw(d); + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, options) { + var d = this.gen.rectangle(x, y, width, height, options); + return this.draw(d); + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, options) { + var d = this.gen.ellipse(x, y, width, height, options); + return this.draw(d); + } + }, { + key: 'circle', + value: function circle(x, y, diameter, options) { + var d = this.gen.circle(x, y, diameter, options); + return this.draw(d); + } + }, { + key: 'linearPath', + value: function linearPath(points, options) { + var d = this.gen.linearPath(points, options); + return this.draw(d); + } + }, { + key: 'polygon', + value: function polygon(points, options) { + var d = this.gen.polygon(points, options); + return this.draw(d); + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = this.gen.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + }, { + key: 'curve', + value: function curve(points, options) { + var d = this.gen.curve(points, options); + return this.draw(d); + } + }, { + key: 'path', + value: function path(d, options) { + var drawing = this.gen.path(d, options); + return this.draw(drawing); + } + }, { + key: 'draw', + value: function draw(drawable) { + var sets = drawable.sets || []; + var o = drawable.options || this.gen.defaultOptions; + var doc = this.svg.ownerDocument || hasDocument$1 && document; + var g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = sets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var drawing = _step.value; + + var path = null; + switch (drawing.type) { + case 'path': + { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.stroke; + path.style.strokeWidth = o.strokeWidth + ''; + path.style.fill = 'none'; + break; + } + case 'fillPath': + { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'fillSketch': + { + path = this.fillSketch(doc, drawing, o); + break; + } + case 'path2Dfill': + { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'path2Dpattern': + { + var size = drawing.size; + var pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); + var id = 'rough-' + Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999)); + pattern.setAttribute('id', id); + pattern.setAttribute('x', '0'); + pattern.setAttribute('y', '0'); + pattern.setAttribute('width', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('viewBox', '0 0 ' + Math.round(size[0]) + ' ' + Math.round(size[1])); + pattern.setAttribute('patternUnits', 'objectBoundingBox'); + var patternPath = this.fillSketch(doc, drawing, o); + pattern.appendChild(patternPath); + this.defs.appendChild(pattern); + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = 'url(#' + id + ')'; + break; + } + } + if (path) { + g.appendChild(path); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return g; + } + }, { + key: 'opsToPath', + value: function opsToPath(drawing) { + return this.gen.opsToPath(drawing); + } + }, { + key: 'fillSketch', + value: function fillSketch(doc, drawing, o) { + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + var path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.fill; + path.style.strokeWidth = fweight + ''; + path.style.fill = 'none'; + return path; + } + }, { + key: 'generator', + get: function get$$1() { + return this.gen; + } + }, { + key: 'defs', + get: function get$$1() { + if (hasDocument$1) { + if (!this._defs) { + var doc = this.svg.ownerDocument || document; + var dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); + if (this.svg.firstChild) { + this.svg.insertBefore(dnode, this.svg.firstChild); + } else { + this.svg.appendChild(dnode); + } + this._defs = dnode; + } + } + return this._defs || null; + } + }], [{ + key: 'createRenderer', + value: function createRenderer() { + return new RoughRenderer(); + } + }]); + return RoughSVG; + }(); + + var RoughSVGAsync = function (_RoughSVG) { + inherits(RoughSVGAsync, _RoughSVG); + + function RoughSVGAsync(svg, config) { + classCallCheck(this, RoughSVGAsync); + + var _this = possibleConstructorReturn(this, (RoughSVGAsync.__proto__ || Object.getPrototypeOf(RoughSVGAsync)).call(this, svg, config)); + + _this.genAsync = new RoughGeneratorAsync(config || null, _this.svg); + return _this; + } + // @ts-ignore + + + createClass(RoughSVGAsync, [{ + key: 'line', + + // @ts-ignore + value: async function line(x1, y1, x2, y2, options) { + var d = await this.genAsync.line(x1, y1, x2, y2, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'rectangle', + value: async function rectangle(x, y, width, height, options) { + var d = await this.genAsync.rectangle(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'ellipse', + value: async function ellipse(x, y, width, height, options) { + var d = await this.genAsync.ellipse(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'circle', + value: async function circle(x, y, diameter, options) { + var d = await this.genAsync.circle(x, y, diameter, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'linearPath', + value: async function linearPath(points, options) { + var d = await this.genAsync.linearPath(points, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'polygon', + value: async function polygon(points, options) { + var d = await this.genAsync.polygon(points, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'arc', + value: async function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'curve', + value: async function curve(points, options) { + var d = await this.genAsync.curve(points, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'path', + value: async function path(d, options) { + var drawing = await this.genAsync.path(d, options); + return this.draw(drawing); + } + }, { + key: 'generator', + get: function get$$1() { + return this.genAsync; + } + }]); + return RoughSVGAsync; + }(RoughSVG); + + var rough = { + canvas: function canvas(_canvas, config) { + if (config && config.async) { + return new RoughCanvasAsync(_canvas, config); + } + return new RoughCanvas(_canvas, config); + }, + svg: function svg(_svg, config) { + if (config && config.async) { + return new RoughSVGAsync(_svg, config); + } + return new RoughSVG(_svg, config); + }, + createRenderer: function createRenderer() { + return RoughCanvas.createRenderer(); + }, + generator: function generator(config, surface) { + if (config && config.async) { + return new RoughGeneratorAsync(config, surface); + } + return new RoughGenerator(config, surface); + } + }; + + return rough; + +}()); diff --git a/dist/rough.es5.min.js b/dist/rough.es5.min.js new file mode 100644 index 0000000..003de9a --- /dev/null +++ b/dist/rough.es5.min.js @@ -0,0 +1 @@ +var rough=function(){"use strict";function e(e,t){return e.type===t}function t(e,t){var l=t.fillStyle||"hachure";if(!T[l])switch(l){case"zigzag":T[l]||(T[l]=new S(e));break;case"cross-hatch":T[l]||(T[l]=new A(e));break;case"hachure":default:l="hachure",T[l]||(T[l]=new O(e));}return T[l]}function l(e){if(z&&E&&self&&self.workly&&e.async&&!e.noWorker){var t=e.worklyURL||"https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js";if(t){var l=URL.createObjectURL(new Blob(["importScripts('"+t+"', '"+E+"');\nworkly.expose(self.rough.createRenderer());"]));return self.workly.proxy(l)}}return new C}var a=Math.round,n=Math.max,s=Math.min,i=Math.pow,r=Math.floor,p=Math.sqrt,d=Math.cos,h=Math.sin,g=Math.abs,f=Math.PI,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},y=function(){function e(e,t){for(var l,a=0;a_){var k=p(1-_/(this._rx*this._rx*this._ry*this._ry));this._rx*=k,this._ry*=k,v=0}else v=(i===o?-1:1)*p(_/(this._rx*this._rx*c*c+this._ry*this._ry*y*y));var s=v*this._rx*c/this._ry,x=-v*this._ry*y/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*s-this._sinPhi*x+(t[0]+l[0])/2,this._C[1]=this._sinPhi*s+this._cosPhi*x+(t[1]+l[1])/2,this._theta=this.calculateVectorAngle(1,0,(y-s)/this._rx,(c-x)/this._ry);var b=this.calculateVectorAngle((y-s)/this._rx,(c-x)/this._ry,(-y-s)/this._rx,(-c-x)/this._ry);!o&&0b&&(b+=2*f),this._numSegs=Math.ceil(g(b/(f/2))),this._delta=b/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}}return y(e,[{key:"getNextSegment",value:function(){if(this._segIndex===this._numSegs)return null;var e=d(this._theta),t=h(this._theta),l=this._theta+this._delta,a=d(l),n=h(l),i=[this._cosPhi*this._rx*a-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*a+this._cosPhi*this._ry*n+this._C[1]],s=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[i[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*a),i[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*a)];return this._theta=l,this._from=[i[0],i[1]],this._segIndex++,{cp1:s,cp2:o,to:i}}},{key:"calculateVectorAngle",value:function(e,t,l,a){var n=Math.atan2,i=n(t,e),s=n(a,l);return s>=i?s-i:2*f-(i-s)}}]),e}(),m=function(){function e(t,l){u(this,e),this.sets=t,this.closed=l}return y(e,[{key:"fit",value:function(e){var t=[],l=!0,a=!1,n=void 0;try{for(var s,o=this.sets[Symbol.iterator]();!(l=(s=o.next()).done);l=!0){var p=s.value,h=p.length,g=r(e*h);if(5>g){if(5>=h)continue;g=5}t.push(this.reduce(p,g))}}catch(e){a=!0,n=e}finally{try{!l&&o.return&&o.return()}finally{if(a)throw n}}var f="",u=!0,y=!1,c=void 0;try{for(var v,_,k=t[Symbol.iterator]();!(u=(v=k.next()).done);u=!0){_=v.value;for(var x,b=0;b<_.length;b++)x=_[b],f+=0===b?"M"+x[0]+","+x[1]:"L"+x[0]+","+x[1];this.closed&&(f+="z ")}}catch(e){y=!0,c=e}finally{try{!u&&k.return&&k.return()}finally{if(y)throw c}}return f}},{key:"distance",value:function(e,t){return p(i(e[0]-t[0],2)+i(e[1]-t[1],2))}},{key:"reduce",value:function(e,t){if(e.length<=t)return e;for(var l=e.slice(0);l.length>t;){for(var n=-1,o=-1,r=1;rn||s=s(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=s(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+r,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===t?(this.xi=e.px1,this.yi=l*this.xi+o,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(p))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):l===i?o==r&&(this.px1>=s(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=s(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(r-o)/(l-i),this.yi=l*this.xi+o,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}]),e}(),P=function(){function e(t,l,a,n,i,s,o,r){u(this,e),this.deltaX=0,this.hGap=0,this.top=t,this.bottom=l,this.left=a,this.right=n,this.gap=i,this.sinAngle=s,this.tanAngle=r,1e-4>g(s)?this.pos=a+i:.9999g(this.sinAngle)){if(this.posthis.right&&l>this.right;)if(this.pos+=this.hGap,t=this.pos-this.deltaX/2,l=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;var i=new w([t,a],[l,n]);this.sLeft&&i.intersects(this.sLeft)&&(t=i.xi,a=i.yi),this.sRight&&i.intersects(this.sRight)&&(l=i.xi,n=i.yi),0v&&(v=4*l.strokeWidth),v=n(v,.1);for(var _=c%180*(f/180),k=d(_),x=h(_),b=t(_),m=new P(g-1,u+1,r-1,p+1,v,x,k,b),w=void 0,O=null;null!=(w=m.nextLine());)for(var S=this.getIntersectingLines(w,e),A=0;A=u&&(u=4*i.strokeWidth);var y=i.fillWeight;0>y&&(y=i.strokeWidth/2);for(var c=t(h%180*(f/180)),v=d/r,_=p(v*c*v*c+1),k=v*c/_,x=1/_,b=u/(r*d/p(d*x*(d*x)+r*k*(r*k))/r),m=p(r*r-(e-r+b)*(e-r+b)),w=null,P=e-r+b;P_;)_+=2*f,k+=2*f;k-_>2*f&&(_=0,k=2*f);var x=2*f/u.curveStepCount,b=s(x/2,(k-_)/2),m=this._arc(b,o,y,c,v,_,k,1,u),w=this._arc(b,o,y,c,v,_,k,1.5,u),P=m.concat(w);return r&&(p?(P=P.concat(this.doubleLine(o,y,o+c*d(_),y+v*h(_),u)),P=P.concat(this.doubleLine(o,y,o+c*d(k),y+v*h(k),u))):(P.push({op:"lineTo",data:[o,y]}),P.push({op:"lineTo",data:[o+c*d(_),y+v*h(_)]}))),{type:"path",ops:P}}},{key:"svgPath",value:function(e,t){e=(e||"").replace(/\n/g," ").replace(/(-\s)/g,"-").replace("/(ss)/g"," ");var l=new x(e);if(t.simplification){var a=new m(l.linearPoints,l.closed),n=a.fit(t.simplification);l=new x(n)}for(var o=[],r=l.segments||[],d=0;dy;)y+=2*f,c+=2*f;c-y>2*f&&(y=0,c=2*f);for(var v=(c-y)/s.curveStepCount,_=[],k=y;k<=c;k+=v)_.push([o+p*d(k),r+u*h(k)]);return _.push([o+p*d(c),r+u*h(c)]),_.push([o,r]),this.patternFillPolygon(_,s)}},{key:"getOffset",value:function(e,t,l){return l.roughness*(Math.random()*(t-e)+e)}},{key:"doubleLine",value:function(e,t,l,a,n){var i=this._line(e,t,l,a,n,!0,!1),s=this._line(e,t,l,a,n,!0,!0);return i.concat(s)}},{key:"_line",value:function(e,t,l,a,n,s,o){var r=i(e-l,2)+i(t-a,2),d=n.maxRandomnessOffset||0;100*(d*d)>r&&(d=p(r)/10);var h=d/2,g=.2+.2*Math.random(),f=n.bowing*n.maxRandomnessOffset*(a-t)/200,u=n.bowing*n.maxRandomnessOffset*(e-l)/200;f=this.getOffset(-f,f,n),u=this.getOffset(-u,u,n);var y=[];return s&&(o?y.push({op:"move",data:[e+this.getOffset(-h,h,n),t+this.getOffset(-h,h,n)]}):y.push({op:"move",data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?y.push({op:"bcurveTo",data:[f+e+(l-e)*g+this.getOffset(-h,h,n),u+t+(a-t)*g+this.getOffset(-h,h,n),f+e+2*(l-e)*g+this.getOffset(-h,h,n),u+t+2*(a-t)*g+this.getOffset(-h,h,n),l+this.getOffset(-h,h,n),a+this.getOffset(-h,h,n)]}):y.push({op:"bcurveTo",data:[f+e+(l-e)*g+this.getOffset(-d,d,n),u+t+(a-t)*g+this.getOffset(-d,d,n),f+e+2*(l-e)*g+this.getOffset(-d,d,n),u+t+2*(a-t)*g+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),a+this.getOffset(-d,d,n)]}),y}},{key:"_curve",value:function(e,t,l){var a=e.length,n=[];if(3g;g++)0===g?o.push({op:"move",data:[r.x,r.y]}):o.push({op:"move",data:[r.x+this.getOffset(-d[0],d[0],p),r.y+this.getOffset(-d[0],d[0],p)]}),h=[n+this.getOffset(-d[g],d[g],p),s+this.getOffset(-d[g],d[g],p)],o.push({op:"bcurveTo",data:[e+this.getOffset(-d[g],d[g],p),t+this.getOffset(-d[g],d[g],p),l+this.getOffset(-d[g],d[g],p),a+this.getOffset(-d[g],d[g],p),h[0],h[1]]});return r.setPosition(h[0],h[1]),o}},{key:"_processSegment",value:function(e,t,l,a){var n=[];switch(t.key){case"M":case"m":{var s="m"===t.key;if(2<=t.data.length){var o=+t.data[0],r=+t.data[1];s&&(o+=e.x,r+=e.y);var p=1*(a.maxRandomnessOffset||0);o+=this.getOffset(-p,p,a),r+=this.getOffset(-p,p,a),e.setPosition(o,r),n.push({op:"move",data:[o,r]})}break}case"L":case"l":{var d="l"===t.key;if(2<=t.data.length){var h=+t.data[0],g=+t.data[1];d&&(h+=e.x,g+=e.y),n=n.concat(this.doubleLine(e.x,e.y,h,g,a)),e.setPosition(h,g)}break}case"H":case"h":{var u="h"===t.key;if(t.data.length){var c=+t.data[0];u&&(c+=e.x),n=n.concat(this.doubleLine(e.x,e.y,c,e.y,a)),e.setPosition(c,e.y)}break}case"V":case"v":{var v="v"===t.key;if(t.data.length){var _=+t.data[0];v&&(_+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,_,a)),e.setPosition(e.x,_)}break}case"Z":case"z":{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],a)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case"C":case"c":{var k="c"===t.key;if(6<=t.data.length){var m=+t.data[0],w=+t.data[1],P=+t.data[2],O=+t.data[3],S=+t.data[4],A=+t.data[5];k&&(m+=e.x,P+=e.x,S+=e.x,w+=e.y,O+=e.y,A+=e.y);var T=this._bezierTo(m,w,P,O,S,A,e,a);n=n.concat(T),e.bezierReflectionPoint=[S+(S-P),A+(A-O)]}break}case"S":case"s":{var C="s"===t.key;if(4<=t.data.length){var z=+t.data[0],E=+t.data[1],L=+t.data[2],R=+t.data[3];C&&(z+=e.x,L+=e.x,E+=e.y,R+=e.y);var W=z,N=E,D=l?l.key:"",B=null;("c"===D||"C"===D||"s"===D||"S"===D)&&(B=e.bezierReflectionPoint),B&&(W=B[0],N=B[1]);var F=this._bezierTo(W,N,z,E,L,R,e,a);n=n.concat(F),e.bezierReflectionPoint=[L+(L-z),R+(R-E)]}break}case"Q":case"q":{var M="q"===t.key;if(4<=t.data.length){var q=+t.data[0],U=+t.data[1],X=+t.data[2],V=+t.data[3];M&&(q+=e.x,X+=e.x,U+=e.y,V+=e.y);var G=1*(1+.2*a.roughness),I=1.5*(1+.22*a.roughness);n.push({op:"move",data:[e.x+this.getOffset(-G,G,a),e.y+this.getOffset(-G,G,a)]});var j=[X+this.getOffset(-G,G,a),V+this.getOffset(-G,G,a)];n.push({op:"qcurveTo",data:[q+this.getOffset(-G,G,a),U+this.getOffset(-G,G,a),j[0],j[1]]}),n.push({op:"move",data:[e.x+this.getOffset(-I,I,a),e.y+this.getOffset(-I,I,a)]}),j=[X+this.getOffset(-I,I,a),V+this.getOffset(-I,I,a)],n.push({op:"qcurveTo",data:[q+this.getOffset(-I,I,a),U+this.getOffset(-I,I,a),j[0],j[1]]}),e.setPosition(j[0],j[1]),e.quadReflectionPoint=[X+(X-q),V+(V-U)]}break}case"T":case"t":{var Q="t"===t.key;if(2<=t.data.length){var $=+t.data[0],Z=+t.data[1];Q&&($+=e.x,Z+=e.y);var H=$,J=Z,Y=l?l.key:"",K=null;("q"===Y||"Q"===Y||"t"===Y||"T"===Y)&&(K=e.quadReflectionPoint),K&&(H=K[0],J=K[1]);var ee=1*(1+.2*a.roughness),te=1.5*(1+.22*a.roughness);n.push({op:"move",data:[e.x+this.getOffset(-ee,ee,a),e.y+this.getOffset(-ee,ee,a)]});var le=[$+this.getOffset(-ee,ee,a),Z+this.getOffset(-ee,ee,a)];n.push({op:"qcurveTo",data:[H+this.getOffset(-ee,ee,a),J+this.getOffset(-ee,ee,a),le[0],le[1]]}),n.push({op:"move",data:[e.x+this.getOffset(-te,te,a),e.y+this.getOffset(-te,te,a)]}),le=[$+this.getOffset(-te,te,a),Z+this.getOffset(-te,te,a)],n.push({op:"qcurveTo",data:[H+this.getOffset(-te,te,a),J+this.getOffset(-te,te,a),le[0],le[1]]}),e.setPosition(le[0],le[1]),e.quadReflectionPoint=[$+($-H),Z+(Z-J)]}break}case"A":case"a":{var ae="a"===t.key;if(7<=t.data.length){var ne=+t.data[0],ie=+t.data[1],se=+t.data[2],oe=+t.data[3],re=+t.data[4],pe=+t.data[5],de=+t.data[6];if(ae&&(pe+=e.x,de+=e.y),pe===e.x&&de===e.y)break;if(0==ne||0==ie)n=n.concat(this.doubleLine(e.x,e.y,pe,de,a)),e.setPosition(pe,de);else for(var he=0;1>he;he++)for(var ge,fe=new b([e.x,e.y],[pe,de],[ne,ie],se,!!oe,!!re),ue=fe.getNextSegment();ue;)ge=this._bezierTo(ue.cp1[0],ue.cp1[1],ue.cp2[0],ue.cp2[1],ue.to[0],ue.to[1],e,a),n=n.concat(ge),ue=fe.getNextSegment()}break}default:}return n}}]),e}(),z="undefined"!=typeof self,E=z&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,L="undefined"!=typeof self,R=function(){function e(t,a){u(this,e),this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=t||{},this.surface=a,this.renderer=l(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}return y(e,[{key:"_options",value:function(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}},{key:"_drawable",value:function(e,t,l){return{shape:e,sets:t||[],options:l||this.defaultOptions}}},{key:"getCanvasSize",value:function(){var e=function(e){return e&&"object"===("undefined"==typeof e?"undefined":o(e))&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100};return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}},{key:"computePolygonSize",value:function(e){if(e.length){for(var t=e[0][0],l=e[0][0],a=e[0][1],o=e[0][1],r=1;rl&&(l=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||"none",strokeWidth:l,fill:"none"}}},{key:"opsToPath",value:function(e){var t="",l=!0,a=!1,n=void 0;try{for(var i,s=e.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case"move":t+="M"+r[0]+" "+r[1]+" ";break;case"bcurveTo":t+="C"+r[0]+" "+r[1]+", "+r[2]+" "+r[3]+", "+r[4]+" "+r[5]+" ";break;case"qcurveTo":t+="Q"+r[0]+" "+r[1]+", "+r[2]+" "+r[3]+" ";break;case"lineTo":t+="L"+r[0]+" "+r[1]+" ";}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}return t.trim()}},{key:"lib",get:function(){return this.renderer}}]),e}(),W="undefined"!=typeof document,N=function(){function e(t,l){u(this,e),this.canvas=t,this.ctx=this.canvas.getContext("2d"),this.gen=new R(l||null,this.canvas)}return y(e,[{key:"line",value:function(e,t,l,a,n){var i=this.gen.line(e,t,l,a,n);return this.draw(i),i}},{key:"rectangle",value:function(e,t,l,a,n){var i=this.gen.rectangle(e,t,l,a,n);return this.draw(i),i}},{key:"ellipse",value:function(e,t,l,a,n){var i=this.gen.ellipse(e,t,l,a,n);return this.draw(i),i}},{key:"circle",value:function(e,t,l,a){var n=this.gen.circle(e,t,l,a);return this.draw(n),n}},{key:"linearPath",value:function(e,t){var l=this.gen.linearPath(e,t);return this.draw(l),l}},{key:"polygon",value:function(e,t){var l=this.gen.polygon(e,t);return this.draw(l),l}},{key:"arc",value:function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2),e.save(),e.strokeStyle=l.fill||"",e.lineWidth=a,this._drawToContext(e,t),e.restore()}},{key:"_drawToContext",value:function(e,t){e.beginPath();var l=!0,a=!1,n=void 0;try{for(var i,s=t.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case"move":e.moveTo(r[0],r[1]);break;case"bcurveTo":e.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);break;case"qcurveTo":e.quadraticCurveTo(r[0],r[1],r[2],r[3]);break;case"lineTo":e.lineTo(r[0],r[1]);}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}"fillPath"===t.type?e.fill():e.stroke()}},{key:"generator",get:function(){return this.gen}}],[{key:"createRenderer",value:function(){return new C}}]),e}(),D=function(e){function t(){return u(this,t),v(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return c(t,e),y(t,[{key:"line",value:async function(e,t,l,a,n){var i=this._options(n);return this._drawable("line",[await this.lib.line(e,t,l,a,i)],i)}},{key:"rectangle",value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill){var o=[[e,t],[e+l,t],[e+l,t+a],[e,t+a]];"solid"===i.fillStyle?s.push((await this.lib.solidFillPolygon(o,i))):s.push((await this.lib.patternFillPolygon(o,i)))}return s.push((await this.lib.rectangle(e,t,l,a,i))),this._drawable("rectangle",s,i)}},{key:"ellipse",value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill)if("solid"===i.fillStyle){var o=await this.lib.ellipse(e,t,l,a,i);o.type="fillPath",s.push(o)}else s.push((await this.lib.patternFillEllipse(e,t,l,a,i)));return s.push((await this.lib.ellipse(e,t,l,a,i))),this._drawable("ellipse",s,i)}},{key:"circle",value:async function(e,t,l,a){var n=await this.ellipse(e,t,l,l,a);return n.shape="circle",n}},{key:"linearPath",value:async function(e,t){var l=this._options(t);return this._drawable("linearPath",[await this.lib.linearPath(e,!1,l)],l)}},{key:"arc",value:async function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2);var n=e.createElementNS("http://www.w3.org/2000/svg","path");return n.setAttribute("d",this.opsToPath(t)),n.style.stroke=l.fill,n.style.strokeWidth=a+"",n.style.fill="none",n}},{key:"generator",get:function(){return this.gen}},{key:"defs",get:function(){if(F&&!this._defs){var e=this.svg.ownerDocument||document,t=e.createElementNS("http://www.w3.org/2000/svg","defs");this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}}],[{key:"createRenderer",value:function(){return new C}}]),e}(),q=function(e){function t(e,l){u(this,t);var a=v(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,l));return a.genAsync=new D(l||null,a.svg),a}return c(t,e),y(t,[{key:"line",value:async function(e,t,l,a,n){var i=await this.genAsync.line(e,t,l,a,n);return this.draw(i)}},{key:"rectangle",value:async function(e,t,l,a,n){var i=await this.genAsync.rectangle(e,t,l,a,n);return this.draw(i)}},{key:"ellipse",value:async function(e,t,l,a,n){var i=await this.genAsync.ellipse(e,t,l,a,n);return this.draw(i)}},{key:"circle",value:async function(e,t,l,a){var n=await this.genAsync.circle(e,t,l,a);return this.draw(n)}},{key:"linearPath",value:async function(e,t){var l=await this.genAsync.linearPath(e,t);return this.draw(l)}},{key:"polygon",value:async function(e,t){var l=await this.genAsync.polygon(e,t);return this.draw(l)}},{key:"arc",value:async function(e,t,l,a,n,i){var s=!!(6 Date: Mon, 18 Jun 2018 15:09:18 -0700 Subject: [PATCH 15/16] es5 umd build --- dist/rough.umd.es5.js | 2824 +++++++++++++++++++++++++++++++++++++ dist/rough.umd.es5.min.js | 1 + rollup.config.js | 18 + 3 files changed, 2843 insertions(+) create mode 100644 dist/rough.umd.es5.js create mode 100644 dist/rough.umd.es5.min.js diff --git a/dist/rough.umd.es5.js b/dist/rough.umd.es5.js new file mode 100644 index 0000000..c483f16 --- /dev/null +++ b/dist/rough.umd.es5.js @@ -0,0 +1,2824 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.rough = factory()); +}(this, (function () { 'use strict'; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; + }; + + var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; + }; + + function isType(token, type) { + return token.type === type; + } + var PARAMS = { + A: { length: 7 }, + a: { length: 7 }, + C: { length: 6 }, + c: { length: 6 }, + H: { length: 1 }, + h: { length: 1 }, + L: { length: 2 }, + l: { length: 2 }, + M: { length: 2 }, + m: { length: 2 }, + Q: { length: 4 }, + q: { length: 4 }, + S: { length: 4 }, + s: { length: 4 }, + T: { length: 4 }, + t: { length: 2 }, + V: { length: 1 }, + v: { length: 1 }, + Z: { length: 0 }, + z: { length: 0 } + }; + + var ParsedPath = function () { + function ParsedPath(d) { + classCallCheck(this, ParsedPath); + + this.COMMAND = 0; + this.NUMBER = 1; + this.EOD = 2; + this.segments = []; + this.parseData(d); + this.processPoints(); + } + + createClass(ParsedPath, [{ + key: 'tokenize', + value: function tokenize(d) { + var 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: this.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: this.NUMBER, text: '' + parseFloat(RegExp.$1) }; + d = d.substr(RegExp.$1.length); + } else { + console.error('Unrecognized segment command: ' + d); + return []; + } + } + tokens[tokens.length] = { type: this.EOD, text: '' }; + return tokens; + } + }, { + key: 'parseData', + value: function parseData(d) { + var tokens = this.tokenize(d); + var index = 0; + var token = tokens[index]; + var mode = 'BOD'; + this.segments = new Array(); + while (!isType(token, this.EOD)) { + var param_length = void 0; + var params = new Array(); + if (mode === 'BOD') { + if (token.text === 'M' || token.text === 'm') { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } else { + this.parseData('M0,0' + d); + return; + } + } else { + if (isType(token, this.NUMBER)) { + param_length = PARAMS[mode].length; + } else { + index++; + param_length = PARAMS[token.text].length; + mode = token.text; + } + } + if (index + param_length < tokens.length) { + for (var i = index; i < index + param_length; i++) { + var numbeToken = tokens[i]; + if (isType(numbeToken, this.NUMBER)) { + params[params.length] = +numbeToken.text; + } else { + console.error('Parameter type is not a number: ' + mode + ',' + numbeToken.text); + return; + } + } + if (PARAMS[mode]) { + var segment = { key: mode, data: params }; + this.segments.push(segment); + index += param_length; + token = tokens[index]; + if (mode === 'M') mode = 'L'; + if (mode === 'm') mode = 'l'; + } else { + console.error('Unsupported segment type: ' + mode); + return; + } + } else { + console.error('Path data ended before all parameters were found'); + } + } + } + }, { + key: 'processPoints', + value: function processPoints() { + var first = null; + var currentPoint = [0, 0]; + for (var i = 0; i < this.segments.length; i++) { + var s = this.segments[i]; + switch (s.key) { + case 'M': + case 'L': + case 'T': + s.point = [s.data[0], s.data[1]]; + break; + case 'm': + case 'l': + case 't': + s.point = [s.data[0] + currentPoint[0], s.data[1] + currentPoint[1]]; + break; + case 'H': + s.point = [s.data[0], currentPoint[1]]; + break; + case 'h': + s.point = [s.data[0] + currentPoint[0], currentPoint[1]]; + break; + case 'V': + s.point = [currentPoint[0], s.data[0]]; + break; + case 'v': + s.point = [currentPoint[0], s.data[0] + currentPoint[1]]; + break; + case 'z': + case 'Z': + if (first) { + s.point = [first[0], first[1]]; + } + break; + case 'C': + s.point = [s.data[4], s.data[5]]; + break; + case 'c': + s.point = [s.data[4] + currentPoint[0], s.data[5] + currentPoint[1]]; + break; + case 'S': + s.point = [s.data[2], s.data[3]]; + break; + case 's': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'Q': + s.point = [s.data[2], s.data[3]]; + break; + case 'q': + s.point = [s.data[2] + currentPoint[0], s.data[3] + currentPoint[1]]; + break; + case 'A': + s.point = [s.data[5], s.data[6]]; + break; + case 'a': + s.point = [s.data[5] + currentPoint[0], s.data[6] + currentPoint[1]]; + break; + } + if (s.key === 'm' || s.key === 'M') { + first = null; + } + if (s.point) { + currentPoint = s.point; + if (!first) { + first = s.point; + } + } + if (s.key === 'z' || s.key === 'Z') { + first = null; + } + } + } + }, { + key: 'closed', + get: function get$$1() { + if (typeof this._closed === 'undefined') { + this._closed = false; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = this.segments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var s = _step.value; + + if (s.key.toLowerCase() === 'z') { + this._closed = true; + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + } + return this._closed; + } + }]); + return ParsedPath; + }(); + + var RoughPath = function () { + function RoughPath(d) { + classCallCheck(this, RoughPath); + + this._position = [0, 0]; + this._first = null; + this.bezierReflectionPoint = null; + this.quadReflectionPoint = null; + this.parsed = new ParsedPath(d); + } + + createClass(RoughPath, [{ + key: 'setPosition', + value: function setPosition(x, y) { + this._position = [x, y]; + if (!this._first) { + this._first = [x, y]; + } + } + }, { + key: 'segments', + get: function get$$1() { + return this.parsed.segments; + } + }, { + key: 'closed', + get: function get$$1() { + return this.parsed.closed; + } + }, { + key: 'linearPoints', + get: function get$$1() { + if (!this._linearPoints) { + var lp = []; + var points = []; + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = this.parsed.segments[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var s = _step2.value; + + var key = s.key.toLowerCase(); + if (key === 'm' || key === 'z') { + if (points.length) { + lp.push(points); + points = []; + } + if (key === 'z') { + continue; + } + } + if (s.point) { + points.push(s.point); + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + if (points.length) { + lp.push(points); + points = []; + } + this._linearPoints = lp; + } + return this._linearPoints; + } + }, { + key: 'first', + get: function get$$1() { + return this._first; + }, + set: function set$$1(v) { + this._first = v; + } + }, { + key: 'position', + get: function get$$1() { + return this._position; + } + }, { + key: 'x', + get: function get$$1() { + return this._position[0]; + } + }, { + key: 'y', + get: function get$$1() { + return this._position[1]; + } + }]); + return RoughPath; + }(); + // Algorithm as described in https://www.w3.org/TR/SVG/implnote.html + // Code adapted from nsSVGPathDataParser.cpp in Mozilla + // https://hg.mozilla.org/mozilla-central/file/17156fbebbc8/content/svg/content/src/nsSVGPathDataParser.cpp#l887 + var RoughArcConverter = function () { + function RoughArcConverter(from, to, radii, angle, largeArcFlag, sweepFlag) { + classCallCheck(this, RoughArcConverter); + + this._segIndex = 0; + this._numSegs = 0; + this._rx = 0; + this._ry = 0; + this._sinPhi = 0; + this._cosPhi = 0; + this._C = [0, 0]; + this._theta = 0; + this._delta = 0; + this._T = 0; + this._from = from; + if (from[0] === to[0] && from[1] === to[1]) { + return; + } + var radPerDeg = Math.PI / 180; + this._rx = Math.abs(radii[0]); + this._ry = Math.abs(radii[1]); + this._sinPhi = Math.sin(angle * radPerDeg); + this._cosPhi = Math.cos(angle * radPerDeg); + var x1dash = this._cosPhi * (from[0] - to[0]) / 2.0 + this._sinPhi * (from[1] - to[1]) / 2.0; + var y1dash = -this._sinPhi * (from[0] - to[0]) / 2.0 + this._cosPhi * (from[1] - to[1]) / 2.0; + var root = 0; + var numerator = this._rx * this._rx * this._ry * this._ry - this._rx * this._rx * y1dash * y1dash - this._ry * this._ry * x1dash * x1dash; + if (numerator < 0) { + var s = Math.sqrt(1 - numerator / (this._rx * this._rx * this._ry * this._ry)); + this._rx = this._rx * s; + this._ry = this._ry * s; + root = 0; + } else { + root = (largeArcFlag === sweepFlag ? -1.0 : 1.0) * Math.sqrt(numerator / (this._rx * this._rx * y1dash * y1dash + this._ry * this._ry * x1dash * x1dash)); + } + var cxdash = root * this._rx * y1dash / this._ry; + var cydash = -root * this._ry * x1dash / this._rx; + this._C = [0, 0]; + this._C[0] = this._cosPhi * cxdash - this._sinPhi * cydash + (from[0] + to[0]) / 2.0; + this._C[1] = this._sinPhi * cxdash + this._cosPhi * cydash + (from[1] + to[1]) / 2.0; + this._theta = this.calculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry); + var dtheta = this.calculateVectorAngle((x1dash - cxdash) / this._rx, (y1dash - cydash) / this._ry, (-x1dash - cxdash) / this._rx, (-y1dash - cydash) / this._ry); + if (!sweepFlag && dtheta > 0) { + dtheta -= 2 * Math.PI; + } else if (sweepFlag && dtheta < 0) { + dtheta += 2 * Math.PI; + } + this._numSegs = Math.ceil(Math.abs(dtheta / (Math.PI / 2))); + this._delta = dtheta / this._numSegs; + this._T = 8 / 3 * Math.sin(this._delta / 4) * Math.sin(this._delta / 4) / Math.sin(this._delta / 2); + } + + createClass(RoughArcConverter, [{ + key: 'getNextSegment', + value: function getNextSegment() { + if (this._segIndex === this._numSegs) { + return null; + } + var cosTheta1 = Math.cos(this._theta); + var sinTheta1 = Math.sin(this._theta); + var theta2 = this._theta + this._delta; + var cosTheta2 = Math.cos(theta2); + var sinTheta2 = Math.sin(theta2); + var to = [this._cosPhi * this._rx * cosTheta2 - this._sinPhi * this._ry * sinTheta2 + this._C[0], this._sinPhi * this._rx * cosTheta2 + this._cosPhi * this._ry * sinTheta2 + this._C[1]]; + var cp1 = [this._from[0] + this._T * (-this._cosPhi * this._rx * sinTheta1 - this._sinPhi * this._ry * cosTheta1), this._from[1] + this._T * (-this._sinPhi * this._rx * sinTheta1 + this._cosPhi * this._ry * cosTheta1)]; + var cp2 = [to[0] + this._T * (this._cosPhi * this._rx * sinTheta2 + this._sinPhi * this._ry * cosTheta2), to[1] + this._T * (this._sinPhi * this._rx * sinTheta2 - this._cosPhi * this._ry * cosTheta2)]; + this._theta = theta2; + this._from = [to[0], to[1]]; + this._segIndex++; + return { + cp1: cp1, + cp2: cp2, + to: to + }; + } + }, { + key: 'calculateVectorAngle', + value: function calculateVectorAngle(ux, uy, vx, vy) { + var ta = Math.atan2(uy, ux); + var tb = Math.atan2(vy, vx); + if (tb >= ta) return tb - ta; + return 2 * Math.PI - (ta - tb); + } + }]); + return RoughArcConverter; + }(); + var PathFitter = function () { + function PathFitter(sets, closed) { + classCallCheck(this, PathFitter); + + this.sets = sets; + this.closed = closed; + } + + createClass(PathFitter, [{ + key: 'fit', + value: function fit(simplification) { + var outSets = []; + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = this.sets[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + var set$$1 = _step3.value; + + var length = set$$1.length; + var estLength = Math.floor(simplification * length); + if (estLength < 5) { + if (length <= 5) { + continue; + } + estLength = 5; + } + outSets.push(this.reduce(set$$1, estLength)); + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3.return) { + _iterator3.return(); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } + + var d = ''; + var _iteratorNormalCompletion4 = true; + var _didIteratorError4 = false; + var _iteratorError4 = undefined; + + try { + for (var _iterator4 = outSets[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { + var _set = _step4.value; + + for (var i = 0; i < _set.length; i++) { + var point = _set[i]; + if (i === 0) { + d += 'M' + point[0] + ',' + point[1]; + } else { + d += 'L' + point[0] + ',' + point[1]; + } + } + if (this.closed) { + d += 'z '; + } + } + } catch (err) { + _didIteratorError4 = true; + _iteratorError4 = err; + } finally { + try { + if (!_iteratorNormalCompletion4 && _iterator4.return) { + _iterator4.return(); + } + } finally { + if (_didIteratorError4) { + throw _iteratorError4; + } + } + } + + return d; + } + }, { + key: 'distance', + value: function distance(p1, p2) { + return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2)); + } + }, { + key: 'reduce', + value: function reduce(set$$1, count) { + if (set$$1.length <= count) { + return set$$1; + } + var points = set$$1.slice(0); + while (points.length > count) { + var minArea = -1; + var minIndex = -1; + for (var i = 1; i < points.length - 1; i++) { + var a = this.distance(points[i - 1], points[i]); + var b = this.distance(points[i], points[i + 1]); + var c = this.distance(points[i - 1], points[i + 1]); + var s = (a + b + c) / 2.0; + var area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); + if (minArea < 0 || area < minArea) { + minArea = area; + minIndex = i; + } + } + if (minIndex > 0) { + points.splice(minIndex, 1); + } else { + break; + } + } + return points; + } + }]); + return PathFitter; + }(); + + var Segment = function () { + function Segment(p1, p2) { + classCallCheck(this, Segment); + + this.xi = Number.MAX_VALUE; + this.yi = Number.MAX_VALUE; + this.px1 = p1[0]; + this.py1 = p1[1]; + this.px2 = p2[0]; + this.py2 = p2[1]; + this.a = this.py2 - this.py1; + this.b = this.px1 - this.px2; + this.c = this.px2 * this.py1 - this.px1 * this.py2; + this._undefined = this.a === 0 && this.b === 0 && this.c === 0; + } + + createClass(Segment, [{ + key: "isUndefined", + value: function isUndefined() { + return this._undefined; + } + }, { + key: "intersects", + value: function intersects(otherSegment) { + if (this.isUndefined() || otherSegment.isUndefined()) { + return false; + } + var grad1 = Number.MAX_VALUE; + var grad2 = Number.MAX_VALUE; + var int1 = 0, + int2 = 0; + var a = this.a, + b = this.b, + c = this.c; + if (Math.abs(b) > 0.00001) { + grad1 = -a / b; + int1 = -c / b; + } + if (Math.abs(otherSegment.b) > 0.00001) { + grad2 = -otherSegment.a / otherSegment.b; + int2 = -otherSegment.c / otherSegment.b; + } + if (grad1 === Number.MAX_VALUE) { + if (grad2 === Number.MAX_VALUE) { + if (-c / a !== -otherSegment.c / otherSegment.a) { + return false; + } + if (this.py1 >= Math.min(otherSegment.py1, otherSegment.py2) && this.py1 <= Math.max(otherSegment.py1, otherSegment.py2)) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if (this.py2 >= Math.min(otherSegment.py1, otherSegment.py2) && this.py2 <= Math.max(otherSegment.py1, otherSegment.py2)) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = this.px1; + this.yi = grad2 * this.xi + int2; + if ((this.py1 - this.yi) * (this.yi - this.py2) < -0.00001 || (otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001) { + return false; + } + if (Math.abs(otherSegment.a) < 0.00001) { + if ((otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad2 === Number.MAX_VALUE) { + this.xi = otherSegment.px1; + this.yi = grad1 * this.xi + int1; + if ((otherSegment.py1 - this.yi) * (this.yi - otherSegment.py2) < -0.00001 || (this.py1 - this.yi) * (this.yi - this.py2) < -0.00001) { + return false; + } + if (Math.abs(a) < 0.00001) { + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001) { + return false; + } + return true; + } + return true; + } + if (grad1 === grad2) { + if (int1 !== int2) { + return false; + } + if (this.px1 >= Math.min(otherSegment.px1, otherSegment.px2) && this.px1 <= Math.max(otherSegment.py1, otherSegment.py2)) { + this.xi = this.px1; + this.yi = this.py1; + return true; + } + if (this.px2 >= Math.min(otherSegment.px1, otherSegment.px2) && this.px2 <= Math.max(otherSegment.px1, otherSegment.px2)) { + this.xi = this.px2; + this.yi = this.py2; + return true; + } + return false; + } + this.xi = (int2 - int1) / (grad1 - grad2); + this.yi = grad1 * this.xi + int1; + if ((this.px1 - this.xi) * (this.xi - this.px2) < -0.00001 || (otherSegment.px1 - this.xi) * (this.xi - otherSegment.px2) < -0.00001) { + return false; + } + return true; + } + }]); + return Segment; + }(); + + var HachureIterator = function () { + function HachureIterator(top, bottom, left, right, gap, sinAngle, cosAngle, tanAngle) { + classCallCheck(this, HachureIterator); + + this.deltaX = 0; + this.hGap = 0; + this.top = top; + this.bottom = bottom; + this.left = left; + this.right = right; + this.gap = gap; + this.sinAngle = sinAngle; + this.tanAngle = tanAngle; + if (Math.abs(sinAngle) < 0.0001) { + this.pos = left + gap; + } else if (Math.abs(sinAngle) > 0.9999) { + this.pos = top + gap; + } else { + this.deltaX = (bottom - top) * Math.abs(tanAngle); + this.pos = left - Math.abs(this.deltaX); + this.hGap = Math.abs(gap / cosAngle); + this.sLeft = new Segment([left, bottom], [left, top]); + this.sRight = new Segment([right, bottom], [right, top]); + } + } + + createClass(HachureIterator, [{ + key: 'nextLine', + value: function nextLine() { + if (Math.abs(this.sinAngle) < 0.0001) { + if (this.pos < this.right) { + var line = [this.pos, this.top, this.pos, this.bottom]; + this.pos += this.gap; + return line; + } + } else if (Math.abs(this.sinAngle) > 0.9999) { + if (this.pos < this.bottom) { + var _line = [this.left, this.pos, this.right, this.pos]; + this.pos += this.gap; + return _line; + } + } else { + var xLower = this.pos - this.deltaX / 2; + var xUpper = this.pos + this.deltaX / 2; + var yLower = this.bottom; + var yUpper = this.top; + if (this.pos < this.right + this.deltaX) { + while (xLower < this.left && xUpper < this.left || xLower > this.right && xUpper > this.right) { + this.pos += this.hGap; + xLower = this.pos - this.deltaX / 2; + xUpper = this.pos + this.deltaX / 2; + if (this.pos > this.right + this.deltaX) { + return null; + } + } + var s = new Segment([xLower, yLower], [xUpper, yUpper]); + if (this.sLeft && s.intersects(this.sLeft)) { + xLower = s.xi; + yLower = s.yi; + } + if (this.sRight && s.intersects(this.sRight)) { + xUpper = s.xi; + yUpper = s.yi; + } + if (this.tanAngle > 0) { + xLower = this.right - (xLower - this.left); + xUpper = this.right - (xUpper - this.left); + } + var _line2 = [xLower, yLower, xUpper, yUpper]; + this.pos += this.hGap; + return _line2; + } + } + return null; + } + }]); + return HachureIterator; + }(); + + var HachureFiller = function () { + function HachureFiller(renderer) { + classCallCheck(this, HachureFiller); + + this.renderer = renderer; + } + + createClass(HachureFiller, [{ + key: 'fillPolygon', + value: function fillPolygon(points, o) { + return this._fillPolygon(points, o); + } + }, { + key: 'fillEllipse', + value: function fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o); + } + }, { + key: '_fillPolygon', + value: function _fillPolygon(points, o) { + var connectEnds = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var ops = []; + if (points && points.length) { + var left = points[0][0]; + var right = points[0][0]; + var top = points[0][1]; + var bottom = points[0][1]; + for (var i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + var angle = o.hachureAngle; + var gap = o.hachureGap; + if (gap < 0) { + gap = o.strokeWidth * 4; + } + gap = Math.max(gap, 0.1); + var radPerDeg = Math.PI / 180; + var hachureAngle = angle % 180 * radPerDeg; + var cosAngle = Math.cos(hachureAngle); + var sinAngle = Math.sin(hachureAngle); + var tanAngle = Math.tan(hachureAngle); + var it = new HachureIterator(top - 1, bottom + 1, left - 1, right + 1, gap, sinAngle, cosAngle, tanAngle); + var rect = void 0; + var prevPoint = null; + while ((rect = it.nextLine()) != null) { + var lines = this.getIntersectingLines(rect, points); + for (var _i = 0; _i < lines.length; _i++) { + if (_i < lines.length - 1) { + var p1 = lines[_i]; + var p2 = lines[_i + 1]; + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + } + } + } + return { type: 'fillSketch', ops: ops }; + } + }, { + key: '_fillEllipse', + value: function _fillEllipse(cx, cy, width, height, o) { + var connectEnds = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; + + var ops = []; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.renderer.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.renderer.getOffset(-ry * 0.05, ry * 0.05, o); + var angle = o.hachureAngle; + var gap = o.hachureGap; + if (gap <= 0) { + gap = o.strokeWidth * 4; + } + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + var radPerDeg = Math.PI / 180; + var hachureAngle = angle % 180 * radPerDeg; + var tanAngle = Math.tan(hachureAngle); + var aspectRatio = ry / rx; + var hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1); + var sinAnglePrime = aspectRatio * tanAngle / hyp; + var cosAnglePrime = 1 / hyp; + var gapPrime = gap / (rx * ry / Math.sqrt(ry * cosAnglePrime * (ry * cosAnglePrime) + rx * sinAnglePrime * (rx * sinAnglePrime)) / rx); + var halfLen = Math.sqrt(rx * rx - (cx - rx + gapPrime) * (cx - rx + gapPrime)); + var prevPoint = null; + for (var xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) { + halfLen = Math.sqrt(rx * rx - (cx - xPos) * (cx - xPos)); + var p1 = this.affine(xPos, cy - halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + var p2 = this.affine(xPos, cy + halfLen, cx, cy, sinAnglePrime, cosAnglePrime, aspectRatio); + ops = ops.concat(this.renderer.doubleLine(p1[0], p1[1], p2[0], p2[1], o)); + if (connectEnds && prevPoint) { + ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], p1[0], p1[1], o)); + } + prevPoint = p2; + } + return { type: 'fillSketch', ops: ops }; + } + }, { + key: 'getIntersectingLines', + value: function getIntersectingLines(line, points) { + var intersections = []; + var s1 = new Segment([line[0], line[1]], [line[2], line[3]]); + for (var i = 0; i < points.length; i++) { + var s2 = new Segment(points[i], points[(i + 1) % points.length]); + if (s1.intersects(s2)) { + intersections.push([s1.xi, s1.yi]); + } + } + return intersections; + } + }, { + key: 'affine', + value: function affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) { + var A = -cx * cosAnglePrime - cy * sinAnglePrime + cx; + var B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy; + var C = cosAnglePrime; + var D = sinAnglePrime; + var E = -R * sinAnglePrime; + var F = R * cosAnglePrime; + return [A + C * x + D * y, B + E * x + F * y]; + } + }]); + return HachureFiller; + }(); + + var ZigZagFiller = function (_HachureFiller) { + inherits(ZigZagFiller, _HachureFiller); + + function ZigZagFiller() { + classCallCheck(this, ZigZagFiller); + return possibleConstructorReturn(this, (ZigZagFiller.__proto__ || Object.getPrototypeOf(ZigZagFiller)).apply(this, arguments)); + } + + createClass(ZigZagFiller, [{ + key: 'fillPolygon', + value: function fillPolygon(points, o) { + return this._fillPolygon(points, o, true); + } + }, { + key: 'fillEllipse', + value: function fillEllipse(cx, cy, width, height, o) { + return this._fillEllipse(cx, cy, width, height, o, true); + } + }]); + return ZigZagFiller; + }(HachureFiller); + + var HatchFiller = function (_HachureFiller) { + inherits(HatchFiller, _HachureFiller); + + function HatchFiller() { + classCallCheck(this, HatchFiller); + return possibleConstructorReturn(this, (HatchFiller.__proto__ || Object.getPrototypeOf(HatchFiller)).apply(this, arguments)); + } + + createClass(HatchFiller, [{ + key: 'fillPolygon', + value: function fillPolygon(points, o) { + var set$$1 = this._fillPolygon(points, o); + var o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + var set2 = this._fillPolygon(points, o2); + set$$1.ops = set$$1.ops.concat(set2.ops); + return set$$1; + } + }, { + key: 'fillEllipse', + value: function fillEllipse(cx, cy, width, height, o) { + var set$$1 = this._fillEllipse(cx, cy, width, height, o); + var o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 }); + var set2 = this._fillEllipse(cx, cy, width, height, o2); + set$$1.ops = set$$1.ops.concat(set2.ops); + return set$$1; + } + }]); + return HatchFiller; + }(HachureFiller); + + var fillers = {}; + function getFiller(renderer, o) { + var fillerName = o.fillStyle || 'hachure'; + if (!fillers[fillerName]) { + switch (fillerName) { + case 'zigzag': + if (!fillers[fillerName]) { + fillers[fillerName] = new ZigZagFiller(renderer); + } + break; + case 'cross-hatch': + if (!fillers[fillerName]) { + fillers[fillerName] = new HatchFiller(renderer); + } + break; + case 'hachure': + default: + fillerName = 'hachure'; + if (!fillers[fillerName]) { + fillers[fillerName] = new HachureFiller(renderer); + } + break; + } + } + return fillers[fillerName]; + } + + var RoughRenderer = function () { + function RoughRenderer() { + classCallCheck(this, RoughRenderer); + } + + createClass(RoughRenderer, [{ + key: 'line', + value: function line(x1, y1, x2, y2, o) { + var ops = this.doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops: ops }; + } + }, { + key: 'linearPath', + value: function linearPath(points, close, o) { + var len = (points || []).length; + if (len > 2) { + var ops = []; + for (var i = 0; i < len - 1; i++) { + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); + } + return { type: 'path', ops: ops }; + } else if (len === 2) { + return this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + }, { + key: 'polygon', + value: function polygon(points, o) { + return this.linearPath(points, true, o); + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, o) { + var points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + return this.polygon(points, o); + } + }, { + key: 'curve', + value: function curve(points, o) { + var o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + var o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, o) { + var increment = Math.PI * 2 / o.curveStepCount; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + var o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); + var o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop, closed, roughClosure, o) { + var cx = x; + var cy = y; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + var strt = start; + var 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; + } + var ellipseInc = Math.PI * 2 / o.curveStepCount; + var arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + var o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + var o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + var ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops: ops }; + } + }, { + key: 'svgPath', + value: function svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + var p = new RoughPath(path); + if (o.simplification) { + var fitter = new PathFitter(p.linearPoints, p.closed); + var d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + var ops = []; + var segments = p.segments || []; + for (var i = 0; i < segments.length; i++) { + var s = segments[i]; + var prev = i > 0 ? segments[i - 1] : null; + var opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops: ops }; + } + }, { + key: 'solidFillPolygon', + value: function solidFillPolygon(points, o) { + var ops = []; + if (PointerEvent.length) { + var offset = o.maxRandomnessOffset || 0; + var len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (var i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops: ops }; + } + }, { + key: 'patternFillPolygon', + value: function patternFillPolygon(points, o) { + var filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + }, { + key: 'patternFillEllipse', + value: function patternFillEllipse(cx, cy, width, height, o) { + var filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + }, { + key: 'patternFillArc', + value: function patternFillArc(x, y, width, height, start, stop, o) { + var cx = x; + var cy = y; + var rx = Math.abs(width / 2); + var ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + var strt = start; + var 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; + } + var increment = (stp - strt) / o.curveStepCount; + var points = []; + for (var 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 this.patternFillPolygon(points, o); + } + /// + + }, { + key: 'getOffset', + value: function getOffset(min, max, ops) { + return ops.roughness * (Math.random() * (max - min) + min); + } + }, { + key: 'doubleLine', + value: function doubleLine(x1, y1, x2, y2, o) { + var o1 = this._line(x1, y1, x2, y2, o, true, false); + var o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + }, { + key: '_line', + value: function _line(x1, y1, x2, y2, o, move, overlay) { + var lengthSq = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2); + var offset = o.maxRandomnessOffset || 0; + if (offset * offset * 100 > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + var halfOffset = offset / 2; + var divergePoint = 0.2 + Math.random() * 0.2; + var midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + var midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); + var ops = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [x1 + this.getOffset(-halfOffset, halfOffset, o), y1 + this.getOffset(-halfOffset, halfOffset, o)] + }); + } else { + ops.push({ + op: 'move', data: [x1 + this.getOffset(-offset, offset, o), y1 + this.getOffset(-offset, offset, o)] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), x2 + this.getOffset(-halfOffset, halfOffset, o), y2 + this.getOffset(-halfOffset, halfOffset, o)] + }); + } else { + ops.push({ + op: 'bcurveTo', data: [midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), x2 + this.getOffset(-offset, offset, o), y2 + this.getOffset(-offset, offset, o)] + }); + } + return ops; + } + }, { + key: '_curve', + value: function _curve(points, closePoint, o) { + var len = points.length; + var ops = []; + if (len > 3) { + var b = []; + var s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (var i = 1; i + 2 < len; i++) { + var 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) { + var ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, 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 = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + }, { + key: '_ellipse', + value: function _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + var radOffset = this.getOffset(-0.5, 0.5, o) - Math.PI / 2; + var points = []; + points.push([this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)]); + for (var angle = radOffset; angle < Math.PI * 2 + radOffset - 0.01; angle = angle + increment) { + points.push([this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle)]); + } + points.push([this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5)]); + points.push([this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap)]); + points.push([this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5)]); + return this._curve(points, null, o); + } + }, { + key: '_curveWithOffset', + value: function _curveWithOffset(points, offset, o) { + var ps = []; + ps.push([points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)]); + ps.push([points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)]); + for (var i = 1; i < points.length; i++) { + ps.push([points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)]); + if (i === points.length - 1) { + ps.push([points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)]); + } + } + return this._curve(ps, null, o); + } + }, { + key: '_arc', + value: function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + var radOffset = strt + this.getOffset(-0.1, 0.1, o); + var points = []; + points.push([this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)]); + for (var angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), this.getOffset(-offset, 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 this._curve(points, null, o); + } + }, { + key: '_bezierTo', + value: function _bezierTo(x1, y1, x2, y2, x, y, path, o) { + var ops = []; + var ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + var f = [0, 0]; + for (var i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } else { + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), f[0], f[1]] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + }, { + key: '_processSegment', + value: function _processSegment(path, seg, prevSeg, o) { + var ops = []; + switch (seg.key) { + case 'M': + case 'm': + { + var delta = seg.key === 'm'; + if (seg.data.length >= 2) { + var x = +seg.data[0]; + var y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + var ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': + { + var _delta = seg.key === 'l'; + if (seg.data.length >= 2) { + var _x = +seg.data[0]; + var _y = +seg.data[1]; + if (_delta) { + _x += path.x; + _y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, _x, _y, o)); + path.setPosition(_x, _y); + } + break; + } + case 'H': + case 'h': + { + var _delta2 = seg.key === 'h'; + if (seg.data.length) { + var _x2 = +seg.data[0]; + if (_delta2) { + _x2 += path.x; + } + ops = ops.concat(this.doubleLine(path.x, path.y, _x2, path.y, o)); + path.setPosition(_x2, path.y); + } + break; + } + case 'V': + case 'v': + { + var _delta3 = seg.key === 'v'; + if (seg.data.length) { + var _y2 = +seg.data[0]; + if (_delta3) { + _y2 += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, _y2, o)); + path.setPosition(path.x, _y2); + } + break; + } + case 'Z': + case 'z': + { + if (path.first) { + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': + { + var _delta4 = seg.key === 'c'; + if (seg.data.length >= 6) { + var x1 = +seg.data[0]; + var y1 = +seg.data[1]; + var x2 = +seg.data[2]; + var y2 = +seg.data[3]; + var _x3 = +seg.data[4]; + var _y3 = +seg.data[5]; + if (_delta4) { + x1 += path.x; + x2 += path.x; + _x3 += path.x; + y1 += path.y; + y2 += path.y; + _y3 += path.y; + } + var ob = this._bezierTo(x1, y1, x2, y2, _x3, _y3, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [_x3 + (_x3 - x2), _y3 + (_y3 - y2)]; + } + break; + } + case 'S': + case 's': + { + var _delta5 = seg.key === 's'; + if (seg.data.length >= 4) { + var _x4 = +seg.data[0]; + var _y4 = +seg.data[1]; + var _x5 = +seg.data[2]; + var _y5 = +seg.data[3]; + if (_delta5) { + _x4 += path.x; + _x5 += path.x; + _y4 += path.y; + _y5 += path.y; + } + var _x6 = _x4; + var _y6 = _y4; + var prevKey = prevSeg ? prevSeg.key : ''; + var ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + _x6 = ref[0]; + _y6 = ref[1]; + } + var _ob = this._bezierTo(_x6, _y6, _x4, _y4, _x5, _y5, path, o); + ops = ops.concat(_ob); + path.bezierReflectionPoint = [_x5 + (_x5 - _x4), _y5 + (_y5 - _y4)]; + } + break; + } + case 'Q': + case 'q': + { + var _delta6 = seg.key === 'q'; + if (seg.data.length >= 4) { + var _x7 = +seg.data[0]; + var _y7 = +seg.data[1]; + var _x8 = +seg.data[2]; + var _y8 = +seg.data[3]; + if (_delta6) { + _x7 += path.x; + _x8 += path.x; + _y7 += path.y; + _y8 += path.y; + } + var offset1 = 1 * (1 + o.roughness * 0.2); + var offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + var f = [_x8 + this.getOffset(-offset1, offset1, o), _y8 + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [_x7 + this.getOffset(-offset1, offset1, o), _y7 + this.getOffset(-offset1, offset1, o), f[0], f[1]] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [_x8 + this.getOffset(-offset2, offset2, o), _y8 + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [_x7 + this.getOffset(-offset2, offset2, o), _y7 + this.getOffset(-offset2, offset2, o), f[0], f[1]] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [_x8 + (_x8 - _x7), _y8 + (_y8 - _y7)]; + } + break; + } + case 'T': + case 't': + { + var _delta7 = seg.key === 't'; + if (seg.data.length >= 2) { + var _x9 = +seg.data[0]; + var _y9 = +seg.data[1]; + if (_delta7) { + _x9 += path.x; + _y9 += path.y; + } + var _x10 = _x9; + var _y10 = _y9; + var _prevKey = prevSeg ? prevSeg.key : ''; + var _ref = null; + if (_prevKey === 'q' || _prevKey === 'Q' || _prevKey === 't' || _prevKey === 'T') { + _ref = path.quadReflectionPoint; + } + if (_ref) { + _x10 = _ref[0]; + _y10 = _ref[1]; + } + var _offset = 1 * (1 + o.roughness * 0.2); + var _offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-_offset, _offset, o), path.y + this.getOffset(-_offset, _offset, o)] }); + var _f = [_x9 + this.getOffset(-_offset, _offset, o), _y9 + this.getOffset(-_offset, _offset, o)]; + ops.push({ + op: 'qcurveTo', data: [_x10 + this.getOffset(-_offset, _offset, o), _y10 + this.getOffset(-_offset, _offset, o), _f[0], _f[1]] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-_offset2, _offset2, o), path.y + this.getOffset(-_offset2, _offset2, o)] }); + _f = [_x9 + this.getOffset(-_offset2, _offset2, o), _y9 + this.getOffset(-_offset2, _offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [_x10 + this.getOffset(-_offset2, _offset2, o), _y10 + this.getOffset(-_offset2, _offset2, o), _f[0], _f[1]] + }); + path.setPosition(_f[0], _f[1]); + path.quadReflectionPoint = [_x9 + (_x9 - _x10), _y9 + (_y9 - _y10)]; + } + break; + } + case 'A': + case 'a': + { + var _delta8 = seg.key === 'a'; + if (seg.data.length >= 7) { + var rx = +seg.data[0]; + var ry = +seg.data[1]; + var angle = +seg.data[2]; + var largeArcFlag = +seg.data[3]; + var sweepFlag = +seg.data[4]; + var _x11 = +seg.data[5]; + var _y11 = +seg.data[6]; + if (_delta8) { + _x11 += path.x; + _y11 += path.y; + } + if (_x11 === path.x && _y11 === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this.doubleLine(path.x, path.y, _x11, _y11, o)); + path.setPosition(_x11, _y11); + } else { + for (var i = 0; i < 1; i++) { + var arcConverter = new RoughArcConverter([path.x, path.y], [_x11, _y11], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + var segment = arcConverter.getNextSegment(); + while (segment) { + var _ob2 = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(_ob2); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } + }]); + return RoughRenderer; + }(); + + var hasSelf = typeof self !== 'undefined'; + var roughScript = hasSelf && self && self.document && self.document.currentScript && self.document.currentScript.src; + function createRenderer(config) { + if (hasSelf && roughScript && self && self.workly && config.async && !config.noWorker) { + var worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; + if (worklySource) { + var code = 'importScripts(\'' + worklySource + '\', \'' + roughScript + '\');\nworkly.expose(self.rough.createRenderer());'; + var ourl = URL.createObjectURL(new Blob([code])); + return self.workly.proxy(ourl); + } + } + return new RoughRenderer(); + } + + var hasSelf$1 = typeof self !== 'undefined'; + var RoughGenerator = function () { + function RoughGenerator(config, surface) { + classCallCheck(this, RoughGenerator); + + this.defaultOptions = { + maxRandomnessOffset: 2, + roughness: 1, + bowing: 1, + stroke: '#000', + strokeWidth: 1, + curveTightness: 0, + curveStepCount: 9, + fill: null, + fillStyle: 'hachure', + fillWeight: -1, + hachureAngle: -41, + hachureGap: -1 + }; + this.config = config || {}; + this.surface = surface; + this.renderer = createRenderer(this.config); + if (this.config.options) { + this.defaultOptions = this._options(this.config.options); + } + } + + createClass(RoughGenerator, [{ + key: '_options', + value: function _options(options) { + return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions; + } + }, { + key: '_drawable', + value: function _drawable(shape, sets, options) { + return { shape: shape, sets: sets || [], options: options || this.defaultOptions }; + } + }, { + key: 'getCanvasSize', + value: function getCanvasSize() { + var val = function val(w) { + if (w && (typeof w === 'undefined' ? 'undefined' : _typeof(w)) === 'object') { + if (w.baseVal && w.baseVal.value) { + return w.baseVal.value; + } + } + return w || 100; + }; + if (this.surface) { + return [val(this.surface.width), val(this.surface.height)]; + } + return [100, 100]; + } + }, { + key: 'computePolygonSize', + value: function computePolygonSize(points) { + if (points.length) { + var left = points[0][0]; + var right = points[0][0]; + var top = points[0][1]; + var bottom = points[0][1]; + for (var i = 1; i < points.length; i++) { + left = Math.min(left, points[i][0]); + right = Math.max(right, points[i][0]); + top = Math.min(top, points[i][1]); + bottom = Math.max(bottom, points[i][1]); + } + return [right - left, bottom - top]; + } + return [0, 0]; + } + }, { + key: 'polygonPath', + value: function polygonPath(points) { + var d = ''; + if (points.length) { + d = 'M' + points[0][0] + ',' + points[0][1]; + for (var i = 1; i < points.length; i++) { + d = d + ' L' + points[i][0] + ',' + points[i][1]; + } + } + return d; + } + }, { + key: 'computePathSize', + value: function computePathSize(d) { + var size = [0, 0]; + if (hasSelf$1 && self.document) { + try { + var ns = 'http://www.w3.org/2000/svg'; + var svg = self.document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + var pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + self.document.body.appendChild(svg); + var bb = pathNode.getBBox(); + if (bb) { + size[0] = bb.width || 0; + size[1] = bb.height || 0; + } + self.document.body.removeChild(svg); + } catch (err) {} + } + var canvasSize = this.getCanvasSize(); + if (!(size[0] * size[1])) { + size = canvasSize; + } + size[0] = Math.min(size[0], canvasSize[0]); + size[1] = Math.min(size[1], canvasSize[1]); + return size; + } + }, { + key: 'line', + value: function line(x1, y1, x2, y2, options) { + var o = this._options(options); + return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + var points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } else { + paths.push(this.lib.patternFillPolygon(points, o)); + } + } + paths.push(this.lib.rectangle(x, y, width, height, o)); + return this._drawable('rectangle', paths, o); + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + } + } + paths.push(this.lib.ellipse(x, y, width, height, o)); + return this._drawable('ellipse', paths, o); + } + }, { + key: 'circle', + value: function circle(x, y, diameter, options) { + var ret = this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + }, { + key: 'linearPath', + value: function linearPath(points, options) { + var o = this._options(options); + return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var o = this._options(options); + var paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + var shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + } + } + paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + return this._drawable('arc', paths, o); + } + }, { + key: 'curve', + value: function curve(points, options) { + var o = this._options(options); + return this._drawable('curve', [this.lib.curve(points, o)], o); + } + }, { + key: 'polygon', + value: function polygon(points, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push(this.lib.solidFillPolygon(points, o)); + } else { + var size = this.computePolygonSize(points); + var fillPoints = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var shape = this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push(this.lib.linearPath(points, true, o)); + return this._drawable('polygon', paths, o); + } + }, { + key: 'path', + value: function path(d, options) { + var o = this._options(options); + var paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } else { + var size = this.computePathSize(d); + var points = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var _shape = this.lib.patternFillPolygon(points, o); + _shape.type = 'path2Dpattern'; + _shape.size = size; + _shape.path = d; + paths.push(_shape); + } + } + paths.push(this.lib.svgPath(d, o)); + return this._drawable('path', paths, o); + } + }, { + key: 'toPaths', + value: function toPaths(drawable) { + var sets = drawable.sets || []; + var o = drawable.options || this.defaultOptions; + var paths = []; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = sets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var drawing = _step.value; + + var path = null; + switch (drawing.type) { + case 'path': + path = { + d: this.opsToPath(drawing), + stroke: o.stroke, + strokeWidth: o.strokeWidth, + fill: 'none' + }; + break; + case 'fillPath': + path = { + d: this.opsToPath(drawing), + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'fillSketch': + path = this.fillSketch(drawing, o); + break; + case 'path2Dfill': + path = { + d: drawing.path || '', + stroke: 'none', + strokeWidth: 0, + fill: o.fill || 'none' + }; + break; + case 'path2Dpattern': + { + var size = drawing.size; + var pattern = { + x: 0, y: 0, width: 1, height: 1, + viewBox: '0 0 ' + Math.round(size[0]) + ' ' + Math.round(size[1]), + patternUnits: 'objectBoundingBox', + path: this.fillSketch(drawing, o) + }; + path = { + d: drawing.path, + stroke: 'none', + strokeWidth: 0, + pattern: pattern + }; + break; + } + } + if (path) { + paths.push(path); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return paths; + } + }, { + key: 'fillSketch', + value: function fillSketch(drawing, o) { + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + return { + d: this.opsToPath(drawing), + stroke: o.fill || 'none', + strokeWidth: fweight, + fill: 'none' + }; + } + }, { + key: 'opsToPath', + value: function opsToPath(drawing) { + var path = ''; + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = drawing.ops[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var item = _step2.value; + + var 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 'qcurveTo': + path += 'Q' + data[0] + ' ' + data[1] + ', ' + data[2] + ' ' + data[3] + ' '; + break; + case 'lineTo': + path += 'L' + data[0] + ' ' + data[1] + ' '; + break; + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + return path.trim(); + } + }, { + key: 'lib', + get: function get$$1() { + return this.renderer; + } + }]); + return RoughGenerator; + }(); + + var hasDocument = typeof document !== 'undefined'; + var RoughCanvas = function () { + function RoughCanvas(canvas, config) { + classCallCheck(this, RoughCanvas); + + this.canvas = canvas; + this.ctx = this.canvas.getContext('2d'); + this.gen = new RoughGenerator(config || null, this.canvas); + } + + createClass(RoughCanvas, [{ + key: 'line', + value: function line(x1, y1, x2, y2, options) { + var d = this.gen.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, options) { + var d = this.gen.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, options) { + var d = this.gen.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + }, { + key: 'circle', + value: function circle(x, y, diameter, options) { + var d = this.gen.circle(x, y, diameter, options); + this.draw(d); + return d; + } + }, { + key: 'linearPath', + value: function linearPath(points, options) { + var d = this.gen.linearPath(points, options); + this.draw(d); + return d; + } + }, { + key: 'polygon', + value: function polygon(points, options) { + var d = this.gen.polygon(points, options); + this.draw(d); + return d; + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = this.gen.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + }, { + key: 'curve', + value: function curve(points, options) { + var d = this.gen.curve(points, options); + this.draw(d); + return d; + } + }, { + key: 'path', + value: function path(d, options) { + var drawing = this.gen.path(d, options); + this.draw(drawing); + return drawing; + } + }, { + key: 'draw', + value: function draw(drawable) { + var sets = drawable.sets || []; + var o = drawable.options || this.gen.defaultOptions; + var ctx = this.ctx; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = sets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var drawing = _step.value; + + switch (drawing.type) { + case 'path': + ctx.save(); + ctx.strokeStyle = o.stroke; + ctx.lineWidth = o.strokeWidth; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillPath': + ctx.save(); + ctx.fillStyle = o.fill || ''; + this._drawToContext(ctx, drawing); + ctx.restore(); + break; + case 'fillSketch': + this.fillSketch(ctx, drawing, o); + break; + case 'path2Dfill': + { + this.ctx.save(); + this.ctx.fillStyle = o.fill || ''; + var p2d = new Path2D(drawing.path); + this.ctx.fill(p2d); + this.ctx.restore(); + break; + } + case 'path2Dpattern': + { + if (hasDocument) { + var size = drawing.size; + var hcanvas = document.createElement('canvas'); + var hcontext = hcanvas.getContext('2d'); + var bbox = this.computeBBox(drawing.path); + if (bbox && (bbox.width || bbox.height)) { + hcanvas.width = this.canvas.width; + hcanvas.height = this.canvas.height; + hcontext.translate(bbox.x || 0, bbox.y || 0); + } else { + hcanvas.width = size[0]; + hcanvas.height = size[1]; + } + this.fillSketch(hcontext, drawing, o); + this.ctx.save(); + this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); + var _p2d = new Path2D(drawing.path); + this.ctx.fill(_p2d); + this.ctx.restore(); + } + break; + } + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + } + }, { + key: 'computeBBox', + value: function computeBBox(d) { + if (hasDocument) { + try { + var ns = 'http://www.w3.org/2000/svg'; + var svg = document.createElementNS(ns, 'svg'); + svg.setAttribute('width', '0'); + svg.setAttribute('height', '0'); + var pathNode = self.document.createElementNS(ns, 'path'); + pathNode.setAttribute('d', d); + svg.appendChild(pathNode); + document.body.appendChild(svg); + var bbox = pathNode.getBBox(); + document.body.removeChild(svg); + return bbox; + } catch (err) {} + } + return null; + } + }, { + key: 'fillSketch', + value: function fillSketch(ctx, drawing, o) { + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + ctx.save(); + ctx.strokeStyle = o.fill || ''; + ctx.lineWidth = fweight; + this._drawToContext(ctx, drawing); + ctx.restore(); + } + }, { + key: '_drawToContext', + value: function _drawToContext(ctx, drawing) { + ctx.beginPath(); + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = drawing.ops[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var item = _step2.value; + + var 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 'qcurveTo': + ctx.quadraticCurveTo(data[0], data[1], data[2], data[3]); + break; + case 'lineTo': + ctx.lineTo(data[0], data[1]); + break; + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + if (drawing.type === 'fillPath') { + ctx.fill(); + } else { + ctx.stroke(); + } + } + }, { + key: 'generator', + get: function get$$1() { + return this.gen; + } + }], [{ + key: 'createRenderer', + value: function createRenderer() { + return new RoughRenderer(); + } + }]); + return RoughCanvas; + }(); + + var RoughGeneratorAsync = function (_RoughGenerator) { + inherits(RoughGeneratorAsync, _RoughGenerator); + + function RoughGeneratorAsync() { + classCallCheck(this, RoughGeneratorAsync); + return possibleConstructorReturn(this, (RoughGeneratorAsync.__proto__ || Object.getPrototypeOf(RoughGeneratorAsync)).apply(this, arguments)); + } + + createClass(RoughGeneratorAsync, [{ + key: 'line', + + // @ts-ignore + value: async function line(x1, y1, x2, y2, options) { + var o = this._options(options); + return this._drawable('line', [await this.lib.line(x1, y1, x2, y2, o)], o); + } + // @ts-ignore + + }, { + key: 'rectangle', + value: async function rectangle(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + var points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; + if (o.fillStyle === 'solid') { + paths.push((await this.lib.solidFillPolygon(points, o))); + } else { + paths.push((await this.lib.patternFillPolygon(points, o))); + } + } + paths.push((await this.lib.rectangle(x, y, width, height, o))); + return this._drawable('rectangle', paths, o); + } + // @ts-ignore + + }, { + key: 'ellipse', + value: async function ellipse(x, y, width, height, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = await this.lib.ellipse(x, y, width, height, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push((await this.lib.patternFillEllipse(x, y, width, height, o))); + } + } + paths.push((await this.lib.ellipse(x, y, width, height, o))); + return this._drawable('ellipse', paths, o); + } + // @ts-ignore + + }, { + key: 'circle', + value: async function circle(x, y, diameter, options) { + var ret = await this.ellipse(x, y, diameter, diameter, options); + ret.shape = 'circle'; + return ret; + } + // @ts-ignore + + }, { + key: 'linearPath', + value: async function linearPath(points, options) { + var o = this._options(options); + return this._drawable('linearPath', [await this.lib.linearPath(points, false, o)], o); + } + // @ts-ignore + + }, { + key: 'arc', + value: async function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var o = this._options(options); + var paths = []; + if (closed && o.fill) { + if (o.fillStyle === 'solid') { + var shape = await this.lib.arc(x, y, width, height, start, stop, true, false, o); + shape.type = 'fillPath'; + paths.push(shape); + } else { + paths.push((await this.lib.patternFillArc(x, y, width, height, start, stop, o))); + } + } + paths.push((await this.lib.arc(x, y, width, height, start, stop, closed, true, o))); + return this._drawable('arc', paths, o); + } + // @ts-ignore + + }, { + key: 'curve', + value: async function curve(points, options) { + var o = this._options(options); + return this._drawable('curve', [await this.lib.curve(points, o)], o); + } + // @ts-ignore + + }, { + key: 'polygon', + value: async function polygon(points, options) { + var o = this._options(options); + var paths = []; + if (o.fill) { + if (o.fillStyle === 'solid') { + paths.push((await this.lib.solidFillPolygon(points, o))); + } else { + var size = this.computePolygonSize(points); + var fillPoints = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var shape = await this.lib.patternFillPolygon(fillPoints, o); + shape.type = 'path2Dpattern'; + shape.size = size; + shape.path = this.polygonPath(points); + paths.push(shape); + } + } + paths.push((await this.lib.linearPath(points, true, o))); + return this._drawable('polygon', paths, o); + } + // @ts-ignore + + }, { + key: 'path', + value: async function path(d, options) { + var o = this._options(options); + var paths = []; + if (!d) { + return this._drawable('path', paths, o); + } + if (o.fill) { + if (o.fillStyle === 'solid') { + var shape = { type: 'path2Dfill', path: d, ops: [] }; + paths.push(shape); + } else { + var size = this.computePathSize(d); + var points = [[0, 0], [size[0], 0], [size[0], size[1]], [0, size[1]]]; + var _shape = await this.lib.patternFillPolygon(points, o); + _shape.type = 'path2Dpattern'; + _shape.size = size; + _shape.path = d; + paths.push(_shape); + } + } + paths.push((await this.lib.svgPath(d, o))); + return this._drawable('path', paths, o); + } + }]); + return RoughGeneratorAsync; + }(RoughGenerator); + + var RoughCanvasAsync = function (_RoughCanvas) { + inherits(RoughCanvasAsync, _RoughCanvas); + + function RoughCanvasAsync(canvas, config) { + classCallCheck(this, RoughCanvasAsync); + + var _this = possibleConstructorReturn(this, (RoughCanvasAsync.__proto__ || Object.getPrototypeOf(RoughCanvasAsync)).call(this, canvas, config)); + + _this.genAsync = new RoughGeneratorAsync(config || null, _this.canvas); + return _this; + } + // @ts-ignore + + + createClass(RoughCanvasAsync, [{ + key: 'line', + + // @ts-ignore + value: async function line(x1, y1, x2, y2, options) { + var d = await this.genAsync.line(x1, y1, x2, y2, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'rectangle', + value: async function rectangle(x, y, width, height, options) { + var d = await this.genAsync.rectangle(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'ellipse', + value: async function ellipse(x, y, width, height, options) { + var d = await this.genAsync.ellipse(x, y, width, height, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'circle', + value: async function circle(x, y, diameter, options) { + var d = await this.genAsync.circle(x, y, diameter, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'linearPath', + value: async function linearPath(points, options) { + var d = await this.genAsync.linearPath(points, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'polygon', + value: async function polygon(points, options) { + var d = await this.genAsync.polygon(points, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'arc', + value: async function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'curve', + value: async function curve(points, options) { + var d = await this.genAsync.curve(points, options); + this.draw(d); + return d; + } + // @ts-ignore + + }, { + key: 'path', + value: async function path(d, options) { + var drawing = await this.genAsync.path(d, options); + this.draw(drawing); + return drawing; + } + }, { + key: 'generator', + get: function get$$1() { + return this.genAsync; + } + }]); + return RoughCanvasAsync; + }(RoughCanvas); + + var hasDocument$1 = typeof document !== 'undefined'; + var RoughSVG = function () { + function RoughSVG(svg, config) { + classCallCheck(this, RoughSVG); + + this.svg = svg; + this.gen = new RoughGenerator(config || null, this.svg); + } + + createClass(RoughSVG, [{ + key: 'line', + value: function line(x1, y1, x2, y2, options) { + var d = this.gen.line(x1, y1, x2, y2, options); + return this.draw(d); + } + }, { + key: 'rectangle', + value: function rectangle(x, y, width, height, options) { + var d = this.gen.rectangle(x, y, width, height, options); + return this.draw(d); + } + }, { + key: 'ellipse', + value: function ellipse(x, y, width, height, options) { + var d = this.gen.ellipse(x, y, width, height, options); + return this.draw(d); + } + }, { + key: 'circle', + value: function circle(x, y, diameter, options) { + var d = this.gen.circle(x, y, diameter, options); + return this.draw(d); + } + }, { + key: 'linearPath', + value: function linearPath(points, options) { + var d = this.gen.linearPath(points, options); + return this.draw(d); + } + }, { + key: 'polygon', + value: function polygon(points, options) { + var d = this.gen.polygon(points, options); + return this.draw(d); + } + }, { + key: 'arc', + value: function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = this.gen.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + }, { + key: 'curve', + value: function curve(points, options) { + var d = this.gen.curve(points, options); + return this.draw(d); + } + }, { + key: 'path', + value: function path(d, options) { + var drawing = this.gen.path(d, options); + return this.draw(drawing); + } + }, { + key: 'draw', + value: function draw(drawable) { + var sets = drawable.sets || []; + var o = drawable.options || this.gen.defaultOptions; + var doc = this.svg.ownerDocument || hasDocument$1 && document; + var g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = sets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var drawing = _step.value; + + var path = null; + switch (drawing.type) { + case 'path': + { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.stroke; + path.style.strokeWidth = o.strokeWidth + ''; + path.style.fill = 'none'; + break; + } + case 'fillPath': + { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'fillSketch': + { + path = this.fillSketch(doc, drawing, o); + break; + } + case 'path2Dfill': + { + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = o.fill; + break; + } + case 'path2Dpattern': + { + var size = drawing.size; + var pattern = doc.createElementNS('http://www.w3.org/2000/svg', 'pattern'); + var id = 'rough-' + Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER || 999999)); + pattern.setAttribute('id', id); + pattern.setAttribute('x', '0'); + pattern.setAttribute('y', '0'); + pattern.setAttribute('width', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('height', '1'); + pattern.setAttribute('viewBox', '0 0 ' + Math.round(size[0]) + ' ' + Math.round(size[1])); + pattern.setAttribute('patternUnits', 'objectBoundingBox'); + var patternPath = this.fillSketch(doc, drawing, o); + pattern.appendChild(patternPath); + this.defs.appendChild(pattern); + path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', drawing.path || ''); + path.style.stroke = 'none'; + path.style.strokeWidth = '0'; + path.style.fill = 'url(#' + id + ')'; + break; + } + } + if (path) { + g.appendChild(path); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return g; + } + }, { + key: 'opsToPath', + value: function opsToPath(drawing) { + return this.gen.opsToPath(drawing); + } + }, { + key: 'fillSketch', + value: function fillSketch(doc, drawing, o) { + var fweight = o.fillWeight; + if (fweight < 0) { + fweight = o.strokeWidth / 2; + } + var path = doc.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', this.opsToPath(drawing)); + path.style.stroke = o.fill; + path.style.strokeWidth = fweight + ''; + path.style.fill = 'none'; + return path; + } + }, { + key: 'generator', + get: function get$$1() { + return this.gen; + } + }, { + key: 'defs', + get: function get$$1() { + if (hasDocument$1) { + if (!this._defs) { + var doc = this.svg.ownerDocument || document; + var dnode = doc.createElementNS('http://www.w3.org/2000/svg', 'defs'); + if (this.svg.firstChild) { + this.svg.insertBefore(dnode, this.svg.firstChild); + } else { + this.svg.appendChild(dnode); + } + this._defs = dnode; + } + } + return this._defs || null; + } + }], [{ + key: 'createRenderer', + value: function createRenderer() { + return new RoughRenderer(); + } + }]); + return RoughSVG; + }(); + + var RoughSVGAsync = function (_RoughSVG) { + inherits(RoughSVGAsync, _RoughSVG); + + function RoughSVGAsync(svg, config) { + classCallCheck(this, RoughSVGAsync); + + var _this = possibleConstructorReturn(this, (RoughSVGAsync.__proto__ || Object.getPrototypeOf(RoughSVGAsync)).call(this, svg, config)); + + _this.genAsync = new RoughGeneratorAsync(config || null, _this.svg); + return _this; + } + // @ts-ignore + + + createClass(RoughSVGAsync, [{ + key: 'line', + + // @ts-ignore + value: async function line(x1, y1, x2, y2, options) { + var d = await this.genAsync.line(x1, y1, x2, y2, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'rectangle', + value: async function rectangle(x, y, width, height, options) { + var d = await this.genAsync.rectangle(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'ellipse', + value: async function ellipse(x, y, width, height, options) { + var d = await this.genAsync.ellipse(x, y, width, height, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'circle', + value: async function circle(x, y, diameter, options) { + var d = await this.genAsync.circle(x, y, diameter, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'linearPath', + value: async function linearPath(points, options) { + var d = await this.genAsync.linearPath(points, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'polygon', + value: async function polygon(points, options) { + var d = await this.genAsync.polygon(points, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'arc', + value: async function arc(x, y, width, height, start, stop) { + var closed = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; + var options = arguments[7]; + + var d = await this.genAsync.arc(x, y, width, height, start, stop, closed, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'curve', + value: async function curve(points, options) { + var d = await this.genAsync.curve(points, options); + return this.draw(d); + } + // @ts-ignore + + }, { + key: 'path', + value: async function path(d, options) { + var drawing = await this.genAsync.path(d, options); + return this.draw(drawing); + } + }, { + key: 'generator', + get: function get$$1() { + return this.genAsync; + } + }]); + return RoughSVGAsync; + }(RoughSVG); + + var rough = { + canvas: function canvas(_canvas, config) { + if (config && config.async) { + return new RoughCanvasAsync(_canvas, config); + } + return new RoughCanvas(_canvas, config); + }, + svg: function svg(_svg, config) { + if (config && config.async) { + return new RoughSVGAsync(_svg, config); + } + return new RoughSVG(_svg, config); + }, + createRenderer: function createRenderer() { + return RoughCanvas.createRenderer(); + }, + generator: function generator(config, surface) { + if (config && config.async) { + return new RoughGeneratorAsync(config, surface); + } + return new RoughGenerator(config, surface); + } + }; + + return rough; + +}))); diff --git a/dist/rough.umd.es5.min.js b/dist/rough.umd.es5.min.js new file mode 100644 index 0000000..0b5bdb3 --- /dev/null +++ b/dist/rough.umd.es5.min.js @@ -0,0 +1 @@ +(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){var l=t.fillStyle||'hachure';if(!T[l])switch(l){case'zigzag':T[l]||(T[l]=new S(e));break;case'cross-hatch':T[l]||(T[l]=new A(e));break;case'hachure':default:l='hachure',T[l]||(T[l]=new O(e));}return T[l]}function l(e){if(z&&E&&self&&self.workly&&e.async&&!e.noWorker){var t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){var l=URL.createObjectURL(new Blob(['importScripts(\''+t+'\', \''+E+'\');\nworkly.expose(self.rough.createRenderer());']));return self.workly.proxy(l)}}return new C}var a=Math.round,n=Math.max,s=Math.min,i=Math.pow,r=Math.floor,p=Math.sqrt,d=Math.cos,h=Math.sin,g=Math.abs,f=Math.PI,o='function'==typeof Symbol&&'symbol'==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&'function'==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?'symbol':typeof e},u=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},y=function(){function e(e,t){for(var l,a=0;a_){var k=p(1-_/(this._rx*this._rx*this._ry*this._ry));this._rx*=k,this._ry*=k,v=0}else v=(i===o?-1:1)*p(_/(this._rx*this._rx*c*c+this._ry*this._ry*y*y));var s=v*this._rx*c/this._ry,x=-v*this._ry*y/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*s-this._sinPhi*x+(t[0]+l[0])/2,this._C[1]=this._sinPhi*s+this._cosPhi*x+(t[1]+l[1])/2,this._theta=this.calculateVectorAngle(1,0,(y-s)/this._rx,(c-x)/this._ry);var b=this.calculateVectorAngle((y-s)/this._rx,(c-x)/this._ry,(-y-s)/this._rx,(-c-x)/this._ry);!o&&0b&&(b+=2*f),this._numSegs=Math.ceil(g(b/(f/2))),this._delta=b/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}}return y(e,[{key:'getNextSegment',value:function(){if(this._segIndex===this._numSegs)return null;var e=d(this._theta),t=h(this._theta),l=this._theta+this._delta,a=d(l),n=h(l),i=[this._cosPhi*this._rx*a-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*a+this._cosPhi*this._ry*n+this._C[1]],s=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[i[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*a),i[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*a)];return this._theta=l,this._from=[i[0],i[1]],this._segIndex++,{cp1:s,cp2:o,to:i}}},{key:'calculateVectorAngle',value:function(e,t,l,a){var n=Math.atan2,i=n(t,e),s=n(a,l);return s>=i?s-i:2*f-(i-s)}}]),e}(),m=function(){function e(t,l){u(this,e),this.sets=t,this.closed=l}return y(e,[{key:'fit',value:function(e){var t=[],l=!0,a=!1,n=void 0;try{for(var s,o=this.sets[Symbol.iterator]();!(l=(s=o.next()).done);l=!0){var p=s.value,h=p.length,g=r(e*h);if(5>g){if(5>=h)continue;g=5}t.push(this.reduce(p,g))}}catch(e){a=!0,n=e}finally{try{!l&&o.return&&o.return()}finally{if(a)throw n}}var f='',u=!0,y=!1,c=void 0;try{for(var v,_,k=t[Symbol.iterator]();!(u=(v=k.next()).done);u=!0){_=v.value;for(var x,b=0;b<_.length;b++)x=_[b],f+=0===b?'M'+x[0]+','+x[1]:'L'+x[0]+','+x[1];this.closed&&(f+='z ')}}catch(e){y=!0,c=e}finally{try{!u&&k.return&&k.return()}finally{if(y)throw c}}return f}},{key:'distance',value:function(e,t){return p(i(e[0]-t[0],2)+i(e[1]-t[1],2))}},{key:'reduce',value:function(e,t){if(e.length<=t)return e;for(var l=e.slice(0);l.length>t;){for(var n=-1,o=-1,r=1;rn||s=s(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=s(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+r,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===t?(this.xi=e.px1,this.yi=l*this.xi+o,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(p))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):l===i?o==r&&(this.px1>=s(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=s(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(r-o)/(l-i),this.yi=l*this.xi+o,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}]),e}(),P=function(){function e(t,l,a,n,i,s,o,r){u(this,e),this.deltaX=0,this.hGap=0,this.top=t,this.bottom=l,this.left=a,this.right=n,this.gap=i,this.sinAngle=s,this.tanAngle=r,1e-4>g(s)?this.pos=a+i:.9999g(this.sinAngle)){if(this.posthis.right&&l>this.right;)if(this.pos+=this.hGap,t=this.pos-this.deltaX/2,l=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;var i=new w([t,a],[l,n]);this.sLeft&&i.intersects(this.sLeft)&&(t=i.xi,a=i.yi),this.sRight&&i.intersects(this.sRight)&&(l=i.xi,n=i.yi),0v&&(v=4*l.strokeWidth),v=n(v,.1);for(var _=c%180*(f/180),k=d(_),x=h(_),b=t(_),m=new P(g-1,u+1,r-1,p+1,v,x,k,b),w=void 0,O=null;null!=(w=m.nextLine());)for(var S=this.getIntersectingLines(w,e),A=0;A=u&&(u=4*i.strokeWidth);var y=i.fillWeight;0>y&&(y=i.strokeWidth/2);for(var c=t(h%180*(f/180)),v=d/r,_=p(v*c*v*c+1),k=v*c/_,x=1/_,b=u/(r*d/p(d*x*(d*x)+r*k*(r*k))/r),m=p(r*r-(e-r+b)*(e-r+b)),w=null,P=e-r+b;P_;)_+=2*f,k+=2*f;k-_>2*f&&(_=0,k=2*f);var x=2*f/u.curveStepCount,b=s(x/2,(k-_)/2),m=this._arc(b,o,y,c,v,_,k,1,u),w=this._arc(b,o,y,c,v,_,k,1.5,u),P=m.concat(w);return r&&(p?(P=P.concat(this.doubleLine(o,y,o+c*d(_),y+v*h(_),u)),P=P.concat(this.doubleLine(o,y,o+c*d(k),y+v*h(k),u))):(P.push({op:'lineTo',data:[o,y]}),P.push({op:'lineTo',data:[o+c*d(_),y+v*h(_)]}))),{type:'path',ops:P}}},{key:'svgPath',value:function(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');var l=new x(e);if(t.simplification){var a=new m(l.linearPoints,l.closed),n=a.fit(t.simplification);l=new x(n)}for(var o=[],r=l.segments||[],d=0;dy;)y+=2*f,c+=2*f;c-y>2*f&&(y=0,c=2*f);for(var v=(c-y)/s.curveStepCount,_=[],k=y;k<=c;k+=v)_.push([o+p*d(k),r+u*h(k)]);return _.push([o+p*d(c),r+u*h(c)]),_.push([o,r]),this.patternFillPolygon(_,s)}},{key:'getOffset',value:function(e,t,l){return l.roughness*(Math.random()*(t-e)+e)}},{key:'doubleLine',value:function(e,t,l,a,n){var i=this._line(e,t,l,a,n,!0,!1),s=this._line(e,t,l,a,n,!0,!0);return i.concat(s)}},{key:'_line',value:function(e,t,l,a,n,s,o){var r=i(e-l,2)+i(t-a,2),d=n.maxRandomnessOffset||0;100*(d*d)>r&&(d=p(r)/10);var h=d/2,g=.2+.2*Math.random(),f=n.bowing*n.maxRandomnessOffset*(a-t)/200,u=n.bowing*n.maxRandomnessOffset*(e-l)/200;f=this.getOffset(-f,f,n),u=this.getOffset(-u,u,n);var y=[];return s&&(o?y.push({op:'move',data:[e+this.getOffset(-h,h,n),t+this.getOffset(-h,h,n)]}):y.push({op:'move',data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?y.push({op:'bcurveTo',data:[f+e+(l-e)*g+this.getOffset(-h,h,n),u+t+(a-t)*g+this.getOffset(-h,h,n),f+e+2*(l-e)*g+this.getOffset(-h,h,n),u+t+2*(a-t)*g+this.getOffset(-h,h,n),l+this.getOffset(-h,h,n),a+this.getOffset(-h,h,n)]}):y.push({op:'bcurveTo',data:[f+e+(l-e)*g+this.getOffset(-d,d,n),u+t+(a-t)*g+this.getOffset(-d,d,n),f+e+2*(l-e)*g+this.getOffset(-d,d,n),u+t+2*(a-t)*g+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),a+this.getOffset(-d,d,n)]}),y}},{key:'_curve',value:function(e,t,l){var a=e.length,n=[];if(3g;g++)0===g?o.push({op:'move',data:[r.x,r.y]}):o.push({op:'move',data:[r.x+this.getOffset(-d[0],d[0],p),r.y+this.getOffset(-d[0],d[0],p)]}),h=[n+this.getOffset(-d[g],d[g],p),s+this.getOffset(-d[g],d[g],p)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[g],d[g],p),t+this.getOffset(-d[g],d[g],p),l+this.getOffset(-d[g],d[g],p),a+this.getOffset(-d[g],d[g],p),h[0],h[1]]});return r.setPosition(h[0],h[1]),o}},{key:'_processSegment',value:function(e,t,l,a){var n=[];switch(t.key){case'M':case'm':{var s='m'===t.key;if(2<=t.data.length){var o=+t.data[0],r=+t.data[1];s&&(o+=e.x,r+=e.y);var p=1*(a.maxRandomnessOffset||0);o+=this.getOffset(-p,p,a),r+=this.getOffset(-p,p,a),e.setPosition(o,r),n.push({op:'move',data:[o,r]})}break}case'L':case'l':{var d='l'===t.key;if(2<=t.data.length){var h=+t.data[0],g=+t.data[1];d&&(h+=e.x,g+=e.y),n=n.concat(this.doubleLine(e.x,e.y,h,g,a)),e.setPosition(h,g)}break}case'H':case'h':{var u='h'===t.key;if(t.data.length){var c=+t.data[0];u&&(c+=e.x),n=n.concat(this.doubleLine(e.x,e.y,c,e.y,a)),e.setPosition(c,e.y)}break}case'V':case'v':{var v='v'===t.key;if(t.data.length){var _=+t.data[0];v&&(_+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,_,a)),e.setPosition(e.x,_)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],a)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{var k='c'===t.key;if(6<=t.data.length){var m=+t.data[0],w=+t.data[1],P=+t.data[2],O=+t.data[3],S=+t.data[4],A=+t.data[5];k&&(m+=e.x,P+=e.x,S+=e.x,w+=e.y,O+=e.y,A+=e.y);var T=this._bezierTo(m,w,P,O,S,A,e,a);n=n.concat(T),e.bezierReflectionPoint=[S+(S-P),A+(A-O)]}break}case'S':case's':{var C='s'===t.key;if(4<=t.data.length){var z=+t.data[0],E=+t.data[1],L=+t.data[2],R=+t.data[3];C&&(z+=e.x,L+=e.x,E+=e.y,R+=e.y);var W=z,N=E,D=l?l.key:'',B=null;('c'===D||'C'===D||'s'===D||'S'===D)&&(B=e.bezierReflectionPoint),B&&(W=B[0],N=B[1]);var F=this._bezierTo(W,N,z,E,L,R,e,a);n=n.concat(F),e.bezierReflectionPoint=[L+(L-z),R+(R-E)]}break}case'Q':case'q':{var M='q'===t.key;if(4<=t.data.length){var q=+t.data[0],U=+t.data[1],X=+t.data[2],V=+t.data[3];M&&(q+=e.x,X+=e.x,U+=e.y,V+=e.y);var G=1*(1+.2*a.roughness),j=1.5*(1+.22*a.roughness);n.push({op:'move',data:[e.x+this.getOffset(-G,G,a),e.y+this.getOffset(-G,G,a)]});var I=[X+this.getOffset(-G,G,a),V+this.getOffset(-G,G,a)];n.push({op:'qcurveTo',data:[q+this.getOffset(-G,G,a),U+this.getOffset(-G,G,a),I[0],I[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-j,j,a),e.y+this.getOffset(-j,j,a)]}),I=[X+this.getOffset(-j,j,a),V+this.getOffset(-j,j,a)],n.push({op:'qcurveTo',data:[q+this.getOffset(-j,j,a),U+this.getOffset(-j,j,a),I[0],I[1]]}),e.setPosition(I[0],I[1]),e.quadReflectionPoint=[X+(X-q),V+(V-U)]}break}case'T':case't':{var Q='t'===t.key;if(2<=t.data.length){var $=+t.data[0],Z=+t.data[1];Q&&($+=e.x,Z+=e.y);var H=$,J=Z,Y=l?l.key:'',K=null;('q'===Y||'Q'===Y||'t'===Y||'T'===Y)&&(K=e.quadReflectionPoint),K&&(H=K[0],J=K[1]);var ee=1*(1+.2*a.roughness),te=1.5*(1+.22*a.roughness);n.push({op:'move',data:[e.x+this.getOffset(-ee,ee,a),e.y+this.getOffset(-ee,ee,a)]});var le=[$+this.getOffset(-ee,ee,a),Z+this.getOffset(-ee,ee,a)];n.push({op:'qcurveTo',data:[H+this.getOffset(-ee,ee,a),J+this.getOffset(-ee,ee,a),le[0],le[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-te,te,a),e.y+this.getOffset(-te,te,a)]}),le=[$+this.getOffset(-te,te,a),Z+this.getOffset(-te,te,a)],n.push({op:'qcurveTo',data:[H+this.getOffset(-te,te,a),J+this.getOffset(-te,te,a),le[0],le[1]]}),e.setPosition(le[0],le[1]),e.quadReflectionPoint=[$+($-H),Z+(Z-J)]}break}case'A':case'a':{var ae='a'===t.key;if(7<=t.data.length){var ne=+t.data[0],ie=+t.data[1],se=+t.data[2],oe=+t.data[3],re=+t.data[4],pe=+t.data[5],de=+t.data[6];if(ae&&(pe+=e.x,de+=e.y),pe===e.x&&de===e.y)break;if(0==ne||0==ie)n=n.concat(this.doubleLine(e.x,e.y,pe,de,a)),e.setPosition(pe,de);else for(var he=0;1>he;he++)for(var ge,fe=new b([e.x,e.y],[pe,de],[ne,ie],se,!!oe,!!re),ue=fe.getNextSegment();ue;)ge=this._bezierTo(ue.cp1[0],ue.cp1[1],ue.cp2[0],ue.cp2[1],ue.to[0],ue.to[1],e,a),n=n.concat(ge),ue=fe.getNextSegment()}break}default:}return n}}]),e}(),z='undefined'!=typeof self,E=z&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,L='undefined'!=typeof self,R=function(){function e(t,a){u(this,e),this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=t||{},this.surface=a,this.renderer=l(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}return y(e,[{key:'_options',value:function(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}},{key:'_drawable',value:function(e,t,l){return{shape:e,sets:t||[],options:l||this.defaultOptions}}},{key:'getCanvasSize',value:function(){var e=function(e){return e&&'object'===('undefined'==typeof e?'undefined':o(e))&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100};return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}},{key:'computePolygonSize',value:function(e){if(e.length){for(var t=e[0][0],l=e[0][0],a=e[0][1],o=e[0][1],r=1;rl&&(l=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:l,fill:'none'}}},{key:'opsToPath',value:function(e){var t='',l=!0,a=!1,n=void 0;try{for(var i,s=e.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case'move':t+='M'+r[0]+' '+r[1]+' ';break;case'bcurveTo':t+='C'+r[0]+' '+r[1]+', '+r[2]+' '+r[3]+', '+r[4]+' '+r[5]+' ';break;case'qcurveTo':t+='Q'+r[0]+' '+r[1]+', '+r[2]+' '+r[3]+' ';break;case'lineTo':t+='L'+r[0]+' '+r[1]+' ';}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}return t.trim()}},{key:'lib',get:function(){return this.renderer}}]),e}(),W='undefined'!=typeof document,N=function(){function e(t,l){u(this,e),this.canvas=t,this.ctx=this.canvas.getContext('2d'),this.gen=new R(l||null,this.canvas)}return y(e,[{key:'line',value:function(e,t,l,a,n){var i=this.gen.line(e,t,l,a,n);return this.draw(i),i}},{key:'rectangle',value:function(e,t,l,a,n){var i=this.gen.rectangle(e,t,l,a,n);return this.draw(i),i}},{key:'ellipse',value:function(e,t,l,a,n){var i=this.gen.ellipse(e,t,l,a,n);return this.draw(i),i}},{key:'circle',value:function(e,t,l,a){var n=this.gen.circle(e,t,l,a);return this.draw(n),n}},{key:'linearPath',value:function(e,t){var l=this.gen.linearPath(e,t);return this.draw(l),l}},{key:'polygon',value:function(e,t){var l=this.gen.polygon(e,t);return this.draw(l),l}},{key:'arc',value:function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2),e.save(),e.strokeStyle=l.fill||'',e.lineWidth=a,this._drawToContext(e,t),e.restore()}},{key:'_drawToContext',value:function(e,t){e.beginPath();var l=!0,a=!1,n=void 0;try{for(var i,s=t.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case'move':e.moveTo(r[0],r[1]);break;case'bcurveTo':e.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);break;case'qcurveTo':e.quadraticCurveTo(r[0],r[1],r[2],r[3]);break;case'lineTo':e.lineTo(r[0],r[1]);}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}'fillPath'===t.type?e.fill():e.stroke()}},{key:'generator',get:function(){return this.gen}}],[{key:'createRenderer',value:function(){return new C}}]),e}(),D=function(e){function t(){return u(this,t),v(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return c(t,e),y(t,[{key:'line',value:async function(e,t,l,a,n){var i=this._options(n);return this._drawable('line',[await this.lib.line(e,t,l,a,i)],i)}},{key:'rectangle',value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill){var o=[[e,t],[e+l,t],[e+l,t+a],[e,t+a]];'solid'===i.fillStyle?s.push((await this.lib.solidFillPolygon(o,i))):s.push((await this.lib.patternFillPolygon(o,i)))}return s.push((await this.lib.rectangle(e,t,l,a,i))),this._drawable('rectangle',s,i)}},{key:'ellipse',value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill)if('solid'===i.fillStyle){var o=await this.lib.ellipse(e,t,l,a,i);o.type='fillPath',s.push(o)}else s.push((await this.lib.patternFillEllipse(e,t,l,a,i)));return s.push((await this.lib.ellipse(e,t,l,a,i))),this._drawable('ellipse',s,i)}},{key:'circle',value:async function(e,t,l,a){var n=await this.ellipse(e,t,l,l,a);return n.shape='circle',n}},{key:'linearPath',value:async function(e,t){var l=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,l)],l)}},{key:'arc',value:async function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2);var n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=l.fill,n.style.strokeWidth=a+'',n.style.fill='none',n}},{key:'generator',get:function(){return this.gen}},{key:'defs',get:function(){if(F&&!this._defs){var e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}}],[{key:'createRenderer',value:function(){return new C}}]),e}(),q=function(e){function t(e,l){u(this,t);var a=v(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,l));return a.genAsync=new D(l||null,a.svg),a}return c(t,e),y(t,[{key:'line',value:async function(e,t,l,a,n){var i=await this.genAsync.line(e,t,l,a,n);return this.draw(i)}},{key:'rectangle',value:async function(e,t,l,a,n){var i=await this.genAsync.rectangle(e,t,l,a,n);return this.draw(i)}},{key:'ellipse',value:async function(e,t,l,a,n){var i=await this.genAsync.ellipse(e,t,l,a,n);return this.draw(i)}},{key:'circle',value:async function(e,t,l,a){var n=await this.genAsync.circle(e,t,l,a);return this.draw(n)}},{key:'linearPath',value:async function(e,t){var l=await this.genAsync.linearPath(e,t);return this.draw(l)}},{key:'polygon',value:async function(e,t){var l=await this.genAsync.polygon(e,t);return this.draw(l)}},{key:'arc',value:async function(e,t,l,a,n,i){var s=!!(6 Date: Mon, 18 Jun 2018 15:32:41 -0700 Subject: [PATCH 16/16] some refactoring --- bin/path.js | 48 ++++++++++++++++++------------------- dist/rough.es5.js | 48 ++++++++++++++++++------------------- dist/rough.es5.min.js | 2 +- dist/rough.js | 48 ++++++++++++++++++------------------- dist/rough.min.js | 2 +- dist/rough.umd.es5.js | 48 ++++++++++++++++++------------------- dist/rough.umd.es5.min.js | 2 +- dist/rough.umd.js | 48 ++++++++++++++++++------------------- dist/rough.umd.min.js | 2 +- src/path.ts | 50 +++++++++++++++++++-------------------- 10 files changed, 149 insertions(+), 149 deletions(-) diff --git a/bin/path.js b/bin/path.js index 5ae57c5..30e3413 100644 --- a/bin/path.js +++ b/bin/path.js @@ -2,26 +2,26 @@ function isType(token, type) { return token.type === type; } const PARAMS = { - A: { length: 7 }, - a: { length: 7 }, - C: { length: 6 }, - c: { length: 6 }, - H: { length: 1 }, - h: { length: 1 }, - L: { length: 2 }, - l: { length: 2 }, - M: { length: 2 }, - m: { length: 2 }, - Q: { length: 4 }, - q: { length: 4 }, - S: { length: 4 }, - s: { length: 4 }, - T: { length: 4 }, - t: { length: 2 }, - V: { length: 1 }, - v: { length: 1 }, - Z: { length: 0 }, - z: { length: 0 } + 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: 4, + t: 2, + V: 1, + v: 1, + Z: 0, + z: 0 }; class ParsedPath { constructor(d) { @@ -66,7 +66,7 @@ class ParsedPath { if (mode === 'BOD') { if (token.text === 'M' || token.text === 'm') { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } else { @@ -76,11 +76,11 @@ class ParsedPath { } else { if (isType(token, this.NUMBER)) { - param_length = PARAMS[mode].length; + param_length = PARAMS[mode]; } else { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } } @@ -95,7 +95,7 @@ class ParsedPath { return; } } - if (PARAMS[mode]) { + if (typeof PARAMS[mode] === 'number') { const segment = { key: mode, data: params }; this.segments.push(segment); index += param_length; diff --git a/dist/rough.es5.js b/dist/rough.es5.js index 399d4ce..6ab7909 100644 --- a/dist/rough.es5.js +++ b/dist/rough.es5.js @@ -59,26 +59,26 @@ var rough = (function () { return token.type === type; } var PARAMS = { - A: { length: 7 }, - a: { length: 7 }, - C: { length: 6 }, - c: { length: 6 }, - H: { length: 1 }, - h: { length: 1 }, - L: { length: 2 }, - l: { length: 2 }, - M: { length: 2 }, - m: { length: 2 }, - Q: { length: 4 }, - q: { length: 4 }, - S: { length: 4 }, - s: { length: 4 }, - T: { length: 4 }, - t: { length: 2 }, - V: { length: 1 }, - v: { length: 1 }, - Z: { length: 0 }, - z: { length: 0 } + 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: 4, + t: 2, + V: 1, + v: 1, + Z: 0, + z: 0 }; var ParsedPath = function () { @@ -128,7 +128,7 @@ var rough = (function () { if (mode === 'BOD') { if (token.text === 'M' || token.text === 'm') { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } else { this.parseData('M0,0' + d); @@ -136,10 +136,10 @@ var rough = (function () { } } else { if (isType(token, this.NUMBER)) { - param_length = PARAMS[mode].length; + param_length = PARAMS[mode]; } else { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } } @@ -153,7 +153,7 @@ var rough = (function () { return; } } - if (PARAMS[mode]) { + if (typeof PARAMS[mode] === 'number') { var segment = { key: mode, data: params }; this.segments.push(segment); index += param_length; diff --git a/dist/rough.es5.min.js b/dist/rough.es5.min.js index 003de9a..a07f8f2 100644 --- a/dist/rough.es5.min.js +++ b/dist/rough.es5.min.js @@ -1 +1 @@ -var rough=function(){"use strict";function e(e,t){return e.type===t}function t(e,t){var l=t.fillStyle||"hachure";if(!T[l])switch(l){case"zigzag":T[l]||(T[l]=new S(e));break;case"cross-hatch":T[l]||(T[l]=new A(e));break;case"hachure":default:l="hachure",T[l]||(T[l]=new O(e));}return T[l]}function l(e){if(z&&E&&self&&self.workly&&e.async&&!e.noWorker){var t=e.worklyURL||"https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js";if(t){var l=URL.createObjectURL(new Blob(["importScripts('"+t+"', '"+E+"');\nworkly.expose(self.rough.createRenderer());"]));return self.workly.proxy(l)}}return new C}var a=Math.round,n=Math.max,s=Math.min,i=Math.pow,r=Math.floor,p=Math.sqrt,d=Math.cos,h=Math.sin,g=Math.abs,f=Math.PI,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},y=function(){function e(e,t){for(var l,a=0;a_){var k=p(1-_/(this._rx*this._rx*this._ry*this._ry));this._rx*=k,this._ry*=k,v=0}else v=(i===o?-1:1)*p(_/(this._rx*this._rx*c*c+this._ry*this._ry*y*y));var s=v*this._rx*c/this._ry,x=-v*this._ry*y/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*s-this._sinPhi*x+(t[0]+l[0])/2,this._C[1]=this._sinPhi*s+this._cosPhi*x+(t[1]+l[1])/2,this._theta=this.calculateVectorAngle(1,0,(y-s)/this._rx,(c-x)/this._ry);var b=this.calculateVectorAngle((y-s)/this._rx,(c-x)/this._ry,(-y-s)/this._rx,(-c-x)/this._ry);!o&&0b&&(b+=2*f),this._numSegs=Math.ceil(g(b/(f/2))),this._delta=b/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}}return y(e,[{key:"getNextSegment",value:function(){if(this._segIndex===this._numSegs)return null;var e=d(this._theta),t=h(this._theta),l=this._theta+this._delta,a=d(l),n=h(l),i=[this._cosPhi*this._rx*a-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*a+this._cosPhi*this._ry*n+this._C[1]],s=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[i[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*a),i[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*a)];return this._theta=l,this._from=[i[0],i[1]],this._segIndex++,{cp1:s,cp2:o,to:i}}},{key:"calculateVectorAngle",value:function(e,t,l,a){var n=Math.atan2,i=n(t,e),s=n(a,l);return s>=i?s-i:2*f-(i-s)}}]),e}(),m=function(){function e(t,l){u(this,e),this.sets=t,this.closed=l}return y(e,[{key:"fit",value:function(e){var t=[],l=!0,a=!1,n=void 0;try{for(var s,o=this.sets[Symbol.iterator]();!(l=(s=o.next()).done);l=!0){var p=s.value,h=p.length,g=r(e*h);if(5>g){if(5>=h)continue;g=5}t.push(this.reduce(p,g))}}catch(e){a=!0,n=e}finally{try{!l&&o.return&&o.return()}finally{if(a)throw n}}var f="",u=!0,y=!1,c=void 0;try{for(var v,_,k=t[Symbol.iterator]();!(u=(v=k.next()).done);u=!0){_=v.value;for(var x,b=0;b<_.length;b++)x=_[b],f+=0===b?"M"+x[0]+","+x[1]:"L"+x[0]+","+x[1];this.closed&&(f+="z ")}}catch(e){y=!0,c=e}finally{try{!u&&k.return&&k.return()}finally{if(y)throw c}}return f}},{key:"distance",value:function(e,t){return p(i(e[0]-t[0],2)+i(e[1]-t[1],2))}},{key:"reduce",value:function(e,t){if(e.length<=t)return e;for(var l=e.slice(0);l.length>t;){for(var n=-1,o=-1,r=1;rn||s=s(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=s(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+r,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===t?(this.xi=e.px1,this.yi=l*this.xi+o,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(p))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):l===i?o==r&&(this.px1>=s(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=s(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(r-o)/(l-i),this.yi=l*this.xi+o,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}]),e}(),P=function(){function e(t,l,a,n,i,s,o,r){u(this,e),this.deltaX=0,this.hGap=0,this.top=t,this.bottom=l,this.left=a,this.right=n,this.gap=i,this.sinAngle=s,this.tanAngle=r,1e-4>g(s)?this.pos=a+i:.9999g(this.sinAngle)){if(this.posthis.right&&l>this.right;)if(this.pos+=this.hGap,t=this.pos-this.deltaX/2,l=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;var i=new w([t,a],[l,n]);this.sLeft&&i.intersects(this.sLeft)&&(t=i.xi,a=i.yi),this.sRight&&i.intersects(this.sRight)&&(l=i.xi,n=i.yi),0v&&(v=4*l.strokeWidth),v=n(v,.1);for(var _=c%180*(f/180),k=d(_),x=h(_),b=t(_),m=new P(g-1,u+1,r-1,p+1,v,x,k,b),w=void 0,O=null;null!=(w=m.nextLine());)for(var S=this.getIntersectingLines(w,e),A=0;A=u&&(u=4*i.strokeWidth);var y=i.fillWeight;0>y&&(y=i.strokeWidth/2);for(var c=t(h%180*(f/180)),v=d/r,_=p(v*c*v*c+1),k=v*c/_,x=1/_,b=u/(r*d/p(d*x*(d*x)+r*k*(r*k))/r),m=p(r*r-(e-r+b)*(e-r+b)),w=null,P=e-r+b;P_;)_+=2*f,k+=2*f;k-_>2*f&&(_=0,k=2*f);var x=2*f/u.curveStepCount,b=s(x/2,(k-_)/2),m=this._arc(b,o,y,c,v,_,k,1,u),w=this._arc(b,o,y,c,v,_,k,1.5,u),P=m.concat(w);return r&&(p?(P=P.concat(this.doubleLine(o,y,o+c*d(_),y+v*h(_),u)),P=P.concat(this.doubleLine(o,y,o+c*d(k),y+v*h(k),u))):(P.push({op:"lineTo",data:[o,y]}),P.push({op:"lineTo",data:[o+c*d(_),y+v*h(_)]}))),{type:"path",ops:P}}},{key:"svgPath",value:function(e,t){e=(e||"").replace(/\n/g," ").replace(/(-\s)/g,"-").replace("/(ss)/g"," ");var l=new x(e);if(t.simplification){var a=new m(l.linearPoints,l.closed),n=a.fit(t.simplification);l=new x(n)}for(var o=[],r=l.segments||[],d=0;dy;)y+=2*f,c+=2*f;c-y>2*f&&(y=0,c=2*f);for(var v=(c-y)/s.curveStepCount,_=[],k=y;k<=c;k+=v)_.push([o+p*d(k),r+u*h(k)]);return _.push([o+p*d(c),r+u*h(c)]),_.push([o,r]),this.patternFillPolygon(_,s)}},{key:"getOffset",value:function(e,t,l){return l.roughness*(Math.random()*(t-e)+e)}},{key:"doubleLine",value:function(e,t,l,a,n){var i=this._line(e,t,l,a,n,!0,!1),s=this._line(e,t,l,a,n,!0,!0);return i.concat(s)}},{key:"_line",value:function(e,t,l,a,n,s,o){var r=i(e-l,2)+i(t-a,2),d=n.maxRandomnessOffset||0;100*(d*d)>r&&(d=p(r)/10);var h=d/2,g=.2+.2*Math.random(),f=n.bowing*n.maxRandomnessOffset*(a-t)/200,u=n.bowing*n.maxRandomnessOffset*(e-l)/200;f=this.getOffset(-f,f,n),u=this.getOffset(-u,u,n);var y=[];return s&&(o?y.push({op:"move",data:[e+this.getOffset(-h,h,n),t+this.getOffset(-h,h,n)]}):y.push({op:"move",data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?y.push({op:"bcurveTo",data:[f+e+(l-e)*g+this.getOffset(-h,h,n),u+t+(a-t)*g+this.getOffset(-h,h,n),f+e+2*(l-e)*g+this.getOffset(-h,h,n),u+t+2*(a-t)*g+this.getOffset(-h,h,n),l+this.getOffset(-h,h,n),a+this.getOffset(-h,h,n)]}):y.push({op:"bcurveTo",data:[f+e+(l-e)*g+this.getOffset(-d,d,n),u+t+(a-t)*g+this.getOffset(-d,d,n),f+e+2*(l-e)*g+this.getOffset(-d,d,n),u+t+2*(a-t)*g+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),a+this.getOffset(-d,d,n)]}),y}},{key:"_curve",value:function(e,t,l){var a=e.length,n=[];if(3g;g++)0===g?o.push({op:"move",data:[r.x,r.y]}):o.push({op:"move",data:[r.x+this.getOffset(-d[0],d[0],p),r.y+this.getOffset(-d[0],d[0],p)]}),h=[n+this.getOffset(-d[g],d[g],p),s+this.getOffset(-d[g],d[g],p)],o.push({op:"bcurveTo",data:[e+this.getOffset(-d[g],d[g],p),t+this.getOffset(-d[g],d[g],p),l+this.getOffset(-d[g],d[g],p),a+this.getOffset(-d[g],d[g],p),h[0],h[1]]});return r.setPosition(h[0],h[1]),o}},{key:"_processSegment",value:function(e,t,l,a){var n=[];switch(t.key){case"M":case"m":{var s="m"===t.key;if(2<=t.data.length){var o=+t.data[0],r=+t.data[1];s&&(o+=e.x,r+=e.y);var p=1*(a.maxRandomnessOffset||0);o+=this.getOffset(-p,p,a),r+=this.getOffset(-p,p,a),e.setPosition(o,r),n.push({op:"move",data:[o,r]})}break}case"L":case"l":{var d="l"===t.key;if(2<=t.data.length){var h=+t.data[0],g=+t.data[1];d&&(h+=e.x,g+=e.y),n=n.concat(this.doubleLine(e.x,e.y,h,g,a)),e.setPosition(h,g)}break}case"H":case"h":{var u="h"===t.key;if(t.data.length){var c=+t.data[0];u&&(c+=e.x),n=n.concat(this.doubleLine(e.x,e.y,c,e.y,a)),e.setPosition(c,e.y)}break}case"V":case"v":{var v="v"===t.key;if(t.data.length){var _=+t.data[0];v&&(_+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,_,a)),e.setPosition(e.x,_)}break}case"Z":case"z":{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],a)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case"C":case"c":{var k="c"===t.key;if(6<=t.data.length){var m=+t.data[0],w=+t.data[1],P=+t.data[2],O=+t.data[3],S=+t.data[4],A=+t.data[5];k&&(m+=e.x,P+=e.x,S+=e.x,w+=e.y,O+=e.y,A+=e.y);var T=this._bezierTo(m,w,P,O,S,A,e,a);n=n.concat(T),e.bezierReflectionPoint=[S+(S-P),A+(A-O)]}break}case"S":case"s":{var C="s"===t.key;if(4<=t.data.length){var z=+t.data[0],E=+t.data[1],L=+t.data[2],R=+t.data[3];C&&(z+=e.x,L+=e.x,E+=e.y,R+=e.y);var W=z,N=E,D=l?l.key:"",B=null;("c"===D||"C"===D||"s"===D||"S"===D)&&(B=e.bezierReflectionPoint),B&&(W=B[0],N=B[1]);var F=this._bezierTo(W,N,z,E,L,R,e,a);n=n.concat(F),e.bezierReflectionPoint=[L+(L-z),R+(R-E)]}break}case"Q":case"q":{var M="q"===t.key;if(4<=t.data.length){var q=+t.data[0],U=+t.data[1],X=+t.data[2],V=+t.data[3];M&&(q+=e.x,X+=e.x,U+=e.y,V+=e.y);var G=1*(1+.2*a.roughness),I=1.5*(1+.22*a.roughness);n.push({op:"move",data:[e.x+this.getOffset(-G,G,a),e.y+this.getOffset(-G,G,a)]});var j=[X+this.getOffset(-G,G,a),V+this.getOffset(-G,G,a)];n.push({op:"qcurveTo",data:[q+this.getOffset(-G,G,a),U+this.getOffset(-G,G,a),j[0],j[1]]}),n.push({op:"move",data:[e.x+this.getOffset(-I,I,a),e.y+this.getOffset(-I,I,a)]}),j=[X+this.getOffset(-I,I,a),V+this.getOffset(-I,I,a)],n.push({op:"qcurveTo",data:[q+this.getOffset(-I,I,a),U+this.getOffset(-I,I,a),j[0],j[1]]}),e.setPosition(j[0],j[1]),e.quadReflectionPoint=[X+(X-q),V+(V-U)]}break}case"T":case"t":{var Q="t"===t.key;if(2<=t.data.length){var $=+t.data[0],Z=+t.data[1];Q&&($+=e.x,Z+=e.y);var H=$,J=Z,Y=l?l.key:"",K=null;("q"===Y||"Q"===Y||"t"===Y||"T"===Y)&&(K=e.quadReflectionPoint),K&&(H=K[0],J=K[1]);var ee=1*(1+.2*a.roughness),te=1.5*(1+.22*a.roughness);n.push({op:"move",data:[e.x+this.getOffset(-ee,ee,a),e.y+this.getOffset(-ee,ee,a)]});var le=[$+this.getOffset(-ee,ee,a),Z+this.getOffset(-ee,ee,a)];n.push({op:"qcurveTo",data:[H+this.getOffset(-ee,ee,a),J+this.getOffset(-ee,ee,a),le[0],le[1]]}),n.push({op:"move",data:[e.x+this.getOffset(-te,te,a),e.y+this.getOffset(-te,te,a)]}),le=[$+this.getOffset(-te,te,a),Z+this.getOffset(-te,te,a)],n.push({op:"qcurveTo",data:[H+this.getOffset(-te,te,a),J+this.getOffset(-te,te,a),le[0],le[1]]}),e.setPosition(le[0],le[1]),e.quadReflectionPoint=[$+($-H),Z+(Z-J)]}break}case"A":case"a":{var ae="a"===t.key;if(7<=t.data.length){var ne=+t.data[0],ie=+t.data[1],se=+t.data[2],oe=+t.data[3],re=+t.data[4],pe=+t.data[5],de=+t.data[6];if(ae&&(pe+=e.x,de+=e.y),pe===e.x&&de===e.y)break;if(0==ne||0==ie)n=n.concat(this.doubleLine(e.x,e.y,pe,de,a)),e.setPosition(pe,de);else for(var he=0;1>he;he++)for(var ge,fe=new b([e.x,e.y],[pe,de],[ne,ie],se,!!oe,!!re),ue=fe.getNextSegment();ue;)ge=this._bezierTo(ue.cp1[0],ue.cp1[1],ue.cp2[0],ue.cp2[1],ue.to[0],ue.to[1],e,a),n=n.concat(ge),ue=fe.getNextSegment()}break}default:}return n}}]),e}(),z="undefined"!=typeof self,E=z&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,L="undefined"!=typeof self,R=function(){function e(t,a){u(this,e),this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=t||{},this.surface=a,this.renderer=l(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}return y(e,[{key:"_options",value:function(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}},{key:"_drawable",value:function(e,t,l){return{shape:e,sets:t||[],options:l||this.defaultOptions}}},{key:"getCanvasSize",value:function(){var e=function(e){return e&&"object"===("undefined"==typeof e?"undefined":o(e))&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100};return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}},{key:"computePolygonSize",value:function(e){if(e.length){for(var t=e[0][0],l=e[0][0],a=e[0][1],o=e[0][1],r=1;rl&&(l=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||"none",strokeWidth:l,fill:"none"}}},{key:"opsToPath",value:function(e){var t="",l=!0,a=!1,n=void 0;try{for(var i,s=e.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case"move":t+="M"+r[0]+" "+r[1]+" ";break;case"bcurveTo":t+="C"+r[0]+" "+r[1]+", "+r[2]+" "+r[3]+", "+r[4]+" "+r[5]+" ";break;case"qcurveTo":t+="Q"+r[0]+" "+r[1]+", "+r[2]+" "+r[3]+" ";break;case"lineTo":t+="L"+r[0]+" "+r[1]+" ";}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}return t.trim()}},{key:"lib",get:function(){return this.renderer}}]),e}(),W="undefined"!=typeof document,N=function(){function e(t,l){u(this,e),this.canvas=t,this.ctx=this.canvas.getContext("2d"),this.gen=new R(l||null,this.canvas)}return y(e,[{key:"line",value:function(e,t,l,a,n){var i=this.gen.line(e,t,l,a,n);return this.draw(i),i}},{key:"rectangle",value:function(e,t,l,a,n){var i=this.gen.rectangle(e,t,l,a,n);return this.draw(i),i}},{key:"ellipse",value:function(e,t,l,a,n){var i=this.gen.ellipse(e,t,l,a,n);return this.draw(i),i}},{key:"circle",value:function(e,t,l,a){var n=this.gen.circle(e,t,l,a);return this.draw(n),n}},{key:"linearPath",value:function(e,t){var l=this.gen.linearPath(e,t);return this.draw(l),l}},{key:"polygon",value:function(e,t){var l=this.gen.polygon(e,t);return this.draw(l),l}},{key:"arc",value:function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2),e.save(),e.strokeStyle=l.fill||"",e.lineWidth=a,this._drawToContext(e,t),e.restore()}},{key:"_drawToContext",value:function(e,t){e.beginPath();var l=!0,a=!1,n=void 0;try{for(var i,s=t.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case"move":e.moveTo(r[0],r[1]);break;case"bcurveTo":e.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);break;case"qcurveTo":e.quadraticCurveTo(r[0],r[1],r[2],r[3]);break;case"lineTo":e.lineTo(r[0],r[1]);}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}"fillPath"===t.type?e.fill():e.stroke()}},{key:"generator",get:function(){return this.gen}}],[{key:"createRenderer",value:function(){return new C}}]),e}(),D=function(e){function t(){return u(this,t),v(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return c(t,e),y(t,[{key:"line",value:async function(e,t,l,a,n){var i=this._options(n);return this._drawable("line",[await this.lib.line(e,t,l,a,i)],i)}},{key:"rectangle",value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill){var o=[[e,t],[e+l,t],[e+l,t+a],[e,t+a]];"solid"===i.fillStyle?s.push((await this.lib.solidFillPolygon(o,i))):s.push((await this.lib.patternFillPolygon(o,i)))}return s.push((await this.lib.rectangle(e,t,l,a,i))),this._drawable("rectangle",s,i)}},{key:"ellipse",value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill)if("solid"===i.fillStyle){var o=await this.lib.ellipse(e,t,l,a,i);o.type="fillPath",s.push(o)}else s.push((await this.lib.patternFillEllipse(e,t,l,a,i)));return s.push((await this.lib.ellipse(e,t,l,a,i))),this._drawable("ellipse",s,i)}},{key:"circle",value:async function(e,t,l,a){var n=await this.ellipse(e,t,l,l,a);return n.shape="circle",n}},{key:"linearPath",value:async function(e,t){var l=this._options(t);return this._drawable("linearPath",[await this.lib.linearPath(e,!1,l)],l)}},{key:"arc",value:async function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2);var n=e.createElementNS("http://www.w3.org/2000/svg","path");return n.setAttribute("d",this.opsToPath(t)),n.style.stroke=l.fill,n.style.strokeWidth=a+"",n.style.fill="none",n}},{key:"generator",get:function(){return this.gen}},{key:"defs",get:function(){if(F&&!this._defs){var e=this.svg.ownerDocument||document,t=e.createElementNS("http://www.w3.org/2000/svg","defs");this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}}],[{key:"createRenderer",value:function(){return new C}}]),e}(),q=function(e){function t(e,l){u(this,t);var a=v(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,l));return a.genAsync=new D(l||null,a.svg),a}return c(t,e),y(t,[{key:"line",value:async function(e,t,l,a,n){var i=await this.genAsync.line(e,t,l,a,n);return this.draw(i)}},{key:"rectangle",value:async function(e,t,l,a,n){var i=await this.genAsync.rectangle(e,t,l,a,n);return this.draw(i)}},{key:"ellipse",value:async function(e,t,l,a,n){var i=await this.genAsync.ellipse(e,t,l,a,n);return this.draw(i)}},{key:"circle",value:async function(e,t,l,a){var n=await this.genAsync.circle(e,t,l,a);return this.draw(n)}},{key:"linearPath",value:async function(e,t){var l=await this.genAsync.linearPath(e,t);return this.draw(l)}},{key:"polygon",value:async function(e,t){var l=await this.genAsync.polygon(e,t);return this.draw(l)}},{key:"arc",value:async function(e,t,l,a,n,i){var s=!!(6_){var k=p(1-_/(this._rx*this._rx*this._ry*this._ry));this._rx*=k,this._ry*=k,v=0}else v=(i===o?-1:1)*p(_/(this._rx*this._rx*c*c+this._ry*this._ry*y*y));var s=v*this._rx*c/this._ry,x=-v*this._ry*y/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*s-this._sinPhi*x+(t[0]+l[0])/2,this._C[1]=this._sinPhi*s+this._cosPhi*x+(t[1]+l[1])/2,this._theta=this.calculateVectorAngle(1,0,(y-s)/this._rx,(c-x)/this._ry);var b=this.calculateVectorAngle((y-s)/this._rx,(c-x)/this._ry,(-y-s)/this._rx,(-c-x)/this._ry);!o&&0b&&(b+=2*h),this._numSegs=Math.ceil(u(b/(h/2))),this._delta=b/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}}return y(e,[{key:"getNextSegment",value:function(){if(this._segIndex===this._numSegs)return null;var e=d(this._theta),t=f(this._theta),l=this._theta+this._delta,a=d(l),n=f(l),i=[this._cosPhi*this._rx*a-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*a+this._cosPhi*this._ry*n+this._C[1]],s=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[i[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*a),i[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*a)];return this._theta=l,this._from=[i[0],i[1]],this._segIndex++,{cp1:s,cp2:o,to:i}}},{key:"calculateVectorAngle",value:function(e,t,l,a){var n=Math.atan2,i=n(t,e),s=n(a,l);return s>=i?s-i:2*h-(i-s)}}]),e}(),m=function(){function e(t,l){g(this,e),this.sets=t,this.closed=l}return y(e,[{key:"fit",value:function(e){var t=[],l=!0,a=!1,n=void 0;try{for(var s,o=this.sets[Symbol.iterator]();!(l=(s=o.next()).done);l=!0){var p=s.value,f=p.length,u=r(e*f);if(5>u){if(5>=f)continue;u=5}t.push(this.reduce(p,u))}}catch(e){a=!0,n=e}finally{try{!l&&o.return&&o.return()}finally{if(a)throw n}}var h="",g=!0,y=!1,c=void 0;try{for(var v,_,k=t[Symbol.iterator]();!(g=(v=k.next()).done);g=!0){_=v.value;for(var x,b=0;b<_.length;b++)x=_[b],h+=0===b?"M"+x[0]+","+x[1]:"L"+x[0]+","+x[1];this.closed&&(h+="z ")}}catch(e){y=!0,c=e}finally{try{!g&&k.return&&k.return()}finally{if(y)throw c}}return h}},{key:"distance",value:function(e,t){return p(i(e[0]-t[0],2)+i(e[1]-t[1],2))}},{key:"reduce",value:function(e,t){if(e.length<=t)return e;for(var l=e.slice(0);l.length>t;){for(var n=-1,o=-1,r=1;rn||s=s(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=s(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+r,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>u(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===t?(this.xi=e.px1,this.yi=l*this.xi+o,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>u(p))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):l===i?o==r&&(this.px1>=s(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=s(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(r-o)/(l-i),this.yi=l*this.xi+o,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}]),e}(),P=function(){function e(t,l,a,n,i,s,o,r){g(this,e),this.deltaX=0,this.hGap=0,this.top=t,this.bottom=l,this.left=a,this.right=n,this.gap=i,this.sinAngle=s,this.tanAngle=r,1e-4>u(s)?this.pos=a+i:.9999u(this.sinAngle)){if(this.posthis.right&&l>this.right;)if(this.pos+=this.hGap,t=this.pos-this.deltaX/2,l=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;var i=new w([t,a],[l,n]);this.sLeft&&i.intersects(this.sLeft)&&(t=i.xi,a=i.yi),this.sRight&&i.intersects(this.sRight)&&(l=i.xi,n=i.yi),0v&&(v=4*l.strokeWidth),v=n(v,.1);for(var _=c%180*(h/180),k=d(_),x=f(_),b=t(_),m=new P(u-1,g+1,r-1,p+1,v,x,k,b),w=void 0,O=null;null!=(w=m.nextLine());)for(var S=this.getIntersectingLines(w,e),A=0;A=g&&(g=4*i.strokeWidth);var y=i.fillWeight;0>y&&(y=i.strokeWidth/2);for(var c=t(f%180*(h/180)),v=d/r,_=p(v*c*v*c+1),k=v*c/_,x=1/_,b=g/(r*d/p(d*x*(d*x)+r*k*(r*k))/r),m=p(r*r-(e-r+b)*(e-r+b)),w=null,P=e-r+b;P_;)_+=2*h,k+=2*h;k-_>2*h&&(_=0,k=2*h);var x=2*h/g.curveStepCount,b=s(x/2,(k-_)/2),m=this._arc(b,o,y,c,v,_,k,1,g),w=this._arc(b,o,y,c,v,_,k,1.5,g),P=m.concat(w);return r&&(p?(P=P.concat(this.doubleLine(o,y,o+c*d(_),y+v*f(_),g)),P=P.concat(this.doubleLine(o,y,o+c*d(k),y+v*f(k),g))):(P.push({op:"lineTo",data:[o,y]}),P.push({op:"lineTo",data:[o+c*d(_),y+v*f(_)]}))),{type:"path",ops:P}}},{key:"svgPath",value:function(e,t){e=(e||"").replace(/\n/g," ").replace(/(-\s)/g,"-").replace("/(ss)/g"," ");var l=new x(e);if(t.simplification){var a=new m(l.linearPoints,l.closed),n=a.fit(t.simplification);l=new x(n)}for(var o=[],r=l.segments||[],d=0;dy;)y+=2*h,c+=2*h;c-y>2*h&&(y=0,c=2*h);for(var v=(c-y)/s.curveStepCount,_=[],k=y;k<=c;k+=v)_.push([o+p*d(k),r+g*f(k)]);return _.push([o+p*d(c),r+g*f(c)]),_.push([o,r]),this.patternFillPolygon(_,s)}},{key:"getOffset",value:function(e,t,l){return l.roughness*(Math.random()*(t-e)+e)}},{key:"doubleLine",value:function(e,t,l,a,n){var i=this._line(e,t,l,a,n,!0,!1),s=this._line(e,t,l,a,n,!0,!0);return i.concat(s)}},{key:"_line",value:function(e,t,l,a,n,s,o){var r=i(e-l,2)+i(t-a,2),d=n.maxRandomnessOffset||0;100*(d*d)>r&&(d=p(r)/10);var f=d/2,u=.2+.2*Math.random(),h=n.bowing*n.maxRandomnessOffset*(a-t)/200,g=n.bowing*n.maxRandomnessOffset*(e-l)/200;h=this.getOffset(-h,h,n),g=this.getOffset(-g,g,n);var y=[];return s&&(o?y.push({op:"move",data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):y.push({op:"move",data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?y.push({op:"bcurveTo",data:[h+e+(l-e)*u+this.getOffset(-f,f,n),g+t+(a-t)*u+this.getOffset(-f,f,n),h+e+2*(l-e)*u+this.getOffset(-f,f,n),g+t+2*(a-t)*u+this.getOffset(-f,f,n),l+this.getOffset(-f,f,n),a+this.getOffset(-f,f,n)]}):y.push({op:"bcurveTo",data:[h+e+(l-e)*u+this.getOffset(-d,d,n),g+t+(a-t)*u+this.getOffset(-d,d,n),h+e+2*(l-e)*u+this.getOffset(-d,d,n),g+t+2*(a-t)*u+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),a+this.getOffset(-d,d,n)]}),y}},{key:"_curve",value:function(e,t,l){var a=e.length,n=[];if(3h;h++)0===h?o.push({op:"move",data:[r.x,r.y]}):o.push({op:"move",data:[r.x+this.getOffset(-d[0],d[0],p),r.y+this.getOffset(-d[0],d[0],p)]}),u=[n+this.getOffset(-d[h],d[h],p),s+this.getOffset(-d[h],d[h],p)],o.push({op:"bcurveTo",data:[e+this.getOffset(-d[h],d[h],p),t+this.getOffset(-d[h],d[h],p),l+this.getOffset(-d[h],d[h],p),a+this.getOffset(-d[h],d[h],p),u[0],u[1]]});return r.setPosition(u[0],u[1]),o}},{key:"_processSegment",value:function(e,t,l,a){var n=[];switch(t.key){case"M":case"m":{var s="m"===t.key;if(2<=t.data.length){var o=+t.data[0],r=+t.data[1];s&&(o+=e.x,r+=e.y);var p=1*(a.maxRandomnessOffset||0);o+=this.getOffset(-p,p,a),r+=this.getOffset(-p,p,a),e.setPosition(o,r),n.push({op:"move",data:[o,r]})}break}case"L":case"l":{var d="l"===t.key;if(2<=t.data.length){var u=+t.data[0],h=+t.data[1];d&&(u+=e.x,h+=e.y),n=n.concat(this.doubleLine(e.x,e.y,u,h,a)),e.setPosition(u,h)}break}case"H":case"h":{var g="h"===t.key;if(t.data.length){var c=+t.data[0];g&&(c+=e.x),n=n.concat(this.doubleLine(e.x,e.y,c,e.y,a)),e.setPosition(c,e.y)}break}case"V":case"v":{var v="v"===t.key;if(t.data.length){var _=+t.data[0];v&&(_+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,_,a)),e.setPosition(e.x,_)}break}case"Z":case"z":{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],a)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case"C":case"c":{var k="c"===t.key;if(6<=t.data.length){var m=+t.data[0],w=+t.data[1],P=+t.data[2],O=+t.data[3],S=+t.data[4],A=+t.data[5];k&&(m+=e.x,P+=e.x,S+=e.x,w+=e.y,O+=e.y,A+=e.y);var T=this._bezierTo(m,w,P,O,S,A,e,a);n=n.concat(T),e.bezierReflectionPoint=[S+(S-P),A+(A-O)]}break}case"S":case"s":{var C="s"===t.key;if(4<=t.data.length){var z=+t.data[0],E=+t.data[1],L=+t.data[2],R=+t.data[3];C&&(z+=e.x,L+=e.x,E+=e.y,R+=e.y);var W=z,N=E,D=l?l.key:"",B=null;("c"===D||"C"===D||"s"===D||"S"===D)&&(B=e.bezierReflectionPoint),B&&(W=B[0],N=B[1]);var F=this._bezierTo(W,N,z,E,L,R,e,a);n=n.concat(F),e.bezierReflectionPoint=[L+(L-z),R+(R-E)]}break}case"Q":case"q":{var M="q"===t.key;if(4<=t.data.length){var q=+t.data[0],U=+t.data[1],X=+t.data[2],V=+t.data[3];M&&(q+=e.x,X+=e.x,U+=e.y,V+=e.y);var G=1*(1+.2*a.roughness),I=1.5*(1+.22*a.roughness);n.push({op:"move",data:[e.x+this.getOffset(-G,G,a),e.y+this.getOffset(-G,G,a)]});var j=[X+this.getOffset(-G,G,a),V+this.getOffset(-G,G,a)];n.push({op:"qcurveTo",data:[q+this.getOffset(-G,G,a),U+this.getOffset(-G,G,a),j[0],j[1]]}),n.push({op:"move",data:[e.x+this.getOffset(-I,I,a),e.y+this.getOffset(-I,I,a)]}),j=[X+this.getOffset(-I,I,a),V+this.getOffset(-I,I,a)],n.push({op:"qcurveTo",data:[q+this.getOffset(-I,I,a),U+this.getOffset(-I,I,a),j[0],j[1]]}),e.setPosition(j[0],j[1]),e.quadReflectionPoint=[X+(X-q),V+(V-U)]}break}case"T":case"t":{var Q="t"===t.key;if(2<=t.data.length){var $=+t.data[0],Z=+t.data[1];Q&&($+=e.x,Z+=e.y);var H=$,J=Z,Y=l?l.key:"",K=null;("q"===Y||"Q"===Y||"t"===Y||"T"===Y)&&(K=e.quadReflectionPoint),K&&(H=K[0],J=K[1]);var ee=1*(1+.2*a.roughness),te=1.5*(1+.22*a.roughness);n.push({op:"move",data:[e.x+this.getOffset(-ee,ee,a),e.y+this.getOffset(-ee,ee,a)]});var le=[$+this.getOffset(-ee,ee,a),Z+this.getOffset(-ee,ee,a)];n.push({op:"qcurveTo",data:[H+this.getOffset(-ee,ee,a),J+this.getOffset(-ee,ee,a),le[0],le[1]]}),n.push({op:"move",data:[e.x+this.getOffset(-te,te,a),e.y+this.getOffset(-te,te,a)]}),le=[$+this.getOffset(-te,te,a),Z+this.getOffset(-te,te,a)],n.push({op:"qcurveTo",data:[H+this.getOffset(-te,te,a),J+this.getOffset(-te,te,a),le[0],le[1]]}),e.setPosition(le[0],le[1]),e.quadReflectionPoint=[$+($-H),Z+(Z-J)]}break}case"A":case"a":{var ae="a"===t.key;if(7<=t.data.length){var ne=+t.data[0],ie=+t.data[1],se=+t.data[2],oe=+t.data[3],re=+t.data[4],pe=+t.data[5],de=+t.data[6];if(ae&&(pe+=e.x,de+=e.y),pe===e.x&&de===e.y)break;if(0==ne||0==ie)n=n.concat(this.doubleLine(e.x,e.y,pe,de,a)),e.setPosition(pe,de);else for(var fe=0;1>fe;fe++)for(var ue,he=new b([e.x,e.y],[pe,de],[ne,ie],se,!!oe,!!re),ge=he.getNextSegment();ge;)ue=this._bezierTo(ge.cp1[0],ge.cp1[1],ge.cp2[0],ge.cp2[1],ge.to[0],ge.to[1],e,a),n=n.concat(ue),ge=he.getNextSegment()}break}default:}return n}}]),e}(),z="undefined"!=typeof self,E=z&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,L="undefined"!=typeof self,R=function(){function e(t,a){g(this,e),this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=t||{},this.surface=a,this.renderer=l(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}return y(e,[{key:"_options",value:function(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}},{key:"_drawable",value:function(e,t,l){return{shape:e,sets:t||[],options:l||this.defaultOptions}}},{key:"getCanvasSize",value:function(){var e=function(e){return e&&"object"===("undefined"==typeof e?"undefined":o(e))&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100};return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}},{key:"computePolygonSize",value:function(e){if(e.length){for(var t=e[0][0],l=e[0][0],a=e[0][1],o=e[0][1],r=1;rl&&(l=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||"none",strokeWidth:l,fill:"none"}}},{key:"opsToPath",value:function(e){var t="",l=!0,a=!1,n=void 0;try{for(var i,s=e.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case"move":t+="M"+r[0]+" "+r[1]+" ";break;case"bcurveTo":t+="C"+r[0]+" "+r[1]+", "+r[2]+" "+r[3]+", "+r[4]+" "+r[5]+" ";break;case"qcurveTo":t+="Q"+r[0]+" "+r[1]+", "+r[2]+" "+r[3]+" ";break;case"lineTo":t+="L"+r[0]+" "+r[1]+" ";}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}return t.trim()}},{key:"lib",get:function(){return this.renderer}}]),e}(),W="undefined"!=typeof document,N=function(){function e(t,l){g(this,e),this.canvas=t,this.ctx=this.canvas.getContext("2d"),this.gen=new R(l||null,this.canvas)}return y(e,[{key:"line",value:function(e,t,l,a,n){var i=this.gen.line(e,t,l,a,n);return this.draw(i),i}},{key:"rectangle",value:function(e,t,l,a,n){var i=this.gen.rectangle(e,t,l,a,n);return this.draw(i),i}},{key:"ellipse",value:function(e,t,l,a,n){var i=this.gen.ellipse(e,t,l,a,n);return this.draw(i),i}},{key:"circle",value:function(e,t,l,a){var n=this.gen.circle(e,t,l,a);return this.draw(n),n}},{key:"linearPath",value:function(e,t){var l=this.gen.linearPath(e,t);return this.draw(l),l}},{key:"polygon",value:function(e,t){var l=this.gen.polygon(e,t);return this.draw(l),l}},{key:"arc",value:function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2),e.save(),e.strokeStyle=l.fill||"",e.lineWidth=a,this._drawToContext(e,t),e.restore()}},{key:"_drawToContext",value:function(e,t){e.beginPath();var l=!0,a=!1,n=void 0;try{for(var i,s=t.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case"move":e.moveTo(r[0],r[1]);break;case"bcurveTo":e.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);break;case"qcurveTo":e.quadraticCurveTo(r[0],r[1],r[2],r[3]);break;case"lineTo":e.lineTo(r[0],r[1]);}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}"fillPath"===t.type?e.fill():e.stroke()}},{key:"generator",get:function(){return this.gen}}],[{key:"createRenderer",value:function(){return new C}}]),e}(),D=function(e){function t(){return g(this,t),v(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return c(t,e),y(t,[{key:"line",value:async function(e,t,l,a,n){var i=this._options(n);return this._drawable("line",[await this.lib.line(e,t,l,a,i)],i)}},{key:"rectangle",value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill){var o=[[e,t],[e+l,t],[e+l,t+a],[e,t+a]];"solid"===i.fillStyle?s.push((await this.lib.solidFillPolygon(o,i))):s.push((await this.lib.patternFillPolygon(o,i)))}return s.push((await this.lib.rectangle(e,t,l,a,i))),this._drawable("rectangle",s,i)}},{key:"ellipse",value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill)if("solid"===i.fillStyle){var o=await this.lib.ellipse(e,t,l,a,i);o.type="fillPath",s.push(o)}else s.push((await this.lib.patternFillEllipse(e,t,l,a,i)));return s.push((await this.lib.ellipse(e,t,l,a,i))),this._drawable("ellipse",s,i)}},{key:"circle",value:async function(e,t,l,a){var n=await this.ellipse(e,t,l,l,a);return n.shape="circle",n}},{key:"linearPath",value:async function(e,t){var l=this._options(t);return this._drawable("linearPath",[await this.lib.linearPath(e,!1,l)],l)}},{key:"arc",value:async function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2);var n=e.createElementNS("http://www.w3.org/2000/svg","path");return n.setAttribute("d",this.opsToPath(t)),n.style.stroke=l.fill,n.style.strokeWidth=a+"",n.style.fill="none",n}},{key:"generator",get:function(){return this.gen}},{key:"defs",get:function(){if(F&&!this._defs){var e=this.svg.ownerDocument||document,t=e.createElementNS("http://www.w3.org/2000/svg","defs");this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}}],[{key:"createRenderer",value:function(){return new C}}]),e}(),q=function(e){function t(e,l){g(this,t);var a=v(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,l));return a.genAsync=new D(l||null,a.svg),a}return c(t,e),y(t,[{key:"line",value:async function(e,t,l,a,n){var i=await this.genAsync.line(e,t,l,a,n);return this.draw(i)}},{key:"rectangle",value:async function(e,t,l,a,n){var i=await this.genAsync.rectangle(e,t,l,a,n);return this.draw(i)}},{key:"ellipse",value:async function(e,t,l,a,n){var i=await this.genAsync.ellipse(e,t,l,a,n);return this.draw(i)}},{key:"circle",value:async function(e,t,l,a){var n=await this.genAsync.circle(e,t,l,a);return this.draw(n)}},{key:"linearPath",value:async function(e,t){var l=await this.genAsync.linearPath(e,t);return this.draw(l)}},{key:"polygon",value:async function(e,t){var l=await this.genAsync.polygon(e,t);return this.draw(l)}},{key:"arc",value:async function(e,t,l,a,n,i){var s=!!(6u){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=h(this._theta),t=f(this._theta),i=this._theta+this._delta,n=h(i),s=f(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=h(y),x=f(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=f&&(f=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(h%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=f/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*h(x),u+_*f(x),d)),v=v.concat(this.doubleLine(o,u,o+y*h(b),u+_*f(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*h(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*h(g),p+d*f(g)]);return x.push([o+r*h(y),p+d*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const h=r/2,f=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-h,h,s),t+this.getOffset(-h,h,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-h,h,s),c+t+(n-t)*f+this.getOffset(-h,h,s),g+e+2*(i-e)*f+this.getOffset(-h,h,s),c+t+2*(n-t)*f+this.getOffset(-h,h,s),i+this.getOffset(-h,h,s),n+this.getOffset(-h,h,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-r,r,s),c+t+(n-t)*f+this.getOffset(-r,r,s),g+e+2*(i-e)*f+this.getOffset(-r,r,s),c+t+2*(n-t)*f+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const h=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(h),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,h=o;const f=i?i.key:'';let g=null;('c'===f||'C'===f||'s'===f||'S'===f)&&(g=e.bezierReflectionPoint),g&&(d=g[0],h=g[1]);const c=this._bezierTo(d,h,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],h=+t.data[6];if(i&&(d+=e.x,h+=e.y),d===e.x&&h===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,h,n)),e.setPosition(d,h);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,h],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class z{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePolygonSize(e){if(e.length){let t=e[0][0],n=e[0][0],s=e[0][1],a=e[0][1];for(let o=1;oi&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const E='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new z(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(E){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(E)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class L extends z{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async polygon(e,t){const i=this._options(t),n=[];if(i.fill)if('solid'===i.fillStyle)n.push((await this.lib.solidFillPolygon(e,i)));else{const t=this.computePolygonSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=this.polygonPath(e),n.push(a)}return n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class N extends W{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const R='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new z(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(R&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||R&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new N(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new L(e,t):new z(e,t)}};return M}(); +var rough=function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var s=Math.round,n=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,f=Math.cos,h=Math.sin,g=Math.abs,c=Math.PI;const u={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:4,t:2,V:1,v:1,Z:0,z:0};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const s=this.tokenize(t);let n=0,i=s[n],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a]:(n++,l=u[i.text],a=i.text);else if('M'===i.text||'m'===i.text)n++,l=u[i.text],a=i.text;else return void this.parseData('M0,0'+t);if(n+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(n===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=f(this._theta),t=h(this._theta),i=this._theta+this._delta,s=f(i),n=h(i),a=[this._cosPhi*this._rx*s-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*s+this._cosPhi*this._ry*n+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*s),a[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*s)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,s){var n=Math.atan2;const a=n(t,e),l=n(s,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const s=i.length;let n=r(e*s);if(5>n){if(5>=s)continue;n=5}t.push(this.reduce(i,n))}let s='';for(const n of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+n,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+s,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?s==n&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(n-s)/(t-i),this.yi=t*this.xi+s,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,s,n,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=s,this.gap=n,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+n:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=f(y),x=h(y),b=n(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,n=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=h&&(h=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=n(f%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=h/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let n=e-p+m;nx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*f(x),u+_*h(x),d)),v=v.concat(this.doubleLine(o,u,o+y*f(b),u+_*h(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*f(x),u+_*h(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*f(g),p+d*h(g)]);return x.push([o+r*f(y),p+d*h(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,s,n){const a=this._line(e,t,i,s,n,!0,!1),l=this._line(e,t,i,s,n,!0,!0);return a.concat(l)}_line(e,t,i,s,n,l,o){const p=a(e-i,2)+a(t-s,2);let r=n.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const f=r/2,h=.2+.2*Math.random();let g=n.bowing*n.maxRandomnessOffset*(s-t)/200,c=n.bowing*n.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,n),c=this.getOffset(-c,c,n);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,n),t+this.getOffset(-r,r,n)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,n),c+t+(s-t)*h+this.getOffset(-f,f,n),g+e+2*(i-e)*h+this.getOffset(-f,f,n),c+t+2*(s-t)*h+this.getOffset(-f,f,n),i+this.getOffset(-f,f,n),s+this.getOffset(-f,f,n)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-r,r,n),c+t+(s-t)*h+this.getOffset(-r,r,n),g+e+2*(i-e)*h+this.getOffset(-r,r,n),c+t+2*(s-t)*h+this.getOffset(-r,r,n),i+this.getOffset(-r,r,n),s+this.getOffset(-r,r,n)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,s){let n=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(s.maxRandomnessOffset||0);a+=this.getOffset(-o,o,s),l+=this.getOffset(-o,o,s),e.setPosition(a,l),n.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),n=n.concat(this.doubleLine(e.x,e.y,a,l,s)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),n=n.concat(this.doubleLine(e.x,e.y,a,e.y,s)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,a,s)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],s)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,s);n=n.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,s);n=n.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*s.roughness),d=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-r,r,s),e.y+this.getOffset(-r,r,s)]});let h=[o+this.getOffset(-r,r,s),p+this.getOffset(-r,r,s)];n.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,s),l+this.getOffset(-r,r,s),h[0],h[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-d,d,s),e.y+this.getOffset(-d,d,s)]}),h=[o+this.getOffset(-d,d,s),p+this.getOffset(-d,d,s)],n.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,s),l+this.getOffset(-d,d,s),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*s.roughness),c=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-g,g,s),e.y+this.getOffset(-g,g,s)]});let u=[l+this.getOffset(-g,g,s),o+this.getOffset(-g,g,s)];n.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,s),r+this.getOffset(-g,g,s),u[0],u[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-c,c,s),e.y+this.getOffset(-c,c,s)]}),u=[l+this.getOffset(-c,c,s),o+this.getOffset(-c,c,s)],n.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,s),r+this.getOffset(-c,c,s),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)n=n.concat(this.doubleLine(e.x,e.y,d,f,s)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,s);n=n.concat(a),i=t.getNextSegment()}}}break}default:}return n}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class z{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePolygonSize(e){if(e.length){let t=e[0][0],s=e[0][0],n=e[0][1],a=e[0][1];for(let o=1;oi&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const E='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new z(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a),a}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a),a}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a),a}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n),n}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.ctx;for(const n of t)switch(n.type){case'path':s.save(),s.strokeStyle=i.stroke,s.lineWidth=i.strokeWidth,this._drawToContext(s,n),s.restore();break;case'fillPath':s.save(),s.fillStyle=i.fill||'',this._drawToContext(s,n),s.restore();break;case'fillSketch':this.fillSketch(s,n,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(n.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(E){const e=n.size,t=document.createElement('canvas'),s=t.getContext('2d'),a=this.computeBBox(n.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,s.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(s,n,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(n.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(E)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const s=i.getBBox();return document.body.removeChild(t),s}catch(e){}return null}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=s,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class L extends z{async line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[await this.lib.line(e,t,i,s,a)],a)}async rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(n,a))):l.push((await this.lib.patternFillPolygon(n,a)))}return l.push((await this.lib.rectangle(e,t,i,s,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=await this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push((await this.lib.patternFillEllipse(e,t,i,s,a)));return l.push((await this.lib.ellipse(e,t,i,s,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,s){const n=await this.ellipse(e,t,i,i,s);return n.shape='circle',n}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,s,n,a,r)));return o.push((await this.lib.arc(e,t,i,s,n,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async polygon(e,t){const i=this._options(t),s=[];if(i.fill)if('solid'===i.fillStyle)s.push((await this.lib.solidFillPolygon(e,i)));else{const t=this.computePolygonSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=this.polygonPath(e),s.push(a)}return s.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',s,i)}async path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push((await this.lib.svgPath(e,i))),this._drawable('path',s,i)}}class N extends W{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a),a}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a),a}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a),a}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n),n}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const R='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new z(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(R&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a)}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a)}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a)}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.svg.ownerDocument||R&&document,a=n.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(n,l,i);break}case'path2Dfill':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=n.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${s(t[0])} ${s(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(n,l,i);a.appendChild(p),this.defs.appendChild(a),e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2);const n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=i.fill,n.style.strokeWidth=s+'',n.style.fill='none',n}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a)}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a)}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a)}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new N(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new L(e,t):new z(e,t)}};return M}(); diff --git a/dist/rough.umd.es5.js b/dist/rough.umd.es5.js index c483f16..5e589d3 100644 --- a/dist/rough.umd.es5.js +++ b/dist/rough.umd.es5.js @@ -62,26 +62,26 @@ return token.type === type; } var PARAMS = { - A: { length: 7 }, - a: { length: 7 }, - C: { length: 6 }, - c: { length: 6 }, - H: { length: 1 }, - h: { length: 1 }, - L: { length: 2 }, - l: { length: 2 }, - M: { length: 2 }, - m: { length: 2 }, - Q: { length: 4 }, - q: { length: 4 }, - S: { length: 4 }, - s: { length: 4 }, - T: { length: 4 }, - t: { length: 2 }, - V: { length: 1 }, - v: { length: 1 }, - Z: { length: 0 }, - z: { length: 0 } + 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: 4, + t: 2, + V: 1, + v: 1, + Z: 0, + z: 0 }; var ParsedPath = function () { @@ -131,7 +131,7 @@ if (mode === 'BOD') { if (token.text === 'M' || token.text === 'm') { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } else { this.parseData('M0,0' + d); @@ -139,10 +139,10 @@ } } else { if (isType(token, this.NUMBER)) { - param_length = PARAMS[mode].length; + param_length = PARAMS[mode]; } else { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } } @@ -156,7 +156,7 @@ return; } } - if (PARAMS[mode]) { + if (typeof PARAMS[mode] === 'number') { var segment = { key: mode, data: params }; this.segments.push(segment); index += param_length; diff --git a/dist/rough.umd.es5.min.js b/dist/rough.umd.es5.min.js index 0b5bdb3..06ca91d 100644 --- a/dist/rough.umd.es5.min.js +++ b/dist/rough.umd.es5.min.js @@ -1 +1 @@ -(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){var l=t.fillStyle||'hachure';if(!T[l])switch(l){case'zigzag':T[l]||(T[l]=new S(e));break;case'cross-hatch':T[l]||(T[l]=new A(e));break;case'hachure':default:l='hachure',T[l]||(T[l]=new O(e));}return T[l]}function l(e){if(z&&E&&self&&self.workly&&e.async&&!e.noWorker){var t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){var l=URL.createObjectURL(new Blob(['importScripts(\''+t+'\', \''+E+'\');\nworkly.expose(self.rough.createRenderer());']));return self.workly.proxy(l)}}return new C}var a=Math.round,n=Math.max,s=Math.min,i=Math.pow,r=Math.floor,p=Math.sqrt,d=Math.cos,h=Math.sin,g=Math.abs,f=Math.PI,o='function'==typeof Symbol&&'symbol'==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&'function'==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?'symbol':typeof e},u=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},y=function(){function e(e,t){for(var l,a=0;a_){var k=p(1-_/(this._rx*this._rx*this._ry*this._ry));this._rx*=k,this._ry*=k,v=0}else v=(i===o?-1:1)*p(_/(this._rx*this._rx*c*c+this._ry*this._ry*y*y));var s=v*this._rx*c/this._ry,x=-v*this._ry*y/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*s-this._sinPhi*x+(t[0]+l[0])/2,this._C[1]=this._sinPhi*s+this._cosPhi*x+(t[1]+l[1])/2,this._theta=this.calculateVectorAngle(1,0,(y-s)/this._rx,(c-x)/this._ry);var b=this.calculateVectorAngle((y-s)/this._rx,(c-x)/this._ry,(-y-s)/this._rx,(-c-x)/this._ry);!o&&0b&&(b+=2*f),this._numSegs=Math.ceil(g(b/(f/2))),this._delta=b/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}}return y(e,[{key:'getNextSegment',value:function(){if(this._segIndex===this._numSegs)return null;var e=d(this._theta),t=h(this._theta),l=this._theta+this._delta,a=d(l),n=h(l),i=[this._cosPhi*this._rx*a-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*a+this._cosPhi*this._ry*n+this._C[1]],s=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[i[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*a),i[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*a)];return this._theta=l,this._from=[i[0],i[1]],this._segIndex++,{cp1:s,cp2:o,to:i}}},{key:'calculateVectorAngle',value:function(e,t,l,a){var n=Math.atan2,i=n(t,e),s=n(a,l);return s>=i?s-i:2*f-(i-s)}}]),e}(),m=function(){function e(t,l){u(this,e),this.sets=t,this.closed=l}return y(e,[{key:'fit',value:function(e){var t=[],l=!0,a=!1,n=void 0;try{for(var s,o=this.sets[Symbol.iterator]();!(l=(s=o.next()).done);l=!0){var p=s.value,h=p.length,g=r(e*h);if(5>g){if(5>=h)continue;g=5}t.push(this.reduce(p,g))}}catch(e){a=!0,n=e}finally{try{!l&&o.return&&o.return()}finally{if(a)throw n}}var f='',u=!0,y=!1,c=void 0;try{for(var v,_,k=t[Symbol.iterator]();!(u=(v=k.next()).done);u=!0){_=v.value;for(var x,b=0;b<_.length;b++)x=_[b],f+=0===b?'M'+x[0]+','+x[1]:'L'+x[0]+','+x[1];this.closed&&(f+='z ')}}catch(e){y=!0,c=e}finally{try{!u&&k.return&&k.return()}finally{if(y)throw c}}return f}},{key:'distance',value:function(e,t){return p(i(e[0]-t[0],2)+i(e[1]-t[1],2))}},{key:'reduce',value:function(e,t){if(e.length<=t)return e;for(var l=e.slice(0);l.length>t;){for(var n=-1,o=-1,r=1;rn||s=s(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=s(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+r,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===t?(this.xi=e.px1,this.yi=l*this.xi+o,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(p))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):l===i?o==r&&(this.px1>=s(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=s(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(r-o)/(l-i),this.yi=l*this.xi+o,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}]),e}(),P=function(){function e(t,l,a,n,i,s,o,r){u(this,e),this.deltaX=0,this.hGap=0,this.top=t,this.bottom=l,this.left=a,this.right=n,this.gap=i,this.sinAngle=s,this.tanAngle=r,1e-4>g(s)?this.pos=a+i:.9999g(this.sinAngle)){if(this.posthis.right&&l>this.right;)if(this.pos+=this.hGap,t=this.pos-this.deltaX/2,l=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;var i=new w([t,a],[l,n]);this.sLeft&&i.intersects(this.sLeft)&&(t=i.xi,a=i.yi),this.sRight&&i.intersects(this.sRight)&&(l=i.xi,n=i.yi),0v&&(v=4*l.strokeWidth),v=n(v,.1);for(var _=c%180*(f/180),k=d(_),x=h(_),b=t(_),m=new P(g-1,u+1,r-1,p+1,v,x,k,b),w=void 0,O=null;null!=(w=m.nextLine());)for(var S=this.getIntersectingLines(w,e),A=0;A=u&&(u=4*i.strokeWidth);var y=i.fillWeight;0>y&&(y=i.strokeWidth/2);for(var c=t(h%180*(f/180)),v=d/r,_=p(v*c*v*c+1),k=v*c/_,x=1/_,b=u/(r*d/p(d*x*(d*x)+r*k*(r*k))/r),m=p(r*r-(e-r+b)*(e-r+b)),w=null,P=e-r+b;P_;)_+=2*f,k+=2*f;k-_>2*f&&(_=0,k=2*f);var x=2*f/u.curveStepCount,b=s(x/2,(k-_)/2),m=this._arc(b,o,y,c,v,_,k,1,u),w=this._arc(b,o,y,c,v,_,k,1.5,u),P=m.concat(w);return r&&(p?(P=P.concat(this.doubleLine(o,y,o+c*d(_),y+v*h(_),u)),P=P.concat(this.doubleLine(o,y,o+c*d(k),y+v*h(k),u))):(P.push({op:'lineTo',data:[o,y]}),P.push({op:'lineTo',data:[o+c*d(_),y+v*h(_)]}))),{type:'path',ops:P}}},{key:'svgPath',value:function(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');var l=new x(e);if(t.simplification){var a=new m(l.linearPoints,l.closed),n=a.fit(t.simplification);l=new x(n)}for(var o=[],r=l.segments||[],d=0;dy;)y+=2*f,c+=2*f;c-y>2*f&&(y=0,c=2*f);for(var v=(c-y)/s.curveStepCount,_=[],k=y;k<=c;k+=v)_.push([o+p*d(k),r+u*h(k)]);return _.push([o+p*d(c),r+u*h(c)]),_.push([o,r]),this.patternFillPolygon(_,s)}},{key:'getOffset',value:function(e,t,l){return l.roughness*(Math.random()*(t-e)+e)}},{key:'doubleLine',value:function(e,t,l,a,n){var i=this._line(e,t,l,a,n,!0,!1),s=this._line(e,t,l,a,n,!0,!0);return i.concat(s)}},{key:'_line',value:function(e,t,l,a,n,s,o){var r=i(e-l,2)+i(t-a,2),d=n.maxRandomnessOffset||0;100*(d*d)>r&&(d=p(r)/10);var h=d/2,g=.2+.2*Math.random(),f=n.bowing*n.maxRandomnessOffset*(a-t)/200,u=n.bowing*n.maxRandomnessOffset*(e-l)/200;f=this.getOffset(-f,f,n),u=this.getOffset(-u,u,n);var y=[];return s&&(o?y.push({op:'move',data:[e+this.getOffset(-h,h,n),t+this.getOffset(-h,h,n)]}):y.push({op:'move',data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?y.push({op:'bcurveTo',data:[f+e+(l-e)*g+this.getOffset(-h,h,n),u+t+(a-t)*g+this.getOffset(-h,h,n),f+e+2*(l-e)*g+this.getOffset(-h,h,n),u+t+2*(a-t)*g+this.getOffset(-h,h,n),l+this.getOffset(-h,h,n),a+this.getOffset(-h,h,n)]}):y.push({op:'bcurveTo',data:[f+e+(l-e)*g+this.getOffset(-d,d,n),u+t+(a-t)*g+this.getOffset(-d,d,n),f+e+2*(l-e)*g+this.getOffset(-d,d,n),u+t+2*(a-t)*g+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),a+this.getOffset(-d,d,n)]}),y}},{key:'_curve',value:function(e,t,l){var a=e.length,n=[];if(3g;g++)0===g?o.push({op:'move',data:[r.x,r.y]}):o.push({op:'move',data:[r.x+this.getOffset(-d[0],d[0],p),r.y+this.getOffset(-d[0],d[0],p)]}),h=[n+this.getOffset(-d[g],d[g],p),s+this.getOffset(-d[g],d[g],p)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[g],d[g],p),t+this.getOffset(-d[g],d[g],p),l+this.getOffset(-d[g],d[g],p),a+this.getOffset(-d[g],d[g],p),h[0],h[1]]});return r.setPosition(h[0],h[1]),o}},{key:'_processSegment',value:function(e,t,l,a){var n=[];switch(t.key){case'M':case'm':{var s='m'===t.key;if(2<=t.data.length){var o=+t.data[0],r=+t.data[1];s&&(o+=e.x,r+=e.y);var p=1*(a.maxRandomnessOffset||0);o+=this.getOffset(-p,p,a),r+=this.getOffset(-p,p,a),e.setPosition(o,r),n.push({op:'move',data:[o,r]})}break}case'L':case'l':{var d='l'===t.key;if(2<=t.data.length){var h=+t.data[0],g=+t.data[1];d&&(h+=e.x,g+=e.y),n=n.concat(this.doubleLine(e.x,e.y,h,g,a)),e.setPosition(h,g)}break}case'H':case'h':{var u='h'===t.key;if(t.data.length){var c=+t.data[0];u&&(c+=e.x),n=n.concat(this.doubleLine(e.x,e.y,c,e.y,a)),e.setPosition(c,e.y)}break}case'V':case'v':{var v='v'===t.key;if(t.data.length){var _=+t.data[0];v&&(_+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,_,a)),e.setPosition(e.x,_)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],a)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{var k='c'===t.key;if(6<=t.data.length){var m=+t.data[0],w=+t.data[1],P=+t.data[2],O=+t.data[3],S=+t.data[4],A=+t.data[5];k&&(m+=e.x,P+=e.x,S+=e.x,w+=e.y,O+=e.y,A+=e.y);var T=this._bezierTo(m,w,P,O,S,A,e,a);n=n.concat(T),e.bezierReflectionPoint=[S+(S-P),A+(A-O)]}break}case'S':case's':{var C='s'===t.key;if(4<=t.data.length){var z=+t.data[0],E=+t.data[1],L=+t.data[2],R=+t.data[3];C&&(z+=e.x,L+=e.x,E+=e.y,R+=e.y);var W=z,N=E,D=l?l.key:'',B=null;('c'===D||'C'===D||'s'===D||'S'===D)&&(B=e.bezierReflectionPoint),B&&(W=B[0],N=B[1]);var F=this._bezierTo(W,N,z,E,L,R,e,a);n=n.concat(F),e.bezierReflectionPoint=[L+(L-z),R+(R-E)]}break}case'Q':case'q':{var M='q'===t.key;if(4<=t.data.length){var q=+t.data[0],U=+t.data[1],X=+t.data[2],V=+t.data[3];M&&(q+=e.x,X+=e.x,U+=e.y,V+=e.y);var G=1*(1+.2*a.roughness),j=1.5*(1+.22*a.roughness);n.push({op:'move',data:[e.x+this.getOffset(-G,G,a),e.y+this.getOffset(-G,G,a)]});var I=[X+this.getOffset(-G,G,a),V+this.getOffset(-G,G,a)];n.push({op:'qcurveTo',data:[q+this.getOffset(-G,G,a),U+this.getOffset(-G,G,a),I[0],I[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-j,j,a),e.y+this.getOffset(-j,j,a)]}),I=[X+this.getOffset(-j,j,a),V+this.getOffset(-j,j,a)],n.push({op:'qcurveTo',data:[q+this.getOffset(-j,j,a),U+this.getOffset(-j,j,a),I[0],I[1]]}),e.setPosition(I[0],I[1]),e.quadReflectionPoint=[X+(X-q),V+(V-U)]}break}case'T':case't':{var Q='t'===t.key;if(2<=t.data.length){var $=+t.data[0],Z=+t.data[1];Q&&($+=e.x,Z+=e.y);var H=$,J=Z,Y=l?l.key:'',K=null;('q'===Y||'Q'===Y||'t'===Y||'T'===Y)&&(K=e.quadReflectionPoint),K&&(H=K[0],J=K[1]);var ee=1*(1+.2*a.roughness),te=1.5*(1+.22*a.roughness);n.push({op:'move',data:[e.x+this.getOffset(-ee,ee,a),e.y+this.getOffset(-ee,ee,a)]});var le=[$+this.getOffset(-ee,ee,a),Z+this.getOffset(-ee,ee,a)];n.push({op:'qcurveTo',data:[H+this.getOffset(-ee,ee,a),J+this.getOffset(-ee,ee,a),le[0],le[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-te,te,a),e.y+this.getOffset(-te,te,a)]}),le=[$+this.getOffset(-te,te,a),Z+this.getOffset(-te,te,a)],n.push({op:'qcurveTo',data:[H+this.getOffset(-te,te,a),J+this.getOffset(-te,te,a),le[0],le[1]]}),e.setPosition(le[0],le[1]),e.quadReflectionPoint=[$+($-H),Z+(Z-J)]}break}case'A':case'a':{var ae='a'===t.key;if(7<=t.data.length){var ne=+t.data[0],ie=+t.data[1],se=+t.data[2],oe=+t.data[3],re=+t.data[4],pe=+t.data[5],de=+t.data[6];if(ae&&(pe+=e.x,de+=e.y),pe===e.x&&de===e.y)break;if(0==ne||0==ie)n=n.concat(this.doubleLine(e.x,e.y,pe,de,a)),e.setPosition(pe,de);else for(var he=0;1>he;he++)for(var ge,fe=new b([e.x,e.y],[pe,de],[ne,ie],se,!!oe,!!re),ue=fe.getNextSegment();ue;)ge=this._bezierTo(ue.cp1[0],ue.cp1[1],ue.cp2[0],ue.cp2[1],ue.to[0],ue.to[1],e,a),n=n.concat(ge),ue=fe.getNextSegment()}break}default:}return n}}]),e}(),z='undefined'!=typeof self,E=z&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,L='undefined'!=typeof self,R=function(){function e(t,a){u(this,e),this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=t||{},this.surface=a,this.renderer=l(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}return y(e,[{key:'_options',value:function(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}},{key:'_drawable',value:function(e,t,l){return{shape:e,sets:t||[],options:l||this.defaultOptions}}},{key:'getCanvasSize',value:function(){var e=function(e){return e&&'object'===('undefined'==typeof e?'undefined':o(e))&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100};return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}},{key:'computePolygonSize',value:function(e){if(e.length){for(var t=e[0][0],l=e[0][0],a=e[0][1],o=e[0][1],r=1;rl&&(l=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:l,fill:'none'}}},{key:'opsToPath',value:function(e){var t='',l=!0,a=!1,n=void 0;try{for(var i,s=e.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case'move':t+='M'+r[0]+' '+r[1]+' ';break;case'bcurveTo':t+='C'+r[0]+' '+r[1]+', '+r[2]+' '+r[3]+', '+r[4]+' '+r[5]+' ';break;case'qcurveTo':t+='Q'+r[0]+' '+r[1]+', '+r[2]+' '+r[3]+' ';break;case'lineTo':t+='L'+r[0]+' '+r[1]+' ';}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}return t.trim()}},{key:'lib',get:function(){return this.renderer}}]),e}(),W='undefined'!=typeof document,N=function(){function e(t,l){u(this,e),this.canvas=t,this.ctx=this.canvas.getContext('2d'),this.gen=new R(l||null,this.canvas)}return y(e,[{key:'line',value:function(e,t,l,a,n){var i=this.gen.line(e,t,l,a,n);return this.draw(i),i}},{key:'rectangle',value:function(e,t,l,a,n){var i=this.gen.rectangle(e,t,l,a,n);return this.draw(i),i}},{key:'ellipse',value:function(e,t,l,a,n){var i=this.gen.ellipse(e,t,l,a,n);return this.draw(i),i}},{key:'circle',value:function(e,t,l,a){var n=this.gen.circle(e,t,l,a);return this.draw(n),n}},{key:'linearPath',value:function(e,t){var l=this.gen.linearPath(e,t);return this.draw(l),l}},{key:'polygon',value:function(e,t){var l=this.gen.polygon(e,t);return this.draw(l),l}},{key:'arc',value:function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2),e.save(),e.strokeStyle=l.fill||'',e.lineWidth=a,this._drawToContext(e,t),e.restore()}},{key:'_drawToContext',value:function(e,t){e.beginPath();var l=!0,a=!1,n=void 0;try{for(var i,s=t.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case'move':e.moveTo(r[0],r[1]);break;case'bcurveTo':e.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);break;case'qcurveTo':e.quadraticCurveTo(r[0],r[1],r[2],r[3]);break;case'lineTo':e.lineTo(r[0],r[1]);}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}'fillPath'===t.type?e.fill():e.stroke()}},{key:'generator',get:function(){return this.gen}}],[{key:'createRenderer',value:function(){return new C}}]),e}(),D=function(e){function t(){return u(this,t),v(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return c(t,e),y(t,[{key:'line',value:async function(e,t,l,a,n){var i=this._options(n);return this._drawable('line',[await this.lib.line(e,t,l,a,i)],i)}},{key:'rectangle',value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill){var o=[[e,t],[e+l,t],[e+l,t+a],[e,t+a]];'solid'===i.fillStyle?s.push((await this.lib.solidFillPolygon(o,i))):s.push((await this.lib.patternFillPolygon(o,i)))}return s.push((await this.lib.rectangle(e,t,l,a,i))),this._drawable('rectangle',s,i)}},{key:'ellipse',value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill)if('solid'===i.fillStyle){var o=await this.lib.ellipse(e,t,l,a,i);o.type='fillPath',s.push(o)}else s.push((await this.lib.patternFillEllipse(e,t,l,a,i)));return s.push((await this.lib.ellipse(e,t,l,a,i))),this._drawable('ellipse',s,i)}},{key:'circle',value:async function(e,t,l,a){var n=await this.ellipse(e,t,l,l,a);return n.shape='circle',n}},{key:'linearPath',value:async function(e,t){var l=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,l)],l)}},{key:'arc',value:async function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2);var n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=l.fill,n.style.strokeWidth=a+'',n.style.fill='none',n}},{key:'generator',get:function(){return this.gen}},{key:'defs',get:function(){if(F&&!this._defs){var e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}}],[{key:'createRenderer',value:function(){return new C}}]),e}(),q=function(e){function t(e,l){u(this,t);var a=v(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,l));return a.genAsync=new D(l||null,a.svg),a}return c(t,e),y(t,[{key:'line',value:async function(e,t,l,a,n){var i=await this.genAsync.line(e,t,l,a,n);return this.draw(i)}},{key:'rectangle',value:async function(e,t,l,a,n){var i=await this.genAsync.rectangle(e,t,l,a,n);return this.draw(i)}},{key:'ellipse',value:async function(e,t,l,a,n){var i=await this.genAsync.ellipse(e,t,l,a,n);return this.draw(i)}},{key:'circle',value:async function(e,t,l,a){var n=await this.genAsync.circle(e,t,l,a);return this.draw(n)}},{key:'linearPath',value:async function(e,t){var l=await this.genAsync.linearPath(e,t);return this.draw(l)}},{key:'polygon',value:async function(e,t){var l=await this.genAsync.polygon(e,t);return this.draw(l)}},{key:'arc',value:async function(e,t,l,a,n,i){var s=!!(6_){var k=p(1-_/(this._rx*this._rx*this._ry*this._ry));this._rx*=k,this._ry*=k,v=0}else v=(i===o?-1:1)*p(_/(this._rx*this._rx*c*c+this._ry*this._ry*y*y));var s=v*this._rx*c/this._ry,x=-v*this._ry*y/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*s-this._sinPhi*x+(t[0]+l[0])/2,this._C[1]=this._sinPhi*s+this._cosPhi*x+(t[1]+l[1])/2,this._theta=this.calculateVectorAngle(1,0,(y-s)/this._rx,(c-x)/this._ry);var b=this.calculateVectorAngle((y-s)/this._rx,(c-x)/this._ry,(-y-s)/this._rx,(-c-x)/this._ry);!o&&0b&&(b+=2*h),this._numSegs=Math.ceil(u(b/(h/2))),this._delta=b/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}}return y(e,[{key:'getNextSegment',value:function(){if(this._segIndex===this._numSegs)return null;var e=d(this._theta),t=f(this._theta),l=this._theta+this._delta,a=d(l),n=f(l),i=[this._cosPhi*this._rx*a-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*a+this._cosPhi*this._ry*n+this._C[1]],s=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[i[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*a),i[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*a)];return this._theta=l,this._from=[i[0],i[1]],this._segIndex++,{cp1:s,cp2:o,to:i}}},{key:'calculateVectorAngle',value:function(e,t,l,a){var n=Math.atan2,i=n(t,e),s=n(a,l);return s>=i?s-i:2*h-(i-s)}}]),e}(),m=function(){function e(t,l){g(this,e),this.sets=t,this.closed=l}return y(e,[{key:'fit',value:function(e){var t=[],l=!0,a=!1,n=void 0;try{for(var s,o=this.sets[Symbol.iterator]();!(l=(s=o.next()).done);l=!0){var p=s.value,f=p.length,u=r(e*f);if(5>u){if(5>=f)continue;u=5}t.push(this.reduce(p,u))}}catch(e){a=!0,n=e}finally{try{!l&&o.return&&o.return()}finally{if(a)throw n}}var h='',g=!0,y=!1,c=void 0;try{for(var v,_,k=t[Symbol.iterator]();!(g=(v=k.next()).done);g=!0){_=v.value;for(var x,b=0;b<_.length;b++)x=_[b],h+=0===b?'M'+x[0]+','+x[1]:'L'+x[0]+','+x[1];this.closed&&(h+='z ')}}catch(e){y=!0,c=e}finally{try{!g&&k.return&&k.return()}finally{if(y)throw c}}return h}},{key:'distance',value:function(e,t){return p(i(e[0]-t[0],2)+i(e[1]-t[1],2))}},{key:'reduce',value:function(e,t){if(e.length<=t)return e;for(var l=e.slice(0);l.length>t;){for(var n=-1,o=-1,r=1;rn||s=s(e.py1,e.py2)&&this.py1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=s(e.py1,e.py2)&&this.py2<=n(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+r,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>u(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===t?(this.xi=e.px1,this.yi=l*this.xi+o,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>u(p))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):l===i?o==r&&(this.px1>=s(e.px1,e.px2)&&this.px1<=n(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=s(e.px1,e.px2)&&this.px2<=n(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(r-o)/(l-i),this.yi=l*this.xi+o,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}]),e}(),P=function(){function e(t,l,a,n,i,s,o,r){g(this,e),this.deltaX=0,this.hGap=0,this.top=t,this.bottom=l,this.left=a,this.right=n,this.gap=i,this.sinAngle=s,this.tanAngle=r,1e-4>u(s)?this.pos=a+i:.9999u(this.sinAngle)){if(this.posthis.right&&l>this.right;)if(this.pos+=this.hGap,t=this.pos-this.deltaX/2,l=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;var i=new w([t,a],[l,n]);this.sLeft&&i.intersects(this.sLeft)&&(t=i.xi,a=i.yi),this.sRight&&i.intersects(this.sRight)&&(l=i.xi,n=i.yi),0v&&(v=4*l.strokeWidth),v=n(v,.1);for(var _=c%180*(h/180),k=d(_),x=f(_),b=t(_),m=new P(u-1,g+1,r-1,p+1,v,x,k,b),w=void 0,O=null;null!=(w=m.nextLine());)for(var S=this.getIntersectingLines(w,e),A=0;A=g&&(g=4*i.strokeWidth);var y=i.fillWeight;0>y&&(y=i.strokeWidth/2);for(var c=t(f%180*(h/180)),v=d/r,_=p(v*c*v*c+1),k=v*c/_,x=1/_,b=g/(r*d/p(d*x*(d*x)+r*k*(r*k))/r),m=p(r*r-(e-r+b)*(e-r+b)),w=null,P=e-r+b;P_;)_+=2*h,k+=2*h;k-_>2*h&&(_=0,k=2*h);var x=2*h/g.curveStepCount,b=s(x/2,(k-_)/2),m=this._arc(b,o,y,c,v,_,k,1,g),w=this._arc(b,o,y,c,v,_,k,1.5,g),P=m.concat(w);return r&&(p?(P=P.concat(this.doubleLine(o,y,o+c*d(_),y+v*f(_),g)),P=P.concat(this.doubleLine(o,y,o+c*d(k),y+v*f(k),g))):(P.push({op:'lineTo',data:[o,y]}),P.push({op:'lineTo',data:[o+c*d(_),y+v*f(_)]}))),{type:'path',ops:P}}},{key:'svgPath',value:function(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');var l=new x(e);if(t.simplification){var a=new m(l.linearPoints,l.closed),n=a.fit(t.simplification);l=new x(n)}for(var o=[],r=l.segments||[],d=0;dy;)y+=2*h,c+=2*h;c-y>2*h&&(y=0,c=2*h);for(var v=(c-y)/s.curveStepCount,_=[],k=y;k<=c;k+=v)_.push([o+p*d(k),r+g*f(k)]);return _.push([o+p*d(c),r+g*f(c)]),_.push([o,r]),this.patternFillPolygon(_,s)}},{key:'getOffset',value:function(e,t,l){return l.roughness*(Math.random()*(t-e)+e)}},{key:'doubleLine',value:function(e,t,l,a,n){var i=this._line(e,t,l,a,n,!0,!1),s=this._line(e,t,l,a,n,!0,!0);return i.concat(s)}},{key:'_line',value:function(e,t,l,a,n,s,o){var r=i(e-l,2)+i(t-a,2),d=n.maxRandomnessOffset||0;100*(d*d)>r&&(d=p(r)/10);var f=d/2,u=.2+.2*Math.random(),h=n.bowing*n.maxRandomnessOffset*(a-t)/200,g=n.bowing*n.maxRandomnessOffset*(e-l)/200;h=this.getOffset(-h,h,n),g=this.getOffset(-g,g,n);var y=[];return s&&(o?y.push({op:'move',data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):y.push({op:'move',data:[e+this.getOffset(-d,d,n),t+this.getOffset(-d,d,n)]})),o?y.push({op:'bcurveTo',data:[h+e+(l-e)*u+this.getOffset(-f,f,n),g+t+(a-t)*u+this.getOffset(-f,f,n),h+e+2*(l-e)*u+this.getOffset(-f,f,n),g+t+2*(a-t)*u+this.getOffset(-f,f,n),l+this.getOffset(-f,f,n),a+this.getOffset(-f,f,n)]}):y.push({op:'bcurveTo',data:[h+e+(l-e)*u+this.getOffset(-d,d,n),g+t+(a-t)*u+this.getOffset(-d,d,n),h+e+2*(l-e)*u+this.getOffset(-d,d,n),g+t+2*(a-t)*u+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),a+this.getOffset(-d,d,n)]}),y}},{key:'_curve',value:function(e,t,l){var a=e.length,n=[];if(3h;h++)0===h?o.push({op:'move',data:[r.x,r.y]}):o.push({op:'move',data:[r.x+this.getOffset(-d[0],d[0],p),r.y+this.getOffset(-d[0],d[0],p)]}),u=[n+this.getOffset(-d[h],d[h],p),s+this.getOffset(-d[h],d[h],p)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[h],d[h],p),t+this.getOffset(-d[h],d[h],p),l+this.getOffset(-d[h],d[h],p),a+this.getOffset(-d[h],d[h],p),u[0],u[1]]});return r.setPosition(u[0],u[1]),o}},{key:'_processSegment',value:function(e,t,l,a){var n=[];switch(t.key){case'M':case'm':{var s='m'===t.key;if(2<=t.data.length){var o=+t.data[0],r=+t.data[1];s&&(o+=e.x,r+=e.y);var p=1*(a.maxRandomnessOffset||0);o+=this.getOffset(-p,p,a),r+=this.getOffset(-p,p,a),e.setPosition(o,r),n.push({op:'move',data:[o,r]})}break}case'L':case'l':{var d='l'===t.key;if(2<=t.data.length){var u=+t.data[0],h=+t.data[1];d&&(u+=e.x,h+=e.y),n=n.concat(this.doubleLine(e.x,e.y,u,h,a)),e.setPosition(u,h)}break}case'H':case'h':{var g='h'===t.key;if(t.data.length){var c=+t.data[0];g&&(c+=e.x),n=n.concat(this.doubleLine(e.x,e.y,c,e.y,a)),e.setPosition(c,e.y)}break}case'V':case'v':{var v='v'===t.key;if(t.data.length){var _=+t.data[0];v&&(_+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,_,a)),e.setPosition(e.x,_)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],a)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{var k='c'===t.key;if(6<=t.data.length){var m=+t.data[0],w=+t.data[1],P=+t.data[2],O=+t.data[3],S=+t.data[4],A=+t.data[5];k&&(m+=e.x,P+=e.x,S+=e.x,w+=e.y,O+=e.y,A+=e.y);var T=this._bezierTo(m,w,P,O,S,A,e,a);n=n.concat(T),e.bezierReflectionPoint=[S+(S-P),A+(A-O)]}break}case'S':case's':{var C='s'===t.key;if(4<=t.data.length){var z=+t.data[0],E=+t.data[1],L=+t.data[2],R=+t.data[3];C&&(z+=e.x,L+=e.x,E+=e.y,R+=e.y);var W=z,N=E,D=l?l.key:'',B=null;('c'===D||'C'===D||'s'===D||'S'===D)&&(B=e.bezierReflectionPoint),B&&(W=B[0],N=B[1]);var F=this._bezierTo(W,N,z,E,L,R,e,a);n=n.concat(F),e.bezierReflectionPoint=[L+(L-z),R+(R-E)]}break}case'Q':case'q':{var M='q'===t.key;if(4<=t.data.length){var q=+t.data[0],U=+t.data[1],X=+t.data[2],V=+t.data[3];M&&(q+=e.x,X+=e.x,U+=e.y,V+=e.y);var G=1*(1+.2*a.roughness),j=1.5*(1+.22*a.roughness);n.push({op:'move',data:[e.x+this.getOffset(-G,G,a),e.y+this.getOffset(-G,G,a)]});var I=[X+this.getOffset(-G,G,a),V+this.getOffset(-G,G,a)];n.push({op:'qcurveTo',data:[q+this.getOffset(-G,G,a),U+this.getOffset(-G,G,a),I[0],I[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-j,j,a),e.y+this.getOffset(-j,j,a)]}),I=[X+this.getOffset(-j,j,a),V+this.getOffset(-j,j,a)],n.push({op:'qcurveTo',data:[q+this.getOffset(-j,j,a),U+this.getOffset(-j,j,a),I[0],I[1]]}),e.setPosition(I[0],I[1]),e.quadReflectionPoint=[X+(X-q),V+(V-U)]}break}case'T':case't':{var Q='t'===t.key;if(2<=t.data.length){var $=+t.data[0],Z=+t.data[1];Q&&($+=e.x,Z+=e.y);var H=$,J=Z,Y=l?l.key:'',K=null;('q'===Y||'Q'===Y||'t'===Y||'T'===Y)&&(K=e.quadReflectionPoint),K&&(H=K[0],J=K[1]);var ee=1*(1+.2*a.roughness),te=1.5*(1+.22*a.roughness);n.push({op:'move',data:[e.x+this.getOffset(-ee,ee,a),e.y+this.getOffset(-ee,ee,a)]});var le=[$+this.getOffset(-ee,ee,a),Z+this.getOffset(-ee,ee,a)];n.push({op:'qcurveTo',data:[H+this.getOffset(-ee,ee,a),J+this.getOffset(-ee,ee,a),le[0],le[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-te,te,a),e.y+this.getOffset(-te,te,a)]}),le=[$+this.getOffset(-te,te,a),Z+this.getOffset(-te,te,a)],n.push({op:'qcurveTo',data:[H+this.getOffset(-te,te,a),J+this.getOffset(-te,te,a),le[0],le[1]]}),e.setPosition(le[0],le[1]),e.quadReflectionPoint=[$+($-H),Z+(Z-J)]}break}case'A':case'a':{var ae='a'===t.key;if(7<=t.data.length){var ne=+t.data[0],ie=+t.data[1],se=+t.data[2],oe=+t.data[3],re=+t.data[4],pe=+t.data[5],de=+t.data[6];if(ae&&(pe+=e.x,de+=e.y),pe===e.x&&de===e.y)break;if(0==ne||0==ie)n=n.concat(this.doubleLine(e.x,e.y,pe,de,a)),e.setPosition(pe,de);else for(var fe=0;1>fe;fe++)for(var ue,he=new b([e.x,e.y],[pe,de],[ne,ie],se,!!oe,!!re),ge=he.getNextSegment();ge;)ue=this._bezierTo(ge.cp1[0],ge.cp1[1],ge.cp2[0],ge.cp2[1],ge.to[0],ge.to[1],e,a),n=n.concat(ue),ge=he.getNextSegment()}break}default:}return n}}]),e}(),z='undefined'!=typeof self,E=z&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,L='undefined'!=typeof self,R=function(){function e(t,a){g(this,e),this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=t||{},this.surface=a,this.renderer=l(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}return y(e,[{key:'_options',value:function(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}},{key:'_drawable',value:function(e,t,l){return{shape:e,sets:t||[],options:l||this.defaultOptions}}},{key:'getCanvasSize',value:function(){var e=function(e){return e&&'object'===('undefined'==typeof e?'undefined':o(e))&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100};return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}},{key:'computePolygonSize',value:function(e){if(e.length){for(var t=e[0][0],l=e[0][0],a=e[0][1],o=e[0][1],r=1;rl&&(l=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:l,fill:'none'}}},{key:'opsToPath',value:function(e){var t='',l=!0,a=!1,n=void 0;try{for(var i,s=e.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case'move':t+='M'+r[0]+' '+r[1]+' ';break;case'bcurveTo':t+='C'+r[0]+' '+r[1]+', '+r[2]+' '+r[3]+', '+r[4]+' '+r[5]+' ';break;case'qcurveTo':t+='Q'+r[0]+' '+r[1]+', '+r[2]+' '+r[3]+' ';break;case'lineTo':t+='L'+r[0]+' '+r[1]+' ';}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}return t.trim()}},{key:'lib',get:function(){return this.renderer}}]),e}(),W='undefined'!=typeof document,N=function(){function e(t,l){g(this,e),this.canvas=t,this.ctx=this.canvas.getContext('2d'),this.gen=new R(l||null,this.canvas)}return y(e,[{key:'line',value:function(e,t,l,a,n){var i=this.gen.line(e,t,l,a,n);return this.draw(i),i}},{key:'rectangle',value:function(e,t,l,a,n){var i=this.gen.rectangle(e,t,l,a,n);return this.draw(i),i}},{key:'ellipse',value:function(e,t,l,a,n){var i=this.gen.ellipse(e,t,l,a,n);return this.draw(i),i}},{key:'circle',value:function(e,t,l,a){var n=this.gen.circle(e,t,l,a);return this.draw(n),n}},{key:'linearPath',value:function(e,t){var l=this.gen.linearPath(e,t);return this.draw(l),l}},{key:'polygon',value:function(e,t){var l=this.gen.polygon(e,t);return this.draw(l),l}},{key:'arc',value:function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2),e.save(),e.strokeStyle=l.fill||'',e.lineWidth=a,this._drawToContext(e,t),e.restore()}},{key:'_drawToContext',value:function(e,t){e.beginPath();var l=!0,a=!1,n=void 0;try{for(var i,s=t.ops[Symbol.iterator]();!(l=(i=s.next()).done);l=!0){var o=i.value,r=o.data;switch(o.op){case'move':e.moveTo(r[0],r[1]);break;case'bcurveTo':e.bezierCurveTo(r[0],r[1],r[2],r[3],r[4],r[5]);break;case'qcurveTo':e.quadraticCurveTo(r[0],r[1],r[2],r[3]);break;case'lineTo':e.lineTo(r[0],r[1]);}}}catch(e){a=!0,n=e}finally{try{!l&&s.return&&s.return()}finally{if(a)throw n}}'fillPath'===t.type?e.fill():e.stroke()}},{key:'generator',get:function(){return this.gen}}],[{key:'createRenderer',value:function(){return new C}}]),e}(),D=function(e){function t(){return g(this,t),v(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return c(t,e),y(t,[{key:'line',value:async function(e,t,l,a,n){var i=this._options(n);return this._drawable('line',[await this.lib.line(e,t,l,a,i)],i)}},{key:'rectangle',value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill){var o=[[e,t],[e+l,t],[e+l,t+a],[e,t+a]];'solid'===i.fillStyle?s.push((await this.lib.solidFillPolygon(o,i))):s.push((await this.lib.patternFillPolygon(o,i)))}return s.push((await this.lib.rectangle(e,t,l,a,i))),this._drawable('rectangle',s,i)}},{key:'ellipse',value:async function(e,t,l,a,n){var i=this._options(n),s=[];if(i.fill)if('solid'===i.fillStyle){var o=await this.lib.ellipse(e,t,l,a,i);o.type='fillPath',s.push(o)}else s.push((await this.lib.patternFillEllipse(e,t,l,a,i)));return s.push((await this.lib.ellipse(e,t,l,a,i))),this._drawable('ellipse',s,i)}},{key:'circle',value:async function(e,t,l,a){var n=await this.ellipse(e,t,l,l,a);return n.shape='circle',n}},{key:'linearPath',value:async function(e,t){var l=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,l)],l)}},{key:'arc',value:async function(e,t,l,a,n,i){var s=!!(6a&&(a=l.strokeWidth/2);var n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=l.fill,n.style.strokeWidth=a+'',n.style.fill='none',n}},{key:'generator',get:function(){return this.gen}},{key:'defs',get:function(){if(F&&!this._defs){var e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}}],[{key:'createRenderer',value:function(){return new C}}]),e}(),q=function(e){function t(e,l){g(this,t);var a=v(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,l));return a.genAsync=new D(l||null,a.svg),a}return c(t,e),y(t,[{key:'line',value:async function(e,t,l,a,n){var i=await this.genAsync.line(e,t,l,a,n);return this.draw(i)}},{key:'rectangle',value:async function(e,t,l,a,n){var i=await this.genAsync.rectangle(e,t,l,a,n);return this.draw(i)}},{key:'ellipse',value:async function(e,t,l,a,n){var i=await this.genAsync.ellipse(e,t,l,a,n);return this.draw(i)}},{key:'circle',value:async function(e,t,l,a){var n=await this.genAsync.circle(e,t,l,a);return this.draw(n)}},{key:'linearPath',value:async function(e,t){var l=await this.genAsync.linearPath(e,t);return this.draw(l)}},{key:'polygon',value:async function(e,t){var l=await this.genAsync.polygon(e,t);return this.draw(l)}},{key:'arc',value:async function(e,t,l,a,n,i){var s=!!(6u){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(s===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*f(this._delta/4)*f(this._delta/4)/f(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=h(this._theta),t=f(this._theta),i=this._theta+this._delta,n=h(i),s=f(i),a=[this._cosPhi*this._rx*n-this._sinPhi*this._ry*s+this._C[0],this._sinPhi*this._rx*n+this._cosPhi*this._ry*s+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*s+this._sinPhi*this._ry*n),a[1]+this._T*(this._sinPhi*this._rx*s-this._cosPhi*this._ry*n)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,n){var s=Math.atan2;const a=s(t,e),l=s(n,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const n=i.length;let s=r(e*n);if(5>s){if(5>=n)continue;s=5}t.push(this.reduce(i,s))}let n='';for(const s of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+s,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+n,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?n==s&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(s-n)/(t-i),this.yi=t*this.xi+n,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,n,s,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=n,this.gap=s,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+s:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=h(y),x=f(y),b=s(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,s=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=f&&(f=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=s(h%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=f/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let s=e-p+m;sx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*h(x),u+_*f(x),d)),v=v.concat(this.doubleLine(o,u,o+y*h(b),u+_*f(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*h(x),u+_*f(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*h(g),p+d*f(g)]);return x.push([o+r*h(y),p+d*f(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,n,s){const a=this._line(e,t,i,n,s,!0,!1),l=this._line(e,t,i,n,s,!0,!0);return a.concat(l)}_line(e,t,i,n,s,l,o){const p=a(e-i,2)+a(t-n,2);let r=s.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const h=r/2,f=.2+.2*Math.random();let g=s.bowing*s.maxRandomnessOffset*(n-t)/200,c=s.bowing*s.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,s),c=this.getOffset(-c,c,s);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-h,h,s),t+this.getOffset(-h,h,s)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,s),t+this.getOffset(-r,r,s)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-h,h,s),c+t+(n-t)*f+this.getOffset(-h,h,s),g+e+2*(i-e)*f+this.getOffset(-h,h,s),c+t+2*(n-t)*f+this.getOffset(-h,h,s),i+this.getOffset(-h,h,s),n+this.getOffset(-h,h,s)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*f+this.getOffset(-r,r,s),c+t+(n-t)*f+this.getOffset(-r,r,s),g+e+2*(i-e)*f+this.getOffset(-r,r,s),c+t+2*(n-t)*f+this.getOffset(-r,r,s),i+this.getOffset(-r,r,s),n+this.getOffset(-r,r,s)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,n){let s=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(n.maxRandomnessOffset||0);a+=this.getOffset(-o,o,n),l+=this.getOffset(-o,o,n),e.setPosition(a,l),s.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),s=s.concat(this.doubleLine(e.x,e.y,a,l,n)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),s=s.concat(this.doubleLine(e.x,e.y,a,e.y,n)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),s=s.concat(this.doubleLine(e.x,e.y,e.x,a,n)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(s=s.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],n)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const h=this._bezierTo(a,l,o,p,r,d,e,n);s=s.concat(h),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,h=o;const f=i?i.key:'';let g=null;('c'===f||'C'===f||'s'===f||'S'===f)&&(g=e.bezierReflectionPoint),g&&(d=g[0],h=g[1]);const c=this._bezierTo(d,h,l,o,p,r,e,n);s=s.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*n.roughness),d=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-r,r,n),e.y+this.getOffset(-r,r,n)]});let h=[o+this.getOffset(-r,r,n),p+this.getOffset(-r,r,n)];s.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,n),l+this.getOffset(-r,r,n),h[0],h[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-d,d,n),e.y+this.getOffset(-d,d,n)]}),h=[o+this.getOffset(-d,d,n),p+this.getOffset(-d,d,n)],s.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,n),l+this.getOffset(-d,d,n),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*n.roughness),c=1.5*(1+.22*n.roughness);s.push({op:'move',data:[e.x+this.getOffset(-g,g,n),e.y+this.getOffset(-g,g,n)]});let u=[l+this.getOffset(-g,g,n),o+this.getOffset(-g,g,n)];s.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,n),r+this.getOffset(-g,g,n),u[0],u[1]]}),s.push({op:'move',data:[e.x+this.getOffset(-c,c,n),e.y+this.getOffset(-c,c,n)]}),u=[l+this.getOffset(-c,c,n),o+this.getOffset(-c,c,n)],s.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,n),r+this.getOffset(-c,c,n),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],h=+t.data[6];if(i&&(d+=e.x,h+=e.y),d===e.x&&h===e.y)break;if(0==a||0==l)s=s.concat(this.doubleLine(e.x,e.y,d,h,n)),e.setPosition(d,h);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,h],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,n);s=s.concat(a),i=t.getNextSegment()}}}break}default:}return s}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class z{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePolygonSize(e){if(e.length){let t=e[0][0],n=e[0][0],s=e[0][1],a=e[0][1];for(let o=1;oi&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const E='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new z(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a),a}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a),a}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a),a}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s),s}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.ctx;for(const s of t)switch(s.type){case'path':n.save(),n.strokeStyle=i.stroke,n.lineWidth=i.strokeWidth,this._drawToContext(n,s),n.restore();break;case'fillPath':n.save(),n.fillStyle=i.fill||'',this._drawToContext(n,s),n.restore();break;case'fillSketch':this.fillSketch(n,s,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(s.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(E){const e=s.size,t=document.createElement('canvas'),n=t.getContext('2d'),a=this.computeBBox(s.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,n.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(n,s,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(s.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(E)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const n=i.getBBox();return document.body.removeChild(t),n}catch(e){}return null}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=n,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class L extends z{async line(e,t,i,n,s){const a=this._options(s);return this._drawable('line',[await this.lib.line(e,t,i,n,a)],a)}async rectangle(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill){const s=[[e,t],[e+i,t],[e+i,t+n],[e,t+n]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(s,a))):l.push((await this.lib.patternFillPolygon(s,a)))}return l.push((await this.lib.rectangle(e,t,i,n,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,n,s){const a=this._options(s),l=[];if(a.fill)if('solid'===a.fillStyle){const s=await this.lib.ellipse(e,t,i,n,a);s.type='fillPath',l.push(s)}else l.push((await this.lib.patternFillEllipse(e,t,i,n,a)));return l.push((await this.lib.ellipse(e,t,i,n,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,n){const s=await this.ellipse(e,t,i,i,n);return s.shape='circle',s}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async arc(e,t,i,n,s,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,n,s,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,n,s,a,r)));return o.push((await this.lib.arc(e,t,i,n,s,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async polygon(e,t){const i=this._options(t),n=[];if(i.fill)if('solid'===i.fillStyle)n.push((await this.lib.solidFillPolygon(e,i)));else{const t=this.computePolygonSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=this.polygonPath(e),n.push(a)}return n.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',n,i)}async path(e,t){const i=this._options(t),n=[];if(!e)return this._drawable('path',n,i);if(i.fill)if('solid'===i.fillStyle){n.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),s=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(s,i);a.type='path2Dpattern',a.size=t,a.path=e,n.push(a)}return n.push((await this.lib.svgPath(e,i))),this._drawable('path',n,i)}}class N extends W{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a),a}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a),a}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a),a}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s),s}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const R='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new z(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(R&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,n,s){const a=this.gen.line(e,t,i,n,s);return this.draw(a)}rectangle(e,t,i,n,s){const a=this.gen.rectangle(e,t,i,n,s);return this.draw(a)}ellipse(e,t,i,n,s){const a=this.gen.ellipse(e,t,i,n,s);return this.draw(a)}circle(e,t,i,n){const s=this.gen.circle(e,t,i,n);return this.draw(s)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,n,s,a,l=!1,o){const p=this.gen.arc(e,t,i,n,s,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.svg.ownerDocument||R&&document,a=s.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(s,l,i);break}case'path2Dfill':{e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=s.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${n(t[0])} ${n(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(s,l,i);a.appendChild(p),this.defs.appendChild(a),e=s.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let n=i.fillWeight;0>n&&(n=i.strokeWidth/2);const s=e.createElementNS('http://www.w3.org/2000/svg','path');return s.setAttribute('d',this.opsToPath(t)),s.style.stroke=i.fill,s.style.strokeWidth=n+'',s.style.fill='none',s}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,n,s){const a=await this.genAsync.line(e,t,i,n,s);return this.draw(a)}async rectangle(e,t,i,n,s){const a=await this.genAsync.rectangle(e,t,i,n,s);return this.draw(a)}async ellipse(e,t,i,n,s){const a=await this.genAsync.ellipse(e,t,i,n,s);return this.draw(a)}async circle(e,t,i,n){const s=await this.genAsync.circle(e,t,i,n);return this.draw(s)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,n,s,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,n,s,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new N(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new L(e,t):new z(e,t)}};return M}); +(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.rough=t()})(this,function(){'use strict';function e(e,t){return e.type===t}function t(e,t){let i=t.fillStyle||'hachure';if(!k[i])switch(i){case'zigzag':k[i]||(k[i]=new P(e));break;case'cross-hatch':k[i]||(k[i]=new v(e));break;case'hachure':default:i='hachure',k[i]||(k[i]=new O(e));}return k[i]}function i(e){if(S&&T&&self&&self.workly&&e.async&&!e.noWorker){const t=e.worklyURL||'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js';if(t){const e=`importScripts('${t}', '${T}');\nworkly.expose(self.rough.createRenderer());`,i=URL.createObjectURL(new Blob([e]));return self.workly.proxy(i)}}return new A}var s=Math.round,n=Math.tan,l=Math.max,p=Math.min,o=Number.MAX_VALUE,a=Math.pow,r=Math.floor,d=Math.sqrt,f=Math.cos,h=Math.sin,g=Math.abs,c=Math.PI;const u={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:4,t:2,V:1,v:1,Z:0,z:0};class y{constructor(e){this.COMMAND=0,this.NUMBER=1,this.EOD=2,this.segments=[],this.parseData(e),this.processPoints()}tokenize(e){const t=[];for(;''!==e;)if(e.match(/^([ \t\r\n,]+)/))e=e.substr(RegExp.$1.length);else if(e.match(/^([aAcChHlLmMqQsStTvVzZ])/))t[t.length]={type:this.COMMAND,text:RegExp.$1},e=e.substr(RegExp.$1.length);else if(e.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))t[t.length]={type:this.NUMBER,text:`${parseFloat(RegExp.$1)}`},e=e.substr(RegExp.$1.length);else return console.error('Unrecognized segment command: '+e),[];return t[t.length]={type:this.EOD,text:''},t}parseData(t){const s=this.tokenize(t);let n=0,i=s[n],a='BOD';for(this.segments=[];!e(i,this.EOD);){let l;const o=[];if(!('BOD'===a))e(i,this.NUMBER)?l=u[a]:(n++,l=u[i.text],a=i.text);else if('M'===i.text||'m'===i.text)n++,l=u[i.text],a=i.text;else return void this.parseData('M0,0'+t);if(n+lu){const e=d(1-u/(this._rx*this._rx*this._ry*this._ry));this._rx*=e,this._ry*=e,r=0}else r=(n===a?-1:1)*d(u/(this._rx*this._rx*p*p+this._ry*this._ry*o*o));const y=r*this._rx*p/this._ry,_=-r*this._ry*o/this._rx;this._C=[0,0],this._C[0]=this._cosPhi*y-this._sinPhi*_+(e[0]+t[0])/2,this._C[1]=this._sinPhi*y+this._cosPhi*_+(e[1]+t[1])/2,this._theta=this.calculateVectorAngle(1,0,(o-y)/this._rx,(p-_)/this._ry);let x=this.calculateVectorAngle((o-y)/this._rx,(p-_)/this._ry,(-o-y)/this._rx,(-p-_)/this._ry);!a&&0x&&(x+=2*c),this._numSegs=Math.ceil(g(x/(c/2))),this._delta=x/this._numSegs,this._T=8/3*h(this._delta/4)*h(this._delta/4)/h(this._delta/2)}getNextSegment(){if(this._segIndex===this._numSegs)return null;const e=f(this._theta),t=h(this._theta),i=this._theta+this._delta,s=f(i),n=h(i),a=[this._cosPhi*this._rx*s-this._sinPhi*this._ry*n+this._C[0],this._sinPhi*this._rx*s+this._cosPhi*this._ry*n+this._C[1]],l=[this._from[0]+this._T*(-this._cosPhi*this._rx*t-this._sinPhi*this._ry*e),this._from[1]+this._T*(-this._sinPhi*this._rx*t+this._cosPhi*this._ry*e)],o=[a[0]+this._T*(this._cosPhi*this._rx*n+this._sinPhi*this._ry*s),a[1]+this._T*(this._sinPhi*this._rx*n-this._cosPhi*this._ry*s)];return this._theta=i,this._from=[a[0],a[1]],this._segIndex++,{cp1:l,cp2:o,to:a}}calculateVectorAngle(e,t,i,s){var n=Math.atan2;const a=n(t,e),l=n(s,i);return l>=a?l-a:2*c-(a-l)}}class x{constructor(e,t){this.sets=e,this.closed=t}fit(e){const t=[];for(const i of this.sets){const s=i.length;let n=r(e*s);if(5>n){if(5>=s)continue;n=5}t.push(this.reduce(i,n))}let s='';for(const n of t){for(let e=0;et;){let e=-1,t=-1;for(let l=1;le||s=p(e.py1,e.py2)&&this.py1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.py2>=p(e.py1,e.py2)&&this.py2<=l(e.py1,e.py2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=this.px1,this.yi=i*this.xi+n,!(-1e-5>(this.py1-this.yi)*(this.yi-this.py2)||-1e-5>(e.py1-this.yi)*(this.yi-e.py2))&&(!(1e-5>g(e.a))||!(-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))):i===o?(this.xi=e.px1,this.yi=t*this.xi+s,!(-1e-5>(e.py1-this.yi)*(this.yi-e.py2)||-1e-5>(this.py1-this.yi)*(this.yi-this.py2))&&(!(1e-5>g(r))||!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)))):t===i?s==n&&(this.px1>=p(e.px1,e.px2)&&this.px1<=l(e.py1,e.py2)?(this.xi=this.px1,this.yi=this.py1,!0):!!(this.px2>=p(e.px1,e.px2)&&this.px2<=l(e.px1,e.px2))&&(this.xi=this.px2,this.yi=this.py2,!0)):(this.xi=(n-s)/(t-i),this.yi=t*this.xi+s,!(-1e-5>(this.px1-this.xi)*(this.xi-this.px2)||-1e-5>(e.px1-this.xi)*(this.xi-e.px2)))}}class m{constructor(e,t,i,s,n,a,l,o){this.deltaX=0,this.hGap=0,this.top=e,this.bottom=t,this.left=i,this.right=s,this.gap=n,this.sinAngle=a,this.tanAngle=o,1e-4>g(a)?this.pos=i+n:.9999g(this.sinAngle)){if(this.posthis.right&&t>this.right;)if(this.pos+=this.hGap,e=this.pos-this.deltaX/2,t=this.pos+this.deltaX/2,this.pos>this.right+this.deltaX)return null;const a=new w([e,i],[t,n]);this.sLeft&&a.intersects(this.sLeft)&&(e=a.xi,i=a.yi),this.sRight&&a.intersects(this.sRight)&&(t=a.xi,n=a.yi),0u&&(u=4*t.strokeWidth),u=l(u,.1);const y=i%180*(c/180),_=f(y),x=h(y),b=n(y),w=new m(d-1,g+1,o-1,r+1,u,x,_,b);for(let i,n=null;null!=(i=w.nextLine());){const l=this.getIntersectingLines(i,e);for(let e=0;e=h&&(h=4*a.strokeWidth);let u=a.fillWeight;0>u&&(u=a.strokeWidth/2);const y=n(f%180*(c/180)),_=r/p,x=d(_*y*_*y+1),b=_*y/x,w=1/x,m=h/(p*r/d(r*w*(r*w)+p*b*(p*b))/p);let O=d(p*p-(e-p+m)*(e-p+m)),P=null;for(let n=e-p+m;nx;)x+=2*c,b+=2*c;b-x>2*c&&(x=0,b=2*c);const w=2*c/d.curveStepCount,m=p(w/2,(b-x)/2),O=this._arc(m,o,u,y,_,x,b,1,d),P=this._arc(m,o,u,y,_,x,b,1.5,d);let v=O.concat(P);return l&&(r?(v=v.concat(this.doubleLine(o,u,o+y*f(x),u+_*h(x),d)),v=v.concat(this.doubleLine(o,u,o+y*f(b),u+_*h(b),d))):(v.push({op:'lineTo',data:[o,u]}),v.push({op:'lineTo',data:[o+y*f(x),u+_*h(x)]}))),{type:'path',ops:v}}svgPath(e,t){e=(e||'').replace(/\n/g,' ').replace(/(-\s)/g,'-').replace('/(ss)/g',' ');let n=new _(e);if(t.simplification){const e=new x(n.linearPoints,n.closed),i=e.fit(t.simplification);n=new _(i)}let a=[];const l=n.segments||[];for(let o=0;ou;)u+=2*c,y+=2*c;y-u>2*c&&(u=0,y=2*c);const _=(y-u)/l.curveStepCount,x=[];for(let g=u;g<=y;g+=_)x.push([o+r*f(g),p+d*h(g)]);return x.push([o+r*f(y),p+d*h(y)]),x.push([o,p]),this.patternFillPolygon(x,l)}getOffset(e,t,i){return i.roughness*(Math.random()*(t-e)+e)}doubleLine(e,t,i,s,n){const a=this._line(e,t,i,s,n,!0,!1),l=this._line(e,t,i,s,n,!0,!0);return a.concat(l)}_line(e,t,i,s,n,l,o){const p=a(e-i,2)+a(t-s,2);let r=n.maxRandomnessOffset||0;100*(r*r)>p&&(r=d(p)/10);const f=r/2,h=.2+.2*Math.random();let g=n.bowing*n.maxRandomnessOffset*(s-t)/200,c=n.bowing*n.maxRandomnessOffset*(e-i)/200;g=this.getOffset(-g,g,n),c=this.getOffset(-c,c,n);const u=[];return l&&(o?u.push({op:'move',data:[e+this.getOffset(-f,f,n),t+this.getOffset(-f,f,n)]}):u.push({op:'move',data:[e+this.getOffset(-r,r,n),t+this.getOffset(-r,r,n)]})),o?u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-f,f,n),c+t+(s-t)*h+this.getOffset(-f,f,n),g+e+2*(i-e)*h+this.getOffset(-f,f,n),c+t+2*(s-t)*h+this.getOffset(-f,f,n),i+this.getOffset(-f,f,n),s+this.getOffset(-f,f,n)]}):u.push({op:'bcurveTo',data:[g+e+(i-e)*h+this.getOffset(-r,r,n),c+t+(s-t)*h+this.getOffset(-r,r,n),g+e+2*(i-e)*h+this.getOffset(-r,r,n),c+t+2*(s-t)*h+this.getOffset(-r,r,n),i+this.getOffset(-r,r,n),s+this.getOffset(-r,r,n)]}),u}_curve(e,t,i){const n=e.length;let a=[];if(3f;f++)0===f?o.push({op:'move',data:[p.x,p.y]}):o.push({op:'move',data:[p.x+this.getOffset(-d[0],d[0],r),p.y+this.getOffset(-d[0],d[0],r)]}),h=[a+this.getOffset(-d[f],d[f],r),l+this.getOffset(-d[f],d[f],r)],o.push({op:'bcurveTo',data:[e+this.getOffset(-d[f],d[f],r),t+this.getOffset(-d[f],d[f],r),s+this.getOffset(-d[f],d[f],r),n+this.getOffset(-d[f],d[f],r),h[0],h[1]]});return p.setPosition(h[0],h[1]),o}_processSegment(e,t,i,s){let n=[];switch(t.key){case'M':case'm':{const i='m'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y);const o=1*(s.maxRandomnessOffset||0);a+=this.getOffset(-o,o,s),l+=this.getOffset(-o,o,s),e.setPosition(a,l),n.push({op:'move',data:[a,l]})}break}case'L':case'l':{const i='l'===t.key;if(2<=t.data.length){let a=+t.data[0],l=+t.data[1];i&&(a+=e.x,l+=e.y),n=n.concat(this.doubleLine(e.x,e.y,a,l,s)),e.setPosition(a,l)}break}case'H':case'h':{const i='h'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.x),n=n.concat(this.doubleLine(e.x,e.y,a,e.y,s)),e.setPosition(a,e.y)}break}case'V':case'v':{const i='v'===t.key;if(t.data.length){let a=+t.data[0];i&&(a+=e.y),n=n.concat(this.doubleLine(e.x,e.y,e.x,a,s)),e.setPosition(e.x,a)}break}case'Z':case'z':{e.first&&(n=n.concat(this.doubleLine(e.x,e.y,e.first[0],e.first[1],s)),e.setPosition(e.first[0],e.first[1]),e.first=null);break}case'C':case'c':{const i='c'===t.key;if(6<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4],d=+t.data[5];i&&(a+=e.x,o+=e.x,r+=e.x,l+=e.y,p+=e.y,d+=e.y);const f=this._bezierTo(a,l,o,p,r,d,e,s);n=n.concat(f),e.bezierReflectionPoint=[r+(r-o),d+(d-p)]}break}case'S':case's':{const a='s'===t.key;if(4<=t.data.length){let l=+t.data[0],o=+t.data[1],p=+t.data[2],r=+t.data[3];a&&(l+=e.x,p+=e.x,o+=e.y,r+=e.y);let d=l,f=o;const h=i?i.key:'';let g=null;('c'===h||'C'===h||'s'===h||'S'===h)&&(g=e.bezierReflectionPoint),g&&(d=g[0],f=g[1]);const c=this._bezierTo(d,f,l,o,p,r,e,s);n=n.concat(c),e.bezierReflectionPoint=[p+(p-l),r+(r-o)]}break}case'Q':case'q':{const i='q'===t.key;if(4<=t.data.length){let a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3];i&&(a+=e.x,o+=e.x,l+=e.y,p+=e.y);const r=1*(1+.2*s.roughness),d=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-r,r,s),e.y+this.getOffset(-r,r,s)]});let h=[o+this.getOffset(-r,r,s),p+this.getOffset(-r,r,s)];n.push({op:'qcurveTo',data:[a+this.getOffset(-r,r,s),l+this.getOffset(-r,r,s),h[0],h[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-d,d,s),e.y+this.getOffset(-d,d,s)]}),h=[o+this.getOffset(-d,d,s),p+this.getOffset(-d,d,s)],n.push({op:'qcurveTo',data:[a+this.getOffset(-d,d,s),l+this.getOffset(-d,d,s),h[0],h[1]]}),e.setPosition(h[0],h[1]),e.quadReflectionPoint=[o+(o-a),p+(p-l)]}break}case'T':case't':{const a='t'===t.key;if(2<=t.data.length){let l=+t.data[0],o=+t.data[1];a&&(l+=e.x,o+=e.y);let p=l,r=o;const d=i?i.key:'';let h=null;('q'===d||'Q'===d||'t'===d||'T'===d)&&(h=e.quadReflectionPoint),h&&(p=h[0],r=h[1]);const g=1*(1+.2*s.roughness),c=1.5*(1+.22*s.roughness);n.push({op:'move',data:[e.x+this.getOffset(-g,g,s),e.y+this.getOffset(-g,g,s)]});let u=[l+this.getOffset(-g,g,s),o+this.getOffset(-g,g,s)];n.push({op:'qcurveTo',data:[p+this.getOffset(-g,g,s),r+this.getOffset(-g,g,s),u[0],u[1]]}),n.push({op:'move',data:[e.x+this.getOffset(-c,c,s),e.y+this.getOffset(-c,c,s)]}),u=[l+this.getOffset(-c,c,s),o+this.getOffset(-c,c,s)],n.push({op:'qcurveTo',data:[p+this.getOffset(-c,c,s),r+this.getOffset(-c,c,s),u[0],u[1]]}),e.setPosition(u[0],u[1]),e.quadReflectionPoint=[l+(l-p),o+(o-r)]}break}case'A':case'a':{const i='a'===t.key;if(7<=t.data.length){const a=+t.data[0],l=+t.data[1],o=+t.data[2],p=+t.data[3],r=+t.data[4];let d=+t.data[5],f=+t.data[6];if(i&&(d+=e.x,f+=e.y),d===e.x&&f===e.y)break;if(0==a||0==l)n=n.concat(this.doubleLine(e.x,e.y,d,f,s)),e.setPosition(d,f);else for(let t=0;1>t;t++){const t=new b([e.x,e.y],[d,f],[a,l],o,!!p,!!r);for(let i=t.getNextSegment();i;){const a=this._bezierTo(i.cp1[0],i.cp1[1],i.cp2[0],i.cp2[1],i.to[0],i.to[1],e,s);n=n.concat(a),i=t.getNextSegment()}}}break}default:}return n}}const S='undefined'!=typeof self,T=S&&self&&self.document&&self.document.currentScript&&self.document.currentScript.src,C='undefined'!=typeof self;class z{constructor(e,t){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:'#000',strokeWidth:1,curveTightness:0,curveStepCount:9,fill:null,fillStyle:'hachure',fillWeight:-1,hachureAngle:-41,hachureGap:-1},this.config=e||{},this.surface=t,this.renderer=i(this.config),this.config.options&&(this.defaultOptions=this._options(this.config.options))}_options(e){return e?Object.assign({},this.defaultOptions,e):this.defaultOptions}_drawable(e,t,i){return{shape:e,sets:t||[],options:i||this.defaultOptions}}get lib(){return this.renderer}getCanvasSize(){const e=e=>e&&'object'==typeof e&&e.baseVal&&e.baseVal.value?e.baseVal.value:e||100;return this.surface?[e(this.surface.width),e(this.surface.height)]:[100,100]}computePolygonSize(e){if(e.length){let t=e[0][0],s=e[0][0],n=e[0][1],a=e[0][1];for(let o=1;oi&&(i=t.strokeWidth/2),{d:this.opsToPath(e),stroke:t.fill||'none',strokeWidth:i,fill:'none'}}opsToPath(e){let t='';for(const i of e.ops){const e=i.data;switch(i.op){case'move':t+=`M${e[0]} ${e[1]} `;break;case'bcurveTo':t+=`C${e[0]} ${e[1]}, ${e[2]} ${e[3]}, ${e[4]} ${e[5]} `;break;case'qcurveTo':t+=`Q${e[0]} ${e[1]}, ${e[2]} ${e[3]} `;break;case'lineTo':t+=`L${e[0]} ${e[1]} `;}}return t.trim()}}const E='undefined'!=typeof document;class W{constructor(e,t){this.canvas=e,this.ctx=this.canvas.getContext('2d'),this.gen=new z(t||null,this.canvas)}get generator(){return this.gen}static createRenderer(){return new A}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a),a}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a),a}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a),a}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n),n}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i),i}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i),i}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i),i}path(e,t){const i=this.gen.path(e,t);return this.draw(i),i}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,s=this.ctx;for(const n of t)switch(n.type){case'path':s.save(),s.strokeStyle=i.stroke,s.lineWidth=i.strokeWidth,this._drawToContext(s,n),s.restore();break;case'fillPath':s.save(),s.fillStyle=i.fill||'',this._drawToContext(s,n),s.restore();break;case'fillSketch':this.fillSketch(s,n,i);break;case'path2Dfill':{this.ctx.save(),this.ctx.fillStyle=i.fill||'';const e=new Path2D(n.path);this.ctx.fill(e),this.ctx.restore();break}case'path2Dpattern':{if(E){const e=n.size,t=document.createElement('canvas'),s=t.getContext('2d'),a=this.computeBBox(n.path);a&&(a.width||a.height)?(t.width=this.canvas.width,t.height=this.canvas.height,s.translate(a.x||0,a.y||0)):(t.width=e[0],t.height=e[1]),this.fillSketch(s,n,i),this.ctx.save(),this.ctx.fillStyle=this.ctx.createPattern(t,'repeat');const l=new Path2D(n.path);this.ctx.fill(l),this.ctx.restore()}break}}}computeBBox(e){if(E)try{const t=document.createElementNS('http://www.w3.org/2000/svg','svg');t.setAttribute('width','0'),t.setAttribute('height','0');const i=self.document.createElementNS('http://www.w3.org/2000/svg','path');i.setAttribute('d',e),t.appendChild(i),document.body.appendChild(t);const s=i.getBBox();return document.body.removeChild(t),s}catch(e){}return null}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2),e.save(),e.strokeStyle=i.fill||'',e.lineWidth=s,this._drawToContext(e,t),e.restore()}_drawToContext(e,t){e.beginPath();for(const i of t.ops){const t=i.data;switch(i.op){case'move':e.moveTo(t[0],t[1]);break;case'bcurveTo':e.bezierCurveTo(t[0],t[1],t[2],t[3],t[4],t[5]);break;case'qcurveTo':e.quadraticCurveTo(t[0],t[1],t[2],t[3]);break;case'lineTo':e.lineTo(t[0],t[1]);}}'fillPath'===t.type?e.fill():e.stroke()}}class L extends z{async line(e,t,i,s,n){const a=this._options(n);return this._drawable('line',[await this.lib.line(e,t,i,s,a)],a)}async rectangle(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill){const n=[[e,t],[e+i,t],[e+i,t+s],[e,t+s]];'solid'===a.fillStyle?l.push((await this.lib.solidFillPolygon(n,a))):l.push((await this.lib.patternFillPolygon(n,a)))}return l.push((await this.lib.rectangle(e,t,i,s,a))),this._drawable('rectangle',l,a)}async ellipse(e,t,i,s,n){const a=this._options(n),l=[];if(a.fill)if('solid'===a.fillStyle){const n=await this.lib.ellipse(e,t,i,s,a);n.type='fillPath',l.push(n)}else l.push((await this.lib.patternFillEllipse(e,t,i,s,a)));return l.push((await this.lib.ellipse(e,t,i,s,a))),this._drawable('ellipse',l,a)}async circle(e,t,i,s){const n=await this.ellipse(e,t,i,i,s);return n.shape='circle',n}async linearPath(e,t){const i=this._options(t);return this._drawable('linearPath',[await this.lib.linearPath(e,!1,i)],i)}async arc(e,t,i,s,n,a,l=!1,p){const r=this._options(p),o=[];if(l&&r.fill)if('solid'===r.fillStyle){const l=await this.lib.arc(e,t,i,s,n,a,!0,!1,r);l.type='fillPath',o.push(l)}else o.push((await this.lib.patternFillArc(e,t,i,s,n,a,r)));return o.push((await this.lib.arc(e,t,i,s,n,a,l,!0,r))),this._drawable('arc',o,r)}async curve(e,t){const i=this._options(t);return this._drawable('curve',[await this.lib.curve(e,i)],i)}async polygon(e,t){const i=this._options(t),s=[];if(i.fill)if('solid'===i.fillStyle)s.push((await this.lib.solidFillPolygon(e,i)));else{const t=this.computePolygonSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=this.polygonPath(e),s.push(a)}return s.push((await this.lib.linearPath(e,!0,i))),this._drawable('polygon',s,i)}async path(e,t){const i=this._options(t),s=[];if(!e)return this._drawable('path',s,i);if(i.fill)if('solid'===i.fillStyle){s.push({type:'path2Dfill',path:e,ops:[]})}else{const t=this.computePathSize(e),n=[[0,0],[t[0],0],[t[0],t[1]],[0,t[1]]],a=await this.lib.patternFillPolygon(n,i);a.type='path2Dpattern',a.size=t,a.path=e,s.push(a)}return s.push((await this.lib.svgPath(e,i))),this._drawable('path',s,i)}}class N extends W{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.canvas)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a),a}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a),a}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a),a}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n),n}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i),i}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i),i}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p),p}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i),i}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i),i}}const R='undefined'!=typeof document;class D{constructor(e,t){this.svg=e,this.gen=new z(t||null,this.svg)}get generator(){return this.gen}static createRenderer(){return new A}get defs(){if(R&&!this._defs){const e=this.svg.ownerDocument||document,t=e.createElementNS('http://www.w3.org/2000/svg','defs');this.svg.firstChild?this.svg.insertBefore(t,this.svg.firstChild):this.svg.appendChild(t),this._defs=t}return this._defs||null}line(e,t,i,s,n){const a=this.gen.line(e,t,i,s,n);return this.draw(a)}rectangle(e,t,i,s,n){const a=this.gen.rectangle(e,t,i,s,n);return this.draw(a)}ellipse(e,t,i,s,n){const a=this.gen.ellipse(e,t,i,s,n);return this.draw(a)}circle(e,t,i,s){const n=this.gen.circle(e,t,i,s);return this.draw(n)}linearPath(e,t){const i=this.gen.linearPath(e,t);return this.draw(i)}polygon(e,t){const i=this.gen.polygon(e,t);return this.draw(i)}arc(e,t,i,s,n,a,l=!1,o){const p=this.gen.arc(e,t,i,s,n,a,l,o);return this.draw(p)}curve(e,t){const i=this.gen.curve(e,t);return this.draw(i)}path(e,t){const i=this.gen.path(e,t);return this.draw(i)}draw(e){const t=e.sets||[],i=e.options||this.gen.defaultOptions,n=this.svg.ownerDocument||R&&document,a=n.createElementNS('http://www.w3.org/2000/svg','g');for(const l of t){let e=null;switch(l.type){case'path':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke=i.stroke,e.style.strokeWidth=i.strokeWidth+'',e.style.fill='none';break}case'fillPath':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',this.opsToPath(l)),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'fillSketch':{e=this.fillSketch(n,l,i);break}case'path2Dfill':{e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=i.fill;break}case'path2Dpattern':{const t=l.size,a=n.createElementNS('http://www.w3.org/2000/svg','pattern'),o=`rough-${r(Math.random()*(Number.MAX_SAFE_INTEGER||999999))}`;a.setAttribute('id',o),a.setAttribute('x','0'),a.setAttribute('y','0'),a.setAttribute('width','1'),a.setAttribute('height','1'),a.setAttribute('height','1'),a.setAttribute('viewBox',`0 0 ${s(t[0])} ${s(t[1])}`),a.setAttribute('patternUnits','objectBoundingBox');const p=this.fillSketch(n,l,i);a.appendChild(p),this.defs.appendChild(a),e=n.createElementNS('http://www.w3.org/2000/svg','path'),e.setAttribute('d',l.path||''),e.style.stroke='none',e.style.strokeWidth='0',e.style.fill=`url(#${o})`;break}}e&&a.appendChild(e)}return a}opsToPath(e){return this.gen.opsToPath(e)}fillSketch(e,t,i){let s=i.fillWeight;0>s&&(s=i.strokeWidth/2);const n=e.createElementNS('http://www.w3.org/2000/svg','path');return n.setAttribute('d',this.opsToPath(t)),n.style.stroke=i.fill,n.style.strokeWidth=s+'',n.style.fill='none',n}}class B extends D{constructor(e,t){super(e,t),this.genAsync=new L(t||null,this.svg)}get generator(){return this.genAsync}async line(e,t,i,s,n){const a=await this.genAsync.line(e,t,i,s,n);return this.draw(a)}async rectangle(e,t,i,s,n){const a=await this.genAsync.rectangle(e,t,i,s,n);return this.draw(a)}async ellipse(e,t,i,s,n){const a=await this.genAsync.ellipse(e,t,i,s,n);return this.draw(a)}async circle(e,t,i,s){const n=await this.genAsync.circle(e,t,i,s);return this.draw(n)}async linearPath(e,t){const i=await this.genAsync.linearPath(e,t);return this.draw(i)}async polygon(e,t){const i=await this.genAsync.polygon(e,t);return this.draw(i)}async arc(e,t,i,s,n,a,l=!1,o){const p=await this.genAsync.arc(e,t,i,s,n,a,l,o);return this.draw(p)}async curve(e,t){const i=await this.genAsync.curve(e,t);return this.draw(i)}async path(e,t){const i=await this.genAsync.path(e,t);return this.draw(i)}}var M={canvas(e,t){return t&&t.async?new N(e,t):new W(e,t)},svg(e,t){return t&&t.async?new B(e,t):new D(e,t)},createRenderer(){return W.createRenderer()},generator(e,t){return e&&e.async?new L(e,t):new z(e,t)}};return M}); diff --git a/src/path.ts b/src/path.ts index 5800d73..818b43e 100644 --- a/src/path.ts +++ b/src/path.ts @@ -15,27 +15,27 @@ export interface Segment { point?: Point; } -const PARAMS: { [key: string]: { length: number } } = { - A: { length: 7 }, - a: { length: 7 }, - C: { length: 6 }, - c: { length: 6 }, - H: { length: 1 }, - h: { length: 1 }, - L: { length: 2 }, - l: { length: 2 }, - M: { length: 2 }, - m: { length: 2 }, - Q: { length: 4 }, - q: { length: 4 }, - S: { length: 4 }, - s: { length: 4 }, - T: { length: 4 }, - t: { length: 2 }, - V: { length: 1 }, - v: { length: 1 }, - Z: { length: 0 }, - z: { length: 0 } +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: 4, + t: 2, + V: 1, + v: 1, + Z: 0, + z: 0 }; class ParsedPath { @@ -82,7 +82,7 @@ class ParsedPath { if (mode === 'BOD') { if (token.text === 'M' || token.text === 'm') { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } else { this.parseData('M0,0' + d); @@ -90,10 +90,10 @@ class ParsedPath { } } else { if (isType(token, this.NUMBER)) { - param_length = PARAMS[mode].length; + param_length = PARAMS[mode]; } else { index++; - param_length = PARAMS[token.text].length; + param_length = PARAMS[token.text]; mode = token.text; } } @@ -108,7 +108,7 @@ class ParsedPath { return; } } - if (PARAMS[mode]) { + if (typeof PARAMS[mode] === 'number') { const segment: Segment = { key: mode, data: params }; this.segments.push(segment); index += param_length;