FreeCAD

Форк
0
/
gui_stretch.py 
483 строки · 24.0 Кб
1
# ***************************************************************************
2
# *   (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net>                  *
3
# *   (c) 2009, 2010 Ken Cline <cline@frii.com>                             *
4
# *   (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de>           *
5
# *                                                                         *
6
# *   This file is part of the FreeCAD CAx development system.              *
7
# *                                                                         *
8
# *   This program is free software; you can redistribute it and/or modify  *
9
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
10
# *   as published by the Free Software Foundation; either version 2 of     *
11
# *   the License, or (at your option) any later version.                   *
12
# *   for detail see the LICENCE text file.                                 *
13
# *                                                                         *
14
# *   FreeCAD is distributed in the hope that it will be useful,            *
15
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
16
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
17
# *   GNU Library General Public License for more details.                  *
18
# *                                                                         *
19
# *   You should have received a copy of the GNU Library General Public     *
20
# *   License along with FreeCAD; if not, write to the Free Software        *
21
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
22
# *   USA                                                                   *
23
# *                                                                         *
24
# ***************************************************************************
25
"""Provides GUI tools to stretch Draft objects.
26

27
It works with rectangles, wires, b-splines, bezier curves, and sketches.
28
It essentially moves the points that are located within a selection area,
29
while keeping other points intact. This means the lines tied by the points
30
that were moved are 'stretched'.
31
"""
32
## @package gui_stretch
33
# \ingroup draftguitools
34
# \brief Provides GUI tools to stretch Draft objects.
35

36
## \addtogroup draftguitools
37
# @{
38
from PySide.QtCore import QT_TRANSLATE_NOOP
39

40
import FreeCAD as App
41
import FreeCADGui as Gui
42
import Draft_rc
43
import DraftVecUtils
44
import draftutils.utils as utils
45
import draftguitools.gui_base_original as gui_base_original
46
import draftguitools.gui_tool_utils as gui_tool_utils
47
import draftguitools.gui_trackers as trackers
48

49
from draftutils.messages import _msg, _toolmsg
50
from draftutils.translate import translate
51

52
# The module is used to prevent complaints from code checkers (flake8)
53
True if Draft_rc.__name__ else False
54

55

56
class Stretch(gui_base_original.Modifier):
57
    """Gui Command for the Stretch tool."""
58

59
    def GetResources(self):
60
        """Set icon, menu and tooltip."""
61

62
        return {'Pixmap': 'Draft_Stretch',
63
                'Accel': "S, H",
64
                'MenuText': QT_TRANSLATE_NOOP("Draft_Stretch", "Stretch"),
65
                'ToolTip': QT_TRANSLATE_NOOP("Draft_Stretch", "Stretches the selected objects.\nSelect an object, then draw a rectangle to pick the vertices that will be stretched,\nthen draw a line to specify the distance and direction of stretching.")}
66

67
    def Activated(self):
68
        """Execute when the command is called."""
69
        super().Activated(name="Stretch")
70
        self.rectracker = None
71
        self.nodetracker = None
72
        if self.ui:
73
            if not Gui.Selection.getSelection():
74
                self.ui.selectUi(on_close_call=self.finish)
75
                _msg(translate("draft", "Select an object to stretch"))
76
                self.call = \
77
                    self.view.addEventCallback("SoEvent",
78
                                               gui_tool_utils.selectObject)
79
            else:
80
                self.proceed()
81

82
    def proceed(self):
83
        """Proceed with execution of the command after proper selection."""
84
        if self.call:
85
            self.view.removeEventCallback("SoEvent", self.call)
86
        supported = ["Rectangle", "Wire", "BSpline", "BezCurve", "Sketch"]
87
        self.sel = []
88
        for obj in Gui.Selection.getSelection():
89
            if utils.getType(obj) in supported:
90
                self.sel.append([obj, App.Placement()])
91
            elif hasattr(obj, "Base"):
92
                if obj.Base:
93
                    if utils.getType(obj.Base) in supported:
94
                        self.sel.append([obj.Base, obj.Placement])
95
                    elif utils.getType(obj.Base) in ["Offset2D", "Array"]:
96
                        base = None
97
                        if hasattr(obj.Base, "Source") and obj.Base.Source:
