From 10ad6c6d028416e800cb2345395380fcb174be53 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 5 Apr 2017 19:04:05 -0300 Subject: [PATCH] Draft: Hold snapping points When drawing (snapping) using the Draft module, it is now possible to press the Q key to record points. Additional snap locations then become available orthogonally to these hold points. --- src/Mod/Draft/DraftGeomUtils.py | 22 ++++++++- src/Mod/Draft/DraftGui.py | 4 ++ src/Mod/Draft/DraftSnap.py | 87 ++++++++++++++++++++++++++++++--- src/Mod/Draft/DraftTrackers.py | 11 +++++ 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 57bd3c07f2..e4268a0c47 100755 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -1252,10 +1252,28 @@ def findDistance(point,edge,strict=False): ''' findDistance(vector,edge,[strict]) - Returns a vector from the point to its closest point on the edge. If strict is True, the vector will be returned - only if its endpoint lies on the edge. + only if its endpoint lies on the edge. Edge can also be a list of 2 points. ''' if isinstance(point, FreeCAD.Vector): - if geomType(edge) == "Line": + if isinstance(edge,list): + segment = edge[1].sub(edge[0]) + chord = edge[0].sub(point) + norm = segment.cross(chord) + perp = segment.cross(norm) + dist = DraftVecUtils.project(chord,perp) + if not dist: return None + newpoint = point.add(dist) + if (dist.Length == 0): + return None + if strict: + s1 = newpoint.sub(edge[0]) + s2 = newpoint.sub(edge[1]) + if (s1.Length <= segment.Length) and (s2.Length <= segment.Length): + return dist + else: + return None + else: return dist + elif geomType(edge) == "Line": segment = vec(edge) chord = edge.Vertexes[0].Point.sub(point) norm = segment.cross(chord) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index fa2ecec94a..299d5c965e 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -1462,6 +1462,10 @@ class DraftToolBar: elif txt.endswith("]"): self.toggleradius(-1) spec = True + elif txt.endswith("q"): + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.addHoldPoint() + spec = True elif txt.endswith("x"): self.constrain("x") self.displayPoint() diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index fb3d0392f3..e87c2280f9 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -89,9 +89,11 @@ class Snapper: self.forceGridOff = False self.lastExtensions = [] # the trackers are stored in lists because there can be several views, each with its own set - self.trackers = [[],[],[],[],[],[],[],[],[]] # view, grid, snap, extline, radius, dim1, dim2, trackLine, extline2 + self.trackers = [[],[],[],[],[],[],[],[],[],[]] # view, grid, snap, extline, radius, dim1, dim2, trackLine, extline2, crosstrackers self.polarAngles = [90,45] self.selectMode = False + self.holdTracker = None + self.holdPoints = [] # the snapmarker has "dot","circle" and "square" available styles if self.snapStyle: @@ -145,7 +147,8 @@ class Snapper: global Part, DraftGeomUtils import Part, DraftGeomUtils - + self.spoint = None + if not hasattr(self,"toolbar"): self.makeSnapToolBar() mw = FreeCADGui.getMainWindow() @@ -243,6 +246,7 @@ class Snapper: # set the arch point tracking if self.lastArchPoint: self.setArchDims(self.lastArchPoint,fp) + self.spoint = fp return fp else: @@ -251,13 +255,15 @@ class Snapper: obj = FreeCAD.ActiveDocument.getObject(self.snapInfo['Object']) if not obj: - return cstr(point) + self.spoint = cstr(point) + return self.spoint self.lastSnappedObject = obj if hasattr(obj.ViewObject,"Selectable"): if not obj.ViewObject.Selectable: - return cstr(point) + self.spoint = cstr(point) + return self.spoint if not active: @@ -347,7 +353,8 @@ class Snapper: self.lastObj[1] = obj.Name if not snaps: - return cstr(point) + self.spoint = cstr(point) + return self.spoint # calculating the nearest snap point shortest = 1000000000000000000 @@ -394,7 +401,8 @@ class Snapper: self.lastArchPoint = None # return the final point - return fp + self.spoint = fp + return self.spoint def toWP(self,point): "projects the given point on the working plane, if needed" @@ -421,6 +429,18 @@ class Snapper: def snapToExtensions(self,point,last,constrain,eline): "returns a point snapped to extension or parallel line to last object, if any" + tsnap = self.snapToHold(point) + if tsnap: + if self.tracker and not self.selectMode: + self.tracker.setCoords(tsnap[2]) + self.tracker.setMarker(self.mk[tsnap[1]]) + self.tracker.on() + if self.extLine: + self.extLine.p1(tsnap[0]) + self.extLine.p2(tsnap[2]) + self.extLine.on() + self.setCursor(tsnap[1]) + return tsnap[2],eline if self.isEnabled("extension"): tsnap = self.snapToExtOrtho(last,constrain,eline) if tsnap: @@ -686,6 +706,46 @@ class Snapper: except: return None return None + + def snapToHold(self,point): + "returns a snap location that is orthogonal to hold points or, if possible, at crossings" + if not self.holdPoints: + return None + if hasattr(FreeCAD,"DraftWorkingPlane"): + u = FreeCAD.DraftWorkingPlane.u + v = FreeCAD.DraftWorkingPlane.v + else: + u = FreeCAD.Vector(1,0,0) + v = FreeCAD.Vector(0,1,0) + if len(self.holdPoints) > 1: + # first try int points + ipoints = [] + l = list(self.holdPoints) + while len(l) > 1: + p1 = l.pop() + for p2 in l: + i1 = DraftGeomUtils.findIntersection(p1,p1.add(u),p2,p2.add(v),True,True) + if i1: + ipoints.append([p1,i1[0]]) + i2 = DraftGeomUtils.findIntersection(p1,p1.add(v),p2,p2.add(u),True,True) + if i2: + ipoints.append([p1,i2[0]]) + for p in ipoints: + if (p[1].sub(point)).Length < self.radius: + return [p[0],'ortho',p[1]] + # then try to stick to a line + for p in self.holdPoints: + d = DraftGeomUtils.findDistance(point,[p,p.add(u)]) + if d: + if d.Length < self.radius: + fp = point.add(d) + return [p,'extension',fp] + d = DraftGeomUtils.findDistance(point,[p,p.add(v)]) + if d: + if d.Length < self.radius: + fp = point.add(d) + return [p,'extension',fp] + return None def snapToExtPerpendicular(self,last): "returns a perpendicular X extension snap location" @@ -912,6 +972,9 @@ class Snapper: if self.grid: if not Draft.getParam("alwaysShowGrid",True): self.grid.off() + if self.holdTracker: + self.holdTracker.clear() + self.holdTracker.off() self.unconstrain() self.radius = 0 self.setCursor() @@ -1257,6 +1320,7 @@ class Snapper: self.dim2 = self.trackers[6][i] self.trackLine = self.trackers[7][i] self.extLine2 = self.trackers[8][i] + self.holdTracker = self.trackers[9][i] else: if Draft.getParam("grid",True): self.grid = DraftTrackers.gridTracker() @@ -1274,6 +1338,9 @@ class Snapper: self.radiusTracker = DraftTrackers.radiusTracker() self.dim1 = DraftTrackers.archDimTracker(mode=2) self.dim2 = DraftTrackers.archDimTracker(mode=3) + self.holdTracker = DraftTrackers.snapTracker() + self.holdTracker.setMarker("cross") + self.holdTracker.clear() self.trackers[0].append(v) self.trackers[1].append(self.grid) self.trackers[2].append(self.tracker) @@ -1283,8 +1350,16 @@ class Snapper: self.trackers[6].append(self.dim2) self.trackers[7].append(self.trackLine) self.trackers[8].append(self.extLine2) + self.trackers[9].append(self.holdTracker) if self.grid and (not self.forceGridOff): self.grid.set() + + def addHoldPoint(self): + if self.spoint: + if self.holdTracker: + self.holdTracker.addCoords(self.spoint) + self.holdTracker.on() + self.holdPoints.append(self.spoint) if not hasattr(FreeCADGui,"Snapper"): FreeCADGui.Snapper = Snapper() diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index c5f4372913..a140fcc748 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -133,11 +133,22 @@ class snapTracker(Tracker): self.marker.markerIndex = coin.SoMarkerSet.SQUARE_FILLED_9_9 elif (style == "empty"): self.marker.markerIndex = coin.SoMarkerSet.SQUARE_LINE_9_9 + elif (style == "cross"): + self.marker.markerIndex = coin.SoMarkerSet.CROSS_9_9 else: self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9 def setCoords(self,point): self.coords.point.setValue((point.x,point.y,point.z)) + + def addCoords(self,point): + l = self.coords.point.getValues() + l.append(coin.SbVec3f(point.x,point.y,point.z)) + self.coords.point.setValues(l) + + def clear(self): + self.coords.point.deleteValues(0) + class lineTracker(Tracker): "A Line tracker, used by the tools that need to draw temporary lines"