FreeCAD

Форк
0
/
prototype.py 
696 строк · 29.3 Кб
1
#!/usr/bin/env python
2

3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU Lesser General Public License (LGPL)
5
# as published by the Free Software Foundation; either version 2 of
6
# the License, or (at your option) any later version.
7

8
import FreeCAD
9
import math
10
import re
11

12
from OpenSCADFeatures import *
13
from OpenSCAD2Dgeom import *
14
from OpenSCADUtils import *
15
from builtins import open as pyopen
16

17

18

19

20
def openscadmesh(doc, scadstr, objname):
21
    import Part
22
    import Mesh
23
    import os
24
    import OpenSCADUtils
25
    tmpfilename = OpenSCADUtils.callopenscadstring(scadstr,'stl')
26
    if tmpfilename:
27
        #mesh1 = doc.getObject(objname) #reuse imported object
28
        Mesh.insert(tmpfilename)
29
        os.unlink(tmpfilename)
30
        mesh1 = doc.getObject(objname) #blog
31
        mesh1.ViewObject.hide()
32
        sh=Part.Shape()
33
        sh.makeShapeFromMesh(mesh1.Mesh.Topology, 0.1)
34
        solid = Part.Solid(sh)
35
        obj = doc.addObject("Part::FeaturePython", objname)
36
        ImportObject(obj, mesh1) #This object is not mutable from the GUI
37
        ViewProviderTree(obj.ViewObject)
38
        solid = solid.removeSplitter()
39
        if solid.Volume < 0:
40
            solid.complement()
41
        obj.Shape = solid#.removeSplitter()
42
        return obj
43
    else:
44
        print(scadstr)
45

46

47
class Node:
48
    #fnmin = 12 # maximal fn for implicit polygon rendering
49
    fnmin = FreeCAD.ParamGet(\
50
        "User parameter:BaseApp/Preferences/Mod/OpenSCAD").GetInt('useMaxFN')
51
    planedim = 1e10 #size of the square used as x-y-plane
52

53
    def __init__(self, name, arguments=None, children=None,):
54
        pass
55
        self.name = name
56
        self.arguments = arguments or {}
57
        self.children = children or []
58

59
    def __repr__(self):
60
        str1 = 'Node(name=%s' % self.name
61
        if self.arguments:
62
            str1 += ',arguments=%s' % self.arguments
63
        if self.children:
64
            str1 += ',children=%s' % self.children
65
        return str1+')'
66

67
    def __nonzero__(self):
68
        '''A Node is not obsolete if doesn't have children.
69
        Only if as neither name children or arguments'''
70
        return bool(self.name or self.arguments or self.children)
71

72
    def __len__(self):
73
        '''return the number of children'''
74
        return len(self.children)
75

76
    def __getitem__(self, key):
77
        '''direct access to the children'''
78
        return self.children.__getitem__(key)
79

80
    def rlen(self, checkmultmarix=False):
81
        '''Total number of nodes'''
82
        if self.children:
83
            return 1+sum([ch.rlen() for ch in self.children])
84
        else:
85
            return 1
86

87
    def addtofreecad(self,doc=None,fcpar=None):
88
        def center(obj,x,y,z):
89
            obj.Placement = FreeCAD.Placement(\
90
                FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\
91
                FreeCAD.Rotation(0,0,0,1))
92

93
        import FreeCAD
94
        import Part
95
        if not doc:
96
            doc = FreeCAD.newDocument()
97
        obj = None
98
        namel = self.name.lower()
99
        multifeature={'union':"Part::MultiFuse",'imp_union':"Part::MultiFuse",
100
                      'intersection':"Part::MultiCommon"}
101
        if namel in multifeature:
102
            if len(self.children)>1:
103
                obj = doc.addObject(multifeature[namel],namel)
104
                subobjs = [child.addtofreecad(doc,obj) for child in self.children]
105
                obj.Shapes = subobjs
106
                for subobj in subobjs:
107
                    subobj.ViewObject.hide()
108
            elif len(self.children) == 1:
109
                obj = self.children[0].addtofreecad(doc,fcpar or True)
110
            else:
111
                obj = fcpar
112
        elif namel == 'difference':
113
            if len(self.children) == 1:
114
                obj = self.children[0].addtofreecad(doc,fcpar or True)
115
            else:
116
                obj = doc.addObject("Part::Cut",namel)
117
                base = self.children[0].addtofreecad(doc,obj)
