FreeCAD

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

23
import FreeCAD
24
import Part
25
import Path
26
import Path.Op.Util as PathOpUtil
27
import Tests.PathTestUtils as PathTestUtils
28
import math
29

30
from FreeCAD import Vector
31

32
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
33
# Path.Log.trackModule(Path.Log.thisModule())
34

35
DOC = FreeCAD.getHomePath() + "Mod/CAM/Tests/test_geomop.fcstd"
36

37

38
def getWire(obj, nr=0):
39
    return obj.Tip.Profile[0].Shape.Wires[nr]
40

41

42
def getWireInside(obj):
43
    w1 = getWire(obj, 0)
44
    w2 = getWire(obj, 1)
45
    if w2.BoundBox.isInside(w1.BoundBox):
46
        return w1
47
    return w2
48

49

50
def getWireOutside(obj):
51
    w1 = getWire(obj, 0)
52
    w2 = getWire(obj, 1)
53
    if w2.BoundBox.isInside(w1.BoundBox):
54
        return w2
55
    return w1
56

57

58
def getPositiveShape(obj):
59
    return obj.Tool.Shape
60

61

62
def getNegativeShape(obj):
63
    return obj.Shape
64

65

66
def makeWire(pts):
67
    edges = []
68
    first = pts[0]
69
    last = pts[0]
70
    for p in pts[1:]:
71
        edges.append(Part.Edge(Part.LineSegment(last, p)))
72
        last = p
73
    edges.append(Part.Edge(Part.LineSegment(last, first)))
74
    return Part.Wire(edges)
75

76

77
def wireMarkers(wire):
78
    pts = [wire.Edges[0].valueAt(wire.Edges[0].FirstParameter)]
79
    for edge in wire.Edges:
80
        pts.append(edge.valueAt(edge.LastParameter))
81
    return pts
82

83

84
class TestPathOpUtil(PathTestUtils.PathTestBase):
85
    @classmethod
86
    def setUpClass(cls):
87
        FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "True")
88
        cls.doc = FreeCAD.openDocument(DOC)
89
        FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "")
90

91
    @classmethod
92
    def tearDownClass(cls):
93
        FreeCAD.closeDocument(cls.doc.Name)
94

95
    def test00(self):
96
        """Verify isWireClockwise for polygon wires."""
97
        pa = Vector(1, 1, 0)
98
        pb = Vector(1, 5, 0)
99
        pc = Vector(5, 5, 0)
100
        pd = Vector(5, 1, 0)
101

102
        self.assertTrue(PathOpUtil.isWireClockwise(makeWire([pa, pb, pc, pd])))
103
        self.assertFalse(PathOpUtil.isWireClockwise(makeWire([pa, pd, pc, pb])))
104

105
    def test01(self):
106
        """Verify isWireClockwise for single edge circle wires."""
107
        self.assertTrue(
108
            PathOpUtil.isWireClockwise(Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1)))
109
        )
110
        self.assertFalse(
111
            PathOpUtil.isWireClockwise(Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1)))
112
        )
113

114
    def test02(self):
115
        """Verify isWireClockwise for two half circle wires."""
116
        e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180)
117
        e1 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 180, 360)
118
        self.assertTrue(PathOpUtil.isWireClockwise(Part.Wire([e0, e1])))
119

120
        e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180)
121
        e1 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 180, 360)
122
        self.assertFalse(PathOpUtil.isWireClockwise(Part.Wire([e0, e1])))
123

124
    def test03(self):
125
        """Verify isWireClockwise for two edge wires with an arc."""
126
        e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180)
127
        e2 = Part.makeLine(e0.valueAt(e0.LastParameter), e0.valueAt(e0.FirstParameter))
128
        self.assertTrue(PathOpUtil.isWireClockwise(Part.Wire([e0, e2])))
129

130
        e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180)
131
        e2 = Part.makeLine(e0.valueAt(e0.LastParameter), e0.valueAt(e0.FirstParameter))
132
        self.assertFalse(PathOpUtil.isWireClockwise(Part.Wire([e0, e2])))
133

134
    def test04(self):
