Webgl minimap squares (#3533)

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 <steveruizok@gmail.com>
pull/3510/head
David Sheldrick 2024-04-19 14:49:15 +01:00 zatwierdzone przez GitHub
rodzic 6b3c0514d8
commit 0a16bc3ae9
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
6 zmienionych plików z 33 dodań i 139 usunięć

Wyświetl plik

@ -8219,7 +8219,11 @@ export class Editor extends EventEmitter<TLEventMap> {
// it will be 0,0 when its actual screen position is equal // it will be 0,0 when its actual screen position is equal
// to screenBounds.point. This is confusing! // to screenBounds.point. This is confusing!
currentScreenPoint.set(sx, sy) 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 this.inputs.isPen = info.type === 'pointer' && info.isPen

Wyświetl plik

@ -57,8 +57,6 @@
"@tldraw/store": "workspace:*", "@tldraw/store": "workspace:*",
"canvas-size": "^1.2.6", "canvas-size": "^1.2.6",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"earcut": "^2.2.4",
"extrude-polyline": "^1.0.6",
"hotkeys-js": "^3.11.2", "hotkeys-js": "^3.11.2",
"lz-string": "^1.4.4" "lz-string": "^1.4.4"
}, },
@ -72,7 +70,6 @@
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",
"@types/canvas-size": "^1.2.0", "@types/canvas-size": "^1.2.0",
"@types/classnames": "^2.3.1", "@types/classnames": "^2.3.1",
"@types/earcut": "^2.1.4",
"@types/lz-string": "^1.3.34", "@types/lz-string": "^1.3.34",
"chokidar-cli": "^3.0.0", "chokidar-cli": "^3.0.0",
"jest-canvas-mock": "^2.5.2", "jest-canvas-mock": "^2.5.2",

Wyświetl plik

@ -12,25 +12,25 @@ import {
} from '@tldraw/editor' } from '@tldraw/editor'
import { getRgba } from './getRgba' import { getRgba } from './getRgba'
import { BufferStuff, appendVertices, setupWebGl } from './minimap-webgl-setup' import { BufferStuff, appendVertices, setupWebGl } from './minimap-webgl-setup'
import { pie, roundedRectangle } from './minimap-webgl-shapes' import { pie, rectangle, roundedRectangle } from './minimap-webgl-shapes'
import { applyTransformToGeometry, triangulateGeometry } from './triangulateGeometry'
export class MinimapManager { export class MinimapManager {
disposables = [] as (() => void)[] disposables = [] as (() => void)[]
close = () => this.disposables.forEach((d) => d()) close = () => this.disposables.forEach((d) => d())
gl: ReturnType<typeof setupWebGl> gl: ReturnType<typeof setupWebGl>
geometryCache: ComputedCache<Float32Array, TLShape> shapeGeometryCache: ComputedCache<Float32Array | null, TLShape>
constructor( constructor(
public editor: Editor, public editor: Editor,
public readonly elem: HTMLCanvasElement public readonly elem: HTMLCanvasElement
) { ) {
this.gl = setupWebGl(elem) this.gl = setupWebGl(elem)
this.geometryCache = editor.store.createComputedCache('webgl-geometry', (r: TLShape) => { this.shapeGeometryCache = editor.store.createComputedCache('webgl-geometry', (r: TLShape) => {
const pageTransform = editor.getShapePageTransform(r.id) const bounds = editor.getShapeMaskedPageBounds(r.id)
const triangles = triangulateGeometry(editor.getShapeGeometry(r.id)) if (!bounds) return null
return applyTransformToGeometry(triangles, pageTransform) const arr = new Float32Array(12)
rectangle(arr, 0, bounds.x, bounds.y, bounds.w, bounds.h)
return arr
}) })
this.colors = this._getColors() this.colors = this._getColors()
this.disposables.push(this._listenForCanvasResize(), react('minimap render', this.render)) 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) let { x: px, y: py } = this.getPagePoint(x, y)
if (clampToBounds) { if (clampToBounds) {
const shapesPageBounds = this.editor.getCurrentPageBounds() const shapesPageBounds = this.editor.getCurrentPageBounds() ?? new Box()
const vpPageBounds = viewportPageBounds const vpPageBounds = viewportPageBounds
const minX = (shapesPageBounds?.minX ?? 0) - vpPageBounds.width / 2 const minX = shapesPageBounds.minX - vpPageBounds.width / 2
const maxX = (shapesPageBounds?.maxX ?? 0) + vpPageBounds.width / 2 const maxX = shapesPageBounds.maxX + vpPageBounds.width / 2
const minY = (shapesPageBounds?.minY ?? 0) - vpPageBounds.height / 2 const minY = shapesPageBounds.minY - vpPageBounds.height / 2
const maxY = (shapesPageBounds?.maxY ?? 0) + vpPageBounds.height / 2 const maxY = shapesPageBounds.maxY + vpPageBounds.height / 2
const lx = Math.max(0, minX + vpPageBounds.width - px) const lx = Math.max(0, minX + vpPageBounds.width - px)
const rx = Math.max(0, -(maxX - vpPageBounds.width - px)) const rx = Math.max(0, -(maxX - vpPageBounds.width - px))
@ -247,8 +247,9 @@ export class MinimapManager {
const ids = this.editor.getCurrentPageShapeIdsSorted() const ids = this.editor.getCurrentPageShapeIdsSorted()
for (const shapeId of ids) { for (let i = 0, len = ids.length; i < len; i++) {
const geometry = this.geometryCache.get(shapeId) const shapeId = ids[i]
const geometry = this.shapeGeometryCache.get(shapeId)
if (!geometry) continue if (!geometry) continue
const len = geometry.length const len = geometry.length

Wyświetl plik

@ -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 numArcSegmentsPerCorner = 10
export const roundedRectangleDataSize = export const roundedRectangleDataSize =
// num triangles in corners // num triangles in corners
4 * 6 * numArcSegmentsPerCorner + 4 * 6 * numArcSegmentsPerCorner +
@ -16,7 +17,7 @@ export function pie(
radius, radius,
numArcSegments = 20, numArcSegments = 20,
startAngle = 0, startAngle = 0,
endAngle = Math.PI * 2, endAngle = PI2,
offset = 0, offset = 0,
}: { }: {
center: Vec center: Vec
@ -40,7 +41,8 @@ export function pie(
return array return array
} }
function rectangle( /** @internal **/
export function rectangle(
array: Float32Array, array: Float32Array,
offset: number, offset: number,
x: number, x: number,
@ -98,8 +100,8 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number):
offset, offset,
center: innerBox.point, center: innerBox.point,
radius, radius,
startAngle: Math.PI, startAngle: PI,
endAngle: Math.PI * 1.5, endAngle: PI * 1.5,
}) })
offset += numArcSegments * 6 offset += numArcSegments * 6
@ -110,8 +112,8 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number):
offset, offset,
center: Vec.Add(innerBox.point, new Vec(innerBox.w, 0)), center: Vec.Add(innerBox.point, new Vec(innerBox.w, 0)),
radius, radius,
startAngle: Math.PI * 1.5, startAngle: PI * 1.5,
endAngle: Math.PI * 2, endAngle: PI2,
}) })
offset += numArcSegments * 6 offset += numArcSegments * 6
@ -123,7 +125,7 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number):
center: Vec.Add(innerBox.point, innerBox.size), center: Vec.Add(innerBox.point, innerBox.size),
radius, radius,
startAngle: 0, startAngle: 0,
endAngle: Math.PI / 2, endAngle: HALF_PI,
}) })
offset += numArcSegments * 6 offset += numArcSegments * 6
@ -134,8 +136,8 @@ export function roundedRectangle(data: Float32Array, box: Box, radius: number):
offset, offset,
center: Vec.Add(innerBox.point, new Vec(0, innerBox.h)), center: Vec.Add(innerBox.point, new Vec(0, innerBox.h)),
radius, radius,
startAngle: Math.PI / 2, startAngle: HALF_PI,
endAngle: Math.PI, endAngle: PI,
}) })
return roundedRectangleDataSize return roundedRectangleDataSize

