Scene Graph unit tests move to packages/scene-graph/tests with a package-local assert helper and test type-checking; five Core- and fig-owned tests move to their owners' engine homes. check:test-homes rejects any new test under tests/engine against a reviewed baseline so the migration debt only shrinks.
110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
import { describe, expect, test } from 'bun:test'
|
|
|
|
import { getWorldMatrix, SceneGraph, TransformMatrix } from '@open-pencil/scene-graph'
|
|
|
|
function setup() {
|
|
const graph = new SceneGraph()
|
|
const pageId = graph.getPages()[0].id
|
|
return { graph, pageId }
|
|
}
|
|
|
|
function worldCorners(graph: SceneGraph, id: string) {
|
|
const node = graph.getNode(id)
|
|
if (!node) throw new Error(`missing node ${id}`)
|
|
const w = node.width
|
|
const h = node.height
|
|
return TransformMatrix.mapPoints(getWorldMatrix(node, graph), [0, 0, w, 0, w, h, 0, h])
|
|
}
|
|
|
|
function expectSameCorners(actual: number[], expected: number[]) {
|
|
expect(actual).toHaveLength(expected.length)
|
|
actual.forEach((value, index) => expect(value).toBeCloseTo(expected[index], 6))
|
|
}
|
|
|
|
describe('reparentNode keeps the node where it is drawn', () => {
|
|
test('moving into a rotated frame keeps the corners and counter-rotates the node', () => {
|
|
const { graph, pageId } = setup()
|
|
const frame = graph.createNode('FRAME', pageId, {
|
|
x: 100,
|
|
y: 50,
|
|
width: 240,
|
|
height: 240,
|
|
rotation: 30
|
|
})
|
|
const rect = graph.createNode('RECTANGLE', pageId, { x: 500, y: 400, width: 40, height: 20 })
|
|
const before = worldCorners(graph, rect.id)
|
|
|
|
graph.reparentNode(rect.id, frame.id)
|
|
|
|
expectSameCorners(worldCorners(graph, rect.id), before)
|
|
expect(graph.getNode(rect.id)?.rotation).toBeCloseTo(-30, 6)
|
|
})
|
|
|
|
test('moving into a rotated frame and back out restores the original transform', () => {
|
|
const { graph, pageId } = setup()
|
|
const frame = graph.createNode('FRAME', pageId, {
|
|
x: 100,
|
|
y: 50,
|
|
width: 240,
|
|
height: 240,
|
|
rotation: 30
|
|
})
|
|
const rect = graph.createNode('RECTANGLE', pageId, { x: 500, y: 400, width: 40, height: 20 })
|
|
|
|
graph.reparentNode(rect.id, frame.id)
|
|
graph.reparentNode(rect.id, pageId)
|
|
|
|
const node = graph.getNode(rect.id)
|
|
expect(node?.x).toBeCloseTo(500, 6)
|
|
expect(node?.y).toBeCloseTo(400, 6)
|
|
expect(node?.rotation).toBeCloseTo(0, 6)
|
|
})
|
|
|
|
test('moving into a flipped frame keeps the corners', () => {
|
|
const { graph, pageId } = setup()
|
|
const frame = graph.createNode('FRAME', pageId, {
|
|
x: 100,
|
|
y: 100,
|
|
width: 300,
|
|
height: 200,
|
|
flipX: true
|
|
})
|
|
const rect = graph.createNode('RECTANGLE', pageId, {
|
|
x: 150,
|
|
y: 120,
|
|
width: 40,
|
|
height: 20,
|
|
rotation: 15
|
|
})
|
|
const before = worldCorners(graph, rect.id)
|
|
|
|
graph.reparentNode(rect.id, frame.id)
|
|
|
|
expectSameCorners(worldCorners(graph, rect.id), before)
|
|
const node = graph.getNode(rect.id)
|
|
expect(node?.flipX).not.toBe(node?.flipY)
|
|
})
|
|
|
|
test('a rotated node moved between unrotated frames keeps its corners and rotation', () => {
|
|
const { graph, pageId } = setup()
|
|
const left = graph.createNode('FRAME', pageId, { x: 0, y: 0, width: 200, height: 200 })
|
|
const right = graph.createNode('FRAME', pageId, { x: 300, y: 40, width: 200, height: 200 })
|
|
const rect = graph.createNode('RECTANGLE', left.id, {
|
|
x: 20,
|
|
y: 30,
|
|
width: 60,
|
|
height: 20,
|
|
rotation: 45
|
|
})
|
|
const before = worldCorners(graph, rect.id)
|
|
|
|
graph.reparentNode(rect.id, right.id)
|
|
|
|
expectSameCorners(worldCorners(graph, rect.id), before)
|
|
const node = graph.getNode(rect.id)
|
|
expect(node?.x).toBe(-280)
|
|
expect(node?.y).toBe(-10)
|
|
expect(node?.rotation).toBe(45)
|
|
})
|
|
})
|