Draft: Huge bugfixing in snap and grid behaviour

This commit is contained in:
Yorik van Havre 2012-06-07 18:20:42 -03:00
parent 10de9074c6
commit fd29f4aa07
5 changed files with 275 additions and 186 deletions

View File

@ -959,6 +959,10 @@ class DraftToolBar:
self.wipeLine()
elif txt.endsWith("s"):
self.togglesnap()
elif txt.endsWith("["):
self.toggleradius(1)
elif txt.endsWith("]"):
self.toggleradius(-1)
elif txt.endsWith("c"):
if self.closeButton.isVisible():
self.closeLine()
@ -1158,6 +1162,11 @@ class DraftToolBar:
if hasattr(FreeCADGui,"Snapper"):
FreeCADGui.Snapper.toggle()
def toggleradius(self,val):
if hasattr(FreeCADGui,"Snapper"):
par = Draft.getParam("snapRange")
Draft.setParam("snapRange",par+val)
FreeCADGui.Snapper.showradius()
#---------------------------------------------------------------------------
# TaskView operations

View File

@ -70,11 +70,12 @@ class Snapper:
self.grid = None
self.constrainLine = None
self.trackLine = None
self.radiusTracker = None
self.snapInfo = None
self.lastSnappedObject = None
self.active = True
self.forceGridOff = False
self.trackers = [[],[],[],[]] # view, grid, snap, extline
self.trackers = [[],[],[],[],[]] # view, grid, snap, extline, radius
self.polarAngles = [90,45]
@ -126,10 +127,13 @@ class Snapper:
def cstr(point):
"constrains if needed"
if constrain:
return self.constrain(point,lastpoint)
fpt = self.constrain(point,lastpoint)
else:
self.unconstrain()
return point
fpt = point
if self.radiusTracker:
self.radiusTracker.update(fpt)
return fpt
snaps = []
self.snapInfo = None
@ -150,6 +154,7 @@ class Snapper:
self.grid = self.trackers[1][i]
self.tracker = self.trackers[2][i]
self.extLine = self.trackers[3][i]
self.radiusTracker = self.trackers[4][i]
else:
if Draft.getParam("grid"):
self.grid = DraftTrackers.gridTracker()
@ -157,19 +162,23 @@ class Snapper:
self.grid = None
self.tracker = DraftTrackers.snapTracker()
self.extLine = DraftTrackers.lineTracker(dotted=True)
self.radiusTracker = DraftTrackers.radiusTracker()
self.trackers[0].append(v)
self.trackers[1].append(self.grid)
self.trackers[2].append(self.tracker)
self.trackers[3].append(self.extLine)
self.trackers[4].append(self.radiusTracker)
# getting current snap Radius
if not self.radius:
self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos)
self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos)
if self.radiusTracker:
self.radiusTracker.update(self.radius)
self.radiusTracker.off()
# set the grid
if self.grid and (not self.forceGridOff):
self.grid.set()
# activate snap
oldActive = False
if Draft.getParam("alwaysSnap"):
@ -195,7 +204,7 @@ class Snapper:
eline = None
point,eline = self.snapToPolar(point,lastpoint)
point,eline = self.snapToExtensions(point,lastpoint,constrain,eline)
if not self.snapInfo:
# nothing has been snapped, check fro grid snap
@ -223,6 +232,9 @@ class Snapper:
snaps = [self.snapToVertex(self.snapInfo)]
else:
# first stick to the snapped object
point = self.snapToVertex(self.snapInfo)[0]
# active snapping
comp = self.snapInfo['Component']
@ -324,7 +336,12 @@ class Snapper:
view = Draft.get3DView()
pt = view.getPoint(x,y)
if hasattr(FreeCAD,"DraftWorkingPlane"):
dv = view.getViewDirection()
if view.getCameraType() == "Perspective":
camera = view.getCameraNode()
p = camera.getField("position").getValue()
vd = pt.sub(Vector(p[0],p[1],p[2]))
else:
dv = view.getViewDirection()
return FreeCAD.DraftWorkingPlane.projectPoint(pt,dv)
else:
return pt
@ -641,6 +658,8 @@ class Snapper:
self.tracker.off()
if self.extLine:
self.extLine.off()
if self.radiusTracker:
self.radiusTracker.off()
if self.grid:
if not Draft.getParam("alwaysShowGrid"):
self.grid.off()
@ -875,6 +894,13 @@ class Snapper:
self.toolbarButtons[i].setEnabled(False)
self.saveSnapModes()
def showradius(self):
"shows the snap radius indicator"
self.radius = self.getScreenDist(Draft.getParam("snapRange"),(400,300))
if self.radiusTracker:
self.radiusTracker.update(self.radius)
self.radiusTracker.on()
def isEnabled(self,but):
"returns true if the given button is turned on"
for b in self.toolbarButtons:

