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.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user