From 58812ebbbfb978e033294affce3bddf21c6205e8 Mon Sep 17 00:00:00 2001 From: vocx-fc Date: Mon, 6 Apr 2020 18:11:32 -0500 Subject: [PATCH] Draft: move Stretch GuiCommand to gui_stretch module --- src/Mod/Draft/CMakeLists.txt | 1 + src/Mod/Draft/DraftTools.py | 332 +------------- src/Mod/Draft/draftguitools/gui_stretch.py | 485 +++++++++++++++++++++ 3 files changed, 487 insertions(+), 331 deletions(-) create mode 100644 src/Mod/Draft/draftguitools/gui_stretch.py diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index 5ed416af38..7f399ffb08 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -106,6 +106,7 @@ SET(Modifier_tools draftguitools/gui_styles.py draftguitools/gui_rotate.py draftguitools/gui_offset.py + draftguitools/gui_stretch.py ) SET(Draft_GUI_tools diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 14c906d040..ba285b00bf 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -178,338 +178,9 @@ from draftguitools.gui_move import Move from draftguitools.gui_styles import ApplyStyle from draftguitools.gui_rotate import Rotate from draftguitools.gui_offset import Offset +from draftguitools.gui_stretch import Stretch -class Stretch(Modifier): - """The Draft_Stretch FreeCAD command definition""" - - def GetResources(self): - return {'Pixmap' : 'Draft_Stretch', - 'Accel' : "S, H", - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Stretch", "Stretch"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Stretch", "Stretches the selected objects")} - - def Activated(self): - Modifier.Activated(self,"Stretch") - if self.ui: - if not FreeCADGui.Selection.getSelection(): - self.ui.selectUi() - FreeCAD.Console.PrintMessage(translate("draft", "Select an object to stretch")+"\n") - self.call = self.view.addEventCallback("SoEvent",selectObject) - else: - self.proceed() - - def proceed(self): - if self.call: - self.view.removeEventCallback("SoEvent",self.call) - supported = ["Rectangle","Wire","BSpline","BezCurve","Sketch"] - self.sel = [] - for obj in FreeCADGui.Selection.getSelection(): - if Draft.getType(obj) in supported: - self.sel.append([obj,FreeCAD.Placement()]) - elif hasattr(obj,"Base"): - if obj.Base: - if Draft.getType(obj.Base) in supported: - self.sel.append([obj.Base,obj.Placement]) - - elif Draft.getType(obj.Base) in ["Offset2D","Array"]: - base = None - if hasattr(obj.Base,"Source") and obj.Base.Source: - base = obj.Base.Source - elif hasattr(obj.Base,"Base") and obj.Base.Base: - base = obj.Base.Base - if base: - if Draft.getType(base) in supported: - self.sel.append([base,obj.Placement.multiply(obj.Base.Placement)]) - elif Draft.getType(obj) in ["Offset2D","Array"]: - base = None - if hasattr(obj,"Source") and obj.Source: - base = obj.Source - elif hasattr(obj,"Base") and obj.Base: - base = obj.Base - if base: - if Draft.getType(base) in supported: - self.sel.append([base,obj.Placement]) - if self.ui and self.sel: - self.step = 1 - self.refpoint = None - self.ui.pointUi("Stretch") - self.ui.extUi() - self.call = self.view.addEventCallback("SoEvent",self.action) - self.rectracker = trackers.rectangleTracker(dotted=True,scolor=(0.0,0.0,1.0),swidth=2) - self.nodetracker = [] - self.displacement = None - FreeCAD.Console.PrintMessage(translate("draft", "Pick first point of selection rectangle")+"\n") - - def action(self,arg): - """scene event handler""" - if arg["Type"] == "SoKeyboardEvent": - if arg["Key"] == "ESCAPE": - self.finish() - elif arg["Type"] == "SoLocation2Event": #mouse movement detection - point,ctrlPoint,info = getPoint(self,arg) #,mobile=True) #,noTracker=(self.step < 3)) - if self.step == 2: - self.rectracker.update(point) - redraw3DView() - elif arg["Type"] == "SoMouseButtonEvent": - if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): - if (arg["Position"] == self.pos): - # clicked twice on the same point - self.finish() - else: - point,ctrlPoint,info = getPoint(self,arg) #,mobile=True) #,noTracker=(self.step < 3)) - self.addPoint(point) - - def addPoint(self,point): - if self.step == 1: - # first rctangle point - FreeCAD.Console.PrintMessage(translate("draft", "Pick opposite point of selection rectangle")+"\n") - self.ui.setRelative() - self.rectracker.setorigin(point) - self.rectracker.on() - if self.planetrack: - self.planetrack.set(point) - self.step = 2 - elif self.step == 2: - # second rectangle point - FreeCAD.Console.PrintMessage(translate("draft", "Pick start point of displacement")+"\n") - self.rectracker.off() - nodes = [] - self.ops = [] - for sel in self.sel: - o = sel[0] - vispla = sel[1] - tp = Draft.getType(o) - if tp in ["Wire","BSpline","BezCurve"]: - np = [] - iso = False - for p in o.Points: - p = o.Placement.multVec(p) - p = vispla.multVec(p) - isi = self.rectracker.isInside(p) - np.append(isi) - if isi: - iso = True - nodes.append(p) - if iso: - self.ops.append([o,np]) - elif tp in ["Rectangle"]: - p1 = Vector(0,0,0) - p2 = Vector(o.Length.Value,0,0) - p3 = Vector(o.Length.Value,o.Height.Value,0) - p4 = Vector(0,o.Height.Value,0) - np = [] - iso = False - for p in [p1,p2,p3,p4]: - p = o.Placement.multVec(p) - p = vispla.multVec(p) - isi = self.rectracker.isInside(p) - np.append(isi) - if isi: - iso = True - nodes.append(p) - if iso: - self.ops.append([o,np]) - elif tp in ["Sketch"]: - np = [] - iso = False - for p in o.Shape.Vertexes: - p = vispla.multVec(p.Point) - isi = self.rectracker.isInside(p) - np.append(isi) - if isi: - iso = True - nodes.append(p) - if iso: - self.ops.append([o,np]) - else: - p = o.Placement.Base - p = vispla.multVec(p) - if self.rectracker.isInside(p): - self.ops.append([o]) - nodes.append(p) - for n in nodes: - nt = trackers.editTracker(n,inactive=True) - nt.on() - self.nodetracker.append(nt) - self.step = 3 - elif self.step == 3: - # first point of displacement line - FreeCAD.Console.PrintMessage(translate("draft", "Pick end point of displacement")+"\n") - self.displacement = point - #print "first point:",point - self.node = [point] - self.step = 4 - elif self.step == 4: - #print "second point:",point - self.displacement = point.sub(self.displacement) - self.doStretch() - if self.point: - self.ui.redraw() - - def numericInput(self,numx,numy,numz): - """this function gets called by the toolbar when valid x, y, and z have been entered there""" - point = Vector(numx,numy,numz) - self.addPoint(point) - - def finish(self,closed=False): - if hasattr(self,"rectracker") and self.rectracker: - self.rectracker.finalize() - if hasattr(self,"nodetracker") and self.nodetracker: - for n in self.nodetracker: - n.finalize() - Modifier.finish(self) - - def doStretch(self): - """does the actual stretching""" - commitops = [] - if self.displacement: - if self.displacement.Length > 0: - #print "displacement: ",self.displacement - for ops in self.ops: - tp = Draft.getType(ops[0]) - localdisp = ops[0].Placement.Rotation.inverted().multVec(self.displacement) - if tp in ["Wire","BSpline","BezCurve"]: - pts = [] - for i in range(len(ops[1])): - if ops[1][i] == False: - pts.append(ops[0].Points[i]) - else: - pts.append(ops[0].Points[i].add(localdisp)) - pts = str(pts).replace("Vector","FreeCAD.Vector") - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Points="+pts) - elif tp in ["Sketch"]: - baseverts = [ops[0].Shape.Vertexes[i].Point for i in range(len(ops[1])) if ops[1][i]] - for i in range(ops[0].GeometryCount): - j = 0 - while True: - try: - p = ops[0].getPoint(i,j) - except ValueError: - break - else: - p = ops[0].Placement.multVec(p) - r = None - for bv in baseverts: - if DraftVecUtils.isNull(p.sub(bv)): - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".movePoint("+str(i)+","+str(j)+",FreeCAD."+str(localdisp)+",True)") - r = bv - break - if r: - baseverts.remove(r) - j += 1 - elif tp in ["Rectangle"]: - p1 = Vector(0,0,0) - p2 = Vector(ops[0].Length.Value,0,0) - p3 = Vector(ops[0].Length.Value,ops[0].Height.Value,0) - p4 = Vector(0,ops[0].Height.Value,0) - if ops[1] == [False,True,True,False]: - optype = 1 - elif ops[1] == [False,False,True,True]: - optype = 2 - elif ops[1] == [True,False,False,True]: - optype = 3 - elif ops[1] == [True,True,False,False]: - optype = 4 - else: - optype = 0 - #print("length:",ops[0].Length,"height:",ops[0].Height," - ",ops[1]," - ",self.displacement) - done = False - if optype > 0: - v1 = ops[0].Placement.multVec(p2).sub(ops[0].Placement.multVec(p1)) - a1 = round(self.displacement.getAngle(v1),4) - v2 = ops[0].Placement.multVec(p4).sub(ops[0].Placement.multVec(p1)) - a2 = round(self.displacement.getAngle(v2),4) - # check if the displacement is along one of the rectangle directions - if a1 == 0: - if optype == 1: - if ops[0].Length.Value >= 0: - d = ops[0].Length.Value + self.displacement.Length - else: - d = ops[0].Length.Value - self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Length="+str(d)) - done = True - elif optype == 3: - if ops[0].Length.Value >= 0: - d = ops[0].Length.Value - self.displacement.Length - else: - d = ops[0].Length.Value + self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Length="+str(d)) - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Placement.Base=FreeCAD."+str(ops[0].Placement.Base.add(self.displacement))) - done = True - elif a1 == 3.1416: - if optype == 1: - if ops[0].Length.Value >= 0: - d = ops[0].Length.Value - self.displacement.Length - else: - d = ops[0].Length.Value + self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Length="+str(d)) - done = True - elif optype == 3: - if ops[0].Length.Value >= 0: - d = ops[0].Length.Value + self.displacement.Length - else: - d = ops[0].Length.Value - self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Length="+str(d)) - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Placement.Base=FreeCAD."+str(ops[0].Placement.Base.add(self.displacement))) - done = True - elif a2 == 0: - if optype == 2: - if ops[0].Height.Value >= 0: - d = ops[0].Height.Value + self.displacement.Length - else: - d = ops[0].Height.Value - self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Height="+str(d)) - done = True - elif optype == 4: - if ops[0].Height.Value >= 0: - d = ops[0].Height.Value - self.displacement.Length - else: - d = ops[0].Height.Value + self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Height="+str(d)) - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Placement.Base=FreeCAD."+str(ops[0].Placement.Base.add(self.displacement))) - done = True - elif a2 == 3.1416: - if optype == 2: - if ops[0].Height.Value >= 0: - d = ops[0].Height.Value - self.displacement.Length - else: - d = ops[0].Height.Value + self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Height="+str(d)) - done = True - elif optype == 4: - if ops[0].Height.Value >= 0: - d = ops[0].Height.Value + self.displacement.Length - else: - d = ops[0].Height.Value - self.displacement.Length - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Height="+str(d)) - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Placement.Base=FreeCAD."+str(ops[0].Placement.Base.add(self.displacement))) - done = True - if not done: - # otherwise create a wire copy and stretch it instead - FreeCAD.Console.PrintMessage(translate("draft","Turning one Rectangle into a Wire")+"\n") - pts = [] - opts = [p1,p2,p3,p4] - for i in range(4): - if ops[1][i] == False: - pts.append(opts[i]) - else: - pts.append(opts[i].add(self.displacement)) - pts = str(pts).replace("Vector","FreeCAD.Vector") - commitops.append("w = Draft.makeWire("+pts+",closed=True)") - commitops.append("Draft.formatObject(w,FreeCAD.ActiveDocument."+ops[0].Name+")") - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".ViewObject.hide()") - for par in ops[0].InList: - if hasattr(par,"Base") and par.Base == ops[0]: - commitops.append("FreeCAD.ActiveDocument."+par.Name+".Base = w") - else: - commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Placement.Base=FreeCAD."+str(ops[0].Placement.Base.add(self.displacement))) - if commitops: - commitops.append("FreeCAD.ActiveDocument.recompute()") - FreeCADGui.addModule("Draft") - self.commit(translate("draft","Stretch"),commitops) - self.finish() - class Join(Modifier): '''The Draft_Join FreeCAD command definition.''' @@ -1866,7 +1537,6 @@ FreeCADGui.addCommand('Draft_PathArray',PathArray()) FreeCADGui.addCommand('Draft_PathLinkArray',PathLinkArray()) FreeCADGui.addCommand('Draft_PointArray',PointArray()) FreeCADGui.addCommand('Draft_Mirror',Mirror()) -FreeCADGui.addCommand('Draft_Stretch',Stretch()) # context commands FreeCADGui.addCommand('Draft_Shape2DView',Shape2DView()) diff --git a/src/Mod/Draft/draftguitools/gui_stretch.py b/src/Mod/Draft/draftguitools/gui_stretch.py new file mode 100644 index 0000000000..36568664b5 --- /dev/null +++ b/src/Mod/Draft/draftguitools/gui_stretch.py @@ -0,0 +1,485 @@ +# *************************************************************************** +# * (c) 2009, 2010 Yorik van Havre * +# * (c) 2009, 2010 Ken Cline * +# * (c) 2020 Eliud Cabrera Castillo * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""Provides tools for stretching objects with the Draft Workbench. + +It works with rectangles, wires, b-splines, bezier curves, and sketches. +It essentially moves the points that are located within a selection area, +while keeping other points intact. This means the lines tied by the points +that were moved are 'stretched'. +""" +## @package gui_stretch +# \ingroup DRAFT +# \brief Provides tools for stretching objects with the Draft Workbench. + +from PySide.QtCore import QT_TRANSLATE_NOOP + +import FreeCAD as App +import FreeCADGui as Gui +import Draft_rc +import DraftVecUtils +import draftutils.utils as utils +import draftguitools.gui_base_original as gui_base_original +import draftguitools.gui_tool_utils as gui_tool_utils +import draftguitools.gui_trackers as trackers +from draftutils.messages import _msg +from draftutils.translate import translate, _tr + +# The module is used to prevent complaints from code checkers (flake8) +True if Draft_rc.__name__ else False + + +class Stretch(gui_base_original.Modifier): + """Gui Command for the Stretch tool.""" + + def GetResources(self): + """Set icon, menu and tooltip.""" + _tip = ("Stretches the selected objects.\n" + "Select an object, then draw a rectangle " + "to pick the vertices that will be stretched,\n" + "then draw a line to specify the distance " + "and direction of stretching.") + + return {'Pixmap': 'Draft_Stretch', + 'Accel': "S, H", + 'MenuText': QT_TRANSLATE_NOOP("Draft_Stretch", "Stretch"), + 'ToolTip': QT_TRANSLATE_NOOP("Draft_Stretch", _tip)} + + def Activated(self): + """Execute when the command is called.""" + super().Activated(name=_tr("Stretch")) + if self.ui: + if not Gui.Selection.getSelection(): + self.ui.selectUi() + _msg(translate("draft", "Select an object to stretch")) + self.call = \ + self.view.addEventCallback("SoEvent", + gui_tool_utils.selectObject) + else: + self.proceed() + + def proceed(self): + """Proceed with execution of the command after proper selection.""" + if self.call: + self.view.removeEventCallback("SoEvent", self.call) + supported = ["Rectangle", "Wire", "BSpline", "BezCurve", "Sketch"] + self.sel = [] + for obj in Gui.Selection.getSelection(): + if utils.getType(obj) in supported: + self.sel.append([obj, App.Placement()]) + elif hasattr(obj, "Base"): + if obj.Base: + if utils.getType(obj.Base) in supported: + self.sel.append([obj.Base, obj.Placement]) + elif utils.getType(obj.Base) in ["Offset2D", "Array"]: + base = None + if hasattr(obj.Base, "Source") and obj.Base.Source: + base = obj.Base.Source + elif hasattr(obj.Base, "Base") and obj.Base.Base: + base = obj.Base.Base + if base: + if utils.getType(base) in supported: + self.sel.append([base, obj.Placement.multiply(obj.Base.Placement)]) + elif utils.getType(obj) in ["Offset2D", "Array"]: + base = None + if hasattr(obj, "Source") and obj.Source: + base = obj.Source + elif hasattr(obj, "Base") and obj.Base: + base = obj.Base + if base: + if utils.getType(base) in supported: + self.sel.append([base, obj.Placement]) + if self.ui and self.sel: + self.step = 1 + self.refpoint = None + self.ui.pointUi("Stretch") + self.ui.extUi() + self.call = self.view.addEventCallback("SoEvent", self.action) + self.rectracker = trackers.rectangleTracker(dotted=True, + scolor=(0.0, 0.0, 1.0), + swidth=2) + self.nodetracker = [] + self.displacement = None + _msg(translate("draft", "Pick first point of selection rectangle")) + + def action(self, arg): + """Handle the 3D scene events. + + This is installed as an EventCallback in the Inventor view. + + Parameters + ---------- + arg: dict + Dictionary with strings that indicates the type of event received + from the 3D view. + """ + if arg["Type"] == "SoKeyboardEvent": + if arg["Key"] == "ESCAPE": + self.finish() + elif arg["Type"] == "SoLocation2Event": # mouse movement detection + # ,mobile=True) #,noTracker=(self.step < 3)) + point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) + if self.step == 2: + self.rectracker.update(point) + gui_tool_utils.redraw3DView() + elif arg["Type"] == "SoMouseButtonEvent": + if arg["State"] == "DOWN" and arg["Button"] == "BUTTON1": + if arg["Position"] == self.pos: + # clicked twice on the same point + self.finish() + else: + # ,mobile=True) #,noTracker=(self.step < 3)) + point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) + self.addPoint(point) + + def addPoint(self, point): + """Add point to defined selection rectangle.""" + if self.step == 1: + # first rctangle point + _msg(translate("draft", "Pick opposite point " + "of selection rectangle")) + self.ui.setRelative() + self.rectracker.setorigin(point) + self.rectracker.on() + if self.planetrack: + self.planetrack.set(point) + self.step = 2 + elif self.step == 2: + # second rectangle point + _msg(translate("draft", "Pick start point of displacement")) + self.rectracker.off() + nodes = [] + self.ops = [] + for sel in self.sel: + o = sel[0] + vispla = sel[1] + tp = utils.getType(o) + if tp in ["Wire", "BSpline", "BezCurve"]: + np = [] + iso = False + for p in o.Points: + p = o.Placement.multVec(p) + p = vispla.multVec(p) + isi = self.rectracker.isInside(p) + np.append(isi) + if isi: + iso = True + nodes.append(p) + if iso: + self.ops.append([o, np]) + elif tp in ["Rectangle"]: + p1 = App.Vector(0, 0, 0) + p2 = App.Vector(o.Length.Value, 0, 0) + p3 = App.Vector(o.Length.Value, o.Height.Value, 0) + p4 = App.Vector(0, o.Height.Value, 0) + np = [] + iso = False + for p in [p1, p2, p3, p4]: + p = o.Placement.multVec(p) + p = vispla.multVec(p) + isi = self.rectracker.isInside(p) + np.append(isi) + if isi: + iso = True + nodes.append(p) + if iso: + self.ops.append([o, np]) + elif tp in ["Sketch"]: + np = [] + iso = False + for p in o.Shape.Vertexes: + p = vispla.multVec(p.Point) + isi = self.rectracker.isInside(p) + np.append(isi) + if isi: + iso = True + nodes.append(p) + if iso: + self.ops.append([o, np]) + else: + p = o.Placement.Base + p = vispla.multVec(p) + if self.rectracker.isInside(p): + self.ops.append([o]) + nodes.append(p) + for n in nodes: + nt = trackers.editTracker(n, inactive=True) + nt.on() + self.nodetracker.append(nt) + self.step = 3 + elif self.step == 3: + # first point of displacement line + _msg(translate("draft", "Pick end point of displacement")) + self.displacement = point + # print("first point:", point) + self.node = [point] + self.step = 4 + elif self.step == 4: + # print("second point:", point) + self.displacement = point.sub(self.displacement) + self.doStretch() + if self.point: + self.ui.redraw() + + def numericInput(self, numx, numy, numz): + """Validate the entry fields in the user interface. + + This function is called by the toolbar or taskpanel interface + when valid x, y, and z have been entered in the input fields. + """ + point = App.Vector(numx, numy, numz) + self.addPoint(point) + + def finish(self, closed=False): + """Terminate the operation of the command. and clean up.""" + if hasattr(self, "rectracker") and self.rectracker: + self.rectracker.finalize() + if hasattr(self, "nodetracker") and self.nodetracker: + for n in self.nodetracker: + n.finalize() + super().finish() + + def doStretch(self): + """Do the actual stretching once the points are selected.""" + commitops = [] + if self.displacement: + if self.displacement.Length > 0: + _doc = "FreeCAD.ActiveDocument." + # print("displacement: ", self.displacement) + + # TODO: break this section into individual functions + # depending on the type of object (wire, curve, sketch, + # rectangle, etc.) that is selected, and use variables + # with common strings to avoid repeating + # the same information every time, for example, the `_doc` + # variable. + # This is necessary to reduce the number of indentation levels + # and make the code easier to read. + for ops in self.ops: + tp = utils.getType(ops[0]) + _rot = ops[0].Placement.Rotation + localdisp = _rot.inverted().multVec(self.displacement) + if tp in ["Wire", "BSpline", "BezCurve"]: + pts = [] + for i in range(len(ops[1])): + if ops[1][i] is False: + pts.append(ops[0].Points[i]) + else: + pts.append(ops[0].Points[i].add(localdisp)) + pts = str(pts).replace("Vector ", "FreeCAD.Vector") + _cmd = _doc + ops[0].Name + ".Points=" + pts + commitops.append(_cmd) + elif tp in ["Sketch"]: + baseverts = [ops[0].Shape.Vertexes[i].Point for i in range(len(ops[1])) if ops[1][i]] + for i in range(ops[0].GeometryCount): + j = 0 + while True: + try: + p = ops[0].getPoint(i, j) + except ValueError: + break + else: + p = ops[0].Placement.multVec(p) + r = None + for bv in baseverts: + if DraftVecUtils.isNull(p.sub(bv)): + _cmd = _doc + _cmd += ops[0].Name + _cmd += ".movePoint" + _cmd += "(" + _cmd += str(i) + ", " + _cmd += str(j) + ", " + _cmd += "FreeCAD." + str(localdisp) + ", " + _cmd += "True" + _cmd += ")" + commitops.append(_cmd) + r = bv + break + if r: + baseverts.remove(r) + j += 1 + elif tp in ["Rectangle"]: + p1 = App.Vector(0, 0, 0) + p2 = App.Vector(ops[0].Length.Value, 0, 0) + p3 = App.Vector(ops[0].Length.Value, + ops[0].Height.Value, + 0) + p4 = App.Vector(0, ops[0].Height.Value, 0) + if ops[1] == [False, True, True, False]: + optype = 1 + elif ops[1] == [False, False, True, True]: + optype = 2 + elif ops[1] == [True, False, False, True]: + optype = 3 + elif ops[1] == [True, True, False, False]: + optype = 4 + else: + optype = 0 + # print("length:", ops[0].Length, + # "height:", ops[0].Height, + # " - ", ops[1], + # " - ", self.displacement) + done = False + if optype > 0: + v1 = ops[0].Placement.multVec(p2).sub(ops[0].Placement.multVec(p1)) + a1 = round(self.displacement.getAngle(v1), 4) + v2 = ops[0].Placement.multVec(p4).sub(ops[0].Placement.multVec(p1)) + a2 = round(self.displacement.getAngle(v2), 4) + # check if the displacement is along one + # of the rectangle directions + if a1 == 0: # 0 degrees + if optype == 1: + if ops[0].Length.Value >= 0: + d = ops[0].Length.Value + self.displacement.Length + else: + d = ops[0].Length.Value - self.displacement.Length + _cmd = _doc + _cmd += ops[0].Name + ".Length=" + str(d) + commitops.append(_cmd) + done = True + elif optype == 3: + if ops[0].Length.Value >= 0: + d = ops[0].Length.Value - self.displacement.Length + else: + d = ops[0].Length.Value + self.displacement.Length + _cmd = _doc + ops[0].Name + _cmd += ".Length=" + str(d) + _pl = _doc + ops[0].Name + _pl += ".Placement.Base=FreeCAD." + _pl += str(ops[0].Placement.Base.add(self.displacement)) + commitops.append(_cmd) + commitops.append(_pl) + done = True + elif a1 == 3.1416: # pi radians, 180 degrees + if optype == 1: + if ops[0].Length.Value >= 0: + d = ops[0].Length.Value - self.displacement.Length + else: + d = ops[0].Length.Value + self.displacement.Length + _cmd = _doc + ops[0].Name + _cmd += ".Length=" + str(d) + commitops.append(_cmd) + done = True + elif optype == 3: + if ops[0].Length.Value >= 0: + d = ops[0].Length.Value + self.displacement.Length + else: + d = ops[0].Length.Value - self.displacement.Length + _cmd = _doc + ops[0].Name + _cmd += ".Length=" + str(d) + _pl = _doc + ops[0].Name + _pl += ".Placement.Base=FreeCAD." + _pl += str(ops[0].Placement.Base.add(self.displacement)) + commitops.append(_cmd) + commitops.append(_pl) + done = True + elif a2 == 0: # 0 degrees + if optype == 2: + if ops[0].Height.Value >= 0: + d = ops[0].Height.Value + self.displacement.Length + else: + d = ops[0].Height.Value - self.displacement.Length + _cmd = _doc + ops[0].Name + _cmd += ".Height=" + str(d) + commitops.append(_cmd) + done = True + elif optype == 4: + if ops[0].Height.Value >= 0: + d = ops[0].Height.Value - self.displacement.Length + else: + d = ops[0].Height.Value + self.displacement.Length + _cmd = _doc + ops[0].Name + _cmd += ".Height=" + str(d) + _pl = _doc + ops[0].Name + _pl += ".Placement.Base=FreeCAD." + _pl += str(ops[0].Placement.Base.add(self.displacement)) + commitops.append(_cmd) + commitops.append(_pl) + done = True + elif a2 == 3.1416: # pi radians, 180 degrees + if optype == 2: + if ops[0].Height.Value >= 0: + d = ops[0].Height.Value - self.displacement.Length + else: + d = ops[0].Height.Value + self.displacement.Length + _cmd = _doc + ops[0].Name + _cmd += ".Height=" + str(d) + commitops.append(_cmd) + done = True + elif optype == 4: + if ops[0].Height.Value >= 0: + d = ops[0].Height.Value + self.displacement.Length + else: + d = ops[0].Height.Value - self.displacement.Length + _cmd = _doc + ops[0].Name + _cmd += ".Height=" + str(d) + _pl = _doc + ops[0].Name + _pl += ".Placement.Base=FreeCAD." + _pl += str(ops[0].Placement.Base.add(self.displacement)) + commitops.append(_cmd) + commitops.append(_pl) + done = True + if not done: + # otherwise create a wire copy and stretch it instead + _msg(translate("draft", "Turning one Rectangle into a Wire")) + pts = [] + opts = [p1, p2, p3, p4] + for i in range(4): + if ops[1][i] == False: + pts.append(opts[i]) + else: + pts.append(opts[i].add(self.displacement)) + pts = str(pts).replace("Vector ", "FreeCAD.Vector") + _cmd = "Draft.makeWire" + _cmd += "(" + pts + ", closed=True)" + _format = "Draft.formatObject" + _format += "(w, " + _format += _doc + ops[0].Name + _format += ")" + _hide = _doc + ops[0].Name + ".ViewObject.hide()" + commitops.append("w = " + _cmd) + commitops.append(_format) + commitops.append(_hide) + # BUG: at this step the produced wire + # doesn't seem to be in the correct position + # compared to the original rectangle. + # The base placement needs to be adjusted + # just like with the other objects. + for par in ops[0].InList: + if hasattr(par, "Base") and par.Base == ops[0]: + _base = _doc + par.Name + ".Base = w" + commitops.append(_base) + else: + _pl = _doc + ops[0].Name + _pl += ".Placement.Base=FreeCAD." + _pl += str(ops[0].Placement.Base.add(self.displacement)) + commitops.append(_pl) + if commitops: + commitops.append("FreeCAD.ActiveDocument.recompute()") + Gui.addModule("Draft") + self.commit(translate("draft", "Stretch"), commitops) + self.finish() + + +Gui.addCommand('Draft_Stretch', Stretch())