From f53eeef3ac2f6e8afcff3f1297f0cd5b70642e2f Mon Sep 17 00:00:00 2001 From: Donovan Hutchence Date: Fri, 28 Jun 2024 12:33:07 +0100 Subject: [PATCH] Rect select fix (#119) --- package-lock.json | 132 +++++++++++++++++----------------- package.json | 14 ++-- src/camera.ts | 2 +- src/editor.ts | 3 +- src/splat.ts | 28 +++++--- src/tools/brush-selection.ts | 61 +++++++++------- src/tools/picker-selection.ts | 14 ++-- src/tools/rect-selection.ts | 73 ++++++++++--------- 8 files changed, 176 insertions(+), 151 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30e4483..f4ed19a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "supersplat", - "version": "0.19.2", + "version": "0.19.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "supersplat", - "version": "0.19.2", + "version": "0.19.3", "license": "MIT", "devDependencies": { "@playcanvas/eslint-config": "^1.7.1", - "@playcanvas/pcui": "^4.3.0", + "@playcanvas/pcui": "^4.4.0", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-image": "^3.0.3", "@rollup/plugin-json": "^6.1.0", @@ -19,19 +19,19 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@types/wicg-file-system-access": "^2023.10.5", - "@typescript-eslint/eslint-plugin": "^7.10.0", - "@typescript-eslint/parser": "^7.10.0", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", "concurrently": "^8.2.2", "cors": "^2.8.5", "cross-env": "^7.0.3", "eslint": "^8.56.0", "jest": "^29.7.0", - "playcanvas": "^1.71.5", + "playcanvas": "^1.72.0", "rollup": "^4.18.0", - "rollup-plugin-sass": "^1.12.22", + "rollup-plugin-sass": "^1.13.0", "rollup-plugin-visualizer": "^5.12.0", "serve": "^14.2.3", - "tslib": "^2.6.2" + "tslib": "^2.6.3" } }, "node_modules/@ampproject/remapping": { @@ -1329,9 +1329,9 @@ "dev": true }, "node_modules/@playcanvas/pcui": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@playcanvas/pcui/-/pcui-4.3.0.tgz", - "integrity": "sha512-nhyF+u55ws1FPA4A3EkD9p4ujK2HfGxuA6GRWJWe2A9tPyKRMGTNNjrubc/4GnmCxfZP7ux9QEd/a82MXSoU3g==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@playcanvas/pcui/-/pcui-4.4.0.tgz", + "integrity": "sha512-pMFM4adUwICRP304b4miWmnfOJQFXiOYQiGVtnzSDWl87PMLEVAoeGnxDIZp/HZ3VCgcXd0j5Vbr3cKURmcWqg==", "dev": true, "dependencies": { "@playcanvas/observer": "^1.4.0" @@ -1882,16 +1882,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz", - "integrity": "sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz", + "integrity": "sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.10.0", - "@typescript-eslint/type-utils": "7.10.0", - "@typescript-eslint/utils": "7.10.0", - "@typescript-eslint/visitor-keys": "7.10.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/type-utils": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1915,15 +1915,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.10.0.tgz", - "integrity": "sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.14.1.tgz", + "integrity": "sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.10.0", - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/typescript-estree": "7.10.0", - "@typescript-eslint/visitor-keys": "7.10.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", "debug": "^4.3.4" }, "engines": { @@ -1943,13 +1943,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", - "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz", + "integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/visitor-keys": "7.10.0" + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1960,13 +1960,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz", - "integrity": "sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.14.1.tgz", + "integrity": "sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.10.0", - "@typescript-eslint/utils": "7.10.0", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/utils": "7.14.1", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1987,9 +1987,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", - "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz", + "integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2000,13 +2000,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", - "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz", + "integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/visitor-keys": "7.10.0", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2028,15 +2028,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", - "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.14.1.tgz", + "integrity": "sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.10.0", - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/typescript-estree": "7.10.0" + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2050,12 +2050,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", - "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz", + "integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/types": "7.14.1", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -5846,9 +5846,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -6299,12 +6299,12 @@ } }, "node_modules/playcanvas": { - "version": "1.71.5", - "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.71.5.tgz", - "integrity": "sha512-1Gba1sdye9HhcSl4DstBAxCgtnKy/oHFgDzmRxcOlcZLvlYIK+G7SxPluiXfRewnTpWIGn03yZ4841zhtNAZDg==", + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.72.0.tgz", + "integrity": "sha512-emtQBXvBrr5gl/9bNT+FK+fl7Zc1Z2/9faxDuo/t/J1Vrz2XFimhjQeJt6QB2F2G2typwylXm6HvRxjVF5TX8w==", "dev": true, "dependencies": { - "@types/webxr": "^0.5.15", + "@types/webxr": "^0.5.16", "@webgpu/types": "^0.1.40" }, "engines": { @@ -6651,9 +6651,9 @@ } }, "node_modules/rollup-plugin-sass": { - "version": "1.12.22", - "resolved": "https://registry.npmjs.org/rollup-plugin-sass/-/rollup-plugin-sass-1.12.22.tgz", - "integrity": "sha512-bwlXqkmRDc1rVjkbN+wxXFh1jfnMEi3vHr5TufM+loUFYKd6RskGMx5Xz4sIVdxIu1oAilZkM4Px8P3QlmwfUA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-sass/-/rollup-plugin-sass-1.13.0.tgz", + "integrity": "sha512-TL/pBbuqN3Qftiub1rLWiPnUGyL5PC7/+4x1ZgFJWzu1Y8n2vwYILt1kPR83AUzPOwqgFfD+B/LqgV6ee0+CYQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^3 || ^4 || ^5", @@ -7462,9 +7462,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, "node_modules/type-check": { diff --git a/package.json b/package.json index c1b6be4..d9c7730 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supersplat", - "version": "0.19.2", + "version": "0.19.3", "author": "PlayCanvas", "homepage": "https://playcanvas.com/supersplat/editor", "description": "3D Gaussian Splat Editor", @@ -52,7 +52,7 @@ }, "devDependencies": { "@playcanvas/eslint-config": "^1.7.1", - "@playcanvas/pcui": "^4.3.0", + "@playcanvas/pcui": "^4.4.0", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-image": "^3.0.3", "@rollup/plugin-json": "^6.1.0", @@ -61,18 +61,18 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@types/wicg-file-system-access": "^2023.10.5", - "@typescript-eslint/eslint-plugin": "^7.10.0", - "@typescript-eslint/parser": "^7.10.0", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", "concurrently": "^8.2.2", "cors": "^2.8.5", "cross-env": "^7.0.3", "eslint": "^8.56.0", "jest": "^29.7.0", - "playcanvas": "^1.71.5", + "playcanvas": "^1.72.0", "rollup": "^4.18.0", - "rollup-plugin-sass": "^1.12.22", + "rollup-plugin-sass": "^1.13.0", "rollup-plugin-visualizer": "^5.12.0", "serve": "^14.2.3", - "tslib": "^2.6.2" + "tslib": "^2.6.3" } } diff --git a/src/camera.ts b/src/camera.ts index 80aa60e..9b6f2e5 100644 --- a/src/camera.ts +++ b/src/camera.ts @@ -363,7 +363,7 @@ class Camera extends Element { const renderTarget = this.entity.camera.renderTarget; // resolve msaa buffer - if (renderTarget._samples > 1) { + if (renderTarget.samples > 1) { renderTarget.resolve(true, false); } diff --git a/src/editor.ts b/src/editor.ts index c4f467b..ba4d546 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -325,7 +325,8 @@ const registerEditorEvents = (events: Events, editHistory: EditHistory, scene: S mat.transformVec4(vec4, vec4); vec4.x /= vec4.w; vec4.y = -vec4.y / vec4.w; - if (vec4.x < sx || vec4.x > ex || vec4.y < sy || vec4.y > ey) { + vec4.z /= vec4.w; + if (vec4.x < sx || vec4.x > ex || vec4.y < sy || vec4.y > ey || vec4.z < -1 || vec4.z > 1) { return false; } return true; diff --git a/src/splat.ts b/src/splat.ts index f73d711..d1e949a 100644 --- a/src/splat.ts +++ b/src/splat.ts @@ -26,15 +26,27 @@ flat varying highp uint vertexState; flat varying highp uint vertexId; #endif +vec4 discardVec = vec4(0.0, 0.0, 2.0, 1.0); void main(void) { - // evaluate center of the splat in object space - vec3 centerLocal = evalCenter(); - - // evaluate the rest of the splat using world space center - vec4 centerWorld = matrix_model * vec4(centerLocal, 1.0); - - gl_Position = evalSplat(centerWorld); + // calculate splat uv + if (!calcSplatUV()) { + gl_Position = discardVec; + return; + } + // read data + readData(); + vec4 pos; + if (!evalSplat(pos)) { + gl_Position = discardVec; + return; + } + gl_Position = pos; + texCoord = vertex_position.xy; + color = getColor(); + #ifndef DITHER_NONE + id = float(splatId); + #endif vertexState = uint(texelFetch(splatState, splatUV, 0).r * 255.0); @@ -47,7 +59,7 @@ void main(void) const fragmentShader = /*glsl*/` #ifdef PICK_PASS -flat varying highp uint vertexId; + flat varying highp uint vertexId; #endif flat varying highp uint vertexState; diff --git a/src/tools/brush-selection.ts b/src/tools/brush-selection.ts index e22a324..89937e2 100644 --- a/src/tools/brush-selection.ts +++ b/src/tools/brush-selection.ts @@ -7,7 +7,6 @@ class BrushSelection { context: CanvasRenderingContext2D; svg: SVGElement; circle: SVGCircleElement; - dragging = false; radius = 40; prev = { x: 0, y: 0 }; @@ -36,14 +35,16 @@ class BrushSelection { const context = canvas.getContext('2d'); context.globalCompositeOperation = 'copy'; - const update = (e: MouseEvent) => { + let dragId: number | undefined; + + const update = (e: PointerEvent) => { const x = e.offsetX; const y = e.offsetY; circle.setAttribute('cx', x.toString()); circle.setAttribute('cy', y.toString()); - if (this.dragging) { + if (dragId !== undefined) { context.beginPath(); context.strokeStyle = '#f60'; context.lineCap = 'round'; @@ -57,22 +58,19 @@ class BrushSelection { } }; - root.oncontextmenu = (e) => { - e.preventDefault(); - }; - - root.onmousedown = (e) => { + root.addEventListener('contextmenu', (e) => { e.preventDefault(); - e.stopPropagation(); + }); - this.prev.x = e.offsetX; - this.prev.y = e.offsetY; + root.addEventListener('pointerdown', (e) => { + if (dragId === undefined && (e.pointerType === 'mouse' ? e.button === 0 : e.isPrimary)) { + e.preventDefault(); + e.stopPropagation(); - update(e); - - if (e.button === 0) { - this.dragging = true; + dragId = e.pointerId; + root.setPointerCapture(dragId); + // initialize canvas if (canvas.width !== parent.clientWidth || canvas.height !== parent.clientHeight) { canvas.width = parent.clientWidth; canvas.height = parent.clientHeight; @@ -83,22 +81,31 @@ class BrushSelection { // display it canvas.style.display = 'inline'; + + this.prev.x = e.offsetX; + this.prev.y = e.offsetY; + + update(e); } - }; + }); - root.onmousemove = (e) => { - e.preventDefault(); - e.stopPropagation(); - update(e); - }; + root.addEventListener('pointermove', (e) => { + if (dragId !== undefined) { + e.preventDefault(); + e.stopPropagation(); + } - root.onmouseup = (e) => { - e.preventDefault(); - e.stopPropagation(); update(e); + }); + + root.addEventListener('pointerup', (e) => { + if (e.pointerId === dragId) { + e.preventDefault(); + e.stopPropagation(); + + root.releasePointerCapture(dragId); + dragId = undefined; - if (e.button === 0) { - this.dragging = false; canvas.style.display = 'none'; this.events.fire( @@ -107,7 +114,7 @@ class BrushSelection { context.getImageData(0, 0, canvas.width, canvas.height) ); } - }; + }); parent.appendChild(root); root.appendChild(svg); diff --git a/src/tools/picker-selection.ts b/src/tools/picker-selection.ts index b35d660..f0a29b8 100644 --- a/src/tools/picker-selection.ts +++ b/src/tools/picker-selection.ts @@ -8,24 +8,24 @@ class PickerSelection { this.root = document.createElement('div'); this.root.id = 'select-root'; - this.root.onmousedown = (e: MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); + this.root.addEventListener('pointerdown', (e) => { + if (e.pointerType === 'mouse' ? e.button === 0 : e.isPrimary) { + e.preventDefault(); + e.stopPropagation(); - if (e.button === 0) { events.fire( 'select.point', e.shiftKey ? 'add' : (e.ctrlKey ? 'remove' : 'set'), { x: e.offsetX / this.root.clientWidth, y: e.offsetY / this.root.clientHeight } ); } - }; + }); parent.appendChild(this.root); - this.root.oncontextmenu = (e) => { + this.root.addEventListener('contextmenu', (e) => { e.preventDefault(); - }; + }); } activate() { diff --git a/src/tools/rect-selection.ts b/src/tools/rect-selection.ts index a65085e..5b35f2c 100644 --- a/src/tools/rect-selection.ts +++ b/src/tools/rect-selection.ts @@ -5,7 +5,6 @@ class RectSelection { root: HTMLElement; svg: SVGElement; rect: SVGRectElement; - dragging = false; start = { x: 0, y: 0 }; end = { x: 0, y: 0 }; @@ -24,66 +23,72 @@ class RectSelection { const root = document.createElement('div'); root.id = 'select-root'; - const updateRect = () => { - if (this.dragging) { - const x = Math.min(this.start.x, this.end.x); - const y = Math.min(this.start.y, this.end.y); - const width = Math.abs(this.start.x - this.end.x); - const height = Math.abs(this.start.y - this.end.y); - - rect.setAttribute('x', x.toString()); - rect.setAttribute('y', y.toString()); - rect.setAttribute('width', width.toString()); - rect.setAttribute('height', height.toString()); - } + let dragId: number | undefined; - this.svg.style.display = this.dragging ? 'inline' : 'none'; + const updateRect = () => { + const x = Math.min(this.start.x, this.end.x); + const y = Math.min(this.start.y, this.end.y); + const width = Math.abs(this.start.x - this.end.x); + const height = Math.abs(this.start.y - this.end.y); + + rect.setAttribute('x', x.toString()); + rect.setAttribute('y', y.toString()); + rect.setAttribute('width', width.toString()); + rect.setAttribute('height', height.toString()); }; - root.oncontextmenu = (e) => { + root.addEventListener('contextmenu', (e) => { e.preventDefault(); - }; + }); - root.onmousedown = (e) => { - e.preventDefault(); - e.stopPropagation(); + root.addEventListener('pointerdown', (e) => { + if (dragId === undefined && (e.pointerType === 'mouse' ? e.button === 0 : e.isPrimary)) { + e.preventDefault(); + e.stopPropagation(); + + dragId = e.pointerId; + root.setPointerCapture(dragId); - if (e.button === 0) { - this.dragging = true; this.start.x = this.end.x = e.offsetX; this.start.y = this.end.y = e.offsetY; + updateRect(); + + this.svg.style.display = 'inline'; } - }; + }); - root.onmousemove = (e) => { - e.preventDefault(); - e.stopPropagation(); + root.addEventListener('pointermove', (e) => { + if (e.pointerId === dragId) { + e.preventDefault(); + e.stopPropagation(); - if (e.button === 0 && this.dragging) { this.end.x = e.offsetX; this.end.y = e.offsetY; + updateRect(); } - }; + }); - root.onmouseup = (e) => { - e.preventDefault(); - e.stopPropagation(); + root.addEventListener('pointerup', (e) => { + if (e.pointerId === dragId) { + e.preventDefault(); + e.stopPropagation(); - if (e.button === 0) { const w = root.clientWidth; const h = root.clientHeight; - this.dragging = false; - updateRect(); + root.releasePointerCapture(dragId); + dragId = undefined; + + this.svg.style.display = 'none'; this.events.fire('select.rect', e.shiftKey ? 'add' : (e.ctrlKey ? 'remove' : 'set'), { start: { x: Math.min(this.start.x, this.end.x) / w, y: Math.min(this.start.y, this.end.y) / h }, end: { x: Math.max(this.start.x, this.end.x) / w, y: Math.max(this.start.y, this.end.y) / h }, }); } - }; + }); parent.appendChild(root); root.appendChild(svg);