private function computeBarWidths()

in web_ui/src/applications/bistro/view/components/StackedBarChart.php [80:145]


  private function computeBarWidths(StackedBarChartData $data) {
    // Clear the widths to deal with double-rendering
    foreach ($data->bars as $bar) {
      $bar->width = null;
    }

    // The retry logic is needed to both fit in total_width, and not to have
    // bars shorter than min_width.  I am lazy, so this is quadratic :(
    // TODO: faster algorithm
    //
    // The loop stores the percentage width (or _USE_MIN_WIDTH) in $bar->width

    // Both values decrease whenever a bar is below min width
    $adjusted_width = $data->totalWidth;
    $total_weight = $data->getTotalWeight();
    if (!$total_weight) {
      return False;
    }

    $retry = True;
    while ($retry) {
      $retry = False;
      foreach ($data->bars as $bar) {
        if ($bar->width === self::_USE_MIN_WIDTH) {
          continue;
        }

        if ($total_weight) {
          $bar->width = $bar->weight / $total_weight;
          $missing_width = $adjusted_width * $bar->width - $data->minBarWidth;
          if ($missing_width < 0) {
            $adjusted_width -= $data->minBarWidth;
            $total_weight -= $bar->weight;
            $bar->width = self::_USE_MIN_WIDTH;
            $retry = True;
            break;
          }
        } else {
          $bar->width = self::_USE_MIN_WIDTH;
        }
      }
    }

    $total_width = 0;
    foreach ($data->bars as $bar) {
      if ($bar->width === self::_USE_MIN_WIDTH) {
        $bar->width = $data->minBarWidth;
      } else {
        $bar->width *= $adjusted_width;
      }
      $total_width += $bar->width;
    }

    // Round to get integers -- important for visually consistent widths
    foreach ($data->bars as $bar) {
      if ($total_width > $data->totalWidth) {
        $rounded = floor($bar->width);
      } else {
        $rounded = ceil($bar->width);
      }
      $total_width += $rounded - $bar->width;
      $bar->width = $rounded;
    }

    return True;
  }