98
                            base = obj.Base.Source
99
                        elif hasattr(obj.Base, "Base") and obj.Base.Base:
100
                            base = obj.Base.Base
101
                        if base:
102
                            if utils.getType(base) in supported:
103
                                self.sel.append([base, obj.Placement.multiply(obj.Base.Placement)])
104
                    elif hasattr(obj.Base, "Base"):
105
                        if obj.Base.Base:
106
                            if utils.getType(obj.Base.Base) in supported:
107
                                self.sel.append([obj.Base.Base, obj.Placement.multiply(obj.Base.Placement)])
108
            elif utils.getType(obj) in ["Offset2D", "Array"]:
109
                base = None
110
                if hasattr(obj, "Source") and obj.Source:
111
                    base = obj.Source
112
                elif hasattr(obj, "Base") and obj.Base:
113
                    base = obj.Base
114
                if base:
115
                    if utils.getType(base) in supported:
116
                        self.sel.append([base, obj.Placement])
117
        if self.ui and self.sel:
118
            self.step = 1
119
            self.refpoint = None
120
            self.ui.pointUi(title=translate("draft", self.featureName), icon="Draft_Stretch")
121
            self.call = self.view.addEventCallback("SoEvent", self.action)
122
            self.rectracker = trackers.rectangleTracker(dotted=True,
123
                                                        scolor=(0.0, 0.0, 1.0),
124
                                                        swidth=2)
125
            self.nodetracker = []
126
            self.displacement = None
127
            _toolmsg(translate("draft", "Pick first point of selection rectangle"))
128

129
    def action(self, arg):
130
        """Handle the 3D scene events.
131

132
        This is installed as an EventCallback in the Inventor view.
133

134
        Parameters
135
        ----------
136
        arg: dict
137
            Dictionary with strings that indicates the type of event received
138
            from the 3D view.
139
        """
140
        if arg["Type"] == "SoKeyboardEvent":
141
            if arg["Key"] == "ESCAPE":
142
                self.finish()
143
        elif arg["Type"] == "SoLocation2Event":  # mouse movement detection
144
            point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
145
            if self.step == 2:
146
                self.rectracker.update(point)
147
            gui_tool_utils.redraw3DView()
148
        elif arg["Type"] == "SoMouseButtonEvent":
149
            if arg["State"] == "DOWN" and arg["Button"] == "BUTTON1":
150
                if arg["Position"] == self.pos:
151
                    # clicked twice on the same point
152
                    self.finish()
153
                else:
154
                    point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
155
                    self.addPoint(point)
156

157
    def addPoint(self, point):
158
        """Add point to defined selection rectangle."""
159
        if self.step == 1:
160
            # first rctangle point
161
            _toolmsg(translate("draft", "Pick opposite point "
162
                                    "of selection rectangle"))
163
            self.ui.setRelative(-1)
164
            self.rectracker.setorigin(point)
165
            self.rectracker.on()
166
            if self.planetrack:
167
                self.planetrack.set(point)
168
            self.step = 2
169
        elif self.step == 2:
170
            # second rectangle point
171
            _toolmsg(translate("draft", "Pick start point of displacement"))
172
            self.ui.setRelative(-2)
173
            self.rectracker.off()
174
            nodes = []
175
            self.ops = []
176
            for sel in self.sel:
177
                o = sel[0]
178
                vispla = sel[1]
179
                tp = utils.getType(o)
180
                if tp in ["Wire", "BSpline", "BezCurve"]:
181
                    np = []
182
                    iso = False
183
                    for p in o.Points:
184
                        p = o.Placement.multVec(p)
185
                        p = vispla.multVec(p)
186
                        isi = self.rectracker.isInside(p)
187
                        np.append(isi)
188
                        if isi:
189
                            iso = True
190
                            nodes.append(p)
191
                    if iso:
192
                        self.ops.append([o, np])
193
                elif tp in ["Rectangle"]:
194
                    p1 = App.Vector(0, 0, 0)
195
                    p2 = App.Vector(o.Length.Value, 0, 0)
196
                    p3 = App.Vector(o.Length.Value, o.Height.Value, 0)
197
                    p4 = App.Vector(0, o.Height.Value, 0)
