Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
373b833
feat: create compute_oriented_bounding_box function in utils
DamienGilliard Mar 8, 2026
4e581a2
fix: exclude poits out of face from brep face loop in DFFace.from_bre…
DamienGilliard Mar 8, 2026
2325233
fix: slight refactor of .run method in JointDetector
DamienGilliard Mar 8, 2026
5f0cf7a
feat: add allow_curved_joint_faces parameter in joint detector and DF…
DamienGilliard Mar 8, 2026
f458f69
feat: add allow_curved_joint_faces parameter in DF_build_assembly com…
DamienGilliard Mar 8, 2026
e87984a
feat: align detected pose vector to DFBeam's (no vector reversing)
DamienGilliard Mar 8, 2026
6aae7c5
fix: coherent spacing fix (cleanup)
DamienGilliard Mar 8, 2026
9a74d41
fix: reversed x-y order in variable passing
DamienGilliard Mar 8, 2026
a999efe
feat: point association a bit more logical and simple
DamienGilliard Mar 8, 2026
08d0b8d
feat-fix: add extremely important brep.Faces.ShrinkFaces() in compone…
DamienGilliard Mar 9, 2026
14703e8
feat: add a compare_to_rh_plane() method to DFPose dataclass
DamienGilliard Mar 18, 2026
8e21f4d
feat-wip: add DFPoseResults dataclass
DamienGilliard Mar 18, 2026
420bd43
feat: add from_gh_tree method to DFPosesAssembly class
DamienGilliard Mar 19, 2026
07bbc9a
fix: complete docstring of from_gh_tree() method
DamienGilliard Mar 19, 2026
28548ca
feat-wip: csv exporter now accepts DFPoseResults object as i_result
DamienGilliard Mar 19, 2026
5d65302
feat: complement DFPoseResults with several methods, notabely compute…
DamienGilliard Mar 19, 2026
090244d
fix: undefined behaviour if gh tree has branchcout 1 in from_gh_tree()
DamienGilliard Mar 19, 2026
f115187
feat: add static method from_rh_plane to DFPose
DamienGilliard Mar 22, 2026
59a2a33
feat: expose poses detected at segmentation step
DamienGilliard Mar 22, 2026
e8c2670
feat: add input parameter i_poses_from_icp to accept poses detected a…
DamienGilliard Mar 22, 2026
544aab8
fix: add reset of pose assembly berfore recreating it from gh tree
DamienGilliard May 17, 2026
8136c3d
fix: Compact mesh face in the conversion from DFFace to Rhino.Geometr…
DamienGilliard May 17, 2026
777bda2
feat: update how poses are added to history in DF_pose_comparison
DamienGilliard May 17, 2026
eccaf03
fix: typo in description of return of DFBuildAssembly
DamienGilliard May 17, 2026
5bd68c8
feat: various small improvements on segmentation implementation (no c…
DamienGilliard May 17, 2026
efee4e5
fix: small cleanup of documentation
DamienGilliard May 17, 2026
08e4bf6
feat: parameter tweaking in DFPointCloud
DamienGilliard May 17, 2026
554f81c
fix: small simplification of IsPointOnFace method
DamienGilliard May 17, 2026
79f6758
fix: remove unused variable in DFMesh.cc
DamienGilliard Sep 7, 2026
6cac79d
fix: keep o_pose_from_icp always same size as df_beams
DamienGilliard Sep 7, 2026
66e5cf4
fix: improve component docu
DamienGilliard Sep 7, 2026
7ef4fc4
fix: specifically check for None data amd accept data == 0
DamienGilliard Sep 7, 2026
1f4c2d1
fix: defensive coding against none i_poses_from_icp
DamienGilliard Sep 7, 2026
fe5f1c6
fix: small defense against elements that might have no pose in history
DamienGilliard Sep 7, 2026
d79cd59
fix: explicitely import df_poses
DamienGilliard Sep 7, 2026
22119a8
fix: disable a check creating an open3d issues with newer versions of…
DamienGilliard Sep 7, 2026
c99c2e8
Merge branch 'feature/export_pose_csv' of https://github.com/diffChec…
DamienGilliard Sep 7, 2026
8a1c6a1
fix: disable a check that is not compatible with newer versions of MSVC
DamienGilliard Sep 8, 2026
09c4913
feat: small adaptation of cmake workflow to dll's are copied at build…
DamienGilliard Sep 8, 2026
610dc87
fix: avoid normalizing a null vector
DamienGilliard Sep 9, 2026
7ad3378
Merge pull request #186 from diffCheckOrg/feature/export_pose_csv
DamienGilliard Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ include(options)
# disabling warnings
if (MSVC)
add_compile_options(/wd4003)
# Disable _SECURE_SCL to avoid issues with stdext::checked_array_iterator
# in newer MSVC versions with Open3D's bundled fmt library
add_compile_definitions(_SECURE_SCL=0 _HAS_STDEXT_CHECKED_ARRAY_ITERATOR=0)
endif()

# check that the -DCMAKE_BUILD_TYPE is set
Expand Down
18 changes: 18 additions & 0 deletions cmake/copy_dlls_script.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This script is executed at build time to copy DLLs
# It uses variables passed from the main CMake configuration

# Remove old DLLs from the destination directory
file(GLOB old_dlls "${DIR_TO_CLEAN}/*.dll")
if(old_dlls)
file(REMOVE ${old_dlls})
endif()

# Get all DLLs from the source directory (evaluated at build time)
file(GLOB dll_files "${SRC_DIR}/*.dll")

# Copy each DLL to the destination directory
foreach(dll_file ${dll_files})
get_filename_component(dll_name ${dll_file} NAME)
message(STATUS "Copying ${dll_name} to ${DST_DIR}")
file(COPY ${dll_file} DESTINATION ${DST_DIR})
endforeach()
30 changes: 15 additions & 15 deletions cmake/external_tools.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -232,19 +232,19 @@ endfunction()

# ------------------------------------------------------------------------------
function (copy_dlls directory_to_copy_dlls post_build_target)
message (STATUS "Erasing old DLLs and copy new ones to ${directory_to_copy_dlls}")
file(GLOB files ${directory_to_copy_dlls}/*.dll)
foreach(file ${files})
message(STATUS "Removing ${file}")
file(REMOVE ${file})
endforeach()
file(GLOB files ${CMAKE_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE}/*.dll)
foreach(file ${files})
message(STATUS "Copying ${file} to ${directory_to_copy_dlls}")
add_custom_command(TARGET ${post_build_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${file}
${directory_to_copy_dlls}
)
endforeach()
message (STATUS "Configuring DLL copy to ${directory_to_copy_dlls} at build time")

# Get the path to the script relative to the project source dir
set(COPY_DLLS_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/copy_dlls_script.cmake)

# Add a post-build command that will copy DLLs at build time
# This ensures the file list is evaluated at build time, not configure time
add_custom_command(TARGET ${post_build_target} POST_BUILD
COMMAND ${CMAKE_COMMAND}
-DDIR_TO_CLEAN="${directory_to_copy_dlls}"
-DSRC_DIR="${CMAKE_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE}"
-DDST_DIR="${directory_to_copy_dlls}"
-P "${COPY_DLLS_SCRIPT}"
COMMENT "Copying DLLs to ${directory_to_copy_dlls}"
)
endfunction()
21 changes: 14 additions & 7 deletions src/diffCheck/geometry/DFMesh.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,12 @@ namespace diffCheck::geometry
Eigen::Vector3d v1 = this->Vertices[triangle[1]];
Eigen::Vector3d v2 = this->Vertices[triangle[2]];
Eigen::Vector3d n = (v1 - v0).cross(v2 - v0);
n.normalize();
double n2 = n.squaredNorm();

// Project the point onto the plane of the triangle
Eigen::Vector3d projectedPoint = point - n * (n.dot(point - v0));
// Handle degenerate triangle
if (n2 < 1e-20){continue;}// skip this triangle

Eigen::Vector3d projectedPoint = point - n * (n.dot(point - v0) / n2);

// Compute vectors
Eigen::Vector3d v0v1 = v1 - v0;
Expand All @@ -130,16 +132,21 @@ namespace diffCheck::geometry
double dot12 = v0v1.dot(v0p);

// create u,v isoparametric mapping to the triangle where (u,v) = (1,0) if projectedPoint = v2, (u,v) = (0,1) if projectedPoint = v1 and (u,v) = (0,0) if projectedPoint = v0
double invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
double denom = dot00 * dot11 - dot01 * dot01;
if (std::abs(denom) < 1e-20)
continue;

double invDenom = 1.0 / denom;
double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
double v = (dot00 * dot12 - dot01 * dot02) * invDenom;

// Check if point is in triangle
if ((u >= -associationThreshold / 100) && (v >= -associationThreshold / 100) && (u + v <= 1 + associationThreshold / 100))
double epsilon = 1e-6;

if ((u >= -epsilon) && (v >= -epsilon) && (u + v <= 1 + epsilon))
{
// Check if the point is close enough to the face
double maxProjectionDistance = associationThreshold * std::min({(v1 - v0).norm(), (v2 - v1).norm(), (v0 - v2).norm()}) ;
if ((projectedPoint - point).norm() < maxProjectionDistance)
if ((projectedPoint - point).squaredNorm() < associationThreshold * associationThreshold)
{
return true;
}
Expand Down
4 changes: 2 additions & 2 deletions src/diffCheck/geometry/DFPointCloud.cc
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ namespace diffCheck::geometry
}
for (auto &normal : O3DPointCloud->normals_)
{
if(normal.z() < -0.8)
if(normal.z() < -0.1)
{
normal = -normal;
}
Expand All @@ -173,7 +173,7 @@ namespace diffCheck::geometry
this->Normals.clear();
for (int i = 0; i < cilantroPointCloud->normals.cols(); i++)
{
if(cilantroPointCloud->normals.col(i).z() < -0.8)
if(cilantroPointCloud->normals.col(i).z() < -0.1)
{
cilantroPointCloud->normals.col(i) = -cilantroPointCloud->normals.col(i);
}
Expand Down
6 changes: 3 additions & 3 deletions src/diffCheck/geometry/DFPointCloud.hh
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ namespace diffCheck::geometry
*
* @param targetSize the target size of the cloud
*/

void DownsampleBySize(int targetSize);

/**
* @brief Get the tight bounding box of the point cloud
*
* @return std::vector<Eigen::Vector3d> A vector of two Eigen::Vector3d, with the first one being the minimum
* point and the second one the maximum point of the bounding box.
* @return std::vector<Eigen::Vector3d> A vector of eight Eigen::Vector3d, representing the corners of the bounding box.
* The order of the corners is as follows:
* /// ------- x
* /// /|
* /// / |
Expand Down
126 changes: 55 additions & 71 deletions src/diffCheck/segmentation/DFSegmentation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -255,25 +255,26 @@ namespace diffCheck::segmentation
}
for (auto segment : clusters)
{
Eigen::Vector3d segmentCenter;
Eigen::Vector3d segmentNormal;
Eigen::Vector3d segmentNormal = Eigen::Vector3d::Zero();

for (auto point : segment->Points){segmentCenter += point;}
if (segment->GetNumPoints() > 0)
{
segmentCenter /= segment->GetNumPoints();
}
else
if (segment->GetNumPoints() == 0)
{
DIFFCHECK_WARN("Empty segment. Skipping the segment.");
continue;
}
Eigen::Vector3d segmentCenter = segment->GetAxixAlignedBoundingBox()[0] + (segment->GetAxixAlignedBoundingBox()[1] - segment->GetAxixAlignedBoundingBox()[0])/2.0;

for (auto normal : segment->Normals){segmentNormal += normal;}
if (segmentNormal.norm() == 0)
{
DIFFCHECK_WARN("Segment normal is zero. Skipping the segment.");
continue;
}
segmentNormal.normalize();
double currentDistance = (faceCenter - segmentCenter).norm();
double currentDitanceOrthogonalToFace = std::abs((faceCenter - segmentCenter).dot(faceNormal));
double currentAngle = std::abs(sin(acos(faceNormal.dot(faceCenter - segmentCenter))));
if (std::abs(sin(acos(faceNormal.dot(segmentNormal)))) < angleThreshold && currentDitanceOrthogonalToFace < maximumFaceSegmentDistance && currentDitanceOrthogonalToFace < faceDistance)
if (std::abs(sin(acos(faceNormal.dot(segmentNormal)))) < angleThreshold
&& currentDitanceOrthogonalToFace < maximumFaceSegmentDistance
&& currentDitanceOrthogonalToFace < faceDistance)
{
correspondingSegment = segment;
faceDistance = currentDitanceOrthogonalToFace;
Expand All @@ -288,64 +289,47 @@ namespace diffCheck::segmentation
}
bool hasColors = correspondingSegment->GetNumColors() > 0;

for (Eigen::Vector3d point : correspondingSegment->Points)
std::vector<int> indicesToRemove;
for (size_t i = 0; i < correspondingSegment->Points.size(); i++)
{
const Eigen::Vector3d& point = correspondingSegment->Points[i];

if (discriminatePoints)
{
bool pointInFace = false;
if (face->IsPointOnFace(point, associationThreshold))
{
facePoints->Points.push_back(point);
facePoints->Normals.push_back(
correspondingSegment->Normals[std::distance(
correspondingSegment->Points.begin(),
std::find(correspondingSegment->Points.begin(),
correspondingSegment->Points.end(),
point))]
);
facePoints->Normals.push_back(correspondingSegment->Normals[i]);
if (hasColors)
{
facePoints->Colors.push_back(
correspondingSegment->Colors[std::distance(
correspondingSegment->Points.begin(),
std::find(correspondingSegment->Points.begin(),
correspondingSegment->Points.end(),
point))]
);
facePoints->Colors.push_back(correspondingSegment->Colors[i]);
}
indicesToRemove.push_back(i);
}
}
else
{
facePoints->Points.push_back(point);
facePoints->Normals.push_back(
correspondingSegment->Normals[std::distance(
correspondingSegment->Points.begin(),
std::find(correspondingSegment->Points.begin(),
correspondingSegment->Points.end(),
point))]
);
facePoints->Normals.push_back(correspondingSegment->Normals[i]);
if (hasColors)
{
facePoints->Colors.push_back(
correspondingSegment->Colors[std::distance(
correspondingSegment->Points.begin(),
std::find(correspondingSegment->Points.begin(),
correspondingSegment->Points.end(),
point))]
);
facePoints->Colors.push_back(correspondingSegment->Colors[i]);
}
indicesToRemove.push_back(i);
}
}

for(Eigen::Vector3d point : facePoints->Points)
for (auto it = indicesToRemove.rbegin(); it != indicesToRemove.rend(); ++it)
{
correspondingSegment->Points.erase(
std::remove(
correspondingSegment->Points.begin(),
correspondingSegment->Points.end(),
point),
correspondingSegment->Points.end());
int i = *it;

correspondingSegment->Points.erase(correspondingSegment->Points.begin() + i);
correspondingSegment->Normals.erase(correspondingSegment->Normals.begin() + i);

if (hasColors)
{
correspondingSegment->Colors.erase(correspondingSegment->Colors.begin() + i);
}
}
faceSegments.push_back(facePoints);
}
Expand Down Expand Up @@ -373,7 +357,7 @@ namespace diffCheck::segmentation
for (std::shared_ptr<geometry::DFPointCloud> cluster : unassociatedClusters)
{
std::shared_ptr<geometry::DFMesh> correspondingMeshFace;
Eigen::Vector3d clusterCenter;
Eigen::Vector3d clusterCenter = Eigen::Vector3d::Zero();
Eigen::Vector3d clusterNormal = Eigen::Vector3d::Zero();

if (cluster->GetNumPoints() == 0)
Expand All @@ -391,7 +375,7 @@ namespace diffCheck::segmentation
DIFFCHECK_WARN("No meshes to associate with the clusters. Skipping the cluster.");
continue;
}
for (Eigen::Vector3d point : cluster->Points)
for (const Eigen::Vector3d& point : cluster->Points)
{
clusterCenter += point;
}
Expand Down Expand Up @@ -435,7 +419,9 @@ namespace diffCheck::segmentation
double currentDistance = (center - clusterCenter).norm() ;
double adaptedDistance = currentDistance * std::abs(dotProduct);

if (std::abs(dotProduct) < angleThreshold && adaptedDistance < distance && currentDistance < (max - min).norm()*associationThreshold)
if (std::abs(dotProduct) < angleThreshold
&& adaptedDistance < distance
&& currentDistance < (max - min).norm()*associationThreshold)
{
goodMeshIndex = meshIndex;
goodFaceIndex = faceIndex;
Expand Down Expand Up @@ -465,11 +451,13 @@ namespace diffCheck::segmentation

double dotProduct = clusterNormal.dot((clusterCenter - faceCenter).normalized());
dotProduct = std::max(-1.0, std::min(1.0, dotProduct));
double clusterNormalToJunctionLineAngle = std::acos(dotProduct);

double currentDistance = (clusterCenter - faceCenter).norm() * std::abs(std::cos(clusterNormalToJunctionLineAngle))
/ std::min(std::abs(clusterNormal.dot(faceNormal)), 0.05) ;
if (std::abs(sin(acos(faceNormal.dot(clusterNormal)))) < angleThreshold && currentDistance < maximumFaceSegmentDistance && currentDistance * (std::abs(faceNormal.dot((faceCenter - clusterCenter) / (faceCenter - clusterCenter).norm()))) < distance)
double anglePenalty = 100*std::abs(clusterNormal.dot(faceNormal));
double currentDistance = (clusterCenter - faceCenter).norm() * (.1 + std::abs(dotProduct)) / std::max(anglePenalty, 1.0);
double normalAlignment = std::abs(faceNormal.dot(clusterNormal));
if (std::abs(std::sqrt(1.0 - normalAlignment * normalAlignment)) < angleThreshold
&& currentDistance < maximumFaceSegmentDistance
&& currentDistance < distance)
{
goodMeshIndex = meshIndex;
goodFaceIndex = faceIndex;
Expand All @@ -494,13 +482,16 @@ namespace diffCheck::segmentation
}
std::shared_ptr<geometry::DFPointCloud> completed_segment = existingPointCloudSegments[goodMeshIndex][goodFaceIndex];

for (Eigen::Vector3d point : cluster->Points)
std::vector<int> indicesToRemove;
for (size_t i = 0; i < cluster->Points.size(); i++)
{
const Eigen::Vector3d& point = cluster->Points[i];
if(isCylinder)
{
completed_segment->Points.push_back(point);
completed_segment->Normals.push_back(cluster->Normals[std::distance(cluster->Points.begin(), std::find(cluster->Points.begin(), cluster->Points.end(), point))]);
completed_segment->Colors.push_back(cluster->Colors[std::distance(cluster->Points.begin(), std::find(cluster->Points.begin(), cluster->Points.end(), point))]);
completed_segment->Normals.push_back(cluster->Normals[i]);
completed_segment->Colors.push_back(cluster->Colors[i]);
indicesToRemove.push_back(i);
}
else
{
Expand All @@ -509,27 +500,20 @@ namespace diffCheck::segmentation
if (correspondingMeshFace->IsPointOnFace(point, associationThreshold))
{
completed_segment->Points.push_back(point);
completed_segment->Normals.push_back(cluster->Normals[std::distance(cluster->Points.begin(), std::find(cluster->Points.begin(), cluster->Points.end(), point))]);
completed_segment->Colors.push_back(cluster->Colors[std::distance(cluster->Points.begin(), std::find(cluster->Points.begin(), cluster->Points.end(), point))]);
completed_segment->Normals.push_back(cluster->Normals[i]);
completed_segment->Colors.push_back(cluster->Colors[i]);
indicesToRemove.push_back(i);
}
}
else
{
completed_segment->Points.push_back(point);
completed_segment->Normals.push_back(cluster->Normals[std::distance(cluster->Points.begin(), std::find(cluster->Points.begin(), cluster->Points.end(), point))]);
completed_segment->Colors.push_back(cluster->Colors[std::distance(cluster->Points.begin(), std::find(cluster->Points.begin(), cluster->Points.end(), point))]);
completed_segment->Normals.push_back(cluster->Normals[i]);
completed_segment->Colors.push_back(cluster->Colors[i]);
indicesToRemove.push_back(i);
}
}
}
std::vector<int> indicesToRemove;

for (int i = 0; i < cluster->Points.size(); ++i)
{
if (std::find(completed_segment->Points.begin(), completed_segment->Points.end(), cluster->Points[i]) != completed_segment->Points.end())
{
indicesToRemove.push_back(i);
}
}
for (auto it = indicesToRemove.rbegin(); it != indicesToRemove.rend(); ++it)
{
std::swap(cluster->Points[*it], cluster->Points.back());
Expand Down
Loading
Loading