FreeCAD

Форк
0
/
OpenSCADFeatures.py 
699 строк · 25.4 Кб
1
#***************************************************************************
2
#*   Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de>       *
3
#*                                                                         *
4
#*   This program is free software; you can redistribute it and/or modify  *
5
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
6
#*   as published by the Free Software Foundation; either version 2 of     *
7
#*   the License, or (at your option) any later version.                   *
8
#*   for detail see the LICENCE text file.                                 *
9
#*                                                                         *
10
#*   This program is distributed in the hope that it will be useful,       *
11
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13
#*   GNU Library General Public License for more details.                  *
14
#*                                                                         *
15
#*   You should have received a copy of the GNU Library General Public     *
16
#*   License along with this program; if not, write to the Free Software   *
17
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
18
#*   USA                                                                   *
19
#*                                                                         *
20
#***************************************************************************
21

22
__title__ = "FreeCAD OpenSCAD Workbench - Parametric Features"
23
__author__ = "Sebastian Hoogen"
24
__url__ = ["https://www.freecad.org"]
25

26
try:
27
    long
28
except NameError:
29
    long = int
30

31
'''
32
This Script includes python Features to represent OpenSCAD Operations
33
'''
34

35

36
class ViewProviderTree:
37
    "A generic View Provider for Elements with Children"
38

39
    def __init__(self, obj):
40
        obj.Proxy = self
41
        self.Object = obj.Object
42

43
    def attach(self, obj):
44
        self.Object = obj.Object
45
        return
46

47
    def updateData(self, fp, prop):
48
        return
49

50
    def getDisplayModes(self,obj):
51
        modes=[]
52
        return modes
53

54
    def setDisplayMode(self,mode):
55
        return mode
56

57
    def onChanged(self, vp, prop):
58
        return
59

60
    def dumps(self):
61
#        return {'ObjectName' : self.Object.Name}
62
        return None
63

64
    def loads(self,state):
65
        if state is not None:
66
            import FreeCAD
67
            doc = FreeCAD.ActiveDocument #crap
68
            self.Object = doc.getObject(state['ObjectName'])
69

70
    def claimChildren(self):
71
        objs = []
72
        if hasattr(self.Object.Proxy,"Base"):
73
            objs.append(self.Object.Proxy.Base)
74
        if hasattr(self.Object,"Base"):
75
            objs.append(self.Object.Base)
76
        if hasattr(self.Object,"Objects"):
77
            objs.extend(self.Object.Objects)
78
        if hasattr(self.Object,"Components"):
79
            objs.extend(self.Object.Components)
80
        if hasattr(self.Object,"Children"):
81
            objs.extend(self.Object.Children)
82

83
        return objs
84

85
    def getIcon(self):
86
        import OpenSCAD_rc
87
        if isinstance(self.Object.Proxy,RefineShape):
88
            return(":/icons/OpenSCAD_RefineShapeFeature.svg")
89
        if isinstance(self.Object.Proxy,IncreaseTolerance):
90
            return(":/icons/OpenSCAD_IncreaseToleranceFeature.svg")
91
        if isinstance(self.Object.Proxy,MatrixTransform):
92
            return """/* XPM */
93
static char * matrix_xpm[] = {
94
"16 16 3 1",
95
" 	c #0079FF",
96
".	c #FFFFFF",
97
"+	c #000000",
98
"   .........   .",
99
" ............. .",
100
" .  .  .  .  . .",
101
" .  .  .  .  . .",
102
" ............. .",
103
" .  .  .  .  . .",
104
" .  .  .  .  . .",
105
" ............. .",
106
" .  .  .  .  . .",
107
" .  .  .  .  . .",
108
" ............. .",
109
" ...........+. .",
110
" ..+..+..+..+. .",
111
" ............. .",
112
"   .........   .",
113
"................"};"""
114
        else:
115
            return """/* XPM */
116
static char * openscadlogo_xpm[] = {
117
"16 16 33 1",
118
" 	c None",
119
".	c #61320B",
120
"+	c #5D420B",
121
"@	c #4F4C09",
122
"#	c #564930",
123
"$	c #754513",
124
"%	c #815106",
125
"&	c #666509",
126
"*	c #875F55",
127
"=	c #6E7000",
128
"-	c #756A53",
129
";	c #717037",
130
">	c #946637",
131
",	c #92710E",
132
"'	c #797A0A",
133
")	c #7C7720",
134
"!	c #8A8603",
135
"~	c #88886F",
136
"{	c #AF8181",
137
"]	c #999908",
138
"^	c #BB8D8D",
139
"/	c #AAAA00",
140
"(	c #A9A880",
141
"_	c #B5B419",
142
":	c #C1A9A9",
143
"<	c #B1B19A",
144
"[	c #BEBE00",
145
"}	c #B9B8B4",
146
"|	c #CACC00",
147
"1	c #D4D4BC",
148
"2	c #DBD2D0",
149
"3	c #EEEEED",
150
"4	c #FDFFFC",
151
"4444444444444444",
152
"4444443113444444",
153
"4444<;']]!;<^^24",
154
"444(&@!]]]=&#^{3",
155
"44<']')@++)!&*{^",
156
"44)]/[|//[/]'@{{",
157
"42=/_|||||[]!&*{",
158
"4(&][|||||[/'@#}",
159
"3-..,|||||[)&&~4",
160
"^*$%.!|||[!+/](4",
161
"^{%%%._[[_&/[_14",
162
":{>%%.!//])_[_44",
163
"2{{%%+!]!!)]]344",
164
"4:{{#@&=&&@#3444",
165
"44224}~--~}44444",
166
"4444444444444444"};
167
"""
168

169

170
class OpenSCADPlaceholder:
171
    def __init__(self,obj,children=None,arguments=None):
172
        obj.addProperty("App::PropertyLinkList",'Children','OpenSCAD',"Base Objects")
173
        obj.addProperty("App::PropertyString",'Arguments','OpenSCAD',"Arguments")
174
        obj.Proxy = self
175
        if children:
176
            obj.Children = children
177
        if arguments:
178
            obj.Arguments = arguments
179

180
    def execute(self,fp):
181
        import Part
182
        fp.Shape = Part.Compound([]) #empty Shape
183

184

185
class Resize:
186
    def __init__(self,obj,target,vector):
187
        import FreeCAD
188
        #self.Obj = obj
189
        self.Target = target
190
        self.Vector = vector
191
        #obj.addProperty("App::PropertyPythonObject","Object","Resize", \
192
        #                "Object to be resized").Object = target
193
        obj.addProperty("Part::PropertyPartShape","Shape","Resize", "Shape of the Resize")
194
        obj.addProperty("App::PropertyVector","Vector","Resize",
195
                        " Resize Vector").Vector = FreeCAD.Vector(vector)
196
        obj.Proxy = self
197

198
    def execute(self, fp):
199
        import FreeCAD
200
        mat = FreeCAD.Matrix()
201
        mat.A11 = self.Vector[0]
202
        mat.A22 = self.Vector[1]
203
        mat.A33 = self.Vector[2]
204
        fp.Shape = self.Target.Shape.transformGeometry(mat)
205

206
    def dumps(self):
207
        return None
208

209
    def loads(self,state):
210
        return None
211

212

213
class MatrixTransform:
214
    def __init__(self, obj,matrix=None,child=None):
215
        obj.addProperty("App::PropertyLink","Base","Base",
216
                        "The base object that must be tranfsformed")
217
        obj.addProperty("App::PropertyMatrix","Matrix","Matrix", "Transformation Matrix")
218
        obj.Proxy = self
219
        obj.Matrix = matrix
220
        obj.Base = child
221

222
    def onChanged(self, fp, prop):
223
        "Do something when a property has changed"
224
        pass
225

226
    def updateProperty(self, fp, prop, value):
227
        epsilon = 0.0001
228
        if abs(getattr(fp, prop) - value) > epsilon:
229
            setattr(fp, prop, value)
230

231
    def execute(self, fp):
232
        if fp.Matrix and fp.Base:
233
            sh = fp.Base.Shape#.copy()
234
            m = sh.Placement.toMatrix().multiply(fp.Matrix)
