Skip to content

Instantly share code, notes, and snippets.

@psyq321
Created June 17, 2023 08:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save psyq321/c35af7b6d7eb0ba30d1cec7aa7575c42 to your computer and use it in GitHub Desktop.
Save psyq321/c35af7b6d7eb0ba30d1cec7aa7575c42 to your computer and use it in GitHub Desktop.

To implement the described algorithm, we'll use an iterative approach where we first calculate the closest triangles for both the beginning and ending points of each tract and then choose the subset of tracts that maximize the mesh coverage and area dispersion. For this, we will create helper functions to find the closest triangle, calculate dispersion, and select the subset of tracts.

Include the necessary headers:

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
#include <tuple>
#include <glm/glm.hpp>

First, let's create a function that calculates the distance from a point to a triangle:

float distancePointTriangle(const glm::vec3 &point, const glm::vec3 &a, const glm::vec3 &b, const glm::vec3 &c) {
  glm::vec3 ab = b - a;
  glm::vec3 ac = c - a;
  glm::vec3 n = glm::cross(ab, ac); // Normal of the triangle
  glm::vec3 p = point - a;
  return std::abs(glm::dot(n, p) / glm::length(n));
}

Then, create a function to find the closest triangle to a point:

uint32_t findClosestTriangle(const glm::vec3& point, const std::vector<glm::vec3>& vertices, const std::vector<uint32_t>& indices) {
  uint32_t closest_triangle = 0;
  float min_distance = std::numeric_limits<float>::max();

  for (uint32_t i = 0; i < indices.size(); i += 3) {
    float dist = distancePointTriangle(point, vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]);
    if (dist < min_distance) {
      min_distance = dist;
      closest_triangle = i;
    }
  }
  return closest_triangle;
}

Next, create a function that calculates the distance between two points using Euclidean distance:

float euclideanDistance(const glm::vec3 &a, const glm::vec3 &b)
{
    return glm::length(a - b);
}

Now, create a function to select the subset of tracts:

std::vector<int> selectTractsSubset(int number_of_subset_tracts, const std::vector<std::vector<glm::vec3>> &tracts, const std::vector<glm::vec3> &vertices, const std::vector<uint32_t> &indices)
{
  std::vector<int> tracts_indices(tracts.size());
  std::iota(tracts_indices.begin(), tracts_indices.end(), 0);

  std::vector<int> selected_tracts;
  selected_tracts.reserve(number_of_subset_tracts);

  for (int selection_iter = 0; selection_iter < number_of_subset_tracts; ++selection_iter)
  {
    std::tuple<int, float, uint32_t, uint32_t> best_score_tract;

    float inf = std::numeric_limits<float>::infinity();
    float maxScore = -inf;
    int best_tract_index = -1;
    uint32_t best_start_triangle = 0;
    uint32_t best_end_triangle = 0;

    for (int i : tracts_indices)
    {
      auto &tract = tracts[i];
      uint32_t start_triangle = findClosestTriangle(tract.front(), vertices, indices);
      uint32_t end_triangle = findClosestTriangle(tract.back(), vertices, indices);

      float coverage_score = 0.0f;
      float dispersion_score = -inf;
      for (int sel : selected_tracts)
      {
        uint32_t sel_start_triangle = findClosestTriangle(tracts[sel].front(), vertices, indices);
        uint32_t sel_end_triangle = findClosestTriangle(tracts[sel].back(), vertices, indices);
        if (sel_start_triangle != start_triangle || sel_end_triangle != end_triangle)
        {
          coverage_score += 1.0f;
        }
        dispersion_score = std::max(dispersion_score, euclideanDistance(tracts[sel].front(), tract.front()) + euclideanDistance(tracts[sel].back(), tract.back()));
      }

      float total_score = coverage_score + dispersion_score;

      if (total_score > maxScore)
      {
        maxScore = total_score;
        best_tract_index = i;
        best_start_triangle = start_triangle;
        best_end_triangle = end_triangle;
      }
    }

    tracts_indices.erase(std::find(tracts_indices.begin(), tracts_indices.end(), best_tract_index));
    selected_tracts.push_back(best_tract_index);
  }

  return selected_tracts;
}

Finally, to initialize and test the algorithm, use:

int main()
{
    std::vector<glm::vec3> vertices;
    std::vector<uint32_t> indices;
    std::vector<std::vector<glm::vec3>> tracts;

    int requested_subset_size = 10;
    std::vector<int> selected_tracts = selectTractsSubset(requested_subset_size, tracts, vertices, indices);

    for (int i : selected_tracts)
    {
        std::cout << "Tract index: " << i << std::endl;
    }

    return 0;
}

Remember to replace the initialization of the vertices, indices, and tracts vectors with your actual data. The code above will return the indices of the subset of tracts that satisfy both criteria: maximizing the mesh coverage and the starting and ending points dispersion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment