Skip to content

Commit 4ac9959

Browse files
committed
Merge branch 'release/2.0.0' into server_improvements
2 parents 68ef5c0 + 84a5d21 commit 4ac9959

File tree

8 files changed

+81
-21
lines changed

8 files changed

+81
-21
lines changed

src/diffCheck/geometry/DFPointCloud.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,14 @@ namespace diffCheck::geometry
155155
DIFFCHECK_INFO("Default estimation of normals with knn = 30");
156156
}
157157
for (auto &normal : O3DPointCloud->normals_)
158+
{
159+
if(normal.z() < -0.8)
160+
{
161+
normal = -normal;
162+
}
158163
this->Normals.push_back(normal);
164+
}
165+
159166
}
160167
else
161168
{
@@ -165,7 +172,13 @@ namespace diffCheck::geometry
165172

166173
this->Normals.clear();
167174
for (int i = 0; i < cilantroPointCloud->normals.cols(); i++)
175+
{
176+
if(cilantroPointCloud->normals.col(i).z() < -0.8)
177+
{
178+
cilantroPointCloud->normals.col(i) = -cilantroPointCloud->normals.col(i);
179+
}
168180
this->Normals.push_back(cilantroPointCloud->normals.col(i).cast<double>());
181+
}
169182
DIFFCHECK_INFO(("Estimating normals with cilantro evaluator with knn = " + std::to_string(knn.value())).c_str());
170183
}
171184

src/diffCheck/segmentation/DFSegmentation.cc

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ namespace diffCheck::segmentation
100100
std::vector<std::shared_ptr<geometry::DFMesh>> referenceMesh,
101101
std::vector<std::shared_ptr<geometry::DFPointCloud>> &clusters,
102102
double angleThreshold,
103-
double associationThreshold)
103+
double associationThreshold,
104+
double angleAssociationThreshold)
104105
{
105106
std::vector<std::shared_ptr<geometry::DFPointCloud>> faceSegments = std::vector<std::shared_ptr<geometry::DFPointCloud>>();
106107

@@ -271,10 +272,10 @@ namespace diffCheck::segmentation
271272
double currentDistance = (faceCenter - segmentCenter).norm();
272273
double currentAngle = std::abs(sin(acos(faceNormal.dot(faceCenter - segmentCenter))));
273274
// if the distance is smaller than the previous one, update the distance and the corresponding segment
274-
if (std::abs(sin(acos(faceNormal.dot(segmentNormal)))) < angleThreshold && currentDistance < faceDistance && std::abs(1 - currentAngle) < angleThreshold)
275+
if (std::abs(sin(acos(faceNormal.dot(segmentNormal)))) < angleThreshold && currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - segmentCenter) / (faceCenter - segmentCenter).norm()))) < faceDistance)
275276
{
276277
correspondingSegment = segment;
277-
faceDistance = currentDistance;
278+
faceDistance = currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - segmentCenter) / (faceCenter - segmentCenter).norm())));
278279
}
279280
}
280281

