compute static method

FluentGaugeLayout compute({
  1. required Size size,
  2. required List<FluentGaugeChartSegment> segments,
  3. required double minValue,
  4. double? maxValue,
  5. required bool hasTitle,
  6. required bool hasSublabel,
  7. required bool hideMinMax,
  8. required bool hideLegend,
  9. required double gaugeMargin,
  10. required double labelWidth,
  11. required double labelHeight,
  12. required double labelOffset,
  13. required double titleOffset,
  14. required double extraNeedleLength,
  15. required double legendsHeight,
  16. required Color unknownColour,
  17. String unknownLegend = 'Unknown',
  18. required bool isDark,
})

Lays a gauge out inside size.

size is the chart's LOGICAL box, legend included: upstream's _height is the root element's height and the svg is drawn 32 shorter (GaugeChart.tsx:594), so passing the svg's own height would move the origin up by a legend.

Ports _getMargins (GaugeChart.tsx:109-117), the outer radius at :138-141, _getStylesBasedOnBreakpoint (:167-180), _processProps (:184-206), the needle length at :253 and the root translate at :599.

Implementation

static FluentGaugeLayout compute({
  required Size size,
  required List<FluentGaugeChartSegment> segments,
  required double minValue,
  double? maxValue,
  required bool hasTitle,
  required bool hasSublabel,
  required bool hideMinMax,
  required bool hideLegend,
  required double gaugeMargin,
  required double labelWidth,
  required double labelHeight,
  required double labelOffset,
  required double titleOffset,
  required double extraNeedleLength,
  required double legendsHeight,
  required Color unknownColour,
  // `GaugeChart.tsx:206`'s literal 'Unknown'. Localized by the caller —
  // `compute` is static and sees no BuildContext.
  String unknownLegend = 'Unknown',
  required bool isDark,
}) {
  // GaugeChart.tsx:110-116.
  final horizontal =
      (hideMinMax ? 0.0 : labelOffset + labelWidth) + gaugeMargin;
  final margins = EdgeInsets.fromLTRB(
    horizontal,
    // The no-title term is EXTRA_NEEDLE_LENGTH / 2, which is 2 — just enough
    // for the needle's half stroke to clear the top of the box.
    (hasTitle ? titleOffset + labelHeight : extraNeedleLength / 2) +
        gaugeMargin,
    horizontal,
    (hasSublabel ? labelOffset + labelHeight : 0.0) + gaugeMargin,
  );
  // GaugeChart.tsx:119.
  final legend = hideLegend ? 0.0 : legendsHeight;

  // GaugeChart.tsx:138-141. Only the width term is halved: the gauge is a
  // half disc, so it spans 2R horizontally and R vertically.
  final outerRadius = math.min(
    (size.width - (margins.left + margins.right)) / 2,
    size.height - (margins.top + margins.bottom + legend),
  );

  // GaugeChart.tsx:167-179 — scan from the largest tier down, then fall back
  // to the first entry rather than to nothing. The comparison is `>=`, so a
  // radius exactly at a minRadius belongs to that tier, not the one below.
  var breakpoint = kFluentGaugeBreakpoints.first;
  for (var i = kFluentGaugeBreakpoints.length - 1; i >= 0; i--) {
    if (outerRadius >= kFluentGaugeBreakpoints[i].minRadius) {
      breakpoint = kFluentGaugeBreakpoints[i];
      break;
    }
  }
  // GaugeChart.tsx:143.
  final innerRadius = outerRadius - breakpoint.arcWidth;

  // GaugeChart.tsx:185-206 — total is SEEDED with minValue, which is why
  // start and end are offset by it.
  var total = minValue;
  final processed = <FluentGaugeSegment>[];
  for (var i = 0; i < segments.length; i++) {
    final segment = segments[i];
    // GaugeChart.tsx:189 — Math.max(segment.size, 0).
    final segmentSize = math.max(segment.size, 0.0);
    total += segmentSize;
    processed.add(
      FluentGaugeSegment(
        legend: segment.legend,
        size: segmentSize,
        // GaugeChart.tsx:194-197 — getNextColor(index, 0, false). Every
        // imperative chart leaves the isDark flag false upstream; here the
        // theme supplies it.
        colour: segment.color ?? FluentDataVizPalette.next(i, isDark: isDark),
        start: total - segmentSize,
        end: total,
        semantics: segment.semantics,
      ),
    );
  }
  // GaugeChart.tsx:204-213. The comparison is a strict `<`, so a maximum
  // exactly at the running total appends nothing.
  if (maxValue != null && total < maxValue) {
    processed.add(
      FluentGaugeSegment(
        legend: unknownLegend,
        size: maxValue - total,
        colour: unknownColour,
        start: total,
        end: maxValue,
      ),
    );
    total = maxValue;
  }

  return FluentGaugeLayout._(
    size: size,
    margins: margins,
    outerRadius: outerRadius,
    innerRadius: innerRadius,
    arcWidth: breakpoint.arcWidth,
    chartValueFontSize: breakpoint.fontSize,
    minValue: minValue,
    // GaugeChart.tsx:240 — the resolved maximum is the running total, seed
    // included.
    maxValue: total,
    // GaugeChart.tsx:253.
    needleLength: outerRadius - innerRadius + extraNeedleLength,
    // GaugeChart.tsx:599 — translate(_width / 2, _height - (margins.bottom +
    // legendsHeight)), against the LOGICAL height rather than the svg's
    // already-shortened one at :594.
    origin: Offset(size.width / 2, size.height - (margins.bottom + legend)),
    segments: List<FluentGaugeSegment>.unmodifiable(processed),
  );
}