From cbe0f782d6df05f2097529d6d17cc3523dd961c0 Mon Sep 17 00:00:00 2001 From: jaisekjames <44542605+jaisekjames@users.noreply.github.com> Date: Sat, 28 Nov 2020 11:58:33 +0530 Subject: [PATCH 1/5] Sketch on sheetmetal sketch on sheetmetal Select a face select a sketch. It will create cut based on sketch. --- SketchOnSheetMetalCmd.py | 420 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 SketchOnSheetMetalCmd.py diff --git a/SketchOnSheetMetalCmd.py b/SketchOnSheetMetalCmd.py new file mode 100644 index 0000000..fad61ce --- /dev/null +++ b/SketchOnSheetMetalCmd.py @@ -0,0 +1,420 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# SketchOnSheetMetalCmd.py +# +# Copyright 2015 Shai Seger +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# +################################################################################### + +from FreeCAD import Gui +from PySide import QtCore, QtGui + +import FreeCAD, FreeCADGui, Part, os, math +__dir__ = os.path.dirname(__file__) +iconPath = os.path.join( __dir__, 'Resources', 'icons' ) +smEpsilon = 0.0000001 + +import SheetMetalBendSolid + +def smWarnDialog(msg): + diag = QtGui.QMessageBox(QtGui.QMessageBox.Warning, 'Error in macro MessageBox', msg) + diag.setWindowModality(QtCore.Qt.ApplicationModal) + diag.exec_() + +def smBelongToBody(item, body): + if (body is None): + return False + for obj in body.Group: + if obj.Name == item.Name: + return True + return False + +def smIsPartDesign(obj): + return str(obj).find(" Facelist[1].Area : + selFace = Facelist[0] + else : + selFace = Facelist[1] + elif type(sel_item) == Part.Face : + selFace = sel_item + return selFace + + +def smthk(obj, foldface) : + normal = foldface.normalAt(0,0) + theVol = obj.Volume + if theVol < 0.0001: + SMError("Shape is not a real 3D-object or to small for a metal-sheet!") + else: + # Make a first estimate of the thickness + estimated_thk = theVol/(obj.Area / 2.0) +# p1 = foldface.CenterOfMass + for v in foldface.Vertexes : + p1 = v.Point + p2 = p1 + estimated_thk * -1.5 * normal + e1 = Part.makeLine(p1, p2) + thkedge = obj.common(e1) + thk = thkedge.Length + if thk > smEpsilon : + break + return thk + +def smCutFace(Face, obj) : + # find face Modified During loop + for face in obj.Faces : + face_common = face.common(Face) + if face_common.Faces : + break + return face + +def smGetEdge(Face, obj) : + # find face Modified During loop + for edge in obj.Edges : + face_common = edge.common(Face) + if face_common.Edges : + break + return edge + +def equal_angle(ang1, ang2, p=5): + # compares two angles + result = False + if round(ang1 - ang2, p)==0: + result = True + if round((ang1-2.0*math.pi) - ang2, p)==0: + result = True + if round(ang1 - (ang2-2.0*math.pi), p)==0: + result = True + return result + +def bendAngle(theFace, edge_vec) : + #Start to investigate the angles at self.__Shape.Faces[face_idx].ParameterRange[0] + #Part.show(theFace,"theFace") + #valuelist = theFace.ParameterRange + #print(valuelist) + angle_0 = theFace.ParameterRange[0] + angle_1 = theFace.ParameterRange[1] + + # idea: identify the angle at edge_vec = P_edge.Vertexes[0].copy().Point + # This will be = angle_start + # calculate the tan_vec from valueAt + edgeAngle, edgePar = theFace.Surface.parameter(edge_vec) + #print('the angles: ', angle_0, ' ', angle_1, ' ', edgeAngle, ' ', edgeAngle - 2*math.pi) + + if equal_angle(angle_0, edgeAngle): + angle_start = angle_0 + angle_end = angle_1 + else: + angle_start = angle_1 + angle_end = angle_0 + + bend_angle = angle_end - angle_start + angle_tan = angle_start + bend_angle/6.0 # need to have the angle_tan before correcting the sign + + if bend_angle < 0.0: + bend_angle = -bend_angle + + #print(math.degrees(bend_angle)) + return math.degrees(bend_angle) + +def smSketchOnSheetMetal(kfactor = 0.5, sketch = '', flipped = False, selFaceNames = '', MainObject = None): + resultSolid = MainObject.Shape.copy() + selElement = resultSolid.getElement(selFaceNames[0]) + LargeFace = smFace(selElement, resultSolid) + sketch_face = Part.makeFace(sketch.Shape.Wires,"Part::FaceMakerBullseye" ) + + + #To get thk of sheet, top face normal + thk = smthk(resultSolid, LargeFace) + #print(thk) + + #To get top face normal, flatsolid + solidlist = [] + normal = LargeFace.normalAt(0,0) + #To check face direction + coeff = normal.dot(sketch_face.Faces[0].normalAt(0,0)) + if coeff < 0 : + sketch_face.reverse() + Flatface = sketch_face.common(LargeFace) + BalanceFaces = sketch_face.cut(Flatface) + #Part.show(BalanceFace,"BalanceFace") + Flatsolid = Flatface.extrude(normal * -thk) + #Part.show(Flatsolid,"Flatsolid") + solidlist.append(Flatsolid) + + if BalanceFaces.Faces : + for BalanceFace in BalanceFaces.Faces : + #Part.show(BalanceFace,"BalanceFace") + TopFace = LargeFace + #Part.show(TopFace,"TopFace") + #flipped = False + while BalanceFace.Faces : + BendEdge = smGetEdge(BalanceFace, TopFace) + #Part.show(BendEdge,"BendEdge") + facelist = resultSolid.ancestorsOfType(BendEdge, Part.Face) + + #To get bend radius, bend angle + for cylface in facelist : + if issubclass(type(cylface.Surface),Part.Cylinder) : + break + if not(issubclass(type(cylface.Surface),Part.Cylinder)) : + break + #Part.show(cylface,"cylface") + for planeface in facelist : + if issubclass(type(planeface.Surface),Part.Plane) : + break + #Part.show(planeface,"planeface") + normal = planeface.normalAt(0,0) + revAxisV = cylface.Surface.Axis + revAxisP = cylface.Surface.Center + bendA = bendAngle(cylface, revAxisP) + #print([bendA, revAxisV, revAxisP, cylface.Orientation]) + + #To check bend direction + offsetface = cylface.makeOffsetShape(-thk, 0.0, fill = False) + #Part.show(offsetface,"offsetface") + if offsetface.Area < cylface.Area : + bendR = cylface.Surface.Radius - thk + flipped = True + else : + bendR = cylface.Surface.Radius + flipped = False + + #To arrive unfold Length, neturalRadius + unfoldLength = ( bendR + kfactor * thk ) * abs(bendA) * math.pi / 180.0 + neturalRadius = ( bendR + kfactor * thk ) + #print([unfoldLength,neturalRadius]) + + #To get faceNormal, bend face + faceNormal = normal.cross(revAxisV).normalize() + #print(faceNormal) + if bendR < cylface.Surface.Radius : + offsetSolid = cylface.makeOffsetShape(bendR/2.0, 0.0, fill = True) + else: + offsetSolid = cylface.makeOffsetShape(-bendR/2.0, 0.0, fill = True) + #Part.show(offsetSolid,"offsetSolid") + tool = BendEdge.copy() + FaceArea = tool.extrude(faceNormal * -unfoldLength ) + #Part.show(FaceArea,"FaceArea") + #Part.show(BalanceFace,"BalanceFace") + SolidFace = offsetSolid.common(FaceArea) + #Part.show(BendSolidFace,"BendSolidFace") + if not(SolidFace.Faces): + faceNormal = faceNormal * -1 + FaceArea = tool.extrude(faceNormal * -unfoldLength ) + BendSolidFace = BalanceFace.common(FaceArea) + #Part.show(FaceArea,"FaceArea") + #Part.show(BendSolidFace,"BendSolidFace") + #print([bendR, bendA, revAxisV, revAxisP, normal, flipped, BendSolidFace.Faces[0].normalAt(0,0)]) + + bendsolid = SheetMetalBendSolid.BendSolid(BendSolidFace.Faces[0], BendEdge, bendR, thk, neturalRadius, revAxisV, flipped) + #Part.show(bendsolid,"bendsolid") + solidlist.append(bendsolid) + + if flipped == True: + bendA = -bendA + if not(SolidFace.Faces): + revAxisV = revAxisV * -1 + sketch_face = BalanceFace.cut(BendSolidFace) + sketch_face.translate(faceNormal * unfoldLength) + #Part.show(sketch_face,"sketch_face") + sketch_face.rotate(revAxisP, -revAxisV, bendA) + #Part.show(sketch_face,"Rsketch_face") + TopFace = smCutFace(sketch_face, resultSolid) + #Part.show(TopFace,"TopFace") + + #To get top face normal, flatsolid + normal = TopFace.normalAt(0,0) + Flatface = sketch_face.common(TopFace) + BalanceFace = sketch_face.cut(Flatface) + #Part.show(BalanceFace,"BalanceFace") + Flatsolid = Flatface.extrude(normal * -thk) + #Part.show(Flatsolid,"Flatsolid") + solidlist.append(Flatsolid) + + #To get relief Solid fused + if len(solidlist) > 1 : + SMSolid = solidlist[0].multiFuse(solidlist[1:]) + #Part.show(SMSolid,"SMSolid") + SMSolid = SMSolid.removeSplitter() + else : + SMSolid = solidlist[0] + #Part.show(SMSolid,"SMSolid") + resultSolid = resultSolid.cut(SMSolid) + + Gui.ActiveDocument.getObject(MainObject.Name).Visibility = False + Gui.ActiveDocument.getObject(sketch.Name).Visibility = False + return resultSolid + +class SMSketchOnSheet: + def __init__(self, obj): + '''"Add Sketch On Sheet metal" ''' + selobj = Gui.Selection.getSelectionEx() + + obj.addProperty("App::PropertyLinkSub", "baseObject", "Parameters", "Base object").baseObject = (selobj[0].Object, selobj[0].SubElementNames) + obj.addProperty("App::PropertyLink","Sketch","Parameters","Sketch on Sheetmetal").Sketch = selobj[1].Object + obj.addProperty("App::PropertyFloatConstraint","kfactor","Parameters","Gap from left side").kfactor = (0.5,0.0,1.0,0.01) + obj.Proxy = self + + def execute(self, fp): + '''"Print a short message when doing a recomputation, this method is mandatory" ''' + + s = smSketchOnSheetMetal(kfactor = fp.kfactor, sketch = fp.Sketch, selFaceNames = fp.baseObject[1], MainObject = fp.baseObject[0]) + fp.Shape = s + +class SMSketchOnSheetVP: + "A View provider that nests children objects under the created one" + + def __init__(self, obj): + obj.Proxy = self + self.Object = obj.Object + + def attach(self, obj): + self.Object = obj.Object + return + + def updateData(self, fp, prop): + return + + def getDisplayModes(self,obj): + modes=[] + return modes + + def setDisplayMode(self,mode): + return mode + + def onChanged(self, vp, prop): + return + + def __getstate__(self): + # return {'ObjectName' : self.Object.Name} + return None + + def __setstate__(self,state): + if state is not None: + import FreeCAD + doc = FreeCAD.ActiveDocument #crap + self.Object = doc.getObject(state['ObjectName']) + + def claimChildren(self): + objs = [] + if hasattr(self.Object,"baseObject"): + objs.append(self.Object.baseObject[0]) + objs.append(self.Object.Sketch) + return objs + + def getIcon(self): + return os.path.join( iconPath , 'SketchOnSheet.svg') + +class SMSketchOnSheetPDVP: + "A View provider that nests children objects under the created one" + + def __init__(self, obj): + obj.Proxy = self + self.Object = obj.Object + + def attach(self, obj): + self.Object = obj.Object + return + + def updateData(self, fp, prop): + return + + def getDisplayModes(self,obj): + modes=[] + return modes + + def setDisplayMode(self,mode): + return mode + + def onChanged(self, vp, prop): + return + + def __getstate__(self): + # return {'ObjectName' : self.Object.Name} + return None + + def __setstate__(self,state): + if state is not None: + import FreeCAD + doc = FreeCAD.ActiveDocument #crap + self.Object = doc.getObject(state['ObjectName']) + + def claimChildren(self): + objs = [] + if hasattr(self.Object,"Sketch"): + objs.append(self.Object.Sketch) + return objs + + def getIcon(self): + return os.path.join( iconPath , 'SketchOnSheet.svg') + +class AddSketchOnSheetCommandClass(): + """Add Sketch On Sheet metal command""" + + def GetResources(self): + return {'Pixmap' : os.path.join( iconPath , 'SketchOnSheet.svg'), # the name of a svg file available in the resources + 'MenuText': QtCore.QT_TRANSLATE_NOOP('SheetMetal','Sketch On Sheet metal'), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP('SheetMetal','Sketch On Sheet metal')} + + def Activated(self): + doc = FreeCAD.ActiveDocument + view = Gui.ActiveDocument.ActiveView + activeBody = None + selobj = Gui.Selection.getSelectionEx()[0].Object + if hasattr(view,'getActiveObject'): + activeBody = view.getActiveObject('pdbody') + if not smIsOperationLegal(activeBody, selobj): + return + doc.openTransaction("SketchOnSheet") + if activeBody is None or not smIsPartDesign(selobj): + a = doc.addObject("Part::FeaturePython","SketchOnSheet") + SMSketchOnSheet(a) + SMSketchOnSheetVP(a.ViewObject) + else: + #FreeCAD.Console.PrintLog("found active body: " + activeBody.Name) + a = doc.addObject("PartDesign::FeaturePython","SketchOnSheet") + SMSketchOnSheet(a) + SMSketchOnSheetPDVP(a.ViewObject) + activeBody.addObject(a) + doc.recompute() + doc.commitTransaction() + return + + def IsActive(self): + if len(Gui.Selection.getSelection()) < 2 : + return False +# selobj = Gui.Selection.getSelection()[1] +# if str(type(selobj)) != "" : +# return False + return True + +Gui.addCommand('SMSketchOnSheet',AddSketchOnSheetCommandClass()) From 10edf89c0f660a896e6eb297dfa32c8d1c21204d Mon Sep 17 00:00:00 2001 From: jaisekjames <44542605+jaisekjames@users.noreply.github.com> Date: Sat, 28 Nov 2020 12:02:04 +0530 Subject: [PATCH 2/5] Update InitGui.py --- InitGui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/InitGui.py b/InitGui.py index ea313e4..2c2082e 100644 --- a/InitGui.py +++ b/InitGui.py @@ -58,8 +58,9 @@ def Initialize(self): import SheetMetalRelief import SheetMetalJunction import SheetMetalBend + SketchOnSheetMetalCmd import os.path - self.list = ["SMBase", "SMMakeWall", "SMExtrudeFace", "SMFoldWall", "SMUnfold", "SMMakeRelief", "SMMakeJunction", "SMMakeBend"] # A list of command names created in the line above + self.list = ["SMBase", "SMMakeWall", "SMExtrudeFace", "SMFoldWall", "SMUnfold", "SMMakeRelief", "SMMakeJunction", "SMMakeBend", "SMSketchOnSheet"] # A list of command names created in the line above if engineering_mode_enabled(): self.list.insert(self.list.index("SMUnfold") + 1,"SMUnfoldUnattended") self.appendToolbar("Sheet Metal",self.list) # creates a new toolbar with your commands From a8e2217adc027a90f290b4381d2c65ce8e11e9ec Mon Sep 17 00:00:00 2001 From: jaisekjames <44542605+jaisekjames@users.noreply.github.com> Date: Sat, 28 Nov 2020 12:05:07 +0530 Subject: [PATCH 3/5] icon --- Resources/icons/SketchOnSheet.svg | 600 ++++++++++++++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 Resources/icons/SketchOnSheet.svg diff --git a/Resources/icons/SketchOnSheet.svg b/Resources/icons/SketchOnSheet.svg new file mode 100644 index 0000000..d01564b --- /dev/null +++ b/Resources/icons/SketchOnSheet.svg @@ -0,0 +1,600 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 540226dc2796e270713a444fb2a9c2195f18b028 Mon Sep 17 00:00:00 2001 From: luz paz Date: Sat, 28 Nov 2020 06:02:09 -0500 Subject: [PATCH 4/5] Added missing import Still doesn't fix the issue in https://github.com/shaise/FreeCAD_SheetMetal/pull/146#issuecomment-735216604 --- InitGui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InitGui.py b/InitGui.py index 2c2082e..e40d7b1 100644 --- a/InitGui.py +++ b/InitGui.py @@ -58,7 +58,7 @@ def Initialize(self): import SheetMetalRelief import SheetMetalJunction import SheetMetalBend - SketchOnSheetMetalCmd + import SketchOnSheetMetalCmd import os.path self.list = ["SMBase", "SMMakeWall", "SMExtrudeFace", "SMFoldWall", "SMUnfold", "SMMakeRelief", "SMMakeJunction", "SMMakeBend", "SMSketchOnSheet"] # A list of command names created in the line above if engineering_mode_enabled(): From 262fd0c5089458ff5cc26f79ebf07c33407fadbc Mon Sep 17 00:00:00 2001 From: jaisekjames <44542605+jaisekjames@users.noreply.github.com> Date: Sat, 28 Nov 2020 18:39:10 +0530 Subject: [PATCH 5/5] white spaces --- SketchOnSheetMetalCmd.py | 62 +++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/SketchOnSheetMetalCmd.py b/SketchOnSheetMetalCmd.py index fad61ce..900650f 100644 --- a/SketchOnSheetMetalCmd.py +++ b/SketchOnSheetMetalCmd.py @@ -45,16 +45,16 @@ def smBelongToBody(item, body): if obj.Name == item.Name: return True return False - + def smIsPartDesign(obj): return str(obj).find("