diff --git a/src/Mod/OpenSCAD/Init.py b/src/Mod/OpenSCAD/Init.py index 6d1c27ded9..0936abb7d4 100644 --- a/src/Mod/OpenSCAD/Init.py +++ b/src/Mod/OpenSCAD/Init.py @@ -2,7 +2,7 @@ # (c) 2001 Juergen Riegel #*************************************************************************** -#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 * +#* Copyright (c) 2002 Juergen Riegel * #* * #* This file is part of the FreeCAD CAx development system. * #* * @@ -22,18 +22,21 @@ #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * -#* Juergen Riegel 2002 * #***************************************************************************/ -import FreeCAD,os -FreeCAD.addImportType("OpenSCAD CSG Format (*.csg)","importCSG") + +import os +import FreeCAD + +FreeCAD.addImportType("OpenSCAD CSG Format (*.csg)", "importCSG") + param = FreeCAD.ParamGet(\ - "User parameter:BaseApp/Preferences/Mod/OpenSCAD") + "User parameter:BaseApp/Preferences/Mod/OpenSCAD") openscadfilename = param.GetString('openscadexecutable') openscadbin = openscadfilename and os.path.isfile(openscadfilename) + if openscadbin: - FreeCAD.addImportType("OpenSCAD Format (*.scad)","importCSG") + FreeCAD.addImportType("OpenSCAD Format (*.scad)", "importCSG") FreeCAD.__unit_test__ += ["TestOpenSCADApp"] -FreeCAD.addExportType("OpenSCAD CSG Format (*.csg)","exportCSG") -FreeCAD.addExportType("OpenSCAD Format (*.scad)","exportCSG") - +FreeCAD.addExportType("OpenSCAD CSG Format (*.csg)", "exportCSG") +FreeCAD.addExportType("OpenSCAD Format (*.scad)", "exportCSG") diff --git a/src/Mod/OpenSCAD/InitGui.py b/src/Mod/OpenSCAD/InitGui.py index 498b39bbc6..938e70a81a 100644 --- a/src/Mod/OpenSCAD/InitGui.py +++ b/src/Mod/OpenSCAD/InitGui.py @@ -5,7 +5,7 @@ # runs when the gui is up #*************************************************************************** -#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 * +#* Copyright (c) 2002 Juergen Riegel * #* * #* This file is part of the FreeCAD CAx development system. * #* * @@ -25,7 +25,6 @@ #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * -#* Juergen Riegel 2002 * #***************************************************************************/ import FreeCAD @@ -63,7 +62,7 @@ class OpenSCADWorkbench ( Workbench ): import OpenSCADUtils openscadfilename = OpenSCADUtils.searchforopenscadexe() if openscadfilename: #automatic search was succsessful - FreeCAD.addImportType("OpenSCAD Format (*.scad)","importCSG") + FreeCAD.addImportType("OpenSCAD Format (*.scad)","importCSG") param.SetString('openscadexecutable',openscadfilename) #save the result if openscadfilename: commands.extend(['OpenSCAD_AddOpenSCADElement', 'OpenSCAD_MeshBoolean', @@ -82,6 +81,7 @@ class OpenSCADWorkbench ( Workbench ): FreeCADGui.addIconPath(":/icons") FreeCADGui.addLanguagePath(":/translations") FreeCADGui.addPreferencePage(":/ui/openscadprefs-base.ui","OpenSCAD") + def GetClassName(self): return "Gui::PythonWorkbench" diff --git a/src/Mod/OpenSCAD/OpenSCAD2Dgeom.py b/src/Mod/OpenSCAD/OpenSCAD2Dgeom.py index f4fe2f39d8..87faa38c78 100644 --- a/src/Mod/OpenSCAD/OpenSCAD2Dgeom.py +++ b/src/Mod/OpenSCAD/OpenSCAD2Dgeom.py @@ -19,7 +19,7 @@ #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - 2D helper functions" +__title__ = "FreeCAD OpenSCAD Workbench - 2D helper functions" __author__ = "Sebastian Hoogen" __url__ = ["https://www.freecadweb.org"] diff --git a/src/Mod/OpenSCAD/OpenSCADCommands.py b/src/Mod/OpenSCAD/OpenSCADCommands.py index f8335f27af..8c33634195 100644 --- a/src/Mod/OpenSCAD/OpenSCADCommands.py +++ b/src/Mod/OpenSCAD/OpenSCADCommands.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2012 Sebastian Hoogen * #* * #* This program is free software; you can redistribute it and/or modify * @@ -20,7 +19,7 @@ #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - GUI Commands" +__title__ = "FreeCAD OpenSCAD Workbench - GUI Commands" __author__ = "Sebastian Hoogen" __url__ = ["https://www.freecadweb.org"] @@ -106,7 +105,7 @@ class ExplodeGroup: explode(obj) def GetResources(self): - return {'Pixmap' : 'OpenSCAD_Explode_Group', + return {'Pixmap' : 'OpenSCAD_Explode_Group', 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Explode Group'), 'ToolTip': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Remove fusion, apply placement to children, and color randomly')} @@ -122,8 +121,8 @@ class ColorCodeShape: objs=FreeCAD.ActiveDocument.Objects colorcodeshapes.colorcodeshapes(objs) def GetResources(self): - return {'Pixmap' : 'OpenSCAD_ColorCodeShape', - 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes'), + return {'Pixmap' : 'OpenSCAD_ColorCodeShape', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes'), 'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes by validity and type')} class Edgestofaces: @@ -411,7 +410,7 @@ class AddSCADTask: except OpenSCADUtils.OpenSCADError as e: self.form.textMsg.setPlainText(e.value) FreeCAD.Console.PrintError(e.value) - + def refreshelement(self): self.form.textMsg.setPlainText('') doc=FreeCAD.activeDocument() @@ -428,7 +427,7 @@ class AddSCADTask: with open(filename,'r') as fp : data = fp.read() self.form.textEdit.setText(data) - + def saveelement(self) : filename, filter = QtGui.QFileDialog.getSaveFileName(parent=self.form, caption='Open file', dir='.', filter='OpenSCAD Files (*.scad)',selectedFilter='',option=0) @@ -436,7 +435,7 @@ class AddSCADTask: Text = self.form.textEdit.toPlainText() with open(filename,'w') as fp : fp.write(Text) - + class OpenSCADMeshBooleanWidget(QtGui.QWidget): def __init__(self,*args): QtGui.QWidget.__init__(self,*args) @@ -513,8 +512,8 @@ class AddOpenSCADElement: FreeCADGui.Control.showDialog(panel) def GetResources(self): - return {'Pixmap' : 'OpenSCAD_AddOpenSCADElement', - 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement', 'Add OpenSCAD Element...'), + return {'Pixmap' : 'OpenSCAD_AddOpenSCADElement', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement', 'Add OpenSCAD Element...'), 'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement', 'Add an OpenSCAD element by entering OpenSCAD code and executing the OpenSCAD binary')} @@ -528,8 +527,8 @@ class OpenSCADMeshBoolean: FreeCADGui.Control.showDialog(panel) def GetResources(self): - return {'Pixmap' : 'OpenSCAD_MeshBooleans', - 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean','Mesh Boolean...'), + return {'Pixmap' : 'OpenSCAD_MeshBooleans', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean','Mesh Boolean...'), 'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean', 'Export objects as meshes and use OpenSCAD to perform a boolean operation')} @@ -567,8 +566,8 @@ class Minkowski: importCSG.process_ObjectsViaOpenSCAD(FreeCAD.activeDocument(),objList,"minkowski") FreeCAD.ActiveDocument.recompute() def GetResources(self): - return {'Pixmap' : 'OpenSCAD_Minkowski', - 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Minkowski'), + return {'Pixmap' : 'OpenSCAD_Minkowski', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Minkowski'), 'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Perform Minkowski')} FreeCADGui.addCommand('OpenSCAD_ColorCodeShape',ColorCodeShape()) diff --git a/src/Mod/OpenSCAD/OpenSCADFeatures.py b/src/Mod/OpenSCAD/OpenSCADFeatures.py index 4f3f4d21d8..f7f5db2851 100644 --- a/src/Mod/OpenSCAD/OpenSCADFeatures.py +++ b/src/Mod/OpenSCAD/OpenSCADFeatures.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2012 Sebastian Hoogen * #* * #* This program is free software; you can redistribute it and/or modify * @@ -20,7 +19,7 @@ #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - Parametric Features" +__title__ = "FreeCAD OpenSCAD Workbench - Parametric Features" __author__ = "Sebastian Hoogen" __url__ = ["https://www.freecadweb.org"] @@ -32,13 +31,15 @@ except NameError: ''' This Script includes python Features to represent OpenSCAD Operations ''' + + class ViewProviderTree: "A generic View Provider for Elements with Children" - + def __init__(self, obj): obj.Proxy = self self.Object = obj.Object - + def attach(self, obj): self.Object = obj.Object return @@ -80,7 +81,7 @@ class ViewProviderTree: objs.extend(self.Object.Children) return objs - + def getIcon(self): import OpenSCAD_rc if isinstance(self.Object.Proxy,RefineShape): @@ -165,6 +166,7 @@ static char * openscadlogo_xpm[] = { "4444444444444444"}; """ + class OpenSCADPlaceholder: def __init__(self,obj,children=None,arguments=None): obj.addProperty("App::PropertyLinkList",'Children','OpenSCAD',"Base Objects") @@ -174,13 +176,14 @@ class OpenSCADPlaceholder: obj.Children = children if arguments: obj.Arguments = arguments - + def execute(self,fp): import Part fp.Shape = Part.Compound([]) #empty Shape -class Resize : - def __init__(self,obj,target,vector) : + +class Resize: + def __init__(self,obj,target,vector): import FreeCAD #self.Obj = obj self.Target = target @@ -193,24 +196,24 @@ class Resize : obj.Proxy = self def onChanged(self, fp, prop): - if prop in ['Object','Vector'] : + if prop in ['Object','Vector']: self.createGeometry(fp) - + def execute(self, fp): self.createGeometry(fp) - def createGeometry(self, fp) : + def createGeometry(self, fp): import FreeCAD mat = FreeCAD.Matrix() mat.A11 = self.Vector[0] mat.A22 = self.Vector[1] mat.A33 = self.Vector[2] - fp.Shape = self.Target.Shape.transformGeometry(mat) + fp.Shape = self.Target.Shape.transformGeometry(mat) def __getstate__(self): return None - - def __setstate__(self,state): + + def __setstate__(self,state): return None @@ -234,15 +237,16 @@ class MatrixTransform: def execute(self, fp): if fp.Matrix and fp.Base: - sh=fp.Base.Shape#.copy() - m=sh.Placement.toMatrix().multiply(fp.Matrix) + sh = fp.Base.Shape#.copy() + m = sh.Placement.toMatrix().multiply(fp.Matrix) fp.Shape = sh.transformGeometry(m) #else: #FreeCAD.Console.PrintMessage('base %s\nmat %s/n' % (fp.Base,fp.Matrix)) + class ImportObject: def __init__(self, obj,child=None): - obj.addProperty("App::PropertyLink","Base","Base", + obj.addProperty("App::PropertyLink", "Base", "Base", "The base object that must be tranfsformed") obj.Proxy = self obj.Base = child @@ -256,10 +260,11 @@ class ImportObject: # if fp.Base: # fp.Shape = fp.Base.Shape.copy() + class RefineShape: '''return a refined shape''' - def __init__(self, obj,child=None): - obj.addProperty("App::PropertyLink","Base","Base", + def __init__(self, obj, child=None): + obj.addProperty("App::PropertyLink", "Base", "Base", "The base object that must be refined") obj.Proxy = self obj.Base = child @@ -271,14 +276,14 @@ class RefineShape: def execute(self, fp): if fp.Base and fp.Base.Shape.isValid(): import OpenSCADUtils - sh=fp.Base.Shape.removeSplitter() - fp.Shape=OpenSCADUtils.applyPlacement(sh) + sh = fp.Base.Shape.removeSplitter() + fp.Shape = OpenSCADUtils.applyPlacement(sh) class IncreaseTolerance: '''increase the tolerance of every vertex in the current implementation its' placement is linked''' def __init__(self,obj,child,tolerance=0): - obj.addProperty("App::PropertyLink","Base","Base", + obj.addProperty("App::PropertyLink", "Base", "Base", "The base object that wire must be extracted") obj.addProperty("App::PropertyDistance","Vertex","Tolerance","Vertexes tolerance (0 default)") obj.addProperty("App::PropertyDistance","Edge","Tolerance","Edges tolerance (0 default)") @@ -307,11 +312,11 @@ class IncreaseTolerance: # New properties else: for vertex in sh.Vertexes: - vertex.Tolerance = max(vertex.Tolerance,fp.Vertex.Value) + vertex.Tolerance = max(vertex.Tolerance, fp.Vertex.Value) for edge in sh.Edges: - edge.Tolerance = max(edge.Tolerance,fp.Edge.Value) + edge.Tolerance = max(edge.Tolerance, fp.Edge.Value) for face in sh.Faces: - face.Tolerance = max(face.Tolerance,fp.Face.Value) + face.Tolerance = max(face.Tolerance, fp.Face.Value) fp.Shape = sh fp.Placement = sh.Placement @@ -319,7 +324,7 @@ class IncreaseTolerance: class GetWire: '''return the first wire from a given shape''' - def __init__(self, obj,child=None): + def __init__(self, obj, child=None): obj.addProperty("App::PropertyLink","Base","Base", "The base object that wire must be extracted") obj.Proxy = self @@ -362,8 +367,8 @@ class Frustum: import FreeCAD,Part #from draftlibs import fcgeo plm = fp.Placement - wires=[] - faces=[] + wires = [] + faces = [] for ir,r in enumerate((fp.Radius1,fp.Radius2)): angle = (math.pi*2)/fp.FacesNumber pts = [FreeCAD.Vector(r.Value,0,ir*fp.Height.Value)] @@ -374,13 +379,13 @@ class Frustum: pts.append(pts[0]) shape = Part.makePolygon(pts) face = Part.Face(shape) - if ir==0: #top face + if ir == 0: #top face face.reverse() wires.append(shape) faces.append(face) - #shellperi=Part.makeRuledSurface(*wires) - shellperi=Part.makeLoft(wires) - shell=Part.Shell(shellperi.Faces+faces) + #shellperi = Part.makeRuledSurface(*wires) + shellperi = Part.makeLoft(wires) + shell = Part.Shell(shellperi.Faces+faces) fp.Shape = Part.Solid(shell) fp.Placement = plm @@ -445,7 +450,7 @@ class Twist: pipe_shell.build() faces.extend(pipe_shell.shape().Faces) try: - fullshell=Part.Shell(faces) + fullshell = Part.Shell(faces) solid=Part.Solid(fullshell) if solid.Volume < 0: solid.reverse() @@ -486,9 +491,9 @@ class PrismaticToroid: sweep_angle_per_segment = fp.Angle / num_segments # Always >= min_sweep_angle_per_segment # From the OpenSCAD documentation: - # The 2D shape must lie completely on either the right (recommended) or the left side of the Y-axis. - # More precisely speaking, every vertex of the shape must have either x >= 0 or x <= 0. If the shape - # spans the X axis a warning appears in the console windows and the rotate_extrude() is ignored. If + # The 2D shape must lie completely on either the right (recommended) or the left side of the Y-axis. + # More precisely speaking, every vertex of the shape must have either x >= 0 or x <= 0. If the shape + # spans the X axis a warning appears in the console windows and the rotate_extrude() is ignored. If # the 2D shape touches the Y axis, i.e. at x=0, it must be a line that touches, not a point. for start_face in fp.Base.Shape.Faces: @@ -509,7 +514,7 @@ class PrismaticToroid: edges.append(edge) ribs.append(Part.Wire(edges)) - + faces = [] shell = Part.makeShellFromWires (ribs) for face in shell.Faces: @@ -520,7 +525,7 @@ class PrismaticToroid: faces.append(start_face.reversed()) # Reversed so the normal faces out of the shell faces.append(end_face) else: - faces.append(start_face) + faces.append(start_face) faces.append(end_face.reversed()) # Reversed so the normal faces out of the shell try: diff --git a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py index f784ffcf9a..3edd93db26 100644 --- a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py +++ b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2021 Chris Hennes * #* * #* This program is free software; you can redistribute it and/or modify * diff --git a/src/Mod/OpenSCAD/OpenSCADTest/gui/test_dummy.py b/src/Mod/OpenSCAD/OpenSCADTest/gui/test_dummy.py index 1b7d729ff0..e77b27c267 100644 --- a/src/Mod/OpenSCAD/OpenSCADTest/gui/test_dummy.py +++ b/src/Mod/OpenSCAD/OpenSCADTest/gui/test_dummy.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2021 Chris Hennes * #* * #* This program is free software; you can redistribute it and/or modify * @@ -26,6 +25,7 @@ import unittest import FreeCAD import OpenSCAD + class TestDummy(unittest.TestCase): MODULE = 'test_dummy' # file name without extension @@ -35,4 +35,4 @@ class TestDummy(unittest.TestCase): pass def test_tests(self): - self.assertTrue(True) \ No newline at end of file + self.assertTrue(True) diff --git a/src/Mod/OpenSCAD/OpenSCADUtils.py b/src/Mod/OpenSCAD/OpenSCADUtils.py index aedbf6fd01..c7908a3ae9 100644 --- a/src/Mod/OpenSCAD/OpenSCADUtils.py +++ b/src/Mod/OpenSCAD/OpenSCADUtils.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2012 Sebastian Hoogen * #* * #* This program is free software; you can redistribute it and/or modify * @@ -20,7 +19,7 @@ #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - Utility Functions" +__title__ = "FreeCAD OpenSCAD Workbench - Utility Functions" __author__ = "Sebastian Hoogen" __url__ = ["https://www.freecadweb.org"] @@ -29,7 +28,8 @@ This Script includes various python helper functions that are shared across the module ''' from exportCSG import mesh2polyhedron -import FreeCAD, io +import FreeCAD +import io if FreeCAD.GuiUp: try: @@ -49,16 +49,20 @@ try: except (ImportError, AttributeError): BaseError = RuntimeError + class OpenSCADError(BaseError): - def __init__(self,value): - self.value= value + def __init__(self, value): + self.value = value #def __repr__(self): # return self.msg def __str__(self): return repr(self.value) + def getopenscadexe(osfilename=None): - import os,subprocess,time + import os + import subprocess + import time if not osfilename: import FreeCAD osfilename = FreeCAD.ParamGet(\ @@ -68,8 +72,11 @@ def getopenscadexe(osfilename=None): return osfilename return searchforopenscadexe() + def searchforopenscadexe(): - import os,sys,subprocess + import os + import sys + import subprocess if sys.platform == 'win32': testpaths = [os.path.join(os.environ.get('Programfiles(x86)','C:'),\ 'OpenSCAD\\openscad.exe')] @@ -84,19 +91,19 @@ def searchforopenscadexe(): b'POSIX path of (application file id "org.openscad.OpenSCAD"' b'as alias)\n' b'end tell') - p1=subprocess.Popen(['osascript','-'],stdin=subprocess.PIPE,\ + p1=subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE,\ stdout=subprocess.PIPE,stderr=subprocess.PIPE) - stdout,stderr = p1.communicate(ascript) + stdout, stderr = p1.communicate(ascript) if p1.returncode == 0: opathl = stdout.decode().split('\n') if sys.version_info.major >= 3 else stdout.split('\n') - if len(opathl) >=1: + if len(opathl) >= 1: return opathl[0]+'Contents/MacOS/OpenSCAD' #test the default path testpath="/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD" if os.path.isfile(testpath): return testpath else: #unix - p1=subprocess.Popen(['which','openscad'],stdout=subprocess.PIPE) + p1 = subprocess.Popen(['which','openscad'], stdout=subprocess.PIPE) if p1.wait() == 0: output = p1.stdout.read() if sys.version_info.major >= 3: @@ -104,59 +111,71 @@ def searchforopenscadexe(): opath = output.split('\n')[0] return opath + def workaroundforissue128needed(): '''sets the import path depending on the OpenSCAD Version for versions <= 2012.06.23 to the current working dir for versions above to the inputfile dir see https://github.com/openscad/openscad/issues/128''' - vdate=getopenscadversion().split('-')[0] - vdate=vdate.split(' ')[2].split('.') + vdate = getopenscadversion().split('-')[0] + vdate = vdate.split(' ')[2].split('.') if len(vdate) == 1: # probably YYYYMMDD format (i.e. git version) vdate = vdate[0] year, mon = int("".join(vdate[0:4])), int("".join(vdate[4:6])) else: # YYYY.MM(.DD?) (latest release) - year,mon=int(vdate[0]),int(vdate[1]) - return (year<2012 or (year==2012 and (mon <6 or (mon == 6 and \ - (len(vdate)<3 or int(vdate[2]) <=23))))) + year, mon = int(vdate[0]), int(vdate[1]) + return (year < 2012 or (year == 2012 and (mon < 6 or (mon == 6 and \ + (len(vdate) < 3 or int(vdate[2]) <= 23))))) #ifdate=int(vdate[0])+(int(vdate[1])-1)/12.0 #if len(vdate)>2: # fdate+=int((vdate[2])-1)/12.0/31.0 #return fdate < 2012.4759 + def getopenscadversion(osfilename=None): - import os,subprocess,time + import os + import subprocess + import time if not osfilename: import FreeCAD osfilename = FreeCAD.ParamGet(\ "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetString('openscadexecutable') if osfilename and os.path.isfile(osfilename): - with subprocess.Popen([osfilename,'-v'],\ - stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True) as p: + with subprocess.Popen([osfilename, '-v'],\ + stdout = subprocess.PIPE,stderr=subprocess.PIPE, universal_newlines=True) as p: p.wait() - stdout=p.stdout.read().strip() - stderr=p.stderr.read().strip() + stdout = p.stdout.read().strip() + stderr = p.stderr.read().strip() return (stdout or stderr) + def newtempfilename(): - import os,time - formatstr='fc-%05d-%06d-%06d' + import os + import time + formatstr = 'fc-%05d-%06d-%06d' count = 0 while True: - count+=1 - yield formatstr % (os.getpid(),int(time.time()*100) % 1000000,count) + count += 1 + yield formatstr % (os.getpid(), int(time.time()*100) % 1000000, count) -tempfilenamegen=newtempfilename() +tempfilenamegen = newtempfilename() -def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=False): + +def callopenscad(inputfilename,outputfilename=None, outputext='csg', keepname=False): '''call the open scad binary returns the filename of the result (or None), please delete the file afterwards''' - import FreeCAD,os,subprocess,tempfile,time - def check_output2(*args,**kwargs): + import FreeCAD + import os + import subprocess + import tempfile + import time + + def check_output2(*args, **kwargs): kwargs.update({'stdout':subprocess.PIPE,'stderr':subprocess.PIPE}) - p=subprocess.Popen(*args,**kwargs) - stdoutd,stderrd = p.communicate() + p = subprocess.Popen(*args, **kwargs) + stdoutd, stderrd = p.communicate() stdoutd = stdoutd.decode("utf8") stderrd = stderrd.decode("utf8") if p.returncode != 0: @@ -172,7 +191,7 @@ def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=Fals osfilename = preferences.GetString('openscadexecutable') transferMechanism = preferences.GetInt('transfermechanism',0) if transferMechanism == 0: # Use the Python temp-directory creation function - transferDirectory = tempfile.gettempdir() + transferDirectory = tempfile.gettempdir() elif transferMechanism == 1: # Use a user-specified directory for the transfer transferDirectory = preferences.GetString('transferdirectory') elif transferMechanism == 2: # Use pipes instead of tempfiles @@ -185,18 +204,19 @@ def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=Fals dir1 = transferDirectory if keepname: - outputfilename=os.path.join(dir1,'%s.%s' % (os.path.split(\ + outputfilename = os.path.join(dir1, '%s.%s' % (os.path.split(\ inputfilename)[1].rsplit('.',1)[0],outputext)) else: - outputfilename=os.path.join(dir1,'%s.%s' % \ + outputfilename = os.path.join(dir1,'%s.%s' % \ (next(tempfilenamegen),outputext)) - check_output2([osfilename,'-o',outputfilename, inputfilename]) + check_output2([osfilename, '-o', outputfilename, inputfilename]) return outputfilename else: raise OpenSCADError('OpenSCAD executable unavailable') + def call_openscad_with_pipes(input_filename, output_filename, output_extension, keep_name): - ''' Call OpenSCAD by sending input data to stdin, and read the output from stdout. + ''' Call OpenSCAD by sending input data to stdin, and read the output from stdout. Returns the tempfile the output is stored in on success, or None on failure. NOTE: This feature was added to OpenSCAD in 2021.01''' @@ -209,13 +229,13 @@ def call_openscad_with_pipes(input_filename, output_filename, output_extension, # Load the data back in from our tempfile: with open(input_filename) as datafile: openscad_data = datafile.read() - # On the command line this looks like: + # On the command line this looks like: # $ cat myfile.scad | openscad --export-format csg -o - - preferences = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") openscad_executable = preferences.GetString('openscadexecutable') - p = subprocess.Popen([openscad_executable,"--export-format","csg", "-o", "-", "-"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, + p = subprocess.Popen([openscad_executable,"--export-format","csg", "-o", "-", "-"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdoutd,stderrd = p.communicate (input = openscad_data.encode('utf8'), timeout=15) stdoutd = stdoutd.decode("utf8") @@ -235,26 +255,28 @@ def call_openscad_with_pipes(input_filename, output_filename, output_extension, outfile.write(stdoutd); return output_filename return None - + + def callopenscadstring(scadstr,outputext='csg'): '''create a tempfile and call the open scad binary returns the filename of the result (or None), please delete the file afterwards''' import os,tempfile,time - dir1=tempfile.gettempdir() - inputfilename=os.path.join(dir1,'%s.scad' % next(tempfilenamegen)) + dir1 = tempfile.gettempdir() + inputfilename = os.path.join(dir1,'%s.scad' % next(tempfilenamegen)) inputfile = io.open(inputfilename,'w', encoding="utf8") inputfile.write(scadstr) inputfile.close() - outputfilename = callopenscad(inputfilename,outputext=outputext,\ + outputfilename = callopenscad(inputfilename, outputext=outputext,\ keepname=True) os.unlink(inputfilename) return outputfilename + def reverseimporttypes(): '''allows to search for supported filetypes by module''' - def getsetfromdict(dict1,index): + def getsetfromdict(dict1, index): if index in dict1: return dict1[index] else: @@ -262,7 +284,7 @@ def reverseimporttypes(): dict1[index]=set1 return set1 - importtypes={} + importtypes = {} import FreeCAD for key,value in FreeCAD.getImportType().items(): if type(value) is str: @@ -278,10 +300,11 @@ def fcsubmatrix(m): as a list of row vectors""" return [[m.A11,m.A12,m.A13],[m.A21,m.A22,m.A23],[m.A31,m.A32,m.A33]] -def multiplymat(l,r): + +def multiplymat(l, r): """multiply matrices given as lists of row vectors""" - rt=zip(*r) #transpose r - rt=list(rt) + rt = zip(*r) #transpose r + rt = list(rt) mat=[] for y in range(len(rt)): mline=[] @@ -290,11 +313,13 @@ def multiplymat(l,r): mat.append(mline) return mat -def isorthogonal(submatrix,precision=4): + +def isorthogonal(submatrix, precision=4): """checking if 3x3 Matrix is orthogonal (M*Transp(M)==I)""" - prod=multiplymat(submatrix,list(zip(*submatrix))) + prod = multiplymat(submatrix,list(zip(*submatrix))) return [[round(f,precision) for f in line] \ - for line in prod]==[[1,0,0],[0,1,0],[0,0,1]] + for line in prod] == [[1,0,0],[0,1,0],[0,0,1]] + def detsubmatrix(s): """get the determinant of a 3x3 Matrix given as list of row vectors""" @@ -302,54 +327,58 @@ def detsubmatrix(s): s[0][2]*s[1][0]*s[2][1]-s[2][0]*s[1][1]*s[0][2]-\ s[2][1]*s[1][2]*s[0][0]-s[2][2]*s[1][0]*s[0][1] -def isspecialorthogonalpython(submat,precision=4): - return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision)==1 +def isspecialorthogonalpython(submat, precision=4): + return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision) == 1 -def isrotoinversionpython(submat,precision=4): - return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision)==-1 +def isrotoinversionpython(submat, precision=4): + return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision) == -1 -def isspecialorthogonal(mat,precision=4): +def isspecialorthogonal(mat, precision=4): return abs(mat.submatrix(3).isOrthogonal(10**(-precision))-1.0) < \ 10**(-precision) and \ abs(mat.submatrix(3).determinant()-1.0) < 10**(-precision) -def decomposerotoinversion(m,precision=4): + +def decomposerotoinversion(m, precision=4): import FreeCAD rmat = [[round(f,precision) for f in line] for line in fcsubmatrix(m)] cmat = FreeCAD.Matrix() - if rmat ==[[-1,0,0],[0,1,0],[0,0,1]]: + if rmat == [[-1,0,0],[0,1,0],[0,0,1]]: cmat.scale(-1,1,1) return m*cmat,FreeCAD.Vector(1) - elif rmat ==[[1,0,0],[0,-1,0],[0,0,1]]: + elif rmat == [[1,0,0],[0,-1,0],[0,0,1]]: cmat.scale(1,-1,1) return m*cmat, FreeCAD.Vector(0,1) - elif rmat ==[[1,0,0],[0,1,0],[0,0,-1]]: + elif rmat == [[1,0,0],[0,1,0],[0,0,-1]]: cmat.scale(1,1,-1) return m*cmat, FreeCAD.Vector(0,0,1) else: cmat.scale(1,1,-1) return m*cmat, FreeCAD.Vector(0,0,1) -def mirror2mat(nv,bv): + +def mirror2mat(nv, bv): import FreeCAD """calculate the transformation matrix of a mirror feature""" - mbef=FreeCAD.Matrix() + mbef = FreeCAD.Matrix() mbef.move(bv * -1) - maft=FreeCAD.Matrix() + maft = FreeCAD.Matrix() maft.move(bv) return maft*vec2householder(nv)*mbef + def vec2householder(nv): """calculated the householder matrix for a given normal vector""" import FreeCAD - lnv=nv.dot(nv) - l=2/lnv if lnv > 0 else 0 - hh=FreeCAD.Matrix(nv.x*nv.x*l,nv.x*nv.y*l,nv.x*nv.z*l,0,\ + lnv = nv.dot(nv) + l = 2/lnv if lnv > 0 else 0 + hh = FreeCAD.Matrix(nv.x*nv.x*l,nv.x*nv.y*l,nv.x*nv.z*l,0,\ nv.y*nv.x*l,nv.y*nv.y*l,nv.y*nv.z*l,0,\ nv.z*nv.x*l,nv.z*nv.y*l,nv.z*nv.z*l,0,0,0,0,0) return FreeCAD.Matrix()-hh -def mirrormesh(msh,vec): + +def mirrormesh(msh, vec): """mirrormesh(mesh,vector) where mesh is a mesh object and vector is a Base.Vector""" poly = mesh2polyhedron(msh) vec_string = '['+str(vec.x)+','+str(vec.y)+','+str(vec.z)+']' @@ -358,7 +387,8 @@ def mirrormesh(msh,vec): mi.flipNormals() return mi -def scalemesh(msh,vec): + +def scalemesh(msh, vec): """scalemesh(mesh,vector) where mesh is a mesh object and vector is a Base.Vector""" poly = mesh2polyhedron(msh) vec_string = '['+str(vec.x)+','+str(vec.y)+','+str(vec.z)+']' @@ -367,7 +397,8 @@ def scalemesh(msh,vec): mi.flipNormals() return mi -def resizemesh(msh,vec): + +def resizemesh(msh, vec): """resizemesh(mesh,vector) where mesh is a mesh object and vector is a Base.Vector""" poly = mesh2polyhedron(msh) vec_string = '['+str(vec.x)+','+str(vec.y)+','+str(vec.z)+']' @@ -376,9 +407,11 @@ def resizemesh(msh,vec): mi.flipNormals() return mi + def angneg(d): return d if (d <= 180.0) else (d-360) + def shorthexfloat(f): mantisse, exponent = f.hex().split('p',1) return '%sp%s' % (mantisse.rstrip('0'),exponent) @@ -388,7 +421,7 @@ def comparerotations(r1,r2): import FreeCAD '''compares two rotations a value of zero means that they are identical''' - r2c=FreeCAD.Rotation(r2) + r2c = FreeCAD.Rotation(r2) r2c.invert() return r1.multiply(r2c).Angle @@ -438,10 +471,11 @@ def findbestmatchingrotation(r1): -2.0, -1.0) def tup2nvect(tup): """convert a tuple to a normalized vector""" - v=FreeCAD.Vector(*tup) + v = FreeCAD.Vector(*tup) v.normalize() return v + def wkaxes(): """well known axes for rotations""" vtupl=((1,0,0),(0,1,0),(0,0,1), @@ -449,34 +483,35 @@ def findbestmatchingrotation(r1): (1,1,1),(1,1,-1),(1,-1,1),(-1,1,1)) return tuple(tup2nvect(tup) for tup in vtupl) - bestrot=FreeCAD.Rotation() - dangle = comparerotations(r1,bestrot) + bestrot = FreeCAD.Rotation() + dangle = comparerotations(r1, bestrot) for axis in wkaxes(): for angle in vangl: for axissign in (1.0,-1.0): - r2=FreeCAD.Rotation(axis*axissign,angle) + r2=FreeCAD.Rotation(axis*axissign, angle) dangletest = comparerotations(r1,r2) if dangletest < dangle: bestrot = r2 dangle = dangletest return (bestrot,dangle) -def roundrotation(rot,maxangulardistance=1e-5): + +def roundrotation(rot, maxangulardistance=1e-5): '''guess the rotation axis and angle for a rotation recreated from rounded floating point values (from a quaterion or transformation matrix)''' - def teststandardrot(r1,maxangulardistance=1e-5): + def teststandardrot(r1, maxangulardistance=1e-5): '''test a few common rotations beforehand''' import FreeCAD,itertools eulers = [] for angle in (90,-90,180,45,-45,135,-135): for euler in itertools.permutations((0,0,angle)): eulers.append(euler) - for euler in itertools.product((0,45,90,135,180,-45,-90,-135),repeat=3): + for euler in itertools.product((0,45,90,135,180,-45,-90,-135), repeat=3): eulers.append(euler) for euler in eulers: r2 = FreeCAD.Rotation(*euler) - if comparerotations(r1,r2) < maxangulardistance: + if comparerotations(r1, r2) < maxangulardistance: return r2 if rot.isNull(): @@ -491,11 +526,12 @@ def roundrotation(rot,maxangulardistance=1e-5): else: #use original return rot + def callopenscadmeshstring(scadstr): """Call OpenSCAD and return the result as a Mesh""" import Mesh,os - tmpfilename=callopenscadstring(scadstr,'stl') - newmesh=Mesh.Mesh() + tmpfilename = callopenscadstring(scadstr, 'stl') + newmesh = Mesh.Mesh() newmesh.read(tmpfilename) try: os.unlink(tmpfilename) @@ -503,7 +539,8 @@ def callopenscadmeshstring(scadstr): pass return newmesh -def meshopinline(opname,iterable1): + +def meshopinline(opname, iterable1): """uses OpenSCAD to combine meshes takes the name of the CGAL operation and an iterable (tuple,list) of FreeCAD Mesh objects @@ -513,17 +550,19 @@ def meshopinline(opname,iterable1): return callopenscadmeshstring('%s(){%s}' % (opname,' '.join(\ (mesh2polyhedron(meshobj) for meshobj in iterable1)))) -def meshoptempfile(opname,iterable1): + +def meshoptempfile(opname, iterable1): """uses OpenSCAD to combine meshes takes the name of the CGAL operation and an iterable (tuple,list) of FreeCAD Mesh objects uses stl files to supply the mesh data """ - import os,tempfile - dir1=tempfile.gettempdir() + import os + import tempfile + dir1 = tempfile.gettempdir() filenames = [] for mesh in iterable1: - outputfilename=os.path.join(dir1,'%s.stl' % next(tempfilenamegen)) + outputfilename = os.path.join(dir1, '%s.stl' % next(tempfilenamegen)) mesh.write(outputfilename) filenames.append(outputfilename) #absolute path causes error. We rely that the scad file will be in the dame tmpdir @@ -538,14 +577,15 @@ def meshoptempfile(opname,iterable1): pass return result -def meshoponobjs(opname,inobjs): + +def meshoponobjs(opname, inobjs): """ takes a string (operation name) and a list of Feature Objects returns a mesh and a list of objects that were used Part Objects will be meshed - """ - objs=[] - meshes=[] + """ + objs = [] + meshes = [] for obj in inobjs: if obj.isDerivedFrom('Mesh::Feature'): objs.append(obj) @@ -572,19 +612,22 @@ def meshoponobjs(opname,inobjs): else: return (None,[]) -def process2D_ObjectsViaOpenSCADShape(ObjList,Operation,doc): - import FreeCAD,importDXF - import os,tempfile + +def process2D_ObjectsViaOpenSCADShape(ObjList, Operation, doc): + import FreeCAD + import importDXF + import os + import tempfile # https://www.freecadweb.org/tracker/view.php?id=3419 params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") fn = params.GetInt('fnForImport',32) fnStr = ",$fn=" + str(fn) # - dir1=tempfile.gettempdir() + dir1 = tempfile.gettempdir() filenames = [] for item in ObjList : outputfilename=os.path.join(dir1,'%s.dxf' % next(tempfilenamegen)) - importDXF.export([item],outputfilename,True,True) + importDXF.export([item],outputfilename, True, True) filenames.append(outputfilename) # https://www.freecadweb.org/tracker/view.php?id=3419 dxfimports = ' '.join("import(file = \"%s\" %s);" % \ @@ -603,7 +646,8 @@ def process2D_ObjectsViaOpenSCADShape(ObjList,Operation,doc): pass return face -def process2D_ObjectsViaOpenSCAD(ObjList,Operation,doc=None): + +def process2D_ObjectsViaOpenSCAD(ObjList, Operation, doc=None): import FreeCAD doc = doc or FreeCAD.activeDocument() face=process2D_ObjectsViaOpenSCADShape(ObjList,Operation,doc) @@ -615,8 +659,11 @@ def process2D_ObjectsViaOpenSCAD(ObjList,Operation,doc=None): index.ViewObject.hide() return(obj) -def process3D_ObjectsViaOpenSCADShape(ObjList,Operation,maxmeshpoints=None): - import FreeCAD,Mesh,Part + +def process3D_ObjectsViaOpenSCADShape(ObjList, Operation, maxmeshpoints=None): + import FreeCAD + import Mesh + import Part params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") if False: # disabled due to issue 1292 import MeshPart @@ -628,27 +675,29 @@ def process3D_ObjectsViaOpenSCADShape(ObjList,Operation,maxmeshpoints=None): meshes = [Mesh.Mesh(obj.Shape.tessellate(params.GetFloat(\ 'meshmaxlength',1.0))) for obj in ObjList] if max(mesh.CountPoints for mesh in meshes) < \ - (maxmeshpoints or params.GetInt('tempmeshmaxpoints',5000)): + (maxmeshpoints or params.GetInt('tempmeshmaxpoints', 5000)): stlmesh = meshoptempfile(Operation,meshes) - sh=Part.Shape() - sh.makeShapeFromMesh(stlmesh.Topology,0.1) + sh = Part.Shape() + sh.makeShapeFromMesh(stlmesh.Topology, 0.1) solid = Part.Solid(sh) - solid=solid.removeSplitter() + solid = solid.removeSplitter() if solid.Volume < 0: solid.complement() return solid -def process3D_ObjectsViaOpenSCAD(doc,ObjList,Operation): + +def process3D_ObjectsViaOpenSCAD(doc,ObjList, Operation): solid = process3D_ObjectsViaOpenSCADShape(ObjList,Operation) if solid is not None: - obj=doc.addObject('Part::Feature',Operation) #non-parametric object + obj = doc.addObject('Part::Feature',Operation) #non-parametric object obj.Shape=solid#.removeSplitter() if FreeCAD.GuiUp: - for index in ObjList : + for index in ObjList: index.ViewObject.hide() return(obj) -def process_ObjectsViaOpenSCADShape(doc,children,name,maxmeshpoints=None): + +def process_ObjectsViaOpenSCADShape(doc, children, name, maxmeshpoints=None): if all((not obj.Shape.isNull() and obj.Shape.Volume == 0) \ for obj in children): return process2D_ObjectsViaOpenSCADShape(children,name,doc) @@ -672,17 +721,18 @@ def process_ObjectsViaOpenSCAD(doc,children,name): FreeCAD.Console.PrintError( translate('OpenSCAD',\ "Error all shapes must be either 2D or both must be 3D")+u'\n') + def removesubtree(objs): - def addsubobjs(obj,toremoveset): + def addsubobjs(obj, toremoveset): toremove.add(obj) for subobj in obj.OutList: - addsubobjs(subobj,toremoveset) + addsubobjs(subobj, toremoveset) import FreeCAD - toremove=set() + toremove = set() for obj in objs: - addsubobjs(obj,toremove) - checkinlistcomplete =False + addsubobjs(obj, toremove) + checkinlistcomplete = False while not checkinlistcomplete: for obj in toremove: if (obj not in objs) and (frozenset(obj.InList) - toremove): @@ -693,6 +743,7 @@ def removesubtree(objs): for obj in toremove: obj.Document.removeObject(obj.Name) + def applyPlacement(shape): if shape.Placement.isNull(): return shape diff --git a/src/Mod/OpenSCAD/TestOpenSCADGui.py b/src/Mod/OpenSCAD/TestOpenSCADGui.py index d0165e9810..f5e392ca2e 100644 --- a/src/Mod/OpenSCAD/TestOpenSCADGui.py +++ b/src/Mod/OpenSCAD/TestOpenSCADGui.py @@ -22,7 +22,7 @@ # *************************************************************************** # Gui Unit tests for the FEM module -from OpenSCADTest.gui.test_dummy import TestDummy as OpenSCADGuiTestDummy +from OpenSCADTest.gui.test_dummy import TestDummy as OpenSCADGuiTestDummy # dummy usage to get flake8 and lgtm quiet diff --git a/src/Mod/OpenSCAD/colorcodeshapes.py b/src/Mod/OpenSCAD/colorcodeshapes.py index 2654e4875f..283d73bf2e 100644 --- a/src/Mod/OpenSCAD/colorcodeshapes.py +++ b/src/Mod/OpenSCAD/colorcodeshapes.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2012 Sebastian Hoogen * #* * #* This program is free software; you can redistribute it and/or modify * @@ -20,7 +19,7 @@ #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - 2D helper functions" +__title__ = "FreeCAD OpenSCAD Workbench - 2D helper functions" __author__ = "Sebastian Hoogen" __url__ = ["https://www.freecadweb.org"] @@ -29,20 +28,23 @@ This Script includes python functions to find out the most basic shape type in a compound and to change the color of shapes according to their shape type ''' + def shapedict(shapelst): return dict([(shape.hashCode(),shape) for shape in shapelst]) + def shapeset(shapelst): return set([shape.hashCode() for shape in shapelst]) + def mostbasiccompound(comp): '''searches for the most basic shape in a Compound''' - solids=shapeset(comp.Solids) - shells=shapeset(comp.Shells) - faces=shapeset(comp.Faces) - wires=shapeset(comp.Wires) - edges=shapeset(comp.Edges) - vertexes=shapeset(comp.Vertexes) + solids = shapeset(comp.Solids) + shells = shapeset(comp.Shells) + faces = shapeset(comp.Faces) + wires = shapeset(comp.Wires) + edges = shapeset(comp.Edges) + vertexes = shapeset(comp.Vertexes) #FreeCAD.Console.PrintMessage('%s\n' % (str((len(solids),len(shells),len(faces),len(wires),len(edges),len(vertexes))))) for shape in comp.Solids: shells -= shapeset(shape.Shells) @@ -80,7 +82,7 @@ def mostbasiccompound(comp): return "Solid" def colorcodeshapes(objs): - shapecolors={ + shapecolors = { "Compound":(0.3,0.3,0.4), "CompSolid":(0.1,0.5,0.0), "Solid":(0.0,0.8,0.0), @@ -93,17 +95,17 @@ def colorcodeshapes(objs): None:(0.0,0.0,0.0)} for obj in objs: - if hasattr(obj,'Shape'): + if hasattr(obj, 'Shape'): try: if obj.Shape.isNull(): continue if not obj.Shape.isValid(): - color=(1.0,0.4,0.4) + color = (1.0,0.4,0.4) else: st=obj.Shape.ShapeType - if st in ["Compound","CompSolid"]: + if st in ["Compound", "CompSolid"]: st = mostbasiccompound(obj.Shape) - color=shapecolors[st] + color = shapecolors[st] obj.ViewObject.ShapeColor = color except Exception: raise diff --git a/src/Mod/OpenSCAD/expandplacements.py b/src/Mod/OpenSCAD/expandplacements.py index b90171910d..f589d4fcee 100644 --- a/src/Mod/OpenSCAD/expandplacements.py +++ b/src/Mod/OpenSCAD/expandplacements.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2012 Sebastian Hoogen * #* * #* This program is free software; you can redistribute it and/or modify * @@ -20,7 +19,7 @@ #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - expand placements and matrices functions" +__title__ = "FreeCAD OpenSCAD Workbench - expand placements and matrices functions" __author__ = "Sebastian Hoogen" __url__ = ["https://www.freecadweb.org"] @@ -34,6 +33,7 @@ from OpenSCADFeatures import * from OpenSCADUtils import isspecialorthogonal import replaceobj + def likeprimitive(obj,extrusion=False): '''we can't push the matrix transformation further down''' return not obj.OutList or obj.isDerivedFrom('Part::Extrusion')\ @@ -41,33 +41,34 @@ def likeprimitive(obj,extrusion=False): or obj.isDerivedFrom('Part::FeaturePython')) or \ not obj.isDerivedFrom('Part::Feature') + def expandplacementsmatrix(obj,matrix): '''expand afine transformation down the feature tree''' - ownmatrix=matrix.multiply(obj.Placement.toMatrix()) + ownmatrix = matrix.multiply(obj.Placement.toMatrix()) if obj.isDerivedFrom('Part::Feature') and \ - isinstance(obj.Proxy,MatrixTransform): - innermatrix=ownmatrix.multiply(obj.Matrix) + isinstance(obj.Proxy, MatrixTransform): + innermatrix = ownmatrix.multiply(obj.Matrix) if likeprimitive(obj.Base,True): #this matrix is needed - obj.Placement=FreeCAD.Placement() + obj.Placement = FreeCAD.Placement() obj.Matrix = innermatrix - else: #the inner object is not a primitive - expandplacementsmatrix(obj.Base,innermatrix) + else: #the inner object is not a primitive + expandplacementsmatrix(obj.Base, innermatrix) #remove the matrix object for parent in obj.Base.InList: - replaceobj.replaceobj(parent,obj,obj.Base) + replaceobj.replaceobj(parent, obj, obj.Base) out.Document.removeObject(obj.Name) - elif likeprimitive(obj,True): + elif likeprimitive(obj, True): #if isspecialorthogonalpython(fcsubmatrix(ownmatrix)): if isspecialorthogonal(ownmatrix): - obj.Placement=FreeCAD.Placement() + obj.Placement = FreeCAD.Placement() #this should never happen unless matrices cancel out - obj.Placement=FreeCAD.Placement(ownmatrix) + obj.Placement = FreeCAD.Placement(ownmatrix) else: - newobj=doc.addObject("Part::FeaturePython",'exp_trans') + newobj = doc.addObject("Part::FeaturePython", 'exp_trans') MatrixTransform(newobj,ownmatrix,obj) #This object is not mutable GUI ViewProviderTree(newobj.ViewObject) for parent in obj.InList: - replaceobj.replaceobj(parent,obj,newobj) # register the new object in the feature tree + replaceobj.replaceobj(parent, obj, newobj) # register the new object in the feature tree obj.Placement=FreeCAD.Placement() else: #not a primitive for outobj in obj.OutList: @@ -79,38 +80,38 @@ def expandplacementsmatrix(obj,matrix): outobj.Matrix = newmatrix outobj.Base.Placement=FreeCAD.Placement() else: #remove the MatrixTransformation - plainobj=outobj.Base + plainobj = outobj.Base for parent in outobj.InList: - replaceobj.replaceobj(parent,outobj,plainobj) + replaceobj.replaceobj(parent, outobj, plainobj) outobj.Document.removeObject(outobj.Name) expandplacementsmatrix(outobj,newmatrix) else: - expandplacementsmatrix(outobj,ownmatrix) - obj.Placement=FreeCAD.Placement() + expandplacementsmatrix(outobj, ownmatrix) + obj.Placement = FreeCAD.Placement() def expandplacements(obj,placement): - ownplacement=placement.multiply(obj.Placement) - if obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy,MatrixTransform): + ownplacement = placement.multiply(obj.Placement) + if obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy, MatrixTransform): #expandplacementsmatrix(obj,ownplacement.toMatrix()) expandplacementsmatrix(obj,placement.toMatrix()) - elif likeprimitive(obj,False): - obj.Placement=ownplacement + elif likeprimitive(obj, False): + obj.Placement = ownplacement elif obj.isDerivedFrom('Part::Mirroring'): import OpenSCADUtils mm = OpenSCADUtils.mirror2mat(obj.Normal,obj.Base) #TODO: set the base to 0,0,0 - innerp=FreeCAD.Placement(mm * ownplacement.toMatrix() *mm) - expandplacements(obj.Source,innerp) - obj.Placement=FreeCAD.Placement() + innerp = FreeCAD.Placement(mm * ownplacement.toMatrix() *mm) + expandplacements(obj.Source, innerp) + obj.Placement = FreeCAD.Placement() else: for outobj in obj.OutList: if obj.isDerivedFrom('Part::Extrusion'): - obj.Dir=ownplacement.Rotation.multVec(obj.Dir) + obj.Dir = ownplacement.Rotation.multVec(obj.Dir) elif obj.isDerivedFrom('Part::Revolution'): - obj.Axis=ownplacement.Rotation.multVec(obj.Axis) + obj.Axis = ownplacement.Rotation.multVec(obj.Axis) #obj.Base=ownplacement.Rotation.multVec(obj.Base) - expandplacements(outobj,ownplacement) - obj.Placement=FreeCAD.Placement() + expandplacements(outobj, ownplacement) + obj.Placement = FreeCAD.Placement() #expandplacements(rootobj,FreeCAD.Placement()) diff --git a/src/Mod/OpenSCAD/exportCSG.py b/src/Mod/OpenSCAD/exportCSG.py index 01385ffcb1..1036e2e0a5 100644 --- a/src/Mod/OpenSCAD/exportCSG.py +++ b/src/Mod/OpenSCAD/exportCSG.py @@ -1,6 +1,5 @@ #*************************************************************************** -#* * #* Copyright (c) 2012 Keith Sloan * #* * #* This program is free software; you can redistribute it and/or modify * @@ -18,14 +17,14 @@ #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * -#* * +#* * #* Acknowledgements : * #* * #* Thanks to shoogen on the FreeCAD forum for programming advice * #* and some code. * #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - CSG exporter Version" +__title__ = "FreeCAD OpenSCAD Workbench - CSG exporter Version" __author__ = "Keith Sloan " __url__ = ["http://www.sloan-home.co.uk/Export/Export.html"] @@ -41,10 +40,10 @@ else: #fafs = '$fa = 12, $fs = 2' #convexity = 'convexity = 10' params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") -fa = params.GetFloat('exportFa',12.0) -fs = params.GetFloat('exportFs',2.0) -conv = params.GetInt('exportConvexity',10) -fafs = '$fa = %f, $fs = %f' % (fa,fs) +fa = params.GetFloat('exportFa', 12.0) +fs = params.GetFloat('exportFs', 2.0) +conv = params.GetInt('exportConvexity', 10) +fafs = '$fa = %f, $fs = %f' % (fa, fs) convexity = 'convexity = %d' % conv #*************************************************************************** # Radius values not fixed for value apart from cylinder & Cone @@ -59,7 +58,8 @@ def center(b): else: return 'false' -def check_multmatrix(csg,ob,x,y,z): + +def check_multmatrix(csg, ob, x, y, z): b = FreeCAD.Vector(x,y,z) if ob.Placement.isNull(): return 0 # center = false no mm @@ -76,68 +76,72 @@ def check_multmatrix(csg,ob,x,y,z): 0, 0, 0, 1]]){\n") return 1 # center = false and mm + def mesh2polyhedron(mesh): - pointstr=','.join(['[%f,%f,%f]' % tuple(vec) for vec in mesh.Topology[0]]) - trianglestr=','.join(['[%d,%d,%d]' % tuple(tri) for tri in mesh.Topology[1]]) + pointstr = ','.join(['[%f,%f,%f]' % tuple(vec) for vec in mesh.Topology[0]]) + trianglestr = ','.join(['[%d,%d,%d]' % tuple(tri) for tri in mesh.Topology[1]]) #avoid deprecation warning by changing triangles to faces - #return 'polyhedron ( points = [%s], triangles = [%s]);' % (pointstr,trianglestr) - return 'polyhedron ( points = [%s], faces = [%s]);' % (pointstr,trianglestr) + #return 'polyhedron ( points = [%s], triangles = [%s]);' % (pointstr, trianglestr) + return 'polyhedron ( points = [%s], faces = [%s]);' % (pointstr, trianglestr) + def vector2d(v): return [v[0],v[1]] + def vertexs2polygon(vertex): - pointstr=','.join(['[%f, %f]' % tuple(vector2d(v.Point)) for v in vertex]) + pointstr = ','.join(['[%f, %f]' % tuple(vector2d(v.Point)) for v in vertex]) return 'polygon ( points = [%s], paths = undef, convexity = 1);}' % pointstr + def shape2polyhedron(shape): import MeshPart return mesh2polyhedron(MeshPart.meshFromShape(Shape=shape,\ - Deflection= params.GetFloat('meshdeflection',0.0))) + Deflection = params.GetFloat('meshdeflection', 0.0))) + def process_object(csg,ob): - print("Placement") print("Pos : "+str(ob.Placement.Base)) print("axis : "+str(ob.Placement.Rotation.Axis)) print("angle : "+str(ob.Placement.Rotation.Angle)) - - if ob.TypeId == "Part::Sphere" : - print("Sphere Radius : "+str(ob.Radius)) - check_multmatrix(csg,ob,0,0,0) - csg.write("sphere($fn = 0, "+fafs+", r = "+str(ob.Radius)+");\n") - - elif ob.TypeId == "Part::Box" : - print("cube : ("+ str(ob.Length)+","+str(ob.Width)+","+str(ob.Height)+")") - mm = check_multmatrix(csg,ob,-ob.Length/2,-ob.Width/2,-ob.Height/2) - csg.write("cube (size = ["+str(ob.Length.Value)+", "+str(ob.Width.Value)+", "+str(ob.Height.Value)+"], center = "+center(mm)+");\n") - if mm == 1 : csg.write("}\n") - elif ob.TypeId == "Part::Cylinder" : - print("cylinder : Height "+str(ob.Height)+ " Radius "+str(ob.Radius)) - mm = check_multmatrix(csg,ob,0,0,-ob.Height/2) - csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value)+ ", r1 = "+str(ob.Radius.Value)+\ + if ob.TypeId == "Part::Sphere": + print("Sphere Radius : "+str(ob.Radius)) + check_multmatrix(csg, ob, 0, 0, 0) + csg.write("sphere($fn = 0, "+fafs+", r = "+str(ob.Radius)+");\n") + + elif ob.TypeId == "Part::Box": + print("cube : ("+ str(ob.Length)+","+str(ob.Width)+","+str(ob.Height)+")") + mm = check_multmatrix(csg,ob,-ob.Length/2,-ob.Width/2,-ob.Height/2) + csg.write("cube (size = ["+str(ob.Length.Value)+", "+str(ob.Width.Value)+", "+str(ob.Height.Value)+"], center = "+center(mm)+");\n") + if mm == 1 : csg.write("}\n") + + elif ob.TypeId == "Part::Cylinder": + print("cylinder : Height "+str(ob.Height) + " Radius "+str(ob.Radius)) + mm = check_multmatrix(csg, ob, 0, 0, -ob.Height/2) + csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value) + ", r1 = "+str(ob.Radius.Value)+\ ", r2 = " + str(ob.Radius.Value) + ", center = "+center(mm)+");\n") - if mm == 1 : csg.write("}\n") - - elif ob.TypeId == "Part::Cone" : - print("cone : Height "+str(ob.Height)+ " Radius1 "+str(ob.Radius1)+" Radius2 "+str(ob.Radius2)) - mm = check_multmatrix(csg,ob,0,0,-ob.Height/2) + if mm == 1 : csg.write("}\n") + + elif ob.TypeId == "Part::Cone": + print("cone : Height "+str(ob.Height) + " Radius1 "+str(ob.Radius1)+" Radius2 "+str(ob.Radius2)) + mm = check_multmatrix(csg, ob, 0, 0, -ob.Height/2) csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value)+ ", r1 = "+str(ob.Radius1.Value)+\ ", r2 = "+str(ob.Radius2.Value)+", center = "+center(mm)+");\n") if mm == 1 : csg.write("}\n") - elif ob.TypeId == "Part::Torus" : + elif ob.TypeId == "Part::Torus": print("Torus") print(ob.Radius1) print(ob.Radius2) - if ob.Angle3 == 360.00 : - mm = check_multmatrix(csg,ob,0,0,0) + if ob.Angle3 == 360.00: + mm = check_multmatrix(csg, ob, 0, 0, 0) csg.write("rotate_extrude("+convexity+", $fn = 0, "+fafs+")\n") csg.write("multmatrix([[1, 0, 0, "+str(ob.Radius1)+"], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])\n") - csg.write("circle($fn = 0, "+fafs+", r = "+str(ob.Radius2)+");\n") + csg.write("circle($fn = 0, "+fafs+", r = "+str(ob.Radius2)+");\n") if mm == 1 : csg.write("}\n") - else : # Cannot convert to rotate extrude so best effort is polyhedron + else: # Cannot convert to rotate extrude so best effort is polyhedron csg.write('%s\n' % shape2polyhedron(ob.Shape)) elif ob.TypeId == "Part::Prism": @@ -146,24 +150,24 @@ def process_object(csg,ob): # r = str(ob.Length/2.0/math.sin(math.pi/ob.Polygon)) r = str(ob.Circumradius) # length seems to be the outer radius h = str(ob.Height.Value) - mm = check_multmatrix(csg,ob,0,0,-float(h)/2) + mm = check_multmatrix(csg, ob, 0, 0, -float(h)/2) csg.write("cylinder($fn = "+f+", "+fafs+", h = "+h+", r1 = "+r+\ ", r2 = "+r+", center = "+center(mm)+");\n") if mm == 1: csg.write("}\n") elif ob.TypeId == "Part::RegularPolygon": - mm = check_multmatrix(csg,ob,0,0,-float(h)/2) + mm = check_multmatrix(csg, ob, 0, 0, -float(h)/2) csg.write("circle($fn = "+str(ob.NumberOfSides)+", "+fafs+", r = "+str(ob.Radius)+");\n") if mm == 1: csg.write("}\n") - elif ob.TypeId == "Part::Extrusion" : + elif ob.TypeId == "Part::Extrusion": print("Extrusion") print(ob.Base) print(ob.Base.Name) if ob.Base.isDerivedFrom('Part::Part2DObjectPython') and \ - hasattr(ob.Base,'Proxy') and hasattr(ob.Base.Proxy,'TypeId'): - ptype=ob.Base.Proxy.TypeId - if ptype == "Polygon" : + hasattr(ob.Base,'Proxy') and hasattr(ob.Base.Proxy, 'TypeId'): + ptype = ob.Base.Proxy.TypeId + if ptype == "Polygon": f = str(ob.Base.FacesNumber) r = str(ob.Base.Radius) h = str(ob.Dir[2]) @@ -175,7 +179,7 @@ def process_object(csg,ob): ", r2 = "+r+", center = "+center(mm)+");\n") if mm == 1: csg.write("}\n") - elif ptype == "Circle" : + elif ptype == "Circle": r = str(ob.Base.Radius) h = str(ob.Dir[2]) print("Radius : " + r) @@ -185,10 +189,10 @@ def process_object(csg,ob): ", r2 = "+r+", center = "+center(mm)+");\n") if mm == 1: csg.write("}\n") - elif ptype == "Wire" : + elif ptype == "Wire": print("Wire extrusion") print(ob.Base) - mm = check_multmatrix(csg,ob,0,0,0) + mm = check_multmatrix(csg, ob, 0, 0, 0) csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = "+center(mm)+", "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n") csg.write(vertexs2polygon(ob.Base.Shape.Vertexes)) if mm == 1: csg.write("}\n") @@ -197,76 +201,76 @@ def process_object(csg,ob): mm = check_multmatrix(csg,ob,0,0,0) csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = true, "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n") csg.write("square (size = ["+str(ob.Base.Length.Value)+", "+str(ob.Base.Width.Value)+"], center = "+center(mm)+");\n}\n") - if mm == 1: csg.write("}\n") + if mm == 1: csg.write("}\n") elif ob.Base.Name.startswith('this_is_a_bad_idea'): pass else: pass # There should be a fallback solution - elif ob.TypeId == "Part::Cut" : + elif ob.TypeId == "Part::Cut": print("Cut") csg.write("difference() {\n") process_object(csg,ob.Base) process_object(csg,ob.Tool) csg.write("}\n") - elif ob.TypeId == "Part::Fuse" : + elif ob.TypeId == "Part::Fuse": print("union") csg.write("union() {\n") process_object(csg,ob.Base) process_object(csg,ob.Tool) csg.write("}\n") - elif ob.TypeId == "Part::Common" : + elif ob.TypeId == "Part::Common": print("intersection") csg.write("intersection() {\n") process_object(csg,ob.Base) process_object(csg,ob.Tool) csg.write("}\n") - elif ob.TypeId == "Part::MultiFuse" : + elif ob.TypeId == "Part::MultiFuse": print("Multi Fuse / union") csg.write("union() {\n") for subobj in ob.Shapes: process_object(csg,subobj) csg.write("}\n") - - elif ob.TypeId == "Part::MultiCommon" : + + elif ob.TypeId == "Part::MultiCommon": print("Multi Common / intersection") csg.write("intersection() {\n") for subobj in ob.Shapes: process_object(csg,subobj) csg.write("}\n") - elif ob.isDerivedFrom('Part::Feature') : + elif ob.isDerivedFrom('Part::Feature'): print("Part::Feature") mm = check_multmatrix(csg,ob,0,0,0) csg.write('%s\n' % shape2polyhedron(ob.Shape)) if mm == 1 : csg.write("}\n") -def export(exportList,filename): +def export(exportList, filename): "called when FreeCAD exports a file" - + # process Objects print("\nStart Export 0.1d\n") print("Open Output File") csg = pythonopen(filename,'w') print("Write Initial Output") - # Not sure if comments as per scad are allowed in csg file + # Not sure if comments as per scad are allowed in csg file csg.write("// CSG file generated from FreeCAD %s\n" % \ '.'.join(FreeCAD.Version()[0:3])) - #write initial group statements - not sure if required + #write initial group statements - not sure if required csg.write("group() {\n group(){\n") for ob in exportList: print(ob) - print("Name : "+ob.Name) - print("Type : "+ob.TypeId) + print("Name : " + ob.Name) + print("Type : " + ob.TypeId) print("Shape : ") print(ob.Shape) - process_object(csg,ob) - + process_object(csg, ob) + # write closing group braces csg.write("}\n}\n") - # close file + # close file csg.close() FreeCAD.Console.PrintMessage("successfully exported" + " " + filename) diff --git a/src/Mod/OpenSCAD/importCSG.py b/src/Mod/OpenSCAD/importCSG.py index 281901df6d..e6737581a0 100644 --- a/src/Mod/OpenSCAD/importCSG.py +++ b/src/Mod/OpenSCAD/importCSG.py @@ -18,21 +18,23 @@ #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * -#* * -#* Acknowledgements : * +#* * +#* Acknowledgements: * #* * #* Thanks to shoogen on the FreeCAD forum and Peter Li * #* for programming advice and some code. * #* * #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - CSG importer" +__title__ = "FreeCAD OpenSCAD Workbench - CSG importer" __author__ = "Keith Sloan " __url__ = ["http://www.sloan-home.co.uk/ImportCSG"] printverbose = False -import FreeCAD, io, os +import FreeCAD +import io +import os import ply.lex as lex import ply.yacc as yacc @@ -42,7 +44,7 @@ from OpenSCADFeatures import * from OpenSCADUtils import * params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") -printverbose = params.GetBool('printVerbose',False) +printverbose = params.GetBool('printVerbose', False) if FreeCAD.GuiUp: gui = True @@ -50,14 +52,15 @@ else: if printverbose: print("FreeCAD Gui not present.") gui = False -hassetcolor=[] -alreadyhidden=[] +hassetcolor = [] +alreadyhidden = [] original_root_objects = [] -# Get the token map from the lexer. This is required. +# Get the token map from the lexer. This is required. import tokrules from tokrules import tokens + def shallHide(subject): for obj in subject.OutListRecursive: if "Matrix_Union" in str(obj.FullName): @@ -66,6 +69,7 @@ def shallHide(subject): return True return False + def setColorRecursively(obj, color, transp): ''' For some reason a part made by cutting or fusing other parts do not have a color @@ -83,16 +87,18 @@ def setColorRecursively(obj, color, transp): if currentObject not in hassetcolor: setColorRecursively(currentObject, color, transp) + def fixVisibility(): - # After an import, only the remaining root objects that we created should be visible, not any + # After an import, only the remaining root objects that we created should be visible, not any # of their individual component objects. But make sure to only handle the ones we just imported, # not anything that already existed. And objects that exist at the toplevel without any # children are ignored. for root_object in FreeCAD.ActiveDocument.RootObjects: if root_object not in original_root_objects: - root_object.ViewObject.Visibility=True + root_object.ViewObject.Visibility = True for obj in root_object.OutListRecursive: - obj.ViewObject.Visibility=False + obj.ViewObject.Visibility = False + if gui: try: @@ -107,6 +113,7 @@ if gui: from PySide import QtGui return QtGui.QApplication.translate(context, text, None) + def open(filename): "called when freecad opens a file." global doc @@ -114,10 +121,10 @@ def open(filename): docname = os.path.splitext(os.path.basename(filename))[0] doc = FreeCAD.newDocument(docname) if filename.lower().endswith('.scad'): - tmpfile=callopenscad(filename) + tmpfile = callopenscad(filename) if workaroundforissue128needed(): - pathName = '' #https://github.com/openscad/openscad/issues/128 - #pathName = os.getcwd() #https://github.com/openscad/openscad/issues/128 + pathName = '' # https://github.com/openscad/openscad/issues/128 + #pathName = os.getcwd() # https://github.com/openscad/openscad/issues/128 else: pathName = os.path.dirname(os.path.normpath(filename)) processcsg(tmpfile) @@ -130,23 +137,24 @@ def open(filename): processcsg(filename) return doc -def insert(filename,docname): + +def insert(filename, docname): "called when freecad imports a file" global doc global pathName groupname_unused = os.path.splitext(os.path.basename(filename))[0] try: - doc=FreeCAD.getDocument(docname) + doc = FreeCAD.getDocument(docname) for obj in doc.RootObjects: original_root_objects.append(obj) except NameError: - doc=FreeCAD.newDocument(docname) + doc = FreeCAD.newDocument(docname) #importgroup = doc.addObject("App::DocumentObjectGroup",groupname) if filename.lower().endswith('.scad'): - tmpfile=callopenscad(filename) + tmpfile = callopenscad(filename) if workaroundforissue128needed(): - pathName = '' #https://github.com/openscad/openscad/issues/128 - #pathName = os.getcwd() #https://github.com/openscad/openscad/issues/128 + pathName = '' # https://github.com/openscad/openscad/issues/128 + #pathName = os.getcwd() # https://github.com/openscad/openscad/issues/128 else: pathName = os.path.dirname(os.path.normpath(filename)) processcsg(tmpfile) @@ -161,13 +169,13 @@ def insert(filename,docname): def processcsg(filename): global doc - if printverbose: print ('ImportCSG Version 0.6a') + if printverbose: print('ImportCSG Version 0.6a') # Build the lexer if printverbose: print('Start Lex') lex.lex(module=tokrules) if printverbose: print('End Lex') - # Build the parser + # Build the parser if printverbose: print('Load Parser') # No debug out otherwise Linux has protection exception parser = yacc.yacc(debug=False) @@ -191,6 +199,7 @@ def processcsg(filename): FreeCAD.Console.PrintMessage('End processing CSG file\n') doc.recompute() + def p_block_list_(p): ''' block_list : statement @@ -200,34 +209,37 @@ def p_block_list_(p): ''' #if printverbose: print("Block List") #if printverbose: print(p[1]) - if(len(p) > 2) : + if(len(p) > 2): if printverbose: print(p[2]) p[0] = p[1] + p[2] - else : + else: p[0] = p[1] #if printverbose: print("End Block List") + def p_render_action(p): 'render_action : render LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' if printverbose: print("Render (ignored)") p[0] = p[6] + def p_group_action1(p): 'group_action1 : group LPAREN RPAREN OBRACE block_list EBRACE' if printverbose: print("Group") # Test if need for implicit fuse - if (len(p[5]) > 1) : - p[0] = [fuse(p[5],"Group")] - else : + if (len(p[5]) > 1): + p[0] = [fuse(p[5], "Group")] + else: p[0] = p[5] -def p_group_action2(p) : +def p_group_action2(p): 'group_action2 : group LPAREN RPAREN SEMICOL' if printverbose: print("Group2") p[0] = [] - -def p_boolean(p) : + + +def p_boolean(p): ''' boolean : true | false @@ -238,10 +250,12 @@ def p_boolean(p) : # 'string : QUOTE ID QUOTE' # p[0] = p[2] + def p_stripped_string(p): 'stripped_string : STRING' p[0] = p[1].strip('"') + def p_statement(p): '''statement : part | operation @@ -254,26 +268,29 @@ def p_statement(p): ''' p[0] = p[1] + def p_anymodifier(p): '''anymodifier : MODIFIERBACK | MODIFIERDEBUG | MODIFIERROOT | MODIFIERDISABLE ''' - #just return the plain modifier for now - #has to be changed when the modifiers are implemented - #please note that disabled objects usually are stripped of the CSG output during compilation + # just return the plain modifier for now + # has to be changed when the modifiers are implemented + # please note that disabled objects usually are stripped of the CSG output during compilation p[0] = p[1] + def p_statementwithmod(p): '''statementwithmod : anymodifier statement''' - #ignore the modifiers but add them to the label + # ignore the modifiers but add them to the label modifier = p[1] obj = p[2] - if hasattr(obj,'Label'): + if hasattr(obj, 'Label'): obj.Label = modifier + obj.Label p[0] = obj + def p_part(p): ''' part : sphere_action @@ -288,11 +305,13 @@ def p_part(p): ''' p[0] = p[1] + def p_2d_point(p): '2d_point : OSQUARE NUMBER COMMA NUMBER ESQUARE' global points_list if printverbose: print("2d Point") - p[0] = [float(p[2]),float(p[4])] + p[0] = [float(p[2]), float(p[4])] + def p_points_list_2d(p): ''' @@ -300,12 +319,12 @@ def p_points_list_2d(p): | points_list_2d 2d_point COMMA | points_list_2d 2d_point ''' - if p[2] == ',' : + if p[2] == ',': #if printverbose: # print("Start List") # print(p[1]) p[0] = [p[1]] - else : + else: if printverbose: print(p[1]) print(p[2]) @@ -313,23 +332,25 @@ def p_points_list_2d(p): p[0] = p[1] #if printverbose: print(p[0]) + def p_3d_point(p): '3d_point : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER ESQUARE' global points_list if printverbose: print("3d point") - p[0] = [p[2],p[4],p[6]] - + p[0] = [p[2], p[4], p[6]] + + def p_points_list_3d(p): ''' points_list_3d : 3d_point COMMA | points_list_3d 3d_point COMMA | points_list_3d 3d_point ''' - if p[2] == ',' : + if p[2] == ',': if printverbose: print("Start List") if printverbose: print(p[1]) p[0] = [p[1]] - else : + else: if printverbose: print(p[1]) if printverbose: print(p[2]) p[1].append(p[2]) @@ -343,11 +364,11 @@ def p_path_points(p): | path_points NUMBER ''' #if printverbose: print("Path point") - if p[2] == ',' : + if p[2] == ',': #if printverbose: print('Start list') #if printverbose: print(p[1]) p[0] = [int(p[1])] - else : + else: #if printverbose: print(p[1]) #if printverbose: print(len(p[1])) #if printverbose: print(p[2]) @@ -362,16 +383,17 @@ def p_path_list(p): #if printverbose: print(p[2]) p[0] = p[2] -def p_path_set(p) : + +def p_path_set(p): ''' path_set : path_list | path_set COMMA path_list ''' #if printverbose: print('Path Set') #if printverbose: print(len(p)) - if len(p) == 2 : + if len(p) == 2: p[0] = [p[1]] - else : + else: p[1].append(p[3]) p[0] = p[1] #if printverbose: print(p[0]) @@ -394,10 +416,10 @@ def p_operation(p): ''' p[0] = p[1] -def placeholder(name,children,arguments): +def placeholder(name, children, arguments): from OpenSCADFeatures import OpenSCADPlaceholder newobj=doc.addObject("Part::FeaturePython",name) - OpenSCADPlaceholder(newobj,children,str(arguments)) + OpenSCADPlaceholder(newobj, children, str(arguments)) if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): @@ -408,9 +430,9 @@ def placeholder(name,children,arguments): #don't hide the children return newobj -def CGALFeatureObj(name,children,arguments=[]): - myobj=doc.addObject("Part::FeaturePython",name) - CGALFeature(myobj,name,children,str(arguments)) +def CGALFeatureObj(name, children,arguments=[]): + myobj=doc.addObject("Part::FeaturePython", name) + CGALFeature(myobj, name, children, str(arguments)) if gui: for subobj in children: subobj.ViewObject.hide() @@ -431,19 +453,19 @@ def p_offset_action(p): subobj = p[6] else: subobj = fuse(p[6],"Offset Union") - if 'r' in p[3] : + if 'r' in p[3]: offset = float(p[3]['r']) - if 'delta' in p[3] : + if 'delta' in p[3]: offset = float(p[3]['delta']) if subobj[0].Shape.Volume == 0 : newobj=doc.addObject("Part::Offset2D",'Offset2D') - newobj.Source = subobj[0] + newobj.Source = subobj[0] newobj.Value = offset - if 'r' in p[3] : - newobj.Join = 0 - else : - newobj.Join = 2 - else : + if 'r' in p[3]: + newobj.Join = 0 + else: + newobj.Join = 2 + else: newobj=doc.addObject("Part::Offset",'offset') newobj.Shape = subobj[0].Shape.makeOffset(offset) newobj.Document.recompute() @@ -470,24 +492,24 @@ def p_resize_action(p): ''' resize_action : resize LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE ''' new_size = p[3]['newsize'] - auto = p[3]['auto'] + auto = p[3]['auto'] p[6][0].recompute() if p[6][0].Shape.isNull(): doc.recompute() p[6][0].Shape.tessellate(0.05) old_bbox = p[6][0].Shape.BoundBox old_size = [old_bbox.XLength, old_bbox.YLength, old_bbox.ZLength] - for r in range(0,3) : - if auto[r] == '1' : + for r in range(0,3): + if auto[r] == '1': new_size[r] = new_size[0] - if new_size[r] == '0' : + if new_size[r] == '0': new_size[r] = str(old_size[r]) # Calculate a transform matrix from the current bounding box to the new one: transform_matrix = FreeCAD.Matrix() - scale = FreeCAD.Vector(float(new_size[0])/old_size[0], - float(new_size[1])/old_size[1], + scale = FreeCAD.Vector(float(new_size[0])/old_size[0], + float(new_size[1])/old_size[1], float(new_size[2])/old_size[2]) transform_matrix.scale(scale) @@ -503,7 +525,7 @@ def p_resize_action(p): new_part.ViewObject.Proxy = 0 p[6][0].ViewObject.hide() p[0] = [new_part] - + def p_not_supported(p): ''' @@ -599,8 +621,8 @@ def p_union_action(p): if printverbose: print("Push Union Result") p[0] = [newpart] if printverbose: print("End Union") - -def p_difference_action(p): + +def p_difference_action(p): 'difference_action : difference LPAREN RPAREN OBRACE block_list EBRACE' if printverbose: print("difference") @@ -611,7 +633,7 @@ def p_difference_action(p): elif (len(p[5]) == 1 ): #single object p[0] = p[5] else: -# Cut using Fuse +# Cut using Fuse mycut = doc.addObject('Part::Cut',p[1]) mycut.Base = p[5][0] # Can only Cut two objects do we need to fuse extras @@ -689,9 +711,9 @@ def process_rotate_extrude_prism(obj, angle, n): obj.ViewObject.hide() return(newobj) -def p_rotate_extrude_action(p): +def p_rotate_extrude_action(p): 'rotate_extrude_action : rotate_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' - if printverbose: print("Rotate Extrude") + if printverbose: print("Rotate Extrude") angle = 360.0 if 'angle' in p[3]: angle = float(p[3]['angle']) @@ -752,7 +774,7 @@ def process_linear_extrude(obj,h) : newobj.ViewObject.hide() return(mylinear) -def process_linear_extrude_with_transform(base,height,twist,scale) : +def process_linear_extrude_with_transform(base,height,twist,scale) : newobj=doc.addObject("Part::FeaturePython",'transform_extrude') Twist(newobj,base,height,-twist,scale) #base is an FreeCAD Object, height and twist are floats, scale is a two-component vector of floats if gui: @@ -861,7 +883,7 @@ def process_mesh_file(fname,ext): def processTextCmd(t): from OpenSCADUtils import callopenscadstring tmpfilename = callopenscadstring(t,'dxf') - from OpenSCAD2Dgeom import importDXFface + from OpenSCAD2Dgeom import importDXFface face = importDXFface(tmpfilename,None,None) obj=doc.addObject('Part::Feature','text') obj.Shape=face @@ -972,16 +994,16 @@ def p_multmatrix_action(p): if part.Shape.isNull(): doc.recompute() new_part = doc.addObject("Part::Feature","Matrix Deformation") - new_part.Shape = part.Shape.transformGeometry(transform_matrix) + new_part.Shape = part.Shape.transformGeometry(transform_matrix) if gui: part.ViewObject.hide() - if False : -# Does not fix problemfile or beltTighener although later is closer + if False : +# Does not fix problemfile or beltTighener although later is closer newobj=doc.addObject("Part::FeaturePython",'RefineMultMatrix') RefineShape(newobj,new_part) if gui: newobj.ViewObject.Proxy = 0 - new_part.ViewObject.hide() + new_part.ViewObject.hide() p[0] = [newobj] else : p[0] = [new_part] @@ -989,7 +1011,7 @@ def p_multmatrix_action(p): new_part.ViewObject.ShapeColor=parentcolor new_part.ViewObject.Transparency = parenttransparency if printverbose: print("Multmatrix applied") - + def p_matrix(p): 'matrix : OSQUARE vector COMMA vector COMMA vector COMMA vector ESQUARE' if printverbose: print("Matrix") @@ -1004,7 +1026,7 @@ def center(obj,x,y,z): obj.Placement = FreeCAD.Placement(\ FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\ FreeCAD.Rotation(0,0,0,1)) - + def p_sphere_action(p): 'sphere_action : sphere LPAREN keywordargument_list RPAREN SEMICOL' if printverbose: print("Sphere : ",p[3]) @@ -1106,8 +1128,8 @@ def p_cylinder_action(p): if printverbose: print("Center = ",tocenter) if tocenter=='true' : center(mycyl,0,0,h) - if False : -# Does not fix problemfile or beltTighener although later is closer + if False : +# Does not fix problemfile or beltTighener although later is closer newobj=doc.addObject("Part::FeaturePython",'RefineCylinder') RefineShape(newobj,mycyl) if gui: @@ -1285,7 +1307,7 @@ def p_polyhedron_action(p) : print(v) print ("Polyhedron "+p[9]) print (p[12]) - faces_list = [] + faces_list = [] mypolyhed = doc.addObject('Part::Feature',p[1]) for i in p[12] : if printverbose: print(i) diff --git a/src/Mod/OpenSCAD/prototype.py b/src/Mod/OpenSCAD/prototype.py index 2f4b0c3888..6f60656965 100644 --- a/src/Mod/OpenSCAD/prototype.py +++ b/src/Mod/OpenSCAD/prototype.py @@ -1,12 +1,14 @@ #!/usr/bin/env python -# This program is free software; you can redistribute it and/or modify +# This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License (LGPL) # as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. +# the License, or (at your option) any later version. import FreeCAD -import re,math +import math +import re + from OpenSCADFeatures import * from OpenSCAD2Dgeom import * from OpenSCADUtils import * @@ -14,50 +16,53 @@ from OpenSCADUtils import * if open.__module__ in ['__builtin__','io']: pythonopen = open # to distinguish python built-in open function from the one declared here -def openscadmesh(doc,scadstr,objname): + +def openscadmesh(doc, scadstr, objname): import Part,Mesh,os,OpenSCADUtils tmpfilename=OpenSCADUtils.callopenscadstring(scadstr,'stl') if tmpfilename: #mesh1 = doc.getObject(objname) #reuse imported object Mesh.insert(tmpfilename) os.unlink(tmpfilename) - mesh1=doc.getObject(objname) #blog + mesh1 = doc.getObject(objname) #blog mesh1.ViewObject.hide() sh=Part.Shape() - sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1) + sh.makeShapeFromMesh(mesh1.Mesh.Topology, 0.1) solid = Part.Solid(sh) - obj=doc.addObject("Part::FeaturePython",objname) - ImportObject(obj,mesh1) #This object is not mutable from the GUI + obj = doc.addObject("Part::FeaturePython", objname) + ImportObject(obj, mesh1) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) - solid=solid.removeSplitter() + solid = solid.removeSplitter() if solid.Volume < 0: solid.complement() - obj.Shape=solid#.removeSplitter() + obj.Shape = solid#.removeSplitter() return obj else: print(scadstr) + class Node: - #fnmin=12 # maximal fn for implicit polygon rendering - fnmin= FreeCAD.ParamGet(\ + #fnmin = 12 # maximal fn for implicit polygon rendering + fnmin = FreeCAD.ParamGet(\ "User parameter:BaseApp/Preferences/Mod/OpenSCAD").GetInt('useMaxFN') - planedim=1e10 #size of the square used as x-y-plane - def __init__(self,name,arguments=None,children=None,): + planedim = 1e10 #size of the square used as x-y-plane + + def __init__(self, name, arguments=None, children=None,): pass - self.name=name - self.arguments=arguments or {} - self.children=children or [] + self.name = name + self.arguments = arguments or {} + self.children = children or [] def __repr__(self): - str1 ='Node(name=%s' % self.name + str1 = 'Node(name=%s' % self.name if self.arguments: str1 += ',arguments=%s' % self.arguments if self.children: str1 += ',children=%s' % self.children return str1+')' - + def __nonzero__(self): - '''A Node is not obsolete if doesn't have children. + '''A Node is not obsolete if doesn't have children. Only if as neither name children or arguments''' return bool(self.name or self.arguments or self.children) @@ -65,50 +70,50 @@ class Node: '''return the number of children''' return len(self.children) - def __getitem__(self,key): + def __getitem__(self, key): '''direct access to the children''' return self.children.__getitem__(key) - def rlen(self,checkmultmarix=False): + def rlen(self, checkmultmarix=False): '''Total number of nodes''' if self.children: return 1+sum([ch.rlen() for ch in self.children]) else: return 1 - def addtofreecad(self,doc=None,fcpar=None): def center(obj,x,y,z): obj.Placement = FreeCAD.Placement(\ FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\ FreeCAD.Rotation(0,0,0,1)) - import FreeCAD,Part + import FreeCAD + import Part if not doc: - doc=FreeCAD.newDocument() - obj=None - namel=self.name.lower() + doc = FreeCAD.newDocument() + obj = None + namel = self.name.lower() multifeature={'union':"Part::MultiFuse",'imp_union':"Part::MultiFuse", 'intersection':"Part::MultiCommon"} if namel in multifeature: if len(self.children)>1: - obj=doc.addObject(multifeature[namel],namel) + obj = doc.addObject(multifeature[namel],namel) subobjs = [child.addtofreecad(doc,obj) for child in self.children] obj.Shapes = subobjs for subobj in subobjs: subobj.ViewObject.hide() - elif len(self.children)==1: + elif len(self.children) == 1: obj = self.children[0].addtofreecad(doc,fcpar or True) else: obj = fcpar elif namel == 'difference': - if len(self.children)==1: + if len(self.children) == 1: obj = self.children[0].addtofreecad(doc,fcpar or True) - else: - obj=doc.addObject("Part::Cut",namel) + else: + obj = doc.addObject("Part::Cut",namel) base = self.children[0].addtofreecad(doc,obj) - - if len(self.children)==2: + + if len(self.children) == 2: tool = self.children[1].addtofreecad(doc,obj) else: tool = Node(name='imp_union',\ @@ -118,57 +123,57 @@ class Node: base.ViewObject.hide() tool.ViewObject.hide() elif namel == 'cube': - obj=doc.addObject('Part::Box',namel) - x,y,z=self.arguments['size'] - obj.Length=x - obj.Width=y - obj.Height=z + obj = doc.addObject('Part::Box', namel) + x,y,z = self.arguments['size'] + obj.Length = x + obj.Width = y + obj.Height = z if self.arguments['center']: center(obj,x,y,z) elif namel == 'sphere': - obj=doc.addObject("Part::Sphere",namel) + obj = doc.addObject("Part::Sphere", namel) obj.Radius = self.arguments['r'] elif namel == 'cylinder': h = self.arguments['h'] - r1 ,r2 = self.arguments['r1'], self.arguments['r2'] + r1, r2 = self.arguments['r1'], self.arguments['r2'] if '$fn' in self.arguments and self.arguments['$fn'] > 2 \ and self.arguments['$fn']<=Node.fnmin: # polygonal if r1 == r2: # prismatic obj = doc.addObject("Part::Prism","prism") obj.Polygon = int(self.arguments['$fn']) - obj.Circumradius = r1 - obj.Height = h + obj.Circumradius = r1 + obj.Height = h if self.arguments['center']: center(obj,0,0,h) #base.ViewObject.hide() elif False: #use Frustum Feature with makeRuledSurface - obj=doc.addObject("Part::FeaturePython",'frustum') - Frustum(obj,r1,r2,int(self.arguments['$fn']),h) + obj = doc.addObject("Part::FeaturePython",'frustum') + Frustum(obj,r1,r2,int(self.arguments['$fn']), h) ViewProviderTree(obj.ViewObject) if self.arguments['center']: center(obj,0,0,h) else: #Use Part::Loft and GetWire Feature - obj=doc.addObject('Part::Loft','frustum') + obj = doc.addObject('Part::Loft', 'frustum') import Draft - p1 = Draft.makePolygon(int(self.arguments['$fn']),r1) - p2 = Draft.makePolygon(int(self.arguments['$fn']),r2) + p1 = Draft.makePolygon(int(self.arguments['$fn']), r1) + p2 = Draft.makePolygon(int(self.arguments['$fn']), r2) if self.arguments['center']: p1.Placement = FreeCAD.Placement(\ FreeCAD.Vector(0.0,0.0,-h/2.0),FreeCAD.Rotation()) p2.Placement = FreeCAD.Placement(\ - FreeCAD.Vector(0.0,0.0,h/2.0),FreeCAD.Rotation()) + FreeCAD.Vector(0.0,0.0,h/2.0), F reeCAD.Rotation()) else: p2.Placement = FreeCAD.Placement(\ FreeCAD.Vector(0.0,0.0,h),FreeCAD.Rotation()) - w1=doc.addObject("Part::FeaturePython",'polygonwire1') - w2=doc.addObject("Part::FeaturePython",'polygonwire2') + w1 = doc.addObject("Part::FeaturePython",'polygonwire1') + w2 = doc.addObject("Part::FeaturePython",'polygonwire2') GetWire(w1,p1) GetWire(w2,p2) ViewProviderTree(w1.ViewObject) ViewProviderTree(w2.ViewObject) - obj.Sections=[w1,w2] - obj.Solid=True - obj.Ruled=True + obj.Sections = [w1,w2] + obj.Solid = True + obj.Ruled = True p1.ViewObject.hide() p2.ViewObject.hide() w1.ViewObject.hide() @@ -199,7 +204,7 @@ class Node: obj.Shape=solid#.removeSplitter() elif namel == 'polygon': - obj = doc.addObject("Part::Feature",namel) + obj = doc.addObject("Part::Feature", namel) points=self.arguments['points'] paths = self.arguments.get('paths') if not paths: @@ -242,7 +247,7 @@ class Node: obj = self.children[0].addtofreecad(doc,fcpar or True) else: obj = Node(name='imp_union',\ - children=self.children).addtofreecad(doc,fcpar or True) + children = self.children).addtofreecad(doc,fcpar or True) #FreeCAD.Console.PrintMessage('obj %s\nmat %s/n' % (obj.Placement,m1)) obj.Placement=FreeCAD.Placement(m1).multiply(obj.Placement) else: #we need to apply the matrix transformation to the Shape using a custom PythonFeature @@ -349,49 +354,49 @@ class Node: Mesh.insert(filename) mesh1=doc.getObject(objname) mesh1.ViewObject.hide() - sh=Part.Shape() + sh = Part.Shape() sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1) solid = Part.Solid(sh) - obj=doc.addObject("Part::FeaturePython",'import_%s_%s'%(extension,objname)) + obj = doc.addObject("Part::FeaturePython",'import_%s_%s'%(extension,objname)) #obj=doc.addObject('Part::Feature',) ImportObject(obj,mesh1) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) - solid=solid.removeSplitter() + solid = solid.removeSplitter() if solid.Volume < 0: #sh.reverse() #sh = sh.copy() solid.complement() - obj.Shape=solid#.removeSplitter() + obj.Shape = solid#.removeSplitter() elif extension in ['dxf']: layera = self.arguments.get('layer') - featname='import_dxf_%s_%s'%(objname,layera) + featname ='import_dxf_%s_%s'%(objname,layera) # reusing an already imported object does not work if the # shape in not yet calculated import importDXF global dxfcache - layers=dxfcache.get(id(doc),[]) + layers = dxfcache.get(id(doc),[]) if layers: - groupobj=[go for go in layers if (not layera) or go.Label == layera] + groupobj = [go for go in layers if (not layera) or go.Label == layera] else: - groupobj= None + groupobj = None if not groupobj: - groupname=objname + groupname = objname layers = importDXF.processdxf(doc,filename) or importDXF.layers dxfcache[id(doc)] = layers[:] for l in layers: for o in l.Group: o.ViewObject.hide() l.ViewObject.hide() - groupobj=[go for go in layers if (not layera) or go.Label == layera] - edges=[] + groupobj = [go for go in layers if (not layera) or go.Label == layera] + edges = [] for shapeobj in groupobj[0].Group: edges.extend(shapeobj.Shape.Edges) try: - f=edgestofaces(edges) + f = edgestofaces(edges) except Part.OCCError: FreeCAD.Console.PrintError('processing of dxf import failed\nPlease rework \'%s\' manually\n' % layera) - f=Part.Shape() #empty Shape - obj=doc.addObject("Part::FeaturePython",'import_dxf_%s_%s'%(objname,layera)) + f = Part.Shape() #empty Shape + obj = doc.addObject("Part::FeaturePython",'import_dxf_%s_%s'%(objname,layera)) #obj=doc.addObject('Part::Feature',) ImportObject(obj,groupobj[0]) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) @@ -405,22 +410,22 @@ class Node: if origin is not None and any([c != 0 for c in origin]): raise(NotImplementedError)# order of transformations unknown child = obj - m1=FreeCAD.Matrix() + m1 = FreeCAD.Matrix() m1.scale(scale,scale,scale) - obj=doc.addObject("Part::FeaturePython",'scale_import') + obj = doc.addObject("Part::FeaturePython",'scale_import') MatrixTransform(obj,m1,child) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) elif origin is not None and any([c != 0 for c in origin]): - placement=FreeCAD.Placement(FreeCAD.Vector(*[-c for c in origin]),FreeCAD.Rotation()) - obj.Placement=placement.multiply(obj.Placement) + placement = FreeCAD.Placement(FreeCAD.Vector(*[-c for c in origin]),FreeCAD.Rotation()) + obj.Placement = placement.multiply(obj.Placement) else: FreeCAD.Console.ErrorMessage('Import of %s failed\n' % (filename)) elif namel == 'minkowski': - childrennames=[child.name.lower() for child in self.children] + childrennames = [child.name.lower() for child in self.children] if len(self.children) == 2 and \ - childrennames.count('cube')==1 and \ + childrennames.count('cube') == 1 and \ (childrennames.count('sphere') + \ childrennames.count('cylinder')) == 1: if self.children[0].name.lower() == 'cube': @@ -429,15 +434,15 @@ class Node: elif self.children[1].name.lower() == 'cube': cube = self.children[1] roundobj = self.children[0] - roundobjname=roundobj.name.lower() + roundobjname = roundobj.name.lower() issphere = roundobjname == 'sphere' - cubeobj=doc.addObject('Part::Box','roundedcube') - x,y,z=cube.arguments['size'] - r=roundobj.arguments.get('r') or \ + cubeobj = doc.addObject('Part::Box','roundedcube') + x,y,z = cube.arguments['size'] + r = roundobj.arguments.get('r') or \ roundobj.arguments.get('r1') - cubeobj.Length=x+2*r - cubeobj.Width=y+2*r - cubeobj.Height=z+2*r*issphere + cubeobj.Length = x+2*r + cubeobj.Width = y+2*r + cubeobj.Height = z+2*r*issphere obj=doc.addObject("Part::Fillet","%s_%s"%(namel,roundobjname)) obj.Base = cubeobj cubeobj.ViewObject.hide() @@ -450,13 +455,13 @@ class Node: else: #htandle a rotated cylinder #OffsetShape raise(NotImplementedError) - elif childrennames.count('sphere')==1: + elif childrennames.count('sphere') == 1: sphereindex=childrennames.index('sphere') - sphere=self.children[sphereindex] - offset=sphere.arguments['r'] - nonsphere=self.children[0:sphereindex]+\ + sphere = self.children[sphereindex] + offset = sphere.arguments['r'] + nonsphere = self.children[0:sphereindex]+\ self.sphere[sphereindex+1:] - obj=doc.addObject("Part::FeaturePython",'Offset') + obj = doc.addObject("Part::FeaturePython",'Offset') if len(nonsphere) == 1: child = nonsphere[0].addtofreecad(doc,obj) else: @@ -466,7 +471,7 @@ class Node: ViewProviderTree(obj.ViewObject) elif False: raise(NotImplementedError) - pass # handle rotated cylinders and select edges that + pass # handle rotated cylinders and select edges that #radius = radius0 * m1.multiply(FreeCAD.Vector(0,0,1)).dot(edge.Curve.tangent(0)[0]) else: raise(NotImplementedError) @@ -486,7 +491,7 @@ class Node: elif namel in ['glide','hull']: raise(NotImplementedError) elif namel in ['render','subdiv'] or True: - lenchld=len(self.children) + lenchld = len(self.children) if lenchld == 1: FreeCAD.Console.PrintMessage('Not recognized %s\n' % (self)) obj = self.children[0].addtofreecad(doc,fcpar) @@ -556,13 +561,13 @@ class Node: for i,child in enumerate(self.children): child.pprint2('%s[%d]'%(path,i),pathjust) - + def parseexpression(e): - e=e.strip() + e = e.strip() el = e.lower() - if len(el)==0: return None + if len(el) == 0: return None if el == 'true': return True elif el == 'false': return False elif el == 'undef': return None @@ -589,59 +594,59 @@ def parseexpression(e): def parseargs(argstring): if '=' in argstring: - level=0 - tok=[] - a=[] + level = 0 + tok = [] + a = [] for i,char in enumerate(argstring): - if char=='[': level+=1 - elif char ==']': level -=1 - if level==0 and (char=='=' or char==','): + if char == '[': level += 1 + elif char ==']': level -= 1 + if level == 0 and (char == '=' or char == ','): tok.append(''.join(a).strip()) - a=[] + a= [] else: a.append(char) tok.append(''.join(a).strip()) #print(tok) - argdict=dict(zip(tok[0::2],[parseexpression(argstring) for argstring in tok[1::2]])) -# argdict={} + argdict = dict(zip(tok[0::2],[parseexpression(argstring) for argstring in tok[1::2]])) +# argdict = {} # for key, value in re.findall(r"(\$?\w+)\s*=\s*(\[?\w+]?),?\s*",argstring): -# argdict[key] = parseexpression(value) +# argdict[key] = parseexpression(value) return argdict else: return parseexpression(argstring) def parsenode(str1): - name,str2=str1.strip().split('(',1) + name, str2 = str1.strip().split('(',1) assert('}' not in name) - name=name.strip('#!%* ')#remove/ignore modifiers - args,str3=str2.split(')',1) - str4=str3.lstrip() + name = name.strip('#!%* ')#remove/ignore modifiers + args, str3 = str2.split(')',1) + str4 = str3.lstrip() if str4.startswith(';'): #has no children - nextelement=str4[1:].lstrip() + nextelement = str4[1:].lstrip() return Node(name,parseargs(args)),nextelement elif str4.startswith('{'): #has children - level=0 + level = 0 for index,char in enumerate(str4): if char == '{': level += 1 elif char == '}': level -= 1 if level == 0: break #end of children - childstr= str4[1:index].strip() + childstr = str4[1:index].strip() nextelement = str4[index+1:].lstrip() - bopen,bclose=childstr.count('{'),childstr.count('}') + bopen,bclose = childstr.count('{'),childstr.count('}') assert(bopen == bclose) - children=[] + children= [] while childstr: try: - childnode,childstr=parsenode(childstr) + childnode,childstr = parsenode(childstr) children.append(childnode) except ValueError: raise if args: - args=parseargs(args) + args = parseargs(args) return Node(name,args,children),nextelement def readfile(filename): @@ -656,7 +661,7 @@ def readfile(filename): f = pythonopen(tmpfile) else: f = pythonopen(filename) - rootnode=parsenode(f.read())[0] + rootnode = p arsenode(f.read())[0] f.close() if isopenscad and tmpfile: try: @@ -667,8 +672,8 @@ def readfile(filename): def open(filename): import os - docname=os.path.split(filename)[1] - doc=FreeCAD.newDocument(docname) + docname = os.path.split(filename)[1] + doc = FreeCAD.newDocument(docname) doc.Label = (docname.split('.',1)[0]) readfile(filename).addtofreecad(doc) #doc.recompute() @@ -676,9 +681,9 @@ def open(filename): def insert(filename,docname): try: - doc=FreeCAD.getDocument(docname) + doc = FreeCAD.getDocument(docname) except NameError: - doc=FreeCAD.newDocument(docname) + doc = FreeCAD.newDocument(docname) readfile(filename).addtofreecad(doc) #doc.recompute() diff --git a/src/Mod/OpenSCAD/replaceobj.py b/src/Mod/OpenSCAD/replaceobj.py index fb6b6f73a8..859ef34df5 100644 --- a/src/Mod/OpenSCAD/replaceobj.py +++ b/src/Mod/OpenSCAD/replaceobj.py @@ -1,5 +1,4 @@ #*************************************************************************** -#* * #* Copyright (c) 2012 Sebastian Hoogen * #* * #* This program is free software; you can redistribute it and/or modify * @@ -20,7 +19,7 @@ #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - replace object function" +__title__ = "FreeCAD OpenSCAD Workbench - replace object function" __author__ = "Sebastian Hoogen" __url__ = ["https://www.freecadweb.org"] @@ -28,9 +27,10 @@ __url__ = ["https://www.freecadweb.org"] This functions allows to replace an object in the feature hierarchy ''' -def replaceobj(parent,oldchild,newchild): + +def replaceobj(parent, oldchild, newchild): for propname in parent.PropertiesList: - propvalue=parent.getPropertyByName(propname) + propvalue = parent.getPropertyByName(propname) if type(propvalue) == list: bModified = False for dontcare in range(propvalue.count(oldchild)): @@ -51,8 +51,8 @@ def replaceobj(parent,oldchild,newchild): def replaceobjfromselection(objs): # The Parent can be omitted as long as one object is orphaned - if len(objs)==2: - InListLength= tuple((len(obj.InList)) for obj in objs) + if len(objs) == 2: + InListLength = tuple((len(obj.InList)) for obj in objs) if InListLength == (0,1): newchild,oldchild = objs parent = oldchild.InList[0] @@ -62,7 +62,7 @@ def replaceobjfromselection(objs): else: raise ValueError("Selection ambiguous. Please select oldchild,\ newchild and parent") - elif len(objs)==3: + elif len(objs) == 3: if objs[2] in objs[0].InList: oldchild, newchild, parent = objs elif objs[0] in objs[1].InList: parent, oldchild, newchild = objs elif objs[0] in objs[2].InList: parent, newchild, oldchild = objs @@ -76,9 +76,9 @@ def replaceobjfromselection(objs): replaceobj(parent,oldchild,newchild) parent.Document.recompute() + if __name__ == '__main__': - import FreeCAD,FreeCADGui - objs=[selobj.Object for selobj in FreeCADGui.Selection.getSelectionEx()] + import FreeCAD + import FreeCADGui + objs = [selobj.Object for selobj in FreeCADGui.Selection.getSelectionEx()] replaceobjfromselection(objs) - - diff --git a/src/Mod/OpenSCAD/tokrules.py b/src/Mod/OpenSCAD/tokrules.py index c4360a1c31..c187ccc67d 100644 --- a/src/Mod/OpenSCAD/tokrules.py +++ b/src/Mod/OpenSCAD/tokrules.py @@ -1,7 +1,6 @@ # -*- coding: utf8 -*- #*************************************************************************** -#* * #* Copyright (c) 2012 Keith Sloan * #* * #* This program is free software; you can redistribute it and/or modify * @@ -21,7 +20,7 @@ #* USA * #* * #*************************************************************************** -__title__="FreeCAD OpenSCAD Workbench - CSG importer Version 0.5c" +__title__ = "FreeCAD OpenSCAD Workbench - CSG importer Version 0.5c" __author__ = "Keith Sloan " __url__ = ["http://www.sloan-home.co.uk/ImportCSG"] @@ -108,27 +107,33 @@ reserved_map = { } for r in reserved: reserved_map[r.lower()] = r + # Deal with Comments -def t_comment1(t) : +def t_comment1(t): r'//[^\r\n]*((\r\n)|<>)' pass -def t_comment2(t) : + +def t_comment2(t): r'//[^\n]*((\n)|<>)' pass + def t_ID(t): r'[$]?[a-zA-Z_]+[0-9]*' t.type = reserved_map.get(t.value, "ID") return t + # Define a rule so we can track line numbers def t_newline(t): r'\n+' t.lexer.lineno += len(t.value) + # A string containing ignored characters (spaces and tabs) -t_ignore = " \t\r" +t_ignore = " \t\r" + # Error handling rule def t_error(t):