surfaceGeometryProperties function
Rendering/geometry projection only. Grid rings remain the stored authority; materialized width/height and node transforms are not recomputed on load.
Implementation
Map<String, dynamic> surfaceGeometryProperties(LayoutNode node) {
final surface = node.surface;
if (node.kind != 'table' || surface == null) return node.properties;
final result = <String, dynamic>{...node.properties};
switch (surface['type']) {
case 'rect':
result['shape'] = 'rect';
case 'circle':
result['shape'] = 'circle';
case 'grid':
bool validRing(Object? raw) =>
raw is List &&
raw.every(
(p) =>
p is List &&
p.length == 2 &&
p.every((v) => v is num && v.isFinite),
);
if (surface['moduleSize'] is! num ||
!(surface['moduleSize'] as num).isFinite ||
!validRing(surface['outer']) ||
surface['holes'] is! List ||
!(surface['holes'] as List).every(validRing)) {
return node.properties;
}
final module = (surface['moduleSize'] as num).toDouble();
List<List<double>> ring(List raw) => [
for (final point in raw)
[
(point[0] as num) * module - node.width / 2,
(point[1] as num) * module - node.height / 2,
],
];
result.addAll({
'shape': 'polygon',
'points': ring(surface['outer'] as List),
'holes': [
for (final hole in surface['holes'] as List) ring(hole as List),
],
});
}
return frozenMap(result);
}