@@ -333,7 +334,8 @@ namespace diffCheck::segmentation
333334
std::vector<std::vector<std::shared_ptr<geometry::DFPointCloud>>> &existingPointCloudSegments,
334335
std::vector<std::vector<std::shared_ptr<geometry::DFMesh>>> meshes,
335336
double angleThreshold,
336-
double associationThreshold)
337+
double associationThreshold,
338+
double angleAssociationThreshold)
337339
{
338340
if (unassociatedClusters.size() == 0)
339341
{
@@ -441,7 +443,7 @@ namespace diffCheck::segmentation
441443

442444
double currentDistance = (clusterCenter - faceCenter).norm() * std::abs(std::cos(clusterNormalToJunctionLineAngle))
443445
/ std::min(std::abs(clusterNormal.dot(faceNormal)), 0.05) ;
444-
if (std::abs(sin(acos(faceNormal.dot(clusterNormal)))) < angleThreshold && currentDistance < distance && std::abs(1 - std::sin(clusterNormalToJunctionLineAngle)) < associationThreshold)
446+
if (std::abs(sin(acos(faceNormal.dot(clusterNormal)))) < angleThreshold && currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - clusterCenter) / (faceCenter - clusterCenter).norm()))) < distance)
445447
{
446448
goodMeshIndex = meshIndex;
447449
goodFaceIndex = faceIndex;

src/diffCheck/segmentation/DFSegmentation.hh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,34 @@ namespace diffCheck::segmentation
3232
* @param clusters the vector of clusters from cilantro to associate with the mesh faces of the reference mesh
3333
* @param angleThreshold the threshold to consider the a cluster as potential candidate for association. the value passed is the minimum sine of the angles. A value of 0 requires perfect alignment (angle = 0), while a value of 0.1 allows an angle of 5.7 degrees.
3434
* @param associationThreshold the threshold to consider the points of a segment and a mesh face as associable. It is the ratio between the surface of the closest mesh triangle and the sum of the areas of the three triangles that form the rest of the pyramid described by the mesh triangle and the point we want to associate or not. The lower the number, the more strict the association will be and some poinnts on the mesh face might be wrongfully excluded.
35+
* @param angleAssociationThreshold a number to indicate how much distance in the plane of the face should be favored, compared to distance orthogonal to the face normal. If set to 0, any face in the same plane as the face will be considered as having a distance of 0. If set to a high value (e.g. 1000000), no difference will be made between distance in the plane of the face and orthogonal to it. Default is 0.5
3536
* @return std::shared_ptr<geometry::DFPointCloud> The unified segments
3637
*/
3738
static std::vector<std::shared_ptr<geometry::DFPointCloud>> DFSegmentation::AssociateClustersToMeshes(
3839
bool isCylinder,
3940
std::vector<std::shared_ptr<geometry::DFMesh>> referenceMesh,
4041
std::vector<std::shared_ptr<geometry::DFPointCloud>> &clusters,
4142
double angleThreshold = 0.1,
42-
double associationThreshold = 0.1);
43+
double associationThreshold = 0.1,
44+
double angleAssociationThreshold = 0.5);
4345

4446
/** @brief Iterated through clusters and finds the corresponding mesh face. It then associates the points of the cluster that are on the mesh face to the segment already associated with the mesh face.
4547
* @param isCylinder a boolean to indicate if the model is a cylinder. If true, the method will use the GetCenterAndAxis method of the mesh to find the center and axis of the mesh. based on that, we only want points that have normals more or less perpendicular to the cylinder axis.
4648
* @param unassociatedClusters the clusters from the normal-based segmentatinon that haven't been associated yet.
4749
* @param existingPointCloudSegments the already associated segments per mesh face.
4850
* @param meshes the mesh faces for all the model. This is used to associate the clusters to the mesh faces.
49-
* * @param angleThreshold the threshold to consider the a cluster as potential candidate for association. the value passed is the minimum sine of the angles. A value of 0 requires perfect alignment (angle = 0), while a value of 0.1 allows an angle of 5.7 degrees.
51+
* @param angleThreshold the threshold to consider the a cluster as potential candidate for association. the value passed is the minimum sine of the angles. A value of 0 requires perfect alignment (angle = 0), while a value of 0.1 allows an angle of 5.7 degrees.
5052
* @param associationThreshold the threshold to consider the points of a segment and a mesh face as associable. It is the ratio between the surface of the closest mesh triangle and the sum of the areas of the three triangles that form the rest of the pyramid described by the mesh triangle and the point we want to associate or not. The lower the number, the more strict the association will be and some poinnts on the mesh face might be wrongfully excluded.
53+
* @param angleAssociationThreshold a number to indicate how much distance in the plane of the face should be favored, compared to distance orthogonal to the face normal. If set to 0, any face in the same plane as the face will be considered as having a distance of 0. If set to a high value (e.g. 1000000), no difference will be made between distance in the plane of the face and orthogonal to it. Default is 0.5
54+
* @return void
5155
*/
5256
static void DFSegmentation::CleanUnassociatedClusters(
5357
bool isCylinder,
5458
std::vector<std::shared_ptr<geometry::DFPointCloud>> &unassociatedClusters,
5559
std::vector<std::vector<std::shared_ptr<geometry::DFPointCloud>>> &existingPointCloudSegments,
5660
std::vector<std::vector<std::shared_ptr<geometry::DFMesh>>> meshes,
5761
double angleThreshold = 0.1,
58-
double associationThreshold = 0.1);
62+
double associationThreshold = 0.1,
63+
double angleAssociationThreshold = 0.5);
5964
};
6065
} // namespace diffCheck::segmentation

