leafBounds method
({Rect2D layout, Rect2D? paint})?
leafBounds(
- Node n, {
- Map<
ElementId, PathIR> ? pathIRById, - Map<
ElementId, ImagePlacement> ? imagePlacementById, - Map<
ElementId, ComputedIcon> ? iconById,
Layout describes intrinsic geometry independently of appearance. Paint is an estimate; null means no visible paint is expected. Text estimates use layout metrics, not guaranteed glyph-pixel bounds.
Implementation
({Rect2D layout, Rect2D? paint})? leafBounds(
Node n, {
Map<ElementId, PathIR>? pathIRById,
Map<ElementId, ImagePlacement>? imagePlacementById,
Map<ElementId, ComputedIcon>? iconById,
}) {
final s = services;
switch (n) {
case TextNode(:final data):
final m = s.textMeasurer.measure(
text: data.text,
fontFamily: data.fontFamily,
fontWeight: data.fontWeight,
fontSize: data.fontSize,
letterSpacing: data.letterSpacing,
);
final layout = Rect2D.fromLTWH(-m.w / 2, -m.h / 2, m.w, m.h);
final sourceBounds = data.text.isEmpty ? null : layout;
final appearance = data.appearance;
return (
layout: layout,
paint: estimateAppearancePaintBounds(
sourceBounds: sourceBounds,
foregroundPresent: fillContributesToPaint(appearance.foreground),
underlays: appearance.underlays,
),
);
case IconNode(id: final id, data: final d):
final size = d.sizePx;
if (!size.isFinite || size <= 0) {
throw ArgumentError.value(
size,
'sizePx',
'Must be finite and positive.',
);
}
final layout = Rect2D.fromLTWH(-size / 2, -size / 2, size, size);
final resolved = s.icons?.resolve(d.iconRef);
if (resolved == null) return (layout: layout, paint: null);
final icon = ComputedIcon.fit(resolved, size);
iconById?[id] = icon;
final sourceBounds = icon.pathIR.cmds.isEmpty
? null
: aabbOfTransformedRect(
icon.pathIR.localBounds(),
icon.sourceToLocal,
);
final appearance = d.appearance;
return (
layout: layout,
paint: estimateAppearancePaintBounds(
sourceBounds: sourceBounds,
foregroundPresent: fillContributesToPaint(appearance.foreground),
underlays: appearance.underlays,
),
);
case ImageNode(id: final id, data: final d):
final intrinsic = s.images?.intrinsicSize(id);
final size = d.size;
if (size.w <= 0 || size.h <= 0) return null;
final placement = imageSrcDst(
intrinsic: intrinsic ?? size,
layout: size,
fit: d.fit,
align: d.align,
);
imagePlacementById?[id] = placement;
// Preserve contain-fit destination geometry, including its offset.
return (layout: placement.dst, paint: placement.dst);
case PathNode(id: final id, data: final d):
final ir = compilePath(d);
pathIRById?[id] = ir;
final geometryBounds = ir.localBounds();
final hasSegments = ir.cmds.any(
(cmd) => cmd.verb != PathVerb.moveTo && cmd.verb != PathVerb.close,
);
return (
layout: geometryBounds,
paint: estimatePathPaintBounds(
sourceBounds: hasSegments ? geometryBounds : null,
appearance: d.appearance,
),
);
default:
return null;
}
}