diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py
index 2d096138d..9ea142e70 100644
--- a/src/Mod/Arch/InitGui.py
+++ b/src/Mod/Arch/InitGui.py
@@ -56,7 +56,7 @@ class ArchWorkbench(Workbench):
"Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
"Draft_Clone"]
self.draftextratools = ["Draft_WireToBSpline","Draft_AddPoint","Draft_DelPoint","Draft_ShapeString",
- "Draft_PathArray","Draft_Mirror"]
+ "Draft_PathArray","Draft_Mirror","Draft_Stretch"]
self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup",
"Draft_SelectGroup","Draft_SelectPlane",
"Draft_ShowSnapBar","Draft_ToggleGrid","Draft_UndoLine",
diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py
index 91bba89a9..a50f8fb77 100755
--- a/src/Mod/Draft/DraftGeomUtils.py
+++ b/src/Mod/Draft/DraftGeomUtils.py
@@ -96,10 +96,14 @@ def isNull(something):
def isPtOnEdge(pt,edge) :
'''isPtOnEdge(Vector,edge): Tests if a point is on an edge'''
v = Part.Vertex(pt)
- d = v.distToShape(edge)
- if d:
- if round(d[0],precision()) == 0:
- return True
+ try:
+ d = v.distToShape(edge)
+ except:
+ return False
+ else:
+ if d:
+ if round(d[0],precision()) == 0:
+ return True
return False
def hasCurves(shape):
diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py
index 2a7d3a51b..6bd409b16 100644
--- a/src/Mod/Draft/DraftTools.py
+++ b/src/Mod/Draft/DraftTools.py
@@ -2670,6 +2670,149 @@ class Offset(Modifier):
self.finish()
+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()
+ msg(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)
+ self.sel = FreeCADGui.Selection.getSelection()
+ 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 = rectangleTracker()
+ self.nodetracker = []
+ self.displacement = None
+ msg(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)
+ 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
+ msg(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
+ msg(translate("draft", "Pick start point of displacement:\n"))
+ self.rectracker.off()
+ nodes = []
+ self.ops = []
+ for o in self.sel:
+ tp = Draft.getType(o)
+ if tp in ["Wire","BSpline","BezCurve"]:
+ np = []
+ iso = False
+ for p in o.Points:
+ p = o.Placement.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 == "Circle":
+ self.ops.append([o])
+ nodes.append(o.Placement.Base)
+ else:
+ print "Draft Stretch: Skipping unsupported object: ",o.Label
+ for n in nodes:
+ nt = editTracker(n)
+ 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:\n"))
+ self.displacement = point
+ self.node = [point]
+ self.step = 4
+ elif self.step == 4:
+ 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 self.rectracker:
+ self.rectracker.finalize()
+ if 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:
+
+ for ops in self.ops:
+ tp = Draft.getType(ops[0])
+ 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(self.displacement))
+ pts = str(pts).replace("Vector","FreeCAD.Vector")
+ commitops.append("FreeCAD.ActiveDocument."+ops[0].Name+".Points="+pts)
+ elif tp == "Circle":
+ 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 Upgrade(Modifier):
'''The Draft_Upgrade FreeCAD command definition.'''
@@ -4923,6 +5066,7 @@ FreeCADGui.addCommand('Draft_Heal',Heal())
FreeCADGui.addCommand('Draft_VisGroup',VisGroup())
FreeCADGui.addCommand('Draft_Mirror',Mirror())
FreeCADGui.addCommand('Draft_Slope',Draft_Slope())
+FreeCADGui.addCommand('Draft_Stretch',Stretch())
# context commands
FreeCADGui.addCommand('Draft_FinishLine',FinishLine())
diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py
index b0e500b7f..731012db3 100644
--- a/src/Mod/Draft/DraftTrackers.py
+++ b/src/Mod/Draft/DraftTrackers.py
@@ -245,6 +245,20 @@ class rectangleTracker(Tracker):
def getNormal(self):
"returns the normal of the rectangle"
return (self.u.cross(self.v)).normalize()
+
+ def isInside(self,point):
+ "returns True if the given point is inside the rectangle"
+ vp = point.sub(self.p1())
+ uv = self.p2().sub(self.p1())
+ vv = self.p4().sub(self.p1())
+ uvp = DraftVecUtils.project(vp,uv)
+ vvp = DraftVecUtils.project(vp,vv)
+ if uvp.getAngle(uv) < 1:
+ if vvp.getAngle(vv) < 1:
+ if uvp.Length <= uv.Length:
+ if vvp.Length <= vv.Length:
+ return True
+ return False
class dimTracker(Tracker):
"A Dimension tracker, used by the dimension tool"
diff --git a/src/Mod/Draft/InitGui.py b/src/Mod/Draft/InitGui.py
index 282b5ebcf..c118df631 100644
--- a/src/Mod/Draft/InitGui.py
+++ b/src/Mod/Draft/InitGui.py
@@ -76,7 +76,7 @@ class DraftWorkbench (Workbench):
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
"Draft_Edit","Draft_WireToBSpline","Draft_AddPoint",
"Draft_DelPoint","Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
- "Draft_PathArray","Draft_Clone","Draft_Drawing","Draft_Mirror"]
+ "Draft_PathArray","Draft_Clone","Draft_Drawing","Draft_Mirror","Draft_Stretch"]
self.treecmdList = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup",
"Draft_SelectGroup","Draft_SelectPlane",
"Draft_ShowSnapBar","Draft_ToggleGrid"]
diff --git a/src/Mod/Draft/Resources/Draft.qrc b/src/Mod/Draft/Resources/Draft.qrc
index 2ade2035b..0bbac2fdb 100644
--- a/src/Mod/Draft/Resources/Draft.qrc
+++ b/src/Mod/Draft/Resources/Draft.qrc
@@ -71,6 +71,7 @@
icons/Draft_Grid.svg
icons/Draft_Slope.svg
icons/DraftWorkbench.svg
+ icons/Draft_Stretch.svg
patterns/concrete.svg
patterns/cross.svg
patterns/line.svg
diff --git a/src/Mod/Draft/Resources/icons/Draft_Stretch.svg b/src/Mod/Draft/Resources/icons/Draft_Stretch.svg
new file mode 100644
index 000000000..cc5a62dc5
--- /dev/null
+++ b/src/Mod/Draft/Resources/icons/Draft_Stretch.svg
@@ -0,0 +1,276 @@
+
+
+
+