198
                    np = []
199
                    iso = False
200
                    for p in [p1, p2, p3, p4]:
201
                        p = o.Placement.multVec(p)
202
                        p = vispla.multVec(p)
203
                        isi = self.rectracker.isInside(p)
204
                        np.append(isi)
205
                        if isi:
206
                            iso = True
207
                            nodes.append(p)
208
                    if iso:
209
                        self.ops.append([o, np])
210
                elif tp in ["Sketch"]:
211
                    np = []
212
                    iso = False
213
                    for p in o.Shape.Vertexes:
214
                        p = vispla.multVec(p.Point)
215
                        isi = self.rectracker.isInside(p)
216
                        np.append(isi)
217
                        if isi:
218
                            iso = True
219
                            nodes.append(p)
220
                    if iso:
221
                        self.ops.append([o, np])
222
                else:
223
                    p = o.Placement.Base
224
                    p = vispla.multVec(p)
225
                    if self.rectracker.isInside(p):
226
                        self.ops.append([o])
227
                        nodes.append(p)
228
            for n in nodes:
229
                nt = trackers.editTracker(n, inactive=True)
230
                nt.on()
231
                self.nodetracker.append(nt)
232
            self.step = 3
233
        elif self.step == 3:
234
            # first point of displacement line
235
            _toolmsg(translate("draft", "Pick end point of displacement"))
236
            self.displacement = point
237
            # print("first point:", point)
238
            self.node = [point]
239
            self.step = 4
240
        elif self.step == 4:
241
            # print("second point:", point)
242
            self.displacement = point.sub(self.displacement)
243
            self.doStretch()
244
        if self.point:
245
            self.ui.redraw()
246

247
    def numericInput(self, numx, numy, numz):
248
        """Validate the entry fields in the user interface.
249

250
        This function is called by the toolbar or taskpanel interface
251
        when valid x, y, and z have been entered in the input fields.
252
        """
253
        point = App.Vector(numx, numy, numz)
254
        self.addPoint(point)
255

256
    def finish(self, cont=False):
257
        """Terminate the operation of the command. and clean up."""
258
        self.end_callbacks(self.call)
259
        if self.rectracker:
260
            self.rectracker.finalize()
261
        if self.nodetracker:
262
            for n in self.nodetracker:
263
                n.finalize()
264
        super().finish()
265

266
    def doStretch(self):
267
        """Do the actual stretching once the points are selected."""
268
        commitops = []
269
        if self.displacement:
270
            if self.displacement.Length > 0:
271
                _doc = "FreeCAD.ActiveDocument."
272
                # print("displacement: ", self.displacement)
273

274
                # TODO: break this section into individual functions
275
                # depending on the type of object (wire, curve, sketch,
276
                # rectangle, etc.) that is selected, and use variables
277
                # with common strings to avoid repeating
278
                # the same information every time, for example, the `_doc`
279
                # variable.
280
                # This is necessary to reduce the number of indentation levels
281
                # and make the code easier to read.
282
                for ops in self.ops:
283
                    tp = utils.getType(ops[0])
284
                    _rot = ops[0].Placement.Rotation
285
                    localdisp = _rot.inverted().multVec(self.displacement)
286
                    if tp in ["Wire", "BSpline", "BezCurve"]:
287
                        pts = []
288
                        for i in range(len(ops[1])):
289
                            if ops[1][i] is False:
290
                                pts.append(ops[0].Points[i])
291
                            else:
292
                                pts.append(ops[0].Points[i].add(localdisp))
293
                        pts = str(pts).replace("Vector ", "FreeCAD.Vector")
294
                        _cmd = _doc + ops[0].Name + ".Points=" + pts
295
                        commitops.append(_cmd)
296
                    elif tp in ["Sketch"]:
297
                        baseverts = [ops[0].Shape.Vertexes[i].Point for i in range(len(ops[1])) if ops[1][i]]
298
                        for i in range(ops[0].GeometryCount):
299
                            j = 0
300
                            while True:
301
                                try:
302
                                    p = ops[0].getPoint(i, j)
303
                                except ValueError:
304
                                    break
305
                                else:
306
                                    p = ops[0].Placement.multVec(p)
