From 01853fe4b77d8e5dc95197f246a55a3d1181fa06 Mon Sep 17 00:00:00 2001 From: Rob Nadal Date: Fri, 13 Jan 2023 04:04:39 -0500 Subject: [PATCH] Bezier-rs: Refactor interactive demo to remove Vue (#959) * Converted bezier example to vanilla js component * Indent with tabs * Add sliders * Converted bezier example pane to vanilla js * Implement the radio buttons + fixes * Converted SubpathExample to vanilla js * Converted SubpathExamplePane to vanilla js * Removed vue components * Remove App.vue * Remove vue and other dependencies * Minor fix in main.ts * Added insert to subpath features * Entry point tweaks * Rename "example" to "demo" * Kebab-case file names (except classes) Co-authored-by: Hannah Li Co-authored-by: Keavon Chambers --- .../other/bezier-rs-demos/package-lock.json | 101 ++- website/other/bezier-rs-demos/package.json | 7 +- website/other/bezier-rs-demos/src/App.vue | 662 ------------------ .../src/components/BezierDemo.ts | 120 ++++ .../src/components/BezierDemoPane.ts | 85 +++ .../src/components/BezierExample.vue | 97 --- .../src/components/BezierExamplePane.vue | 93 --- .../src/components/SubpathDemo.ts | 116 +++ .../src/components/SubpathDemoPane.ts | 76 ++ .../src/components/SubpathExample.vue | 92 --- .../src/components/SubpathExamplePane.vue | 81 --- .../src/features/bezier-features.ts | 461 ++++++++++++ .../src/features/subpath-features.ts | 60 ++ website/other/bezier-rs-demos/src/main.ts | 63 +- .../other/bezier-rs-demos/src/shims-vue.d.ts | 6 - website/other/bezier-rs-demos/src/style.css | 52 ++ .../bezier-rs-demos/src/utils/options.ts | 23 + .../other/bezier-rs-demos/src/utils/render.ts | 98 +++ .../other/bezier-rs-demos/src/utils/types.ts | 44 +- 19 files changed, 1272 insertions(+), 1065 deletions(-) delete mode 100644 website/other/bezier-rs-demos/src/App.vue create mode 100644 website/other/bezier-rs-demos/src/components/BezierDemo.ts create mode 100644 website/other/bezier-rs-demos/src/components/BezierDemoPane.ts delete mode 100644 website/other/bezier-rs-demos/src/components/BezierExample.vue delete mode 100644 website/other/bezier-rs-demos/src/components/BezierExamplePane.vue create mode 100644 website/other/bezier-rs-demos/src/components/SubpathDemo.ts create mode 100644 website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts delete mode 100644 website/other/bezier-rs-demos/src/components/SubpathExample.vue delete mode 100644 website/other/bezier-rs-demos/src/components/SubpathExamplePane.vue create mode 100644 website/other/bezier-rs-demos/src/features/bezier-features.ts create mode 100644 website/other/bezier-rs-demos/src/features/subpath-features.ts delete mode 100644 website/other/bezier-rs-demos/src/shims-vue.d.ts create mode 100644 website/other/bezier-rs-demos/src/style.css create mode 100644 website/other/bezier-rs-demos/src/utils/options.ts create mode 100644 website/other/bezier-rs-demos/src/utils/render.ts diff --git a/website/other/bezier-rs-demos/package-lock.json b/website/other/bezier-rs-demos/package-lock.json index 96c6791d..c116b20b 100644 --- a/website/other/bezier-rs-demos/package-lock.json +++ b/website/other/bezier-rs-demos/package-lock.json @@ -8,8 +8,7 @@ "name": "bezier-rs-demos", "version": "0.1.0", "dependencies": { - "core-js": "^3.26.1", - "vue": "^3.2.13" + "core-js": "^3.26.1" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.43.0", @@ -17,7 +16,6 @@ "@vue/cli-plugin-eslint": "^5.0.8", "@vue/cli-plugin-typescript": "^5.0.8", "@vue/cli-service": "^5.0.8", - "@vue/compiler-sfc": "^3.2.31", "@vue/eslint-config-airbnb": "^6.0.0", "@vue/eslint-config-typescript": "^11.0.2", "@wasm-tool/wasm-pack-plugin": "^1.6.0", @@ -26,8 +24,7 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier-vue": "^4.2.0", "eslint-plugin-vue": "^9.7.0", - "typescript": "^4.9.3", - "vue-template-compiler": "^2.7.14" + "typescript": "^4.9.3" }, "optionalDependencies": { "wasm-pack": "^0.10.3" @@ -323,6 +320,7 @@ "version": "7.20.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1395,6 +1393,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==", + "dev": true, "dependencies": { "@babel/parser": "^7.16.4", "@vue/shared": "3.2.45", @@ -1406,6 +1405,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz", "integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==", + "dev": true, "dependencies": { "@vue/compiler-core": "3.2.45", "@vue/shared": "3.2.45" @@ -1415,6 +1415,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz", "integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==", + "dev": true, "dependencies": { "@babel/parser": "^7.16.4", "@vue/compiler-core": "3.2.45", @@ -1432,6 +1433,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz", "integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==", + "dev": true, "dependencies": { "@vue/compiler-dom": "3.2.45", "@vue/shared": "3.2.45" @@ -1552,6 +1554,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz", "integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==", + "dev": true, + "peer": true, "dependencies": { "@vue/shared": "3.2.45" } @@ -1560,6 +1564,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz", "integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==", + "dev": true, "dependencies": { "@babel/parser": "^7.16.4", "@vue/compiler-core": "3.2.45", @@ -1572,6 +1577,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz", "integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==", + "dev": true, + "peer": true, "dependencies": { "@vue/reactivity": "3.2.45", "@vue/shared": "3.2.45" @@ -1581,6 +1588,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz", "integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==", + "dev": true, + "peer": true, "dependencies": { "@vue/runtime-core": "3.2.45", "@vue/shared": "3.2.45", @@ -1591,6 +1600,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz", "integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==", + "dev": true, + "peer": true, "dependencies": { "@vue/compiler-ssr": "3.2.45", "@vue/shared": "3.2.45" @@ -1602,7 +1613,8 @@ "node_modules/@vue/shared": { "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz", - "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==" + "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==", + "dev": true }, "node_modules/@vue/vue-loader-v15": { "name": "vue-loader", @@ -3290,13 +3302,17 @@ "node_modules/csstype": { "version": "2.6.21", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "dev": true, + "peer": true }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/debug": { "version": "4.3.4", @@ -4563,7 +4579,8 @@ "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true }, "node_modules/esutils": { "version": "2.0.3", @@ -6800,6 +6817,7 @@ "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, "dependencies": { "sourcemap-codec": "^1.4.8" } @@ -7140,6 +7158,7 @@ "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -7781,7 +7800,8 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -7886,6 +7906,7 @@ "version": "8.4.19", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -9253,6 +9274,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9261,6 +9283,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9278,7 +9301,8 @@ "node_modules/sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "node_modules/spdx-correct": { "version": "3.1.1", @@ -10140,6 +10164,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz", "integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==", + "dev": true, + "peer": true, "dependencies": { "@vue/compiler-dom": "3.2.45", "@vue/compiler-sfc": "3.2.45", @@ -10327,6 +10353,8 @@ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" @@ -11445,7 +11473,8 @@ "@babel/parser": { "version": "7.20.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", - "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==" + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", + "dev": true }, "@babel/template": { "version": "7.18.10", @@ -12262,6 +12291,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==", + "dev": true, "requires": { "@babel/parser": "^7.16.4", "@vue/shared": "3.2.45", @@ -12273,6 +12303,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz", "integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==", + "dev": true, "requires": { "@vue/compiler-core": "3.2.45", "@vue/shared": "3.2.45" @@ -12282,6 +12313,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz", "integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==", + "dev": true, "requires": { "@babel/parser": "^7.16.4", "@vue/compiler-core": "3.2.45", @@ -12299,6 +12331,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz", "integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==", + "dev": true, "requires": { "@vue/compiler-dom": "3.2.45", "@vue/shared": "3.2.45" @@ -12388,6 +12421,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz", "integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==", + "dev": true, + "peer": true, "requires": { "@vue/shared": "3.2.45" } @@ -12396,6 +12431,7 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz", "integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==", + "dev": true, "requires": { "@babel/parser": "^7.16.4", "@vue/compiler-core": "3.2.45", @@ -12408,6 +12444,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz", "integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==", + "dev": true, + "peer": true, "requires": { "@vue/reactivity": "3.2.45", "@vue/shared": "3.2.45" @@ -12417,6 +12455,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz", "integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==", + "dev": true, + "peer": true, "requires": { "@vue/runtime-core": "3.2.45", "@vue/shared": "3.2.45", @@ -12427,6 +12467,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz", "integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==", + "dev": true, + "peer": true, "requires": { "@vue/compiler-ssr": "3.2.45", "@vue/shared": "3.2.45" @@ -12435,7 +12477,8 @@ "@vue/shared": { "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz", - "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==" + "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==", + "dev": true }, "@vue/vue-loader-v15": { "version": "npm:vue-loader@15.10.1", @@ -13702,13 +13745,17 @@ "csstype": { "version": "2.6.21", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "dev": true, + "peer": true }, "de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "debug": { "version": "4.3.4", @@ -14671,7 +14718,8 @@ "estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true }, "esutils": { "version": "2.0.3", @@ -16336,6 +16384,7 @@ "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, "requires": { "sourcemap-codec": "^1.4.8" } @@ -16595,7 +16644,8 @@ "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true }, "natural-compare": { "version": "1.4.0", @@ -17072,7 +17122,8 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "picomatch": { "version": "2.3.1", @@ -17154,6 +17205,7 @@ "version": "8.4.19", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", + "dev": true, "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -18103,12 +18155,14 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true }, "source-map-support": { "version": "0.5.21", @@ -18123,7 +18177,8 @@ "sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "spdx-correct": { "version": "3.1.1", @@ -18765,6 +18820,8 @@ "version": "3.2.45", "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz", "integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==", + "dev": true, + "peer": true, "requires": { "@vue/compiler-dom": "3.2.45", "@vue/compiler-sfc": "3.2.45", @@ -18908,6 +18965,8 @@ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "de-indent": "^1.0.2", "he": "^1.2.0" diff --git a/website/other/bezier-rs-demos/package.json b/website/other/bezier-rs-demos/package.json index 83346523..d5baa055 100644 --- a/website/other/bezier-rs-demos/package.json +++ b/website/other/bezier-rs-demos/package.json @@ -9,8 +9,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { - "core-js": "^3.26.1", - "vue": "^3.2.13" + "core-js": "^3.26.1" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.43.0", @@ -18,7 +17,6 @@ "@vue/cli-plugin-eslint": "^5.0.8", "@vue/cli-plugin-typescript": "^5.0.8", "@vue/cli-service": "^5.0.8", - "@vue/compiler-sfc": "^3.2.31", "@vue/eslint-config-airbnb": "^6.0.0", "@vue/eslint-config-typescript": "^11.0.2", "@wasm-tool/wasm-pack-plugin": "^1.6.0", @@ -27,8 +25,7 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier-vue": "^4.2.0", "eslint-plugin-vue": "^9.7.0", - "typescript": "^4.9.3", - "vue-template-compiler": "^2.7.14" + "typescript": "^4.9.3" }, "optionalDependencies": { "wasm-pack": "^0.10.3" diff --git a/website/other/bezier-rs-demos/src/App.vue b/website/other/bezier-rs-demos/src/App.vue deleted file mode 100644 index 0cde8495..00000000 --- a/website/other/bezier-rs-demos/src/App.vue +++ /dev/null @@ -1,662 +0,0 @@ - - - - - diff --git a/website/other/bezier-rs-demos/src/components/BezierDemo.ts b/website/other/bezier-rs-demos/src/components/BezierDemo.ts new file mode 100644 index 00000000..25a546fc --- /dev/null +++ b/website/other/bezier-rs-demos/src/components/BezierDemo.ts @@ -0,0 +1,120 @@ +import { WasmBezier } from "@/../wasm/pkg"; +import bezierFeatures, { BezierFeatureName } from "@/features/bezier-features"; +import { renderDemo } from "@/utils/render"; +import { getConstructorKey, getCurveType, BezierCallback, BezierCurveType, SliderOption, WasmBezierManipulatorKey, ComputeType, Demo } from "@/utils/types"; + +const SELECTABLE_RANGE = 10; + +// Given the number of points in the curve, map the index of a point to the correct manipulator key +const MANIPULATOR_KEYS_FROM_BEZIER_TYPE: { [key in BezierCurveType]: WasmBezierManipulatorKey[] } = { + Linear: ["set_start", "set_end"], + Quadratic: ["set_start", "set_handle_start", "set_end"], + Cubic: ["set_start", "set_handle_start", "set_handle_end", "set_end"], +}; + +class BezierDemo extends HTMLElement implements Demo { + // Props + title!: string; + + points!: number[][]; + + name!: BezierFeatureName; + + sliderOptions!: SliderOption[]; + + triggerOnMouseMove!: boolean; + + computeType!: ComputeType; + + // Data + bezier!: WasmBezier; + + callback!: BezierCallback; + + manipulatorKeys!: WasmBezierManipulatorKey[]; + + activeIndex!: number | undefined; + + sliderData!: Record; + + sliderUnits!: Record; + + static get observedAttributes(): string[] { + return ["computetype"]; + } + + attributeChangedCallback(name: string, oldValue: string, newValue: string): void { + if (name === "computetype" && oldValue) { + this.computeType = (newValue || "Parametric") as ComputeType; + const figure = this.querySelector("figure") as HTMLElement; + this.drawDemo(figure); + } + } + + connectedCallback(): void { + this.title = this.getAttribute("title") || ""; + this.points = JSON.parse(this.getAttribute("points") || "[]"); + this.name = this.getAttribute("name") as BezierFeatureName; + this.sliderOptions = JSON.parse(this.getAttribute("sliderOptions") || "[]"); + this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true"; + this.computeType = (this.getAttribute("computetype") || "Parametric") as ComputeType; + + this.callback = bezierFeatures[this.name].callback as BezierCallback; + const curveType = getCurveType(this.points.length); + + this.manipulatorKeys = MANIPULATOR_KEYS_FROM_BEZIER_TYPE[curveType]; + this.bezier = WasmBezier[getConstructorKey(curveType)](this.points); + this.activeIndex = undefined as number | undefined; + this.sliderData = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.default }))); + this.sliderUnits = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.unit }))); + this.render(); + + const figure = this.querySelector("figure") as HTMLElement; + this.drawDemo(figure); + } + + render(): void { + renderDemo(this); + } + + drawDemo(figure: HTMLElement, mouseLocation?: [number, number]): void { + figure.innerHTML = this.callback(this.bezier, this.sliderData, mouseLocation, this.computeType); + } + + onMouseDown(event: MouseEvent): void { + const mx = event.offsetX; + const my = event.offsetY; + for (let pointIndex = 0; pointIndex < this.points.length; pointIndex += 1) { + const point = this.points[pointIndex]; + if (point && Math.abs(mx - point[0]) < SELECTABLE_RANGE && Math.abs(my - point[1]) < SELECTABLE_RANGE) { + this.activeIndex = pointIndex; + return; + } + } + } + + onMouseUp(): void { + this.activeIndex = undefined; + } + + onMouseMove(event: MouseEvent): void { + const mx = event.offsetX; + const my = event.offsetY; + const figure = event.currentTarget as HTMLElement; + + if (this.activeIndex !== undefined) { + this.bezier[this.manipulatorKeys[this.activeIndex]](mx, my); + this.points[this.activeIndex] = [mx, my]; + this.drawDemo(figure); + } else if (this.triggerOnMouseMove) { + this.drawDemo(figure, [mx, my]); + } + } + + getSliderUnit(sliderValue: number, variable: string): string { + const sliderUnit = this.sliderUnits[variable]; + return (Array.isArray(sliderUnit) ? sliderUnit[sliderValue] : sliderUnit) || ""; + } +} + +export default BezierDemo; diff --git a/website/other/bezier-rs-demos/src/components/BezierDemoPane.ts b/website/other/bezier-rs-demos/src/components/BezierDemoPane.ts new file mode 100644 index 00000000..39c2699b --- /dev/null +++ b/website/other/bezier-rs-demos/src/components/BezierDemoPane.ts @@ -0,0 +1,85 @@ +import { BezierFeatureName } from "@/features/bezier-features"; +import { renderDemoPane } from "@/utils/render"; +import { BezierCurveType, BEZIER_CURVE_TYPE, ComputeType, BezierDemoOptions, SliderOption, Demo, DemoPane, BezierDemoArgs } from "@/utils/types"; + +const demoDefaults = { + Linear: { + points: [ + [30, 60], + [140, 120], + ], + }, + Quadratic: { + points: [ + [30, 50], + [140, 30], + [160, 170], + ], + }, + Cubic: { + points: [ + [30, 30], + [60, 140], + [150, 30], + [160, 160], + ], + }, +}; + +class BezierDemoPane extends HTMLElement implements DemoPane { + // Props + name!: BezierFeatureName; + + demoOptions!: BezierDemoOptions; + + triggerOnMouseMove!: boolean; + + chooseComputeType!: boolean; + + // Data + demos!: BezierDemoArgs[]; + + id!: string; + + computeType!: ComputeType; + + connectedCallback(): void { + this.id = `${Math.random()}`.substring(2); + this.computeType = "Parametric"; + + this.name = (this.getAttribute("name") || "") as BezierFeatureName; + this.demoOptions = JSON.parse(this.getAttribute("demoOptions") || "[]"); + this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true"; + this.chooseComputeType = this.getAttribute("chooseComputeType") === "true"; + // Use quadratic slider options as a default if sliders are not provided for the other curve types. + const defaultSliderOptions: SliderOption[] = this.demoOptions.Quadratic?.sliderOptions || []; + this.demos = BEZIER_CURVE_TYPE.map((curveType: BezierCurveType) => { + const givenData = this.demoOptions[curveType]; + const defaultData = demoDefaults[curveType]; + return { + title: curveType, + disabled: givenData?.disabled || false, + points: givenData?.customPoints || defaultData.points, + sliderOptions: givenData?.sliderOptions || defaultSliderOptions, + }; + }); + this.render(); + } + + render(): void { + renderDemoPane(this); + } + + buildDemo(demo: BezierDemoArgs): Demo { + const bezierDemo = document.createElement("bezier-demo"); + bezierDemo.setAttribute("title", demo.title); + bezierDemo.setAttribute("points", JSON.stringify(demo.points)); + bezierDemo.setAttribute("name", this.name); + bezierDemo.setAttribute("sliderOptions", JSON.stringify(demo.sliderOptions)); + bezierDemo.setAttribute("triggerOnMouseMove", String(this.triggerOnMouseMove)); + bezierDemo.setAttribute("computetype", this.computeType); + return bezierDemo; + } +} + +export default BezierDemoPane; diff --git a/website/other/bezier-rs-demos/src/components/BezierExample.vue b/website/other/bezier-rs-demos/src/components/BezierExample.vue deleted file mode 100644 index cf45a4d2..00000000 --- a/website/other/bezier-rs-demos/src/components/BezierExample.vue +++ /dev/null @@ -1,97 +0,0 @@ - - - - - diff --git a/website/other/bezier-rs-demos/src/components/BezierExamplePane.vue b/website/other/bezier-rs-demos/src/components/BezierExamplePane.vue deleted file mode 100644 index df489362..00000000 --- a/website/other/bezier-rs-demos/src/components/BezierExamplePane.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - diff --git a/website/other/bezier-rs-demos/src/components/SubpathDemo.ts b/website/other/bezier-rs-demos/src/components/SubpathDemo.ts new file mode 100644 index 00000000..cfc318d5 --- /dev/null +++ b/website/other/bezier-rs-demos/src/components/SubpathDemo.ts @@ -0,0 +1,116 @@ +import { WasmSubpath } from "@/../wasm/pkg"; +import subpathFeatures, { SubpathFeatureName } from "@/features/subpath-features"; +import { renderDemo } from "@/utils/render"; + +import { SubpathCallback, WasmSubpathInstance, WasmSubpathManipulatorKey, SliderOption, ComputeType } from "@/utils/types"; + +const SELECTABLE_RANGE = 10; +const POINT_INDEX_TO_MANIPULATOR: WasmSubpathManipulatorKey[] = ["set_anchor", "set_in_handle", "set_out_handle"]; + +class SubpathDemo extends HTMLElement { + // Props + title!: string; + + triples!: (number[] | undefined)[][]; + + name!: SubpathFeatureName; + + closed!: boolean; + + sliderOptions!: SliderOption[]; + + triggerOnMouseMove!: boolean; + + computeType!: ComputeType; + + // Data + subpath!: WasmSubpath; + + callback!: SubpathCallback; + + manipulatorKeys!: WasmSubpathManipulatorKey[]; + + activeIndex!: number[] | undefined; + + sliderData!: Record; + + sliderUnits!: Record; + + static get observedAttributes(): string[] { + return ["computetype"]; + } + + attributeChangedCallback(name: string, oldValue: string, newValue: string): void { + if (name === "computetype" && oldValue) { + this.computeType = (newValue || "Parametric") as ComputeType; + const figure = this.querySelector("figure") as HTMLElement; + this.drawDemo(figure); + } + } + + connectedCallback(): void { + this.title = this.getAttribute("title") || ""; + this.triples = JSON.parse(this.getAttribute("triples") || "[]"); + this.name = this.getAttribute("name") as SubpathFeatureName; + this.sliderOptions = JSON.parse(this.getAttribute("sliderOptions") || "[]"); + this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true"; + this.closed = this.getAttribute("closed") === "true"; + this.computeType = (this.getAttribute("computetype") || "Parametric") as ComputeType; + + this.callback = subpathFeatures[this.name].callback as SubpathCallback; + this.subpath = WasmSubpath.from_triples(this.triples, this.closed) as WasmSubpathInstance; + this.sliderData = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.default }))); + this.sliderUnits = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.unit }))); + + this.render(); + + const figure = this.querySelector("figure") as HTMLElement; + this.drawDemo(figure); + } + + render(): void { + renderDemo(this); + } + + drawDemo(figure: HTMLElement, mouseLocation?: [number, number]): void { + figure.innerHTML = this.callback(this.subpath, this.sliderData, mouseLocation, this.computeType); + } + + onMouseDown(event: MouseEvent): void { + const mx = event.offsetX; + const my = event.offsetY; + for (let controllerIndex = 0; controllerIndex < this.triples.length; controllerIndex += 1) { + for (let pointIndex = 0; pointIndex < 3; pointIndex += 1) { + const point = this.triples[controllerIndex][pointIndex]; + if (point && Math.abs(mx - point[0]) < SELECTABLE_RANGE && Math.abs(my - point[1]) < SELECTABLE_RANGE) { + this.activeIndex = [controllerIndex, pointIndex]; + return; + } + } + } + } + + onMouseUp(): void { + this.activeIndex = undefined; + } + + onMouseMove(event: MouseEvent): void { + const mx = event.offsetX; + const my = event.offsetY; + const figure = event.currentTarget as HTMLElement; + if (this.activeIndex) { + this.subpath[POINT_INDEX_TO_MANIPULATOR[this.activeIndex[1]]](this.activeIndex[0], mx, my); + this.triples[this.activeIndex[0]][this.activeIndex[1]] = [mx, my]; + this.drawDemo(figure); + } else if (this.triggerOnMouseMove) { + this.drawDemo(figure, [mx, my]); + } + } + + getSliderUnit(sliderValue: number, variable: string): string { + const sliderUnit = this.sliderUnits[variable]; + return (Array.isArray(sliderUnit) ? sliderUnit[sliderValue] : sliderUnit) || ""; + } +} + +export default SubpathDemo; diff --git a/website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts b/website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts new file mode 100644 index 00000000..608c997a --- /dev/null +++ b/website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts @@ -0,0 +1,76 @@ +import { SubpathFeatureName } from "@/features/subpath-features"; +import { renderDemoPane } from "@/utils/render"; +import { ComputeType, Demo, DemoPane, SliderOption, SubpathDemoArgs } from "@/utils/types"; + +class SubpathDemoPane extends HTMLElement implements DemoPane { + // Props + name!: SubpathFeatureName; + + sliderOptions!: SliderOption[]; + + triggerOnMouseMove!: boolean; + + chooseComputeType!: boolean; + + // Data + demos!: SubpathDemoArgs[]; + + id!: string; + + computeType!: ComputeType; + + connectedCallback(): void { + this.demos = [ + { + title: "Open Subpath", + triples: [ + [[20, 20], undefined, [10, 90]], + [[150, 40], [60, 40], undefined], + [[175, 175], undefined, undefined], + [[100, 100], [40, 120], undefined], + ], + closed: false, + }, + { + title: "Closed Subpath", + triples: [ + [[35, 125], undefined, [40, 40]], + [[130, 30], [120, 120], undefined], + [ + [145, 150], + [175, 90], + [70, 185], + ], + ], + closed: true, + }, + ]; + this.id = `${Math.random()}`.substring(2); + this.computeType = "Parametric"; + + this.name = (this.getAttribute("name") || "") as SubpathFeatureName; + this.sliderOptions = JSON.parse(this.getAttribute("sliderOptions") || "[]"); + this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true"; + this.chooseComputeType = this.getAttribute("chooseComputeType") === "true"; + + this.render(); + } + + render(): void { + renderDemoPane(this); + } + + buildDemo(demo: SubpathDemoArgs): Demo { + const subpathDemo = document.createElement("subpath-demo"); + subpathDemo.setAttribute("title", demo.title); + subpathDemo.setAttribute("triples", JSON.stringify(demo.triples)); + subpathDemo.setAttribute("closed", String(demo.closed)); + subpathDemo.setAttribute("name", this.name); + subpathDemo.setAttribute("sliderOptions", JSON.stringify(this.sliderOptions)); + subpathDemo.setAttribute("triggerOnMouseMove", String(this.triggerOnMouseMove)); + subpathDemo.setAttribute("computetype", this.computeType); + return subpathDemo; + } +} + +export default SubpathDemoPane; diff --git a/website/other/bezier-rs-demos/src/components/SubpathExample.vue b/website/other/bezier-rs-demos/src/components/SubpathExample.vue deleted file mode 100644 index 6372a08b..00000000 --- a/website/other/bezier-rs-demos/src/components/SubpathExample.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - - diff --git a/website/other/bezier-rs-demos/src/components/SubpathExamplePane.vue b/website/other/bezier-rs-demos/src/components/SubpathExamplePane.vue deleted file mode 100644 index aef447f9..00000000 --- a/website/other/bezier-rs-demos/src/components/SubpathExamplePane.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/website/other/bezier-rs-demos/src/features/bezier-features.ts b/website/other/bezier-rs-demos/src/features/bezier-features.ts new file mode 100644 index 00000000..75396019 --- /dev/null +++ b/website/other/bezier-rs-demos/src/features/bezier-features.ts @@ -0,0 +1,461 @@ +import { WasmBezier } from "@/../wasm/pkg"; +import { tSliderOptions, tErrorOptions, tMinimumSeperationOptions } from "@/utils/options"; +import { ComputeType, BezierDemoOptions, WasmBezierInstance, BezierCallback } from "@/utils/types"; + +const bezierFeatures = { + Constructor: { + callback: (bezier: WasmBezierInstance, _: Record): string => bezier.to_svg(), + }, + "Bezier Through Points": { + callback: (bezier: WasmBezierInstance, options: Record): string => { + const points = JSON.parse(bezier.get_points()); + if (Object.values(options).length === 1) { + return WasmBezier.quadratic_through_points(points, options.t); + } + return WasmBezier.cubic_through_points(points, options.t, options["midpoint separation"]); + }, + demoOptions: { + Linear: { + disabled: true, + }, + Quadratic: { + customPoints: [ + [30, 50], + [120, 70], + [160, 170], + ], + sliderOptions: [ + { + min: 0.01, + max: 0.99, + step: 0.01, + default: 0.5, + variable: "t", + }, + ], + }, + Cubic: { + customPoints: [ + [30, 50], + [120, 70], + [160, 170], + ], + sliderOptions: [ + { + min: 0.01, + max: 0.99, + step: 0.01, + default: 0.5, + variable: "t", + }, + { + min: 0, + max: 100, + step: 2, + default: 30, + variable: "midpoint separation", + }, + ], + }, + }, + }, + Length: { + callback: (bezier: WasmBezierInstance, _: Record): string => bezier.length(), + }, + Evaluate: { + callback: (bezier: WasmBezierInstance, options: Record, _: undefined, computeType: ComputeType): string => bezier.evaluate(options.computeArgument, computeType), + demoOptions: { + Quadratic: { + sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }], + }, + }, + chooseComputeType: true, + }, + "Lookup Table": { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.compute_lookup_table(options.steps), + demoOptions: { + Quadratic: { + sliderOptions: [ + { + min: 2, + max: 15, + step: 1, + default: 5, + variable: "steps", + }, + ], + }, + }, + }, + Derivative: { + callback: (bezier: WasmBezierInstance, _: Record): string => bezier.derivative(), + demoOptions: { + Linear: { + disabled: true, + }, + Quadratic: { + customPoints: [ + [30, 40], + [110, 50], + [120, 130], + ], + }, + Cubic: { + customPoints: [ + [50, 50], + [60, 100], + [100, 140], + [140, 150], + ], + }, + }, + }, + Tangent: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.tangent(options.t), + demoOptions: { + Quadratic: { + sliderOptions: [tSliderOptions], + }, + }, + }, + Normal: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.normal(options.t), + demoOptions: { + Quadratic: { + sliderOptions: [tSliderOptions], + }, + }, + }, + Curvature: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.curvature(options.t), + demoOptions: { + Linear: { + disabled: true, + }, + Quadratic: { + sliderOptions: [tSliderOptions], + }, + }, + }, + Split: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.split(options.t), + demoOptions: { + Quadratic: { + sliderOptions: [tSliderOptions], + }, + }, + }, + Trim: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.trim(options.t1, options.t2), + demoOptions: { + Quadratic: { + sliderOptions: [ + { + variable: "t1", + min: 0, + max: 1, + step: 0.01, + default: 0.25, + }, + { + variable: "t2", + min: 0, + max: 1, + step: 0.01, + default: 0.75, + }, + ], + }, + }, + }, + Project: { + callback: (bezier: WasmBezierInstance, _: Record, mouseLocation?: [number, number]): string => + mouseLocation ? bezier.project(mouseLocation[0], mouseLocation[1]) : bezier.to_svg(), + triggerOnMouseMove: true, + }, + "Local Extrema": { + callback: (bezier: WasmBezierInstance, _: Record): string => bezier.local_extrema(), + demoOptions: { + Quadratic: { + customPoints: [ + [40, 40], + [160, 30], + [110, 150], + ], + }, + Cubic: { + customPoints: [ + [160, 180], + [170, 10], + [30, 90], + [180, 160], + ], + }, + }, + }, + "Bounding Box": { + callback: (bezier: WasmBezierInstance, _: Record): string => bezier.bounding_box(), + }, + Inflections: { + callback: (bezier: WasmBezierInstance, _: Record): string => bezier.inflections(), + demoOptions: { + Linear: { + disabled: true, + }, + Quadratic: { + disabled: true, + }, + }, + }, + Reduce: { + callback: (bezier: WasmBezierInstance): string => bezier.reduce(), + }, + Offset: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.offset(options.distance), + demoOptions: { + Quadratic: { + sliderOptions: [ + { + variable: "distance", + min: -50, + max: 50, + step: 1, + default: 20, + }, + ], + }, + }, + }, + Outline: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.outline(options.distance), + demoOptions: { + Quadratic: { + sliderOptions: [ + { + variable: "distance", + min: 0, + max: 50, + step: 1, + default: 20, + }, + ], + }, + }, + }, + "Graduated Outline": { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.graduated_outline(options.start_distance, options.end_distance), + demoOptions: { + Quadratic: { + sliderOptions: [ + { + variable: "start_distance", + min: 0, + max: 50, + step: 1, + default: 30, + }, + { + variable: "end_distance", + min: 0, + max: 50, + step: 1, + default: 30, + }, + ], + }, + }, + customPoints: { + Cubic: [ + [31, 94], + [40, 40], + [107, 107], + [106, 106], + ], + }, + }, + "Skewed Outline": { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.skewed_outline(options.distance1, options.distance2, options.distance3, options.distance4), + demoOptions: { + Quadratic: { + sliderOptions: [ + { + variable: "distance1", + min: 0, + max: 50, + step: 1, + default: 20, + }, + { + variable: "distance2", + min: 0, + max: 50, + step: 1, + default: 10, + }, + { + variable: "distance3", + min: 0, + max: 50, + step: 1, + default: 30, + }, + { + variable: "distance4", + min: 0, + max: 50, + step: 1, + default: 5, + }, + ], + }, + }, + }, + Arcs: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.arcs(options.error, options.max_iterations, options.strategy), + demoOptions: ((): Omit => { + const sliderOptions = [ + { + variable: "strategy", + min: 0, + max: 2, + step: 1, + default: 0, + unit: [": Automatic", ": FavorLargerArcs", ": FavorCorrectness"], + }, + { + variable: "error", + min: 0.05, + max: 1, + step: 0.05, + default: 0.5, + }, + { + variable: "max_iterations", + min: 50, + max: 200, + step: 1, + default: 100, + }, + ]; + + return { + Quadratic: { + customPoints: [ + [50, 50], + [85, 65], + [100, 100], + ], + sliderOptions, + disabled: false, + }, + Cubic: { + customPoints: [ + [160, 180], + [170, 10], + [30, 90], + [180, 160], + ], + sliderOptions, + disabled: false, + }, + }; + })(), + }, + "Intersect (Line Segment)": { + callback: (bezier: WasmBezierInstance): string => { + const line = [ + [150, 150], + [20, 20], + ]; + return bezier.intersect_line_segment(line); + }, + }, + "Intersect (Quadratic)": { + callback: (bezier: WasmBezierInstance, options: Record): string => { + const quadratic = [ + [20, 80], + [180, 10], + [90, 120], + ]; + return bezier.intersect_quadratic_segment(quadratic, options.error, options.minimum_seperation); + }, + demoOptions: { + Quadratic: { + sliderOptions: [tErrorOptions, tMinimumSeperationOptions], + }, + }, + }, + "Intersect (Cubic)": { + callback: (bezier: WasmBezierInstance, options: Record): string => { + const cubic = [ + [40, 20], + [100, 40], + [40, 120], + [175, 140], + ]; + return bezier.intersect_cubic_segment(cubic, options.error, options.minimum_seperation); + }, + demoOptions: { + Quadratic: { + sliderOptions: [tErrorOptions, tMinimumSeperationOptions], + }, + }, + }, + "Intersect (Self)": { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.intersect_self(options.error), + demoOptions: { + Quadratic: { + sliderOptions: [tErrorOptions], + }, + Cubic: { + customPoints: [ + [160, 180], + [170, 10], + [30, 90], + [180, 140], + ], + }, + }, + }, + "Intersect (Rectangle)": { + callback: (bezier: WasmBezierInstance): string => + bezier.intersect_rectangle([ + [50, 50], + [150, 150], + ]), + }, + Rotate: { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.rotate(options.angle * Math.PI, 100, 100), + demoOptions: { + Quadratic: { + sliderOptions: [ + { + variable: "angle", + min: 0, + max: 2, + step: 1 / 50, + default: 0.12, + unit: "π", + }, + ], + }, + }, + }, + "De Casteljau Points": { + callback: (bezier: WasmBezierInstance, options: Record): string => bezier.de_casteljau_points(options.t), + demoOptions: { + Quadratic: { + sliderOptions: [tSliderOptions], + }, + }, + }, +}; + +export type BezierFeatureName = keyof typeof bezierFeatures; +export type BezierFeatureOptions = { + callback: BezierCallback; + demoOptions?: Partial; + triggerOnMouseMove?: boolean; + chooseComputeType?: boolean; +}; +export default bezierFeatures as Record; diff --git a/website/other/bezier-rs-demos/src/features/subpath-features.ts b/website/other/bezier-rs-demos/src/features/subpath-features.ts new file mode 100644 index 00000000..fd4d68e0 --- /dev/null +++ b/website/other/bezier-rs-demos/src/features/subpath-features.ts @@ -0,0 +1,60 @@ +import { tSliderOptions } from "@/utils/options"; +import { ComputeType, SliderOption, SubpathCallback, WasmSubpathInstance } from "@/utils/types"; + +const subpathFeatures = { + Constructor: { + callback: (subpath: WasmSubpathInstance): string => subpath.to_svg(), + }, + Insert: { + callback: (subpath: WasmSubpathInstance, options: Record, _: undefined, computeType: ComputeType): string => subpath.insert(options.computeArgument, computeType), + sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }], + // TODO: Uncomment this after implementing the Euclidean version + // chooseComputeType: true, + }, + Length: { + callback: (subpath: WasmSubpathInstance): string => subpath.length(), + }, + Evaluate: { + callback: (subpath: WasmSubpathInstance, options: Record, _: undefined, computeType: ComputeType): string => subpath.evaluate(options.computeArgument, computeType), + sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }], + chooseComputeType: true, + }, + Project: { + callback: (subpath: WasmSubpathInstance, _: Record, mouseLocation?: [number, number]): string => + mouseLocation ? subpath.project(mouseLocation[0], mouseLocation[1]) : subpath.to_svg(), + triggerOnMouseMove: true, + }, + "Intersect (Line Segment)": { + callback: (subpath: WasmSubpathInstance): string => + subpath.intersect_line_segment([ + [150, 150], + [20, 20], + ]), + }, + "Intersect (Quadratic segment)": { + callback: (subpath: WasmSubpathInstance): string => + subpath.intersect_quadratic_segment([ + [20, 80], + [180, 10], + [90, 120], + ]), + }, + "Intersect (Cubic segment)": { + callback: (subpath: WasmSubpathInstance): string => + subpath.intersect_cubic_segment([ + [40, 20], + [100, 40], + [40, 120], + [175, 140], + ]), + }, +}; + +export type SubpathFeatureName = keyof typeof subpathFeatures; +export type SubpathFeatureOptions = { + callback: SubpathCallback; + sliderOptions?: SliderOption[]; + triggerOnMouseMove?: boolean; + chooseComputeType?: boolean; +}; +export default subpathFeatures as Record; diff --git a/website/other/bezier-rs-demos/src/main.ts b/website/other/bezier-rs-demos/src/main.ts index 3fe7e03a..d18dc7a3 100644 --- a/website/other/bezier-rs-demos/src/main.ts +++ b/website/other/bezier-rs-demos/src/main.ts @@ -1,7 +1,62 @@ -import { createApp } from "vue"; +import BezierDemo from "@/components/BezierDemo"; +import BezierDemoPane from "@/components/BezierDemoPane"; +import SubpathDemo from "@/components/SubpathDemo"; +import SubpathDemoPane from "@/components/SubpathDemoPane"; -import App from "@/App.vue"; +import bezierFeatures, { BezierFeatureName } from "@/features/bezier-features"; +import subpathFeatures, { SubpathFeatureName } from "@/features/subpath-features"; -document.title = "Bezier-rs Interactive Documentation"; +import "@/style.css"; -createApp(App).mount("#app"); +window.document.title = "Bezier-rs Interactive Documentation"; +window.document.body.innerHTML = ` +

Bezier-rs Interactive Documentation

+

+ This is the interactive documentation for the Bezier-rs library. View the + crate documentation + for detailed function descriptions and API usage. Click and drag on the endpoints of the demo curves to visualize the various Bezier utilities and functions. +

+ +

Beziers

+
+

Subpaths

+
+`.trim(); + +declare global { + interface HTMLElementTagNameMap { + "bezier-demo": BezierDemo; + "bezier-demo-pane": BezierDemoPane; + "subpath-demo": SubpathDemo; + "subpath-demo-pane": SubpathDemoPane; + } +} + +window.customElements.define("bezier-demo", BezierDemo); +window.customElements.define("bezier-demo-pane", BezierDemoPane); +window.customElements.define("subpath-demo", SubpathDemo); +window.customElements.define("subpath-demo-pane", SubpathDemoPane); + +const bezierDemos = document.getElementById("bezier-demos"); +(Object.keys(bezierFeatures) as BezierFeatureName[]).forEach((featureName) => { + const feature = bezierFeatures[featureName]; + const demo = document.createElement("bezier-demo-pane"); + + demo.setAttribute("name", featureName); + demo.setAttribute("demoOptions", JSON.stringify(feature.demoOptions || {})); + demo.setAttribute("triggerOnMouseMove", String(feature.triggerOnMouseMove)); + demo.setAttribute("chooseComputeType", String(feature.chooseComputeType)); + bezierDemos?.append(demo); +}); + +const subpathDemos = document.getElementById("subpath-demos"); +(Object.keys(subpathFeatures) as SubpathFeatureName[]).forEach((featureName) => { + const feature = subpathFeatures[featureName]; + const demo = document.createElement("subpath-demo-pane"); + + demo.setAttribute("name", featureName); + demo.setAttribute("sliderOptions", JSON.stringify(feature.sliderOptions || [])); + demo.setAttribute("triggerOnMouseMove", String(feature.triggerOnMouseMove)); + demo.setAttribute("chooseComputeType", String(feature.chooseComputeType)); + subpathDemos?.append(demo); +}); diff --git a/website/other/bezier-rs-demos/src/shims-vue.d.ts b/website/other/bezier-rs-demos/src/shims-vue.d.ts deleted file mode 100644 index cd545695..00000000 --- a/website/other/bezier-rs-demos/src/shims-vue.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable */ -declare module "*.vue" { - import type { DefineComponent } from "vue"; - const component: DefineComponent<{}, {}, any>; - export default component; -} diff --git a/website/other/bezier-rs-demos/src/style.css b/website/other/bezier-rs-demos/src/style.css new file mode 100644 index 00000000..70c685e7 --- /dev/null +++ b/website/other/bezier-rs-demos/src/style.css @@ -0,0 +1,52 @@ +html, +body { + height: 100%; + font-family: Arial, sans-serif; + text-align: center; + margin: 40px 0; +} + +body > h1 + p { + max-width: 768px; + line-height: 1.4; + margin: auto; + text-align: justify; +} + +body > h2 { + margin-top: 40px; +} + +/* Demo Pane styles */ +.demo-row { + display: flex; + flex-direction: row; + justify-content: center; +} + +.compute-type-choice { + margin-top: 20px; +} + +.demo-pane-header { + margin-top: 2em; + margin-bottom: 0; +} + +.demo-pane-container { + position: relative; + width: fit-content; + margin: auto; +} + +/* Demo styles */ +.demo-header { + margin: 20px 0; +} + +.demo-figure { + width: 200px; + height: 200px; + margin-bottom: 20px; + border: solid 1px black; +} diff --git a/website/other/bezier-rs-demos/src/utils/options.ts b/website/other/bezier-rs-demos/src/utils/options.ts new file mode 100644 index 00000000..11ef20a6 --- /dev/null +++ b/website/other/bezier-rs-demos/src/utils/options.ts @@ -0,0 +1,23 @@ +export const tSliderOptions = { + min: 0, + max: 1, + step: 0.01, + default: 0.5, + variable: "t", +}; + +export const tErrorOptions = { + variable: "error", + min: 0.1, + max: 2, + step: 0.1, + default: 0.5, +}; + +export const tMinimumSeperationOptions = { + variable: "minimum_seperation", + min: 0.001, + max: 0.25, + step: 0.001, + default: 0.05, +}; diff --git a/website/other/bezier-rs-demos/src/utils/render.ts b/website/other/bezier-rs-demos/src/utils/render.ts new file mode 100644 index 00000000..6c75630f --- /dev/null +++ b/website/other/bezier-rs-demos/src/utils/render.ts @@ -0,0 +1,98 @@ +import { ComputeType, Demo, DemoPane, SliderOption } from "@/utils/types"; + +export function renderDemo(demo: Demo): void { + const header = document.createElement("h4"); + header.className = "demo-header"; + header.innerText = demo.title; + + const figure = document.createElement("figure"); + figure.className = "demo-figure"; + figure.addEventListener("mousedown", demo.onMouseDown.bind(demo)); + figure.addEventListener("mouseup", demo.onMouseUp.bind(demo)); + figure.addEventListener("mousemove", demo.onMouseMove.bind(demo)); + + demo.append(header); + demo.append(figure); + + demo.sliderOptions.forEach((sliderOption: SliderOption) => { + const sliderLabel = document.createElement("div"); + const sliderData = demo.sliderData[sliderOption.variable]; + const sliderUnit = demo.getSliderUnit(sliderData, sliderOption.variable); + sliderLabel.className = "slider-label"; + sliderLabel.innerText = `${sliderOption.variable} = ${sliderData}${sliderUnit}`; + demo.append(sliderLabel); + + const sliderInput = document.createElement("input"); + sliderInput.className = "slider-input"; + sliderInput.type = "range"; + sliderInput.max = String(sliderOption.max); + sliderInput.min = String(sliderOption.min); + sliderInput.step = String(sliderOption.step); + sliderInput.value = String(sliderOption.default); + sliderInput.addEventListener("input", (event: Event): void => { + demo.sliderData[sliderOption.variable] = Number((event.target as HTMLInputElement).value); + sliderLabel.innerText = `${sliderOption.variable} = ${demo.sliderData[sliderOption.variable]}${sliderUnit}`; + demo.drawDemo(figure); + }); + demo.append(sliderInput); + }); +} + +export function renderDemoPane(demoPane: DemoPane): void { + const container = document.createElement("div"); + container.className = "demo-pane-container"; + + const header = document.createElement("h3"); + header.innerText = demoPane.name; + header.className = "demo-pane-header"; + + const computeTypeContainer = document.createElement("div"); + computeTypeContainer.className = "compute-type-choice"; + + const computeTypeLabel = document.createElement("strong"); + computeTypeLabel.innerText = "ComputeType:"; + computeTypeContainer.append(computeTypeLabel); + + const radioInputs = ["Parametric", "Euclidean"].map((computeType) => { + const id = `${demoPane.id}-${computeType}`; + const radioInput = document.createElement("input"); + radioInput.type = "radio"; + radioInput.id = id; + radioInput.value = computeType; + radioInput.name = "ComputeType"; + radioInput.checked = computeType === "Parametric"; + computeTypeContainer.append(radioInput); + + const label = document.createElement("label"); + label.htmlFor = id; + label.innerText = computeType; + computeTypeContainer.append(label); + return radioInput; + }); + + const demoRow = document.createElement("div"); + demoRow.className = "demo-row"; + + demoPane.demos.forEach((demo) => { + if (demo.disabled) { + return; + } + const demoComponent = demoPane.buildDemo(demo); + + radioInputs.forEach((radioInput: HTMLElement) => { + radioInput.addEventListener("input", (event: Event): void => { + demoPane.computeType = (event.target as HTMLInputElement).value as ComputeType; + demoComponent.setAttribute("computetype", demoPane.computeType); + }); + }); + demoRow.append(demoComponent); + }); + + container.append(header); + if (demoPane.chooseComputeType) { + container.append(computeTypeContainer); + } + container.append(demoRow); + + demoPane.append(container); +} diff --git a/website/other/bezier-rs-demos/src/utils/types.ts b/website/other/bezier-rs-demos/src/utils/types.ts index 1c9e4101..30ce0370 100644 --- a/website/other/bezier-rs-demos/src/utils/types.ts +++ b/website/other/bezier-rs-demos/src/utils/types.ts @@ -16,11 +16,11 @@ export type ComputeType = "Euclidean" | "Parametric"; export type BezierCallback = (bezier: WasmBezierInstance, options: Record, mouseLocation?: [number, number], computeType?: ComputeType) => string; export type SubpathCallback = (subpath: WasmSubpathInstance, options: Record, mouseLocation?: [number, number], computeType?: ComputeType) => string; -export type ExampleOptions = { +export type BezierDemoOptions = { [key in BezierCurveType]: { - disabled: boolean; - sliderOptions: SliderOption[]; - customPoints: number[][]; + disabled?: boolean; + sliderOptions?: SliderOption[]; + customPoints?: number[][]; }; }; @@ -53,3 +53,39 @@ export function getConstructorKey(bezierCurveType: BezierCurveType): WasmBezierC }; return mapping[bezierCurveType]; } + +export interface DemoArgs { + title: string; + disabled?: boolean; +} + +export interface BezierDemoArgs extends DemoArgs { + points: number[][]; + sliderOptions: SliderOption[]; +} + +export interface SubpathDemoArgs extends DemoArgs { + triples: (number[] | undefined)[][]; + closed: boolean; +} + +export interface Demo extends HTMLElement { + sliderOptions: SliderOption[]; + sliderData: Record; + sliderUnits: Record; + + drawDemo(figure: HTMLElement, mouseLocation?: [number, number]): void; + onMouseDown(event: MouseEvent): void; + onMouseUp(): void; + onMouseMove(event: MouseEvent): void; + getSliderUnit(sliderValue: number, variable: string): string; +} + +export interface DemoPane extends HTMLElement { + name: string; + demos: DemoArgs[]; + id: string; + chooseComputeType: boolean; + computeType: ComputeType; + buildDemo(demo: DemoArgs): Demo; +}