118

119
                if len(self.children) == 2:
120
                    tool = self.children[1].addtofreecad(doc,obj)
121
                else:
122
                    tool = Node(name='imp_union',\
123
                        children=self.children[1:]).addtofreecad(doc,obj)
124
                obj.Base = base
125
                obj.Tool = tool
126
                base.ViewObject.hide()
127
                tool.ViewObject.hide()
128
        elif namel == 'cube':
129
            obj = doc.addObject('Part::Box', namel)
130
            x,y,z = self.arguments['size']
131
            obj.Length = x
132
            obj.Width = y
133
            obj.Height = z
134
            if self.arguments['center']:
135
                center(obj,x,y,z)
136
        elif namel == 'sphere':
137
            obj = doc.addObject("Part::Sphere", namel)
138
            obj.Radius = self.arguments['r']
139
        elif namel == 'cylinder':
140
            h = self.arguments['h']
141
            r1, r2 = self.arguments['r1'], self.arguments['r2']
142
            if '$fn' in self.arguments and self.arguments['$fn'] > 2 \
143
            and self.arguments['$fn']<=Node.fnmin: # polygonal
144
                if r1 == r2: # prismatic
145
                    obj = doc.addObject("Part::Prism","prism")
146
                    obj.Polygon = int(self.arguments['$fn'])
147
                    obj.Circumradius = r1
148
                    obj.Height = h
149
                    if self.arguments['center']:
150
                        center(obj,0,0,h)
151
                    #base.ViewObject.hide()
152
                elif False: #use Frustum Feature with makeRuledSurface
153
                    obj = doc.addObject("Part::FeaturePython",'frustum')
154
                    Frustum(obj,r1,r2,int(self.arguments['$fn']), h)
155
                    ViewProviderTree(obj.ViewObject)
156
                    if self.arguments['center']:
157
                        center(obj,0,0,h)
158
                else: #Use Part::Loft and GetWire Feature
159
                    obj = doc.addObject('Part::Loft', 'frustum')
160
                    import Draft
161
                    p1 = Draft.makePolygon(int(self.arguments['$fn']), r1)
162
                    p2 = Draft.makePolygon(int(self.arguments['$fn']), r2)
163
                    if self.arguments['center']:
164
                        p1.Placement = FreeCAD.Placement(\
165
                        FreeCAD.Vector(0.0,0.0,-h/2.0),FreeCAD.Rotation())
166
                        p2.Placement = FreeCAD.Placement(\
167
                        FreeCAD.Vector(0.0,0.0,h/2.0), FreeCAD.Rotation())
168
                    else:
169
                        p2.Placement = FreeCAD.Placement(\
170
                        FreeCAD.Vector(0.0,0.0,h),FreeCAD.Rotation())
171
                    w1 = doc.addObject("Part::FeaturePython",'polygonwire1')
172
                    w2 = doc.addObject("Part::FeaturePython",'polygonwire2')
173
                    GetWire(w1,p1)
174
                    GetWire(w2,p2)
175
                    ViewProviderTree(w1.ViewObject)
176
                    ViewProviderTree(w2.ViewObject)
177
                    obj.Sections = [w1,w2]
178
                    obj.Solid = True
179
                    obj.Ruled = True
180
                    p1.ViewObject.hide()
181
                    p2.ViewObject.hide()
182
                    w1.ViewObject.hide()
183
                    w2.ViewObject.hide()
184
            else:
185
                if r1 == r2:
186
                    obj=doc.addObject("Part::Cylinder",namel)
187
                    obj.Height = h
188
                    obj.Radius = r1
189
                else:
190
                    obj=doc.addObject("Part::Cone",'cone')
191
                    obj.Height = h
192
                    obj.Radius1, obj.Radius2 = r1, r2
193
                if self.arguments['center']:
194
                    center(obj,0,0,h)
195
        elif namel == 'polyhedron':
196
            obj = doc.addObject("Part::Feature",namel)
197
            points = self.arguments['points']
198
            faces = self.arguments['triangles']
199
            shell = Part.Shell([Part.Face(Part.makePolygon(\
200
                     [tuple(points[pointindex]) for pointindex in \
201
                     (face+face[0:1])])) for face in faces])
202
#            obj.Shape=Part.Solid(shell).removeSplitter()
203
            solid=Part.Solid(shell).removeSplitter()
204
            if solid.Volume < 0:
205
#                solid.complement()
206
                solid.reverse()
