FreeCAD

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

25
import FreeCAD
26
import Part
27
import Path.Op.Adaptive as PathAdaptive
28
import Path.Main.Job as PathJob
29
from Tests.PathTestUtils import PathTestBase
30

31
if FreeCAD.GuiUp:
32
    import Path.Main.Gui.Job as PathJobGui
33
    import Path.Op.Gui.Adaptive as PathAdaptiveGui
34

35

36
class TestPathAdaptive(PathTestBase):
37
    """Unit tests for the Adaptive operation."""
38

39
    @classmethod
40
    def setUpClass(cls):
41
        """setUpClass()...
42
        This method is called upon instantiation of this test class.  Add code and objects here
43
        that are needed for the duration of the test() methods in this class.  In other words,
44
        set up the 'global' test environment here; use the `setUp()` method to set up a 'local'
45
        test environment.
46
        This method does not have access to the class `self` reference, but it
47
        is able to call static methods within this same class.
48
        """
49
        cls.needsInit = True
50

51
    @classmethod
52
    def initClass(cls):
53
        # Open existing FreeCAD document with test geometry
54
        cls.needsInit = False
55
        FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "True")
56
        cls.doc = FreeCAD.open(FreeCAD.getHomePath() + "Mod/CAM/Tests/test_adaptive.fcstd")
57
        FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "")
58

59
        # Create Job object, adding geometry objects from file opened above
60
        cls.job = PathJob.Create("Job", [cls.doc.Fusion], None)
61
        cls.job.GeometryTolerance.Value = 0.001
62
        if FreeCAD.GuiUp:
63
            cls.job.ViewObject.Proxy = PathJobGui.ViewProvider(cls.job.ViewObject)
64

65
        # Instantiate an Adaptive operation for querying available properties
66
        cls.prototype = PathAdaptive.Create("Adaptive")
67
        cls.prototype.Base = [(cls.doc.Fusion, ["Face3"])]
68
        cls.prototype.Label = "Prototype"
69
        _addViewProvider(cls.prototype)
70

71
        cls.doc.recompute()
72

73
    @classmethod
74
    def tearDownClass(cls):
75
        """tearDownClass()...
76
        This method is called prior to destruction of this test class.  Add code and objects here
77
        that cleanup the test environment after the test() methods in this class have been executed.
78
        This method does not have access to the class `self` reference.  This method
79
        is able to call static methods within this same class.
80
        """
81
        # FreeCAD.Console.PrintMessage("TestPathAdaptive.tearDownClass()\n")
82

83
        # Close geometry document without saving
84
        if not cls.needsInit:
85
            FreeCAD.closeDocument(cls.doc.Name)
86

87
    # Setup and tear down methods called before and after each unit test
88
    def setUp(self):
89
        """setUp()...
90
        This method is called prior to each `test()` method.  Add code and objects here
91
        that are needed for multiple `test()` methods.
92
        """
93
        if self.needsInit:
94
            self.initClass()
95

96
    def tearDown(self):
97
        """tearDown()...
98
        This method is called after each test() method. Add cleanup instructions here.
99
        Such cleanup instructions will likely undo those in the setUp() method.
100
        """
101
        pass
102

103
    # Unit tests
104
    def test00(self):
105
        """test00() Empty test."""
106
        return
107

108
    def test01(self):
109
        """test01() Verify path generated on Face3."""
110

111
        # Instantiate a Adaptive operation and set Base Geometry
112
        adaptive = PathAdaptive.Create("Adaptive")
113
        adaptive.Base = [(self.doc.Fusion, ["Face3"])]  # (base, subs_list)
114
        adaptive.Label = "test01+"
115
        adaptive.Comment = "test01() Verify path generated on Face3."
116

117
        # Set additional operation properties
118
        # setDepthsAndHeights(adaptive)
119
        adaptive.FinishingProfile = False
120
        adaptive.HelixAngle = 75.0
121
        adaptive.HelixDiameterLimit.Value = 1.0
122
        adaptive.LiftDistance.Value = 1.0
123
        adaptive.StepOver = 75