235
            fp.Shape = sh.transformGeometry(m)
236
        #else:
237
            #FreeCAD.Console.PrintMessage('base %s\nmat %s/n' % (fp.Base,fp.Matrix))
238

239

240
class ImportObject:
241
    def __init__(self, obj,child=None):
242
        obj.addProperty("App::PropertyLink", "Base", "Base",
243
                        "The base object that must be tranfsformed")
244
        obj.Proxy = self
245
        obj.Base = child
246

247
    def onChanged(self, fp, prop):
248
        "Do something when a property has changed"
249
        pass
250

251
    def execute(self, fp):
252
        pass
253
#        if fp.Base:
254
#            fp.Shape = fp.Base.Shape.copy()
255

256

257
class RefineShape:
258
    '''return a refined shape'''
259
    def __init__(self, obj, child=None):
260
        obj.addProperty("App::PropertyLink", "Base", "Base",
261
                        "The base object that must be refined")
262
        obj.Proxy = self
263
        obj.Base = child
264

265
    def onChanged(self, fp, prop):
266
        "Do something when a property has changed"
267
        pass
268

269
    def execute(self, fp):
270
        if fp.Base and fp.Base.Shape.isValid():
271
            import OpenSCADUtils
272
            sh = fp.Base.Shape.removeSplitter()
273
            fp.Shape = OpenSCADUtils.applyPlacement(sh)
274

275
class IncreaseTolerance:
276
    '''increase the tolerance of every vertex
277
    in the current implementation its' placement is linked'''
278
    def __init__(self,obj,child,tolerance=0):
279
        obj.addProperty("App::PropertyLink", "Base", "Base",
280
                        "The base object that wire must be extracted")
281
        obj.addProperty("App::PropertyDistance","Vertex","Tolerance","Vertexes tolerance (0 default)")
282
        obj.addProperty("App::PropertyDistance","Edge","Tolerance","Edges tolerance (0 default)")
283
        obj.addProperty("App::PropertyDistance","Face","Tolerance","Faces tolerance (0 default)")
284
        obj.Base = child
285
        obj.Vertex = tolerance
286
        obj.Edge = tolerance
287
        obj.Face = tolerance
288
        obj.Proxy = self
289

290
    def execute(self, fp):
291
        if fp.Base:
292
            sh=fp.Base.Shape.copy()
293
            # Check if property Tolerance exist and preserve support for backward compatibility
294
            if hasattr(fp, "Tolerance") and fp.Proxy.__module__ == "OpenSCADFeatures":
295
                for vertex in sh.Vertexes:
296
                    vertex.Tolerance = max(vertex.Tolerance,fp.Tolerance.Value)
297
            # New properties
298
            else:
299
                for vertex in sh.Vertexes:
300
                    vertex.Tolerance = max(vertex.Tolerance, fp.Vertex.Value)
301
                for edge in sh.Edges:
302
                    edge.Tolerance = max(edge.Tolerance, fp.Edge.Value)
303
                for face in sh.Faces:
304
                    face.Tolerance = max(face.Tolerance, fp.Face.Value)
305

306
            fp.Shape = sh
307
            fp.Placement = sh.Placement
308

309

310
class GetWire:
311
    '''return the first wire from a given shape'''
312
    def __init__(self, obj, child=None):
313
        obj.addProperty("App::PropertyLink","Base","Base",
314
                        "The base object that wire must be extracted")
315
        obj.Proxy = self
316
        obj.Base = child
317

318
    def onChanged(self, fp, prop):
319
        "Do something when a property has changed"
320
        pass
321

322
    def execute(self, fp):
323
        if fp.Base:
324
            import Part
325
            #fp.Shape=fp.Base.Shape.Wires[0]
326
            fp.Shape=Part.Wire(fp.Base.Shape.Wires[0]) # works with 0.13 stable
327
            #sh = fp.Base.Shape.Wires[0].copy; sh.transformSahpe(fp.Base.Shape.Placement.toMatrix()); fp.Shape = sh #untested
328

329
class Frustum:
330
    def __init__(self, obj,r1=1,r2=2,n=3,h=4):