207
            obj.Shape=solid#.removeSplitter()
208

209
        elif namel == 'polygon':
210
            obj = doc.addObject("Part::Feature", namel)
211
            points = self.arguments['points']
212
            paths = self.arguments.get('paths')
213
            if not paths:
214
                faces = [Part.Face(Part.makePolygon([(x,y,0) for x,y in points+points[0:1]]))]
215
            else:
216
                faces = [Part.Face(Part.makePolygon([(points[pointindex][0],points[pointindex][1],0) for \
217
                    pointindex in (path+path[0:1])])) for path in paths]
218
            obj.Shape=subtractfaces(faces)
219
        elif namel == 'square':
220
            obj = doc.addObject("Part::Plane",namel)
221
            x,y = self.arguments['size']
222
            obj.Length = x
223
            obj.Width = y
224
            if self.arguments['center']:
225
                center(obj,x,y,0)
226
        elif namel == 'circle':
227
            r = self.arguments['r']
228
            import Draft
229
            if '$fn' in self.arguments and self.arguments['$fn'] != 0 \
230
            and self.arguments['$fn']<=Node.fnmin:
231
                obj = Draft.makePolygon(int(self.arguments['$fn']),r)
232
            else:
233
                obj = Draft.makeCircle(r) # create a Face
234
                #obj = doc.addObject("Part::Circle",namel);obj.Radius = r
235
        elif namel == 'color':
236
            if len(self.children) == 1:
237
                obj = self.children[0].addtofreecad(doc,fcpar or True)
238
            else:
239
                obj = Node(name='imp_union',\
240
                        children=self.children).addtofreecad(doc,fcpar or True)
241
            obj.ViewObject.ShapeColor = tuple([float(p) for p in self.arguments[:3]]) #RGB
242
            transp = 100 - int(math.floor(100*self.arguments[3])) #Alpha
243
            obj.ViewObject.Transparency = transp
244
        elif namel == 'multmatrix':
245
            assert(len(self.children)>0)
246
            m1l = [round(f,12) for f in sum(self.arguments,[])] #That's the original matrix
247
            m1 = FreeCAD.Matrix(*tuple(m1l)) #That's the original matrix
248
            if isspecialorthogonalpython(fcsubmatrix(m1)): #a Placement can represent the transformation
249
                if len(self.children) == 1:
250
                    obj = self.children[0].addtofreecad(doc,fcpar or True)
251
                else:
252
                    obj = Node(name='imp_union',\
253
                            children = self.children).addtofreecad(doc,fcpar or True)
254
                    #FreeCAD.Console.PrintMessage('obj %s\nmat %s/n' % (obj.Placement,m1))
255
                obj.Placement=FreeCAD.Placement(m1).multiply(obj.Placement)
256
            else: #we need to apply the matrix transformation to the Shape using a custom PythonFeature
257
                obj = doc.addObject("Part::FeaturePython",namel)
258
                if len(self.children) == 1:
259
                    child = self.children[0].addtofreecad(doc,obj)
260
                else:
261
                    child = Node(name='imp_union',\
262
                            children=self.children).addtofreecad(doc,obj)
263
                MatrixTransform(obj,m1,child) #This object is not mutable from the GUI
264
                ViewProviderTree(obj.ViewObject)
265
        #elif namel == 'import': pass #Custom Feature
266
        elif namel == 'linear_extrude':
267
            height = self.arguments['height']
268
            twist = self.arguments.get('twist')
269
            if not twist:
270
                obj = doc.addObject("Part::Extrusion",namel)
271
            else: #twist
272
                obj = doc.addObject("Part::FeaturePython",'twist_extrude')
273
            if len(self.children)==0:
274
                base = Node('import',self.arguments).addtofreecad(doc,obj)
275
            elif len(self.children)==1:
276
                base = self.children[0].addtofreecad(doc,obj)
277
            else:
278
                base = Node(name='imp_union',\
279
                            children=self.children).addtofreecad(doc,obj)
280
            if False and base.isDerivedFrom('Part::MultiFuse'):
281
                #does not solve all the problems
282
                newobj=doc.addObject("Part::FeaturePython",'refine')
283
                RefineShape(newobj,base)
284
                ViewProviderTree(newobj.ViewObject)
285
                base.ViewObject.hide()
286
                base=newobj
287
            if not twist:
288
                obj.Base = base
289
                obj.Dir = (0,0,height)
290
            else: #twist