135
        """Verify isWireClockwise for unoriented wires."""
136
        e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180)
137
        e3 = Part.makeLine(e0.valueAt(e0.FirstParameter), e0.valueAt(e0.LastParameter))
138
        self.assertTrue(PathOpUtil.isWireClockwise(Part.Wire([e0, e3])))
139

140
        e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180)
141
        e3 = Part.makeLine(e0.valueAt(e0.FirstParameter), e0.valueAt(e0.LastParameter))
142
        self.assertFalse(PathOpUtil.isWireClockwise(Part.Wire([e0, e3])))
143

144
    def test11(self):
145
        """Check offsetting a circular hole."""
146
        obj = self.doc.getObjectsByLabel("offset-circle")[0]
147

148
        small = getWireInside(obj)
149
        self.assertRoughly(10, small.Edges[0].Curve.Radius)
150

151
        wire = PathOpUtil.offsetWire(small, obj.Shape, 3, True)
152
        self.assertIsNotNone(wire)
153
        self.assertEqual(1, len(wire.Edges))
154
        self.assertRoughly(7, wire.Edges[0].Curve.Radius)
155
        self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis)
156

157
        wire = PathOpUtil.offsetWire(small, obj.Shape, 9.9, True)
158
        self.assertIsNotNone(wire)
159
        self.assertEqual(1, len(wire.Edges))
160
        self.assertRoughly(0.1, wire.Edges[0].Curve.Radius)
161
        self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis)
162

163
    def test12(self):
164
        """Check offsetting a circular hole by the radius or more makes the hole vanish."""
165
        obj = self.doc.getObjectsByLabel("offset-circle")[0]
166

167
        small = getWireInside(obj)
168
        self.assertRoughly(10, small.Edges[0].Curve.Radius)
169
        wire = PathOpUtil.offsetWire(small, obj.Shape, 10, True)
170
        self.assertIsNone(wire)
171

172
        wire = PathOpUtil.offsetWire(small, obj.Shape, 15, True)
173
        self.assertIsNone(wire)
174

175
    def test13(self):
176
        """Check offsetting a cylinder succeeds."""
177
        obj = self.doc.getObjectsByLabel("offset-circle")[0]
178

179
        big = getWireOutside(obj)
180
        self.assertRoughly(20, big.Edges[0].Curve.Radius)
181

182
        wire = PathOpUtil.offsetWire(big, obj.Shape, 10, True)
183
        self.assertIsNotNone(wire)
184
        self.assertEqual(1, len(wire.Edges))
185
        self.assertRoughly(30, wire.Edges[0].Curve.Radius)
186
        self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis)
187

188
        wire = PathOpUtil.offsetWire(big, obj.Shape, 20, True)
189
        self.assertIsNotNone(wire)
190
        self.assertEqual(1, len(wire.Edges))
191
        self.assertRoughly(40, wire.Edges[0].Curve.Radius)
192
        self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis)
193

194
    def test14(self):
195
        """Check offsetting a hole with Placement."""
196
        obj = self.doc.getObjectsByLabel("offset-placement")[0]
197

198
        wires = [
199
            w
200
            for w in obj.Shape.Wires
201
            if 1 == len(w.Edges) and Path.Geom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z)
202
        ]
203
        self.assertEqual(2, len(wires))
204
        w = wires[1] if wires[0].BoundBox.isInside(wires[1].BoundBox) else wires[0]
205

206
        self.assertRoughly(10, w.Edges[0].Curve.Radius)
207
        # make sure there is a placement and I didn't mess up the model
208
        self.assertFalse(Path.Geom.pointsCoincide(Vector(), w.Edges[0].Placement.Base))
209

210
        wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True)
211
        self.assertIsNotNone(wire)
212
        self.assertEqual(1, len(wire.Edges))
213
        self.assertRoughly(8, wire.Edges[0].Curve.Radius)
214
        self.assertCoincide(Vector(0, 0, 0), wire.Edges[0].Curve.Center)
215
        self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis)
216

217
    def test15(self):
218
        """Check offsetting a cylinder with Placement."""
