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",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"core-js": "^3.26.1",
|
||||
"vue": "^3.2.13"
|
||||
"core-js": "^3.26.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||
|
|
@ -17,7 +16,6 @@
|
|||
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"@vue/compiler-sfc": "^3.2.31",
|
||||
"@vue/eslint-config-airbnb": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.2",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||
|
|
@ -26,8 +24,7 @@
|
|||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier-vue": "^4.2.0",
|
||||
"eslint-plugin-vue": "^9.7.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vue-template-compiler": "^2.7.14"
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"wasm-pack": "^0.10.3"
|
||||
|
|
@ -323,6 +320,7 @@
|
|||
"version": "7.20.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz",
|
||||
"integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
|
|
@ -1395,6 +1393,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/shared": "3.2.45",
|
||||
|
|
@ -1406,6 +1405,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -1415,6 +1415,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
||||
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
|
|
@ -1432,6 +1433,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
||||
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -1552,6 +1554,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
||||
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.45"
|
||||
}
|
||||
|
|
@ -1560,6 +1564,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
||||
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
|
|
@ -1572,6 +1577,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
||||
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -1581,6 +1588,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
|
|
@ -1591,6 +1600,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
||||
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -1602,7 +1613,8 @@
|
|||
"node_modules/@vue/shared": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
|
||||
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
|
||||
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/vue-loader-v15": {
|
||||
"name": "vue-loader",
|
||||
|
|
@ -3290,13 +3302,17 @@
|
|||
"node_modules/csstype": {
|
||||
"version": "2.6.21",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
|
||||
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
|
||||
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/de-indent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
|
|
@ -4563,7 +4579,8 @@
|
|||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/esutils": {
|
||||
"version": "2.0.3",
|
||||
|
|
@ -6800,6 +6817,7 @@
|
|||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"sourcemap-codec": "^1.4.8"
|
||||
}
|
||||
|
|
@ -7140,6 +7158,7 @@
|
|||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
|
|
@ -7781,7 +7800,8 @@
|
|||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
|
|
@ -7886,6 +7906,7 @@
|
|||
"version": "8.4.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -9253,6 +9274,7 @@
|
|||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -9261,6 +9283,7 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -9278,7 +9301,8 @@
|
|||
"node_modules/sourcemap-codec": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/spdx-correct": {
|
||||
"version": "3.1.1",
|
||||
|
|
@ -10140,6 +10164,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
|
||||
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/compiler-sfc": "3.2.45",
|
||||
|
|
@ -10327,6 +10353,8 @@
|
|||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"de-indent": "^1.0.2",
|
||||
"he": "^1.2.0"
|
||||
|
|
@ -11445,7 +11473,8 @@
|
|||
"@babel/parser": {
|
||||
"version": "7.20.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz",
|
||||
"integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg=="
|
||||
"integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.18.10",
|
||||
|
|
@ -12262,6 +12291,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/shared": "3.2.45",
|
||||
|
|
@ -12273,6 +12303,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -12282,6 +12313,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
||||
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
|
|
@ -12299,6 +12331,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
||||
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -12388,6 +12421,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
||||
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@vue/shared": "3.2.45"
|
||||
}
|
||||
|
|
@ -12396,6 +12431,7 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
||||
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
|
|
@ -12408,6 +12444,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
||||
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@vue/reactivity": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -12417,6 +12455,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@vue/runtime-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
|
|
@ -12427,6 +12467,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
||||
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@vue/compiler-ssr": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
|
|
@ -12435,7 +12477,8 @@
|
|||
"@vue/shared": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
|
||||
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
|
||||
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==",
|
||||
"dev": true
|
||||
},
|
||||
"@vue/vue-loader-v15": {
|
||||
"version": "npm:vue-loader@15.10.1",
|
||||
|
|
@ -13702,13 +13745,17 @@
|
|||
"csstype": {
|
||||
"version": "2.6.21",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
|
||||
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
|
||||
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"de-indent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
|
|
@ -14671,7 +14718,8 @@
|
|||
"estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.3",
|
||||
|
|
@ -16336,6 +16384,7 @@
|
|||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sourcemap-codec": "^1.4.8"
|
||||
}
|
||||
|
|
@ -16595,7 +16644,8 @@
|
|||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
|
|
@ -17072,7 +17122,8 @@
|
|||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.1",
|
||||
|
|
@ -17154,6 +17205,7 @@
|
|||
"version": "8.4.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
|
|
@ -18103,12 +18155,14 @@
|
|||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.21",
|
||||
|
|
@ -18123,7 +18177,8 @@
|
|||
"sourcemap-codec": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"dev": true
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.1.1",
|
||||
|
|
@ -18765,6 +18820,8 @@
|
|||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
|
||||
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/compiler-sfc": "3.2.45",
|
||||
|
|
@ -18908,6 +18965,8 @@
|
|||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"de-indent": "^1.0.2",
|
||||
"he": "^1.2.0"
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@
|
|||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.26.1",
|
||||
"vue": "^3.2.13"
|
||||
"core-js": "^3.26.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||
|
|
@ -18,7 +17,6 @@
|
|||
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"@vue/compiler-sfc": "^3.2.31",
|
||||
"@vue/eslint-config-airbnb": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.2",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||
|
|
@ -27,8 +25,7 @@
|
|||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier-vue": "^4.2.0",
|
||||
"eslint-plugin-vue": "^9.7.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vue-template-compiler": "^2.7.14"
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"wasm-pack": "^0.10.3"
|
||||
|
|
|
|||
|
|
@ -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 SubpathCallback = (subpath: WasmSubpathInstance, options: Record<string, number>, mouseLocation?: [number, number], computeType?: ComputeType) => string;
|
||||
|
||||
export type ExampleOptions = {
|
||||
export type BezierDemoOptions = {
|
||||
[key in BezierCurveType]: {
|
||||
disabled: boolean;
|
||||
sliderOptions: SliderOption[];
|
||||
customPoints: number[][];
|
||||
disabled?: boolean;
|
||||
sliderOptions?: SliderOption[];
|
||||
customPoints?: number[][];
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -53,3 +53,39 @@ export function getConstructorKey(bezierCurveType: BezierCurveType): WasmBezierC
|
|||
};
|
||||
return mapping[bezierCurveType];
|
||||
}
|
||||
|
||||
export interface DemoArgs {
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface BezierDemoArgs extends DemoArgs {
|
||||
points: number[][];
|
||||
sliderOptions: SliderOption[];
|
||||
}
|
||||
|
||||
export interface SubpathDemoArgs extends DemoArgs {
|
||||
triples: (number[] | undefined)[][];
|
||||
closed: boolean;
|
||||
}
|
||||
|
||||
export interface Demo extends HTMLElement {
|
||||
sliderOptions: SliderOption[];
|
||||
sliderData: Record<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