diff --git a/include/viennals/lsCalculateVisibilities.hpp b/include/viennals/lsCalculateVisibilities.hpp new file mode 100644 index 00000000..a1fea5c5 --- /dev/null +++ b/include/viennals/lsCalculateVisibilities.hpp @@ -0,0 +1,162 @@ +#pragma once + +#include +#include + +namespace viennals { +using namespace viennacore; + +template class CalculateVisibilities { + SmartPointer> levelSet; + Vec3D direction; + const NumericType epsilon = static_cast(1e-6); + const std::string visibilitiesLabel; + +public: + CalculateVisibilities( + const SmartPointer> &passedLevelSet, + const Vec3D passedDirection, + const std::string label = "Visibilities") + : levelSet(passedLevelSet), direction(passedDirection), + visibilitiesLabel(std::move(label)) {} + + void apply() { + + auto &domain = levelSet->getDomain(); + auto &grid = levelSet->getGrid(); + + // *** Determine extents of domain *** + Vec3D minDefinedPoint; + Vec3D maxDefinedPoint; + // Initialize with extreme values + for (int i = 0; i < D; ++i) { + minDefinedPoint[i] = std::numeric_limits::max(); + maxDefinedPoint[i] = std::numeric_limits::lowest(); + } + // Iterate through all defined points in the domain + for (hrleSparseIterator::DomainType> it( + domain); + !it.isFinished(); it.next()) { + if (!it.isDefined()) + continue; // Skip undefined points + + // Get the coordinate of the current point + auto point = it.getStartIndices(); + for (int i = 0; i < D; ++i) { + // Compare to update min and max defined points + NumericType coord = point[i]; // * grid.getGridDelta(); + minDefinedPoint[i] = std::min(minDefinedPoint[i], coord); + maxDefinedPoint[i] = std::max(maxDefinedPoint[i], coord); + } + } + //**************************** + + // Invert the vector + auto dir = Normalize(Inv(direction)) * + static_cast(domain.getGrid().getGridDelta()); + + auto numDefinedPoints = domain.getNumberOfPoints(); + std::vector visibilities(numDefinedPoints); + +#pragma omp parallel num_threads(levelSet->getNumberOfSegments()) + { + int p = 0; +#ifdef _OPENMP + p = omp_get_thread_num(); +#endif + + hrleVectorType startVector = + (p == 0) ? grid.getMinGridPoint() : domain.getSegmentation()[p - 1]; + + hrleVectorType endVector = + (p != static_cast(domain.getNumberOfSegments() - 1)) + ? domain.getSegmentation()[p] + : grid.incrementIndices(grid.getMaxGridPoint()); + + // Calculate the starting index for the visibility vector + hrleSizeType id = 0; + for (int i = 0; i < p; ++i) { + id += domain.getDomainSegment(i).getNumberOfPoints(); + } + + for (hrleSparseIterator::DomainType> it( + domain, startVector); + it.getStartIndices() < endVector; ++it) { + + if (!it.isDefined()) + continue; + + // Starting position of the point + Vec3D currentPos; + for (int i = 0; i < D; ++i) { + currentPos[i] = it.getStartIndices(i); + } + + // Start tracing the ray + NumericType minLevelSetValue = + it.getValue(); // Starting level set value + Vec3D rayPos = currentPos; + bool visibility = true; + + while (1) { + // Update the ray position + for (int i = 0; i < D; ++i) { + rayPos[i] += dir[i]; + } + + // Determine the nearest grid cell (round to nearest index) + Vec3D nearestCell; + for (int i = 0; i < D; ++i) { + nearestCell[i] = static_cast(rayPos[i]); + } + + // // Before adding a cell, check if it's already visited + // if (std::find(visitedCells.begin(), visitedCells.end(), + // nearestCell) + // == visitedCells.end()) { + // visitedCells.push_back(nearestCell); + // } + + // Check if the nearest cell is within bounds + bool outOfBounds = false; + for (int i = 0; i < D; ++i) { + if (nearestCell[i] < minDefinedPoint[i] || + nearestCell[i] > maxDefinedPoint[i]) { + outOfBounds = true; + break; + } + } + + if (outOfBounds) { + break; // Ray is outside the grid + } + + // Access the level set value at the nearest cell + NumericType neighborValue = std::numeric_limits::max(); + hrleSparseIterator::DomainType> + neighborIt(domain); + neighborIt.goToIndices(nearestCell); + neighborValue = neighborIt.getValue(); + + // Update the minimum value encountered + if (neighborValue < minLevelSetValue) { + visibility = false; + break; + } + } + + // Update visibility for this point + visibilities[id++] = visibility ? 1.0 : 0.0; + } + } + + auto &pointData = levelSet->getPointData(); + // delete if already exists + if (int i = pointData.getScalarDataIndex(visibilitiesLabel); i != -1) { + pointData.eraseScalarData(i); + } + pointData.insertNextScalarData(visibilities, visibilitiesLabel); + } +}; + +} // namespace viennals \ No newline at end of file diff --git a/tests/Visibility/CMakeLists.txt b/tests/Visibility/CMakeLists.txt new file mode 100644 index 00000000..3d61b659 --- /dev/null +++ b/tests/Visibility/CMakeLists.txt @@ -0,0 +1,7 @@ +project(Visibility LANGUAGES CXX) + +add_executable(${PROJECT_NAME} "${PROJECT_NAME}.cpp") +target_link_libraries(${PROJECT_NAME} PRIVATE ViennaLS) + +add_dependencies(ViennaLS_Tests ${PROJECT_NAME}) +add_test(NAME ${PROJECT_NAME} COMMAND $) diff --git a/tests/Visibility/Visibility.cpp b/tests/Visibility/Visibility.cpp new file mode 100644 index 00000000..7624aaf5 --- /dev/null +++ b/tests/Visibility/Visibility.cpp @@ -0,0 +1,38 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace ls = viennals; + +int main() { + + constexpr int D = 2; + + double gridDelta = 0.4; + + auto sphere1 = ls::SmartPointer>::New( + gridDelta); //, boundaryCons); + + double origin[3] = {5., 0., 0.}; + double radius = 7.3; + + ls::MakeGeometry( + sphere1, ls::SmartPointer>::New(origin, radius)) + .apply(); + + ls::Expand(sphere1, 5).apply(); + ls::CalculateVisibilities(sphere1, ls::Vec3D{0., -1.0, 0.}) + .apply(); + + auto mesh = ls::SmartPointer>::New(); + ls::ToMesh(sphere1, mesh).apply(); + ls::VTKWriter(mesh, "visibility_test.vtp").apply(); + + return 0; +}