331
        obj.addProperty("App::PropertyInteger","FacesNumber","Base","Number of faces")
332
        obj.addProperty("App::PropertyDistance","Radius1","Base","Radius of lower the inscribed control circle")
333
        obj.addProperty("App::PropertyDistance","Radius2","Base","Radius of upper the inscribed control circle")
334
        obj.addProperty("App::PropertyDistance","Height","Base","Height of the Frustum")
335

336
        obj.FacesNumber = n
337
        obj.Radius1 = r1
338
        obj.Radius2=  r2
339
        obj.Height= h
340
        obj.Proxy = self
341

342
    def execute(self, fp):
343
        if all((fp.Radius1,fp.Radius2,fp.FacesNumber,fp.Height)):
344
            import math
345
            import FreeCAD
346
            import Part
347
            #from draftlibs import fcgeo
348
            plm = fp.Placement
349
            wires = []
350
            faces = []
351
            for ir,r in enumerate((fp.Radius1,fp.Radius2)):
352
                angle = (math.pi*2)/fp.FacesNumber
353
                pts = [FreeCAD.Vector(r.Value,0,ir*fp.Height.Value)]
354
                for i in range(fp.FacesNumber-1):
355
                    ang = (i+1)*angle
356
                    pts.append(FreeCAD.Vector(r.Value*math.cos(ang),\
357
                            r.Value*math.sin(ang),ir*fp.Height.Value))
358
                pts.append(pts[0])
359
                shape = Part.makePolygon(pts)
360
                face = Part.Face(shape)
361
                if ir == 0: #top face
362
                    face.reverse()
363
                wires.append(shape)
364
                faces.append(face)
365
            #shellperi = Part.makeRuledSurface(*wires)
366
            shellperi = Part.makeLoft(wires)
367
            shell = Part.Shell(shellperi.Faces+faces)
368
            fp.Shape = Part.Solid(shell)
369
            fp.Placement = plm
370

371
class Twist:
372
    def __init__(self, obj, child=None, h=1.0, angle=0.0, scale=[1.0,1.0]):
373
        import FreeCAD
374
        obj.addProperty("App::PropertyLink","Base","Base",
375
                        "The base object that must be transformed")
376
        obj.addProperty("App::PropertyQuantity","Angle","Base","Twist Angle")
377
        obj.Angle = FreeCAD.Units.Angle # assign the Angle unit
378
        obj.addProperty("App::PropertyDistance","Height","Base","Height of the Extrusion")
379
        obj.addProperty("App::PropertyFloatList","Scale","Base","Scale to apply during the Extrusion")
380

381
        obj.Base = child
382
        obj.Angle = angle
383
        obj.Height = h
384
        obj.Scale = scale
385
        obj.Proxy = self
386

387
    def execute(self, fp):
388
        import FreeCAD
389
        import Part
390
        import math
391
        import sys
392
        if fp.Base and fp.Height and fp.Base.Shape.isValid():
393
            solids = []
394
            for lower_face in fp.Base.Shape.Faces:
395
                upper_face = lower_face.copy()
396
                face_transform = FreeCAD.Matrix()
397
                face_transform.rotateZ(math.radians(fp.Angle.Value))
398
                face_transform.scale(fp.Scale[0], fp.Scale[1], 1.0)
399
                face_transform.move(FreeCAD.Vector(0,0,fp.Height.Value))
400
                upper_face.transformShape(face_transform, False, True) # True to check for non-uniform scaling
401

402
                spine = Part.makePolygon([(0,0,0),(0,0,fp.Height.Value)])
403
                if fp.Angle.Value == 0.0:
404
                    auxiliary_spine = None
405
                else:
406
                    num_revolutions = abs(fp.Angle.Value)/360.0
407
                    pitch = fp.Height.Value / num_revolutions
408
                    height = fp.Height.Value
409
                    radius = 1.0
410
                    if fp.Angle.Value < 0.0:
411
                        left_handed = True
412
                    else:
413
                        left_handed = False
414

415
                    auxiliary_spine = Part.makeHelix(pitch, height, radius, 0.0, left_handed)
416

417
                faces = [lower_face,upper_face]
