computeScene function

ComputedScene computeScene(
  1. CanvasSceneDocument doc,
  2. CoreServices services
)

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,
  );
}