219
        obj = self.doc.getObjectsByLabel("offset-placement")[0]
220

221
        wires = [
222
            w
223
            for w in obj.Shape.Wires
224
            if 1 == len(w.Edges) and Path.Geom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z)
225
        ]
226
        self.assertEqual(2, len(wires))
227
        w = wires[0] if wires[0].BoundBox.isInside(wires[1].BoundBox) else wires[1]
228

229
        self.assertRoughly(20, w.Edges[0].Curve.Radius)
230
        # make sure there is a placement and I didn't mess up the model
231
        self.assertFalse(Path.Geom.pointsCoincide(Vector(), w.Edges[0].Placement.Base))
232

233
        wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True)
234
        self.assertIsNotNone(wire)
235
        self.assertEqual(1, len(wire.Edges))
236
        self.assertRoughly(22, wire.Edges[0].Curve.Radius)
237
        self.assertCoincide(Vector(0, 0, 0), wire.Edges[0].Curve.Center)
238
        self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis)
239

240
    def test20(self):
241
        """Check offsetting hole wire succeeds."""
242
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
243

244
        small = getWireInside(obj)
245
        # sanity check
246
        y = 10
247
        x = 10 * math.cos(math.pi / 6)
248
        self.assertLines(
249
            small.Edges,
250
            False,
251
            [
252
                Vector(0, y, 0),
253
                Vector(-x, -y / 2, 0),
254
                Vector(x, -y / 2, 0),
255
                Vector(0, y, 0),
256
            ],
257
        )
258

259
        wire = PathOpUtil.offsetWire(small, obj.Shape, 3, True)
260
        self.assertIsNotNone(wire)
261
        self.assertEqual(3, len(wire.Edges))
262
        self.assertTrue(wire.isClosed())
263
        # for holes processing "forward" means CCW
264
        self.assertFalse(PathOpUtil.isWireClockwise(wire))
265
        y = 4  # offset works in both directions
266
        x = 4 * math.cos(math.pi / 6)
267
        self.assertLines(
268
            wire.Edges,
269
            False,
270
            [Vector(0, 4, 0), Vector(-x, -2, 0), Vector(x, -2, 0), Vector(0, 4, 0)],
271
        )
272

273
    def test21(self):
274
        """Check offsetting hole wire for more than it's size makes hole vanish."""
275
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
276

277
        small = getWireInside(obj)
278
        # sanity check
279
        y = 10
280
        x = 10 * math.cos(math.pi / 6)
281
        self.assertLines(
282
            small.Edges,
283
            False,
284
            [
285
                Vector(0, y, 0),
286
                Vector(-x, -y / 2, 0),
287
                Vector(x, -y / 2, 0),
288
                Vector(0, y, 0),
289
            ],
290
        )
291
        wire = PathOpUtil.offsetWire(small, obj.Shape, 5, True)
292
        self.assertIsNone(wire)
293

294
    def test22(self):
295
        """Check offsetting a body wire succeeds."""
296
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
297

298
        big = getWireOutside(obj)
299
        # sanity check
300
        y = 20
301
        x = 20 * math.cos(math.pi / 6)
302
        self.assertLines(
303
            big.Edges,
304
            False,
305
            [
306
                Vector(0, y, 0),
307
                Vector(-x, -y / 2, 0),
308
                Vector(x, -y / 2, 0),
309
                Vector(0, y, 0),
310
            ],
311
        )
312

313
        wire = PathOpUtil.offsetWire(big, obj.Shape, 5, True)
314
        self.assertIsNotNone(wire)
315
        self.assertEqual(6, len(wire.Edges))
316
        lastAngle = None
317
        refAngle = math.pi / 3
318
        for e in wire.Edges:
319
            if Part.Circle == type(e.Curve):
320
                self.assertRoughly(5, e.Curve.Radius)
321
                self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
322
            else:
323
                self.assertRoughly(34.641, e.Length, 0.001)
324
                begin = e.Vertexes[0].Point
325
                end = e.Vertexes[1].Point
326
                v = end - begin
327
                angle = Path.Geom.getAngle(v)