291
                Twist(obj,base,height,-twist)
292
                ViewProviderTree(obj.ViewObject)
293
            if self.arguments['center']:
294
                center(obj,0,0,height)
295
            base.ViewObject.hide()
296

297
        elif namel == 'rotate_extrude':
298
            obj = doc.addObject("Part::Revolution",namel)
299
            if len(self.children)==0:
300
                base = Node('import',self.arguments).addtofreecad(doc,obj)
301
            elif len(self.children)==1:
302
                base = self.children[0].addtofreecad(doc,obj)
303
            else:
304
                base = Node(name='imp_union',\
305
                            children=self.children).addtofreecad(doc,obj)
306
            if False and base.isDerivedFrom('Part::MultiFuse'):
307
                #creates 'Axe and meridian are confused' Errors
308
                newobj=doc.addObject("Part::FeaturePython",'refine')
309
                RefineShape(newobj,base)
310
                ViewProviderTree(newobj.ViewObject)
311
                base.ViewObject.hide()
312
                base=newobj
313
            obj.Source= base
314
            obj.Axis = (0.00,1.00,0.00)
315
            obj.Base = (0.00,0.00,0.00)
316
            obj.Angle = 360.00
317
            base.ViewObject.hide()
318
            obj.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90))
319
        elif namel == 'projection':
320
            if self.arguments['cut']:
321
                planename = 'xy_plane_used_for_project_cut'
322
                obj = doc.addObject('Part::MultiCommon','projection_cut')
323
                plane = doc.getObject(planename)
324
                if not plane:
325
                    plane=doc.addObject("Part::Plane",planename)
326
                    plane.Length=Node.planedim*2
327
                    plane.Width=Node.planedim*2
328
                    plane.Placement = FreeCAD.Placement(FreeCAD.Vector(\
329
                    -Node.planedim,-Node.planedim,0),FreeCAD.Rotation(0,0,0,1))
330
                #plane.ViewObject.hide()
331
                subobjs = [child.addtofreecad(doc,obj) for child in self.children]
332
                subobjs.append(plane)
333
                obj.Shapes = subobjs
334
                for subobj in subobjs:
335
                    subobj.ViewObject.hide()
336
            else:
337
                #Do a proper projection
338
                raise(NotImplementedError)
339
        elif namel == 'import':
340
            filename = self.arguments.get('file')
341
            scale = self.arguments.get('scale')
342
            origin = self.arguments.get('origin')
343
            if filename:
344
                import os
345
                docname = os.path.split(filename)[1]
346
                objname,extension = docname.split('.',1)
347
                if not os.path.isabs(filename):
348
                    try:
349
                        global lastimportpath
350
                        filename = os.path.join(lastimportpath,filename)
351
                    except: raise #no path given
352
                # Check for a mesh fileformat support by the Mesh mddule
353
                if extension.lower() in reverseimporttypes()['Mesh']:
354
                    import Mesh
355
                    mesh1 = doc.getObject(objname) #reuse imported object
356
                    if not mesh1:
357
                        Mesh.insert(filename)
358
                        mesh1 = doc.getObject(objname)
359
                    mesh1.ViewObject.hide()
360
                    sh = Part.Shape()
361
                    sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1)
362
                    solid = Part.Solid(sh)
363
                    obj = doc.addObject("Part::FeaturePython",'import_%s_%s'%(extension,objname))
364
                    #obj=doc.addObject('Part::Feature',)
365
                    ImportObject(obj,mesh1) #This object is not mutable from the GUI
366
                    ViewProviderTree(obj.ViewObject)
367
                    solid = solid.removeSplitter()
368
                    if solid.Volume < 0:
369
                        #sh.reverse()
370
                        #sh = sh.copy()
371
                        solid.complement()
372
                    obj.Shape = solid#.removeSplitter()
373
                elif extension in ['dxf']:
374
                    layera = self.arguments.get('layer')
375
                    featname ='import_dxf_%s_%s'%(objname,layera)
376
                    # reusing an already imported object does not work if the
377
                    # shape in not yet calculated
378
                    import importDXF
379
                    global dxfcache
380
                    layers = dxfcache.get(id(doc),[])
381
                    if layers:
382
                        groupobj = [go for go in layers if (not layera) or go.Label == layera]
383
                    else:
384
                        groupobj = None
385
                    if not groupobj:
386
                        groupname = objname
387
                        layers = importDXF.processdxf(doc,filename) or importDXF.layers
