From 38bccb31faa60590bae1cb817dde5ac6f073e860 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Sat, 3 Sep 2022 09:06:51 +0200 Subject: [PATCH] Arch: Fix baseface generation for walls with a trace that self-intersects, has T-connections or that overlaps --- src/Mod/Arch/ArchWall.py | 20 +++++----- src/Mod/Draft/draftgeoutils/faces.py | 58 ++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index d9d54bca2d..6a1816ef40 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -1351,7 +1351,7 @@ class _Wall(ArchComponent.Component): if not DraftVecUtils.isNull(dvec): dvec.normalize() - sh = None + face = None curAligns = aligns[0] off = obj.Offset.Value @@ -1399,7 +1399,7 @@ class _Wall(ArchComponent.Component): normal=normal, basewireOffset=off) - sh = DraftGeomUtils.bind(w1,w2) + face = DraftGeomUtils.bind(w1, w2, per_segment=True) elif curAligns == "Right": dvec = dvec.negative() @@ -1440,7 +1440,7 @@ class _Wall(ArchComponent.Component): normal=normal, basewireOffset=off) - sh = DraftGeomUtils.bind(w1,w2) + face = DraftGeomUtils.bind(w1, w2, per_segment=True) #elif obj.Align == "Center": elif curAligns == "Center": @@ -1473,18 +1473,16 @@ class _Wall(ArchComponent.Component): alignList=aligns, normal=normal, basewireOffset=off) - sh = DraftGeomUtils.bind(w1,w2) + face = DraftGeomUtils.bind(w1, w2, per_segment=True) del widths[0:edgeNum] del aligns[0:edgeNum] - if sh: + if face: if layers and (layers[i] < 0): # layers with negative values are not drawn continue - sh.fix(0.1,0,1) # fixes self-intersecting wires - f = Part.Face(sh) if baseface: # To allow exportIFC.py to work properly on @@ -1505,14 +1503,14 @@ class _Wall(ArchComponent.Component): # - 1st finding : if a rectangle + 1 line, can't removesSplitter properly... # - 2nd finding : if 2 faces do not touch, can't form a shell; then, subsequently for remaining faces even though touch each faces, can't form a shell - baseface.append(f) + baseface.append(face) # The above make Refine methods below (in else) useless, regardless removeSpitters yet to be improved for cases do not work well - ''' Whether layers or not, all baseface.append(f) ''' + ''' Whether layers or not, all baseface.append(face) ''' else: - baseface = [f] + baseface = [face] - ''' Whether layers or not, all baseface = [f] ''' + ''' Whether layers or not, all baseface = [face] ''' if baseface: base,placement = self.rebase(baseface) diff --git a/src/Mod/Draft/draftgeoutils/faces.py b/src/Mod/Draft/draftgeoutils/faces.py index dac30a432e..57e4ab8a29 100644 --- a/src/Mod/Draft/draftgeoutils/faces.py +++ b/src/Mod/Draft/draftgeoutils/faces.py @@ -110,32 +110,58 @@ def is_coplanar(faces, tol=-1): isCoplanar = is_coplanar -def bind(w1, w2): - """Bind 2 wires by their endpoints and returns a face.""" - if not w1 or not w2: - print("DraftGeomUtils: unable to bind wires") - return None +def bind(w1, w2, per_segment=False): + """Bind 2 wires by their endpoints and returns a face. - if w1.isClosed() and w2.isClosed(): - d1 = w1.BoundBox.DiagonalLength - d2 = w2.BoundBox.DiagonalLength - if d1 > d2: - # w2.reverse() - return Part.Face([w1, w2]) - else: - # w1.reverse() - return Part.Face([w2, w1]) - else: + If per_segment is True and the wires have the same number of edges, the + wires are processed per segment: a separate face is created for each pair + of edges (one from w1 and one from w2), and the faces are then fused. This + avoids problems with walls based on wires that selfintersect, or that have + a loop that ends in a T-connection (f.e. a wire shaped like a number 6). + """ + + 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])) + return Part.Face(Part.Wire(w1.Edges + [w3] + w2.Edges + [w4])) except Part.OCCError: print("DraftGeomUtils: unable to bind wires") return None + if not w1 or not w2: + print("DraftGeomUtils: unable to bind wires") + return None + + if (per_segment + and len(w1.Edges) > 1 + and len(w1.Edges) == len(w2.Edges)): + faces = [] + for (edge1, edge2) in zip(w1.Edges, w2.Edges): + face = create_face(edge1, edge2) + if face is None: + return None + faces.append(face) + # return concatenate(faces[0].fuse(faces[1:])) # Also works. + return faces[0].fuse(faces[1:]).removeSplitter().Faces[0] + elif w1.isClosed() and w2.isClosed(): + d1 = w1.BoundBox.DiagonalLength + d2 = w2.BoundBox.DiagonalLength + if d1 < d2: + w1, w2 = w2, w1 + # return Part.Face(w1).cut(Part.Face(w2)).Faces[0] # Only works if wires do not self-intersect. + try: + face = Part.Face([w1, w2]) + face.fix(1e-7, 0, 1) + return face + except Part.OCCError: + print("DraftGeomUtils: unable to bind wires") + return None + else: + return create_face(w1, w2) + def cleanFaces(shape): """Remove inner edges from coplanar faces."""