328
                if Path.Geom.isRoughly(0, angle) or Path.Geom.isRoughly(math.pi, math.fabs(angle)):
329
                    if lastAngle:
330
                        self.assertRoughly(-refAngle, lastAngle)
331
                elif Path.Geom.isRoughly(+refAngle, angle):
332
                    if lastAngle:
333
                        self.assertRoughly(math.pi, math.fabs(lastAngle))
334
                elif Path.Geom.isRoughly(-refAngle, angle):
335
                    if lastAngle:
336
                        self.assertRoughly(+refAngle, lastAngle)
337
                else:
338
                    self.assertIsNone("%s: angle=%s" % (type(e.Curve), angle))
339
                lastAngle = angle
340
        self.assertTrue(PathOpUtil.isWireClockwise(wire))
341

342
    def test31(self):
343
        """Check offsetting a cylinder."""
344
        obj = self.doc.getObjectsByLabel("circle-cut")[0]
345

346
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True)
347
        self.assertEqual(1, len(wire.Edges))
348
        edge = wire.Edges[0]
349
        self.assertCoincide(Vector(), edge.Curve.Center)
350
        self.assertCoincide(Vector(0, 0, -1), edge.Curve.Axis)
351
        self.assertRoughly(33, edge.Curve.Radius)
352

353
        # the other way around everything's the same except the axis is negative
354
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
355
        self.assertEqual(1, len(wire.Edges))
356
        edge = wire.Edges[0]
357
        self.assertCoincide(Vector(), edge.Curve.Center)
358
        self.assertCoincide(Vector(0, 0, +1), edge.Curve.Axis)
359
        self.assertRoughly(33, edge.Curve.Radius)
360

361
    def test32(self):
362
        """Check offsetting a box."""
363
        obj = self.doc.getObjectsByLabel("square-cut")[0]
364

365
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True)
366
        self.assertEqual(8, len(wire.Edges))
367
        self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
368
        self.assertEqual(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
369
        for e in wire.Edges:
370
            if Part.Line == type(e.Curve):
371
                if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
372
                    self.assertEqual(40, e.Length)
373
                if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
374
                    self.assertEqual(60, e.Length)
375
            if Part.Circle == type(e.Curve):
376
                self.assertRoughly(3, e.Curve.Radius)
377
                self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
378
        self.assertTrue(PathOpUtil.isWireClockwise(wire))
379

380
        # change offset orientation
381
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
382
        self.assertEqual(8, len(wire.Edges))
383
        self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
384
        self.assertEqual(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
385
        for e in wire.Edges:
386
            if Part.Line == type(e.Curve):
387
                if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
388
                    self.assertEqual(40, e.Length)
389
                if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
390
                    self.assertEqual(60, e.Length)
391
            if Part.Circle == type(e.Curve):
392
                self.assertRoughly(3, e.Curve.Radius)
393
                self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
394
        self.assertFalse(PathOpUtil.isWireClockwise(wire))
395

396
    def test33(self):
397
        """Check offsetting a triangle."""
398
        obj = self.doc.getObjectsByLabel("triangle-cut")[0]
399

400
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True)
401
        self.assertEqual(6, len(wire.Edges))
402
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
403
        self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
404
        length = 60 * math.sin(math.radians(60))
405
        for e in wire.Edges:
406
            if Part.Line == type(e.Curve):
407
                self.assertRoughly(length, e.Length)
408
            if Part.Circle == type(e.Curve):
409
                self.assertRoughly(3, e.Curve.Radius)
410
                self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
411

412
        # change offset orientation
413
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
414
        self.assertEqual(6, len(wire.Edges))
415
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
416
        self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
417
        for e in wire.Edges:
418
            if Part.Line == type(e.Curve):
419
                self.assertRoughly(length, e.Length)
420
            if Part.Circle == type(e.Curve):
421
                self.assertRoughly(3, e.Curve.Radius)
422
                self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
423

424
    def test34(self):
425
        """Check offsetting a shape."""
426
        obj = self.doc.getObjectsByLabel("shape-cut")[0]
427

428
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True)
429
        self.assertEqual(6, len(wire.Edges))