Wyświetl plik

@ -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
}

Wyświetl plik

@ -7953,13 +7953,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@types/eslint-scope@npm:^3.7.3":
version: 3.7.7 version: 3.7.7
resolution: "@types/eslint-scope@npm:3.7.7" resolution: "@types/eslint-scope@npm:3.7.7"
@ -9728,13 +9721,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "as-table@npm:^1.0.36":
version: 1.0.55 version: 1.0.55
resolution: "as-table@npm:1.0.55" resolution: "as-table@npm:1.0.55"
@ -11985,13 +11971,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "eastasianwidth@npm:^0.2.0":
version: 0.2.0 version: 0.2.0
resolution: "eastasianwidth@npm:0.2.0" resolution: "eastasianwidth@npm:0.2.0"
@ -13813,17 +13792,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "extsprintf@npm:1.3.0":
version: 1.3.0 version: 1.3.0
resolution: "extsprintf@npm:1.3.0" resolution: "extsprintf@npm:1.3.0"
@ -14624,13 +14592,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
version: 5.1.2 version: 5.1.2
resolution: "glob-parent@npm:5.1.2" resolution: "glob-parent@npm:5.1.2"
@ -20609,15 +20570,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "postcss-discard-duplicates@npm:^5.1.0":
version: 5.1.0 version: 5.1.0
resolution: "postcss-discard-duplicates@npm:5.1.0" resolution: "postcss-discard-duplicates@npm:5.1.0"
@ -23588,13 +23540,10 @@ __metadata:
"@tldraw/store": "workspace:*" "@tldraw/store": "workspace:*"
"@types/canvas-size": "npm:^1.2.0" "@types/canvas-size": "npm:^1.2.0"
"@types/classnames": "npm:^2.3.1" "@types/classnames": "npm:^2.3.1"
"@types/earcut": "npm:^2.1.4"
"@types/lz-string": "npm:^1.3.34" "@types/lz-string": "npm:^1.3.34"
canvas-size: "npm:^1.2.6" canvas-size: "npm:^1.2.6"
chokidar-cli: "npm:^3.0.0" chokidar-cli: "npm:^3.0.0"
classnames: "npm:^2.3.2" classnames: "npm:^2.3.2"
earcut: "npm:^2.2.4"
extrude-polyline: "npm:^1.0.6"
hotkeys-js: "npm:^3.11.2" hotkeys-js: "npm:^3.11.2"
jest-canvas-mock: "npm:^2.5.2" jest-canvas-mock: "npm:^2.5.2"
jest-environment-jsdom: "npm:^29.4.3" jest-environment-jsdom: "npm:^29.4.3"