418
                for wire1,wire2 in zip(lower_face.Wires,upper_face.Wires):
419
                    pipe_shell = Part.BRepOffsetAPI.MakePipeShell(spine)
420
                    pipe_shell.setSpineSupport(spine)
421
                    pipe_shell.add(wire1)
422
                    pipe_shell.add(wire2)
423
                    if auxiliary_spine:
424
                        pipe_shell.setAuxiliarySpine(auxiliary_spine,True,0)
425
                    assert(pipe_shell.isReady())
426
                    pipe_shell.build()
427
                    faces.extend(pipe_shell.shape().Faces)
428
                try:
429
                    fullshell = Part.Shell(faces)
430
                    solid=Part.Solid(fullshell)
431
                    if solid.Volume < 0:
432
                        solid.reverse()
433
                    assert(solid.Volume >= 0)
434
                    solids.append(solid)
435
                except Part.OCCError:
436
                    solids.append(Part.Compound(faces))
437
                fp.Shape=Part.Compound(solids)
438

439

440

441
class PrismaticToroid:
442
    def __init__(self, obj,child=None,angle=360.0,n=3):
443
        obj.addProperty("App::PropertyLink","Base","Base",
444
                        "The 2D face that will be swept")
445
        obj.addProperty("App::PropertyAngle","Angle","Base","Angle to sweep through")
446
        obj.addProperty("App::PropertyInteger","Segments","Base","Number of segments per 360° (OpenSCAD's \"$fn\")")
447

448
        obj.Base = child
449
        obj.Angle =  angle
450
        obj.Segments = n
451
        obj.Proxy = self
452

453
    def execute(self, fp):
454
        import FreeCAD
455
        import Part
456
        import math
457
        import sys
458
        if fp.Base and fp.Angle and fp.Segments and fp.Base.Shape.isValid():
459
            solids = []
460
            min_sweep_angle_per_segment = 360.0 / fp.Segments # This is how OpenSCAD defines $fn
461
            num_segments = math.floor(abs(fp.Angle) / min_sweep_angle_per_segment)
462
            num_ribs = num_segments + 1
463
            sweep_angle_per_segment = fp.Angle / num_segments # Always >= min_sweep_angle_per_segment
464

465
            # From the OpenSCAD documentation:
466
            # The 2D shape must lie completely on either the right (recommended) or the left side of the Y-axis.
467
            # More precisely speaking, every vertex of the shape must have either x >= 0 or x <= 0. If the shape
468
            # spans the X axis a warning appears in the console windows and the rotate_extrude() is ignored. If
469
            # the 2D shape touches the Y axis, i.e. at x=0, it must be a line that touches, not a point.
470

471
            for start_face in fp.Base.Shape.Faces:
472
                ribs = []
473
                end_face = start_face
474
                for rib in range(num_ribs):
475
                    angle = rib * sweep_angle_per_segment
476
                    intermediate_face = start_face.copy()
477
                    face_transform = FreeCAD.Matrix()
478
                    face_transform.rotateY (math.radians (angle))
479
                    intermediate_face.transformShape (face_transform)
480
                    if rib == num_ribs-1:
481
                        end_face = intermediate_face
482

483
                    edges = []
484
                    for edge in intermediate_face.OuterWire.Edges:
485
                        if edge.BoundBox.XMin != 0.0 or edge.BoundBox.XMax != 0.0:
486
                            edges.append(edge)
487

488
                    ribs.append(Part.Wire(edges))
489

490
                faces = []
491
                shell = Part.makeShellFromWires (ribs)
492
                for face in shell.Faces:
493
                    faces.append(face)
494

495
                if abs(fp.Angle) < 360.0 and faces:
496
                    if fp.Angle > 0:
497
                        faces.append(start_face.reversed()) # Reversed so the normal faces out of the shell
498
                        faces.append(end_face)
499
                    else:
500
                        faces.append(start_face)
501
                        faces.append(end_face.reversed()) # Reversed so the normal faces out of the shell
502

503
                try:
504
                    shell = Part.makeShell(faces)
505
                    shell.sewShape()
506
                    shell.fix(1e-7,1e-7,1e-7)