430
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
431
        self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
432
        length = 40
433
        radius = 20 + 3
434
        for e in wire.Edges:
435
            if Part.Line == type(e.Curve):
436
                self.assertRoughly(length, e.Length)
437
            if Part.Circle == type(e.Curve):
438
                self.assertRoughly(radius, e.Curve.Radius)
439
                self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
440

441
        # change offset orientation
442
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
443
        self.assertEqual(6, len(wire.Edges))
444
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
445
        self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
446
        for e in wire.Edges:
447
            if Part.Line == type(e.Curve):
448
                self.assertRoughly(length, e.Length)
449
            if Part.Circle == type(e.Curve):
450
                self.assertRoughly(radius, e.Curve.Radius)
451
                self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
452

453
    def test35(self):
454
        """Check offsetting a cylindrical hole."""
455
        obj = self.doc.getObjectsByLabel("circle-cut")[0]
456

457
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True)
458
        self.assertEqual(1, len(wire.Edges))
459
        edge = wire.Edges[0]
460
        self.assertCoincide(Vector(), edge.Curve.Center)
461
        self.assertCoincide(Vector(0, 0, +1), edge.Curve.Axis)
462
        self.assertRoughly(27, edge.Curve.Radius)
463

464
        # the other way around everything's the same except the axis is negative
465
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
466
        self.assertEqual(1, len(wire.Edges))
467
        edge = wire.Edges[0]
468
        self.assertCoincide(Vector(), edge.Curve.Center)
469
        self.assertCoincide(Vector(0, 0, -1), edge.Curve.Axis)
470
        self.assertRoughly(27, edge.Curve.Radius)
471

472
    def test36(self):
473
        """Check offsetting a square hole."""
474
        obj = self.doc.getObjectsByLabel("square-cut")[0]
475

476
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True)
477
        self.assertEqual(4, len(wire.Edges))
478
        self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
479
        for e in wire.Edges:
480
            if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
481
                self.assertRoughly(34, e.Length)
482
            if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
483
                self.assertRoughly(54, e.Length)
484
        self.assertFalse(PathOpUtil.isWireClockwise(wire))
485

486
        # change offset orientation
487
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
488
        self.assertEqual(4, len(wire.Edges))
489
        self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
490
        for e in wire.Edges:
491
            if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
492
                self.assertRoughly(34, e.Length)
493
            if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
494
                self.assertRoughly(54, e.Length)
495
        self.assertTrue(PathOpUtil.isWireClockwise(wire))
496

497
    def test37(self):
498
        """Check offsetting a triangular holee."""
499
        obj = self.doc.getObjectsByLabel("triangle-cut")[0]
500

501
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True)
502
        self.assertEqual(3, len(wire.Edges))
503
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
504
        length = 48 * math.sin(math.radians(60))
505
        for e in wire.Edges:
506
            self.assertRoughly(length, e.Length)
507
        self.assertFalse(PathOpUtil.isWireClockwise(wire))
508

509
        # change offset orientation
510
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
511
        self.assertEqual(3, len(wire.Edges))
512
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
513
        for e in wire.Edges:
514
            self.assertRoughly(length, e.Length)
515
        self.assertTrue(PathOpUtil.isWireClockwise(wire))
516

517
    def test38(self):
518
        """Check offsetting a shape hole."""
519
        obj = self.doc.getObjectsByLabel("shape-cut")[0]
520

521
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True)
522
        self.assertEqual(6, len(wire.Edges))
523
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
524
        self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
525
        length = 40
526
        radius = 20 - 3
527
        for e in wire.Edges:
528
            if Part.Line == type(e.Curve):
529
                self.assertRoughly(length, e.Length)
530
            if Part.Circle == type(e.Curve):
531
                self.assertRoughly(radius, e.Curve.Radius)
532
                self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
533

534
        # change offset orientation
535
        wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
536
        self.assertEqual(6, len(wire.Edges))
537
        self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
538
        self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
539
        for e in wire.Edges:
540
            if Part.Line == type(e.Curve):
541
                self.assertRoughly(length, e.Length)
542
            if Part.Circle == type(e.Curve):
