FreeCAD

Форк
0
/
importCSG.py 
1436 строк · 49.1 Кб
1
# -*- coding: utf8 -*-
2

3
#***************************************************************************
4
#*   Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk>               *
5
#*                                                                         *
6
#*   This program is free software; you can redistribute it and/or modify  *
7
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
8
#*   as published by the Free Software Foundation; either version 2 of     *
9
#*   the License, or (at your option) any later version.                   *
10
#*   for detail see the LICENCE text file.                                 *
11
#*                                                                         *
12
#*   This program is distributed in the hope that it will be useful,       *
13
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15
#*   GNU Library General Public License for more details.                  *
16
#*                                                                         *
17
#*   You should have received a copy of the GNU Library General Public     *
18
#*   License along with this program; if not, write to the Free Software   *
19
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
20
#*   USA                                                                   *
21
#*                                                                         *
22
#*   Acknowledgements:                                                     *
23
#*                                                                         *
24
#*     Thanks to shoogen on the FreeCAD forum and Peter Li                 *
25
#*     for programming advice and some code.                               *
26
#*                                                                         *
27
#*                                                                         *
28
#***************************************************************************
29
__title__ = "FreeCAD OpenSCAD Workbench - CSG importer"
30
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
31
__url__ = ["http://www.sloan-home.co.uk/ImportCSG"]
32

33
printverbose = False
34

35
import io
36
import os
37

38
import xml.sax
39

40
import FreeCAD
41
import Part
42
import Draft
43

44
from OpenSCADFeatures import *
45
from OpenSCADUtils import *
46

47

48
import ply.lex as lex
49
import ply.yacc as yacc
50

51
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
52
printverbose = params.GetBool('printVerbose', False)
53

54
if FreeCAD.GuiUp:
55
    gui = True
56
else:
57
    if printverbose: print("FreeCAD Gui not present.")
58
    gui = False
59

60
hassetcolor = []
61
alreadyhidden = []
62
original_root_objects = []
63

64
# Get the token map from the lexer. This is required.
65
import tokrules
66
from tokrules import tokens
67
from builtins import open as pyopen
68

69
translate = FreeCAD.Qt.translate
70

71

72
def shallHide(subject):
73
    for obj in subject.OutListRecursive:
74
        if "Matrix_Union" in str(obj.FullName):
75
            return False
76
        if "Extrude" in str(obj.FullName):
77
            return True
78
    return False
79

80

81
def setColorRecursively(obj, color, transp):
82
    '''
83
    For some reason a part made by cutting or fusing other parts do not have a color
84
    unless its constituents are also colored. This code sets colors for those
85
    constituents unless already set elsewhere.
86
    '''
87
    obj.ViewObject.ShapeColor = color
88
    obj.ViewObject.Transparency = transp
89
    # Add any other relevant features to this list
90
    boolean_features = ["Part::Fuse", "Part::MultiFuse", "Part::Cut",
91
                        "Part::Common", "Part::MultiCommon"]
92
    if obj.TypeId in boolean_features:
93
        for currentObject in obj.OutList:
94
            if printverbose: print(f"Fixing up colors for: {currentObject.FullName}")
95
            if currentObject not in hassetcolor:
96
                setColorRecursively(currentObject, color, transp)
97

98

99
def fixVisibility():
100
    # After an import, only the remaining root objects that we created should be visible, not any
101
    # of their individual component objects. But make sure to only handle the ones we just imported,
102
    # not anything that already existed. And objects that exist at the toplevel without any
103
    # children are ignored.
104
    for root_object in FreeCAD.ActiveDocument.RootObjects:
105
        if root_object not in original_root_objects:
106
            root_object.ViewObject.Visibility = True
107
            for obj in root_object.OutListRecursive:
108
                obj.ViewObject.Visibility = False
109

110

111
def open(filename):
112
    "called when freecad opens a file."
113
    global doc
114
    global pathName
115
    docname = os.path.splitext(os.path.basename(filename))[0]
116
    doc = FreeCAD.newDocument(docname)
117
    if filename.lower().endswith('.scad'):
118
        tmpfile = callopenscad(filename)
119
        pathName = os.path.dirname(os.path.normpath(filename))
120
        processcsg(tmpfile)
121
        try:
122
            os.unlink(tmpfile)
123
        except OSError:
124
            pass
125
    else:
126
        pathName = os.path.dirname(os.path.normpath(filename))
127
        processcsg(filename)
128
    return doc
129

130

131
def insert(filename, docname):
132
    "called when freecad imports a file"
133
    global doc
134
    global pathName
135
    groupname_unused = os.path.splitext(os.path.basename(filename))[0]
136
    try:
137
        doc = FreeCAD.getDocument(docname)
138
        for obj in doc.RootObjects:
139
            original_root_objects.append(obj)
140
    except NameError:
141
        doc = FreeCAD.newDocument(docname)
142
    #importgroup = doc.addObject("App::DocumentObjectGroup",groupname)
143
    if filename.lower().endswith('.scad'):
144
        tmpfile = callopenscad(filename)
145
        pathName = os.path.dirname(os.path.normpath(filename))
146
        processcsg(tmpfile)
147
        try:
148
            os.unlink(tmpfile)
149
        except OSError:
150
            pass
151
    else:
152
        pathName = os.path.dirname(os.path.normpath(filename))
153
        processcsg(filename)
154

155
def processcsg(filename):
156
    global doc
157

158
    if printverbose: print('ImportCSG Version 0.6a')
159
    # Build the lexer
160
    if printverbose: print('Start Lex')
161
    lex.lex(module=tokrules)
162
    if printverbose: print('End Lex')
163

164
    # Build the parser
165
    if printverbose: print('Load Parser')
166
    # Disable generation of debug ('parser.out') and table cache ('parsetab.py'),
167
    # as it requires a writable location
168
    parser = yacc.yacc(debug=False, write_tables=False)
169
    if printverbose: print('Parser Loaded')
170
    # Give the lexer some input
171
    #f=open('test.scad', 'r')
172
    f = io.open(filename, 'r', encoding="utf8")
173
    #lexer.input(f.read())
174

175
    if printverbose: print('Start Parser')
176
    # Swap statements to enable Parser debugging
177
    #result = parser.parse(f.read(),debug=1)
178
    result = parser.parse(f.read())
179
    f.close()
180
    if printverbose:
181
        print('End Parser')
182
        print(result)
183
    if gui:
184
        fixVisibility()
185
    hassetcolor.clear()
186
    alreadyhidden.clear()
187
    FreeCAD.Console.PrintMessage('End processing CSG file\n')
188
    doc.recompute()
189

190

191
def p_block_list_(p):
192
    '''
193
    block_list : statement
194
               | block_list statement
195
               | statementwithmod
196
               | block_list statementwithmod
197
    '''
198
    #if printverbose: print("Block List")
199
    #if printverbose: print(p[1])
200
    if(len(p) > 2):
201
        if printverbose: print(p[2])
202
        p[0] = p[1] + p[2]
203
    else:
204
        p[0] = p[1]
205
    #if printverbose: print("End Block List")
206

207

208
def p_render_action(p):
209
    'render_action : render LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'
210
    if printverbose: print("Render (ignored)")
211
    p[0] = p[6]
212

213

214
def p_group_action1(p):
215
    'group_action1 : group LPAREN RPAREN OBRACE block_list EBRACE'
