Program Listing for File streamlines_ops.h

Return to documentation for file (include/trx/streamlines_ops.h)

#ifndef TRX_STREAMLINES_OPS_H
#define TRX_STREAMLINES_OPS_H

#include <Eigen/Core>

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <functional>
#include <numeric>
#include <string>
#include <unordered_map>
#include <vector>

namespace trx {
constexpr int kMinNbPoints = 5;

inline float round_to_precision(float value, int precision) {
  if (precision < 0) {
    return value;
  }
  const float scale = std::pow(10.0f, static_cast<float>(precision));
  return std::round(value * scale) / scale;
}

inline std::string get_streamline_key(const Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor> &streamline,
                                      int precision = -1) {
  const Eigen::Index rows = streamline.rows();
  std::vector<float> key_data;
  if (rows < kMinNbPoints) {
    key_data.reserve(static_cast<size_t>(rows * 3));
    for (Eigen::Index i = 0; i < rows; ++i) {
      for (Eigen::Index j = 0; j < 3; ++j) {
        key_data.push_back(round_to_precision(streamline(i, j), precision));
      }
    }
  } else {
    key_data.reserve(static_cast<size_t>(kMinNbPoints * 2 * 3));
    for (int i = 0; i < kMinNbPoints; ++i) {
      for (int j = 0; j < 3; ++j) {
        key_data.push_back(round_to_precision(streamline(i, j), precision));
      }
    }
    for (Eigen::Index i = rows - kMinNbPoints; i < rows; ++i) {
      for (int j = 0; j < 3; ++j) {
        key_data.push_back(round_to_precision(streamline(i, j), precision));
      }
    }
  }

  std::string key;
  key.resize(key_data.size() * sizeof(float));
  if (!key.empty()) {
    std::memcpy(key.data(), key_data.data(), key.size());
  }
  return key;
}

using StreamlineKeyMap = std::unordered_map<std::string, uint32_t>;

inline StreamlineKeyMap
hash_streamlines(const std::vector<Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor>> &streamlines,
                 uint32_t start_index = 0,
                 int precision = -1) {
  StreamlineKeyMap result;
  result.reserve(streamlines.size());
  for (size_t i = 0; i < streamlines.size(); ++i) {
    const auto key = get_streamline_key(streamlines[i], precision);
    result[key] = static_cast<uint32_t>(start_index + i);
  }
  return result;
}

inline StreamlineKeyMap intersection(const StreamlineKeyMap &left, const StreamlineKeyMap &right) {
  StreamlineKeyMap result;
  for (const auto &item : left) {
    if (right.find(item.first) != right.end()) {
      result[item.first] = item.second;
    }
  }
  return result;
}

inline StreamlineKeyMap difference(const StreamlineKeyMap &left, const StreamlineKeyMap &right) {
  StreamlineKeyMap result;
  for (const auto &item : left) {
    if (right.find(item.first) == right.end()) {
      result[item.first] = item.second;
    }
  }
  return result;
}

inline StreamlineKeyMap union_maps(const StreamlineKeyMap &left, const StreamlineKeyMap &right) {
  StreamlineKeyMap result = right;
  for (const auto &item : left) {
    result[item.first] = item.second;
  }
  return result;
}

inline std::pair<std::vector<Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor>>, std::vector<uint32_t>>
perform_streamlines_operation(
    const std::function<StreamlineKeyMap(const StreamlineKeyMap &, const StreamlineKeyMap &)> &operation,
    const std::vector<std::vector<Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor>>> &streamlines,
    int precision = 0) {
  std::vector<uint32_t> offsets;
  offsets.reserve(streamlines.size());
  uint32_t acc = 0;
  for (size_t i = 0; i < streamlines.size(); ++i) {
    offsets.push_back(acc);
    acc += static_cast<uint32_t>(streamlines[i].size());
  }

  std::vector<StreamlineKeyMap> hashes;
  hashes.reserve(streamlines.size());
  for (size_t i = 0; i < streamlines.size(); ++i) {
    hashes.push_back(hash_streamlines(streamlines[i], offsets[i], precision));
  }

  if (hashes.empty()) {
    return {{}, {}};
  }

  StreamlineKeyMap to_keep = hashes[0];
  for (size_t i = 1; i < hashes.size(); ++i) {
    to_keep = operation(to_keep, hashes[i]);
  }

  std::vector<Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor>> all_streamlines;
  for (const auto &group : streamlines) {
    all_streamlines.insert(all_streamlines.end(), group.begin(), group.end());
  }

  std::vector<uint32_t> indices;
  indices.reserve(to_keep.size());
  for (const auto &item : to_keep) {
    indices.push_back(item.second);
  }
  std::sort(indices.begin(), indices.end());

  std::vector<Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor>> output;
  output.reserve(indices.size());
  for (uint32_t idx : indices) {
    if (idx < all_streamlines.size()) {
      output.push_back(all_streamlines[idx]);
    }
  }

  return {output, indices};
}
} // namespace trx

#endif