388
                        dxfcache[id(doc)] = layers[:]
389
                        for l in layers:
390
                            for o in l.Group:
391
                                o.ViewObject.hide()
392
                            l.ViewObject.hide()
393
                        groupobj = [go for go in layers if (not layera) or go.Label == layera]
394
                    edges = []
395
                    for shapeobj in groupobj[0].Group:
396
                        edges.extend(shapeobj.Shape.Edges)
397
                    try:
398
                        f = edgestofaces(edges)
399
                    except Part.OCCError:
400
                        FreeCAD.Console.PrintError('processing of dxf import failed\nPlease rework \'%s\' manually\n' % layera)
401
                        f = Part.Shape() #empty Shape
402
                    obj = doc.addObject("Part::FeaturePython",'import_dxf_%s_%s'%(objname,layera))
403
                    #obj=doc.addObject('Part::Feature',)
404
                    ImportObject(obj,groupobj[0]) #This object is not mutable from the GUI
405
                    ViewProviderTree(obj.ViewObject)
406
                    obj.Shape=f
407

408
                else:
409
                    FreeCAD.Console.ErrorMessage('Filetype of %s not supported\n' % (filename))
410
                    raise(NotImplementedError)
411
                if obj: #handle origin and scale
412
                    if scale is not None and scale !=1:
413
                        if origin is not None and any([c != 0 for c in origin]):
414
                            raise(NotImplementedError)# order of transformations unknown
415
                        child = obj
416
                        m1 = FreeCAD.Matrix()
417
                        m1.scale(scale,scale,scale)
418
                        obj = doc.addObject("Part::FeaturePython",'scale_import')
419
                        MatrixTransform(obj,m1,child) #This object is not mutable from the GUI
420
                        ViewProviderTree(obj.ViewObject)
421
                    elif origin is not None and any([c != 0 for c in origin]):
422
                        placement = FreeCAD.Placement(FreeCAD.Vector(*[-c for c in origin]),FreeCAD.Rotation())
423
                        obj.Placement = placement.multiply(obj.Placement)
424
                else:
425
                    FreeCAD.Console.ErrorMessage('Import of %s failed\n' % (filename))
426

427

428
        elif namel == 'minkowski':
429
            childrennames = [child.name.lower() for child in self.children]
430
            if len(self.children) == 2 and \
431
                childrennames.count('cube') == 1 and \
432
                (childrennames.count('sphere') + \
433
                childrennames.count('cylinder')) == 1:
434
                if self.children[0].name.lower() == 'cube':
435
                    cube = self.children[0]
436
                    roundobj = self.children[1]
437
                elif self.children[1].name.lower() == 'cube':
438
                    cube = self.children[1]
439
                    roundobj = self.children[0]
440
                roundobjname = roundobj.name.lower()
441
                issphere =  roundobjname == 'sphere'
442
                cubeobj = doc.addObject('Part::Box','roundedcube')
443
                x,y,z = cube.arguments['size']
444
                r = roundobj.arguments.get('r') or \
445
                        roundobj.arguments.get('r1')
446
                cubeobj.Length = x+2*r
447
                cubeobj.Width = y+2*r
448
                cubeobj.Height = z+2*r*issphere
449
                obj = doc.addObject("Part::Fillet","%s_%s"%(namel,roundobjname))
450
                obj.Base = cubeobj
451
                cubeobj.ViewObject.hide()
452
                if issphere:
453
                    obj.Edges = [(i,r,r) for i in range(1,13)]
454
                else:#cylinder
455
                    obj.Edges = [(i,r,r) for i in [1,3,5,7]]
456
                if cube.arguments['center']:
457
                    center(cubeobj,x+2*r,y+2*r,z+2*r*issphere)
458
                else: #htandle a rotated cylinder
459
                    #OffsetShape
460
                    raise(NotImplementedError)
461
            elif childrennames.count('sphere') == 1:
462
                sphereindex = childrennames.index('sphere')
463
                sphere = self.children[sphereindex]
464
                offset = sphere.arguments['r']
465
                nonsphere = self.children[0:sphereindex]+\
466
                        self.sphere[sphereindex+1:]
467
                obj = doc.addObject("Part::FeaturePython",'Offset')
468
                if len(nonsphere) == 1:
469
                    child = nonsphere[0].addtofreecad(doc,obj)
470
                else:
471
                    child = Node(name='imp_union',\
472
                        children=nonsphere).addtofreecad(doc,obj)
473
                OffsetShape(obj,child,offset)
474
                ViewProviderTree(obj.ViewObject)
475
            elif False:
476
                raise(NotImplementedError)
477
                pass # handle rotated cylinders and select edges that
478
                     #radius = radius0 * m1.multiply(FreeCAD.Vector(0,0,1)).dot(edge.Curve.tangent(0)[0])
479
            else:
480
                raise(NotImplementedError)
481
        elif namel == 'surface':
482
            obj = doc.addObject("Part::Feature",namel) #include filename?
483
            obj.Shape,xoff,yoff=makeSurfaceVolume(self.arguments['file'])
484
            if self.arguments['center']:
485
                center(obj,xoff,yoff,0.0)
486
            return obj
487
            #import os
488
            #scadstr = 'surface(file = "%s", center = %s );' % \
489
            #    (self.arguments['file'], 'true' if self.arguments['center'] else 'false')
490
            #docname=os.path.split(self.arguments['file'])[1]
491
            #objname,extension = docname.split('.',1)
492
            #obj = openscadmesh(doc,scadstr,objname)
493

494
        elif namel in ['glide','hull']:
495
            raise(NotImplementedError)
496
        elif namel in ['render','subdiv'] or True:
497
            lenchld = len(self.children)
498
            if lenchld == 1:
499
                FreeCAD.Console.PrintMessage('Not recognized %s\n' % (self))
500
                obj = self.children[0].addtofreecad(doc,fcpar)
501
            elif lenchld >1:
502
                obj = Node(name='imp_union',\
503
                        children=self.children).addtofreecad(doc,fcpar or True)
504
            else:
505
                obj = doc.addObject("Part::Feature",'Not_Impl_%s'%namel)
506
        if fcpar == True: #We are the last real object, our parent is not rendered.
507
            return obj
508

509
        if fcpar:
510
            try:
511
                obj.ViewObject.hide()
512
            except: raise
513
            if True: #never refine the Shape, as it itroduces crashes
514
                return obj
515
            else: #refine Shape
516
                import Draft
517
                if obj.Type =='Part::Extrusion' and obj.Base.Type == 'Part::Part2DObjectPython' and \
518
                    isinstance(obj.Base.Proxy,Draft._Polygon) or \
519
                    (not obj.isDerivedFrom('Part::Extrusion') and \
520
                    not obj.isDerivedFrom('Part::Boolean') and \
521
                    not obj.isDerivedFrom('Part::Cut') and \
522
                    not obj.isDerivedFrom('Part::MultiCommon') and \
523
                    not obj.isDerivedFrom('Part::MultiFuse') and \
524
                    not obj.isDerivedFrom('Part::Revolution') ) \
525
                    or (obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy,RefineShape)):
526
                    return obj
527
                else:
528
                    newobj = doc.addObject("Part::FeaturePython",'refine')
529
                    RefineShape(newobj,obj)
530
                    ViewProviderTree(newobj.ViewObject)
531
                    obj.ViewObject.hide()
532
                    return newobj
533

534
        else:
535
            doc.recompute()
536

537
    def flattengroups(self,name='group'):
538
        """removes group node with only one child and no arguments and empty groups"""
539
        node = self
540
        while (node.name == name and len(node.children) == 1 and len(node.arguments) == 0):
541
            node = node.children[0]
542
        node.children = [child for child in node.children if not (len(child.children) == 0 and child.name == name)]
543
        if node.children:
544
            node.children = [child.flattengroups() for child in node.children]
545
        return node
546

547
    def pprint(self,level=0):
548
        """prints the indented tree"""
549
        if self.arguments:
550
            argstr = ' (%s)' % self.arguments
551
        else:
552
            argstr = ''
553
        print('%s %s%s' %('  '*level,self.name,argstr))
554
        for child in self.children:
555
            child.pprint(level+1)
556

557
    def pprint2(self,path='root',pathjust=24):
558
        """prints the tree. Left column contains the syntax to access a child"""
559
        if self.arguments:
560
            argstr = ' (%s)' % self.arguments
561
        else:
562
            argstr = ''
563
        print('%s %s%s' %(path.ljust(pathjust),self.name,argstr))
564
        for i,child in enumerate(self.children):
565
            child.pprint2('%s[%d]'%(path,i),pathjust)
566

567

568

569

570
def parseexpression(e):
571
    e = e.strip()