216
    if printverbose: print("Group")
217
    # Test need for implicit fuse
218
    if p[5] is None:
219
        p[0] = []
220
        return
221
    if len(p[5]) > 1:
222
        if printverbose: print('Fuse Group')
223
        for obj in p[5]:
224
            checkObjShape(obj)
225
        p[0] = [fuse(p[5], "Group")]
226
    else:
227
        if printverbose: print(f"Group {p[5]} type {type(p[5])}")
228
        checkObjShape(p[5])
229
        p[0] = p[5]
230

231

232
def p_group_action2(p):
233
    'group_action2 : group LPAREN RPAREN SEMICOL'
234
    if printverbose: print("Group2")
235
    p[0] = []
236

237

238
def p_boolean(p):
239
    '''
240
    boolean : true
241
            | false
242
    '''
243
    p[0] = p[1]
244

245
#def p_string(p):
246
#    'string : QUOTE ID QUOTE'
247
#    p[0] = p[2]
248

249

250
def p_stripped_string(p):
251
    'stripped_string : STRING'
252
    p[0] = p[1].strip('"')
253

254

255
def p_statement(p):
256
    '''statement : part
257
                 | operation
258
                 | multmatrix_action
259
                 | group_action1
260
                 | group_action2
261
                 | color_action
262
                 | render_action
263
                 | not_supported
264
    '''
265
    p[0] = p[1]
266

267

268
def p_anymodifier(p):
269
    '''anymodifier : MODIFIERBACK
270
                   | MODIFIERDEBUG
271
                   | MODIFIERROOT
272
                   | MODIFIERDISABLE
273
    '''
274
    # just return the plain modifier for now
275
    # has to be changed when the modifiers are implemented
276
    # please note that disabled objects usually are stripped of the CSG output during compilation
277
    p[0] = p[1]
278

279

280
def p_statementwithmod(p):
281
    '''statementwithmod : anymodifier statement'''
282
    # ignore the modifiers but add them to the label
283
    modifier = p[1]
284
    obj = p[2]
285
    if hasattr(obj, 'Label'):
286
        obj.Label = modifier + obj.Label
287
    p[0] = obj
288

289

290
def p_part(p):
291
    '''
292
    part : sphere_action
293
         | cylinder_action
294
         | cube_action
295
         | circle_action
296
         | square_action
297
         | text_action
298
         | polygon_action_nopath
299
         | polygon_action_plus_path
300
         | polyhedron_action
301
         '''
302
    p[0] = p[1]
303

304

305
def p_2d_point(p):
306
    '2d_point : OSQUARE NUMBER COMMA NUMBER ESQUARE'
307
    global points_list
308
    if printverbose: print("2d Point")
309
    p[0] = [float(p[2]), float(p[4])]
310

311

312
def p_points_list_2d(p):
313
    '''
314
    points_list_2d : 2d_point COMMA
315
                   | points_list_2d 2d_point COMMA
316
                   | points_list_2d 2d_point
317
                   '''
318
    if p[2] == ',':
319
        #if printverbose:
320
        #    print("Start List")
321
        #    print(p[1])
322
        p[0] = [p[1]]
323
    else:
324
        if printverbose:
325
            print(p[1])
326
            print(p[2])
327
        p[1].append(p[2])
328
        p[0] = p[1]
329
    #if printverbose: print(p[0])
330

331

332
def p_3d_point(p):
333
    '3d_point : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER ESQUARE'
334
    global points_list
335
    if printverbose: print("3d point")
336
    p[0] = [p[2], p[4], p[6]]
337

338

339
def p_points_list_3d(p):
340
    '''
341
    points_list_3d : 3d_point COMMA
342
               | points_list_3d 3d_point COMMA
343
               | points_list_3d 3d_point
344
               '''
345
    if p[2] == ',':
346
        if printverbose: print("Start List")
347
        if printverbose: print(p[1])
348
        p[0] = [p[1]]
349
    else:
350
        if printverbose: print(p[1])
351
        if printverbose: print(p[2])
352
        p[1].append(p[2])
353
        p[0] = p[1]
354
    if printverbose: print(p[0])
355

356
def p_path_points(p):
357
    '''
358
    path_points : NUMBER COMMA
359
                | path_points NUMBER COMMA
360
                | path_points NUMBER
361
                '''
362
    #if printverbose: print("Path point")
363
    if p[2] == ',':
364
        #if printverbose: print('Start list')
365
        #if printverbose: print(p[1])
366
        p[0] = [int(p[1])]
367
    else:
368
        #if printverbose: print(p[1])
369
        #if printverbose: print(len(p[1]))
370
        #if printverbose: print(p[2])
371
        p[1].append(int(p[2]))
372
        p[0] = p[1]
373
    #if printverbose: print(p[0])
374

375

376
def p_path_list(p):
377
    'path_list : OSQUARE path_points ESQUARE'
378
    #if printverbose: print('Path List ')
379
    #if printverbose: print(p[2])
380
    p[0] = p[2]
381

382

383
def p_path_set(p):
384
    '''
385
    path_set : path_list
386
             | path_set COMMA path_list
387
             '''
388
    #if printverbose: print('Path Set')
389
    #if printverbose: print(len(p))
390
    if len(p) == 2:
391
        p[0] = [p[1]]
392
    else:
393
        p[1].append(p[3])
394
        p[0] = p[1]
395
    #if printverbose: print(p[0])
396

397
def p_operation(p):
398
    '''
399
    operation : difference_action
400
              | intersection_action
401
              | union_action
402
              | rotate_extrude_action
403
              | linear_extrude_with_transform
404
              | rotate_extrude_file
405
              | import_file1
406
              | resize_action
407
              | surface_action
408
              | projection_action
409
              | hull_action
410
              | minkowski_action
411
              | offset_action
412
              '''
413
    p[0] = p[1]
414

415
def placeholder(name, children, arguments):
416
    from OpenSCADFeatures import OpenSCADPlaceholder
417
    newobj=doc.addObject("Part::FeaturePython",name)
418
    OpenSCADPlaceholder(newobj, children, str(arguments))
419
    if gui:
420
        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
421
            GetBool('useViewProviderTree'):
422
            from OpenSCADFeatures import ViewProviderTree
423
            ViewProviderTree(newobj.ViewObject)
424
        else:
425
            newobj.ViewObject.Proxy = 0
426
    #don't hide the children
427
    return newobj
428

429
def CGALFeatureObj(name, children,arguments=[]):
430
    myobj=doc.addObject("Part::FeaturePython", name)
431
    CGALFeature(myobj, name, children, str(arguments))
432
    if gui:
433
        for subobj in children:
434
            subobj.ViewObject.hide()
435
        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
436
            GetBool('useViewProviderTree'):
437
            from OpenSCADFeatures import ViewProviderTree
438
            ViewProviderTree(myobj.ViewObject)
439
        else:
440
            myobj.ViewObject.Proxy = 0
441
    return myobj
442

443
def p_offset_action(p):
444
    'offset_action : offset LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'
445
    subobj = None
446
    if len(p[6]) == 0:
447
        newobj = placeholder('group',[],'{}')
448
    elif (len(p[6]) == 1 ): #single object
449
        subobj = p[6][0]
450
    else:
451
        subobj = fuse(p[6],"Offset Union")
452
    if 'r' in p[3]:
453
        offset = float(p[3]['r'])
