computeHighlightRects static method
List<MapEntry<HighlightRect, HighlightRange> >
computeHighlightRects(
- List<
LatexNode> children, - List<
HighlightRange> ranges, - RenderContext context,
- NodeLayout groupLayout, {
- LayoutCache? cache,
Calculates the bounding rectangles of highlight areas.
Implementation
static List<MapEntry<HighlightRect, HighlightRange>> computeHighlightRects(
List<LatexNode> children,
List<HighlightRange> ranges,
RenderContext context,
NodeLayout groupLayout, {
LayoutCache? cache,
}) {
if (children.isEmpty) return const [];
// Pre-measure each child node's size to calculate x offsets.
// If a cache exists, these nodes will hit the cache from the main measurement phase.
final childLayouts = children
.map((it) => measureNode(it, context, cache: cache))
.toList();
final fontSizePx = context.fontSize;
// Calculate baseline alignment parameters
double maxAscent = 0.0;
double maxDescent = 0.0;
for (final layout in childLayouts) {
final ascent = layout.baseline;
final descent = layout.height - layout.baseline;
if (ascent > maxAscent) maxAscent = ascent;
if (descent > maxDescent) maxDescent = descent;
}
// Calculate TeX standard atom spacing (matches the logic in measureGroup)
final isScript =
context.mathStyle == MathStyle.script ||
context.mathStyle == MathStyle.scriptScript;
final spacings = List<double>.filled(children.length, 0.0);
for (int i = 0; i < children.length - 1; i++) {
final leftNode = children[i];
final rightNode = children[i + 1];
if (leftNode is SpaceNode ||
leftNode is HSpaceNode ||
rightNode is SpaceNode ||
rightNode is HSpaceNode) {
continue;
}
final leftType = MathSpacing.classifyNode(leftNode);
final rightType = MathSpacing.classifyNode(rightNode);
final spacingFactor = MathSpacing.spaceBetween(
leftType,
rightType,
isScript,
);
spacings[i] = spacingFactor * fontSizePx;
}
// Calculate x offset for each child node
final xOffsets = List<double>.filled(children.length, 0.0);
double currentX = 0.0;
for (int i = 0; i < children.length; i++) {
xOffsets[i] = currentX;
currentX += childLayouts[i].width;
if (i < spacings.length) {
currentX += spacings[i];
}
}
final result = <MapEntry<HighlightRect, HighlightRange>>[];
for (final range in ranges) {
if (range.nodeIndices != null) {
// clamp start and end indices to bounds using your Dart IntRange properties
final startIdx = range.nodeIndices!.start.clamp(0, children.length - 1);
final endIdx = range.nodeIndices!.endInclusive.clamp(
0,
children.length - 1,
);
final x = xOffsets[startIdx];
final endX = xOffsets[endIdx] + childLayouts[endIdx].width;
final rect = HighlightRect(
x: x,
y: 0.0,
width: endX - x,
height: groupLayout.height,
);
result.add(MapEntry(rect, range));
} else if (range.pattern != null) {
for (int i = 0; i < children.length; i++) {
if (_nodeContainsText(children[i], range.pattern!)) {
final rect = HighlightRect(
x: xOffsets[i],
y: maxAscent - childLayouts[i].baseline,
width: childLayouts[i].width,
height: childLayouts[i].height,
);
result.add(MapEntry(rect, range));
}
}
}
}
return result;
}