543
                self.assertRoughly(radius, e.Curve.Radius)
544
                self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
545

546
    def test40(self):
547
        """Check offsetting a single outside edge forward."""
548
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
549

550
        w = getWireOutside(obj)
551
        length = 40 * math.cos(math.pi / 6)
552
        for e in w.Edges:
553
            self.assertRoughly(length, e.Length)
554

555
        # let's offset the horizontal edge for starters
556
        hEdges = [
557
            e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
558
        ]
559

560
        x = length / 2
561
        y = -10
562
        self.assertEqual(1, len(hEdges))
563
        edge = hEdges[0]
564

565
        self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point)
566
        self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point)
567

568
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, True)
569
        self.assertEqual(1, len(wire.Edges))
570

571
        y = y - 5
572
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
573
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point)
574

575
        # make sure we get the same result even if the edge is oriented the other way
576
        edge = Path.Geom.flipEdge(edge)
577
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, True)
578
        self.assertEqual(1, len(wire.Edges))
579

580
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
581
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point)
582

583
    def test41(self):
584
        """Check offsetting a single outside edge not forward."""
585
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
586

587
        w = getWireOutside(obj)
588
        length = 40 * math.cos(math.pi / 6)
589
        for e in w.Edges:
590
            self.assertRoughly(length, e.Length)
591

592
        # let's offset the horizontal edge for starters
593
        hEdges = [
594
            e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
595
        ]
596

597
        x = length / 2
598
        y = -10
599
        self.assertEqual(1, len(hEdges))
600
        edge = hEdges[0]
601
        self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point)
602
        self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point)
603

604
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, False)
605
        self.assertEqual(1, len(wire.Edges))
606

607
        y = y - 5
608
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
609
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point)
610

611
        # make sure we get the same result on a reversed edge
612
        edge = Path.Geom.flipEdge(edge)
613
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, False)
614
        self.assertEqual(1, len(wire.Edges))
615

616
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
617
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point)
618

619
    def test42(self):
620
        """Check offsetting multiple outside edges."""
621
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
622
        obj.Shape.tessellate(0.01)
623
        self.doc.recompute()
624

625
        w = getWireOutside(obj)
626
        length = 40 * math.cos(math.pi / 6)
627

628
        # let's offset the other two legs
629
        lEdges = [
630
            e
631
            for e in w.Edges
632
            if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
633
        ]
634
        self.assertEqual(2, len(lEdges))
635

636
        wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, True)
637

638
        x = length / 2 + 2 * math.cos(math.pi / 6)
639
        y = -10 + 2 * math.sin(math.pi / 6)
640

641
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
642
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point)
643

644
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
645

646
        self.assertEqual(1, len(rEdges))
647
        self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center)
648
        self.assertCoincide(Vector(0, 0, -1), rEdges[0].Curve.Axis)
649

650
        # offset the other way
651
        wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False)
652

653
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
654
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point)
655

656
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
657

658
        self.assertEqual(1, len(rEdges))
659
        self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center)
660
        self.assertCoincide(Vector(0, 0, +1), rEdges[0].Curve.Axis)
661

662
    def test43(self):
663
        """Check offsetting multiple backwards outside edges."""
664
        # This is exactly the same as test32, except that the wire is flipped to make
665
        # sure the input orientation doesn't matter
666
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
667

668
        w = getWireOutside(obj)
669
        length = 40 * math.cos(math.pi / 6)
670

671
        # let's offset the other two legs
672
        lEdges = [
673
            e
674
            for e in w.Edges
675
            if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
676
        ]
677
        self.assertEqual(2, len(lEdges))
678

679
        w = Path.Geom.flipWire(Part.Wire(lEdges))
680
        wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True)
681

682
        x = length / 2 + 2 * math.cos(math.pi / 6)
683
        y = -10 + 2 * math.sin(math.pi / 6)
684

685
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
686
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point)
687

688
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
689

690
        self.assertEqual(1, len(rEdges))
691
        self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center)
692
        self.assertCoincide(Vector(0, 0, -1), rEdges[0].Curve.Axis)
