openpencil/tests/helpers/large-document.ts
Danila Poyarkov d7613f34ec
perf(scene-graph): accelerate axis-aligned hit tests (#534)
* perf(scene-graph): accelerate axis-aligned hit tests

- Reuse cached world positions for untransformed node chains
- Preserve exact matrix hit testing beneath rotated and flipped ancestors
- Profile pointer, hit-test, cached, overlay, and volatile rendering at 500 and 2,000 nodes

* fix(vue): restore layer drop indicators

- Forward reactive drag instructions through virtualized layer item slots
- Render above, below, and child feedback before the drop completes
- Add browser regressions for reorder lines and container highlights

* fix(scene-graph): harden hit-test performance coverage

- Cache transformed ancestry within each hit-test traversal
- Invalidate absolute positions after preview flips and reparenting
- Attach scale-relative browser profiles and correct auto-layout fixture geometry

* fix(scene-graph): reject cyclic layer reorder

- Prevent reorderChild from parenting a node beneath its descendant
- Cover graph preservation after a rejected cyclic reorder
2026-08-16 15:43:25 +03:00

98 lines
2.9 KiB
TypeScript

import type { Page } from '@playwright/test'
export type LargeDocumentProfile = {
nodeCount: number
leafIds: string[]
worldWidth: number
worldHeight: number
}
/** Build a deterministic mixed document without timing browser-side setup. */
export async function seedLargeDocument(
page: Page,
nodeCount: number
): Promise<LargeDocumentProfile> {
return page.evaluate((count) => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
const graph = store.graph
const pageId = store.state.currentPageId
const leafIds: string[] = []
const cards = Math.ceil(count / 10)
const columns = Math.ceil(Math.sqrt(cards))
const cardWidth = 240
const cardHeight = 236
const gap = 32
for (let cardIndex = 0; cardIndex < cards && leafIds.length < count; cardIndex++) {
const column = cardIndex % columns
const row = Math.floor(cardIndex / columns)
const frame = graph.createNode('FRAME', pageId, {
name: `Card ${cardIndex}`,
x: column * (cardWidth + gap),
y: row * (cardHeight + gap),
width: cardWidth,
height: cardHeight,
layoutMode: 'VERTICAL',
itemSpacing: 8,
paddingTop: 12,
paddingRight: 12,
paddingBottom: 12,
paddingLeft: 12,
fills: [
{
type: 'SOLID',
color: { r: 0.96, g: 0.97, b: 0.99, a: 1 },
opacity: 1,
visible: true
}
],
effects:
cardIndex % 4 === 0
? [
{
type: 'DROP_SHADOW',
color: { r: 0, g: 0, b: 0, a: 0.12 },
offset: { x: 0, y: 3 },
radius: 8,
spread: 0,
visible: true
}
]
: []
})
for (let childIndex = 0; childIndex < 10 && leafIds.length < count; childIndex++) {
const isText = childIndex % 3 === 0
const node = graph.createNode(isText ? 'TEXT' : 'RECTANGLE', frame.id, {
name: `${isText ? 'Label' : 'Row'} ${cardIndex}-${childIndex}`,
text: isText ? `Item ${cardIndex}-${childIndex}` : undefined,
width: 200,
height: 14,
cornerRadius: isText ? 0 : 4,
fills: [
{
type: 'SOLID',
color: isText
? { r: 0.12, g: 0.14, b: 0.18, a: 1 }
: { r: 0.25, g: 0.48, b: 0.92, a: 1 },
opacity: 1,
visible: true
}
]
})
leafIds.push(node.id)
}
}
store.requestRender()
const rows = Math.ceil(cards / columns)
return {
nodeCount: leafIds.length,
leafIds,
worldWidth: columns * (cardWidth + gap),
worldHeight: rows * (cardHeight + gap)
}
}, nodeCount)
}