computeScene function
Implementation
ComputedScene computeScene(CanvasSceneDocument doc, CoreServices services) {
final geom = NodeGeometry(services);
final drawList = <DrawItem>[];
final nodeById = <ElementId, Node>{};
final worldById = <ElementId, vm.Matrix4>{};
final layoutBoundsLocalById = <ElementId, Rect2D>{};
final paintBoundsLocalById = <ElementId, Rect2D>{};
final paintBoundsWorldById = <ElementId, Rect2D>{};
final pathIRById = <ElementId, PathIR>{};
final imagePlacementById = <ElementId, ImagePlacement>{};
final iconTextById = <ElementId, ResolvedIconText>{};
final iconPathIRById = <ElementId, PathIR>{};
vm.Matrix4 localMatrixFromLayout(Node n) {
final xf = n.xf;
final pivot = _pivotFromOrigin(
xf.origin,
layoutBoundsLocalById[n.id],
xf.customPivotPx,
);
return matFromTRS(
position: xf.position,
rotationRad: xf.rotationRad,
scale: xf.scale,
pivotPx: pivot,
);
}
// Post-order: resolve each leaf once, then aggregate local layout/paint
// bounds. A child's layout is settled before its transform is used by its
// parent.
void computeLocalBounds(Node n) {
if (n.hidden) return;
final leaf = geom.leafBounds(
n,
pathIRById: pathIRById,
imagePlacementById: imagePlacementById,
iconTextById: iconTextById,
iconPathIRById: iconPathIRById,
);
if (leaf != null) {
layoutBoundsLocalById[n.id] = leaf.layout;
final paint = leaf.paint;
if (paint != null) paintBoundsLocalById[n.id] = paint;
return;
}
if (n is! GroupNode) return;
Rect2D? layoutUnion;
Rect2D? paintUnion;
for (final child in nodesInPaintOrder(n.children)) {
computeLocalBounds(child);
final layout = layoutBoundsLocalById[child.id];
final paint = paintBoundsLocalById[child.id];
if (layout == null && paint == null) continue;
final childLocal = localMatrixFromLayout(child);
if (layout != null) {
final transformed = aabbOfTransformedRect(layout, childLocal);
layoutUnion = layoutUnion == null
? transformed
: Rect2DX.union(layoutUnion, transformed);
}
if (paint != null) {
final transformed = aabbOfTransformedRect(paint, childLocal);
paintUnion = paintUnion == null
? transformed
: Rect2DX.union(paintUnion, transformed);
}
}
if (layoutUnion != null) {
layoutBoundsLocalById[n.id] = layoutUnion;
}
if (paintUnion != null) {
paintBoundsLocalById[n.id] = paintUnion;
}
}
for (final root in nodesInPaintOrder(doc.children)) {
computeLocalBounds(root);
}
// Pre-order transforms, followed by child-world paint unions for groups.
//
// Do not transform a group's already-aggregated local paint AABB again:
// unioning child world paint bounds remains tighter under nested/counter
// rotations.
void walk(Node n, vm.Matrix4 parentWorld, List<ElementId> groupStack) {
if (n.hidden) return;
nodeById[n.id] = n;
final world = vm.Matrix4.copy(parentWorld)
..multiply(localMatrixFromLayout(n));
worldById[n.id] = world;
if (n is GroupNode) {
final nextStack = [...groupStack, n.id];
Rect2D? paintUnion;
for (final child in nodesInPaintOrder(n.children)) {
walk(child, world, nextStack);
final paint = paintBoundsWorldById[child.id];
if (paint != null) {
paintUnion = paintUnion == null
? paint
: Rect2DX.union(paintUnion, paint);
}
}
if (paintUnion != null) {
paintBoundsWorldById[n.id] = paintUnion;
}
return;
}
drawList.add(DrawItem(leafId: n.id, groupStack: groupStack));
final paint = paintBoundsLocalById[n.id];
if (paint != null) {
paintBoundsWorldById[n.id] = aabbOfTransformedRect(paint, world);
}
}
for (final root in nodesInPaintOrder(doc.children)) {
walk(root, vm.Matrix4.identity(), const <ElementId>[]);
}
return ComputedScene(
drawList: drawList,
nodeById: nodeById,
worldById: worldById,
layoutBoundsLocalById: layoutBoundsLocalById,
paintBoundsLocalById: paintBoundsLocalById,
paintBoundsWorldById: paintBoundsWorldById,
pathIRById: pathIRById,
imagePlacementById: imagePlacementById,
iconTextById: iconTextById,
iconPathIRById: iconPathIRById,
);
}