124
        adaptive.UseOutline = False
125
        adaptive.setExpression("StepDown", None)
126
        adaptive.StepDown.Value = (
127
            20.0  # Have to set expression to None before numerical value assignment
128
        )
129

130
        _addViewProvider(adaptive)
131
        self.doc.recompute()
132

133
        # moves = getGcodeMoves(adaptive.Path.Commands, includeRapids=False)
134
        # operationMoves = ";  ".join(moves)
135
        # FreeCAD.Console.PrintMessage("test00_moves: " + operationMoves + "\n")
136

137
        # self.assertTrue(expected_moves_test01 == operationMoves,
138
        #                "expected_moves_test01: {}\noperationMoves: {}".format(expected_moves_test01, operationMoves))
139
        self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
140

141
    def test02(self):
142
        """test02() Verify path generated on adjacent, combined Face3 and Face10.  The Z heights are different."""
143

144
        # Instantiate a Adaptive operation and set Base Geometry
145
        adaptive = PathAdaptive.Create("Adaptive")
146
        adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])]  # (base, subs_list)
147
        adaptive.Label = "test02+"
148
        adaptive.Comment = "test02() Verify path generated on adjacent, combined Face3 and Face10.  The Z heights are different."
149

150
        # Set additional operation properties
151
        # setDepthsAndHeights(adaptive)
152
        adaptive.FinishingProfile = False
153
        adaptive.HelixAngle = 75.0
154
        adaptive.HelixDiameterLimit.Value = 1.0
155
        adaptive.LiftDistance.Value = 1.0
156
        adaptive.StepOver = 75
157
        adaptive.UseOutline = False
158
        adaptive.setExpression("StepDown", None)
159
        adaptive.StepDown.Value = (
160
            20.0  # Have to set expression to None before numerical value assignment
161
        )
162

163
        _addViewProvider(adaptive)
164
        self.doc.recompute()
165

166
        self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
167

168
    def test03(self):
169
        """test03() Verify path generated on adjacent, combined Face3 and Face10.  The Z heights are different."""
170

171
        # Instantiate a Adaptive operation and set Base Geometry
172
        adaptive = PathAdaptive.Create("Adaptive")
173
        adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])]  # (base, subs_list)
174
        adaptive.Label = "test03+"
175
        adaptive.Comment = "test03() Verify path generated on adjacent, combined Face3 and Face10.  The Z heights are different."
176

177
        # Set additional operation properties
178
        # setDepthsAndHeights(adaptive)
179
        adaptive.FinishingProfile = False
180
        adaptive.HelixAngle = 75.0
181
        adaptive.HelixDiameterLimit.Value = 1.0
182
        adaptive.LiftDistance.Value = 1.0
183
        adaptive.StepOver = 75
184
        adaptive.UseOutline = True
185
        adaptive.setExpression("StepDown", None)
186
        adaptive.StepDown.Value = (
187
            20.0  # Have to set expression to None before numerical value assignment
188
        )
189

190
        _addViewProvider(adaptive)
191
        self.doc.recompute()
192

193
        self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
194

195
    def test04(self):
196
        """test04() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19"."""
197

198
        # Instantiate a Adaptive operation and set Base Geometry
199
        adaptive = PathAdaptive.Create("Adaptive")
200
        adaptive.Base = [
201
            (
202
                self.doc.Fusion,
203
                [
204
                    "Edge9",
205
                    "Edge2",
206
                    "Edge8",
207
                    "Edge15",
208
                    "Edge30",
209
                    "Edge31",
210
                    "Edge29",
211
                    "Edge19",
212
                ],
213
            )
214
        ]  # (base, subs_list)
215
        adaptive.Label = "test04+"
216
        adaptive.Comment = 'test04() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".'
217

218
        # Set additional operation properties
219
        # setDepthsAndHeights(adaptive)
220
        adaptive.FinishingProfile = False
221
        adaptive.HelixAngle = 75.0
222
        adaptive.HelixDiameterLimit.Value = 1.0
223
        adaptive.LiftDistance.Value = 1.0