src/diffCheckBindings.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,15 @@ PYBIND11_MODULE(diffcheck_bindings, m) {
229229
py::arg("reference_mesh"),
230230
py::arg("unassociated_clusters"),
231231
py::arg("angle_threshold") = 0.1,
232-
py::arg("association_threshold") = 0.1)
232+
py::arg("association_threshold") = 0.1,
233+
py::arg("angle_association_threshold") = 0.5)
233234

234235
.def_static("clean_unassociated_clusters", &diffCheck::segmentation::DFSegmentation::CleanUnassociatedClusters,
235236
py::arg("is_roundwood"),
236237
py::arg("unassociated_clusters"),
237238
py::arg("associated_clusters"),
238239
py::arg("reference_mesh"),
239240
py::arg("angle_threshold") = 0.1,
240-
py::arg("association_threshold") = 0.1);
241+
py::arg("association_threshold") = 0.1,
242+
py::arg("angle_association_threshold") = 0.5);
241243
}

src/gh/components/DF_CAD_segmentator/code.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ def RunScript(self,
2020
i_clouds: System.Collections.Generic.IList[Rhino.Geometry.PointCloud],
2121
i_assembly,
2222
i_angle_threshold: float = 0.1,
23-
i_association_threshold: float = 0.1):
23+
i_association_threshold: float = 0.1,
24+
i_angle_association_threshold: float = 0.5):
2425

2526
if i_clouds is None or i_assembly is None:
2627
self.AddRuntimeMessage(RML.Warning, "Please provide a cloud and an assembly to segment.")
@@ -29,17 +30,17 @@ def RunScript(self,
2930
i_angle_threshold = 0.1
3031
if i_association_threshold is None:
3132
i_association_threshold = 0.1
32-
33+
if i_angle_association_threshold is None:
34+
i_angle_association_threshold = 0.5
3335
o_face_clusters = []
3436
df_clusters = []
3537
# we make a deepcopy of the input clouds
3638
df_clouds = [df_cvt_bindings.cvt_rhcloud_2_dfcloud(cloud.Duplicate()) for cloud in i_clouds]
3739

3840
df_beams = i_assembly.beams
41+
df_asssociated_cluster_faces_per_beam = []
3942

4043
for df_b in df_beams:
41-
o_face_clusters.append([])
42-
4344
rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces]
4445
df_b_mesh_faces = [df_cvt_bindings.cvt_rhmesh_2_dfmesh(rh_b_mesh_face) for rh_b_mesh_face in rh_b_mesh_faces]
4546

@@ -49,22 +50,30 @@ def RunScript(self,
4950
reference_mesh=df_b_mesh_faces,
5051
unassociated_clusters=df_clouds,
5152
angle_threshold=i_angle_threshold,
52-
association_threshold=i_association_threshold
53+
association_threshold=i_association_threshold,
54+
angle_association_threshold=i_angle_association_threshold
5355
)
56+
df_asssociated_cluster_faces_per_beam.append(df_asssociated_cluster_faces)
57+
58+
for i, df_b in enumerate(df_beams):
59+
o_face_clusters.append([])
60+
rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces]
61+
df_b_mesh_faces = [df_cvt_bindings.cvt_rhmesh_2_dfmesh(rh_b_mesh_face) for rh_b_mesh_face in rh_b_mesh_faces]
5462

5563
dfb_segmentation.DFSegmentation.clean_unassociated_clusters(
5664
is_roundwood=df_b.is_roundwood,
5765
unassociated_clusters=df_clouds,
58-
associated_clusters=[df_asssociated_cluster_faces],
66+
associated_clusters=[df_asssociated_cluster_faces_per_beam[i]],
5967
reference_mesh=[df_b_mesh_faces],
6068
angle_threshold=i_angle_threshold,
61-
association_threshold=i_association_threshold
69+
association_threshold=i_association_threshold,
70+
angle_association_threshold=i_angle_association_threshold
6271
)
6372

64-
o_face_clusters[-1] = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_asssociated_cluster_faces]
73+
o_face_clusters[-1] = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_asssociated_cluster_faces_per_beam[i]]
6574

6675
df_asssociated_cluster = dfb_geometry.DFPointCloud()
67-
for df_associated_face in df_asssociated_cluster_faces:
76+
for df_associated_face in df_asssociated_cluster_faces_per_beam[i]:
6877
df_asssociated_cluster.add_points(df_associated_face)
6978

