Skip to content

Commit

Permalink
ENH: Refactors the calculation of triangle normals and areas into uti…
Browse files Browse the repository at this point in the history
…lity functions (#1142)

Refactors the calculation of triangle normals and areas into utility functions

Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson committed Nov 28, 2024
1 parent f36ca6c commit 093c79f
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,17 @@
#include "simplnx/Parameters/DataGroupSelectionParameter.hpp"
#include "simplnx/Parameters/DataObjectNameParameter.hpp"
#include "simplnx/Parameters/GeometrySelectionParameter.hpp"
#include "simplnx/Utilities/GeometryUtilities.hpp"
#include "simplnx/Utilities/Math/MatrixMath.hpp"

#include "simplnx/Utilities/SIMPLConversion.hpp"

#include "simplnx/Utilities/ParallelDataAlgorithm.hpp"
#include "simplnx/Utilities/SIMPLConversion.hpp"

using namespace nx::core;

namespace
{
constexpr nx::core::int32 k_MissingFeatureAttributeMatrix = -75769;

/**
* @brief The CalculateAreasImpl class implements a threaded algorithm that computes the area of each
* triangle for a set of triangles
*/
class CalculateAreasImpl
{
public:
CalculateAreasImpl(const TriangleGeom* triangleGeom, Float64AbstractDataStore& areas, const std::atomic_bool& shouldCancel)
: m_TriangleGeom(triangleGeom)
, m_Areas(areas)
, m_ShouldCancel(shouldCancel)
{
}
virtual ~CalculateAreasImpl() = default;

void convert(size_t start, size_t end) const
{
std::array<float, 3> cross = {0.0f, 0.0f, 0.0f};
for(size_t triangleIndex = start; triangleIndex < end; triangleIndex++)
{
if(m_ShouldCancel)
{
break;
}
std::array<Point3Df, 3> vertCoords;
m_TriangleGeom->getFaceCoordinates(triangleIndex, vertCoords);

auto vecA = (vertCoords[0] - vertCoords[1]).toArray();
auto vecB = (vertCoords[0] - vertCoords[2]).toArray();

MatrixMath::CrossProduct(vecA.data(), vecB.data(), cross.data());

m_Areas[triangleIndex] = 0.5F * MatrixMath::Magnitude3x1(cross.data());
}
}

void operator()(const Range& range) const
{
convert(range.min(), range.max());
}

private:
const TriangleGeom* m_TriangleGeom = nullptr;
Float64AbstractDataStore& m_Areas;
const std::atomic_bool& m_ShouldCancel;
};
} // namespace

namespace nx::core
Expand Down Expand Up @@ -173,12 +126,7 @@ Result<> ComputeTriangleAreasFilter::executeImpl(DataStructure& dataStructure, c
DataPath pCalculatedAreasDataPath = pTriangleGeometryDataPath.createChildPath(faceAttributeMatrix->getName()).createChildPath(pCalculatedAreasName);
auto& faceAreas = dataStructure.getDataAs<Float64Array>(pCalculatedAreasDataPath)->getDataStoreRef();

// Parallel algorithm to find duplicate nodes
ParallelDataAlgorithm dataAlg;
dataAlg.setRange(0ULL, static_cast<size_t>(triangleGeom->getNumberOfFaces()));
dataAlg.execute(CalculateAreasImpl(triangleGeom, faceAreas, shouldCancel));

return {};
return nx::core::GeometryUtilities::ComputeTriangleAreas(triangleGeom, faceAreas, shouldCancel);
}

namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "simplnx/Parameters/DataGroupSelectionParameter.hpp"
#include "simplnx/Parameters/DataObjectNameParameter.hpp"
#include "simplnx/Parameters/GeometrySelectionParameter.hpp"
#include "simplnx/Utilities/GeometryUtilities.hpp"
#include "simplnx/Utilities/Math/MatrixMath.hpp"
#include "simplnx/Utilities/ParallelDataAlgorithm.hpp"
#include "simplnx/Utilities/SIMPLConversion.hpp"
Expand All @@ -18,56 +19,6 @@ namespace

constexpr nx::core::int32 k_MissingFeatureAttributeMatrix = -75969;

/**
* @brief The CalculateAreasImpl class implements a threaded algorithm that computes the normal of each
* triangle for a set of triangles
*/
class CalculateNormalsImpl
{
public:
CalculateNormalsImpl(const TriangleGeom* triangleGeom, Float64AbstractDataStore& normals, const std::atomic_bool& shouldCancel)
: m_TriangleGeom(triangleGeom)
, m_Normals(normals)
, m_ShouldCancel(shouldCancel)
{
}
virtual ~CalculateNormalsImpl() = default;

void generate(size_t start, size_t end) const
{
std::array<float32, 3> normal = {0.0f, 0.0f, 0.0f};
for(size_t triangleIndex = start; triangleIndex < end; triangleIndex++)
{

if(m_ShouldCancel)
{
break;
}
std::array<Point3Df, 3> vertCoords;
m_TriangleGeom->getFaceCoordinates(triangleIndex, vertCoords);

auto vecA = (vertCoords[1] - vertCoords[0]).toArray();
auto vecB = (vertCoords[2] - vertCoords[0]).toArray();

MatrixMath::CrossProduct(vecA.data(), vecB.data(), normal.data());
MatrixMath::Normalize3x1(normal.data());

m_Normals[triangleIndex * 3] = static_cast<float64>(normal[0]);
m_Normals[triangleIndex * 3 + 1] = static_cast<float64>(normal[1]);
m_Normals[triangleIndex * 3 + 2] = static_cast<float64>(normal[2]);
}
}

void operator()(const Range& range) const
{
generate(range.min(), range.max());
}

private:
const TriangleGeom* m_TriangleGeom = nullptr;
Float64AbstractDataStore& m_Normals;
const std::atomic_bool& m_ShouldCancel;
};
} // namespace