224
        adaptive.StepOver = 75
225
        adaptive.UseOutline = False
226
        adaptive.setExpression("StepDown", None)
227
        adaptive.StepDown.Value = (
228
            20.0  # Have to set expression to None before numerical value assignment
229
        )
230

231
        _addViewProvider(adaptive)
232
        self.doc.recompute()
233

234
        self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
235

236
    def test05(self):
237
        """test05() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19"."""
238

239
        # Instantiate a Adaptive operation and set Base Geometry
240
        adaptive = PathAdaptive.Create("Adaptive")
241
        adaptive.Base = [
242
            (
243
                self.doc.Fusion,
244
                [
245
                    "Edge13",
246
                    "Edge7",
247
                    "Edge9",
248
                    "Edge2",
249
                    "Edge8",
250
                    "Edge15",
251
                    "Edge30",
252
                    "Edge31",
253
                    "Edge29",
254
                    "Edge19",
255
                ],
256
            )
257
        ]  # (base, subs_list)
258
        adaptive.Label = "test05+"
259
        adaptive.Comment = 'test05() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".'
260

261
        # Set additional operation properties
262
        # setDepthsAndHeights(adaptive)
263
        adaptive.FinishingProfile = False
264
        adaptive.HelixAngle = 75.0
265
        adaptive.HelixDiameterLimit.Value = 1.0
266
        adaptive.LiftDistance.Value = 1.0
267
        adaptive.StepOver = 75
268
        adaptive.UseOutline = False
269
        adaptive.setExpression("StepDown", None)
270
        adaptive.StepDown.Value = (
271
            20.0  # Have to set expression to None before numerical value assignment
272
        )
273

274
        _addViewProvider(adaptive)
275
        self.doc.recompute()
276

277
        self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
278

279
    def test06(self):
280
        """test06() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33"."""
281

282
        # Instantiate a Adaptive operation and set Base Geometry
283
        adaptive = PathAdaptive.Create("Adaptive")
284
        adaptive.Base = [
285
            (
286
                self.doc.Fusion,
287
                [
288
                    "Edge15",
289
                    "Edge30",
290
                    "Edge31",
291
                    "Edge29",
292
                    "Edge19",
293
                    "Edge18",
294
                    "Edge35",
295
                    "Edge32",
296
                    "Edge34",
297
                    "Edge33",
298
                ],
299
            )
300
        ]  # (base, subs_list)
301
        adaptive.Label = "test06+"
302
        adaptive.Comment = 'test06() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33".'
303

304
        # Set additional operation properties
305
        # setDepthsAndHeights(adaptive)
306
        adaptive.FinishingProfile = False
307
        adaptive.HelixAngle = 75.0
308
        adaptive.HelixDiameterLimit.Value = 1.0
309
        adaptive.LiftDistance.Value = 1.0
310
        adaptive.StepOver = 75
311
        adaptive.UseOutline = False
312
        adaptive.setExpression("StepDown", None)
313
        adaptive.StepDown.Value = (
314
            20.0  # Have to set expression to None before numerical value assignment
315
        )
316

317
        _addViewProvider(adaptive)
318
        self.doc.recompute()
319

320
        # Check command count
321
        self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
322

323
        # Check if any paths originate inside inner hole of donut.  They should not.
324
        isInBox = False
325
        edges = [
326
            self.doc.Fusion.Shape.getElement(e) for e in ["Edge35", "Edge32", "Edge33", "Edge34"]
327
        ]
328
        square = Part.Wire(edges)
329
        sqrBB = square.BoundBox
330
        minPoint = FreeCAD.Vector(sqrBB.XMin, sqrBB.YMin, 0.0)
331
        maxPoint = FreeCAD.Vector(sqrBB.XMax, sqrBB.YMax, 0.0)
332
        for c in adaptive.Path.Commands:
333
            if pathOriginatesInBox(c, minPoint, maxPoint):
334
                isInBox = True
335
                break
336
        self.assertFalse(isInBox, "Paths originating within the inner hole.")
337

338
    def test07(self):
