From 429844a7dfcba66735940bb5f15def4484f00e41 Mon Sep 17 00:00:00 2001 From: Erhannis Date: Wed, 18 Sep 2024 07:09:16 -0400 Subject: [PATCH] Bend perforation (#370) * Add perforation feature to bends --- SheetMetalCmd.py | 183 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 1 deletion(-) diff --git a/SheetMetalCmd.py b/SheetMetalCmd.py index 0d6b55f..bdb0dbc 100644 --- a/SheetMetalCmd.py +++ b/SheetMetalCmd.py @@ -176,6 +176,75 @@ def smMakeReliefFace(edge, dir, gap, reliefW, reliefD, reliefType, op=""): return face +def smMakePerforationFace( + edge, + dir, + bendR, + bendA, + perforationAngle, + flipped, + extLen, + gap1, + gap2, + lenIPerf1, + lenIPerf2, + lenPerf, + lenNPerf, + op="", +): + L0 = (edge.LastParameter - gap2 - lenIPerf2) - (edge.FirstParameter + gap1 + lenIPerf1) + Lp = lenPerf + Ln = lenNPerf + P0 = (L0-Ln) / (Lp+Ln) + P = math.ceil(P0) + N = P+1 + F = L0 / (math.ceil(P0)*Lp + math.ceil(P0)*Ln + Ln) + + if perforationAngle == 0: + perforationAngle = bendA + extAngle = (perforationAngle - bendA) / 2; + S = (1 / math.cos(extAngle * (2*math.pi/360))) + + pivotL = -bendR + swingL = extLen + (S-1)*(bendR+extLen) + if not flipped: + pivotL = extLen - pivotL + swingL = extLen - swingL + + # Initial perf, near + p1 = edge.valueAt(edge.FirstParameter + gap1) + dir.normalize() * pivotL + p2 = edge.valueAt(edge.FirstParameter + gap1 + lenIPerf1) + dir.normalize() * pivotL + p3 = edge.valueAt(edge.FirstParameter + gap1 + lenIPerf1) + dir.normalize() * swingL + p4 = edge.valueAt(edge.FirstParameter + gap1) + dir.normalize() * swingL + w = Part.makePolygon([p1, p2, p3, p4, p1]) + face = Part.Face(w) + totalFace = face + + # Initial perf, far + p1 = edge.valueAt(edge.LastParameter - gap2 - lenIPerf2) + dir.normalize() * pivotL + p2 = edge.valueAt(edge.LastParameter - gap2) + dir.normalize() * pivotL + p3 = edge.valueAt(edge.LastParameter - gap2) + dir.normalize() * swingL + p4 = edge.valueAt(edge.LastParameter - gap2 - lenIPerf2) + dir.normalize() * swingL + w = Part.makePolygon([p1, p2, p3, p4, p1]) + face = Part.Face(w) + totalFace = totalFace.fuse(face) + + # Perforations, inner + for i in range(P): + x = (edge.FirstParameter + gap1 + lenIPerf1) + (Ln * F * (i+1)) + (Lp * F * i) + p1 = edge.valueAt(x) + dir.normalize() * pivotL + p2 = edge.valueAt(x + Lp*F) + dir.normalize() * pivotL + p3 = edge.valueAt(x + Lp*F) + dir.normalize() * swingL + p4 = edge.valueAt(x) + dir.normalize() * swingL + w = Part.makePolygon([p1, p2, p3, p4, p1]) + face = Part.Face(w) + totalFace = totalFace.fuse(face) + + if hasattr(totalFace, "mapShapes"): + totalFace.mapShapes([(edge, totalFace)], None, op) + return totalFace + + def smMakeFace(edge, dir, extLen, gap1=0.0, gap2=0.0, angle1=0.0, angle2=0.0, op=""): len1 = extLen * math.tan(math.radians(angle1)) len2 = extLen * math.tan(math.radians(angle2)) @@ -917,6 +986,11 @@ def smBend( sketch=None, extendType="Simple", LengthSpec="Leg", + Perforate=False, + PerforationAngle=0.0, + PerforationInitialLength=5.0, + PerforationMaxLength=5.0, + NonperforationMaxLength=5.0, ): # if sketch is as wall sketches = False @@ -1287,8 +1361,8 @@ def smBend( # Part.show(wallSolid.Faces[2]) thk_faceList.append(wallSolid.Faces[2]) - # Produce bend Solid if not (unfold): + # Produce bend Solid if bendA > 0.0: # create bend # narrow the wall if we have gaps @@ -1302,6 +1376,41 @@ def smBend( resultSolid = resultSolid.fuse(wallSolid) # Part.show(resultSolid,"resultSolid") + # Remove perforation + if Perforate: + #CHECK I'm not sure about flipped - the main one gets overwritten for each sublist item + perfFace = smMakePerforationFace( + lenEdge, + thkDir, + bendR, + bendA, + PerforationAngle, + flipped, + thk, + gap1, + gap2, + PerforationInitialLength, + PerforationInitialLength, + PerforationMaxLength, + NonperforationMaxLength, + op="SMR", + ) + # Part.show(perfFace) + #CHECK 'Part.Compound' object has no attribute 'normalAt' ; might need it + # if perfFace.normalAt(0, 0) != FaceDir: + # perfFace.reverse() + if PerforationAngle > 0.0: + perfFace = perfFace.rotate( + revAxisP, + revAxisV, + (bendA/2)-(PerforationAngle/2) + ) + perfSolid = perfFace.revolve(revAxisP, revAxisV, PerforationAngle) + else: + perfSolid = perfFace.revolve(revAxisP, revAxisV, bendA) + # Part.show(perfSolid) + resultSolid = resultSolid.cut(perfSolid) + # Produce unfold Solid else: if bendA > 0.0: @@ -1316,10 +1425,42 @@ def smBend( resultSolid = resultSolid.fuse(unfoldSolid) if extLen > 0.0: + # Flatten the wall back out wallSolid.rotate(revAxisP, revAxisV, -bendA) # Part.show(wallSolid, "wallSolid") wallSolid.translate(FaceDir * unfoldLength) resultSolid = resultSolid.fuse(wallSolid) + + # Remove perforation + if Perforate: + perfFace = smMakePerforationFace( + lenEdge, + thkDir, + bendR, + bendA, + PerforationAngle, + flipped, + thk, + gap1, + gap2, + PerforationInitialLength, + PerforationInitialLength, + PerforationMaxLength, + NonperforationMaxLength, + op="SMR", + ) + #CHECK 'Part.Compound' object has no attribute 'normalAt' ; might need it + # if perfFace.normalAt(0, 0) != FaceDir: + # perfFace.reverse() + if PerforationAngle > 0.0: + perfUnfoldLength = (bendR + kfactor * thk) * PerforationAngle * math.pi / 180.0 + perfFace = perfFace.translate(FaceDir * ((unfoldLength/2)-(perfUnfoldLength/2))) + perfSolid = perfFace.extrude(FaceDir * perfUnfoldLength) + else: + perfSolid = perfFace.extrude(FaceDir * unfoldLength) + # Part.show(perfSolid) + resultSolid = resultSolid.cut(perfSolid) + # Part.show(resultSolid, "resultSolid") return resultSolid, thk_faceList @@ -1530,6 +1671,41 @@ def _addProperties(self, obj): None, "ParametersEx3", ) + smAddBoolProperty( + obj, + "Perforate", + FreeCAD.Qt.translate("App::Property", "Enable Perforation"), + False, + "ParametersPerforation", + ) + smAddAngleProperty( + obj, + "PerforationAngle", + FreeCAD.Qt.translate("App::Property", "Perforation Angle"), + 0.0, + "ParametersPerforation", + ) + smAddLengthProperty( + obj, + "PerforationInitialLength", + FreeCAD.Qt.translate("App::Property", "Initial Perforation Length"), + 5.0, + "ParametersPerforation", + ) + smAddLengthProperty( + obj, + "PerforationMaxLength", + FreeCAD.Qt.translate("App::Property", "Perforation Max Length"), + 5.0, + "ParametersPerforation", + ) + smAddLengthProperty( + obj, + "NonperforationMaxLength", + FreeCAD.Qt.translate("App::Property", "Non-Perforation Max Length"), + 5.0, + "ParametersPerforation", + ) def getElementMapVersion(self, _fp, ver, _prop, restored): if not restored: @@ -1613,6 +1789,11 @@ def execute(self, fp): mingap=fp.minGap.Value, maxExtendGap=fp.maxExtendDist.Value, LengthSpec=fp.LengthSpec, + Perforate=fp.Perforate, + PerforationAngle=fp.PerforationAngle.Value, + PerforationInitialLength=fp.PerforationInitialLength.Value, + PerforationMaxLength=fp.PerforationMaxLength.Value, + NonperforationMaxLength=fp.NonperforationMaxLength.Value, ) faces = smGetFace(f, s) face = faces