507
                    clean_shell = shell.removeSplitter()
508
                    solid = Part.makeSolid (clean_shell)
509
                    if solid.Volume < 0:
510
                        solid.reverse()
511
                    solids.append(solid)
512
                except Part.OCCError:
513
                    FreeCAD.Console.PrintWarning("Could not create solid: creating compound instead")
514
                    solids.append(Part.Compound(faces))
515
            fp.Shape = Part.Compound(solids)
516

517
class OffsetShape:
518
    def __init__(self, obj,child=None,offset=1.0):
519
        obj.addProperty("App::PropertyLink","Base","Base",
520
                        "The base object that must be transformed")
521
        obj.addProperty("App::PropertyDistance","Offset","Base","Offset outwards")
522

523
        obj.Base = child
524
        obj.Offset = offset
525
        obj.Proxy = self
526

527
    def execute(self, fp):
528
        if fp.Base and fp.Offset:
529
            fp.Shape=fp.Base.Shape.makeOffsetShape(fp.Offset.Value,1e-6)
530

531
class CGALFeature:
532
    def __init__(self,obj,opname=None,children=None,arguments=None):
533
        obj.addProperty("App::PropertyLinkList",'Children','OpenSCAD',"Base Objects")
534
        obj.addProperty("App::PropertyString",'Arguments','OpenSCAD',"Arguments")
535
        obj.addProperty("App::PropertyString",'Operation','OpenSCAD',"Operation")
536
        obj.Proxy = self
537
        if opname:
538
            obj.Operation = opname
539
        if children:
540
            obj.Children = children
541
        if arguments:
542
            obj.Arguments = arguments
543

544
    def execute(self,fp):
545
        #arguments are ignored
546
        maxmeshpoints = None #TBD: add as property
547
        import Part
548
        import OpenSCAD.OpenSCADUtils
549
        shape = OpenSCAD.OpenSCADUtils.process_ObjectsViaOpenSCADShape(fp.Document,fp.Children,\
550
                fp.Operation, maxmeshpoints=maxmeshpoints)
551
        if shape:
552
            fp.Shape = shape
553
        else:
554
            raise ValueError
555

556
def makeSurfaceVolume(filename):
557
    import FreeCAD
558
    import Part
559
    import sys
560
    coords = []
561
    with open(filename) as f1:
562
        min_z = sys.float_info.max
563
        for line in f1.readlines():
564
            sline = line.strip()
565
            if sline and not sline.startswith('#'):
566
                ycoord = len(coords)
567
                lcoords = []
568
                for xcoord, num in enumerate(sline.split()):
569
                    fnum = float(num)
570
                    lcoords.append(FreeCAD.Vector(float(xcoord),float(ycoord),fnum))
571
                    min_z = min(fnum,min_z)
572
                coords.append(lcoords)
573

574
    num_rows = len(coords)
575
    if num_rows == 0:
576
        FreeCAD.Console.PrintWarning(f"No data found in surface file {filename}")
577
        return None,0,0
578
    num_cols = len(coords[0])
579

580
    # OpenSCAD does not spline this surface, so neither do we: just create a
581
    # bunch of faces,
582
    # using four triangles per quadrilateral
583
    faces = []
584
    for row in range(num_rows - 1):
585
        for col in range(num_cols - 1):
586
            a = coords[row + 0][col + 0]
587
            b = coords[row + 0][col + 1]
588
            c = coords[row + 1][col + 1]
589
            d = coords[row + 1][col + 0]
590
            centroid = 0.25 * (a + b + c + d)
591
            ab = Part.makeLine(a,b)
592
            bc = Part.makeLine(b,c)
593
            cd = Part.makeLine(c,d)
594
            da = Part.makeLine(d,a)
595

596
            diag_a = Part.makeLine(a, centroid)
597
            diag_b = Part.makeLine(b, centroid)
598
            diag_c = Part.makeLine(c, centroid)
599
            diag_d = Part.makeLine(d, centroid)
600

601
            wire1 = Part.Wire([ab,diag_a,diag_b])
602
            wire2 = Part.Wire([bc,diag_b,diag_c])