339
        """test07() Verify path generated on donut-shaped Face10."""
340

341
        # Instantiate a Adaptive operation and set Base Geometry
342
        adaptive = PathAdaptive.Create("Adaptive")
343
        adaptive.Base = [(self.doc.Fusion, ["Face10"])]  # (base, subs_list)
344
        adaptive.Label = "test07+"
345
        adaptive.Comment = "test07() Verify path generated on donut-shaped Face10."
346

347
        # Set additional operation properties
348
        # setDepthsAndHeights(adaptive)
349
        adaptive.FinishingProfile = False
350
        adaptive.HelixAngle = 75.0
351
        adaptive.HelixDiameterLimit.Value = 1.0
352
        adaptive.LiftDistance.Value = 1.0
353
        adaptive.StepOver = 75
354
        adaptive.UseOutline = False
355
        adaptive.setExpression("StepDown", None)
356
        adaptive.StepDown.Value = (
357
            20.0  # Have to set expression to None before numerical value assignment
358
        )
359

360
        _addViewProvider(adaptive)
361
        self.doc.recompute()
362

363
        self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
364

365
        # Check if any paths originate inside inner hole of donut.  They should not.
366
        isInBox = False
367
        edges = [
368
            self.doc.Fusion.Shape.getElement(e) for e in ["Edge35", "Edge32", "Edge33", "Edge34"]
369
        ]
370
        square = Part.Wire(edges)
371
        sqrBB = square.BoundBox
372
        minPoint = FreeCAD.Vector(sqrBB.XMin, sqrBB.YMin, 0.0)
373
        maxPoint = FreeCAD.Vector(sqrBB.XMax, sqrBB.YMax, 0.0)
374
        for c in adaptive.Path.Commands:
375
            if pathOriginatesInBox(c, minPoint, maxPoint):
376
                isInBox = True
377
                break
378
        self.assertFalse(isInBox, "Paths originating within the inner hole.")
379

380
        # Set Adaptive op to only use the outline of the face.
381
        adaptive.UseOutline = True
382
        self.doc.recompute()
383

384
        # Check if any paths originate inside inner hole of donut.  They should not.
385
        isInBox = False
386
        edges = [
387
            self.doc.Fusion.Shape.getElement(e) for e in ["Edge35", "Edge32", "Edge33", "Edge34"]
388
        ]
389
        square = Part.Wire(edges)
390
        sqrBB = square.BoundBox
391
        minPoint = FreeCAD.Vector(sqrBB.XMin, sqrBB.YMin, 0.0)
392
        maxPoint = FreeCAD.Vector(sqrBB.XMax, sqrBB.YMax, 0.0)
393
        for c in adaptive.Path.Commands:
394
            if pathOriginatesInBox(c, minPoint, maxPoint):
395
                isInBox = True
396
                break
397
        self.assertTrue(isInBox, "No paths originating within the inner hole.")
398

399

400
# Eclass
401

402

403
def setDepthsAndHeights(op, strDep=20.0, finDep=0.0):
404
    """setDepthsAndHeights(op, strDep=20.0, finDep=0.0)... Sets default depths and heights for `op` passed to it"""
405

406
    # Set start and final depth in order to eliminate effects of stock (and its default values)
407
    op.setExpression("StartDepth", None)
408
    op.StartDepth.Value = strDep
409
    op.setExpression("FinalDepth", None)
410
    op.FinalDepth.Value = finDep
411

412
    # Set step down so as to only produce one layer path
413
    op.setExpression("StepDown", None)
414
    op.StepDown.Value = 20.0
415

416
    # Set Heights
417
    # default values used
418

419

420
def getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=True):
421
    """getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=True)...
422
    Accepts command dict and returns point string coordinate.
423
    """
424
    gcode_list = list()
425
    last = FreeCAD.Vector(0.0, 0.0, 0.0)
426
    for c in cmdList:
427
        p = c.Parameters
428
        name = c.Name
429
        if includeRapids and name in ["G0", "G00"]:
430
            gcode = name
431
            x = last.x