693

694
        # offset the other way
695
        wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False)
696

697
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
698
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point)
699

700
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
701

702
        self.assertEqual(1, len(rEdges))
703
        self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center)
704
        self.assertCoincide(Vector(0, 0, +1), rEdges[0].Curve.Axis)
705

706
    def test44(self):
707
        """Check offsetting a single inside edge forward."""
708
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
709

710
        w = getWireInside(obj)
711
        length = 20 * math.cos(math.pi / 6)
712
        for e in w.Edges:
713
            self.assertRoughly(length, e.Length)
714

715
        # let's offset the horizontal edge for starters
716
        hEdges = [
717
            e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
718
        ]
719

720
        x = length / 2
721
        y = -5
722
        self.assertEqual(1, len(hEdges))
723
        edge = hEdges[0]
724

725
        self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point)
726
        self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point)
727

728
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, True)
729
        self.assertEqual(1, len(wire.Edges))
730

731
        y = y + 2
732
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
733
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point)
734

735
        # make sure we get the same result even if the edge is oriented the other way
736
        edge = Path.Geom.flipEdge(edge)
737
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, True)
738
        self.assertEqual(1, len(wire.Edges))
739

740
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
741
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point)
742

743
    def test45(self):
744
        """Check offsetting a single inside edge not forward."""
745
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
746

747
        w = getWireInside(obj)
748
        length = 20 * math.cos(math.pi / 6)
749
        for e in w.Edges:
750
            self.assertRoughly(length, e.Length)
751

752
        # let's offset the horizontal edge for starters
753
        hEdges = [
754
            e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
755
        ]
756

757
        x = length / 2
758
        y = -5
759
        self.assertEqual(1, len(hEdges))
760
        edge = hEdges[0]
761

762
        self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point)
763
        self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point)
764

765
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, False)
766
        self.assertEqual(1, len(wire.Edges))
767

768
        y = y + 2
769
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
770
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point)
771

772
        # make sure we get the same result even if the edge is oriented the other way
773
        edge = Path.Geom.flipEdge(edge)
774
        wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, False)
775
        self.assertEqual(1, len(wire.Edges))
776

777
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
778
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point)
779

780
    def test46(self):
781
        """Check offsetting multiple inside edges."""
782
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
783

784
        w = getWireInside(obj)
785
        length = 20 * math.cos(math.pi / 6)
786

787
        # let's offset the other two legs
788
        lEdges = [
789
            e
790
            for e in w.Edges
791
            if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
792
        ]
793
        self.assertEqual(2, len(lEdges))
794

795
        wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, True)
796

797
        x = length / 2 - 2 * math.cos(math.pi / 6)
798
        y = -5 - 2 * math.sin(math.pi / 6)
799

800
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
801
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point)
802

803
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
804
        self.assertEqual(0, len(rEdges))
805

806
        # offset the other way
807
        wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False)
808

809
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
810
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point)
811

812
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
813
        self.assertEqual(0, len(rEdges))
814

815
    def test47(self):
816
        """Check offsetting multiple backwards inside edges."""
817
        # This is exactly the same as test36 except that the wire is flipped to make
818
        # sure it's orientation doesn't matter
819
        obj = self.doc.getObjectsByLabel("offset-edge")[0]
820

821
        w = getWireInside(obj)
822
        length = 20 * math.cos(math.pi / 6)
823

824
        # let's offset the other two legs
825
        lEdges = [
826
            e
827
            for e in w.Edges
828
            if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y)
829
        ]
830
        self.assertEqual(2, len(lEdges))
831

832
        w = Path.Geom.flipWire(Part.Wire(lEdges))
833
        wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True)
834

835
        x = length / 2 - 2 * math.cos(math.pi / 6)
836
        y = -5 - 2 * math.sin(math.pi / 6)
837

838
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point)
839
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point)
840

841
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
842
        self.assertEqual(0, len(rEdges))
843

844
        # offset the other way
845
        wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False)
846

847
        self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point)
848
        self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point)
849

850
        rEdges = [e for e in wire.Edges if Part.Circle == type(e.Curve)]
