# -*- coding: utf8 -*- #*************************************************************************** #* * #* Copyright (c) 2012 Keith Sloan * #* * #* 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. * #* for detail see the LICENCE text file. * #* * #* This program is distributed in the hope that it will be useful, * #* but WITHOUT ANY WARRANTY; without even the implied warranty of * #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * #* GNU Library General Public License for more details. * #* * #* You should have received a copy of the GNU Library General Public * #* 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 and Peter Li * #* for programming advice and some code. * #* * #* * #*************************************************************************** __title__="FreeCAD OpenSCAD Workbench - CSG importer" __author__ = "Keith Sloan " __url__ = ["http://www.sloan-home.co.uk/ImportCSG"] printverbose = False import FreeCAD, os, sys if FreeCAD.GuiUp: import FreeCADGui gui = True else: if printverbose: print "FreeCAD Gui not present." gui = False import ply.lex as lex import ply.yacc as yacc import Part from OpenSCADFeatures import * from OpenSCADUtils import * if open.__module__ == '__builtin__': pythonopen = open # to distinguish python built-in open function from the one declared here # Get the token map from the lexer. This is required. import tokrules from tokrules import tokens def translate(context,text): "convenience function for Qt translator" from PySide import QtGui return QtGui.QApplication.translate(context, text, None, \ QtGui.QApplication.UnicodeUTF8) def open(filename): "called when freecad opens a file." global doc global pathName docname = os.path.splitext(os.path.basename(filename))[0] doc = FreeCAD.newDocument(docname) if filename.lower().endswith('.scad'): tmpfile=callopenscad(filename) if workaroundforissue128needed(): 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) try: os.unlink(tmpfile) except OSError: pass else: pathName = os.path.dirname(os.path.normpath(filename)) processcsg(filename) return doc def insert(filename,docname): "called when freecad imports a file" global doc global pathName groupname = os.path.splitext(os.path.basename(filename))[0] try: doc=FreeCAD.getDocument(docname) except: doc=FreeCAD.newDocument(docname) #importgroup = doc.addObject("App::DocumentObjectGroup",groupname) if filename.lower().endswith('.scad'): tmpfile=callopenscad(filename) if workaroundforissue128needed(): 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) try: os.unlink(tmpfile) except OSError: pass else: pathName = os.path.dirname(os.path.normpath(filename)) processcsg(filename) def processcsg(filename): global doc if printverbose: print 'ImportCSG Version 0.5d' # Build the lexer if printverbose: print 'Start Lex' lex.lex(module=tokrules) if printverbose: print 'End Lex' # Build the parser if printverbose: print 'Load Parser' # No debug out otherwise Linux has protection exception parser = yacc.yacc(debug=0) if printverbose: print 'Parser Loaded' # Give the lexer some input #f=open('test.scad', 'r') f = pythonopen(filename, 'r') #lexer.input(f.read()) if printverbose: print 'Start Parser' # Swap statements to enable Parser debugging #result = parser.parse(f.read(),debug=1) result = parser.parse(f.read()) f.close() if printverbose: print 'End Parser' print result FreeCAD.Console.PrintMessage('End processing CSG file\n') doc.recompute() def p_block_list_(p): ''' block_list : statement | block_list statement | statementwithmod | block_list statementwithmod ''' if printverbose: print "Block List" if printverbose: print p[1] if(len(p) > 2) : if printverbose: print p[2] p[0] = p[1] + p[2] 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 : p[0] = p[5] def p_group_action2(p) : 'group_action2 : group LPAREN RPAREN SEMICOL' if printverbose: print "Group2" p[0] = [] def p_boolean(p) : ''' boolean : true | false ''' p[0] = p[1] #def p_string(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 | multmatrix_action | group_action1 | group_action2 | color_action | render_action | not_supported ''' 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 inplemented #please note that disabled objects usualy are stript of the CSG ouput during compilation p[0] = p[1] def p_statementwithmod(p): '''statementwithmod : anymodifier statement''' #ignore the modifiers but add them to the label modifier = p[1] obj = p[2] if hasattr(obj,'Label'): obj.Label = modifier + obj.Label p[0] = obj def p_part(p): ''' part : sphere_action | cylinder_action | cube_action | circle_action | square_action | polygon_action_nopath | polygon_action_plus_path | polyhedron_action ''' 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])] def p_points_list_2d(p): ''' points_list_2d : 2d_point COMMA | points_list_2d 2d_point COMMA | points_list_2d 2d_point ''' if p[2] == ',' : if printverbose: print "Start List" print p[1] p[0] = [p[1]] else : if printverbose: print p[1] print p[2] p[1].append(p[2]) 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]] 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 printverbose: print "Start List" if printverbose: print p[1] p[0] = [p[1]] else : if printverbose: print p[1] if printverbose: print p[2] p[1].append(p[2]) p[0] = p[1] if printverbose: print p[0] def p_path_points(p): ''' path_points : NUMBER COMMA | path_points NUMBER COMMA | path_points NUMBER ''' if printverbose: print "Path point" if p[2] == ',' : if printverbose: print 'Start list' if printverbose: print p[1] p[0] = [int(p[1])] else : if printverbose: print p[1] if printverbose: print len(p[1]) if printverbose: print p[2] p[1].append(int(p[2])) p[0] = p[1] if printverbose: print p[0] def p_path_list(p): 'path_list : OSQUARE path_points ESQUARE' if printverbose: print 'Path List ' if printverbose: print p[2] p[0] = p[2] 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 : p[0] = [p[1]] else : p[1].append(p[3]) p[0] = p[1] if printverbose: print p[0] def p_operation(p): ''' operation : difference_action | intersection_action | union_action | rotate_extrude_action | linear_extrude_with_twist | rotate_extrude_file | import_file1 | surface_action | projection_action | hull_action | minkowski_action ''' p[0] = p[1] def placeholder(name,children,arguments): from OpenSCADFeatures import OpenSCADPlaceholder newobj=doc.addObject("Part::FeaturePython",name) OpenSCADPlaceholder(newobj,children,str(arguments)) if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(newobj.ViewObject) else: newobj.ViewObject.Proxy = 0 #don't hide the children return newobj 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() if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(myobj.ViewObject) else: myobj.ViewObject.Proxy = 0 return myobj #def p_offset_action(p): # 'offset_action : offset LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' # if len(p[5]) == 0: # mycut = placeholder('group',[],'{}') # elif (len(p[5]) == 1 ): #single object # subobj = p[5] # else: # subobj = fuse(p[6],"Offset Union") # newobj=doc.addObject("Part::FeaturePython",'offset') # OffsetShape(newobj,subobj,p[3]['delta']) # if gui: # if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ # GetBool('useViewProviderTree'): # from OpenSCADFeatures import ViewProviderTree # ViewProviderTree(newobj.ViewObject) # else: # newobj.ViewObject.Proxy = 0 # return [newobj] def p_hull_action(p): 'hull_action : hull LPAREN RPAREN OBRACE block_list EBRACE' p[0] = [ CGALFeatureObj(p[1],p[5]) ] def p_minkowski_action(p): ''' minkowski_action : minkowski LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE''' p[0] = [ CGALFeatureObj(p[1],p[6],p[3]) ] def p_not_supported(p): ''' not_supported : glide LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE | offset LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE | resize LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE ''' if gui and not FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('usePlaceholderForUnsupported'): from PySide import QtGui QtGui.QMessageBox.critical(None, unicode(translate('OpenSCAD',"Unsupported Function"))+" : "+p[1],unicode(translate('OpenSCAD',"Press OK"))) else: p[0] = [placeholder(p[1],p[6],p[3])] def p_size_vector(p): 'size_vector : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER ESQUARE' if printverbose: print "size vector" p[0] = [p[2],p[4],p[6]] def p_keywordargument(p): '''keywordargument : ID EQ boolean | ID EQ NUMBER | ID EQ size_vector | ID EQ vector | ID EQ 2d_point | ID EQ stripped_string ''' p[0] = (p[1],p[3]) if printverbose: print p[0] def p_keywordargument_list(p): ''' keywordargument_list : keywordargument | keywordargument_list COMMA keywordargument ''' if len(p) == 2: p[0] = {p[1][0] : p[1][1]} else: p[1][p[3][0]] = p[3][1] p[0]=p[1] def p_color_action(p): 'color_action : color LPAREN vector RPAREN OBRACE block_list EBRACE' import math if printverbose: print "Color" color = tuple([float(f) for f in p[3][:3]]) #RGB transp = 100 - int(math.floor(100*float(p[3][3]))) #Alpha if gui: for obj in p[6]: obj.ViewObject.ShapeColor =color obj.ViewObject.Transparency = transp p[0] = p[6] # Error rule for syntax errors def p_error(p): if printverbose: print "Syntax error in input!" if printverbose: print p def fuse(lst,name): global doc if printverbose: print "Fuse" if printverbose: print lst if len(lst) == 0: myfuse = placeholder('group',[],'{}') elif len(lst) == 1: return lst[0] # Is this Multi Fuse elif len(lst) > 2: if printverbose: print "Multi Fuse" myfuse = doc.addObject('Part::MultiFuse',name) myfuse.Shapes = lst if gui: for subobj in myfuse.Shapes: subobj.ViewObject.hide() else: if printverbose: print "Single Fuse" myfuse = doc.addObject('Part::Fuse',name) myfuse.Base = lst[0] myfuse.Tool = lst[1] if gui: myfuse.Base.ViewObject.hide() myfuse.Tool.ViewObject.hide() return(myfuse) def p_union_action(p): 'union_action : union LPAREN RPAREN OBRACE block_list EBRACE' if printverbose: print "union" newpart = fuse(p[5],p[1]) if printverbose: print "Push Union Result" p[0] = [newpart] if printverbose: print "End Union" def p_difference_action(p): 'difference_action : difference LPAREN RPAREN OBRACE block_list EBRACE' if printverbose: print "difference" if printverbose: print len(p[5]) if printverbose: print p[5] if (len(p[5]) == 0 ): #nochild mycut = placeholder('group',[],'{}') elif (len(p[5]) == 1 ): #single object p[0] = p[5] else: # 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 if (len(p[5]) > 2 ): if printverbose: print "Need to Fuse Extra First" mycut.Tool = fuse(p[5][1:],'union') else : mycut.Tool = p[5][1] if gui: mycut.Base.ViewObject.hide() mycut.Tool.ViewObject.hide() if printverbose: print "Push Resulting Cut" p[0] = [mycut] if printverbose: print "End Cut" def p_intersection_action(p): 'intersection_action : intersection LPAREN RPAREN OBRACE block_list EBRACE' if printverbose: print "intersection" # Is this Multi Common if (len(p[5]) > 2): if printverbose: print "Multi Common" mycommon = doc.addObject('Part::MultiCommon',p[1]) mycommon.Shapes = p[5] if gui: for subobj in mycommon.Shapes: subobj.ViewObject.hide() elif (len(p[5]) == 2): if printverbose: print "Single Common" mycommon = doc.addObject('Part::Common',p[1]) mycommon.Base = p[5][0] mycommon.Tool = p[5][1] if gui: mycommon.Base.ViewObject.hide() mycommon.Tool.ViewObject.hide() elif (len(p[5]) == 1): mycommon = p[5][0] else : # 1 child mycommon = placeholder('group',[],'{}') p[0] = [mycommon] if printverbose: print "End Intersection" def process_rotate_extrude(obj): newobj=doc.addObject("Part::FeaturePython",'RefineRotateExtrude') RefineShape(newobj,obj) if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(newobj.ViewObject) else: newobj.ViewObject.Proxy = 0 obj.ViewObject.hide() myrev = doc.addObject("Part::Revolution","RotateExtrude") myrev.Source = newobj myrev.Axis = (0.00,1.00,0.00) myrev.Base = (0.00,0.00,0.00) myrev.Angle = 360.00 myrev.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90)) if gui: newobj.ViewObject.hide() return(myrev) 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 (len(p[6]) > 1) : part = fuse(p[6],"Rotate Extrude Union") else : part = p[6][0] p[0] = [process_rotate_extrude(part)] if printverbose: print "End Rotate Extrude" def p_rotate_extrude_file(p): 'rotate_extrude_file : rotate_extrude LPAREN keywordargument_list RPAREN SEMICOL' if printverbose: print "Rotate Extrude File" filen,ext =p[3]['file'] .rsplit('.',1) obj = process_import_file(filen,ext,p[3]['layer']) p[0] = [process_rotate_extrude(obj)] if printverbose: print "End Rotate Extrude File" def process_linear_extrude(obj,h) : #if gui: newobj=doc.addObject("Part::FeaturePython",'RefineLinearExtrude') RefineShape(newobj,obj)#mylinear) if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(newobj.ViewObject) else: newobj.ViewObject.Proxy = 0 obj.ViewObject.hide() #mylinear.ViewObject.hide() mylinear = doc.addObject("Part::Extrusion","LinearExtrude") mylinear.Base = newobj #obj mylinear.Dir = (0,0,h) mylinear.Placement=FreeCAD.Placement() try: mylinear.Solid = True except: pass if gui: newobj.ViewObject.hide() return(mylinear) def process_linear_extrude_with_twist(base,height,twist) : newobj=doc.addObject("Part::FeaturePython",'twist_extrude') Twist(newobj,base,height,-twist) #base is an FreeCAD Object, heigth and twist are floats if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(newobj.ViewObject) else: newobj.ViewObject.Proxy = 0 #import ViewProviderTree from OpenSCADFeatures #ViewProviderTree(obj.ViewObject) return(newobj) def p_linear_extrude_with_twist(p): 'linear_extrude_with_twist : linear_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' if printverbose: print "Linear Extrude With Twist" h = float(p[3]['height']) if printverbose: print "Twist : ",p[3] if 'twist' in p[3]: t = float(p[3]['twist']) else: t = 0 if (len(p[6]) > 1) : obj = fuse(p[6],"Linear Extrude Union") else : obj = p[6][0] if t: newobj = process_linear_extrude_with_twist(obj,h,t) else: newobj = process_linear_extrude(obj,h) if p[3]['center']=='true' : center(newobj,0,0,h) p[0] = [newobj] if printverbose: print "End Linear Extrude with twist" def p_import_file1(p): 'import_file1 : import LPAREN keywordargument_list RPAREN SEMICOL' if printverbose: print "Import File" filen,ext =p[3]['file'].rsplit('.',1) p[0] = [process_import_file(filen,ext,p[3]['layer'])] if printverbose: print "End Import File" def p_surface_action(p): 'surface_action : surface LPAREN keywordargument_list RPAREN SEMICOL' if printverbose: print "Surface" obj = doc.addObject("Part::Feature",'surface') obj.Shape,xoff,yoff=makeSurfaceVolume(p[3]['file']) if p[3]['center']=='true' : center(obj,xoff,yoff,0.0) p[0] = [obj] if printverbose: print "End surface" def process_import_file(fname,ext,layer): if printverbose: print "Importing : "+fname+"."+ext+" Layer : "+layer if ext.lower() in reverseimporttypes()['Mesh']: obj=process_mesh_file(fname,ext) elif ext.lower() == 'dxf' : obj=processDXF(fname,layer) else: raise ValueError, "Unsupported file extension %s" % ext return(obj) def process_mesh_file(fname,ext): import Mesh,Part fullname = fname+'.'+ext filename = os.path.join(pathName,fullname) objname = os.path.split(fname)[1] mesh1 = doc.getObject(objname) #reuse imported object if not mesh1: Mesh.insert(filename) mesh1=doc.getObject(objname) if mesh1 is not None: if gui: mesh1.ViewObject.hide() sh=Part.Shape() sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1) solid = Part.Solid(sh) obj=doc.addObject('Part::Feature',"Mesh") #ImportObject(obj,mesh1) #This object is not mutable from the GUI #ViewProviderTree(obj.ViewObject) solid=solid.removeSplitter() if solid.Volume < 0: #sh.reverse() #sh = sh.copy() solid.complement() obj.Shape=solid#.removeSplitter() else: #mesh1 is None FreeCAD.Console.PrintError('Mesh not imported %s.%s %s\n' % \ (objname,ext,filename)) import Part obj=doc.addObject('Part::Feature',"FailedMeshImport") obj.Shape=Part.Compound([]) return(obj) def processDXF(fname,layer): global doc global pathName from OpenSCAD2Dgeom import importDXFface if printverbose: print "Process DXF file" if printverbose: print "File Name : "+fname if printverbose: print "Layer : "+layer if printverbose: print "PathName : "+pathName dxfname = fname+'.dxf' filename = os.path.join(pathName,dxfname) shortname = os.path.split(fname)[1] if printverbose: print "DXF Full path : "+filename face = importDXFface(filename,layer,doc) obj=doc.addObject('Part::Feature','dxf_%s_%s' % (shortname,layer or "all")) obj.Shape=face if printverbose: print "DXF Diagnostics" if printverbose: print obj.Shape.ShapeType if printverbose: print "Closed : "+str(f.isClosed()) if printverbose: print f.check() if printverbose: print [w.isClosed() for w in obj.Shape.Wires] return(obj) def processSTL(fname): if printverbose: print "Process STL file" def p_multmatrix_action(p): 'multmatrix_action : multmatrix LPAREN matrix RPAREN OBRACE block_list EBRACE' if printverbose: print "MultMatrix" transform_matrix = FreeCAD.Matrix() if printverbose: print "Multmatrix" if printverbose: print p[3] m1l=sum(p[3],[]) if any('x' in me for me in m1l): #hexfloats m1l=[float.fromhex(me) for me in m1l] matrixisrounded=False elif max((len(me) for me in m1l)) >= 14: #might have double precision m1l=[float(me) for me in m1l] # assume precise output m1l=[(0 if (abs(me) < 1e-15) else me) for me in m1l] matrixisrounded=False else: #trucanted numbers m1l=[round(float(me),12) for me in m1l] #round matrixisrounded=True transform_matrix = FreeCAD.Matrix(*tuple(m1l)) if printverbose: print transform_matrix if printverbose: print "Apply Multmatrix" # If more than one object on the stack for multmatrix fuse first if (len(p[6]) == 0) : part = placeholder('group',[],'{}') elif (len(p[6]) > 1) : part = fuse(p[6],"Matrix Union") else : part = p[6][0] if (isspecialorthogonalpython(fcsubmatrix(transform_matrix))) : if printverbose: print "special orthogonal" if matrixisrounded: if printverbose: print "rotation rounded" plm=FreeCAD.Placement(transform_matrix) plm=FreeCAD.Placement(plm.Base,roundrotation(plm.Rotation)) part.Placement=plm.multiply(part.Placement) else: part.Placement=FreeCAD.Placement(transform_matrix).multiply(\ part.Placement) new_part = part elif isrotoinversionpython(fcsubmatrix(transform_matrix)): if printverbose: print "orthogonal and inversion" cmat,axisvec = decomposerotoinversion(transform_matrix) new_part=doc.addObject("Part::Mirroring",'mirr_%s'%part.Name) new_part.Source=part new_part.Normal=axisvec if matrixisrounded: if printverbose: print "rotation rounded" plm=FreeCAD.Placement(cmat) new_part.Placement=FreeCAD.Placement(plm.Base,roundrotation(plm.Rotation)) else: new_part.Placement=FreeCAD.Placement(cmat) new_part.Label="mirrored %s" % part.Label if gui: part.ViewObject.hide() elif FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useMultmatrixFeature'): from OpenSCADFeatures import MatrixTransform new_part=doc.addObject("Part::FeaturePython",'Matrix Deformation') MatrixTransform(new_part,transform_matrix,part) if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(new_part.ViewObject) else: new_part.ViewObject.Proxy = 0 part.ViewObject.hide() else : if printverbose: print "Transform Geometry" # Need to recompute to stop transformGeometry causing a crash doc.recompute() new_part = doc.addObject("Part::Feature","Matrix Deformation") # new_part.Shape = part.Base.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 newobj=doc.addObject("Part::FeaturePython",'RefineMultMatrix') RefineShape(newobj,new_part) if gui: newobj.ViewObject.Proxy = 0 new_part.ViewObject.hide() p[0] = [newobj] else : p[0] = [new_part] if printverbose: print "Multmatrix applied" def p_matrix(p): 'matrix : OSQUARE vector COMMA vector COMMA vector COMMA vector ESQUARE' if printverbose: print "Matrix" p[0] = [p[2],p[4],p[6],p[8]] def p_vector(p): 'vector : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER ESQUARE' if printverbose: print "Vector" p[0] = [p[2],p[4],p[6],p[8]] 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] r = float(p[3]['r']) mysphere = doc.addObject("Part::Sphere",p[1]) mysphere.Radius = r if printverbose: print "Push Sphere" p[0] = [mysphere] if printverbose: print "End Sphere" def myPolygon(n,r1): # Adapted from Draft::_Polygon import math if printverbose: print "My Polygon" angle = math.pi*2/n nodes = [FreeCAD.Vector(r1,0,0)] for i in range(n-1) : th = (i+1) * angle nodes.append(FreeCAD.Vector(r1*math.cos(th),r1*math.sin(th),0)) nodes.append(nodes[0]) polygonwire = Part.makePolygon(nodes) polygon = doc.addObject("Part::Feature","Polygon") polygon.Shape = Part.Face(polygonwire) return(polygon) def p_cylinder_action(p): 'cylinder_action : cylinder LPAREN keywordargument_list RPAREN SEMICOL' if printverbose: print "Cylinder" tocenter = p[3]['center'] h = float(p[3]['h']) r1 = float(p[3]['r1']) r2 = float(p[3]['r2']) n = int(p[3]['$fn']) fnmax = FreeCAD.ParamGet(\ "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetInt('useMaxFN') if printverbose: print p[3] if h > 0: if ( r1 == r2 and r1 > 0): if printverbose: print "Make Cylinder" if n < 3 or fnmax != 0 and n > fnmax: mycyl=doc.addObject("Part::Cylinder",p[1]) mycyl.Height = h mycyl.Radius = r1 else : if printverbose: print "Make Prism" if False: #user Draft Polygon mycyl=doc.addObject("Part::Extrusion","prism") mycyl.Dir = (0,0,h) try : import Draft mycyl.Base = Draft.makePolygon(n,r1) except : # If Draft can't import (probably due to lack of Pivy on Mac and # Linux builds of FreeCAD), this is a fallback. # or old level of FreeCAD if printverbose: print "Draft makePolygon Failed, falling back on manual polygon" mycyl.Base = myPolygon(n,r1) # mycyl.Solid = True else : pass if gui: mycyl.Base.ViewObject.hide() else: #Use Part::Prism primitive mycyl=doc.addObject("Part::Prism","prism") mycyl.Polygon = n mycyl.Circumradius = r1 mycyl.Height = h elif (r1 != r2): if n < 3 or fnmax != 0 and n > fnmax: if printverbose: print "Make Cone" mycyl=doc.addObject("Part::Cone",p[1]) mycyl.Height = h mycyl.Radius1 = r1 mycyl.Radius2 = r2 else: if printverbose: print "Make Frustum" mycyl=doc.addObject("Part::FeaturePython",'frustum') Frustum(mycyl,r1,r2,n,h) if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(mycyl.ViewObject) else: mycyl.ViewObject.Proxy = 0 else: # r1 == r2 == 0 FreeCAD.Console.PrintWarning('cylinder with radius zero\n') mycyl=doc.addObject("Part::Feature","emptycyl") mycyl.Shape = Part.Compound([]) else: # h == 0 FreeCAD.Console.PrintWarning('cylinder with height <= zero\n') mycyl=doc.addObject("Part::Feature","emptycyl") mycyl.Shape = Part.Compound([]) 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 newobj=doc.addObject("Part::FeaturePython",'RefineCylinder') RefineShape(newobj,mycyl) if gui: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetBool('useViewProviderTree'): from OpenSCADFeatures import ViewProviderTree ViewProviderTree(newobj.ViewObject) else: newobj.ViewObject.Proxy = 0 mycyl.ViewObject.hide() p[0] = [newobj] else : p[0] = [mycyl] if printverbose: print "End Cylinder" def p_cube_action(p): 'cube_action : cube LPAREN keywordargument_list RPAREN SEMICOL' global doc l,w,h = [float(str1) for str1 in p[3]['size']] if (l > 0 and w > 0 and h >0): if printverbose: print "cube : ",p[3] mycube=doc.addObject('Part::Box',p[1]) mycube.Length=l mycube.Width=w mycube.Height=h else: FreeCAD.Console.PrintWarning('cube with radius zero\n') mycube=doc.addObject("Part::Feature","emptycube") mycube.Shape = Part.Compound([]) if p[3]['center']=='true' : center(mycube,l,w,h); p[0] = [mycube] if printverbose: print "End Cube" def p_circle_action(p) : 'circle_action : circle LPAREN keywordargument_list RPAREN SEMICOL' if printverbose: print "Circle : "+str(p[3]) r = float(p[3]['r']) n = int(p[3]['$fn']) fnmax = FreeCAD.ParamGet(\ "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ GetInt('useMaxFN',50) # Alter Max polygon to control if polygons are circles or polygons # in the modules preferences import Draft if n == 0 or fnmax != 0 and n >= fnmax: mycircle = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",'circle') Draft._Circle(mycircle) mycircle.Radius = r #mycircle = Draft.makeCircle(r) # would call doc.recompute #mycircle = doc.addObject('Part::Circle',p[1]) #would not create a face #mycircle.Radius = r else : #mycircle = Draft.makePolygon(n,r) # would call doc.recompute mycircle = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",'polygon') Draft._Polygon(mycricle) mycircle.FacesNumber = n mycircle.Radius = r mycircle.DrawMode = "inscribed" if gui: Draft._ViewProviderDraft(mycircle.ViewObject) if printverbose: print "Push Circle" p[0] = [mycircle] def p_square_action(p) : 'square_action : square LPAREN keywordargument_list RPAREN SEMICOL' if printverbose: print "Square" size = p[3]['size'] x = float(size[0]) y = float(size[1]) mysquare = doc.addObject('Part::Plane',p[1]) mysquare.Length=x mysquare.Width=y if p[3]['center']=='true' : center(mysquare,x,y,0) p[0] = [mysquare] def convert_points_list_to_vector(l): v = [] for i in l : if printverbose: print i v.append(FreeCAD.Vector(i[0],i[1])) if printverbose: print v return(v) def p_polygon_action_nopath(p) : 'polygon_action_nopath : polygon LPAREN points EQ OSQUARE points_list_2d ESQUARE COMMA paths EQ undef COMMA keywordargument_list RPAREN SEMICOL' if printverbose: print "Polygon" if printverbose: print p[6] v = convert_points_list_to_vector(p[6]) mypolygon = doc.addObject('Part::Feature',p[1]) if printverbose: print "Make Parts" # Close Polygon v.append(v[0]) parts = Part.makePolygon(v) if printverbose: print "update object" mypolygon.Shape = Part.Face(parts) p[0] = [mypolygon] def p_polygon_action_plus_path(p) : 'polygon_action_plus_path : polygon LPAREN points EQ OSQUARE points_list_2d ESQUARE COMMA paths EQ OSQUARE path_set ESQUARE COMMA keywordargument_list RPAREN SEMICOL' if printverbose: print "Polygon with Path" if printverbose: print p[6] v = convert_points_list_to_vector(p[6]) if printverbose: print "Path Set List" if printverbose: print p[12] for i in p[12] : if printverbose: print i mypolygon = doc.addObject('Part::Feature','wire') path_list = [] for j in i : j = int(j) if printverbose: print j path_list.append(v[j]) # Close path path_list.append(v[int(i[0])]) if printverbose: print 'Path List' if printverbose: print path_list wire = Part.makePolygon(path_list) mypolygon.Shape = Part.Face(wire) p[0] = [mypolygon] # This only pushes last polygon def make_face(v1,v2,v3): wire = Part.makePolygon([v1,v2,v3,v1]) face = Part.Face(wire) return face def p_polyhedron_action(p) : '''polyhedron_action : polyhedron LPAREN points EQ OSQUARE points_list_3d ESQUARE COMMA faces EQ OSQUARE points_list_3d ESQUARE COMMA keywordargument_list RPAREN SEMICOL | polyhedron LPAREN points EQ OSQUARE points_list_3d ESQUARE COMMA triangles EQ OSQUARE points_list_3d ESQUARE COMMA keywordargument_list RPAREN SEMICOL''' if printverbose: print "Polyhedron Points" v = [] for i in p[6] : if printverbose: print i v.append(FreeCAD.Vector(float(i[0]),float(i[1]),float(i[2]))) if printverbose: print v print "Polyhedron triangles" print p[12] faces_list = [] mypolyhed = doc.addObject('Part::Feature',p[1]) for i in p[12] : if printverbose: print i f = make_face(v[int(i[0])],v[int(i[1])],v[int(i[2])]) faces_list.append(f) shell=Part.makeShell(faces_list) solid=Part.Solid(shell).removeSplitter() if solid.Volume < 0: solid.reverse() mypolyhed.Shape=solid p[0] = [mypolyhed] def p_projection_action(p) : 'projection_action : projection LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' if printverbose: print 'Projection' if gui: from PySide import QtGui QtGui.QMessageBox.critical(None, unicode(translate('OpenSCAD',"Projection Not yet Coded waiting for Peter Li")),unicode(translate('OpenSCAD'," Press OK")))