From 07fcf551d0908ded54fe0309027664252a4fcf98 Mon Sep 17 00:00:00 2001 From: Paul Lee Date: Sun, 23 Mar 2025 12:06:27 +0800 Subject: [PATCH 1/2] [Draft-Faces] Bind: Improve fuse and warning 1. Face fuse per segment (original code has a whole face/segment disappear when a face is self-intersecting or reversed) 2. Return warning if face is self-intersecting Or reversed Github Issue: - https://github.com/FreeCAD/FreeCAD/issues/19721#issuecomment-2744738019 FreeCAD Forum: - https://forum.freecad.org/viewtopic.php?p=816758#p816758 - https://forum.freecad.org/viewtopic.php?p=813062#p813062 --- src/Mod/Draft/draftgeoutils/faces.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Mod/Draft/draftgeoutils/faces.py b/src/Mod/Draft/draftgeoutils/faces.py index 60b8a8faa5..1c329f0fc8 100644 --- a/src/Mod/Draft/draftgeoutils/faces.py +++ b/src/Mod/Draft/draftgeoutils/faces.py @@ -120,15 +120,21 @@ def bind(w1, w2, per_segment=False): """ def create_face(w1, w2): + try: w3 = Part.LineSegment(w1.Vertexes[0].Point, w2.Vertexes[0].Point).toShape() w4 = Part.LineSegment(w1.Vertexes[-1].Point, w2.Vertexes[-1].Point).toShape() - return Part.Face(Part.Wire(w1.Edges + [w3] + w2.Edges + [w4])) except Part.OCCError: print("DraftGeomUtils: unable to bind wires") return None + if w3.section(w4).Vertexes: + print("DraftGeomUtils: Problem, a segment is self-intersecting, please check!") + f = Part.Face(Part.Wire(w1.Edges + [w3] + w2.Edges + [w4])) + if f.normalAt(0,0).z == -1: + print("DraftGeomUtils: Problem, a segment direction is reversed, please check!") + return f if not w1 or not w2: print("DraftGeomUtils: unable to bind wires") @@ -164,6 +170,7 @@ def bind(w1, w2, per_segment=False): if face is None: return None faces.append(face) + # Usually there is last series of face after above 'for' routine, # EXCEPT when the last edge pair touch, faces had been appended # to faces_list, and reset faces =[] @@ -185,14 +192,25 @@ def bind(w1, w2, per_segment=False): faces_fused_list = [] for faces in faces_list: if len(faces) > 1 : - faces_fused = faces[0].fuse(faces[1:]).removeSplitter().Faces[0] - faces_fused_list.append(faces_fused) + # Below not good if a face is self-intersecting or reversed + #faces_fused = faces[0].fuse(faces[1:]).removeSplitter().Faces[0] + rf = faces[0] + for f in faces[1:]: + rf = rf.fuse(f).removeSplitter().Faces[0] + faces_fused_list.append(rf) + # faces might be empty list [], see above; skip if empty elif faces: faces_fused_list.append(faces[0]) # Only 1 face + return Part.Compound(faces_fused_list) else: - return faces[0].fuse(faces[1:]).removeSplitter().Faces[0] + # Below not good if a face is self-intersecting or reversed + #return faces[0].fuse(faces[1:]).removeSplitter().Faces[0] + rf = faces[0] + for f in faces[1:]: + rf = rf.fuse(f).removeSplitter().Faces[0] + return rf elif w1.isClosed() and w2.isClosed(): d1 = w1.BoundBox.DiagonalLength From b05c02b6f1c3fe7fad7066de093ac1dcda347e19 Mon Sep 17 00:00:00 2001 From: Paul Lee Date: Sun, 30 Mar 2025 10:29:36 +0800 Subject: [PATCH 2/2] [Draft-Faces] Bind: Improve fuse and warning - Further Fix Github Discussion: - Fix problem Roy-043 pointed out https://github.com/FreeCAD/FreeCAD/pull/20395#pullrequestreview-2726624360 FC Forum: - https://forum.freecad.org/viewtopic.php?p=819121#p819121 --- src/Mod/Draft/draftgeoutils/faces.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Mod/Draft/draftgeoutils/faces.py b/src/Mod/Draft/draftgeoutils/faces.py index 1c329f0fc8..06469daa11 100644 --- a/src/Mod/Draft/draftgeoutils/faces.py +++ b/src/Mod/Draft/draftgeoutils/faces.py @@ -132,8 +132,6 @@ def bind(w1, w2, per_segment=False): if w3.section(w4).Vertexes: print("DraftGeomUtils: Problem, a segment is self-intersecting, please check!") f = Part.Face(Part.Wire(w1.Edges + [w3] + w2.Edges + [w4])) - if f.normalAt(0,0).z == -1: - print("DraftGeomUtils: Problem, a segment direction is reversed, please check!") return f if not w1 or not w2: @@ -185,31 +183,53 @@ def bind(w1, w2, per_segment=False): # if w1.isClosed() and w2.isClosed() \ and len(faces_list) > 1 and faces_list[0]: - faces_list[0].extend(faces) + faces_list[0].extend(faces) # TODO: To be reviewed, 'afterthought' on 2025.3.29, seems by 'extend', faces in 1st and last faces are not in sequential order else: faces_list.append(faces) # Break into separate list + from collections import Counter if faces_list: faces_fused_list = [] for faces in faces_list: + dir = [] + countDir = None + for f in faces: + dir.append(f.normalAt(0,0).z) + countDir = Counter(dir) + l = len(faces) + m = max(countDir.values()) # max(countDir, key=countDir.get) + if m != l: + print("DraftGeomUtils: Problem, the direction of " + str(l-m) + " out of " + str(l) + " segment is reversed, please check!") if len(faces) > 1 : # Below not good if a face is self-intersecting or reversed #faces_fused = faces[0].fuse(faces[1:]).removeSplitter().Faces[0] rf = faces[0] for f in faces[1:]: rf = rf.fuse(f).removeSplitter().Faces[0] + #rf = rf.fuse(f) # Not working + #rf = rf.removeSplitter().Faces[0] # Not working faces_fused_list.append(rf) - # faces might be empty list [], see above; skip if empty elif faces: faces_fused_list.append(faces[0]) # Only 1 face return Part.Compound(faces_fused_list) else: + dir = [] + countDir = None + for f in faces: + dir.append(f.normalAt(0,0).z) + countDir = Counter(dir) + l = len(faces) + m = max(countDir.values()) # max(countDir, key=countDir.get) + if m != l: + print("DraftGeomUtils: Problem, the direction of " + str(l-m) + " out of " + str(l) + " segment is reversed, please check!") # Below not good if a face is self-intersecting or reversed #return faces[0].fuse(faces[1:]).removeSplitter().Faces[0] rf = faces[0] for f in faces[1:]: rf = rf.fuse(f).removeSplitter().Faces[0] + #rf = rf.fuse(f) # Not working + #rf = rf.removeSplitter().Faces[0] # Not working return rf elif w1.isClosed() and w2.isClosed():