namespace nx::core
Expand Down Expand Up @@ -172,12 +123,7 @@ Result<> TriangleNormalFilter::executeImpl(DataStructure& dataStructure, const A
DataPath pNormalsArrayPath = pTriangleGeometryDataPath.createChildPath(faceAttributeMatrix->getName()).createChildPath(pNormalsName);
auto& normals = dataStructure.getDataAs<Float64Array>(pNormalsArrayPath)->getDataStoreRef();

// Parallel algorithm to find duplicate nodes
ParallelDataAlgorithm dataAlg;
dataAlg.setRange(0ULL, static_cast<size_t>(triangleGeom->getNumberOfFaces()));
dataAlg.execute(CalculateNormalsImpl(triangleGeom, normals, shouldCancel));

return {};
return nx::core::GeometryUtilities::ComputeTriangleNormals(triangleGeom, normals, shouldCancel);
}

namespace
Expand Down
10 changes: 5 additions & 5 deletions src/Plugins/SimplnxCore/test/TriangleNormalFilterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ TEST_CASE("SimplnxCore::TriangleNormalFilter", "[SimplnxCore][TriangleNormalFilt
auto executeResult = filter.execute(dataStructure, args);
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result);

// Write the DataStructure out to the file system
#ifdef SIMPLNX_WRITE_TEST_OUTPUT
nx::core::UnitTest::WriteTestDataStructure(dataStructure, fs::path(fmt::format("{}/TriangleNormals.dream3d", unit_test::k_BinaryTestOutputDir)));
#endif

DataPath triangleNormalsDataPath = geometryPath.createChildPath(triangleFaceDataGroupName).createChildPath(triangleNormalsName);

// Let's compare the normals.
Expand All @@ -91,9 +96,4 @@ TEST_CASE("SimplnxCore::TriangleNormalFilter", "[SimplnxCore][TriangleNormalFilt
REQUIRE(result < ::k_max_difference);
}
}

// Write the DataStructure out to the file system
#ifdef SIMPLNX_WRITE_TEST_OUTPUT
WriteTestDataStructure(dataStructure, fs::path(fmt::format("{}/TriangleNormals.dream3d", unit_test::k_BinaryTestOutputDir)));
#endif
}
108 changes: 108 additions & 0 deletions src/simplnx/Utilities/GeometryUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "simplnx/Common/Array.hpp"
#include "simplnx/Common/Result.hpp"
#include "simplnx/Utilities/Math/MatrixMath.hpp"

using namespace nx::core;

Expand Down Expand Up @@ -142,3 +143,110 @@ Result<FloatVec3> GeometryUtilities::CalculatePartitionLengthsOfBoundingBox(cons
FloatVec3 lengthPerPartition = {lengthX, lengthY, lengthZ};
return Result<FloatVec3>{lengthPerPartition};
}

/**
* @brief The ComputeTriangleAreasImpl class implements a threaded algorithm that computes the area of each
* triangle for a set of triangles
*/
class ComputeTriangleAreasImpl
{
public:
ComputeTriangleAreasImpl(const TriangleGeom* triangleGeom, Float64AbstractDataStore& areas, const std::atomic_bool& shouldCancel)
: m_TriangleGeom(triangleGeom)
, m_Areas(areas)
, m_ShouldCancel(shouldCancel)
{
}
virtual ~ComputeTriangleAreasImpl() = default;

void convert(size_t start, size_t end) const
{
std::array<float, 3> cross = {0.0f, 0.0f, 0.0f};
for(size_t triangleIndex = start; triangleIndex < end; triangleIndex++)
{
if(m_ShouldCancel)
{
break;
}
std::array<Point3Df, 3> vertCoords;
m_TriangleGeom->getFaceCoordinates(triangleIndex, vertCoords);
m_Areas[triangleIndex] = 0.5F * (vertCoords[0] - vertCoords[1]).cross(vertCoords[0] - vertCoords[2]).magnitude();
}
}

void operator()(const Range& range) const
{
convert(range.min(), range.max());
}

private:
const TriangleGeom* m_TriangleGeom = nullptr;
Float64AbstractDataStore& m_Areas;
const std::atomic_bool& m_ShouldCancel;
};