307
                                    r = None
308
                                    for bv in baseverts:
309
                                        if DraftVecUtils.isNull(p.sub(bv)):
310
                                            _cmd = _doc
311
                                            _cmd += ops[0].Name
312
                                            _cmd += ".movePoint"
313
                                            _cmd += "("
314
                                            _cmd += str(i) + ", "
315
                                            _cmd += str(j) + ", "
316
                                            _cmd += "FreeCAD." + str(localdisp) + ", "
317
                                            _cmd += "True"
318
                                            _cmd += ")"
319
                                            commitops.append(_cmd)
320
                                            r = bv
321
                                            break
322
                                    if r:
323
                                        baseverts.remove(r)
324
                                    j += 1
325
                    elif tp in ["Rectangle"]:
326
                        p1 = App.Vector(0, 0, 0)
327
                        p2 = App.Vector(ops[0].Length.Value, 0, 0)
328
                        p3 = App.Vector(ops[0].Length.Value,
329
                                        ops[0].Height.Value,
330
                                        0)
331
                        p4 = App.Vector(0, ops[0].Height.Value, 0)
332
                        if ops[1] == [False, True, True, False]:
333
                            optype = 1
334
                        elif ops[1] == [False, False, True, True]:
335
                            optype = 2
336
                        elif ops[1] == [True, False, False, True]:
337
                            optype = 3
338
                        elif ops[1] == [True, True, False, False]:
339
                            optype = 4
340
                        else:
341
                            optype = 0
342
                        # print("length:", ops[0].Length,
343
                        #       "height:", ops[0].Height,
344
                        #       " - ", ops[1],
345
                        #       " - ", self.displacement)
346
                        done = False
347
                        if optype > 0:
348
                            v1 = ops[0].Placement.multVec(p2).sub(ops[0].Placement.multVec(p1))
349
                            a1 = round(self.displacement.getAngle(v1), 4)
350
                            v2 = ops[0].Placement.multVec(p4).sub(ops[0].Placement.multVec(p1))
351
                            a2 = round(self.displacement.getAngle(v2), 4)
352
                            # check if the displacement is along one
353
                            # of the rectangle directions
354
                            if a1 == 0:  # 0 degrees
355
                                if optype == 1:
356
                                    if ops[0].Length.Value >= 0:
357
                                        d = ops[0].Length.Value + self.displacement.Length
358
                                    else:
359
                                        d = ops[0].Length.Value - self.displacement.Length
360
                                    _cmd = _doc
361
                                    _cmd += ops[0].Name + ".Length=" + str(d)
362
                                    commitops.append(_cmd)
363
                                    done = True
364
                                elif optype == 3:
365
                                    if ops[0].Length.Value >= 0:
366
                                        d = ops[0].Length.Value - self.displacement.Length
367
                                    else:
368
                                        d = ops[0].Length.Value + self.displacement.Length
369
                                    _cmd = _doc + ops[0].Name
370
                                    _cmd += ".Length=" + str(d)
371
                                    _pl = _doc + ops[0].Name
372
                                    _pl += ".Placement.Base=FreeCAD."
373
                                    _pl += str(ops[0].Placement.Base.add(self.displacement))
374
                                    commitops.append(_cmd)
375
                                    commitops.append(_pl)
376
                                    done = True
377
                            elif a1 == 3.1416:  # pi radians, 180 degrees
378
                                if optype == 1:
379
                                    if ops[0].Length.Value >= 0:
380
                                        d = ops[0].Length.Value - self.displacement.Length
381
                                    else:
382
                                        d = ops[0].Length.Value + self.displacement.Length
383
                                    _cmd = _doc + ops[0].Name
384
                                    _cmd += ".Length=" + str(d)
385
                                    commitops.append(_cmd)
386
                                    done = True
387
                                elif optype == 3:
388
                                    if ops[0].Length.Value >= 0:
389
                                        d = ops[0].Length.Value + self.displacement.Length
390
                                    else:
391
                                        d = ops[0].Length.Value - self.displacement.Length
392
                                    _cmd = _doc + ops[0].Name
393
                                    _cmd += ".Length=" + str(d)
394
                                    _pl = _doc + ops[0].Name
395
                                    _pl += ".Placement.Base=FreeCAD."