454
    if 'delta' in p[3]:
455
        offset = float(p[3]['delta'])
456
    checkObjShape(subobj)
457
    if subobj.Shape.Volume == 0 :
458
        newobj = doc.addObject("Part::Offset2D",'Offset2D')
459
        newobj.Source = subobj
460
        newobj.Value = offset
461
        if 'r' in p[3]:
462
            newobj.Join = 0
463
        else:
464
            newobj.Join = 2
465
    else:
466
        newobj = doc.addObject("Part::Offset",'offset')
467
        newobj.Shape = subobj[0].Shape.makeOffset(offset)
468
    newobj.Document.recompute()
469
    if gui:
470
        subobj.ViewObject.hide()
471
#        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
472
#            GetBool('useViewProviderTree'):
473
#            from OpenSCADFeatures import ViewProviderTree
474
#            ViewProviderTree(newobj.ViewObject)
475
#        else:
476
#            newobj.ViewObject.Proxy = 0
477
    p[0] = [newobj]
478

479
def checkObjShape(obj):
480
    if printverbose: print('Check Object Shape')
481
    if hasattr(obj, 'Shape'):
482
        if obj.Shape.isNull():
483
            if printverbose: print('Shape is Null - recompute')
484
            obj.recompute()
485
        if obj.Shape.isNull():
486
            print(f'Recompute failed : {obj.Name}')
487
    else:
488
        if hasattr(obj, 'Name'):
489
            print(f"obj {obj.Name} has no Shape")
490
        else:
491
            print(f"obj {obj} has no Name & Shape")
492

493
def p_hull_action(p):
494
    'hull_action : hull LPAREN RPAREN OBRACE block_list EBRACE'
495
    p[0] = [ CGALFeatureObj(p[1],p[5]) ]
496

497
def p_minkowski_action(p):
498
    '''
499
    minkowski_action : minkowski LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'''
500
    p[0] = [ CGALFeatureObj(p[1],p[6],p[3]) ]
501

502
def p_resize_action(p):
503
    '''
504
    resize_action : resize LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE '''
505
    new_size = p[3]['newsize']
506
    auto     = p[3]['auto']
507
    p[6][0].recompute()
508
    if p[6][0].Shape.isNull():
509
        doc.recompute()
510
    p[6][0].Shape.tessellate(0.05)
511
    old_bbox = p[6][0].Shape.BoundBox
512
    old_size = [old_bbox.XLength, old_bbox.YLength, old_bbox.ZLength]
513
    for r in range(0,3):
514
        if auto[r] == '1':
515
            new_size[r] = new_size[0]
516
        if new_size[r] == '0':
517
            new_size[r] = str(old_size[r])
518

519
    # Calculate a transform matrix from the current bounding box to the new one:
520
    transform_matrix = FreeCAD.Matrix()
521

522
    scale = FreeCAD.Vector(float(new_size[0])/old_size[0],
523
                           float(new_size[1])/old_size[1],
524
                           float(new_size[2])/old_size[2])
525

526
    transform_matrix.scale(scale)
527

528
    new_part=doc.addObject("Part::FeaturePython",'Matrix Deformation')
529
    new_part.Shape = p[6][0].Shape.transformGeometry(transform_matrix)
530
    if gui:
531
        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
532
            GetBool('useViewProviderTree'):
533
            from OpenSCADFeatures import ViewProviderTree
534
            ViewProviderTree(new_part.ViewObject)
535
        else:
536
            new_part.ViewObject.Proxy = 0
537
        p[6][0].ViewObject.hide()
538
    p[0] = [new_part]
539

540

541
def p_not_supported(p):
542
    '''
543
    not_supported : glide LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE
544
                  | subdiv LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE
545
                  '''
546
    if gui and not FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
547
            GetBool('usePlaceholderForUnsupported'):
548
        from PySide import QtGui
549
        QtGui.QMessageBox.critical(None, translate('OpenSCAD',"Unsupported Function")+" : "+p[1],translate('OpenSCAD',"Press OK"))
550
    else:
551
        p[0] = [placeholder(p[1],p[6],p[3])]
552

553
def p_size_vector(p):
554
    'size_vector : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER ESQUARE'
555
    if printverbose: print("size vector")
556
    p[0] = [p[2],p[4],p[6]]
557

558
def p_keywordargument(p):
559
    '''keywordargument : ID EQ boolean
560
    | ID EQ NUMBER
561
    | ID EQ size_vector
562
    | ID EQ vector
563
    | ID EQ 2d_point
564
    | text EQ stripped_string
565
    | ID EQ stripped_string
566
     '''
567
    p[0] = (p[1],p[3])
568
    if printverbose: print(p[0])
569

570
def p_keywordargument_list(p):
571
    '''
572
    keywordargument_list : keywordargument
573
               | keywordargument_list COMMA keywordargument
574
    '''
575
    if len(p) == 2:
576
        p[0] = {p[1][0] : p[1][1]}
577
    else:
578
        p[1][p[3][0]] = p[3][1]
579
        p[0]=p[1]
580

581
def p_color_action(p):
582
    'color_action : color LPAREN vector RPAREN OBRACE block_list EBRACE'
583
    import math
584
    if printverbose: print("Color")
585
    color = tuple([float(f) for f in p[3][:3]]) #RGB
586
    transp = 100 - int(math.floor(100*float(p[3][3]))) #Alpha
587
    if gui:
588
        for obj in p[6]:
589
            if shallHide(obj):
590
                if "Group" in obj.FullName:
591
                    obj.ViewObject.Visibility=False
592
                    alreadyhidden.append(obj)
593
            setColorRecursively(obj, color, transp)
594
            hassetcolor.append(obj)
595
    p[0] = p[6]
596

597
# Error rule for syntax errors
598
def p_error(p):
599
    if printverbose: print("Syntax error in input!")
600
    if printverbose: print(p)
601

602
def fuse(lst,name):
603
    global doc
604
    if printverbose: print("Fuse")
605
    if printverbose: print(lst)
606
    if len(lst) == 0:
607
        myfuse = placeholder('group',[],'{}')
608
    elif len(lst) == 1:
609
        return lst[0]
610
    # Is this Multi Fuse
611
    elif len(lst) > 2:
612
        if printverbose: print("Multi Fuse")
613
        myfuse = doc.addObject('Part::MultiFuse',name)
614
        myfuse.Shapes = lst
615
        if gui:
616
            for subobj in myfuse.Shapes:
617
                subobj.ViewObject.hide()
618
    else:
619
        if printverbose: print("Single Fuse")
620
        myfuse = doc.addObject('Part::Fuse',name)
621
        myfuse.Base = lst[0]
622
        myfuse.Tool = lst[1]
623
        checkObjShape(myfuse.Base)
624
        checkObjShape(myfuse.Tool)
625
        myfuse.Shape = myfuse.Base.Shape.fuse(myfuse.Tool.Shape)
626
        if gui:
627
            myfuse.Base.ViewObject.hide()
628
            myfuse.Tool.ViewObject.hide()
629
    myfuse.Placement = FreeCAD.Placement()
630
    return myfuse
631

632
def p_empty_union_action(p):
633
    'union_action : union LPAREN RPAREN SEMICOL'
634
    if printverbose: print("empty union")
635
    newpart = fuse([],p[1])
636
    if printverbose: print("Push Union Result")
637
    p[0] = [newpart]