View File

@ -143,22 +143,6 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True):
cmod = hasMod(args,MODCONSTRAIN)
point = FreeCADGui.Snapper.snap(args["Position"],lastpoint=last,active=amod,constrain=cmod)
info = FreeCADGui.Snapper.snapInfo
# project onto working plane if needed
if (not plane.weak) and workingplane:
# working plane was explicitely selected - project onto it
viewDirection = view.getViewDirection()
if view.getCameraType() == "Perspective":
camera = view.getCameraNode()
p = camera.getField("position").getValue()
# view is from camera to point:
viewDirection = point.sub(Vector(p[0],p[1],p[2]))
# if we are not snapping to anything, project along view axis,
# otherwise perpendicularly
if view.getObjectInfo((args["Position"][0],args["Position"][1])):
pass
# point = plane.projectPoint(point)
else:
point = plane.projectPoint(point, viewDirection)
ctrlPoint = Vector(point)
mask = FreeCADGui.Snapper.affinity
if target.node:
@ -169,8 +153,19 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True):
else: ui.displayPoint(point, plane=plane, mask=mask)
return point,ctrlPoint,info
def getSupport(args):
def getSupport(args=None):
"returns the supporting object and sets the working plane"
if not args:
sel = FreeCADGui.Selection.getSelectionEx()
if len(sel) == 1:
sel = sel[0]
if sel.HasSubObjects:
if len(sel.SubElementNames) == 1:
if "Face" in sel.SubElementNames[0]:
plane.alignToFace(sel.SubObjects[0])
return sel.Object
return None
snapped = Draft.get3DView().getObjectInfo((args["Position"][0],args["Position"][1]))
if not snapped: return None
obj = None
@ -204,11 +199,125 @@ def setMod(args,mod,state):
elif mod == "alt":
args["AltDown"] = state
#---------------------------------------------------------------------------
# Base Class
#---------------------------------------------------------------------------
class DraftTool:
"The base class of all Draft Tools"
def __init__(self):
self.commitList = []
def IsActive(self):
if FreeCADGui.ActiveDocument:
return True
else:
return False
def Activated(self,name="None"):
if FreeCAD.activeDraftCommand:
FreeCAD.activeDraftCommand.finish()
global Part, DraftGeomUtils
import Part, DraftGeomUtils
self.ui = None
self.call = None
self.support = None
self.commitList = []
self.doc = FreeCAD.ActiveDocument
if not self.doc:
self.finish()
return
FreeCAD.activeDraftCommand = self
self.view = Draft.get3DView()
self.ui = FreeCADGui.draftToolBar
self.ui.sourceCmd = self
self.ui.setTitle(name)
self.ui.show()
rot = self.view.getCameraNode().getField("orientation").getValue()
upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue())
plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv)
self.node = []
self.pos = []
self.constrain = None
self.obj = None
self.extendedCopy = False
self.ui.setTitle(name)
self.featureName = name
#self.snap = snapTracker()
#self.extsnap = lineTracker(dotted=True)
self.planetrack = None
if Draft.getParam("showPlaneTracker"):
self.planetrack = PlaneTracker()
def finish(self):
self.node = []
#self.snap.finalize()
#self.extsnap.finalize()
FreeCAD.activeDraftCommand = None
if self.ui:
self.ui.offUi()
self.ui.sourceCmd = None
#self.ui.cross(False)
msg("")
if self.planetrack:
self.planetrack.finalize()
if self.support:
plane.restore()
FreeCADGui.Snapper.off()
if self.call:
self.view.removeEventCallback("SoEvent",self.call)
self.call = None
if self.commitList:
todo.delayCommit(self.commitList)
self.commitList = []
def commit(self,name,func):
"stores actions to be committed to the FreeCAD document"
# print "committing"
self.commitList.append((name,func))
def getStrings(self):
"returns a couple of useful strings fro building python commands"
# current plane rotation
p = plane.getRotation()
qr = p.Rotation.Q
qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')'
# support object
if self.support:
sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")'
else:
sup = 'None'
# contents of self.node
points='['
for n in self.node:
if len(points) > 1:
points += ','
points += DraftVecUtils.toString(n)
points += ']'
# fill mode
if self.ui:
fil = str(bool(self.ui.fillmode))
else:
fil = "True"
return qr,sup,points,fil
#---------------------------------------------------------------------------
# Helper tools
#---------------------------------------------------------------------------
class SelectPlane:
#---------------------------------------------------------------------------
class SelectPlane(DraftTool):
"The Draft_SelectPlane FreeCAD command definition"
def GetResources(self):
@ -216,20 +325,10 @@ class SelectPlane:
'Accel' : "W, P",
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "SelectPlane"),
'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "Select a working plane for geometry creation")}
def IsActive(self):
if FreeCADGui.ActiveDocument:
return True
else:
return False
def Activated(self):
if FreeCAD.activeDraftCommand:
FreeCAD.activeDraftCommand.finish()
DraftTool.Activated(self)
self.offset = 0
self.ui = None
self.call = None
self.doc = FreeCAD.ActiveDocument
if self.doc:
sel = FreeCADGui.Selection.getSelectionEx()
if len(sel) == 1:
@ -242,12 +341,8 @@ class SelectPlane:
self.display(plane.axis)
self.finish()
return
FreeCAD.activeDraftCommand = self
self.view = Draft.get3DView()
self.ui = FreeCADGui.draftToolBar
self.ui.selectPlaneUi()
msg(translate("draft", "Pick a face to define the drawing plane\n"))
self.ui.sourceCmd = self
if plane.alignToSelection(self.offset):
FreeCADGui.Selection.clearSelection()
self.display(plane.axis)
@ -316,135 +411,25 @@ class SelectPlane:
self.ui.wplabel.setText(plv+suffix)
FreeCADGui.Snapper.setGrid()
def finish(self):
if self.call:
self.view.removeEventCallback("SoEvent",self.call)
FreeCAD.activeDraftCommand = None
if self.ui:
self.ui.offUi()
#---------------------------------------------------------------------------
# Geometry constructors
#---------------------------------------------------------------------------
class DraftTool:
"The base class of all Draft Tools"
def __init__(self):
self.commitList = []
def Activated(self,name="None"):
if FreeCAD.activeDraftCommand:
FreeCAD.activeDraftCommand.finish()
global Part, DraftGeomUtils
import Part, DraftGeomUtils
self.ui = None
self.call = None
self.support = None
self.commitList = []
self.doc = FreeCAD.ActiveDocument
if not self.doc:
self.finish()
return
FreeCAD.activeDraftCommand = self
self.view = Draft.get3DView()
self.ui = FreeCADGui.draftToolBar
self.ui.sourceCmd = self
self.ui.setTitle(name)
self.ui.show()
rot = self.view.getCameraNode().getField("orientation").getValue()
upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue())
plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv)
self.node = []
self.pos = []
self.constrain = None
self.obj = None
self.extendedCopy = False
self.ui.setTitle(name)
self.featureName = name
#self.snap = snapTracker()
#self.extsnap = lineTracker(dotted=True)
self.planetrack = None
if Draft.getParam("showPlaneTracker"):
self.planetrack = PlaneTracker()
def finish(self):
self.node = []
#self.snap.finalize()
#self.extsnap.finalize()
FreeCAD.activeDraftCommand = None
if self.ui:
self.ui.offUi()
self.ui.sourceCmd=None
#self.ui.cross(False)
msg("")
if self.planetrack:
self.planetrack.finalize()
if self.support:
plane.restore()
FreeCADGui.Snapper.off()
if self.call:
self.view.removeEventCallback("SoEvent",self.call)
self.call = None
if self.commitList:
todo.delayCommit(self.commitList)
self.commitList = []
def commit(self,name,func):
"stores actions to be committed to the FreeCAD document"
# print "committing"
self.commitList.append((name,func))
def getStrings(self):
"returns a couple of useful strings fro building python commands"
# current plane rotation
p = plane.getRotation()
qr = p.Rotation.Q
qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')'
# support object
if self.support:
sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")'
else:
sup = 'None'
# contents of self.node
points='['
for n in self.node:
if len(points) > 1:
points += ','
points += DraftVecUtils.toString(n)
points += ']'
# fill mode
if self.ui:
fil = str(bool(self.ui.fillmode))
else:
fil = "True"
return qr,sup,points,fil
class Creator(DraftTool):
"A generic Draft Creator Tool used by creation tools such as line or arc"
def __init__(self):
DraftTool.__init__(self)
def IsActive(self):
if FreeCADGui.ActiveDocument:
return True
else:
return False
def Activated(self,name="None"):
DraftTool.Activated(self)
self.support = getSupport()
class Line(Creator):
"The Line FreeCAD command definition"
def __init__(self, wiremode=False):
Creator.__init__(self)
self.isWire = wiremode
def GetResources(self):
@ -508,7 +493,8 @@ class Line(Creator):
if (arg["Position"] == self.pos):
self.finish(False,cont=True)
else:
if not self.node: self.support = getSupport(arg)
if (not self.node) and (not self.support):
self.support = getSupport(arg)
point,ctrlPoint,info = getPoint(self,arg)
self.pos = arg["Position"]
self.node.append(point)
@ -632,7 +618,8 @@ class BSpline(Line):
if (arg["Position"] == self.pos):
self.finish(False,cont=True)
else:
if not self.node: self.support = getSupport(arg)
if (not self.node) and (not self.support):
self.support = getSupport(arg)
point,ctrlPoint,info = getPoint(self,arg)
self.pos = arg["Position"]
self.node.append(point)
@ -822,7 +809,8 @@ class Rectangle(Creator):
if (arg["Position"] == self.pos):
self.finish()
else:
if not self.node: self.support = getSupport(arg)
if (not self.node) and (not self.support):
self.support = getSupport(arg)
point,ctrlPoint,info = getPoint(self,arg)
self.appendPoint(point)
@ -1019,7 +1007,8 @@ class Arc(Creator):
if not DraftVecUtils.isNull(viewdelta):
point = point.add(DraftVecUtils.neg(viewdelta))
if (self.step == 0): # choose center
self.support = getSupport(arg)
if not self.support:
self.support = getSupport(arg)
if hasMod(arg,MODALT):
snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
if snapped:
@ -1288,7 +1277,8 @@ class Polygon(Creator):
if not DraftVecUtils.isNull(viewdelta):
point = point.add(DraftVecUtils.neg(viewdelta))
if (self.step == 0): # choose center
if not self.node: self.support = getSupport(arg)
if (not self.node) and (not self.support):
self.support = getSupport(arg)
if hasMod(arg,MODALT):
snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1]))
if snapped:
@ -1635,7 +1625,8 @@ class Dimension(Creator):
elif arg["Type"] == "SoMouseButtonEvent":
if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
point,ctrlPoint,info = getPoint(self,arg)
if not self.node: self.support = getSupport(arg)
if (not self.node) and (not self.support):
self.support = getSupport(arg)
if hasMod(arg,MODALT) and (len(self.node)<3):
print "snapped: ",info
if info:

View File

@ -640,24 +640,16 @@ class gridTracker(Tracker):
def getClosestNode(self,point):
"returns the closest node from the given point"
print "in:",point
# get the 2D coords.
point = FreeCAD.DraftWorkingPlane.projectPoint(point)
u = DraftVecUtils.project(point,FreeCAD.DraftWorkingPlane.u)
lu = u.Length
if u.getAngle(FreeCAD.DraftWorkingPlane.u) > 1.5:
lu = -lu
v = DraftVecUtils.project(point,FreeCAD.DraftWorkingPlane.v)
lv = v.Length
if v.getAngle(FreeCAD.DraftWorkingPlane.v) > 1.5:
lv = -lv
# print "u = ",u," v = ",v
# find nearest grid node
pu = (round(lu/self.space,0))*self.space
pv = (round(lv/self.space,0))*self.space
rot = FreeCAD.Rotation()
rot.Q = self.trans.rotation.getValue().getValue()
return rot.multVec(Vector(pu,pv,0))
# point = FreeCAD.DraftWorkingPlane.projectPoint(point)
pt = FreeCAD.DraftWorkingPlane.getLocalCoords(point)
pu = (round(pt.x/self.space,0))*self.space
pv = (round(pt.y/self.space,0))*self.space
pt = FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(pu,pv,0))
print "out:",pt
return pt
class boxTracker(Tracker):
"A box tracker, can be based on a line object"
def __init__(self,line=None,width=0.1,height=1):
@ -719,3 +711,27 @@ class boxTracker(Tracker):
self.update()
else:
return self.cube.depth.getValue()
class radiusTracker(Tracker):
"A tracker that displays a transparent sphere to inicate a radius"
def __init__(self,position=FreeCAD.Vector(0,0,0),radius=1):
self.trans = coin.SoTransform()
self.trans.translation.setValue([position.x,position.y,position.z])
m = coin.SoMaterial()
m.transparency.setValue(0.9)
m.diffuseColor.setValue([0,1,0])
self.sphere = coin.SoSphere()
self.sphere.radius.setValue(radius)
self.baseline = None
Tracker.__init__(self,children=[self.trans,m,self.sphere])
def update(self,arg1,arg2=None):
if isinstance(arg1,FreeCAD.Vector):
self.trans.translation.setValue([arg1.x,arg1.y,arg1.z])
else:
self.sphere.radius.setValue(arg1)
if arg2 != None:
if isinstance(arg2,FreeCAD.Vector):
self.trans.translation.setValue([arg2.x,arg2.y,arg2.z])
else:
self.sphere.radius.setValue(arg2)

