openpencil/tests/engine/geometry/scene.test.ts
Danila Poyarkov d16400b3e6 feat(editor): checkpoint live interaction improvements
Unify preview-aware transforms across scene drawing, labels, selection and input. Give live property and creation edits explicit preview ownership, preserve cancellation and one-step undo, and reuse bounded text preparation resources.

Include retained device-grid handling, worktree HMR coverage, and nested/reflected interaction regressions in this cohesive progress checkpoint.

Validation: full check passes; 734 scoped unit tests and 53 targeted browser tests pass. This is NOT merge-ready: the unchanged exact nested filled-section held/released regression still fails with 63 differing pixels (maximum channel delta 5/255). The browser pass count excludes that separately run failure and the previously classified paint-field-width baseline. Raster-origin investigation and broader acceptance remain outstanding.
2026-09-15 11:36:17 +03:00

74 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { expect, test } from 'bun:test'
import { SceneGraph } from '@open-pencil/scene-graph'
import Matrix from '@open-pencil/scene-graph/matrix'
import { createSceneGeometry, nodeOrientationMatrix } from '#core/geometry'
import { expectDefined } from '#tests/helpers/assert'
import { applyFixtureTransform, nestedGeometryFixture } from '#tests/helpers/geometry'
for (const flipX of [false, true]) {
for (const flipY of [false, true]) {
test(`nested world, screen and inverse transforms agree (flipX=${flipX}, flipY=${flipY})`, () => {
const graph = new SceneGraph()
const page = expectDefined(graph.getPages()[0], 'page')
const { section, frame } = nestedGeometryFixture(graph, page.id, flipX, flipY)
const geometry = createSceneGeometry(graph)
const viewport = { panX: 137.25, panY: -89.75, zoom: 1.75 }
for (const point of [
{ x: 0, y: 0 },
{ x: 220, y: 120 },
{ x: 110, y: -24 }
]) {
const expected = applyFixtureTransform(section, applyFixtureTransform(frame, point))
const world = geometry.toWorld(frame, point)
expect(world.x).toBeCloseTo(expected.x, 9)
expect(world.y).toBeCloseTo(expected.y, 9)
const screen = geometry.toScreen(frame, point, viewport)
expect(screen.x).toBeCloseTo(expected.x * viewport.zoom + viewport.panX, 9)
expect(screen.y).toBeCloseTo(expected.y * viewport.zoom + viewport.panY, 9)
const local = expectDefined(geometry.screenToLocal(frame, screen, viewport), 'local point')
expect(local.x).toBeCloseTo(point.x, 9)
expect(local.y).toBeCloseTo(point.y, 9)
}
expect(geometry.screenToLocal(frame, { x: 0, y: 0 }, { ...viewport, zoom: 0 })).toBeNull()
})
}
}
test('ancestor previews resolve equally for geometry, cancellation and committed transforms', () => {
const graph = new SceneGraph()
const page = expectDefined(graph.getPages()[0], 'page')
const { section, frame } = nestedGeometryFixture(graph, page.id, true, true)
const original = createSceneGeometry(graph).worldMatrix(frame)
const preview = createSceneGeometry(graph, { nodeId: section.id, angle: -70 })
const held = preview.worldMatrix(frame)
expect(held).not.toEqual(original)
expect(section.rotation).toBe(25)
expect(createSceneGeometry(graph, null).worldMatrix(frame)).toEqual(original)
const composed = Matrix.multiply(preview.localMatrix(section), preview.localMatrix(frame))
for (const [index, value] of held.entries()) expect(composed[index]).toBeCloseTo(value, 9)
graph.updateNode(section.id, { rotation: -70 })
expect(createSceneGeometry(graph).worldMatrix(frame)).toEqual(held)
})
test('scene drawing and geometry share each node type’s pivot', () => {
const graph = new SceneGraph()
const page = expectDefined(graph.getPages()[0], 'page')
for (const type of ['LINE', 'RECTANGLE', 'FRAME'] as const) {
const node = graph.createNode(type, page.id, {
x: 100,
y: 200,
width: 220,
height: 40,
rotation: 35,
flipX: true
})
const geometry = createSceneGeometry(graph)
const drawn = Matrix.multiply(Matrix.translated(node.x, node.y), nodeOrientationMatrix(node))
const expected = geometry.worldMatrix(node)
for (const [index, value] of expected.entries()) expect(drawn[index]).toBeCloseTo(value, 9)
}
})