void fillConvexPoly()

in src/simulator/image_to_box2d.cpp [63:128]


void fillConvexPoly(const std::vector<Vector>& v, const T color,
                    Array2d<T>* array) {
  struct Edge {
    Vector start, end;
  };

  std::vector<Edge> leftEdges, rightEdges;
  for (size_t i = 0; i < v.size(); ++i) {
    size_t prev = i == 0 ? v.size() - 1 : i - 1;
    if (std::abs(v[i].y - v[prev].y) < 1e-3) {
      continue;
    }
    if (v[prev].y < v[i].y) {
      Edge edge{v[prev], v[i]};
      rightEdges.push_back(edge);
    } else {
      Edge edge{v[i], v[prev]};
      leftEdges.push_back(edge);
    }
  }

  const auto width = array->width;
  const auto height = array->height;
  if (leftEdges.empty() || rightEdges.empty()) return;

  std::sort(leftEdges.begin(), leftEdges.end(),
            [](const Edge& lhs, const Edge& rhs) {
              return lhs.start.y < rhs.start.y;
            });
  std::sort(rightEdges.begin(), rightEdges.end(),
            [](const Edge& lhs, const Edge& rhs) {
              return lhs.start.y < rhs.start.y;
            });
  int leftActive = 0, rightActive = 0;

  auto cmpY = [](const Vector& lhs, const Vector& rhs) {
    return lhs.y < rhs.y;
  };

  const float polygonMinY = std::min_element(v.begin(), v.end(), cmpY)->y;
  const float polygonMaxY = std::max_element(v.begin(), v.end(), cmpY)->y;
  const int drawStartY = std::max<int>(0, std::lrint(polygonMinY));
  const int drawEndY = std::min<int>(height, std::lrint(polygonMaxY));

  auto getX = [](const Edge& edge, float y) {
    const float alpha = (y - edge.start.y) / (edge.end.y - edge.start.y);
    return alpha * (edge.end.x - edge.start.x) + edge.start.x;
  };

  for (int y = drawStartY; y < drawEndY; ++y) {
    while (leftActive + 1 < leftEdges.size() &&
           leftEdges[leftActive].end.y < y + 0.5)
      ++leftActive;
    while (rightActive + 1 < rightEdges.size() &&
           rightEdges[rightActive].end.y < y + 0.5)
      ++rightActive;
    const float leftX = getX(leftEdges[leftActive], y + 0.5);
    const float rightX = getX(rightEdges[rightActive], y + 0.5);
    const int leftXInt = std::max<int>(0, std::lrint(leftX));
    const int rightXInt = std::min<int>(width, std::lrint(rightX));
    if (leftXInt < rightXInt) {
      auto start = array->data + y * width;
      std::fill(&start[leftXInt], &start[rightXInt], color);
    }
  }
}