603
            wire3 = Part.Wire([cd,diag_c,diag_d])
604
            wire4 = Part.Wire([da,diag_d,diag_a])
605

606
            try:
607
                face = Part.Face(wire1)
608
                faces.append(face)
609
                face = Part.Face(wire2)
610
                faces.append(face)
611
                face = Part.Face(wire3)
612
                faces.append(face)
613
                face = Part.Face(wire4)
614
                faces.append(face)
615
            except Exception:
616
                FreeCAD.Console.PrintWarning("Failed to create the face from {},{},{},{}".format(coords[row + 0][col + 0],\
617
                    coords[row + 0][col + 1],coords[row + 1][col + 1],coords[row + 1][col + 0]))
618

619
    last_row = num_rows - 1
620
    last_col = num_cols - 1
621

622
    # Create the face to close off the y-min border: OpenSCAD places the lower
623
    # surface of the shell
624
    # at 1 unit below the lowest coordinate in the surface
625
    lines = []
626
    corner1 = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z - 1)
627
    lines.append(Part.makeLine(corner1,coords[0][0]))
628
    for col in range(num_cols - 1):
629
        a = coords[0][col]
630
        b = coords[0][col + 1]
631
        lines.append(Part.makeLine(a, b))
632
    corner2 = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z - 1)
633
    lines.append(Part.makeLine(corner2,coords[0][last_col]))
634
    lines.append(Part.makeLine(corner1,corner2))
635
    wire = Part.Wire(lines)
636
    face = Part.Face(wire)
637
    faces.append(face)
638

639
    # Create the face to close off the y-max border
640
    lines = []
641
    corner1 = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z - 1)
642
    lines.append(Part.makeLine(corner1,coords[last_row][0]))
643
    for col in range(num_cols - 1):
644
        a = coords[last_row][col]
645
        b = coords[last_row][col + 1]
646
        lines.append(Part.makeLine(a, b))
647
    corner2 = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z - 1)
648
    lines.append(Part.makeLine(corner2,coords[last_row][last_col]))
649
    lines.append(Part.makeLine(corner1,corner2))
650
    wire = Part.Wire(lines)
651
    face = Part.Face(wire)
652
    faces.append(face)
653

654
    # Create the face to close off the x-min border
655
    lines = []
656
    corner1 = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z - 1)
657
    lines.append(Part.makeLine(corner1,coords[0][0]))
658
    for row in range(num_rows - 1):
659
        a = coords[row][0]
660
        b = coords[row + 1][0]
661
        lines.append(Part.makeLine(a, b))
662
    corner2 = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z - 1)
663
    lines.append(Part.makeLine(corner2,coords[last_row][0]))
664
    lines.append(Part.makeLine(corner1,corner2))
665
    wire = Part.Wire(lines)
666
    face = Part.Face(wire)
667
    faces.append(face)
668

669
    # Create the face to close off the x-max border
670
    lines = []
671
    corner1 = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z - 1)
672
    lines.append(Part.makeLine(corner1,coords[0][last_col]))
673
    for row in range(num_rows - 1):
674
        a = coords[row][last_col]
675
        b = coords[row + 1][last_col]
676
        lines.append(Part.makeLine(a, b))
677
    corner2 = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z - 1)
678
    lines.append(Part.makeLine(corner2,coords[last_row][last_col]))
679
    lines.append(Part.makeLine(corner1,corner2))
680
    wire = Part.Wire(lines)
681
    face = Part.Face(wire)
682
    faces.append(face)
683

684
    # Create a bottom surface to close off the shell
685
    a = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z - 1)
686
    b = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z - 1)
687
    c = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z - 1)
688
    d = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z - 1)
689
    ab = Part.makeLine(a,b)
690
    bc = Part.makeLine(b,c)
691
    cd = Part.makeLine(c,d)
692
    da = Part.makeLine(d,a)
693
    wire = Part.Wire([ab,bc,cd,da])
694
    face = Part.Face(wire)
695
    faces.append(face)
696

697
    s = Part.Shell(faces)
698
    solid = Part.Solid(s)
699
    return solid,last_col,last_row
700

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

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

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

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