Skip to content

File test_contour_gen.cpp

File List > contour > tests > test_contour_gen.cpp

Go to the documentation of this file

#include <gtest/gtest.h>

#include <algorithm>
#include <set>
#include <vector>

#include "config_input/config_input.hpp"
#include "contour/contour.hpp"
#include "contour/contour_gen.hpp"
#include "geometry/polygon.hpp"
#include "isom/colors.hpp"
#include "lib/grid/grid.hpp"
#include "utilities/progress_tracker.hpp"

TEST(ContourGen, ContourGen) {
  std::vector<std::vector<double>> data = {{0.5, 0.5, 0.5}, {0.5, 1.5, 0.5}, {0.5, 0.5, 0.5}};
  GeoGrid<double> grid(data);

  ContourConfigs configs({{"normal", ContourConfig{1.0, 1, RGBColor(0, 0, 0, 0), 0.14}}});
  std::vector<Contour> contours = generate_contours(
      grid, ContourConfigs({{"normal", ContourConfig{1.0, 1, RGBColor(0, 0, 0, 0), 0.14}}}),
      ProgressTracker());
  EXPECT_EQ(contours.size(), 1);
  EXPECT_EQ(contours[0].points().size(), 5);
  EXPECT_DOUBLE_EQ(contours[0].height(), 1.0);
  EXPECT_TRUE(contours[0].is_loop());
}

TEST(ContourGen, ContourGen2) {
  std::vector<std::vector<double>> data = {{0.5, 0.5, 0.5, 0.5, 0.5}, {0.5, 1.5, 1.5, 1.5, 0.5},
                                           {0.5, 1.5, 2.1, 1.5, 0.5}, {0.5, 1.5, 2.1, 1.5, 0.5},
                                           {0.5, 1.5, 1.5, 1.5, 1.5}, {0.5, 0.5, 0.5, 1.5, 1.5}};
  GeoGrid<double> grid(data);

  std::vector<Contour> contours = generate_contours(
      grid, ContourConfigs{{{"normal", ContourConfig{1.0, 1, RGBColor(0, 0, 0, 0), 0.14}}}},
      ProgressTracker());
  EXPECT_EQ(contours.size(), 2);

  EXPECT_EQ(contours[0].points().size(), 14);
  EXPECT_DOUBLE_EQ(contours[0].height(), 1.0);
  EXPECT_FALSE(contours[0].is_loop());

  EXPECT_EQ(contours[1].points().size(), 7);
  EXPECT_DOUBLE_EQ(contours[1].height(), 2.0);
  EXPECT_TRUE(contours[1].is_loop());
}

TEST(IdentifyContoursAtHeights, SingleHeightCrossing) {
  std::vector<std::vector<float>> data = {{0.3f, 0.7f}, {0.3f, 0.7f}};
  GeoGrid<float> grid(data);

  std::set<double> heights = {0.5};
  GridGraph<std::set<double>> result =
      identify_contours_at_heights(grid, heights, ProgressTracker());

  LineCoord2D<size_t> crossing_edge({0, 0}, Direction2D::RIGHT);
  EXPECT_TRUE(result.in_bounds(crossing_edge));
  EXPECT_TRUE(result[crossing_edge].contains(0.5));

  LineCoord2D<size_t> crossing_edge2({0, 1}, Direction2D::RIGHT);
  EXPECT_TRUE(result.in_bounds(crossing_edge2));
  EXPECT_TRUE(result[crossing_edge2].contains(0.5));
}

TEST(IdentifyContoursAtHeights, NoCrossing) {
  std::vector<std::vector<float>> data = {{0.1f, 0.2f}, {0.1f, 0.2f}};
  GeoGrid<float> grid(data);

  std::set<double> heights = {0.5};
  GridGraph<std::set<double>> result =
      identify_contours_at_heights(grid, heights, ProgressTracker());

  for (size_t i = 0; i < result.height(); i++) {
    for (size_t j = 0; j < result.width(); j++) {
      for (Direction2D dir : {Direction2D::DOWN, Direction2D::RIGHT}) {
        LineCoord2D<size_t> edge({j, i}, dir);
        if (result.in_bounds(edge)) {
          EXPECT_TRUE(result[edge].empty());
        }
      }
    }
  }
}