View File

@ -86,8 +86,29 @@ class plane:
def projectPoint(self, p, direction=None):
'''project point onto plane, default direction is orthogonal'''
if not direction: direction = self.axis
if not direction:
direction = self.axis
lp = self.getLocalCoords(p)
gp = self.getGlobalCoords(Vector(lp.x,lp.y,0))
a = direction.getAngle(gp.sub(p))
if a > math.pi/2:
direction = DraftVecUtils.neg(direction)
a = math.pi - a
ld = self.getLocalRot(direction)
gd = self.getGlobalRot(Vector(ld.x,ld.y,0))
hyp = abs(math.tan(a) * lp.z)
return gp.add(DraftVecUtils.scaleTo(gd,hyp))
def projectPointOld(self, p, direction=None):
'''project point onto plane, default direction is orthogonal. Obsolete'''
if not direction:
direction = self.axis
t = Vector(direction)
#t.normalize()
a = round(t.getAngle(self.axis),DraftVecUtils.precision())
pp = round((math.pi)/2,DraftVecUtils.precision())
if a == pp:
return p
t.multiply(self.offsetToPoint(p, direction))
return p.add(t)
@ -196,6 +217,31 @@ class plane:
def getLocalCoords(self,point):
"returns the coordinates of a given point on the working plane"
pt = point.sub(self.position)
xv = DraftVecUtils.project(pt,self.u)
x = xv.Length
if xv.getAngle(self.u) > 1:
x = -x
yv = DraftVecUtils.project(pt,self.v)
y = yv.Length
if yv.getAngle(self.v) > 1:
y = -y
zv = DraftVecUtils.project(pt,self.axis)
z = zv.Length
if zv.getAngle(self.axis) > 1:
z = -z
return Vector(x,y,z)
def getGlobalCoords(self,point):
"returns the global coordinates of the given point, taken relatively to this working plane"
vx = DraftVecUtils.scale(self.u,point.x)
vy = DraftVecUtils.scale(self.v,point.y)
vz = DraftVecUtils.scale(self.axis,point.z)
pt = (vx.add(vy)).add(vz)
return pt.add(self.position)
def getLocalRot(self,point):
"Same as getLocalCoords, but discards the WP position"
xv = DraftVecUtils.project(point,self.u)
x = xv.Length
if xv.getAngle(self.u) > 1:
@ -210,13 +256,14 @@ class plane:
z = -z
return Vector(x,y,z)
def getGlobalCoords(self,point):
"returns the global coordinates of the given point, taken relatively to this working plane"
def getGlobalRot(self,point):
"Same as getGlobalCoords, but discards the WP position"
vx = DraftVecUtils.scale(self.u,point.x)
vy = DraftVecUtils.scale(self.v,point.y)
vz = DraftVecUtils.scale(self.axis,point.z)
return (vx.add(vy)).add(vz)
pt = (vx.add(vy)).add(vz)
return pt
def getClosestAxis(self,point):
"returns which of the workingplane axes is closest from the given vector"
ax = point.getAngle(self.u)