From f03a7d3051846d8a315c99ef87b7e7700da17063 Mon Sep 17 00:00:00 2001 From: DamienGilliard <127743632+DamienGilliard@users.noreply.github.com> Date: Fri, 16 Jan 2026 17:57:47 +0100 Subject: [PATCH 1/7] feat: add sophisticated parameter to discard segments that are too far --- src/diffCheck/segmentation/DFSegmentation.cc | 7 ++++--- src/diffCheck/segmentation/DFSegmentation.hh | 4 +++- src/diffCheckBindings.cc | 3 ++- src/gh/components/DF_CAD_segmentator/code.py | 6 ++++-- src/gh/components/DF_CAD_segmentator/metadata.json | 12 ++++++++++++ 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/diffCheck/segmentation/DFSegmentation.cc b/src/diffCheck/segmentation/DFSegmentation.cc index d84ef254..520e2fdd 100644 --- a/src/diffCheck/segmentation/DFSegmentation.cc +++ b/src/diffCheck/segmentation/DFSegmentation.cc @@ -100,7 +100,8 @@ namespace diffCheck::segmentation std::vector> referenceMesh, std::vector> &clusters, double angleThreshold, - double associationThreshold) + double associationThreshold, + double angleAssociationThreshold) { std::vector> faceSegments = std::vector>(); @@ -271,10 +272,10 @@ namespace diffCheck::segmentation double currentDistance = (faceCenter - segmentCenter).norm(); double currentAngle = std::abs(sin(acos(faceNormal.dot(faceCenter - segmentCenter)))); // if the distance is smaller than the previous one, update the distance and the corresponding segment - if (std::abs(sin(acos(faceNormal.dot(segmentNormal)))) < angleThreshold && currentDistance < faceDistance && std::abs(1 - currentAngle) < angleThreshold) + if (std::abs(sin(acos(faceNormal.dot(segmentNormal)))) < angleThreshold && currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - segmentCenter) / (faceCenter - segmentCenter).norm()))) < faceDistance) { correspondingSegment = segment; - faceDistance = currentDistance; + faceDistance = currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - segmentCenter) / (faceCenter - segmentCenter).norm()))); } } diff --git a/src/diffCheck/segmentation/DFSegmentation.hh b/src/diffCheck/segmentation/DFSegmentation.hh index 38a0a487..39063445 100644 --- a/src/diffCheck/segmentation/DFSegmentation.hh +++ b/src/diffCheck/segmentation/DFSegmentation.hh @@ -32,6 +32,7 @@ namespace diffCheck::segmentation * @param clusters the vector of clusters from cilantro to associate with the mesh faces of the reference mesh * @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. * @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. + * @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 * @return std::shared_ptr The unified segments */ static std::vector> DFSegmentation::AssociateClustersToMeshes( @@ -39,7 +40,8 @@ namespace diffCheck::segmentation std::vector> referenceMesh, std::vector> &clusters, double angleThreshold = 0.1, - double associationThreshold = 0.1); + double associationThreshold = 0.1, + double angleAssociationThreshold = 0.5); /** @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. * @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. diff --git a/src/diffCheckBindings.cc b/src/diffCheckBindings.cc index 25dab20e..e826c50d 100644 --- a/src/diffCheckBindings.cc +++ b/src/diffCheckBindings.cc @@ -229,7 +229,8 @@ PYBIND11_MODULE(diffcheck_bindings, m) { py::arg("reference_mesh"), py::arg("unassociated_clusters"), py::arg("angle_threshold") = 0.1, - py::arg("association_threshold") = 0.1) + py::arg("association_threshold") = 0.1, + py::arg("angle_association_threshold") = 0.5) .def_static("clean_unassociated_clusters", &diffCheck::segmentation::DFSegmentation::CleanUnassociatedClusters, py::arg("is_roundwood"), diff --git a/src/gh/components/DF_CAD_segmentator/code.py b/src/gh/components/DF_CAD_segmentator/code.py index 5a561c33..4856b663 100644 --- a/src/gh/components/DF_CAD_segmentator/code.py +++ b/src/gh/components/DF_CAD_segmentator/code.py @@ -20,7 +20,8 @@ def RunScript(self, i_clouds: System.Collections.Generic.IList[Rhino.Geometry.PointCloud], i_assembly, i_angle_threshold: float = 0.1, - i_association_threshold: float = 0.1): + i_association_threshold: float = 0.1, + i_angle_association_threshold: float = 0.5): if i_clouds is None or i_assembly is None: self.AddRuntimeMessage(RML.Warning, "Please provide a cloud and an assembly to segment.") @@ -49,7 +50,8 @@ def RunScript(self, reference_mesh=df_b_mesh_faces, unassociated_clusters=df_clouds, angle_threshold=i_angle_threshold, - association_threshold=i_association_threshold + association_threshold=i_association_threshold, + angle_association_threshold=i_angle_association_threshold ) dfb_segmentation.DFSegmentation.clean_unassociated_clusters( diff --git a/src/gh/components/DF_CAD_segmentator/metadata.json b/src/gh/components/DF_CAD_segmentator/metadata.json index 087ade1c..ddfe81bd 100644 --- a/src/gh/components/DF_CAD_segmentator/metadata.json +++ b/src/gh/components/DF_CAD_segmentator/metadata.json @@ -60,6 +60,18 @@ "wireDisplay": "default", "sourceCount": 0, "typeHintID": "float" + }, + { + "name": "i_angle_association_threshold", + "nickname": "i_angle_association_threshold", + "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", + "optional": true, + "allowTreeAccess": true, + "showTypeHints": true, + "scriptParamAccess": "item", + "wireDisplay": "default", + "sourceCount": 0, + "typeHintID": "float" } ], "outputParameters": [ From 07eccdacb6ebe3b382ea0204fd7bdcfe220c8953 Mon Sep 17 00:00:00 2001 From: DamienGilliard <127743632+DamienGilliard@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:07:26 +0100 Subject: [PATCH 2/7] fix: associate all clusters before calling clean_unassociated_clusters --- src/gh/components/DF_CAD_segmentator/code.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gh/components/DF_CAD_segmentator/code.py b/src/gh/components/DF_CAD_segmentator/code.py index 4856b663..0026bd97 100644 --- a/src/gh/components/DF_CAD_segmentator/code.py +++ b/src/gh/components/DF_CAD_segmentator/code.py @@ -37,6 +37,7 @@ def RunScript(self, df_clouds = [df_cvt_bindings.cvt_rhcloud_2_dfcloud(cloud.Duplicate()) for cloud in i_clouds] df_beams = i_assembly.beams + df_asssociated_cluster_faces_per_beam = [] for df_b in df_beams: o_face_clusters.append([]) @@ -53,20 +54,25 @@ def RunScript(self, association_threshold=i_association_threshold, angle_association_threshold=i_angle_association_threshold ) + df_asssociated_cluster_faces_per_beam.append(df_asssociated_cluster_faces) + + for i, df_b in enumerate(df_beams): + rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces] + 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] dfb_segmentation.DFSegmentation.clean_unassociated_clusters( is_roundwood=df_b.is_roundwood, unassociated_clusters=df_clouds, - associated_clusters=[df_asssociated_cluster_faces], + associated_clusters=[df_asssociated_cluster_faces_per_beam[i]], reference_mesh=[df_b_mesh_faces], angle_threshold=i_angle_threshold, association_threshold=i_association_threshold ) - o_face_clusters[-1] = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_asssociated_cluster_faces] + o_face_clusters[-1] = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_asssociated_cluster_faces_per_beam[i]] df_asssociated_cluster = dfb_geometry.DFPointCloud() - for df_associated_face in df_asssociated_cluster_faces: + for df_associated_face in df_asssociated_cluster_faces_per_beam[i]: df_asssociated_cluster.add_points(df_associated_face) df_clusters.append(df_asssociated_cluster) From b8cadeeb67a4974c1a57add7efcac0eac81c7766 Mon Sep 17 00:00:00 2001 From: DamienGilliard <127743632+DamienGilliard@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:45:15 +0100 Subject: [PATCH 3/7] feat: inverse the normals that have z component under -0.8 --- src/diffCheck/geometry/DFPointCloud.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/diffCheck/geometry/DFPointCloud.cc b/src/diffCheck/geometry/DFPointCloud.cc index 5324f3f6..e7021f70 100644 --- a/src/diffCheck/geometry/DFPointCloud.cc +++ b/src/diffCheck/geometry/DFPointCloud.cc @@ -155,7 +155,14 @@ namespace diffCheck::geometry DIFFCHECK_INFO("Default estimation of normals with knn = 30"); } for (auto &normal : O3DPointCloud->normals_) + { + if(normal.z() < -0.8) + { + normal = -normal; + } this->Normals.push_back(normal); + } + } else { @@ -165,7 +172,13 @@ namespace diffCheck::geometry this->Normals.clear(); for (int i = 0; i < cilantroPointCloud->normals.cols(); i++) + { + if(cilantroPointCloud->normals.col(i).z() < -0.8) + { + cilantroPointCloud->normals.col(i) = -cilantroPointCloud->normals.col(i); + } this->Normals.push_back(cilantroPointCloud->normals.col(i).cast()); + } DIFFCHECK_INFO(("Estimating normals with cilantro evaluator with knn = " + std::to_string(knn.value())).c_str()); } From a4b4f21f141e91a796f6c94a20e19d62e7c007f2 Mon Sep 17 00:00:00 2001 From: DamienGilliard <127743632+DamienGilliard@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:47:11 +0100 Subject: [PATCH 4/7] feat: adapt distance computation depending of angle in CAD segmentation --- src/diffCheck/segmentation/DFSegmentation.cc | 16 ++++++++++++++-- src/diffCheck/segmentation/DFSegmentation.hh | 7 +++++-- src/diffCheckBindings.cc | 3 ++- src/gh/components/DF_CAD_segmentator/code.py | 6 ++++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/diffCheck/segmentation/DFSegmentation.cc b/src/diffCheck/segmentation/DFSegmentation.cc index 520e2fdd..ca48f2e6 100644 --- a/src/diffCheck/segmentation/DFSegmentation.cc +++ b/src/diffCheck/segmentation/DFSegmentation.cc @@ -1,4 +1,6 @@ #include "DFSegmentation.hh" +#include +#include #include #include @@ -234,6 +236,12 @@ namespace diffCheck::segmentation } else { + std::string timestamp = std::to_string( + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ).count() + ); + std::ofstream logFile("C:\\Users\\localuser\\Desktop\\association_log_" + timestamp + ".txt", std::ios::app); for (std::shared_ptr face : referenceMesh) { std::shared_ptr correspondingSegment; @@ -274,6 +282,8 @@ namespace diffCheck::segmentation // if the distance is smaller than the previous one, update the distance and the corresponding segment if (std::abs(sin(acos(faceNormal.dot(segmentNormal)))) < angleThreshold && currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - segmentCenter) / (faceCenter - segmentCenter).norm()))) < faceDistance) { + logFile << std::abs(sin(acos(faceNormal.dot(segmentNormal)))) << " < " << angleThreshold << " and " << currentDistance << " * (" << angleAssociationThreshold << " + " << std::abs(faceNormal.dot((faceCenter - segmentCenter) / (faceCenter - segmentCenter).norm())) << ") < " << faceDistance << std::endl; + logFile << " considered face: Face normal: " << faceNormal.transpose() << ", Segment normal: " << segmentNormal.transpose() << ", Current distance: " << currentDistance << ", Face distance: " << faceDistance << std::endl; correspondingSegment = segment; faceDistance = currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - segmentCenter) / (faceCenter - segmentCenter).norm()))); } @@ -324,6 +334,7 @@ namespace diffCheck::segmentation } faceSegments.push_back(facePoints); } + logFile.close(); } return faceSegments; } @@ -334,7 +345,8 @@ namespace diffCheck::segmentation std::vector>> &existingPointCloudSegments, std::vector>> meshes, double angleThreshold, - double associationThreshold) + double associationThreshold, + double angleAssociationThreshold) { if (unassociatedClusters.size() == 0) { @@ -442,7 +454,7 @@ namespace diffCheck::segmentation 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 < distance && std::abs(1 - std::sin(clusterNormalToJunctionLineAngle)) < associationThreshold) + if (std::abs(sin(acos(faceNormal.dot(clusterNormal)))) < angleThreshold && currentDistance * (angleAssociationThreshold + std::abs(faceNormal.dot((faceCenter - clusterCenter) / (faceCenter - clusterCenter).norm()))) < distance) { goodMeshIndex = meshIndex; goodFaceIndex = faceIndex; diff --git a/src/diffCheck/segmentation/DFSegmentation.hh b/src/diffCheck/segmentation/DFSegmentation.hh index 39063445..cb453158 100644 --- a/src/diffCheck/segmentation/DFSegmentation.hh +++ b/src/diffCheck/segmentation/DFSegmentation.hh @@ -48,8 +48,10 @@ namespace diffCheck::segmentation * @param unassociatedClusters the clusters from the normal-based segmentatinon that haven't been associated yet. * @param existingPointCloudSegments the already associated segments per mesh face. * @param meshes the mesh faces for all the model. This is used to associate the clusters to the mesh faces. - * * @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. + * @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. * @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. + * @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 + * @return void */ static void DFSegmentation::CleanUnassociatedClusters( bool isCylinder, @@ -57,6 +59,7 @@ namespace diffCheck::segmentation std::vector>> &existingPointCloudSegments, std::vector>> meshes, double angleThreshold = 0.1, - double associationThreshold = 0.1); + double associationThreshold = 0.1, + double angleAssociationThreshold = 0.5); }; } // namespace diffCheck::segmentation \ No newline at end of file diff --git a/src/diffCheckBindings.cc b/src/diffCheckBindings.cc index e826c50d..e1a7a081 100644 --- a/src/diffCheckBindings.cc +++ b/src/diffCheckBindings.cc @@ -238,5 +238,6 @@ PYBIND11_MODULE(diffcheck_bindings, m) { py::arg("associated_clusters"), py::arg("reference_mesh"), py::arg("angle_threshold") = 0.1, - py::arg("association_threshold") = 0.1); + py::arg("association_threshold") = 0.1, + py::arg("angle_association_threshold") = 0.5); } diff --git a/src/gh/components/DF_CAD_segmentator/code.py b/src/gh/components/DF_CAD_segmentator/code.py index 0026bd97..31809367 100644 --- a/src/gh/components/DF_CAD_segmentator/code.py +++ b/src/gh/components/DF_CAD_segmentator/code.py @@ -30,7 +30,8 @@ def RunScript(self, i_angle_threshold = 0.1 if i_association_threshold is None: i_association_threshold = 0.1 - + if i_angle_association_threshold is None: + i_angle_association_threshold = 0.5 o_face_clusters = [] df_clusters = [] # we make a deepcopy of the input clouds @@ -66,7 +67,8 @@ def RunScript(self, associated_clusters=[df_asssociated_cluster_faces_per_beam[i]], reference_mesh=[df_b_mesh_faces], angle_threshold=i_angle_threshold, - association_threshold=i_association_threshold + association_threshold=i_association_threshold, + angle_association_threshold=i_angle_association_threshold ) o_face_clusters[-1] = [df_cvt_bindings.cvt_dfcloud_2_rhcloud(cluster) for cluster in df_asssociated_cluster_faces_per_beam[i]] From 858d156e8234f7b7ee145b8adf82e3291d499b98 Mon Sep 17 00:00:00 2001 From: DamienGilliard <127743632+DamienGilliard@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:41:40 +0100 Subject: [PATCH 5/7] feat-wip: creating fallbac solution when only 2 parallel faces found --- src/gh/components/DF_pose_estimation/code.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/gh/components/DF_pose_estimation/code.py b/src/gh/components/DF_pose_estimation/code.py index 0534a9e8..b1a9fab1 100644 --- a/src/gh/components/DF_pose_estimation/code.py +++ b/src/gh/components/DF_pose_estimation/code.py @@ -51,7 +51,20 @@ def RunScript(self, df_bb_centroid = (df_bb_points[0] + df_bb_points[1]) / 2 rh_bb_centroid = Rhino.Geometry.Point3d(df_bb_centroid[0], df_bb_centroid[1], df_bb_centroid[2]) + new_xDirection, new_yDirection = df_poses.select_vectors(rh_face_normals, i_assembly.beams[i].plane.XAxis, i_assembly.beams[i].plane.YAxis) + if not new_yDirection: + df_beam_pc = dfb_geometry.DFPointCloud() + for face_cloud in face_clouds: + df_face_cloud = df_cvt_bindings.cvt_rhcloud_2_dfcloud(face_cloud) + df_beam_pc.add_points(df_face_cloud) + corners = df_beam_pc.get_tight_bounding_box() + rh_corners = [Rhino.Geometry.Point3d(pt[0], pt[1], pt[2]) for pt in corners] + plane = Rhino.Geometry.Plane.CreateFromPoints(rh_corners[0],rh_corners[1],rh_corners[2]) + box = Rhino.Geometry.Box(plane, rh_corners) + longest_edge = sorted(box.ToBrep().Edges, key=lambda e: e.GetLength())[-1] + longest_edge_direction = longest_edge.TangentAtEnd + new_yDirection = Rhino.Geometry.Vector3d.CrossProduct(new_xDirection, longest_edge_direction) pose = df_poses.DFPose( origin = [rh_bb_centroid.X, rh_bb_centroid.Y, rh_bb_centroid.Z], From 4d0538d9d8b965b0b6bd49ddbf242e4f166b990f Mon Sep 17 00:00:00 2001 From: DamienGilliard <127743632+DamienGilliard@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:43:11 +0100 Subject: [PATCH 6/7] fix-wip: use absolute value of dot product to test colinearity --- src/gh/diffCheck/diffCheck/df_poses.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gh/diffCheck/diffCheck/df_poses.py b/src/gh/diffCheck/diffCheck/df_poses.py index adaeee16..98f836c4 100644 --- a/src/gh/diffCheck/diffCheck/df_poses.py +++ b/src/gh/diffCheck/diffCheck/df_poses.py @@ -130,7 +130,7 @@ def select_vectors(vectors, previous_xDirection, previous_yDirection): Select the vectors that are aligned with the xDirection and yDirection. """ if previous_xDirection is not None and previous_yDirection is not None: - sorted_vectors_by_alignment = sorted(vectors, key=lambda v: compute_dot_product(v, previous_xDirection), reverse=True) + sorted_vectors_by_alignment = sorted(vectors, key=lambda v: abs(compute_dot_product(v, previous_xDirection)), reverse=True) new_xDirection = sorted_vectors_by_alignment[0] else: new_xDirection = vectors[0] @@ -139,8 +139,12 @@ def select_vectors(vectors, previous_xDirection, previous_yDirection): for v in vectors: if compute_dot_product(v, new_xDirection) ** 2 < 0.5: condidates_for_yDirection.append(v) + + if not condidates_for_yDirection: + return new_xDirection, None + if previous_xDirection is not None and previous_yDirection is not None: - sorted_vectors_by_perpendicularity = sorted(condidates_for_yDirection, key=lambda v: compute_dot_product(v, previous_yDirection), reverse=True) + sorted_vectors_by_perpendicularity = sorted(condidates_for_yDirection, key=lambda v: abs(compute_dot_product(v, previous_yDirection)), reverse=True) new_xDirection = sorted_vectors_by_alignment[0] new_yDirection = sorted_vectors_by_perpendicularity[0] - compute_dot_product(sorted_vectors_by_perpendicularity[0], new_xDirection) * new_xDirection new_yDirection.Unitize() From 1b2903f7d2d667bc8c0650b714da45971992f7e7 Mon Sep 17 00:00:00 2001 From: DamienGilliard <127743632+DamienGilliard@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:44:23 +0100 Subject: [PATCH 7/7] feat: fully deal with first association before cleaning up unassociated clusters --- src/gh/components/DF_CAD_segmentator/code.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gh/components/DF_CAD_segmentator/code.py b/src/gh/components/DF_CAD_segmentator/code.py index 31809367..81bc8e94 100644 --- a/src/gh/components/DF_CAD_segmentator/code.py +++ b/src/gh/components/DF_CAD_segmentator/code.py @@ -41,8 +41,6 @@ def RunScript(self, df_asssociated_cluster_faces_per_beam = [] for df_b in df_beams: - o_face_clusters.append([]) - rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces] 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] @@ -58,6 +56,7 @@ def RunScript(self, df_asssociated_cluster_faces_per_beam.append(df_asssociated_cluster_faces) for i, df_b in enumerate(df_beams): + o_face_clusters.append([]) rh_b_mesh_faces = [df_b_f.to_mesh() for df_b_f in df_b.side_faces] 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]