TEST(IdentifyContoursAtHeights, MultipleHeights) {
  std::vector<std::vector<float>> data = {{0.1f, 0.4f, 0.8f}};
  GeoGrid<float> grid(data);

  std::set<double> heights = {0.25, 0.5};
  GridGraph<std::set<double>> result =
      identify_contours_at_heights(grid, heights, ProgressTracker());

  LineCoord2D<size_t> edge1({0, 0}, Direction2D::RIGHT);
  EXPECT_TRUE(result[edge1].contains(0.25));
  EXPECT_FALSE(result[edge1].contains(0.5));

  LineCoord2D<size_t> edge2({1, 0}, Direction2D::RIGHT);
  EXPECT_FALSE(result[edge2].contains(0.25));
  EXPECT_TRUE(result[edge2].contains(0.5));
}

TEST(IdentifyContoursAtHeights, ExactThresholdValue) {
  std::vector<std::vector<float>> data = {{0.3f, 0.5f}};
  GeoGrid<float> grid(data);

  std::set<double> heights = {0.5};
  GridGraph<std::set<double>> result =
      identify_contours_at_heights(grid, heights, ProgressTracker());

  LineCoord2D<size_t> edge({0, 0}, Direction2D::RIGHT);
  EXPECT_TRUE(result[edge].contains(0.5));
}

TEST(IdentifyContoursAtHeights, MinExactlyAtThreshold) {
  std::vector<std::vector<float>> data = {{0.5f, 0.7f}};
  GeoGrid<float> grid(data);

  std::set<double> heights = {0.5};
  GridGraph<std::set<double>> result =
      identify_contours_at_heights(grid, heights, ProgressTracker());

  LineCoord2D<size_t> edge({0, 0}, Direction2D::RIGHT);
  EXPECT_FALSE(result[edge].contains(0.5));
}

TEST(GenerateContoursAtHeights, SinglePeakClosedLoop) {
  std::vector<std::vector<float>> data = {
      {0.6f, 0.6f},
      {0.6f, 0.6f},
  };
  GeoGrid<float> grid(data);

  std::vector<double> heights = {0.5};
  auto contours_by_height = generate_contours_at_heights(grid, heights, ProgressTracker(), 1, 0.0f);

  EXPECT_EQ(contours_by_height.size(), 1u);
  EXPECT_TRUE(contours_by_height.contains(0.5));

  const auto& contours = contours_by_height.at(0.5);
  ASSERT_EQ(contours.size(), 1u);

  const Contour& c = contours[0];
  EXPECT_TRUE(c.is_loop());
  EXPECT_EQ(c.points().size(), 9u);
  EXPECT_DOUBLE_EQ(c.height(), 0.5);
}

TEST(GenerateContoursAtHeights, EdgeContoursAreClosed) {
  std::vector<std::vector<float>> data = {
      {0.7f, 0.7f, 0.2f},
      {0.7f, 0.7f, 0.2f},
      {0.2f, 0.2f, 0.2f},
  };
  GeoGrid<float> grid(data);

  std::vector<double> heights = {0.5};
  auto contours_by_height = generate_contours_at_heights(grid, heights, ProgressTracker(), 1, 0.0f);

  EXPECT_EQ(contours_by_height.size(), 1u);
  const auto& contours = contours_by_height.at(0.5);

  for (const Contour& c : contours) {
    EXPECT_TRUE(c.is_loop()) << "Contour at height " << c.height() << " should be closed";
  }
}