396
                                    _pl += str(ops[0].Placement.Base.add(self.displacement))
397
                                    commitops.append(_cmd)
398
                                    commitops.append(_pl)
399
                                    done = True
400
                            elif a2 == 0:  # 0 degrees
401
                                if optype == 2:
402
                                    if ops[0].Height.Value >= 0:
403
                                        d = ops[0].Height.Value + self.displacement.Length
404
                                    else:
405
                                        d = ops[0].Height.Value - self.displacement.Length
406
                                    _cmd = _doc + ops[0].Name
407
                                    _cmd += ".Height=" + str(d)
408
                                    commitops.append(_cmd)
409
                                    done = True
410
                                elif optype == 4:
411
                                    if ops[0].Height.Value >= 0:
412
                                        d = ops[0].Height.Value - self.displacement.Length
413
                                    else:
414
                                        d = ops[0].Height.Value + self.displacement.Length
415
                                    _cmd = _doc + ops[0].Name
416
                                    _cmd += ".Height=" + str(d)
417
                                    _pl = _doc + ops[0].Name
418
                                    _pl += ".Placement.Base=FreeCAD."
419
                                    _pl += str(ops[0].Placement.Base.add(self.displacement))
420
                                    commitops.append(_cmd)
421
                                    commitops.append(_pl)
422
                                    done = True
423
                            elif a2 == 3.1416:  # pi radians, 180 degrees
424
                                if optype == 2:
425
                                    if ops[0].Height.Value >= 0:
426
                                        d = ops[0].Height.Value - self.displacement.Length
427
                                    else:
428
                                        d = ops[0].Height.Value + self.displacement.Length
429
                                    _cmd = _doc + ops[0].Name
430
                                    _cmd += ".Height=" + str(d)
431
                                    commitops.append(_cmd)
432
                                    done = True
433
                                elif optype == 4:
434
                                    if ops[0].Height.Value >= 0:
435
                                        d = ops[0].Height.Value + self.displacement.Length
436
                                    else:
437
                                        d = ops[0].Height.Value - self.displacement.Length
438
                                    _cmd = _doc + ops[0].Name
439
                                    _cmd += ".Height=" + str(d)
440
                                    _pl = _doc + ops[0].Name
441
                                    _pl += ".Placement.Base=FreeCAD."
442
                                    _pl += str(ops[0].Placement.Base.add(self.displacement))
443
                                    commitops.append(_cmd)
444
                                    commitops.append(_pl)
445
                                    done = True
446
                        if not done:
447
                            # otherwise create a wire copy and stretch it instead
448
                            _msg(translate("draft", "Turning one Rectangle into a Wire"))
449
                            pts = []
450
                            vts = ops[0].Shape.Vertexes
451
                            for i in range(4):
452
                                if ops[1][i] == False:
453
                                    pts.append(vts[i].Point)
454
                                else:
455
                                    pts.append(vts[i].Point.add(self.displacement))
456
                            pts = str(pts).replace("Vector ", "FreeCAD.Vector")
457
                            _cmd = "Draft.make_wire"
458
                            _cmd += "(" + pts + ", closed=True, "
459
                            _cmd += "face=" + str(ops[0].MakeFace)
460
                            _cmd += ")"
461
                            _format = "Draft.formatObject"
462
                            _format += "(w, "
463
                            _format += _doc + ops[0].Name
464
                            _format += ")"
465
                            _hide = _doc + ops[0].Name + ".ViewObject.hide()"
466
                            commitops.append("w = " + _cmd)
467
                            commitops.append(_format)
468
                            commitops.append(_hide)
469
                    else:
470
                        _pl = _doc + ops[0].Name
471
                        _pl += ".Placement.Base=FreeCAD."
472
                        _pl += str(ops[0].Placement.Base.add(self.displacement))
473
                        commitops.append(_pl)
474
        if commitops:
475
            commitops.append("FreeCAD.ActiveDocument.recompute()")
476
            Gui.addModule("Draft")
477
            self.commit(translate("draft", "Stretch"), commitops)
478
        self.finish()
479

480

481
Gui.addCommand('Draft_Stretch', Stretch())
482

483
## @}
484

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

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

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

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