572
    el = e.lower()
573
    if len(el) == 0: return None
574
    if el == 'true': return True
575
    elif el == 'false': return False
576
    elif el == 'undef': return None
577
    elif e[0].isdigit() or e[0] == '-' and len(e)>1 and e[1].isdigit():
578
        try:
579
            return float(e)
580
        except ValueError:
581
            import FreeCAD
582
            FreeCAD.Console.PrintMessage('%s\n' % (el))
583
            return 1.0
584

585
    elif el.startswith('"'): return e.strip('"') #string literal
586
    elif el.startswith('['):
587
        bopen, bclose = e.count('['), e.count(']')
588
        if bopen == bclose:
589
            return eval(el)
590
        else:
591
            import FreeCAD
592
            FreeCAD.Console.PrintMessage('%s\n' % (el))
593
            #return eval(el)
594
            #assert(False) #Malformed
595
    else:
596
        return e #Return the string
597

598
def parseargs(argstring):
599
    if '=' in argstring:
600
        level = 0
601
        tok = []
602
        a = []
603
        for i,char in enumerate(argstring):
604
            if char == '[': level += 1
605
            elif char ==']': level -= 1
606
            if level == 0 and (char == '=' or char == ','):
607
                tok.append(''.join(a).strip())
608
                a= []
609
            else:
610
                a.append(char)
611
        tok.append(''.join(a).strip())
612
        #print(tok)
613
        argdict = dict(zip(tok[0::2],[parseexpression(argstring) for argstring in tok[1::2]]))
614
#        argdict = {}
615
#        for key, value in re.findall(r"(\$?\w+)\s*=\s*(\[?\w+]?),?\s*",argstring):
616
#            argdict[key] = parseexpression(value)
617
        return argdict
618
    else:
619
        return parseexpression(argstring)
620

621
def parsenode(str1):
622
    name, str2 = str1.strip().split('(',1)
623
    assert('}' not in name)
624
    name = name.strip('#!%* ')#remove/ignore modifiers
625
    args, str3 = str2.split(')',1)
626
    str4 = str3.lstrip()
627
    if str4.startswith(';'):
628
        #has no children
629
        nextelement = str4[1:].lstrip()
630
        return Node(name,parseargs(args)),nextelement
631
    elif str4.startswith('{'):
632
        #has children
633
        level = 0
634
        for index,char in enumerate(str4):
635
            if char == '{': level += 1
636
            elif char == '}': level -= 1
637
            if level == 0:
638
                break
639
                #end of children
640
        childstr = str4[1:index].strip()
641
        nextelement = str4[index+1:].lstrip()
642
        bopen,bclose = childstr.count('{'),childstr.count('}')
643
        assert(bopen == bclose)
644
        children= []
645
        while childstr:
646
            try:
647
                childnode,childstr = parsenode(childstr)
648
                children.append(childnode)
649
            except ValueError:
650
                raise
651
        if args:
652
            args = parseargs(args)
653
        return Node(name,args,children),nextelement
654

655
def readfile(filename):
656
    import os
657
    global lastimportpath
658
    lastimportpath,relname = os.path.split(filename)
659
    isopenscad = relname.lower().endswith('.scad')
660
    if isopenscad:
661
        tmpfile=callopenscad(filename)
662
        if OpenSCADUtils.workaroundforissue128needed():
663
            lastimportpath = os.getcwd() #https://github.com/openscad/openscad/issues/128
664
        f = pyopen(tmpfile)
665
    else:
666
        f = pyopen(filename)
667
    rootnode =  parsenode(f.read())[0]
668
    f.close()
669
    if isopenscad and tmpfile:
670
        try:
671
            os.unlink(tmpfile)
672
        except OSError:
673
            pass
674
    return rootnode.flattengroups()
675

676
def open(filename):
677
    import os
678
    docname = os.path.split(filename)[1]
679
    doc = FreeCAD.newDocument(docname)
680
    doc.Label = (docname.split('.',1)[0])
681
    readfile(filename).addtofreecad(doc)
682
    #doc.recompute()
683
    return doc
684

685
def insert(filename,docname):
686
    try:
687
        doc = FreeCAD.getDocument(docname)
688
    except NameError:
689
        doc = FreeCAD.newDocument(docname)
690
    readfile(filename).addtofreecad(doc)
691
    #doc.recompute()
692

693

694

695
global dxfcache
696
dxfcache = {}
697

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.