638
    if printverbose: print("End Union")
639

640
def p_union_action(p):
641
    'union_action : union LPAREN RPAREN OBRACE block_list EBRACE'
642
    if printverbose: print("union")
643
    newpart = fuse(p[5],p[1])
644
    if printverbose: print("Push Union Result")
645
    p[0] = [newpart]
646
    if printverbose: print("End Union")
647

648
def p_difference_action(p):
649
    'difference_action : difference LPAREN RPAREN OBRACE block_list EBRACE'
650

651
    if printverbose: print("difference")
652
    if printverbose: print(len(p[5]))
653
    if printverbose: print(p[5])
654
    if (len(p[5]) == 0 ): #nochild
655
        mycut_unused = placeholder('group',[],'{}')
656
    elif (len(p[5]) == 1 ): #single object
657
        p[0] = p[5]
658
    else:
659
# Cut using Fuse
660
        mycut = doc.addObject('Part::Cut',p[1])
661
        mycut.Base = p[5][0]
662
#       Can only Cut two objects do we need to fuse extras
663
        if (len(p[5]) > 2 ):
664
            if printverbose: print("Need to Fuse Extra First")
665
            mycut.Tool = fuse(p[5][1:],'union')
666
        else :
667
            mycut.Tool = p[5][1]
668
            checkObjShape(mycut.Tool)
669
        if gui:
670
            mycut.Base.ViewObject.hide()
671
            mycut.Tool.ViewObject.hide()
672
        if printverbose: print("Push Resulting Cut")
673
        p[0] = [mycut]
674
    if printverbose: print("End Cut")
675

676
def p_intersection_action(p):
677
    'intersection_action : intersection LPAREN RPAREN OBRACE block_list EBRACE'
678

679
    if printverbose: print("intersection")
680
    # Is this Multi Common
681
    if (len(p[5]) > 2):
682
        if printverbose: print("Multi Common")
683
        mycommon = doc.addObject('Part::MultiCommon',p[1])
684
        mycommon.Shapes = p[5]
685
        if gui:
686
            for subobj in mycommon.Shapes:
687
                subobj.ViewObject.hide()
688
    elif (len(p[5]) == 2):
689
        if printverbose: print("Single Common")
690
        mycommon = doc.addObject('Part::Common',p[1])
691
        mycommon.Base = p[5][0]
692
        mycommon.Tool = p[5][1]
693
        checkObjShape(mycommon.Base)
694
        checkObjShape(mycommon.Tool)
695
        if gui:
696
            mycommon.Base.ViewObject.hide()
697
            mycommon.Tool.ViewObject.hide()
698
    elif (len(p[5]) == 1):
699
        mycommon = p[5][0]
700
    else : # 1 child
701
        mycommon = placeholder('group',[],'{}')
702
    mycommon.Shape = mycommon.Base.Shape.common(mycommon.Tool.Shape)
703
    p[0] = [mycommon]
704
    if printverbose: print("End Intersection")
705

706
def process_rotate_extrude(obj, angle):
707
    newobj=doc.addObject("Part::FeaturePython",'RefineRotateExtrude')
708
    RefineShape(newobj,obj)
709
    if gui:
710
        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
711
            GetBool('useViewProviderTree'):
712
            from OpenSCADFeatures import ViewProviderTree
713
            ViewProviderTree(newobj.ViewObject)
714
        else:
715
            newobj.ViewObject.Proxy = 0
716
        obj.ViewObject.hide()
717
    myrev = doc.addObject("Part::Revolution","RotateExtrude")
718
    myrev.Source = newobj
719
    myrev.Axis = (0.00,1.00,0.00)
720
    myrev.Base = (0.00,0.00,0.00)
721
    myrev.Angle = angle
722
    myrev.Placement = FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90))
723
    if gui:
724
        newobj.ViewObject.hide()
725
    return myrev
726

727
def process_rotate_extrude_prism(obj, angle, n):
728
    newobj=doc.addObject("Part::FeaturePython",'PrismaticToroid')
729
    PrismaticToroid(newobj, obj, angle, n)
730
    newobj.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90))
731
    if gui:
732
        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
733
            GetBool('useViewProviderTree'):
734
            from OpenSCADFeatures import ViewProviderTree
735
            ViewProviderTree(newobj.ViewObject)
736
        else:
737
            newobj.ViewObject.Proxy = 0
738
        obj.ViewObject.hide()
739
    return newobj
740

741
def p_rotate_extrude_action(p):
742
    'rotate_extrude_action : rotate_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'
743
    if printverbose: print("Rotate Extrude")
744
    angle = 360.0
745
    if 'angle' in p[3]:
746
        angle = float(p[3]['angle'])
747
    n = int(round(float(p[3]['$fn'])))
