Download src/Mod/OpenSCAD/importCSG.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 48.4 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/OpenSCAD/importCSG.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/OpenSCAD/importCSG.py
-
curl -L -o importCSG.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/OpenSCAD/importCSG.py
48.4 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # -*- coding: utf8 -*- | |
| #*************************************************************************** | |
| #* Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk> * | |
| #* * | |
| #* 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 <keith@sloan-home.co.uk>" | |
| __url__ = ["http://www.sloan-home.co.uk/ImportCSG"] | |
| printverbose = False | |
| import io | |
| import os | |
| import xml.sax | |
| import FreeCAD | |
| import Part | |
| import Draft | |
| from OpenSCADFeatures import * | |
| from OpenSCADUtils import * | |
| import ply.lex as lex | |
| import ply.yacc as yacc | |
| params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") | |
| printverbose = params.GetBool('printVerbose', False) | |
| if FreeCAD.GuiUp: | |
| gui = True | |
| else: | |
| if printverbose: print("FreeCAD Gui not present.") | |
| gui = False | |
| hassetcolor = [] | |
| alreadyhidden = [] | |
| original_root_objects = [] | |
| # Get the token map from the lexer. This is required. | |
| import tokrules | |
| from tokrules import tokens | |
| from builtins import open as pyopen | |
| translate = FreeCAD.Qt.translate | |
| def shallHide(subject): | |
| for obj in subject.OutListRecursive: | |
| if "Matrix_Union" in str(obj.FullName): | |
| return False | |
| if "Extrude" in str(obj.FullName): | |
| 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 | |
| unless its constituents are also colored. This code sets colors for those | |
| constituents unless already set elsewhere. | |
| ''' | |
| obj.ViewObject.ShapeColor = color | |
| obj.ViewObject.Transparency = transp | |
| # Add any other relevant features to this list | |
| boolean_features = ["Part::Fuse", "Part::MultiFuse", "Part::Cut", | |
| "Part::Common", "Part::MultiCommon"] | |
| if obj.TypeId in boolean_features: | |
| for currentObject in obj.OutList: | |
| if printverbose: print(f"Fixing up colors for: {currentObject.FullName}") | |
| 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 | |
| # 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 | |
| for obj in root_object.OutListRecursive: | |
| obj.ViewObject.Visibility = False | |
| 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) | |
| 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_unused = os.path.splitext(os.path.basename(filename))[0] | |
| try: | |
| doc = FreeCAD.getDocument(docname) | |
| for obj in doc.RootObjects: | |
| original_root_objects.append(obj) | |
| except NameError: | |
| doc = FreeCAD.newDocument(docname) | |
| if filename.lower().endswith('.scad'): | |
| tmpfile = callopenscad(filename) | |
| 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.6a') | |
| # 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') | |
| # Disable generation of debug ('parser.out') and table cache ('parsetab.py'), | |
| # as it requires a writable location | |
| parser = yacc.yacc(debug=False, write_tables=False) | |
| if printverbose: print('Parser Loaded') | |
| f = io.open(filename, 'r', encoding="utf8") | |
| if printverbose: print('Start Parser') | |
| result = parser.parse(f.read()) | |
| f.close() | |
| if printverbose: | |
| print('End Parser') | |
| print(result) | |
| if gui: | |
| fixVisibility() | |
| hassetcolor.clear() | |
| alreadyhidden.clear() | |
| 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(len(p) > 2): | |
| if printverbose: print(p[2]) | |
| p[0] = p[1] + p[2] | |
| else: | |
| p[0] = p[1] | |
| 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 need for implicit fuse | |
| if p[5] is None: | |
| p[0] = [] | |
| return | |
| if len(p[5]) > 1: | |
| if printverbose: print('Fuse Group') | |
| for obj in p[5]: | |
| checkObjShape(obj) | |
| p[0] = [fuse(p[5], "Group")] | |
| else: | |
| if printverbose: print(f"Group {p[5]} type {type(p[5])}") | |
| checkObjShape(p[5]) | |
| 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_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 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 | |
| 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 | |
| | text_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] == ',': | |
| p[0] = [p[1]] | |
| else: | |
| if printverbose: | |
| print(p[1]) | |
| print(p[2]) | |
| p[1].append(p[2]) | |
| p[0] = p[1] | |
| 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 p[2] == ',': | |
| p[0] = [int(p[1])] | |
| else: | |
| p[1].append(int(p[2])) | |
| p[0] = p[1] | |
| def p_path_list(p): | |
| 'path_list : OSQUARE path_points ESQUARE' | |
| p[0] = p[2] | |
| def p_path_set(p): | |
| ''' | |
| path_set : path_list | |
| | path_set COMMA path_list | |
| ''' | |
| if len(p) == 2: | |
| p[0] = [p[1]] | |
| else: | |
| p[1].append(p[3]) | |
| p[0] = p[1] | |
| def p_operation(p): | |
| ''' | |
| operation : difference_action | |
| | intersection_action | |
| | union_action | |
| | rotate_extrude_action | |
| | linear_extrude_with_transform | |
| | rotate_extrude_file | |
| | import_file1 | |
| | resize_action | |
| | surface_action | |
| | projection_action | |
| | hull_action | |
| | minkowski_action | |
| | offset_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' | |
| subobj = None | |
| if len(p[6]) == 0: | |
| newobj = placeholder('group',[],'{}') | |
| elif (len(p[6]) == 1 ): #single object | |
| subobj = p[6][0] | |
| else: | |
| subobj = fuse(p[6],"Offset Union") | |
| if 'r' in p[3]: | |
| offset = float(p[3]['r']) | |
| if 'delta' in p[3]: | |
| offset = float(p[3]['delta']) | |
| checkObjShape(subobj) | |
| if subobj.Shape.Volume == 0 : | |
| newobj = doc.addObject("Part::Offset2D",'Offset2D') | |
| newobj.Source = subobj | |
| newobj.Value = offset | |
| 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() | |
| if gui: | |
| subobj.ViewObject.hide() | |
| p[0] = [newobj] | |
| def checkObjShape(obj): | |
| if printverbose: print('Check Object Shape') | |
| if hasattr(obj, 'Shape'): | |
| if obj.Shape.isNull(): | |
| if printverbose: print('Shape is Null - recompute') | |
| obj.recompute() | |
| if obj.Shape.isNull(): | |
| print(f'Recompute failed : {obj.Name}') | |
| else: | |
| if hasattr(obj, 'Name'): | |
| print(f"obj {obj.Name} has no Shape") | |
| else: | |
| print(f"obj {obj} has no Name & Shape") | |
| 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_resize_action(p): | |
| ''' | |
| resize_action : resize LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE ''' | |
| new_size = p[3]['newsize'] | |
| 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': | |
| new_size[r] = new_size[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], | |
| float(new_size[2])/old_size[2]) | |
| transform_matrix.scale(scale) | |
| new_part=doc.addObject("Part::FeaturePython",'Matrix Deformation') | |
| new_part.Shape = p[6][0].Shape.transformGeometry(transform_matrix) | |
| 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 | |
| p[6][0].ViewObject.hide() | |
| p[0] = [new_part] | |
| def p_not_supported(p): | |
| ''' | |
| not_supported : glide LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE | |
| | subdiv 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, translate('OpenSCAD',"Unsupported Function")+" : "+p[1],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 | |
| | text EQ stripped_string | |
| | 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]: | |
| if shallHide(obj): | |
| if "Group" in obj.FullName: | |
| obj.ViewObject.Visibility=False | |
| alreadyhidden.append(obj) | |
| setColorRecursively(obj, color, transp) | |
| hassetcolor.append(obj) | |
| 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] | |
| checkObjShape(myfuse.Base) | |
| checkObjShape(myfuse.Tool) | |
| myfuse.Shape = myfuse.Base.Shape.fuse(myfuse.Tool.Shape) | |
| if gui: | |
| myfuse.Base.ViewObject.hide() | |
| myfuse.Tool.ViewObject.hide() | |
| myfuse.Placement = FreeCAD.Placement() | |
| return myfuse | |
| def p_empty_union_action(p): | |
| 'union_action : union LPAREN RPAREN SEMICOL' | |
| if printverbose: print("empty union") | |
| newpart = fuse([],p[1]) | |
| if printverbose: print("Push Union Result") | |
| p[0] = [newpart] | |
| if printverbose: print("End Union") | |
| 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_unused = 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] | |
| checkObjShape(mycut.Tool) | |
| 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] | |
| checkObjShape(mycommon.Base) | |
| checkObjShape(mycommon.Tool) | |
| 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',[],'{}') | |
| mycommon.Shape = mycommon.Base.Shape.common(mycommon.Tool.Shape) | |
| p[0] = [mycommon] | |
| if printverbose: print("End Intersection") | |
| def process_rotate_extrude(obj, angle): | |
| 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 = angle | |
| myrev.Placement = FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90)) | |
| if gui: | |
| newobj.ViewObject.hide() | |
| return myrev | |
| def process_rotate_extrude_prism(obj, angle, n): | |
| newobj=doc.addObject("Part::FeaturePython",'PrismaticToroid') | |
| PrismaticToroid(newobj, obj, angle, n) | |
| newobj.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90)) | |
| 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() | |
| return newobj | |
| def p_rotate_extrude_action(p): | |
| 'rotate_extrude_action : rotate_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' | |
| if printverbose: print("Rotate Extrude") | |
| angle = 360.0 | |
| if 'angle' in p[3]: | |
| angle = float(p[3]['angle']) | |
| n = int(round(float(p[3]['$fn']))) | |
| fnmax = FreeCAD.ParamGet(\ | |
| "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ | |
| GetInt('useMaxFN', 16) | |
| if (len(p[6]) > 1) : | |
| part = fuse(p[6],"Rotate Extrude Union") | |
| else : | |
| part = p[6][0] | |
| if n < 3 or fnmax != 0 and n > fnmax: | |
| p[0] = [process_rotate_extrude(part,angle)] | |
| else: | |
| p[0] = [process_rotate_extrude_prism(part,angle,n)] | |
| 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") | |
| angle = 360.0 | |
| if 'angle' in p[3]: | |
| angle = float(p[3]['angle']) | |
| filen,ext = p[3]['file'] .rsplit('.',1) | |
| obj = process_import_file(filen,ext,p[3]['layer']) | |
| n = int(round(float(p[3]['$fn']))) | |
| fnmax = FreeCAD.ParamGet(\ | |
| "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ | |
| GetInt('useMaxFN', 16) | |
| if n < 3 or fnmax != 0 and n > fnmax: | |
| p[0] = [process_rotate_extrude(obj,angle)] | |
| else: | |
| p[0] = [process_rotate_extrude_prism(obj,angle,n)] | |
| 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() | |
| # V17 change to False mylinear.Solid = True | |
| mylinear.Solid = False | |
| if gui: | |
| newobj.ViewObject.hide() | |
| return mylinear | |
| def process_linear_extrude_with_transform(base,height,twist,scale) : | |
| newobj = doc.addObject("Part::FeaturePython",'transform_extrude') | |
| # base is a FreeCAD Object, height & twist are floats, scale is a two-component vector of floats | |
| Twist(newobj,base,height,-twist,scale) | |
| 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_linear_extrude_with_transform(p): | |
| 'linear_extrude_with_transform : linear_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' | |
| if printverbose: print("Linear Extrude With Transform") | |
| h = float(p[3]['height']) | |
| if printverbose: print("Height : ",h) | |
| s = [1.0,1.0] | |
| t = 0.0 | |
| if 'scale' in p[3]: | |
| if isinstance(p[3]['scale'], str): | |
| s = [float(p[3]['scale']), float(p[3]['scale'])] | |
| else: | |
| s = [float(p[3]['scale'][0]), float(p[3]['scale'][1])] | |
| if printverbose: print ("Scale: " + str(s)) | |
| if 'twist' in p[3]: | |
| t = float(p[3]['twist']) | |
| if printverbose: print("Twist : ",t) | |
| # Test if null object like from null text | |
| if (len(p[6]) == 0) : | |
| p[0] = [] | |
| return | |
| if (len(p[6]) > 1) : | |
| obj = fuse(p[6],"Linear Extrude Union") | |
| else : | |
| obj = p[6][0] | |
| checkObjShape(obj) | |
| if t != 0.0 or s[0] != 1.0 or s[1] != 1.0: | |
| newobj = process_linear_extrude_with_transform(obj,h,t,s) | |
| else: | |
| newobj = process_linear_extrude(obj,h) | |
| if p[3].get('center','false')=='true' : | |
| center(newobj,0,0,h) | |
| p[0] = [newobj] | |
| if gui: | |
| obj.ViewObject.hide() | |
| if printverbose: print("End Linear Extrude with Transform") | |
| 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].get('center','false') == '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) | |
| elif ext.lower() == 'svg': | |
| obj = processSVG(fname, ext) | |
| else: | |
| raise ValueError("Unsupported file extension %s" % ext) | |
| return obj | |
| def processSVG(fname, ext): | |
| from importSVG import svgHandler | |
| if printverbose: print("SVG Handler") | |
| doc = FreeCAD.ActiveDocument | |
| docSVG = FreeCAD.newDocument(fname+'_tmp') | |
| FreeCAD.ActiveDocument = docSVG | |
| # Set up the parser | |
| parser = xml.sax.make_parser() | |
| parser.setFeature(xml.sax.handler.feature_external_ges, False) | |
| parser.setContentHandler(svgHandler()) | |
| parser._cont_handler.doc = docSVG | |
| # pathName is a Global | |
| filename = os.path.join(pathName,fname+'.'+ext) | |
| # Use the native Python open which was saved as `pyopen` | |
| parser.parse(pyopen(filename)) | |
| #combine SVG objects into one | |
| shapes = [] | |
| for obj in FreeCAD.ActiveDocument.Objects: | |
| if printverbose: print(obj.Name) | |
| if printverbose: print(obj.Shape) | |
| shapes.append(obj.Shape) | |
| FreeCAD.closeDocument(docSVG.Name) | |
| FreeCAD.ActiveDocument=doc | |
| obj=doc.addObject('Part::Feature',fname) | |
| obj.Shape=Part.Compound(shapes) | |
| return obj | |
| def process_mesh_file(fname,ext): | |
| import Mesh | |
| import 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") | |
| solid = solid.removeSplitter() | |
| if solid.Volume < 0: | |
| solid.complement() | |
| obj.Shape = solid | |
| 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 processTextCmd(t): | |
| from OpenSCADUtils import callopenscadstring | |
| tmpfilename = callopenscadstring(t,'dxf') | |
| from OpenSCAD2Dgeom import importDXFface | |
| face = importDXFface(tmpfilename,None,None) | |
| obj = doc.addObject('Part::Feature','text') | |
| obj.Shape = face | |
| try: | |
| os.unlink(tmpfilename) | |
| except OSError: | |
| pass | |
| 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(obj.Shape.isClosed())) | |
| if printverbose: print(obj.Shape.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]) | |
| if gui and p[6]: | |
| parentcolor=p[6][0].ViewObject.ShapeColor | |
| parenttransparency=p[6][0].ViewObject.Transparency | |
| 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") | |
| part.recompute() | |
| if part.Shape.isNull(): | |
| doc.recompute() | |
| new_part = doc.addObject("Part::Feature","Matrix Deformation") | |
| 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 gui and p[6]: | |
| 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") | |
| 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].get('center','false') | |
| h = float(p[3]['h']) | |
| r1 = float(p[3]['r1']) | |
| r2 = float(p[3]['r2']) | |
| n = int(round(float(p[3]['$fn']))) | |
| fnmax = FreeCAD.ParamGet(\ | |
| "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ | |
| GetInt('useMaxFN', 16) | |
| 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,face=True) | |
| except Exception: | |
| # 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) | |
| 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].get('center','false')=='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']) | |
| # Avoid zero radius | |
| if r == 0 : r = 0.00001 | |
| n = int(p[3]['$fn']) | |
| fnmax = FreeCAD.ParamGet(\ | |
| "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ | |
| GetInt('useMaxFN',16) | |
| # 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.MakeFace = True | |
| mycircle = Draft.makeCircle(r,face=True) # would call doc.recompute | |
| FreeCAD.ActiveDocument.recompute() | |
| else : | |
| mycircle = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",'polygon') | |
| Draft._Polygon(mycircle) | |
| mycircle.FacesNumber = n | |
| mycircle.Radius = r | |
| mycircle.DrawMode = "inscribed" | |
| mycircle.MakeFace = True | |
| 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].get('center','false')=='true' : | |
| center(mysquare,x,y,0) | |
| p[0] = [mysquare] | |
| def addString(t,s,p): | |
| return(t + ', ' +s+' = "'+p[3][s]+'"') | |
| def addValue(t,v,p): | |
| return(t + ', ' +v+' = '+p[3][v]) | |
| def p_text_action(p) : | |
| 'text_action : text LPAREN keywordargument_list RPAREN SEMICOL' | |
| # If text string is null ignore | |
| if p[3]['text'] == "" or p[3]['text'] == " " : | |
| p[0] = [] | |
| return | |
| t = 'text ( text="'+p[3]['text']+'"' | |
| t = addValue(t,'size',p) | |
| t = addString(t,'spacing',p) | |
| t = addString(t,'font',p) | |
| t = addString(t,'direction',p) | |
| t = addString(t,'language',p) | |
| if "script" in p[3]: | |
| t = addString(t,'script',p) | |
| else: | |
| t += ', script="latin"' | |
| t = addString(t,'halign',p) | |
| t = addString(t,'valign',p) | |
| t = addValue(t,'$fn',p) | |
| t = addValue(t,'$fa',p) | |
| t = addValue(t,'$fs',p) | |
| t = t+');' | |
| FreeCAD.Console.PrintMessage("textmsg : "+t+"\n") | |
| p[0] = [processTextCmd(t)] | |
| 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 path_set 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 "+p[9]) | |
| print (p[12]) | |
| faces_list = [] | |
| mypolyhed = doc.addObject('Part::Feature',p[1]) | |
| for i in p[12] : | |
| if printverbose: print(i) | |
| v2 = FreeCAD.Vector | |
| pp =[v2(v[k]) for k in i] | |
| # Add first point to end of list to close polygon | |
| pp.append(pp[0]) | |
| try: | |
| w = Part.makePolygon(pp) | |
| f = Part.Face(w) | |
| except Exception: | |
| secWireList = w.Edges[:] | |
| f = Part.makeFilledFace(Part.__sortEdges__(secWireList)) | |
| 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') | |
| doc.recompute() | |
| p[6][0].Shape.tessellate(0.05) # Ensure the bounding box calculation is not done with the splines, which can give a bad result | |
| bbox = p[6][0].Shape.BoundBox | |
| for shape in p[6]: | |
| shape.Shape.tessellate(0.05) | |
| bbox.add(shape.Shape.BoundBox) | |
| plane = doc.addObject("Part::Plane","xy_plane_used_for_projection") | |
| plane.Length = bbox.XLength | |
| plane.Width = bbox.YLength | |
| plane.Placement = FreeCAD.Placement(FreeCAD.Vector(\ | |
| bbox.XMin,bbox.YMin,0),FreeCAD.Rotation()) | |
| if gui: | |
| plane.ViewObject.hide() | |
| if p[3]['cut'] == 'true' : | |
| obj = doc.addObject('Part::MultiCommon','projection_cut') | |
| if (len(p[6]) > 1): | |
| subobj = [fuse(p[6],"projection_cut_implicit_group")] | |
| else: | |
| subobj = p[6] | |
| obj.Shapes = [plane] + subobj | |
| if gui: | |
| subobj[0].ViewObject.hide() | |
| p[0] = [obj] | |
| else: # cut == 'false' => true projection | |
| if gui and not FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\ | |
| GetBool('usePlaceholderForUnsupported'): | |
| from PySide import QtGui | |
| QtGui.QMessageBox.critical(None, translate('OpenSCAD',"Unsupported Function") + " : " + p[1],translate('OpenSCAD',"Press OK")) | |
| else: | |
| p[0] = [placeholder(p[1],p[6],p[3])] | |