TEST(GenerateContoursAtHeights, MultipleDisjointPatches) {
  std::vector<std::vector<float>> data = {
      {0.7f, 0.2f, 0.7f},
  };
  GeoGrid<float> grid(data);

  std::vector<double> heights = {0.5};
  auto contours_by_height = generate_contours_at_heights(grid, heights, ProgressTracker(), 3, 0.0f);

  const auto& contours = contours_by_height.at(0.5);
  EXPECT_EQ(contours.size(), 2u);

  for (const Contour& c : contours) {
    EXPECT_TRUE(c.is_loop());
    EXPECT_EQ(c.points().size(), 5u);
  }
}

TEST(GenerateContoursAtHeights, AllZerosNoContours) {
  std::vector<std::vector<float>> data = {
      {0.0f, 0.0f, 0.0f},
      {0.0f, 0.0f, 0.0f},
      {0.0f, 0.0f, 0.0f},
  };
  GeoGrid<float> grid(data);

  std::vector<double> heights = {0.5};
  ProgressTracker progress_tracker;
  auto contours_by_height =
      generate_contours_at_heights(grid, heights, SUBTRACKER(0.0, 0.5), 1, 0.0f);

  EXPECT_EQ(contours_by_height.count(0.5), 0u);

  contours_by_height = generate_contours_at_heights(grid, heights, SUBTRACKER(0.5, 1.0), 1, 1.0f);
  EXPECT_EQ(contours_by_height.count(0.5), 1u);
  EXPECT_EQ(contours_by_height.at(0.5).size(), 1u);
  EXPECT_EQ(contours_by_height.at(0.5)[0].points().size(), 13u);
}

TEST(GenerateContoursAtHeights, MultipleHeights) {
  std::vector<std::vector<float>> data = {
      {0.3f, 0.5f, 0.3f},
      {0.5f, 0.9f, 0.5f},
      {0.3f, 0.5f, 0.3f},
  };
  GeoGrid<float> grid(data);

  std::vector<double> heights = {0.2, 0.4, 0.7};
  auto contours_by_height = generate_contours_at_heights(grid, heights, ProgressTracker(), 3, 0.0f);

  EXPECT_EQ(contours_by_height.size(), 3u);
  EXPECT_TRUE(contours_by_height.contains(0.2));
  EXPECT_TRUE(contours_by_height.contains(0.4));
  EXPECT_TRUE(contours_by_height.contains(0.7));
}

TEST(GenerateContoursAtHeights, FilteredByMinPoints) {
  std::vector<std::vector<float>> data = {{0.8f}};
  GeoGrid<float> grid(data);

  std::vector<double> heights = {0.5};
  ProgressTracker progress_tracker;

  auto contours_low = generate_contours_at_heights(grid, heights, SUBTRACKER(0.0, 0.5), 1, 0.0f);
  EXPECT_EQ(contours_low[0.5].size(), 1u);

  auto contours_high = generate_contours_at_heights(grid, heights, SUBTRACKER(0.5, 1.0), 100, 0.0f);
  EXPECT_EQ(contours_high[0.5].size(), 0u);
}

TEST(GenerateContoursAtHeights, OrientsDonutRings) {
  std::vector<std::vector<float>> data = {
      {0.8f, 0.8f, 0.8f, 0.8f, 0.8f},
      {0.8f, 0.0f, 0.0f, 0.0f, 0.8f},
      {0.8f, 0.0f, 0.0f, 0.0f, 0.8f},
      {0.8f, 0.8f, 0.8f, 0.8f, 0.8f},
  };
  GeoGrid<float> grid(data);

  auto contours_by_height = generate_contours_at_heights(grid, {0.5}, ProgressTracker(), 3, 0.0f);

  ASSERT_EQ(contours_by_height[0.5].size(), 2u);
  const Contour* outer = nullptr;
  const Contour* inner = nullptr;
  for (const Contour& c : contours_by_height[0.5]) {
    if (signed_area(c.points()) > 0.0) {
      outer = &c;
    } else if (signed_area(c.points()) < 0.0) {
      inner = &c;
    }
  }
  ASSERT_NE(outer, nullptr);
  ASSERT_NE(inner, nullptr);
  EXPECT_TRUE(point_in_ring(inner->points()[0], outer->points()));
}