748
    fnmax = FreeCAD.ParamGet(\
749
        "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
750
        GetInt('useMaxFN', 16)
751
    if (len(p[6]) > 1) :
752
        part = fuse(p[6],"Rotate Extrude Union")
753
    else :
754
        part = p[6][0]
755

756
    if n < 3 or fnmax != 0 and n > fnmax:
757
        p[0] = [process_rotate_extrude(part,angle)]
758
    else:
759
        p[0] = [process_rotate_extrude_prism(part,angle,n)]
760
    if printverbose: print("End Rotate Extrude")
761

762
def p_rotate_extrude_file(p):
763
    'rotate_extrude_file : rotate_extrude LPAREN keywordargument_list RPAREN SEMICOL'
764
    if printverbose: print("Rotate Extrude File")
765
    angle = 360.0
766
    if 'angle' in p[3]:
767
        angle = float(p[3]['angle'])
768
    filen,ext = p[3]['file'] .rsplit('.',1)
769
    obj = process_import_file(filen,ext,p[3]['layer'])
770
    n = int(round(float(p[3]['$fn'])))
771
    fnmax = FreeCAD.ParamGet(\
772
        "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
773
        GetInt('useMaxFN', 16)
774

775
    if n < 3 or fnmax != 0 and n > fnmax:
776
        p[0] = [process_rotate_extrude(obj,angle)]
777
    else:
778
        p[0] = [process_rotate_extrude_prism(obj,angle,n)]
779
    if printverbose: print("End Rotate Extrude File")
780

781
def process_linear_extrude(obj,h) :
782
    #if gui:
783
    newobj = doc.addObject("Part::FeaturePython",'RefineLinearExtrude')
784
    RefineShape(newobj,obj)#mylinear)
785
    if gui:
786
        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
787
            GetBool('useViewProviderTree'):
788
            from OpenSCADFeatures import ViewProviderTree
789
            ViewProviderTree(newobj.ViewObject)
790
        else:
791
            newobj.ViewObject.Proxy = 0
792
        obj.ViewObject.hide()
793
        #mylinear.ViewObject.hide()
794
    mylinear = doc.addObject("Part::Extrusion","LinearExtrude")
795
    mylinear.Base = newobj #obj
796
    mylinear.Dir = (0,0,h)
797
    mylinear.Placement=FreeCAD.Placement()
798
    # V17 change to False mylinear.Solid = True
799
    mylinear.Solid = False
800
    if gui:
801
        newobj.ViewObject.hide()
802
    return mylinear
803

804
def process_linear_extrude_with_transform(base,height,twist,scale) :
805
    newobj = doc.addObject("Part::FeaturePython",'transform_extrude')
806
    # base is a FreeCAD Object, height & twist are floats, scale is a two-component vector of floats
807
    Twist(newobj,base,height,-twist,scale)
808
    if gui:
809
        if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
810
            GetBool('useViewProviderTree'):
811
            from OpenSCADFeatures import ViewProviderTree
812
            ViewProviderTree(newobj.ViewObject)
813
        else:
814
            newobj.ViewObject.Proxy = 0
815
    #import ViewProviderTree from OpenSCADFeatures
816
    #ViewProviderTree(obj.ViewObject)
817
    return newobj
818

819
def p_linear_extrude_with_transform(p):
820
    'linear_extrude_with_transform : linear_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'
821
    if printverbose: print("Linear Extrude With Transform")
822
    h = float(p[3]['height'])
823
    if printverbose: print("Height : ",h)
824
    s = [1.0,1.0]
825
    t = 0.0
826
    if 'scale' in p[3]:
827
        if isinstance(p[3]['scale'], str):
828
            s = [float(p[3]['scale']), float(p[3]['scale'])]
829
        else:
830
            s = [float(p[3]['scale'][0]), float(p[3]['scale'][1])]
831
        if printverbose: print ("Scale: " + str(s))
832
    if 'twist' in p[3]:
833
        t = float(p[3]['twist'])
834
        if printverbose: print("Twist : ",t)
835
    # Test if null object like from null text
836
    if (len(p[6]) == 0) :
837
        p[0] = []
838
        return
839
    if (len(p[6]) > 1) :
840
        obj = fuse(p[6],"Linear Extrude Union")
841
    else :
842
        obj = p[6][0]
843
    checkObjShape(obj)
844
    if t != 0.0 or s[0] != 1.0 or s[1] != 1.0:
845
        newobj = process_linear_extrude_with_transform(obj,h,t,s)
846
    else:
847
        newobj = process_linear_extrude(obj,h)
848
    if p[3].get('center','false')=='true' :
849
        center(newobj,0,0,h)
850
    p[0] = [newobj]
851
    if gui:
852
        obj.ViewObject.hide()
853
    if printverbose: print("End Linear Extrude with Transform")
854

855
def p_import_file1(p):
856
    'import_file1 : import LPAREN keywordargument_list RPAREN SEMICOL'
857
    if printverbose: print("Import File")
858
    filen,ext = p[3]['file'].rsplit('.',1)
859
    p[0] = [process_import_file(filen,ext,p[3]['layer'])]
860
    if printverbose: print("End Import File")
861

862
def p_surface_action(p):
863
    'surface_action : surface LPAREN keywordargument_list RPAREN SEMICOL'
864
    if printverbose: print("Surface")
865
    obj = doc.addObject("Part::Feature",'surface')
866
    obj.Shape,xoff,yoff=makeSurfaceVolume(p[3]['file'])
867
    if p[3].get('center','false') == 'true' :
868
        center(obj,xoff,yoff,0.0)
869
    p[0] = [obj]
870
    if printverbose: print("End surface")
871

872
def process_import_file(fname,ext,layer):
873
    if printverbose: print("Importing : "+fname+"."+ext+" Layer : "+layer)
874
    if ext.lower() in reverseimporttypes()['Mesh']:
875
        obj = process_mesh_file(fname,ext)
876
    elif ext.lower() == 'dxf' :
877
        obj = processDXF(fname,layer)
878
    elif ext.lower() == 'svg':
879
        obj = processSVG(fname, ext)
880
    else:
881
        raise ValueError("Unsupported file extension %s" % ext)
882
    return obj
883

884
def processSVG(fname, ext):
885
    from importSVG import svgHandler
886
    if printverbose: print("SVG Handler")
887
    doc = FreeCAD.ActiveDocument
888
    docSVG = FreeCAD.newDocument(fname+'_tmp')
889
    FreeCAD.ActiveDocument = docSVG
890

891
    # Set up the parser
892
    parser = xml.sax.make_parser()
893
    parser.setFeature(xml.sax.handler.feature_external_ges, False)
894
    parser.setContentHandler(svgHandler())
895
    parser._cont_handler.doc = docSVG
896

897
    # pathName is a Global
898
    filename = os.path.join(pathName,fname+'.'+ext)
899
    # Use the native Python open which was saved as `pyopen`
900
    parser.parse(pyopen(filename))
901

902
    #combine SVG objects into one
903
    shapes = []
904
    for obj in FreeCAD.ActiveDocument.Objects:
905
        if printverbose: print(obj.Name)
906
        if printverbose: print(obj.Shape)
907
        shapes.append(obj.Shape)
908
    #compoundSVG = Part.makeCompound(shapes)
909
    #compoundSVG = Draft.join(objects)
910
    FreeCAD.closeDocument(docSVG.Name)
911
    FreeCAD.ActiveDocument=doc
912
    obj=doc.addObject('Part::Feature',fname)
913
    obj.Shape=Part.Compound(shapes)
914
    return obj
915

916
def process_mesh_file(fname,ext):
917
    import Mesh
918
    import Part
919
    fullname = fname+'.'+ext
920
    filename = os.path.join(pathName,fullname)
921
    objname = os.path.split(fname)[1]
922
    mesh1 = doc.getObject(objname) #reuse imported object
923
    if not mesh1:
924
        Mesh.insert(filename)
925
        mesh1 = doc.getObject(objname)
926
    if mesh1 is not None:
927
        if gui:
928
            mesh1.ViewObject.hide()
929
        sh = Part.Shape()
930
        sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1)
931
        solid = Part.Solid(sh)
932
        obj = doc.addObject('Part::Feature',"Mesh")
933
        #ImportObject(obj,mesh1) #This object is not mutable from the GUI
934
        #ViewProviderTree(obj.ViewObject)
935
        solid = solid.removeSplitter()
936
        if solid.Volume < 0:
937
            #sh.reverse()
938
            #sh = sh.copy()
939
            solid.complement()
940
        obj.Shape = solid#.removeSplitter()
941
    else: #mesh1 is None
942
        FreeCAD.Console.PrintError('Mesh not imported %s.%s %s\n' % \
943
                (objname,ext,filename))
944
        import Part
945
        obj = doc.addObject('Part::Feature',"FailedMeshImport")
946
        obj.Shape = Part.Compound([])
947
    return obj
948

949

950
def processTextCmd(t):
951
    from OpenSCADUtils import callopenscadstring
952
    tmpfilename = callopenscadstring(t,'dxf')
953
    from OpenSCAD2Dgeom import importDXFface
954
    face = importDXFface(tmpfilename,None,None)
955
    obj = doc.addObject('Part::Feature','text')
956
    obj.Shape = face
957
    try:
958
        os.unlink(tmpfilename)
959
    except OSError:
960
        pass
961
    return obj
962

963
def processDXF(fname,layer):
964
    global doc
965
    global pathName
966
    from OpenSCAD2Dgeom import importDXFface
967
    if printverbose: print("Process DXF file")
968
    if printverbose: print("File Name : "+fname)
969
    if printverbose: print("Layer : "+layer)
970
    if printverbose: print("PathName : "+pathName)
971
    dxfname = fname+'.dxf'
972
    filename = os.path.join(pathName,dxfname)
973
    shortname = os.path.split(fname)[1]
974
    if printverbose: print("DXF Full path : "+filename)
975
    face = importDXFface(filename,layer,doc)
976
    obj=doc.addObject('Part::Feature','dxf_%s_%s' % (shortname,layer or "all"))
977
    obj.Shape=face
978
    if printverbose: print("DXF Diagnostics")
979
    if printverbose: print(obj.Shape.ShapeType)
980
    if printverbose: print("Closed : "+str(obj.Shape.isClosed()))
981
    if printverbose: print(obj.Shape.check())
982
    if printverbose: print([w.isClosed() for w in obj.Shape.Wires])
983
    return obj
984

985
def processSTL(fname):
986
    if printverbose: print("Process STL file")
987

988
def p_multmatrix_action(p):
989
    'multmatrix_action : multmatrix LPAREN matrix RPAREN OBRACE block_list EBRACE'
990
    if printverbose: print("MultMatrix")
991
    transform_matrix = FreeCAD.Matrix()
992
    if printverbose: print("Multmatrix")
993
    if printverbose: print(p[3])
994
    if gui and p[6]:
995
        parentcolor=p[6][0].ViewObject.ShapeColor
996
        parenttransparency=p[6][0].ViewObject.Transparency
997

998
    m1l=sum(p[3],[])
999
    if any('x' in me for me in m1l): #hexfloats
1000
        m1l=[float.fromhex(me) for me in m1l]
1001
        matrixisrounded=False
1002
    elif max((len(me) for me in m1l)) >= 14: #might have double precision
1003
        m1l=[float(me) for me in m1l] # assume precise output
1004
        m1l=[(0 if (abs(me) < 1e-15) else me) for me in m1l]
1005
        matrixisrounded=False
1006
    else: #trucanted numbers
1007
        m1l=[round(float(me),12) for me in m1l] #round
1008
        matrixisrounded=True
1009
    transform_matrix = FreeCAD.Matrix(*tuple(m1l))
1010
    if printverbose: print(transform_matrix)
1011
    if printverbose: print("Apply Multmatrix")
1012
#   If more than one object on the stack for multmatrix fuse first
1013
    if (len(p[6]) == 0) :
1014
        part = placeholder('group',[],'{}')
1015
    elif (len(p[6]) > 1) :
1016
        part = fuse(p[6],"Matrix Union")
1017
    else :
1018
        part = p[6][0]
1019
    if (isspecialorthogonalpython(fcsubmatrix(transform_matrix))) :
1020
        if printverbose: print("special orthogonal")
1021
        if matrixisrounded:
1022
            if printverbose: print("rotation rounded")
1023
            plm=FreeCAD.Placement(transform_matrix)
1024
            plm=FreeCAD.Placement(plm.Base,roundrotation(plm.Rotation))
1025
            part.Placement=plm.multiply(part.Placement)
1026
        else:
1027
            part.Placement=FreeCAD.Placement(transform_matrix).multiply(\
1028
                    part.Placement)
1029
        new_part = part
1030
    elif isrotoinversionpython(fcsubmatrix(transform_matrix)):
1031
        if printverbose: print("orthogonal and inversion")
1032
        cmat,axisvec = decomposerotoinversion(transform_matrix)
1033
        new_part=doc.addObject("Part::Mirroring",'mirr_%s'%part.Name)
1034
        new_part.Source=part
1035
        new_part.Normal=axisvec
1036
        if matrixisrounded:
1037
            if printverbose: print("rotation rounded")
1038
            plm=FreeCAD.Placement(cmat)
1039
            new_part.Placement=FreeCAD.Placement(plm.Base,roundrotation(plm.Rotation))
1040
        else:
1041
            new_part.Placement=FreeCAD.Placement(cmat)
1042
        new_part.Label="mirrored %s" % part.Label
1043
        if gui:
1044
            part.ViewObject.hide()
1045
    elif FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
1046
        GetBool('useMultmatrixFeature'):
1047
        from OpenSCADFeatures import MatrixTransform
1048
        new_part=doc.addObject("Part::FeaturePython",'Matrix Deformation')
1049
        MatrixTransform(new_part,transform_matrix,part)
1050
        if gui:
1051
            if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
1052
                GetBool('useViewProviderTree'):
1053
                from OpenSCADFeatures import ViewProviderTree
1054
                ViewProviderTree(new_part.ViewObject)
1055
            else:
1056
                new_part.ViewObject.Proxy = 0
1057
            part.ViewObject.hide()
1058
    else :
1059
        if printverbose: print("Transform Geometry")
1060
        part.recompute()
1061
        if part.Shape.isNull():
1062
            doc.recompute()
1063
        new_part = doc.addObject("Part::Feature","Matrix Deformation")
1064
        new_part.Shape = part.Shape.transformGeometry(transform_matrix)
1065
        if gui:
1066
            part.ViewObject.hide()
1067
    if False :
1068
#   Does not fix problemfile or beltTighener although later is closer
1069
        newobj=doc.addObject("Part::FeaturePython",'RefineMultMatrix')
1070
        RefineShape(newobj,new_part)
1071
        if gui:
1072
            newobj.ViewObject.Proxy = 0
1073
            new_part.ViewObject.hide()
1074
        p[0] = [newobj]
1075
    else :
1076
        p[0] = [new_part]
1077
    if gui and p[6]:
1078
        new_part.ViewObject.ShapeColor=parentcolor
1079
        new_part.ViewObject.Transparency = parenttransparency
1080
    if printverbose: print("Multmatrix applied")
1081

1082
def p_matrix(p):
1083
    'matrix : OSQUARE vector COMMA vector COMMA vector COMMA vector ESQUARE'
1084
    if printverbose: print("Matrix")
1085
    p[0] = [p[2],p[4],p[6],p[8]]
1086

1087
def p_vector(p):
1088
    'vector : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER ESQUARE'
1089
    if printverbose: print("Vector")
1090
    p[0] = [p[2],p[4],p[6],p[8]]
1091

1092
def center(obj,x,y,z):
1093
    obj.Placement = FreeCAD.Placement(\
1094
        FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\
1095
        FreeCAD.Rotation(0,0,0,1))
1096

1097
def p_sphere_action(p):
1098
    'sphere_action : sphere LPAREN keywordargument_list RPAREN SEMICOL'
1099
    if printverbose: print("Sphere : ",p[3])
1100
    r = float(p[3]['r'])
1101
    mysphere = doc.addObject("Part::Sphere",p[1])
1102
    mysphere.Radius = r
1103
    if printverbose: print("Push Sphere")
1104
    p[0] = [mysphere]
1105
    if printverbose: print("End Sphere")
1106

1107
def myPolygon(n,r1):
1108
    # Adapted from Draft::_Polygon
1109
    import math
1110
    if printverbose: print("My Polygon")
1111
    angle = math.pi*2/n
1112
    nodes = [FreeCAD.Vector(r1,0,0)]
1113
    for i in range(n-1) :
1114
        th = (i+1) * angle
1115
        nodes.append(FreeCAD.Vector(r1*math.cos(th),r1*math.sin(th),0))
1116
    nodes.append(nodes[0])
1117
    polygonwire = Part.makePolygon(nodes)
1118

1119
    polygon = doc.addObject("Part::Feature","Polygon")
1120
    polygon.Shape = Part.Face(polygonwire)
1121
    return polygon
1122

1123
def p_cylinder_action(p):
1124
    'cylinder_action : cylinder LPAREN keywordargument_list RPAREN SEMICOL'
1125
    if printverbose: print("Cylinder")
1126
    tocenter = p[3].get('center','false')
1127
    h = float(p[3]['h'])
1128
    r1 = float(p[3]['r1'])
1129
    r2 = float(p[3]['r2'])
1130
    #n = int(p[3]['$fn'])
1131
    n = int(round(float(p[3]['$fn'])))
1132
    fnmax = FreeCAD.ParamGet(\
1133
        "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
1134
        GetInt('useMaxFN', 16)
1135
    if printverbose: print(p[3])
1136
    if h > 0:
1137
        if ( r1 == r2 and r1 > 0):
1138
            if printverbose: print("Make Cylinder")
1139
            if n < 3 or fnmax != 0 and n > fnmax:
1140
                mycyl=doc.addObject("Part::Cylinder",p[1])
1141
                mycyl.Height = h
1142
                mycyl.Radius = r1
1143
            else :
1144
                if printverbose: print("Make Prism")
1145
                if False: #user Draft Polygon
1146
                    mycyl=doc.addObject("Part::Extrusion","prism")
1147
                    mycyl.Dir = (0,0,h)
1148
                    try :
1149
                        import Draft
1150
                        mycyl.Base = Draft.makePolygon(n,r1,face=True)
1151
                    except Exception:
1152
                        # If Draft can't import (probably due to lack of Pivy on Mac and
1153
                        # Linux builds of FreeCAD), this is a fallback.
1154
                        # or old level of FreeCAD
1155
                        if printverbose:
1156
                            print("Draft makePolygon Failed, falling back on manual polygon")
1157
                        mycyl.Base = myPolygon(n,r1)
1158
                        # mycyl.Solid = True
1159

1160
                    else :
1161
                        pass
1162
                    if gui:
1163
                        mycyl.Base.ViewObject.hide()
1164
                else: #Use Part::Prism primitive
1165
                    mycyl=doc.addObject("Part::Prism","prism")
1166
                    mycyl.Polygon = n
1167
                    mycyl.Circumradius  = r1
1168
                    mycyl.Height  = h
1169

1170
        elif (r1 != r2):
1171
            if n < 3 or fnmax != 0 and n > fnmax:
1172
                if printverbose: print("Make Cone")
1173
                mycyl=doc.addObject("Part::Cone",p[1])
1174
                mycyl.Height = h
1175
                mycyl.Radius1 = r1
1176
                mycyl.Radius2 = r2
1177
            else:
1178
                if printverbose: print("Make Frustum")
1179
                mycyl=doc.addObject("Part::FeaturePython",'frustum')
1180
                Frustum(mycyl,r1,r2,n,h)
1181
                if gui:
1182
                    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
1183
                        GetBool('useViewProviderTree'):
1184
                        from OpenSCADFeatures import ViewProviderTree
1185
                        ViewProviderTree(mycyl.ViewObject)
1186
                    else:
1187
                        mycyl.ViewObject.Proxy = 0
1188
        else: # r1 == r2 == 0
1189
            FreeCAD.Console.PrintWarning('cylinder with radius zero\n')
1190
            mycyl=doc.addObject("Part::Feature","emptycyl")
1191
            mycyl.Shape = Part.Compound([])
1192
    else: # h == 0
1193
        FreeCAD.Console.PrintWarning('cylinder with height <= zero\n')
1194
        mycyl=doc.addObject("Part::Feature","emptycyl")
1195
        mycyl.Shape = Part.Compound([])
1196
    if printverbose: print("Center = ",tocenter)
1197
    if tocenter=='true' :
1198
        center(mycyl,0,0,h)
1199
    if False :
1200
#   Does not fix problemfile or beltTighener although later is closer
1201
        newobj=doc.addObject("Part::FeaturePython",'RefineCylinder')
1202
        RefineShape(newobj,mycyl)
1203
        if gui:
1204
            if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
1205
                GetBool('useViewProviderTree'):
1206
                from OpenSCADFeatures import ViewProviderTree
1207
                ViewProviderTree(newobj.ViewObject)
1208
            else:
1209
                newobj.ViewObject.Proxy = 0
1210
            mycyl.ViewObject.hide()
1211
        p[0] = [newobj]
1212
    else :
1213
        p[0] = [mycyl]
1214
    if printverbose: print("End Cylinder")
1215

1216
def p_cube_action(p):
1217
    'cube_action : cube LPAREN keywordargument_list RPAREN SEMICOL'
1218
    global doc
1219
    l,w,h = [float(str1) for str1 in p[3]['size']]
1220
    if (l > 0 and w > 0 and h >0):
1221
        if printverbose: print("cube : ",p[3])
1222
        mycube=doc.addObject('Part::Box',p[1])
1223
        mycube.Length=l
1224
        mycube.Width=w
1225
        mycube.Height=h
1226
    else:
1227
        FreeCAD.Console.PrintWarning('cube with radius zero\n')
1228
        mycube=doc.addObject("Part::Feature","emptycube")
1229
        mycube.Shape = Part.Compound([])
1230
    if p[3].get('center','false')=='true' :
1231
        center(mycube,l,w,h)
1232
    p[0] = [mycube]
1233
    if printverbose: print("End Cube")
1234

1235
def p_circle_action(p) :
1236
    'circle_action : circle LPAREN keywordargument_list RPAREN SEMICOL'
1237
    if printverbose: print("Circle : "+str(p[3]))
1238
    r = float(p[3]['r'])
1239
    # Avoid zero radius
1240
    if r == 0 : r = 0.00001
1241
    n = int(p[3]['$fn'])
1242
    fnmax = FreeCAD.ParamGet(\
1243
        "User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
1244
        GetInt('useMaxFN',16)
1245
    # Alter Max polygon to control if polygons are circles or polygons
1246
    # in the modules preferences
1247
    import Draft
1248
    if n == 0 or fnmax != 0 and n >= fnmax:
1249
        mycircle = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",'circle')
1250
        Draft._Circle(mycircle)
1251
        mycircle.Radius = r
1252
        mycircle.MakeFace = True
1253
        mycircle = Draft.makeCircle(r,face=True) # would call doc.recompute
1254
        FreeCAD.ActiveDocument.recompute()
1255
        #mycircle = doc.addObject('Part::Circle',p[1]) #would not create a face
1256
        #mycircle.Radius = r
1257
    else :
1258
        #mycircle = Draft.makePolygon(n,r) # would call doc.recompute
1259
        mycircle = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",'polygon')
1260
        Draft._Polygon(mycircle)
1261
        mycircle.FacesNumber = n
1262
        mycircle.Radius = r
1263
        mycircle.DrawMode = "inscribed"
1264
        mycircle.MakeFace = True
1265
    if gui:
1266
        Draft._ViewProviderDraft(mycircle.ViewObject)
1267
    if printverbose: print("Push Circle")
1268
    p[0] = [mycircle]
1269

1270
def p_square_action(p) :
1271
    'square_action : square LPAREN keywordargument_list RPAREN SEMICOL'
1272
    if printverbose: print("Square")
1273
    size = p[3]['size']
1274
    x = float(size[0])
1275
    y = float(size[1])
1276
    mysquare = doc.addObject('Part::Plane',p[1])
1277
    mysquare.Length=x
1278
    mysquare.Width=y
1279
    if p[3].get('center','false')=='true' :
1280
        center(mysquare,x,y,0)
1281
    p[0] = [mysquare]
1282

1283
def addString(t,s,p):
1284
    return(t + ', ' +s+' = "'+p[3][s]+'"')
1285

1286
def addValue(t,v,p):
1287
    return(t + ', ' +v+' = '+p[3][v])
1288

1289
def p_text_action(p) :
1290
    'text_action : text LPAREN keywordargument_list RPAREN SEMICOL'
1291
    # If text string is null ignore
1292
    if p[3]['text'] == "" or p[3]['text'] == " " :
1293
        p[0] = []
1294
        return
1295
    t = 'text ( text="'+p[3]['text']+'"'
1296
    t = addValue(t,'size',p)
1297
    t = addString(t,'spacing',p)
1298
    t = addString(t,'font',p)
1299
    t = addString(t,'direction',p)
1300
    t = addString(t,'language',p)
1301
    if "script" in p[3]:
1302
        t = addString(t,'script',p)
1303
    else:
1304
        t += ', script="latin"'
1305
    t = addString(t,'halign',p)
1306
    t = addString(t,'valign',p)
1307
    t = addValue(t,'$fn',p)
1308
    t = addValue(t,'$fa',p)
1309
    t = addValue(t,'$fs',p)
1310
    t = t+');'
1311

1312
    FreeCAD.Console.PrintMessage("textmsg : "+t+"\n")
1313
    p[0] = [processTextCmd(t)]
1314

1315
def convert_points_list_to_vector(l):
1316
    v = []
1317
    for i in l :
1318
        if printverbose: print(i)
1319
        v.append(FreeCAD.Vector(i[0],i[1]))
1320
    if printverbose: print(v)
1321
    return v
1322

1323

1324
def p_polygon_action_nopath(p) :
1325
    'polygon_action_nopath : polygon LPAREN points EQ OSQUARE points_list_2d ESQUARE COMMA paths EQ undef COMMA keywordargument_list RPAREN SEMICOL'
1326
    if printverbose: print("Polygon")
1327
    if printverbose: print(p[6])
1328
    v = convert_points_list_to_vector(p[6])
1329
    mypolygon = doc.addObject('Part::Feature',p[1])
1330
    if printverbose: print("Make Parts")
1331
    # Close Polygon
1332
    v.append(v[0])
1333
    parts = Part.makePolygon(v)
1334
    if printverbose: print("update object")
1335
    mypolygon.Shape = Part.Face(parts)
1336
    p[0] = [mypolygon]
1337

1338
def p_polygon_action_plus_path(p) :
1339
    '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'
1340
    if printverbose: print("Polygon with Path")
1341
    if printverbose: print(p[6])
1342
    v = convert_points_list_to_vector(p[6])
1343
    if printverbose: print("Path Set List")
1344
    if printverbose: print(p[12])
1345
    for i in p[12] :
1346
        if printverbose: print(i)
1347
        mypolygon = doc.addObject('Part::Feature','wire')
1348
        path_list = []
1349
        for j in i :
1350
            j = int(j)
1351
            if printverbose: print(j)
1352
            path_list.append(v[j])
1353
#       Close path
1354
        path_list.append(v[int(i[0])])
1355
        if printverbose: print('Path List')
1356
        if printverbose: print(path_list)
1357
        wire = Part.makePolygon(path_list)
1358
        mypolygon.Shape = Part.Face(wire)
1359
        p[0] = [mypolygon]
1360
#       This only pushes last polygon
1361

1362
def make_face(v1,v2,v3):
1363
    wire = Part.makePolygon([v1,v2,v3,v1])
1364
    face = Part.Face(wire)
1365
    return face
1366

1367
def p_polyhedron_action(p) :
1368
    '''polyhedron_action : polyhedron LPAREN points EQ OSQUARE points_list_3d ESQUARE COMMA faces EQ OSQUARE path_set ESQUARE COMMA keywordargument_list RPAREN SEMICOL
1369
                      | polyhedron LPAREN points EQ OSQUARE points_list_3d ESQUARE COMMA triangles EQ OSQUARE points_list_3d ESQUARE COMMA keywordargument_list RPAREN SEMICOL'''
1370
    if printverbose: print("Polyhedron Points")
1371
    v = []
1372
    for i in p[6] :
1373
        if printverbose: print(i)
1374
        v.append(FreeCAD.Vector(float(i[0]),float(i[1]),float(i[2])))
1375
    if printverbose:
1376
        print(v)
1377
        print ("Polyhedron "+p[9])
1378
        print (p[12])
1379
    faces_list = []
1380
    mypolyhed = doc.addObject('Part::Feature',p[1])
1381
    for i in p[12] :
1382
        if printverbose: print(i)
1383
        v2 = FreeCAD.Vector
1384
        pp =[v2(v[k]) for k in i]
1385
        # Add first point to end of list to close polygon
1386
        pp.append(pp[0])
1387
        try:
1388
            w = Part.makePolygon(pp)
1389
            f = Part.Face(w)
1390
        except Exception:
1391
            secWireList = w.Edges[:]
1392
            f = Part.makeFilledFace(Part.__sortEdges__(secWireList))
1393
        #f = make_face(v[int(i[0])],v[int(i[1])],v[int(i[2])])
1394
        faces_list.append(f)
1395
    shell=Part.makeShell(faces_list)
1396
    solid=Part.Solid(shell).removeSplitter()
1397
    if solid.Volume < 0:
1398
        solid.reverse()
1399
    mypolyhed.Shape=solid
1400
    p[0] = [mypolyhed]
1401

1402
def p_projection_action(p) :
1403
    'projection_action : projection LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE'
1404
    if printverbose: print('Projection')
1405

1406
    doc.recompute()
1407
    p[6][0].Shape.tessellate(0.05) # Ensure the bounding box calculation is not done with the splines, which can give a bad result
1408
    bbox = p[6][0].Shape.BoundBox
1409
    for shape in p[6]:
1410
        shape.Shape.tessellate(0.05)
1411
        bbox.add(shape.Shape.BoundBox)
1412
    plane = doc.addObject("Part::Plane","xy_plane_used_for_projection")
1413
    plane.Length = bbox.XLength
1414
    plane.Width = bbox.YLength
1415
    plane.Placement = FreeCAD.Placement(FreeCAD.Vector(\
1416
                     bbox.XMin,bbox.YMin,0),FreeCAD.Rotation())
1417
    if gui:
1418
        plane.ViewObject.hide()
1419

1420
    if p[3]['cut'] == 'true' :
1421
        obj = doc.addObject('Part::MultiCommon','projection_cut')
1422
        if (len(p[6]) > 1):
1423
            subobj = [fuse(p[6],"projection_cut_implicit_group")]
1424
        else:
1425
            subobj = p[6]
1426
        obj.Shapes = [plane] + subobj
1427
        if gui:
1428
            subobj[0].ViewObject.hide()
1429
        p[0] = [obj]
1430
    else: # cut == 'false' => true projection
1431
        if gui and not FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
1432
                GetBool('usePlaceholderForUnsupported'):
1433
            from PySide import QtGui
1434
            QtGui.QMessageBox.critical(None, translate('OpenSCAD',"Unsupported Function") + " : " + p[1],translate('OpenSCAD',"Press OK"))
1435
        else:
1436
            p[0] = [placeholder(p[1],p[6],p[3])]
1437

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

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

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

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