Result<> GeometryUtilities::ComputeTriangleAreas(const nx::core::TriangleGeom* triangleGeom, Float64AbstractDataStore& faceAreas, const std::atomic_bool& shouldCancel)
{
// Parallel algorithm to find duplicate nodes
ParallelDataAlgorithm dataAlg;
dataAlg.setRange(0ULL, static_cast<size_t>(triangleGeom->getNumberOfFaces()));
dataAlg.execute(ComputeTriangleAreasImpl(triangleGeom, faceAreas, shouldCancel));

return {};
}

/**
* @brief The CalculateAreasImpl class implements a threaded algorithm that computes the normal of each
* triangle for a set of triangles
*/
class CalculateNormalsImpl
{
public:
CalculateNormalsImpl(const TriangleGeom* triangleGeom, Float64AbstractDataStore& normals, const std::atomic_bool& shouldCancel)
: m_TriangleGeom(triangleGeom)
, m_Normals(normals)
, m_ShouldCancel(shouldCancel)
{
}
virtual ~CalculateNormalsImpl() = default;

void generate(size_t start, size_t end) const
{
for(size_t triangleIndex = start; triangleIndex < end; triangleIndex++)
{
if(m_ShouldCancel)
{
break;
}
std::array<Point3Df, 3> vertCoords;
m_TriangleGeom->getFaceCoordinates(triangleIndex, vertCoords);

auto normal = (vertCoords[1] - vertCoords[0]).cross(vertCoords[2] - vertCoords[0]);
normal = normal / normal.magnitude();

m_Normals[triangleIndex * 3] = static_cast<float64>(normal[0]);
m_Normals[triangleIndex * 3 + 1] = static_cast<float64>(normal[1]);
m_Normals[triangleIndex * 3 + 2] = static_cast<float64>(normal[2]);
}
}

void operator()(const Range& range) const
{
generate(range.min(), range.max());
}

private:
const TriangleGeom* m_TriangleGeom = nullptr;
Float64AbstractDataStore& m_Normals;
const std::atomic_bool& m_ShouldCancel;
};

Result<> GeometryUtilities::ComputeTriangleNormals(const nx::core::TriangleGeom* triangleGeom, Float64AbstractDataStore& normals, const std::atomic_bool& shouldCancel)
{
// Parallel algorithm to find duplicate nodes
ParallelDataAlgorithm dataAlg;
dataAlg.setRange(0ULL, static_cast<size_t>(triangleGeom->getNumberOfFaces()));
dataAlg.execute(CalculateNormalsImpl(triangleGeom, normals, shouldCancel));

return {};
}
20 changes: 20 additions & 0 deletions src/simplnx/Utilities/GeometryUtilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "simplnx/DataStructure/Geometry/INodeGeometry3D.hpp"
#include "simplnx/DataStructure/Geometry/ImageGeom.hpp"
#include "simplnx/DataStructure/Geometry/RectGridGeom.hpp"
#include "simplnx/DataStructure/Geometry/TriangleGeom.hpp"
#include "simplnx/Filter/IFilter.hpp"
#include "simplnx/Utilities/ParallelDataAlgorithm.hpp"

Expand Down Expand Up @@ -251,4 +252,23 @@ Result<> EliminateDuplicateNodes(GeometryType& geom, std::optional<float32> scal

return {};
}

/**
* @brief This will compute, in parallel, the area of each triangle in a triangle geometry
* @param triangleGeom
* @param faceAreas
* @param shouldCancel
* @return
*/
SIMPLNX_EXPORT Result<> ComputeTriangleAreas(const nx::core::TriangleGeom* triangleGeom, Float64AbstractDataStore& faceAreas, const std::atomic_bool& shouldCancel);

/**
* @brief This will compute, in parallel, the normal of each triangle in a triangle geometry
* @param triangleGeom
* @param normals
* @param shouldCancel
* @return
*/
SIMPLNX_EXPORT Result<> ComputeTriangleNormals(const nx::core::TriangleGeom* triangleGeom, Float64AbstractDataStore& normals, const std::atomic_bool& shouldCancel);

} // namespace nx::core::GeometryUtilities

0 comments on commit 093c79f

Please sign in to comment.