851
        self.assertEqual(0, len(rEdges))
852

853
    def test50(self):
854
        """Orient an already oriented wire"""
855
        p0 = Vector()
856
        p1 = Vector(1, 2, 3)
857
        p2 = Vector(2, 3, 4)
858
        pts = [p0, p1, p2]
859

860
        e0 = Part.Edge(Part.LineSegment(p0, p1))
861
        e1 = Part.Edge(Part.LineSegment(p1, p2))
862

863
        wire = PathOpUtil.orientWire(Part.Wire([e0, e1]))
864
        wirePts = wireMarkers(wire)
865

866
        self.assertPointsMatch(wirePts, pts)
867

868
    def test51(self):
869
        """Orient a potentially misoriented wire"""
870
        p0 = Vector()
871
        p1 = Vector(1, 2, 3)
872
        p2 = Vector(2, 3, 4)
873
        pts = [p0, p1, p2]
874

875
        e0p = Part.Edge(Part.LineSegment(p0, p1))
876
        e0m = Part.Edge(Part.LineSegment(p1, p0))
877
        e1p = Part.Edge(Part.LineSegment(p1, p2))
878
        e1m = Part.Edge(Part.LineSegment(p2, p1))
879

880
        wire = PathOpUtil.orientWire(Part.Wire([e0p, e1p]))
881
        self.assertPointsMatch(wireMarkers(wire), pts)
882

883
        wire = PathOpUtil.orientWire(Part.Wire([e0p, e1m]))
884
        self.assertPointsMatch(wireMarkers(wire), pts)
885

886
        wire = PathOpUtil.orientWire(Part.Wire([e0m, e1p]))
887
        self.assertPointsMatch(wireMarkers(wire), pts)
888

889
        wire = PathOpUtil.orientWire(Part.Wire([e0m, e1m]))
890
        self.assertPointsMatch(wireMarkers(wire), pts)
891

892
    def test52(self):
893
        """Orient a potentially misoriented longer wire"""
894
        p0 = Vector()
895
        p1 = Vector(1, 2, 3)
896
        p2 = Vector(4, 5, 6)
897
        p3 = Vector(7, 8, 9)
898
        pts = [p0, p1, p2, p3]
899

900
        e0p = Part.Edge(Part.LineSegment(p0, p1))
901
        e0m = Part.Edge(Part.LineSegment(p1, p0))
902
        e1p = Part.Edge(Part.LineSegment(p1, p2))
903
        e1m = Part.Edge(Part.LineSegment(p2, p1))
904
        e2p = Part.Edge(Part.LineSegment(p2, p3))
905
        e2m = Part.Edge(Part.LineSegment(p3, p2))
906

907
        wire = PathOpUtil.orientWire(Part.Wire([e0p, e1p, e2p]))
908
        self.assertPointsMatch(wireMarkers(wire), pts)
909

910
        wire = PathOpUtil.orientWire(Part.Wire([e0p, e1m, e2p]))
911
        self.assertPointsMatch(wireMarkers(wire), pts)
912

913
        wire = PathOpUtil.orientWire(Part.Wire([e0m, e1p, e2p]))
914
        self.assertPointsMatch(wireMarkers(wire), pts)
915

916
        wire = PathOpUtil.orientWire(Part.Wire([e0m, e1m, e2p]))
917
        self.assertPointsMatch(wireMarkers(wire), pts)
918

919
        wire = PathOpUtil.orientWire(Part.Wire([e0p, e1p, e2m]))
920
        self.assertPointsMatch(wireMarkers(wire), pts)
921

922
        wire = PathOpUtil.orientWire(Part.Wire([e0p, e1m, e2m]))
923
        self.assertPointsMatch(wireMarkers(wire), pts)
924

925
        wire = PathOpUtil.orientWire(Part.Wire([e0m, e1p, e2m]))
926
        self.assertPointsMatch(wireMarkers(wire), pts)
927

928
        wire = PathOpUtil.orientWire(Part.Wire([e0m, e1m, e2m]))
929
        self.assertPointsMatch(wireMarkers(wire), pts)
930

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

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

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

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