pull/3302/head
Mitja Bezenšek 2024-03-28 20:08:37 +01:00
rodzic a59ff81a7d
commit 4fdc67b8a3
4 zmienionych plików z 63 dodań i 23 usunięć

Wyświetl plik

@ -45,6 +45,7 @@
"lint": "yarn run -T tsx ../../scripts/lint.ts"
},
"dependencies": {
"@timohausmann/quadtree-ts": "^2.2.2",
"@tldraw/state": "workspace:*",
"@tldraw/store": "workspace:*",
"@tldraw/tlschema": "workspace:*",

Wyświetl plik

@ -1,7 +1,7 @@
import { EASINGS } from './primitives/easings'
/** @internal */
export const MAX_SHAPES_PER_PAGE = 2000
export const MAX_SHAPES_PER_PAGE = 10000
/** @internal */
export const MAX_PAGES = 40

Wyświetl plik

@ -1,4 +1,5 @@
import { EMPTY_ARRAY, atom, computed, transact } from '@tldraw/state'
import { Quadtree, Rectangle } from '@timohausmann/quadtree-ts'
import { EMPTY_ARRAY, atom, computed, transact, whyAmIRunning } from '@tldraw/state'
import { ComputedCache, RecordType, StoreSnapshot } from '@tldraw/store'
import {
CameraRecordType,
@ -3087,6 +3088,38 @@ export class Editor extends EventEmitter<TLEventMap> {
}
}
quadTree = new Quadtree({ width: 40000, height: 40000, x: -20000, y: -20000 })
shapesInTheTree = new Set<TLShapeId>()
quadRectsById = new Map<TLShapeId, Rectangle<TLShapeId>>()
@computed quadTree2() {
const shapes = this.getCurrentPageShapes()
whyAmIRunning()
for (let i = 0; i < shapes.length; i++) {
const shape = shapes[i]
if (!this.shapesInTheTree.has(shape.id)) {
const maskedPageBounds = this.getShapeMaskedPageBounds(shape.id)
if (!maskedPageBounds) continue
let rect = this.quadRectsById.get(shape.id)
if (!rect) {
rect = new Rectangle({
x: maskedPageBounds?.x,
y: maskedPageBounds?.y,
width: maskedPageBounds?.width,
height: maskedPageBounds?.height,
data: shape.id,
})
}
this.quadRectsById.set(shape.id, rect)
this.quadTree.insert(rect)
this.shapesInTheTree.add(shape.id)
}
}
return 'bla'
}
/** @internal */
getUnorderedRenderingShapes(
// The rendering state. We use this method both for rendering, which
@ -3112,20 +3145,24 @@ export class Editor extends EventEmitter<TLEventMap> {
backgroundIndex: number
opacity: number
isCulled: boolean
maskedPageBounds: Box | undefined
}[] = []
let nextIndex = MAX_SHAPES_PER_PAGE * 2
let nextBackgroundIndex = MAX_SHAPES_PER_PAGE
// We only really need these if we're using editor state, but that's ok
const editingShapeId = this.getEditingShapeId()
const selectedShapeIds = this.getSelectedShapeIds()
const erasingShapeIds = this.getErasingShapeIds()
const renderingBoundsExpanded = this.getRenderingBoundsExpanded()
// If renderingBoundsMargin is set to Infinity, then we won't cull offscreen shapes
const isCullingOffScreenShapes = Number.isFinite(this.renderingBoundsMargin)
const shapesInView = this.quadTree.retrieve(
new Rectangle({
x: renderingBoundsExpanded.x,
y: renderingBoundsExpanded.y,
width: renderingBoundsExpanded.width,
height: renderingBoundsExpanded.height,
})
)
const idsOfShapesInView = new Set(shapesInView.map((s) => (s as any).data))
const addShapeById = (id: TLShapeId, opacity: number, isAncestorErasing: boolean) => {
const shape = this.getShape(id)
@ -3135,27 +3172,14 @@ export class Editor extends EventEmitter<TLEventMap> {
let isCulled = false
let isShapeErasing = false
const util = this.getShapeUtil(shape)
const maskedPageBounds = this.getShapeMaskedPageBounds(id)
if (useEditorState) {
isShapeErasing = !isAncestorErasing && erasingShapeIds.includes(id)
if (isShapeErasing) {
opacity *= 0.32
}
isCulled =
isCullingOffScreenShapes &&
// only cull shapes that allow unmounting, i.e. not stateful components
util.canUnmount(shape) &&
// never cull editingg shapes
editingShapeId !== id &&
// if the shape is fully outside of its parent's clipping bounds...
(maskedPageBounds === undefined ||
// ...or if the shape is outside of the expanded viewport bounds...
(!renderingBoundsExpanded.includes(maskedPageBounds) &&
// ...and if it's not selected... then cull it
!selectedShapeIds.includes(id)))
}
isCulled = !idsOfShapesInView.has(id)
renderingShapes.push({
id,
@ -3165,7 +3189,6 @@ export class Editor extends EventEmitter<TLEventMap> {
backgroundIndex: nextBackgroundIndex,
opacity,
isCulled,
maskedPageBounds,
})
nextIndex += 1
@ -3193,6 +3216,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// If we're using editor state, then we're only interested in on-screen shapes.
// If we're not using the editor state, then we're interested in ALL shapes, even those from other pages.
const pages = useEditorState ? [this.getCurrentPage()] : this.getPages()
// const ids = shapesInView.map((s) => (s as any).data)
for (const page of pages) {
for (const childId of this.getSortedChildIdsForParent(page.id)) {
addShapeById(childId, 1, false)
@ -3208,7 +3232,11 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public
*/
@computed getRenderingShapes() {
let now = Date.now()
const bl = this.quadTree2()
const renderingShapes = this.getUnorderedRenderingShapes(true)
console.log('get rendering shapes in ', Date.now() - now)
now = Date.now()
// Its IMPORTANT that the result be sorted by id AND include the index
// that the shape should be displayed at. Steve, this is the past you
@ -3220,7 +3248,10 @@ export class Editor extends EventEmitter<TLEventMap> {
// drain. By always sorting by 'id' we keep the shapes always in the
// same order; but we later use index to set the element's 'z-index'
// to change the "rendered" position in z-space.
return renderingShapes.sort(sortById)
const result = renderingShapes.sort(sortById)
console.log('sorted shapes in ', Date.now() - now)
return result
}
/**

Wyświetl plik

@ -7172,6 +7172,13 @@ __metadata:
languageName: node
linkType: hard
"@timohausmann/quadtree-ts@npm:^2.2.2":
version: 2.2.2
resolution: "@timohausmann/quadtree-ts@npm:2.2.2"
checksum: d5d02bd5ad5042c976a6443c9415ec4fe401ca3c8853d0886357f65f3a01af2a07a6ea05bb24b57753b1a4c3ea03d90f81b11a90bcc1074ef25eef76fc338e92
languageName: node
linkType: hard
"@tldraw/assets@workspace:*, @tldraw/assets@workspace:packages/assets":
version: 0.0.0-use.local
resolution: "@tldraw/assets@workspace:packages/assets"
@ -7281,6 +7288,7 @@ __metadata:
"@peculiar/webcrypto": "npm:^1.4.0"
"@testing-library/jest-dom": "npm:^5.16.5"
"@testing-library/react": "npm:^14.0.0"
"@timohausmann/quadtree-ts": "npm:^2.2.2"
"@tldraw/state": "workspace:*"
"@tldraw/store": "workspace:*"
"@tldraw/tlschema": "workspace:*"