FreeCAD

Форк
0
/
importCSG.py 
1434 строки · 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
# Save the native open function to avoid collisions
48
if open.__module__ in ['__builtin__', 'io']:
49
    pythonopen = open
50
import ply.lex as lex
51
import ply.yacc as yacc
52

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

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

62
hassetcolor = []
63
alreadyhidden = []
64
original_root_objects = []
65

66
# Get the token map from the lexer. This is required.
67
import tokrules
68
from tokrules import tokens
69

70
translate = FreeCAD.Qt.translate
71

72

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

81

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

99

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

111

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

131

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

156
def processcsg(filename):
157
    global doc
158

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

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

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

191

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

208

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

214

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

232

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

238

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

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

250

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

255

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

268

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

280

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

290

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

305

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

312

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

332

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

339

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

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

376

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

383

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

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

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

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

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

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

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

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

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

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

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

527
    transform_matrix.scale(scale)
528

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

541

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

947

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

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

983
def processSTL(fname):
984
    if printverbose: print("Process STL file")
985

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1321

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

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

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

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

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

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

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

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

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

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

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