432
            y = last.y
433
            z = last.z
434
            if p.get("X"):
435
                x = round(p["X"], 2)
436
                gcode += " X" + str(x)
437
            if p.get("Y"):
438
                y = round(p["Y"], 2)
439
                gcode += " Y" + str(y)
440
            if p.get("Z"):
441
                z = round(p["Z"], 2)
442
                gcode += " Z" + str(z)
443
            last.x = x
444
            last.y = y
445
            last.z = z
446
            gcode_list.append(gcode)
447
        elif includeLines and name in ["G1", "G01"]:
448
            gcode = name
449
            x = last.x
450
            y = last.y
451
            z = last.z
452
            if p.get("X"):
453
                x = round(p["X"], 2)
454
                gcode += " X" + str(x)
455
            if p.get("Y"):
456
                y = round(p["Y"], 2)
457
                gcode += " Y" + str(y)
458
            if p.get("Z"):
459
                z = round(p["Z"], 2)
460
                gcode += " Z" + str(z)
461
            last.x = x
462
            last.y = y
463
            last.z = z
464
            gcode_list.append(gcode)
465
        elif includeArcs and name in ["G2", "G3", "G02", "G03"]:
466
            gcode = name
467
            x = last.x
468
            y = last.y
469
            z = last.z
470
            i = 0.0
471
            j = 0.0
472
            k = 0.0
473
            if p.get("I"):
474
                i = round(p["I"], 2)
475
            gcode += " I" + str(i)
476
            if p.get("J"):
477
                j = round(p["J"], 2)
478
            gcode += " J" + str(j)
479
            if p.get("K"):
480
                k = round(p["K"], 2)
481
            gcode += " K" + str(k)
482

483
            if p.get("X"):
484
                x = round(p["X"], 2)
485
            gcode += " X" + str(x)
486
            if p.get("Y"):
487
                y = round(p["Y"], 2)
488
            gcode += " Y" + str(y)
489
            if p.get("Z"):
490
                z = round(p["Z"], 2)
491
            gcode += " Z" + str(z)
492

493
            gcode_list.append(gcode)
494
            last.x = x
495
            last.y = y
496
            last.z = z
497
    return gcode_list
498

499

500
def pathOriginatesInBox(cmd, minPoint, maxPoint):
501
    p = cmd.Parameters
502
    name = cmd.Name
503
    if name in ["G0", "G00", "G1", "G01"]:
504
        if p.get("X") and p.get("Y"):
505
            x = p.get("X")
506
            y = p.get("Y")
507
            if x > minPoint.x and y > minPoint.y and x < maxPoint.x and y < maxPoint.y:
508
                return True
509
    return False
510

511

512
def _addViewProvider(adaptiveOp):
513
    if FreeCAD.GuiUp:
514
        PathOpGui = PathAdaptiveGui.PathOpGui
515
        cmdRes = PathAdaptiveGui.Command.res
516
        adaptiveOp.ViewObject.Proxy = PathOpGui.ViewProvider(adaptiveOp.ViewObject, cmdRes)
517

518

519
# Example string literal of expected path moves from an operation
520
# Expected moves for unit test01
521
expected_moves_test01 = "G1 X32.5 Y32.5 Z5.0;  \
522
G1 X17.5 Y32.5 Z5.0;  \
523
G1 X17.5 Y30.0 Z5.0;  \
524
G1 X32.5 Y30.0 Z5.0;  \
525
G1 X32.5 Y27.5 Z5.0;  \
526
G1 X17.5 Y27.5 Z5.0;  \
527
G1 X17.5 Y25.0 Z5.0;  \
528
G1 X32.5 Y25.0 Z5.0;  \
529
G1 X32.5 Y22.5 Z5.0;  \
530
G1 X17.5 Y22.5 Z5.0;  \
531
G1 X17.5 Y20.0 Z5.0;  \
532
G1 X32.5 Y20.0 Z5.0;  \
533
G1 X32.5 Y17.5 Z5.0;  \
534
G1 X17.5 Y17.5 Z5.0"
535

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

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

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

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