7079
df_clusters.append(df_asssociated_cluster)

src/gh/components/DF_CAD_segmentator/metadata.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@
6060
"wireDisplay": "default",
6161
"sourceCount": 0,
6262
"typeHintID": "float"
63+
},
64+
{
65+
"name": "i_angle_association_threshold",
66+
"nickname": "i_angle_association_threshold",
67+
"description": "A number to indicate how much distance in the plane of the face should be favored, compared to distance orthogonal to the face normal. Default is 0.5",
68+
"optional": true,
69+
"allowTreeAccess": true,
70+
"showTypeHints": true,
71+
"scriptParamAccess": "item",
72+
"wireDisplay": "default",
73+
"sourceCount": 0,
74+
"typeHintID": "float"
6375
}
6476
],
6577
"outputParameters": [

src/gh/components/DF_pose_estimation/code.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,20 @@ def RunScript(self,
5151
df_bb_centroid = (df_bb_points[0] + df_bb_points[1]) / 2
5252
rh_bb_centroid = Rhino.Geometry.Point3d(df_bb_centroid[0], df_bb_centroid[1], df_bb_centroid[2])
5353

54+
5455
new_xDirection, new_yDirection = df_poses.select_vectors(rh_face_normals, i_assembly.beams[i].plane.XAxis, i_assembly.beams[i].plane.YAxis)
56+
if not new_yDirection:
57+
df_beam_pc = dfb_geometry.DFPointCloud()
58+
for face_cloud in face_clouds:
59+
df_face_cloud = df_cvt_bindings.cvt_rhcloud_2_dfcloud(face_cloud)
60+
df_beam_pc.add_points(df_face_cloud)
61+
corners = df_beam_pc.get_tight_bounding_box()
62+
rh_corners = [Rhino.Geometry.Point3d(pt[0], pt[1], pt[2]) for pt in corners]
63+
plane = Rhino.Geometry.Plane.CreateFromPoints(rh_corners[0],rh_corners[1],rh_corners[2])
64+
box = Rhino.Geometry.Box(plane, rh_corners)
65+
longest_edge = sorted(box.ToBrep().Edges, key=lambda e: e.GetLength())[-1]
66+
longest_edge_direction = longest_edge.TangentAtEnd
67+
new_yDirection = Rhino.Geometry.Vector3d.CrossProduct(new_xDirection, longest_edge_direction)
5568

5669
pose = df_poses.DFPose(
5770
origin = [rh_bb_centroid.X, rh_bb_centroid.Y, rh_bb_centroid.Z],

src/gh/diffCheck/diffCheck/df_poses.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def select_vectors(vectors, previous_xDirection, previous_yDirection):
130130
Select the vectors that are aligned with the xDirection and yDirection.
131131
"""
132132
if previous_xDirection is not None and previous_yDirection is not None:
133-
sorted_vectors_by_alignment = sorted(vectors, key=lambda v: compute_dot_product(v, previous_xDirection), reverse=True)
133+
sorted_vectors_by_alignment = sorted(vectors, key=lambda v: abs(compute_dot_product(v, previous_xDirection)), reverse=True)
134134
new_xDirection = sorted_vectors_by_alignment[0]
135135
else:
136136
new_xDirection = vectors[0]
@@ -139,8 +139,12 @@ def select_vectors(vectors, previous_xDirection, previous_yDirection):
139139
for v in vectors:
140140
if compute_dot_product(v, new_xDirection) ** 2 < 0.5:
141141
condidates_for_yDirection.append(v)
142+
143+
if not condidates_for_yDirection:
144+
return new_xDirection, None
145+
142146
if previous_xDirection is not None and previous_yDirection is not None:
143-
sorted_vectors_by_perpendicularity = sorted(condidates_for_yDirection, key=lambda v: compute_dot_product(v, previous_yDirection), reverse=True)
147+
sorted_vectors_by_perpendicularity = sorted(condidates_for_yDirection, key=lambda v: abs(compute_dot_product(v, previous_yDirection)), reverse=True)
144148
new_xDirection = sorted_vectors_by_alignment[0]
145149
new_yDirection = sorted_vectors_by_perpendicularity[0] - compute_dot_product(sorted_vectors_by_perpendicularity[0], new_xDirection) * new_xDirection
146150
new_yDirection.Unitize()

0 commit comments

Comments
 (0)