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 <hannahli2010@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
758f757783
commit
01853fe4b7
|
|
@ -8,8 +8,7 @@
|
||||||
"name": "bezier-rs-demos",
|
"name": "bezier-rs-demos",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.26.1",
|
"core-js": "^3.26.1"
|
||||||
"vue": "^3.2.13"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||||
|
|
@ -17,7 +16,6 @@
|
||||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||||
"@vue/cli-service": "^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-airbnb": "^6.0.0",
|
||||||
"@vue/eslint-config-typescript": "^11.0.2",
|
"@vue/eslint-config-typescript": "^11.0.2",
|
||||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||||
|
|
@ -26,8 +24,7 @@
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-prettier-vue": "^4.2.0",
|
"eslint-plugin-prettier-vue": "^4.2.0",
|
||||||
"eslint-plugin-vue": "^9.7.0",
|
"eslint-plugin-vue": "^9.7.0",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3"
|
||||||
"vue-template-compiler": "^2.7.14"
|
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"wasm-pack": "^0.10.3"
|
"wasm-pack": "^0.10.3"
|
||||||
|
|
@ -323,6 +320,7 @@
|
||||||
"version": "7.20.3",
|
"version": "7.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz",
|
"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,
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
},
|
},
|
||||||
|
|
@ -1395,6 +1393,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||||
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
"@vue/shared": "3.2.45",
|
"@vue/shared": "3.2.45",
|
||||||
|
|
@ -1406,6 +1405,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
||||||
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.2.45",
|
"@vue/compiler-core": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -1415,6 +1415,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
||||||
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
"@vue/compiler-core": "3.2.45",
|
"@vue/compiler-core": "3.2.45",
|
||||||
|
|
@ -1432,6 +1433,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
||||||
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.2.45",
|
"@vue/compiler-dom": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -1552,6 +1554,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
||||||
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
}
|
}
|
||||||
|
|
@ -1560,6 +1564,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
||||||
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
"@vue/compiler-core": "3.2.45",
|
"@vue/compiler-core": "3.2.45",
|
||||||
|
|
@ -1572,6 +1577,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
||||||
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.2.45",
|
"@vue/reactivity": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -1581,6 +1588,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
||||||
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/runtime-core": "3.2.45",
|
"@vue/runtime-core": "3.2.45",
|
||||||
"@vue/shared": "3.2.45",
|
"@vue/shared": "3.2.45",
|
||||||
|
|
@ -1591,6 +1600,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
||||||
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.2.45",
|
"@vue/compiler-ssr": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -1602,7 +1613,8 @@
|
||||||
"node_modules/@vue/shared": {
|
"node_modules/@vue/shared": {
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
|
"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": {
|
"node_modules/@vue/vue-loader-v15": {
|
||||||
"name": "vue-loader",
|
"name": "vue-loader",
|
||||||
|
|
@ -3290,13 +3302,17 @@
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "2.6.21",
|
"version": "2.6.21",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
|
"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": {
|
"node_modules/de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
|
|
@ -4563,7 +4579,8 @@
|
||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"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": {
|
"node_modules/esutils": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
|
|
@ -6800,6 +6817,7 @@
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sourcemap-codec": "^1.4.8"
|
"sourcemap-codec": "^1.4.8"
|
||||||
}
|
}
|
||||||
|
|
@ -7140,6 +7158,7 @@
|
||||||
"version": "3.3.4",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
"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,
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.cjs"
|
||||||
},
|
},
|
||||||
|
|
@ -7781,7 +7800,8 @@
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"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": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
|
|
@ -7886,6 +7906,7 @@
|
||||||
"version": "8.4.19",
|
"version": "8.4.19",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||||
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|
@ -9253,6 +9274,7 @@
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -9261,6 +9283,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
"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,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -9278,7 +9301,8 @@
|
||||||
"node_modules/sourcemap-codec": {
|
"node_modules/sourcemap-codec": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
"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": {
|
"node_modules/spdx-correct": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
|
|
@ -10140,6 +10164,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
|
||||||
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.2.45",
|
"@vue/compiler-dom": "3.2.45",
|
||||||
"@vue/compiler-sfc": "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",
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||||
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"de-indent": "^1.0.2",
|
"de-indent": "^1.0.2",
|
||||||
"he": "^1.2.0"
|
"he": "^1.2.0"
|
||||||
|
|
@ -11445,7 +11473,8 @@
|
||||||
"@babel/parser": {
|
"@babel/parser": {
|
||||||
"version": "7.20.3",
|
"version": "7.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz",
|
"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": {
|
"@babel/template": {
|
||||||
"version": "7.18.10",
|
"version": "7.18.10",
|
||||||
|
|
@ -12262,6 +12291,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||||
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
"@vue/shared": "3.2.45",
|
"@vue/shared": "3.2.45",
|
||||||
|
|
@ -12273,6 +12303,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
||||||
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-core": "3.2.45",
|
"@vue/compiler-core": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -12282,6 +12313,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
||||||
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
"@vue/compiler-core": "3.2.45",
|
"@vue/compiler-core": "3.2.45",
|
||||||
|
|
@ -12299,6 +12331,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
||||||
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-dom": "3.2.45",
|
"@vue/compiler-dom": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -12388,6 +12421,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
||||||
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
}
|
}
|
||||||
|
|
@ -12396,6 +12431,7 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
||||||
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
"@vue/compiler-core": "3.2.45",
|
"@vue/compiler-core": "3.2.45",
|
||||||
|
|
@ -12408,6 +12444,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
||||||
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/reactivity": "3.2.45",
|
"@vue/reactivity": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -12417,6 +12455,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
||||||
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/runtime-core": "3.2.45",
|
"@vue/runtime-core": "3.2.45",
|
||||||
"@vue/shared": "3.2.45",
|
"@vue/shared": "3.2.45",
|
||||||
|
|
@ -12427,6 +12467,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
||||||
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-ssr": "3.2.45",
|
"@vue/compiler-ssr": "3.2.45",
|
||||||
"@vue/shared": "3.2.45"
|
"@vue/shared": "3.2.45"
|
||||||
|
|
@ -12435,7 +12477,8 @@
|
||||||
"@vue/shared": {
|
"@vue/shared": {
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
|
"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": {
|
"@vue/vue-loader-v15": {
|
||||||
"version": "npm:vue-loader@15.10.1",
|
"version": "npm:vue-loader@15.10.1",
|
||||||
|
|
@ -13702,13 +13745,17 @@
|
||||||
"csstype": {
|
"csstype": {
|
||||||
"version": "2.6.21",
|
"version": "2.6.21",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
|
"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": {
|
"de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
|
|
@ -14671,7 +14718,8 @@
|
||||||
"estree-walker": {
|
"estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"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": {
|
"esutils": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
|
|
@ -16336,6 +16384,7 @@
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"sourcemap-codec": "^1.4.8"
|
"sourcemap-codec": "^1.4.8"
|
||||||
}
|
}
|
||||||
|
|
@ -16595,7 +16644,8 @@
|
||||||
"nanoid": {
|
"nanoid": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
"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": {
|
"natural-compare": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
|
|
@ -17072,7 +17122,8 @@
|
||||||
"picocolors": {
|
"picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"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": {
|
"picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
|
|
@ -17154,6 +17205,7 @@
|
||||||
"version": "8.4.19",
|
"version": "8.4.19",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"nanoid": "^3.3.4",
|
"nanoid": "^3.3.4",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
|
|
@ -18103,12 +18155,14 @@
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"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": {
|
"source-map-js": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
"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": {
|
"source-map-support": {
|
||||||
"version": "0.5.21",
|
"version": "0.5.21",
|
||||||
|
|
@ -18123,7 +18177,8 @@
|
||||||
"sourcemap-codec": {
|
"sourcemap-codec": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
"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": {
|
"spdx-correct": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
|
|
@ -18765,6 +18820,8 @@
|
||||||
"version": "3.2.45",
|
"version": "3.2.45",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
|
||||||
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-dom": "3.2.45",
|
"@vue/compiler-dom": "3.2.45",
|
||||||
"@vue/compiler-sfc": "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",
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||||
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"de-indent": "^1.0.2",
|
"de-indent": "^1.0.2",
|
||||||
"he": "^1.2.0"
|
"he": "^1.2.0"
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.26.1",
|
"core-js": "^3.26.1"
|
||||||
"vue": "^3.2.13"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||||
|
|
@ -18,7 +17,6 @@
|
||||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||||
"@vue/cli-service": "^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-airbnb": "^6.0.0",
|
||||||
"@vue/eslint-config-typescript": "^11.0.2",
|
"@vue/eslint-config-typescript": "^11.0.2",
|
||||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||||
|
|
@ -27,8 +25,7 @@
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-prettier-vue": "^4.2.0",
|
"eslint-plugin-prettier-vue": "^4.2.0",
|
||||||
"eslint-plugin-vue": "^9.7.0",
|
"eslint-plugin-vue": "^9.7.0",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3"
|
||||||
"vue-template-compiler": "^2.7.14"
|
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"wasm-pack": "^0.10.3"
|
"wasm-pack": "^0.10.3"
|
||||||
|
|
|
||||||
|
|
@ -1,662 +0,0 @@
|
||||||
<template>
|
|
||||||
<h1>Bezier-rs Interactive Documentation</h1>
|
|
||||||
<p>
|
|
||||||
This is the interactive documentation for the <a href="https://crates.io/crates/bezier-rs"><b>Bezier-rs</b></a> library. View the
|
|
||||||
<a href="https://docs.rs/bezier-rs/latest/bezier_rs">crate documentation</a>
|
|
||||||
for detailed function descriptions and API usage. Click and drag on the endpoints of the example curves to visualize the various Bezier utilities and functions.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Beziers</h2>
|
|
||||||
<div v-for="(feature, index) in bezierFeatures" :key="index">
|
|
||||||
<BezierExamplePane
|
|
||||||
:name="feature.name"
|
|
||||||
:callback="feature.callback"
|
|
||||||
:exampleOptions="feature.exampleOptions"
|
|
||||||
:triggerOnMouseMove="feature.triggerOnMouseMove"
|
|
||||||
:chooseComputeType="feature.chooseComputeType"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Subpaths</h2>
|
|
||||||
<div v-for="(feature, index) in subpathFeatures" :key="index">
|
|
||||||
<SubpathExamplePane
|
|
||||||
:name="feature.name"
|
|
||||||
:callback="feature.callback"
|
|
||||||
:sliderOptions="feature.sliderOptions"
|
|
||||||
:triggerOnMouseMove="feature.triggerOnMouseMove"
|
|
||||||
:chooseComputeType="feature.chooseComputeType"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
#app {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
margin: 40px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app > h1 + p {
|
|
||||||
max-width: 768px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: auto;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app > h2 {
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Example Pane styles */
|
|
||||||
.example-row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compute-type-choice {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-pane-header {
|
|
||||||
margin-top: 2em;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-pane-container {
|
|
||||||
position: relative;
|
|
||||||
width: fit-content;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Example styles */
|
|
||||||
.example-header {
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-figure {
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border: solid 1px black;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from "vue";
|
|
||||||
|
|
||||||
import { WasmBezier } from "@/../wasm/pkg";
|
|
||||||
import { ComputeType, ExampleOptions, WasmBezierInstance, WasmSubpathInstance } from "@/utils/types";
|
|
||||||
|
|
||||||
import BezierExamplePane from "@/components/BezierExamplePane.vue";
|
|
||||||
import SubpathExamplePane from "@/components/SubpathExamplePane.vue";
|
|
||||||
|
|
||||||
const tSliderOptions = {
|
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
step: 0.01,
|
|
||||||
default: 0.5,
|
|
||||||
variable: "t",
|
|
||||||
};
|
|
||||||
|
|
||||||
const tErrorOptions = {
|
|
||||||
variable: "error",
|
|
||||||
min: 0.1,
|
|
||||||
max: 2,
|
|
||||||
step: 0.1,
|
|
||||||
default: 0.5,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tMinimumSeperationOptions = {
|
|
||||||
variable: "minimum_seperation",
|
|
||||||
min: 0.001,
|
|
||||||
max: 0.25,
|
|
||||||
step: 0.001,
|
|
||||||
default: 0.05,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
bezierFeatures: [
|
|
||||||
{
|
|
||||||
name: "Constructor",
|
|
||||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.to_svg(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Bezier Through Points",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): 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"]);
|
|
||||||
},
|
|
||||||
exampleOptions: {
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Length",
|
|
||||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.length(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Evaluate",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => bezier.evaluate(options.computeArgument, computeType),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
chooseComputeType: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Lookup Table",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.compute_lookup_table(options.steps),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [
|
|
||||||
{
|
|
||||||
min: 2,
|
|
||||||
max: 15,
|
|
||||||
step: 1,
|
|
||||||
default: 5,
|
|
||||||
variable: "steps",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Derivative",
|
|
||||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.derivative(),
|
|
||||||
exampleOptions: {
|
|
||||||
Linear: {
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
Quadratic: {
|
|
||||||
customPoints: [
|
|
||||||
[30, 40],
|
|
||||||
[110, 50],
|
|
||||||
[120, 130],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Cubic: {
|
|
||||||
customPoints: [
|
|
||||||
[50, 50],
|
|
||||||
[60, 100],
|
|
||||||
[100, 140],
|
|
||||||
[140, 150],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Tangent",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.tangent(options.t),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tSliderOptions],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Normal",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.normal(options.t),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tSliderOptions],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Curvature",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.curvature(options.t),
|
|
||||||
exampleOptions: {
|
|
||||||
Linear: {
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tSliderOptions],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Split",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.split(options.t),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tSliderOptions],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Trim",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.trim(options.t1, options.t2),
|
|
||||||
exampleOptions: {
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Project",
|
|
||||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>, mouseLocation?: [number, number]): string =>
|
|
||||||
mouseLocation ? bezier.project(mouseLocation[0], mouseLocation[1]) : bezier.to_svg(),
|
|
||||||
triggerOnMouseMove: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Local Extrema",
|
|
||||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.local_extrema(),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
customPoints: [
|
|
||||||
[40, 40],
|
|
||||||
[160, 30],
|
|
||||||
[110, 150],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Cubic: {
|
|
||||||
customPoints: [
|
|
||||||
[160, 180],
|
|
||||||
[170, 10],
|
|
||||||
[30, 90],
|
|
||||||
[180, 160],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Bounding Box",
|
|
||||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.bounding_box(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Inflections",
|
|
||||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.inflections(),
|
|
||||||
exampleOptions: {
|
|
||||||
Linear: {
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
Quadratic: {
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Reduce",
|
|
||||||
callback: (bezier: WasmBezierInstance): string => bezier.reduce(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Offset",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.offset(options.distance),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [
|
|
||||||
{
|
|
||||||
variable: "distance",
|
|
||||||
min: -50,
|
|
||||||
max: 50,
|
|
||||||
step: 1,
|
|
||||||
default: 20,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Outline",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.outline(options.distance),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [
|
|
||||||
{
|
|
||||||
variable: "distance",
|
|
||||||
min: 0,
|
|
||||||
max: 50,
|
|
||||||
step: 1,
|
|
||||||
default: 20,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Graduated Outline",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.graduated_outline(options.start_distance, options.end_distance),
|
|
||||||
exampleOptions: {
|
|
||||||
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],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Skewed Outline",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string =>
|
|
||||||
bezier.skewed_outline(options.distance1, options.distance2, options.distance3, options.distance4),
|
|
||||||
exampleOptions: {
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Arcs",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.arcs(options.error, options.max_iterations, options.strategy),
|
|
||||||
exampleOptions: ((): Omit<ExampleOptions, "Linear"> => {
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Line Segment)",
|
|
||||||
callback: (bezier: WasmBezierInstance): string => {
|
|
||||||
const line = [
|
|
||||||
[150, 150],
|
|
||||||
[20, 20],
|
|
||||||
];
|
|
||||||
return bezier.intersect_line_segment(line);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Quadratic)",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => {
|
|
||||||
const quadratic = [
|
|
||||||
[20, 80],
|
|
||||||
[180, 10],
|
|
||||||
[90, 120],
|
|
||||||
];
|
|
||||||
return bezier.intersect_quadratic_segment(quadratic, options.error, options.minimum_seperation);
|
|
||||||
},
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tErrorOptions, tMinimumSeperationOptions],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Cubic)",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => {
|
|
||||||
const cubic = [
|
|
||||||
[40, 20],
|
|
||||||
[100, 40],
|
|
||||||
[40, 120],
|
|
||||||
[175, 140],
|
|
||||||
];
|
|
||||||
return bezier.intersect_cubic_segment(cubic, options.error, options.minimum_seperation);
|
|
||||||
},
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tErrorOptions, tMinimumSeperationOptions],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Self)",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.intersect_self(options.error),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tErrorOptions],
|
|
||||||
},
|
|
||||||
Cubic: {
|
|
||||||
customPoints: [
|
|
||||||
[160, 180],
|
|
||||||
[170, 10],
|
|
||||||
[30, 90],
|
|
||||||
[180, 140],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Rectangle)",
|
|
||||||
callback: (bezier: WasmBezierInstance): string =>
|
|
||||||
bezier.intersect_rectangle([
|
|
||||||
[50, 50],
|
|
||||||
[150, 150],
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Rotate",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.rotate(options.angle * Math.PI, 100, 100),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [
|
|
||||||
{
|
|
||||||
variable: "angle",
|
|
||||||
min: 0,
|
|
||||||
max: 2,
|
|
||||||
step: 1 / 50,
|
|
||||||
default: 0.12,
|
|
||||||
unit: "π",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "De Casteljau Points",
|
|
||||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.de_casteljau_points(options.t),
|
|
||||||
exampleOptions: {
|
|
||||||
Quadratic: {
|
|
||||||
sliderOptions: [tSliderOptions],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
subpathFeatures: [
|
|
||||||
{
|
|
||||||
name: "Constructor",
|
|
||||||
callback: (subpath: WasmSubpathInstance): string => subpath.to_svg(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Insert",
|
|
||||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => subpath.insert(options.computeArgument, computeType),
|
|
||||||
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
|
|
||||||
// TODO: Uncomment this after implementing the Euclidean version
|
|
||||||
// chooseComputeType: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Length",
|
|
||||||
callback: (subpath: WasmSubpathInstance): string => subpath.length(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Evaluate",
|
|
||||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => subpath.evaluate(options.computeArgument, computeType),
|
|
||||||
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
|
|
||||||
chooseComputeType: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Project",
|
|
||||||
callback: (subpath: WasmSubpathInstance, _: Record<string, number>, mouseLocation?: [number, number]): string =>
|
|
||||||
mouseLocation ? subpath.project(mouseLocation[0], mouseLocation[1]) : subpath.to_svg(),
|
|
||||||
triggerOnMouseMove: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Line Segment)",
|
|
||||||
callback: (subpath: WasmSubpathInstance): string =>
|
|
||||||
subpath.intersect_line_segment([
|
|
||||||
[150, 150],
|
|
||||||
[20, 20],
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Quadratic segment)",
|
|
||||||
callback: (subpath: WasmSubpathInstance): string =>
|
|
||||||
subpath.intersect_quadratic_segment([
|
|
||||||
[20, 80],
|
|
||||||
[180, 10],
|
|
||||||
[90, 120],
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Intersect (Cubic segment)",
|
|
||||||
callback: (subpath: WasmSubpathInstance): string =>
|
|
||||||
subpath.intersect_cubic_segment([
|
|
||||||
[40, 20],
|
|
||||||
[100, 40],
|
|
||||||
[40, 120],
|
|
||||||
[175, 140],
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
BezierExamplePane,
|
|
||||||
SubpathExamplePane,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -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<string, number>;
|
||||||
|
|
||||||
|
sliderUnits!: Record<string, string | string[]>;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h4 class="example-header">{{ title }}</h4>
|
|
||||||
<figure @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onMouseMove" class="example-figure" v-html="bezierSVG"></figure>
|
|
||||||
<div v-for="(slider, index) in sliderOptions" :key="index">
|
|
||||||
<div class="slider-label">{{ slider.variable }} = {{ sliderData[slider.variable] }}{{ getSliderValue(sliderData[slider.variable], sliderUnits[slider.variable]) }}</div>
|
|
||||||
<input class="slider" v-model.number="sliderData[slider.variable]" type="range" :step="slider.step" :min="slider.min" :max="slider.max" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style></style>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, PropType } from "vue";
|
|
||||||
|
|
||||||
import { WasmBezier } from "@/../wasm/pkg";
|
|
||||||
import { getConstructorKey, getCurveType, BezierCallback, BezierCurveType, WasmBezierManipulatorKey, SliderOption, ComputeType } 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"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
title: { type: String as PropType<string>, required: true },
|
|
||||||
points: { type: Array as PropType<Array<Array<number>>>, required: true, mutable: true },
|
|
||||||
callback: { type: Function as PropType<BezierCallback>, required: true },
|
|
||||||
sliderOptions: { type: Object as PropType<Array<SliderOption>>, default: () => ({}) },
|
|
||||||
triggerOnMouseMove: { type: Boolean as PropType<boolean>, default: false },
|
|
||||||
computeType: { type: String as PropType<ComputeType>, default: "Parametric" },
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
const curveType = getCurveType(this.points.length);
|
|
||||||
const manipulatorKeys = MANIPULATOR_KEYS_FROM_BEZIER_TYPE[curveType];
|
|
||||||
|
|
||||||
const bezier = WasmBezier[getConstructorKey(curveType)](this.points);
|
|
||||||
const sliderData = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.default })));
|
|
||||||
|
|
||||||
return {
|
|
||||||
bezier,
|
|
||||||
bezierSVG: this.callback(bezier, sliderData, undefined, "Euclidean"),
|
|
||||||
manipulatorKeys,
|
|
||||||
activeIndex: undefined as number | undefined,
|
|
||||||
mutablePoints: JSON.parse(JSON.stringify(this.points)),
|
|
||||||
sliderData,
|
|
||||||
sliderUnits: Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.unit }))),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onMouseDown(event: MouseEvent) {
|
|
||||||
const mx = event.offsetX;
|
|
||||||
const my = event.offsetY;
|
|
||||||
for (let pointIndex = 0; pointIndex < this.points.length; pointIndex += 1) {
|
|
||||||
const point = this.mutablePoints[pointIndex];
|
|
||||||
if (point && Math.abs(mx - point[0]) < SELECTABLE_RANGE && Math.abs(my - point[1]) < SELECTABLE_RANGE) {
|
|
||||||
this.activeIndex = pointIndex;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onMouseUp() {
|
|
||||||
this.activeIndex = undefined;
|
|
||||||
},
|
|
||||||
onMouseMove(event: MouseEvent) {
|
|
||||||
const mx = event.offsetX;
|
|
||||||
const my = event.offsetY;
|
|
||||||
if (this.activeIndex !== undefined) {
|
|
||||||
this.bezier[this.manipulatorKeys[this.activeIndex]](mx, my);
|
|
||||||
this.mutablePoints[this.activeIndex] = [mx, my];
|
|
||||||
this.bezierSVG = this.callback(this.bezier, this.sliderData, undefined, this.computeType);
|
|
||||||
} else if (this.triggerOnMouseMove) {
|
|
||||||
this.bezierSVG = this.callback(this.bezier, this.sliderData, [mx, my], this.computeType);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getSliderValue: (sliderValue: number, sliderUnit?: string | string[]) => (Array.isArray(sliderUnit) ? sliderUnit[sliderValue] : sliderUnit),
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
sliderData: {
|
|
||||||
handler() {
|
|
||||||
this.bezierSVG = this.callback(this.bezier, this.sliderData, undefined, this.computeType);
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
computeType: {
|
|
||||||
handler() {
|
|
||||||
this.bezierSVG = this.callback(this.bezier, this.sliderData, undefined, this.computeType);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="example-pane-container">
|
|
||||||
<h3 class="example-pane-header">{{ name }}</h3>
|
|
||||||
<div v-if="chooseComputeType" class="compute-type-choice">
|
|
||||||
<strong>ComputeType:</strong>
|
|
||||||
|
|
||||||
<input type="radio" :id="`${id}-parametric`" value="Parametric" v-model="computeTypeChoice" />
|
|
||||||
<label :for="`${id}-parametric`">Parametric</label>
|
|
||||||
|
|
||||||
<input type="radio" :id="`${id}-euclidean`" value="Euclidean" v-model="computeTypeChoice" />
|
|
||||||
<label :for="`${id}-euclidean`">Euclidean</label>
|
|
||||||
</div>
|
|
||||||
<div class="example-row">
|
|
||||||
<div v-for="(example, index) in examples" :key="index">
|
|
||||||
<BezierExample
|
|
||||||
v-if="!example.disabled"
|
|
||||||
:title="example.title"
|
|
||||||
:points="example.points"
|
|
||||||
:callback="callback"
|
|
||||||
:sliderOptions="example.sliderOptions"
|
|
||||||
:triggerOnMouseMove="triggerOnMouseMove"
|
|
||||||
:computeType="computeTypeChoice"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style></style>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, PropType } from "vue";
|
|
||||||
|
|
||||||
import { BezierCallback, BezierCurveType, BEZIER_CURVE_TYPE, ComputeType, ExampleOptions, SliderOption } from "@/utils/types";
|
|
||||||
|
|
||||||
import BezierExample from "@/components/BezierExample.vue";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
name: { type: String as PropType<string>, required: true },
|
|
||||||
callback: { type: Function as PropType<BezierCallback>, required: true },
|
|
||||||
exampleOptions: { type: Object as PropType<ExampleOptions>, default: () => ({}) },
|
|
||||||
triggerOnMouseMove: { type: Boolean as PropType<boolean>, default: false },
|
|
||||||
chooseComputeType: { type: Boolean as PropType<boolean>, default: false },
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
const exampleDefaults = {
|
|
||||||
Linear: {
|
|
||||||
points: [
|
|
||||||
[30, 60],
|
|
||||||
[140, 120],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Quadratic: {
|
|
||||||
points: [
|
|
||||||
[30, 50],
|
|
||||||
[140, 30],
|
|
||||||
[160, 170],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
Cubic: {
|
|
||||||
points: [
|
|
||||||
[30, 30],
|
|
||||||
[60, 140],
|
|
||||||
[150, 30],
|
|
||||||
[160, 160],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use quadratic slider options as a default if sliders are not provided for the other curve types.
|
|
||||||
const defaultSliderOptions: SliderOption[] = this.exampleOptions.Quadratic?.sliderOptions || [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
examples: BEZIER_CURVE_TYPE.map((curveType: BezierCurveType) => {
|
|
||||||
const givenData = this.exampleOptions[curveType];
|
|
||||||
const defaultData = exampleDefaults[curveType];
|
|
||||||
return {
|
|
||||||
title: curveType,
|
|
||||||
disabled: givenData?.disabled || false,
|
|
||||||
points: givenData?.customPoints || defaultData.points,
|
|
||||||
sliderOptions: givenData?.sliderOptions || defaultSliderOptions,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
id: `${Math.random()}`.substring(2),
|
|
||||||
computeTypeChoice: "Parametric" as ComputeType,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
BezierExample,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -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<string, number>;
|
||||||
|
|
||||||
|
sliderUnits!: Record<string, string | string[]>;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h4 class="example-header">{{ title }}</h4>
|
|
||||||
<figure @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onMouseMove" class="example-figure" v-html="subpathSVG"></figure>
|
|
||||||
<div v-for="(slider, index) in sliderOptions" :key="index">
|
|
||||||
<div class="slider-label">{{ slider.variable }} = {{ sliderData[slider.variable] }}{{ getSliderValue(sliderData[slider.variable], sliderUnits[slider.variable]) }}</div>
|
|
||||||
<input class="slider" v-model.number="sliderData[slider.variable]" type="range" :step="slider.step" :min="slider.min" :max="slider.max" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style></style>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, PropType } from "vue";
|
|
||||||
|
|
||||||
import { WasmSubpath } from "@/../wasm/pkg";
|
|
||||||
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"];
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
title: { type: String as PropType<string>, required: true },
|
|
||||||
triples: { type: Array as PropType<Array<Array<number[] | undefined>>>, mutable: true, required: true },
|
|
||||||
closed: { type: Boolean as PropType<boolean>, default: false },
|
|
||||||
callback: { type: Function as PropType<SubpathCallback>, required: true },
|
|
||||||
sliderOptions: { type: Object as PropType<Array<SliderOption>>, default: () => ({}) },
|
|
||||||
triggerOnMouseMove: { type: Boolean as PropType<boolean>, default: false },
|
|
||||||
computeType: { type: String as PropType<ComputeType>, default: "Parametric" },
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
const subpath = WasmSubpath.from_triples(this.triples, this.closed) as WasmSubpathInstance;
|
|
||||||
|
|
||||||
const sliderData = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.default })));
|
|
||||||
const sliderUnits = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.unit })));
|
|
||||||
|
|
||||||
return {
|
|
||||||
subpath,
|
|
||||||
subpathSVG: this.callback(subpath, sliderData, undefined, "Euclidean"),
|
|
||||||
activeIndex: undefined as number[] | undefined,
|
|
||||||
mutableTriples: JSON.parse(JSON.stringify(this.triples)),
|
|
||||||
sliderData,
|
|
||||||
sliderUnits,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onMouseDown(event: MouseEvent) {
|
|
||||||
const mx = event.offsetX;
|
|
||||||
const my = event.offsetY;
|
|
||||||
for (let controllerIndex = 0; controllerIndex < this.mutableTriples.length; controllerIndex += 1) {
|
|
||||||
for (let pointIndex = 0; pointIndex < 3; pointIndex += 1) {
|
|
||||||
const point = this.mutableTriples[controllerIndex][pointIndex];
|
|
||||||
if (point && Math.abs(mx - point[0]) < SELECTABLE_RANGE && Math.abs(my - point[1]) < SELECTABLE_RANGE) {
|
|
||||||
this.activeIndex = [controllerIndex, pointIndex];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onMouseUp() {
|
|
||||||
this.activeIndex = undefined;
|
|
||||||
},
|
|
||||||
onMouseMove(event: MouseEvent) {
|
|
||||||
const mx = event.offsetX;
|
|
||||||
const my = event.offsetY;
|
|
||||||
if (this.activeIndex) {
|
|
||||||
this.subpath[POINT_INDEX_TO_MANIPULATOR[this.activeIndex[1]]](this.activeIndex[0], mx, my);
|
|
||||||
this.mutableTriples[this.activeIndex[0]][this.activeIndex[1]] = [mx, my];
|
|
||||||
this.subpathSVG = this.callback(this.subpath, this.sliderData, [mx, my], this.computeType);
|
|
||||||
} else if (this.triggerOnMouseMove) {
|
|
||||||
this.subpathSVG = this.callback(this.subpath, this.sliderData, [mx, my], this.computeType);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getSliderValue: (sliderValue: number, sliderUnit?: string | string[]) => (Array.isArray(sliderUnit) ? sliderUnit[sliderValue] : sliderUnit),
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
sliderData: {
|
|
||||||
handler() {
|
|
||||||
this.subpathSVG = this.callback(this.subpath, this.sliderData, undefined, this.computeType);
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
computeType: {
|
|
||||||
handler() {
|
|
||||||
this.subpathSVG = this.callback(this.subpath, this.sliderData, undefined, this.computeType);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h3 class="example-pane-header">{{ name }}</h3>
|
|
||||||
<div v-if="chooseComputeType" class="compute-type-choice">
|
|
||||||
<strong>ComputeType:</strong>
|
|
||||||
|
|
||||||
<input type="radio" :id="`${id}-parametric`" value="Parametric" v-model="computeTypeChoice" />
|
|
||||||
<label :for="`${id}-parametric`">Parametric</label>
|
|
||||||
|
|
||||||
<input type="radio" :id="`${id}-euclidean`" value="Euclidean" v-model="computeTypeChoice" />
|
|
||||||
<label :for="`${id}-euclidean`">Euclidean</label>
|
|
||||||
</div>
|
|
||||||
<div class="example-row">
|
|
||||||
<div v-for="(example, index) in examples" :key="index">
|
|
||||||
<SubpathExample
|
|
||||||
:title="example.title"
|
|
||||||
:triples="example.triples"
|
|
||||||
:closed="example.closed"
|
|
||||||
:callback="callback"
|
|
||||||
:sliderOptions="sliderOptions"
|
|
||||||
:triggerOnMouseMove="triggerOnMouseMove"
|
|
||||||
:computeType="computeTypeChoice"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style></style>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, PropType } from "vue";
|
|
||||||
|
|
||||||
import { SubpathCallback, SliderOption, ComputeType } from "@/utils/types";
|
|
||||||
|
|
||||||
import SubpathExample from "@/components/SubpathExample.vue";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
name: { type: String as PropType<string>, required: true },
|
|
||||||
callback: { type: Function as PropType<SubpathCallback>, required: true },
|
|
||||||
sliderOptions: { type: Array as PropType<Array<SliderOption>>, default: () => [] },
|
|
||||||
triggerOnMouseMove: { type: Boolean as PropType<boolean>, default: false },
|
|
||||||
chooseComputeType: { type: Boolean as PropType<boolean>, default: false },
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
examples: [
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
id: `${Math.random()}`.substring(2),
|
|
||||||
computeTypeChoice: "Parametric" as ComputeType,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
SubpathExample,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -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, number>): string => bezier.to_svg(),
|
||||||
|
},
|
||||||
|
"Bezier Through Points": {
|
||||||
|
callback: (bezier: WasmBezierInstance, options: Record<string, number>): 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, number>): string => bezier.length(),
|
||||||
|
},
|
||||||
|
Evaluate: {
|
||||||
|
callback: (bezier: WasmBezierInstance, options: Record<string, number>, _: 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, number>): 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, number>): 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, number>): string => bezier.tangent(options.t),
|
||||||
|
demoOptions: {
|
||||||
|
Quadratic: {
|
||||||
|
sliderOptions: [tSliderOptions],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Normal: {
|
||||||
|
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.normal(options.t),
|
||||||
|
demoOptions: {
|
||||||
|
Quadratic: {
|
||||||
|
sliderOptions: [tSliderOptions],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Curvature: {
|
||||||
|
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.curvature(options.t),
|
||||||
|
demoOptions: {
|
||||||
|
Linear: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
Quadratic: {
|
||||||
|
sliderOptions: [tSliderOptions],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Split: {
|
||||||
|
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.split(options.t),
|
||||||
|
demoOptions: {
|
||||||
|
Quadratic: {
|
||||||
|
sliderOptions: [tSliderOptions],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trim: {
|
||||||
|
callback: (bezier: WasmBezierInstance, options: Record<string, number>): 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<string, number>, mouseLocation?: [number, number]): string =>
|
||||||
|
mouseLocation ? bezier.project(mouseLocation[0], mouseLocation[1]) : bezier.to_svg(),
|
||||||
|
triggerOnMouseMove: true,
|
||||||
|
},
|
||||||
|
"Local Extrema": {
|
||||||
|
callback: (bezier: WasmBezierInstance, _: Record<string, number>): 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, number>): string => bezier.bounding_box(),
|
||||||
|
},
|
||||||
|
Inflections: {
|
||||||
|
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.inflections(),
|
||||||
|
demoOptions: {
|
||||||
|
Linear: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
Quadratic: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Reduce: {
|
||||||
|
callback: (bezier: WasmBezierInstance): string => bezier.reduce(),
|
||||||
|
},
|
||||||
|
Offset: {
|
||||||
|
callback: (bezier: WasmBezierInstance, options: Record<string, number>): 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, number>): 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, number>): 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, number>): 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, number>): string => bezier.arcs(options.error, options.max_iterations, options.strategy),
|
||||||
|
demoOptions: ((): Omit<BezierDemoOptions, "Linear"> => {
|
||||||
|
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, number>): 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, number>): 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, number>): 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, number>): 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, number>): string => bezier.de_casteljau_points(options.t),
|
||||||
|
demoOptions: {
|
||||||
|
Quadratic: {
|
||||||
|
sliderOptions: [tSliderOptions],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BezierFeatureName = keyof typeof bezierFeatures;
|
||||||
|
export type BezierFeatureOptions = {
|
||||||
|
callback: BezierCallback;
|
||||||
|
demoOptions?: Partial<BezierDemoOptions>;
|
||||||
|
triggerOnMouseMove?: boolean;
|
||||||
|
chooseComputeType?: boolean;
|
||||||
|
};
|
||||||
|
export default bezierFeatures as Record<BezierFeatureName, BezierFeatureOptions>;
|
||||||
|
|
@ -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<string, number>, _: 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<string, number>, _: undefined, computeType: ComputeType): string => subpath.evaluate(options.computeArgument, computeType),
|
||||||
|
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
|
||||||
|
chooseComputeType: true,
|
||||||
|
},
|
||||||
|
Project: {
|
||||||
|
callback: (subpath: WasmSubpathInstance, _: Record<string, number>, 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<SubpathFeatureName, SubpathFeatureOptions>;
|
||||||
|
|
@ -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 = `
|
||||||
|
<h1>Bezier-rs Interactive Documentation</h1>
|
||||||
|
<p>
|
||||||
|
This is the interactive documentation for the <a href="https://crates.io/crates/bezier-rs"><b>Bezier-rs</b></a> library. View the
|
||||||
|
<a href="https://docs.rs/bezier-rs/latest/bezier_rs">crate documentation</a>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Beziers</h2>
|
||||||
|
<div id="bezier-demos"></div>
|
||||||
|
<h2>Subpaths</h2>
|
||||||
|
<div id="subpath-demos"></div>
|
||||||
|
`.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);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
declare module "*.vue" {
|
|
||||||
import type { DefineComponent } from "vue";
|
|
||||||
const component: DefineComponent<{}, {}, any>;
|
|
||||||
export default component;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -16,11 +16,11 @@ export type ComputeType = "Euclidean" | "Parametric";
|
||||||
export type BezierCallback = (bezier: WasmBezierInstance, options: Record<string, number>, mouseLocation?: [number, number], computeType?: ComputeType) => string;
|
export type BezierCallback = (bezier: WasmBezierInstance, options: Record<string, number>, mouseLocation?: [number, number], computeType?: ComputeType) => string;
|
||||||
export type SubpathCallback = (subpath: WasmSubpathInstance, options: Record<string, number>, mouseLocation?: [number, number], computeType?: ComputeType) => string;
|
export type SubpathCallback = (subpath: WasmSubpathInstance, options: Record<string, number>, mouseLocation?: [number, number], computeType?: ComputeType) => string;
|
||||||
|
|
||||||
export type ExampleOptions = {
|
export type BezierDemoOptions = {
|
||||||
[key in BezierCurveType]: {
|
[key in BezierCurveType]: {
|
||||||
disabled: boolean;
|
disabled?: boolean;
|
||||||
sliderOptions: SliderOption[];
|
sliderOptions?: SliderOption[];
|
||||||
customPoints: number[][];
|
customPoints?: number[][];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -53,3 +53,39 @@ export function getConstructorKey(bezierCurveType: BezierCurveType): WasmBezierC
|
||||||
};
|
};
|
||||||
return mapping[bezierCurveType];
|
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<string, number>;
|
||||||
|
sliderUnits: Record<string, string | string[]>;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue