From 0a16bc3ae9d8f0da6e2e695c3cda5d45c061cb50 Mon Sep 17 00:00:00 2001 From: David Sheldrick Date: Fri, 19 Apr 2024 14:49:15 +0100 Subject: [PATCH] Webgl minimap squares (#3533) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reopening #3528 @steveruizok plz start writing actual commit messages again 🙈 this was a bit harder to untangle because they were all 'ok' --------- Co-authored-by: Steve Ruiz --- packages/editor/src/lib/editor/Editor.ts | 6 +- packages/tldraw/package.json | 3 - .../ui/components/Minimap/MinimapManager.ts | 31 +++++----- .../Minimap/minimap-webgl-shapes.ts | 22 +++---- .../components/Minimap/triangulateGeometry.ts | 59 ------------------- yarn.lock | 51 ---------------- 6 files changed, 33 insertions(+), 139 deletions(-) delete mode 100644 packages/tldraw/src/lib/ui/components/Minimap/triangulateGeometry.ts diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts index aa66b1856..867e7a7be 100644 --- a/packages/editor/src/lib/editor/Editor.ts +++ b/packages/editor/src/lib/editor/Editor.ts @@ -8219,7 +8219,11 @@ export class Editor extends EventEmitter { // it will be 0,0 when its actual screen position is equal // to screenBounds.point. This is confusing! currentScreenPoint.set(sx, sy) - currentPagePoint.set(sx / cz - cx, sy / cz - cy, sz) + const nx = sx / cz - cx + const ny = sy / cz - cy + if (isFinite(nx) && isFinite(ny)) { + currentPagePoint.set(nx, ny, sz) + } this.inputs.isPen = info.type === 'pointer' && info.isPen diff --git a/packages/tldraw/package.json b/packages/tldraw/package.json index ff9d25937..1fa49505f 100644 --- a/packages/tldraw/package.json +++ b/packages/tldraw/package.json @@ -57,8 +57,6 @@ "@tldraw/store": "workspace:*", "canvas-size": "^1.2.6", "classnames": "^2.3.2", - "earcut": "^2.2.4", - "extrude-polyline": "^1.0.6", "hotkeys-js": "^3.11.2", "lz-string": "^1.4.4" }, @@ -72,7 +70,6 @@ "@testing-library/react": "^14.0.0", "@types/canvas-size": "^1.2.0", "@types/classnames": "^2.3.1", - "@types/earcut": "^2.1.4", "@types/lz-string": "^1.3.34", "chokidar-cli": "^3.0.0", "jest-canvas-mock": "^2.5.2", diff --git a/packages/tldraw/src/lib/ui/components/Minimap/MinimapManager.ts b/packages/tldraw/src/lib/ui/components/Minimap/MinimapManager.ts index ef4682b9a..3e7757b15 100644 --- a/packages/tldraw/src/lib/ui/components/Minimap/MinimapManager.ts +++ b/packages/tldraw/src/lib/ui/components/Minimap/MinimapManager.ts @@ -12,25 +12,25 @@ import { } from '@tldraw/editor' import { getRgba } from './getRgba' import { BufferStuff, appendVertices, setupWebGl } from './minimap-webgl-setup' -import { pie, roundedRectangle } from './minimap-webgl-shapes' -import { applyTransformToGeometry, triangulateGeometry } from './triangulateGeometry' +import { pie, rectangle, roundedRectangle } from './minimap-webgl-shapes' export class MinimapManager { disposables = [] as (() => void)[] close = () => this.disposables.forEach((d) => d()) gl: ReturnType - geometryCache: ComputedCache + shapeGeometryCache: ComputedCache constructor( public editor: Editor, public readonly elem: HTMLCanvasElement ) { this.gl = setupWebGl(elem) - this.geometryCache = editor.store.createComputedCache('webgl-geometry', (r: TLShape) => { - const pageTransform = editor.getShapePageTransform(r.id) - const triangles = triangulateGeometry(editor.getShapeGeometry(r.id)) - return applyTransformToGeometry(triangles, pageTransform) + this.shapeGeometryCache = editor.store.createComputedCache('webgl-geometry', (r: TLShape) => { + const bounds = editor.getShapeMaskedPageBounds(r.id) + if (!bounds) return null + const arr = new Float32Array(12) + rectangle(arr, 0, bounds.x, bounds.y, bounds.w, bounds.h) + return arr }) - this.colors = this._getColors() this.disposables.push(this._listenForCanvasResize(), react('minimap render', this.render)) } @@ -168,13 +168,13 @@ export class MinimapManager { let { x: px, y: py } = this.getPagePoint(x, y) if (clampToBounds) { - const shapesPageBounds = this.editor.getCurrentPageBounds() + const shapesPageBounds = this.editor.getCurrentPageBounds() ?? new Box() const vpPageBounds = viewportPageBounds - const minX = (shapesPageBounds?.minX ?? 0) - vpPageBounds.width / 2 - const maxX = (shapesPageBounds?.maxX ?? 0) + vpPageBounds.width / 2 - const minY = (shapesPageBounds?.minY ?? 0) - vpPageBounds.height / 2 - const maxY = (shapesPageBounds?.maxY ?? 0) + vpPageBounds.height / 2 + const minX = shapesPageBounds.minX - vpPageBounds.width / 2 + const maxX = shapesPageBounds.maxX + vpPageBounds.width / 2 + const minY = shapesPageBounds.minY - vpPageBounds.height / 2 + const maxY = shapesPageBounds.maxY + vpPageBounds.height / 2 const lx = Math.max(0, minX + vpPageBounds.width - px) const rx = Math.max(0, -(maxX - vpPageBounds.width - px)) @@ -247,8 +247,9 @@ export class MinimapManager { const ids = this.editor.getCurrentPageShapeIdsSorted() - for (const shapeId of ids) { - const geometry = this.geometryCache.get(shapeId) + for (let i = 0, len = ids.length; i < len; i++) { + const shapeId = ids[i] + const geometry = this.shapeGeometryCache.get(shapeId) if (!geometry) continue const len = geometry.length diff --git a/packages/tldraw/src/lib/ui/components/Minimap/minimap-webgl-shapes.ts b/packages/tldraw/src/lib/ui/components/Minimap/minimap-webgl-shapes.ts index 8509c2700..283e89344 100644 --- a/packages/tldraw/src/lib/ui/components/Minimap/minimap-webgl-shapes.ts +++ b/packages/tldraw/src/lib/ui/components/Minimap/minimap-webgl-shapes.ts @@ -1,6 +1,7 @@ -import { Box, Vec } from '@tldraw/editor' +import { Box, HALF_PI, PI, PI2, Vec } from '@tldraw/editor' export const numArcSegmentsPerCorner = 10 + export const roundedRectangleDataSize = // num triangles in corners 4 * 6 * numArcSegmentsPerCorner + @@ -16,7 +17,7 @@ export function pie( radius, numArcSegments = 20, startAngle = 0, - endAngle = Math.PI * 2, + endAngle = PI2, offset = 0, }: { center: Vec @@ -40,7 +41,8 @@ export function pie( return array } -function rectangle( +/** @internal **/ +export function rectangle( array: Float32Array, offset: number, x: number, @@ -98,8 +100,8 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number): offset, center: innerBox.point, radius, - startAngle: Math.PI, - endAngle: Math.PI * 1.5, + startAngle: PI, + endAngle: PI * 1.5, }) offset += numArcSegments * 6 @@ -110,8 +112,8 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number): offset, center: Vec.Add(innerBox.point, new Vec(innerBox.w, 0)), radius, - startAngle: Math.PI * 1.5, - endAngle: Math.PI * 2, + startAngle: PI * 1.5, + endAngle: PI2, }) offset += numArcSegments * 6 @@ -123,7 +125,7 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number): center: Vec.Add(innerBox.point, innerBox.size), radius, startAngle: 0, - endAngle: Math.PI / 2, + endAngle: HALF_PI, }) offset += numArcSegments * 6 @@ -134,8 +136,8 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number): offset, center: Vec.Add(innerBox.point, new Vec(0, innerBox.h)), radius, - startAngle: Math.PI / 2, - endAngle: Math.PI, + startAngle: HALF_PI, + endAngle: PI, }) return roundedRectangleDataSize diff --git a/packages/tldraw/src/lib/ui/components/Minimap/triangulateGeometry.ts b/packages/tldraw/src/lib/ui/components/Minimap/triangulateGeometry.ts deleted file mode 100644 index f8f06f5bd..000000000 --- a/packages/tldraw/src/lib/ui/components/Minimap/triangulateGeometry.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Geometry2d, Mat } from '@tldraw/editor' -import earcut from 'earcut' -// @ts-expect-error -import extrudePolyline from 'extrude-polyline' - -const stroke = extrudePolyline({ - thickness: 10, // this is in page space - cap: 'square', - join: 'bevel', - miterLimit: 1, -}) - -export function triangulateGeometry(geometry: Geometry2d) { - const vertices = geometry.vertices - if (!geometry.isClosed || !geometry.isFilled) { - const points = vertices.map((v) => [v.x, v.y]) - if (geometry.isClosed) points.push([vertices[0].x, vertices[0].y]) - const triangles = stroke.build(points) - const arr = new Float32Array(triangles.cells.length * 3 * 2) - let j = 0 - for (const cell of triangles.cells) { - arr[j++] = triangles.positions[cell[0]][0] - arr[j++] = triangles.positions[cell[0]][1] - - arr[j++] = triangles.positions[cell[1]][0] - arr[j++] = triangles.positions[cell[1]][1] - - arr[j++] = triangles.positions[cell[2]][0] - arr[j++] = triangles.positions[cell[2]][1] - } - - return new Float32Array(arr) - } else { - const points = vertices.map((v) => [v.x, v.y]).flat() - if (geometry.isClosed) points.push(vertices[0].x, vertices[0].y) - const triangles = earcut(points) - const arr = new Float32Array(triangles.length * 2) - let j = 0 - for (const i of triangles) { - arr[j * 2] = points[i * 2] - arr[j * 2 + 1] = points[i * 2 + 1] - j++ - } - - return arr - } -} - -export function applyTransformToGeometry(geometry: Float32Array, transform: Mat) { - const transformedGeometry = new Float32Array(geometry.length) - for (let i = 0; i < transformedGeometry.length; i += 2) { - ;[transformedGeometry[i], transformedGeometry[i + 1]] = Mat.applyToXY( - transform, - geometry[i], - geometry[i + 1] - ) - } - return transformedGeometry -} diff --git a/yarn.lock b/yarn.lock index a9f9f1439..0b94e4b30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7953,13 +7953,6 @@ __metadata: languageName: node linkType: hard -"@types/earcut@npm:^2.1.4": - version: 2.1.4 - resolution: "@types/earcut@npm:2.1.4" - checksum: 167e1c6d60e9d964d0f496c11b5065b9d155b4c1df04fb9abcec88af2c16213999ea8ed7cddeec9475620152e7e9e99a0f118e1c8070e1396cf0f299642beb6b - languageName: node - linkType: hard - "@types/eslint-scope@npm:^3.7.3": version: 3.7.7 resolution: "@types/eslint-scope@npm:3.7.7" @@ -9728,13 +9721,6 @@ __metadata: languageName: node linkType: hard -"as-number@npm:^1.0.0": - version: 1.0.0 - resolution: "as-number@npm:1.0.0" - checksum: 672e0593d170e00e0f2c4c62438be8dac9d01fb60b73dc7d1df82446704b77b6d02bf61c87e061ec0050408cb4157aa6b46021578fe0ff67d7a8085e9d673305 - languageName: node - linkType: hard - "as-table@npm:^1.0.36": version: 1.0.55 resolution: "as-table@npm:1.0.55" @@ -11985,13 +11971,6 @@ __metadata: languageName: node linkType: hard -"earcut@npm:^2.2.4": - version: 2.2.4 - resolution: "earcut@npm:2.2.4" - checksum: ca8b24714cc2fa67f98fbca6ddcf64bb42ee8d75d0b4f1a81486b3282b0f7f1bf9ec49ad4d02149985886a0c8a03a173463f2acb1f51fa0bb7ba2e1d4aa1254d - languageName: node - linkType: hard - "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -13813,17 +13792,6 @@ __metadata: languageName: node linkType: hard -"extrude-polyline@npm:^1.0.6": - version: 1.0.6 - resolution: "extrude-polyline@npm:1.0.6" - dependencies: - as-number: "npm:^1.0.0" - gl-vec2: "npm:^1.0.0" - polyline-miter-util: "npm:^1.0.1" - checksum: b44652bc25fe37979813b03e58f36c6ceb7661916d2e6f861f3652909c934d49e919e73ce8fabed7798432c3c61603f796f2ff9c030b49ae43cd072f104bbc45 - languageName: node - linkType: hard - "extsprintf@npm:1.3.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" @@ -14624,13 +14592,6 @@ __metadata: languageName: node linkType: hard -"gl-vec2@npm:^1.0.0": - version: 1.3.0 - resolution: "gl-vec2@npm:1.3.0" - checksum: 6d5210d6c4288e1ea470c429d27967bec48a4305be2fa6a09db9f76d995b38eb78b71a27507d2703f97dad2376b317d7d2d970fdcc16feee6d463e592fe783fd - languageName: node - linkType: hard - "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -20609,15 +20570,6 @@ __metadata: languageName: node linkType: hard -"polyline-miter-util@npm:^1.0.1": - version: 1.0.1 - resolution: "polyline-miter-util@npm:1.0.1" - dependencies: - gl-vec2: "npm:^1.0.0" - checksum: 50fef7e78575f31fd281fe8f9a7683de1e36672e5ba84bddc338c3cfcf6fd0270742ae68e33350fd3a47eb3befa457ef31eefc408778ad178532a626d45e8aaf - languageName: node - linkType: hard - "postcss-discard-duplicates@npm:^5.1.0": version: 5.1.0 resolution: "postcss-discard-duplicates@npm:5.1.0" @@ -23588,13 +23540,10 @@ __metadata: "@tldraw/store": "workspace:*" "@types/canvas-size": "npm:^1.2.0" "@types/classnames": "npm:^2.3.1" - "@types/earcut": "npm:^2.1.4" "@types/lz-string": "npm:^1.3.34" canvas-size: "npm:^1.2.6" chokidar-cli: "npm:^3.0.0" classnames: "npm:^2.3.2" - earcut: "npm:^2.2.4" - extrude-polyline: "npm:^1.0.6" hotkeys-js: "npm:^3.11.2" jest-canvas-mock: "npm:^2.5.2" jest-environment-jsdom: "npm:^29.4.3"