Files
Genarrative/apps/ai-game-creator-shell/tests/nodeTransformGeometry.test.ts
T
k88936 10317e16e7 收拢 UI Editor 节点几何 seam
新增唯一节点几何模块并统一页面坐标、节点上下文与 offset 反算

迁移状态层重挂载、子节点保位、Inspector 与预览 resize 适配器

补充纯几何回归测试、更新 UI Editor 技术方案
2026-08-19 15:08:40 +08:00

120 lines
3.1 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
findNodePageContext,
pageRectFromSize,
resolveAnchorPresetTransform,
resolveChildrenTransformsForParentRect,
resolvePageRect,
resolveReparentTransform,
} from '../src/features/ui-editor/nodeTransformGeometry';
import type { Node } from '../src/features/ui-editor/types/Node';
import type { Transform } from '../src/features/ui-editor/types/Transform';
const ROOT_TRANSFORM: Transform = {
anchor_min: [0, 0],
anchor_max: [1, 1],
offset_min: [0, 0],
offset_max: [0, 0],
};
function node(id: string, transform: Transform, children: Node[] = []): Node {
return {
id,
layout: { transform } as Node['layout'],
metadata: {} as Node['metadata'],
components: [],
children_display_mode: 'Stack',
children,
};
}
describe('nodeTransformGeometry', () => {
it('resolves nested context and inverts page offsets at the same seam', () => {
const childTransform: Transform = {
anchor_min: [0.5, 0.5],
anchor_max: [0.5, 0.5],
offset_min: [-10, -15],
offset_max: [30, 25],
};
const root = node('root', ROOT_TRANSFORM, [
node(
'parent',
{
anchor_min: [0, 0],
anchor_max: [0, 0],
offset_min: [20, 30],
offset_max: [220, 130],
},
[node('child', childTransform)],
),
]);
const context = findNodePageContext(
root,
'child',
pageRectFromSize([400, 300]),
);
expect(context).toMatchObject({
parentRect: { min: [20, 30], max: [220, 130] },
rect: { min: [110, 65], max: [150, 105] },
});
expect(
resolveReparentTransform(
childTransform,
context!.rect,
context!.parentRect,
),
).toEqual(childTransform);
});
it('preserves direct child page rectangles when a parent changes', () => {
const child = node('child', {
anchor_min: [0.25, 0],
anchor_max: [0.75, 1],
offset_min: [5, 10],
offset_max: [-10, -15],
});
const oldParentRect = {
min: [20, 30] as [number, number],
max: [220, 130] as [number, number],
};
const newParentRect = {
min: [40, 50] as [number, number],
max: [340, 250] as [number, number],
};
const oldChildRect = resolvePageRect(child.layout.transform, oldParentRect);
const [{ transform }] = resolveChildrenTransformsForParentRect(
[child],
oldParentRect,
newParentRect,
);
expect(resolvePageRect(transform, newParentRect)).toEqual(oldChildRect);
});
it('keeps the page rectangle stable when inspector anchors change', () => {
const transform: Transform = {
anchor_min: [0.5, 0.5],
anchor_max: [0.5, 0.5],
offset_min: [-60, -20],
offset_max: [40, 30],
};
const parentSize = { width: 300, height: 200 };
const pageRect = pageRectFromSize([parentSize.width, parentSize.height]);
const next = resolveAnchorPresetTransform(
transform,
[0, 0],
[1, 1],
parentSize,
);
expect(resolvePageRect(next, pageRect)).toEqual(
resolvePageRect(transform, pageRect),
);
});
});