+ separated Draft Snap and Trackers into their own files

git-svn-id: https://free-cad.svn.sourceforge.net/svnroot/free-cad/trunk@5277 e8eeb9e2-ec13-0410-a4a9-efa5cf37419d
This commit is contained in:
yorikvanhavre 2011-12-12 16:20:41 +00:00
parent 6c601565e2
commit f5d2ae2295
6 changed files with 975 additions and 907 deletions

View File

@ -5,6 +5,8 @@ SET(Draft_SRCS
Draft.py
DraftTools.py
DraftGui.py
DraftSnap.py
DraftTrackers.py
WorkingPlane.py
importDXF.py
importOCA.py

338
src/Mod/Draft/DraftSnap.py Normal file
View File

@ -0,0 +1,338 @@
#***************************************************************************
#* *
#* Copyright (c) 2011 *
#* Yorik van Havre <yorik@uncreated.net> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU General Public License (GPL) *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="FreeCAD Draft Snap tools"
__author__ = "Yorik van Havre"
__url__ = "http://free-cad.sourceforge.net"
import FreeCAD, math, Draft,
from draftlibs import fcvec,fcgeo
from FreeCAD import Vector
from pivy import coin
# last snapped objects, for quick intersection calculation
lastObj = [0,0]
def snapPoint(target,point,cursor,ctrl=False):
'''
Snap function used by the Draft tools
Currently has two modes: passive and active. Pressing CTRL while
clicking puts you in active mode:
- In passive mode (an open circle appears), your point is
snapped to the nearest point on any underlying geometry.
- In active mode (ctrl pressed, a filled circle appears), your point
can currently be snapped to the following points:
- Nodes and midpoints of all Part shapes
- Nodes and midpoints of lines/wires
- Centers and quadrant points of circles
- Endpoints of arcs
- Intersection between line, wires segments, arcs and circles
- When constrained (SHIFT pressed), Intersections between
constraining axis and lines/wires
'''
def getConstrainedPoint(edge,last,constrain):
"check for constrained snappoint"
p1 = edge.Vertexes[0].Point
p2 = edge.Vertexes[-1].Point
ar = []
if (constrain == 0):
if ((last.y > p1.y) and (last.y < p2.y) or (last.y > p2.y) and (last.y < p1.y)):
pc = (last.y-p1.y)/(p2.y-p1.y)
cp = (Vector(p1.x+pc*(p2.x-p1.x),p1.y+pc*(p2.y-p1.y),p1.z+pc*(p2.z-p1.z)))
ar.append([cp,1,cp]) # constrainpoint
if (constrain == 1):
if ((last.x > p1.x) and (last.x < p2.x) or (last.x > p2.x) and (last.x < p1.x)):
pc = (last.x-p1.x)/(p2.x-p1.x)
cp = (Vector(p1.x+pc*(p2.x-p1.x),p1.y+pc*(p2.y-p1.y),p1.z+pc*(p2.z-p1.z)))
ar.append([cp,1,cp]) # constrainpoint
return ar
def getPassivePoint(info):
"returns a passive snap point"
cur = Vector(info['x'],info['y'],info['z'])
return [cur,2,cur]
def getScreenDist(dist,cursor):
"returns a 3D distance from a screen pixels distance"
p1 = FreeCADGui.ActiveDocument.ActiveView.getPoint(cursor)
p2 = FreeCADGui.ActiveDocument.ActiveView.getPoint((cursor[0]+dist,cursor[1]))
return (p2.sub(p1)).Length
def getGridSnap(target,point):
"returns a grid snap point if available"
if target.grid:
return target.grid.getClosestNode(point)
return None
def getPerpendicular(edge,last):
"returns a point on an edge, perpendicular to the given point"
dv = last.sub(edge.Vertexes[0].Point)
nv = fcvec.project(dv,fcgeo.vec(edge))
np = (edge.Vertexes[0].Point).add(nv)
return np
# checking if alwaySnap setting is on
extractrl = False
if Draft.getParam("alwaysSnap"):
extractrl = ctrl
ctrl = True
# setting Radius
radius = getScreenDist(Draft.getParam("snapRange"),cursor)
# checking if parallel to one of the edges of the last objects
target.snap.off()
target.extsnap.off()
if (len(target.node) > 0):
for o in [lastObj[1],lastObj[0]]:
if o:
ob = target.doc.getObject(o)
if ob:
edges = ob.Shape.Edges
if len(edges)<10:
for e in edges:
if isinstance(e.Curve,Part.Line):
last = target.node[len(target.node)-1]
de = Part.Line(last,last.add(fcgeo.vec(e))).toShape()
np = getPerpendicular(e,point)
if (np.sub(point)).Length < radius:
target.snap.coords.point.setValue((np.x,np.y,np.z))
target.snap.setMarker("circle")
target.snap.on()
target.extsnap.p1(e.Vertexes[0].Point)
target.extsnap.p2(np)
target.extsnap.on()
point = np
else:
last = target.node[len(target.node)-1]
de = Part.Line(last,last.add(fcgeo.vec(e))).toShape()
np = getPerpendicular(de,point)
if (np.sub(point)).Length < radius:
target.snap.coords.point.setValue((np.x,np.y,np.z))
target.snap.setMarker("circle")
target.snap.on()
point = np
# check if we snapped to something
snapped=target.view.getObjectInfo((cursor[0],cursor[1]))
if (snapped == None):
# nothing has been snapped, check fro grid snap
gpt = getGridSnap(target,point)
if gpt:
if radius != 0:
dv = point.sub(gpt)
if dv.Length <= radius:
target.snap.coords.point.setValue((gpt.x,gpt.y,gpt.z))
target.snap.setMarker("point")
target.snap.on()
return gpt
return point
else:
# we have something to snap
obj = target.doc.getObject(snapped['Object'])
if hasattr(obj.ViewObject,"Selectable"):
if not obj.ViewObject.Selectable:
return point
if not ctrl:
# are we in passive snap?
snapArray = [getPassivePoint(snapped)]
else:
snapArray = []
comp = snapped['Component']
if obj.isDerivedFrom("Part::Feature"):
if "Edge" in comp:
# get the stored objects to calculate intersections
intedges = []
if lastObj[0]:
lo = target.doc.getObject(lastObj[0])
if lo:
if lo.isDerivedFrom("Part::Feature"):
intedges = lo.Shape.Edges
nr = int(comp[4:])-1
edge = obj.Shape.Edges[nr]
for v in edge.Vertexes:
snapArray.append([v.Point,0,v.Point])
if isinstance(edge.Curve,Part.Line):
# the edge is a line
midpoint = fcgeo.findMidpoint(edge)
snapArray.append([midpoint,1,midpoint])
if (len(target.node) > 0):
last = target.node[len(target.node)-1]
snapArray.extend(getConstrainedPoint(edge,last,target.constrain))
np = getPerpendicular(edge,last)
snapArray.append([np,1,np])
elif isinstance (edge.Curve,Part.Circle):
# the edge is an arc
rad = edge.Curve.Radius
pos = edge.Curve.Center
for i in [0,30,45,60,90,120,135,150,180,210,225,240,270,300,315,330]:
ang = math.radians(i)
cur = Vector(math.sin(ang)*rad+pos.x,math.cos(ang)*rad+pos.y,pos.z)
snapArray.append([cur,1,cur])
for i in [15,37.5,52.5,75,105,127.5,142.5,165,195,217.5,232.5,255,285,307.5,322.5,345]:
ang = math.radians(i)
cur = Vector(math.sin(ang)*rad+pos.x,math.cos(ang)*rad+pos.y,pos.z)
snapArray.append([cur,0,pos])
for e in intedges:
# get the intersection points
pt = fcgeo.findIntersection(e,edge)
if pt:
for p in pt:
snapArray.append([p,3,p])
elif "Vertex" in comp:
# directly snapped to a vertex
p = Vector(snapped['x'],snapped['y'],snapped['z'])
snapArray.append([p,0,p])
elif comp == '':
# workaround for the new view provider
p = Vector(snapped['x'],snapped['y'],snapped['z'])
snapArray.append([p,2,p])
else:
snapArray = [getPassivePoint(snapped)]
elif Draft.getType(obj) == "Dimension":
for pt in [obj.Start,obj.End,obj.Dimline]:
snapArray.append([pt,0,pt])
elif Draft.getType(obj) == "Mesh":
for v in obj.Mesh.Points:
snapArray.append([v.Vector,0,v.Vector])
if not lastObj[0]:
lastObj[0] = obj.Name
lastObj[1] = obj.Name
if (lastObj[1] != obj.Name):
lastObj[0] = lastObj[1]
lastObj[1] = obj.Name
# calculating shortest distance
shortest = 1000000000000000000
spt = Vector(snapped['x'],snapped['y'],snapped['z'])
newpoint = [Vector(0,0,0),0,Vector(0,0,0)]
for pt in snapArray:
if pt[0] == None: print "snapPoint: debug 'i[0]' is 'None'"
di = pt[0].sub(spt)
if di.Length < shortest:
shortest = di.Length
newpoint = pt
if radius != 0:
dv = point.sub(newpoint[2])
if (not extractrl) and (dv.Length > radius):
newpoint = getPassivePoint(snapped)
target.snap.coords.point.setValue((newpoint[2].x,newpoint[2].y,newpoint[2].z))
if (newpoint[1] == 1):
target.snap.setMarker("square")
elif (newpoint[1] == 0):
target.snap.setMarker("point")
elif (newpoint[1] == 3):
target.snap.setMarker("square")
else:
target.snap.setMarker("circle")
target.snap.on()
return newpoint[2]
def constrainPoint (target,pt,mobile=False,sym=False):
'''
Constrain function used by the Draft tools
On commands that need to enter several points (currently only line/wire),
you can constrain the next point to be picked to the last drawn point by
pressing SHIFT. The vertical or horizontal constraining depends on the
position of your mouse in relation to last point at the moment you press
SHIFT. if mobile=True, mobile behaviour applies. If sym=True, x alway = y
'''
point = Vector(pt)
if len(target.node) > 0:
last = target.node[-1]
dvec = point.sub(last)
affinity = plane.getClosestAxis(dvec)
if ((target.constrain == None) or mobile):
if affinity == "x":
dv = fcvec.project(dvec,plane.u)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
target.constrain = 0 #x direction
target.ui.xValue.setEnabled(True)
target.ui.yValue.setEnabled(False)
target.ui.zValue.setEnabled(False)
target.ui.xValue.setFocus()
elif affinity == "y":
dv = fcvec.project(dvec,plane.v)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.v) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
target.constrain = 1 #y direction
target.ui.xValue.setEnabled(False)
target.ui.yValue.setEnabled(True)
target.ui.zValue.setEnabled(False)
target.ui.yValue.setFocus()
elif affinity == "z":
dv = fcvec.project(dvec,plane.axis)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.axis) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
target.constrain = 2 #z direction
target.ui.xValue.setEnabled(False)
target.ui.yValue.setEnabled(False)
target.ui.zValue.setEnabled(True)
target.ui.zValue.setFocus()
else: target.constrain = 3
elif (target.constrain == 0):
dv = fcvec.project(dvec,plane.u)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
elif (target.constrain == 1):
dv = fcvec.project(dvec,plane.v)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
elif (target.constrain == 2):
dv = fcvec.project(dvec,plane.axis)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
return point

View File

@ -34,30 +34,17 @@ from functools import partial
from draftlibs import fcvec,fcgeo
from FreeCAD import Vector
from DraftGui import todo,QtCore,QtGui
from DraftSnap import *
from DraftTrackers import *
from pivy import coin
# loads a translation engine
#locale = QtCore.QLocale(eval("QtCore.QLocale."+FreeCADGui.getLocale())).name()
#translator = QtCore.QTranslator()
#translator.load('Draft_'+locale+'.qm',':/translations/')
#QtGui.QApplication.installTranslator(translator)
#---------------------------------------------------------------------------
# Preflight stuff
#---------------------------------------------------------------------------
# update the translation engine
FreeCADGui.updateLocale()
def translate(context,text):
"convenience function for Qt translator"
return QtGui.QApplication.translate(context, text, None, QtGui.QApplication.UnicodeUTF8).toUtf8()
def msg(text=None,mode=None):
"prints the given message on the FreeCAD status bar"
if not text: FreeCAD.Console.PrintMessage("")
else:
if mode == 'warning':
FreeCAD.Console.PrintWarning(text)
elif mode == 'error':
FreeCAD.Console.PrintError(text)
else:
FreeCAD.Console.PrintMessage(text)
# loads the fill patterns
FreeCAD.svgpatterns = importSVG.getContents(Draft_rc.qt_resource_data,'pattern',True)
altpat = Draft.getParam("patternFile")
@ -85,312 +72,24 @@ MODSNAP = MODS[Draft.getParam("modsnap")]
MODALT = MODS[Draft.getParam("modalt")]
#---------------------------------------------------------------------------
# Snapping stuff
# General functions
#---------------------------------------------------------------------------
def snapPoint(target,point,cursor,ctrl=False):
'''
Snap function used by the Draft tools
Currently has two modes: passive and active. Pressing CTRL while
clicking puts you in active mode:
def translate(context,text):
"convenience function for Qt translator"
return QtGui.QApplication.translate(context, text, None, QtGui.QApplication.UnicodeUTF8).toUtf8()
- In passive mode (an open circle appears), your point is
snapped to the nearest point on any underlying geometry.
- In active mode (ctrl pressed, a filled circle appears), your point
can currently be snapped to the following points:
- Nodes and midpoints of all Part shapes
- Nodes and midpoints of lines/wires
- Centers and quadrant points of circles
- Endpoints of arcs
- Intersection between line, wires segments, arcs and circles
- When constrained (SHIFT pressed), Intersections between
constraining axis and lines/wires
'''
def getConstrainedPoint(edge,last,constrain):
"check for constrained snappoint"
p1 = edge.Vertexes[0].Point
p2 = edge.Vertexes[-1].Point
ar = []
if (constrain == 0):
if ((last.y > p1.y) and (last.y < p2.y) or (last.y > p2.y) and (last.y < p1.y)):
pc = (last.y-p1.y)/(p2.y-p1.y)
cp = (Vector(p1.x+pc*(p2.x-p1.x),p1.y+pc*(p2.y-p1.y),p1.z+pc*(p2.z-p1.z)))
ar.append([cp,1,cp]) # constrainpoint
if (constrain == 1):
if ((last.x > p1.x) and (last.x < p2.x) or (last.x > p2.x) and (last.x < p1.x)):
pc = (last.x-p1.x)/(p2.x-p1.x)
cp = (Vector(p1.x+pc*(p2.x-p1.x),p1.y+pc*(p2.y-p1.y),p1.z+pc*(p2.z-p1.z)))
ar.append([cp,1,cp]) # constrainpoint
return ar
def getPassivePoint(info):
"returns a passive snap point"
cur = Vector(info['x'],info['y'],info['z'])
return [cur,2,cur]
def getScreenDist(dist,cursor):
"returns a 3D distance from a screen pixels distance"
p1 = FreeCADGui.ActiveDocument.ActiveView.getPoint(cursor)
p2 = FreeCADGui.ActiveDocument.ActiveView.getPoint((cursor[0]+dist,cursor[1]))
return (p2.sub(p1)).Length
def getGridSnap(target,point):
"returns a grid snap point if available"
if target.grid:
return target.grid.getClosestNode(point)
return None
def getPerpendicular(edge,last):
"returns a point on an edge, perpendicular to the given point"
dv = last.sub(edge.Vertexes[0].Point)
nv = fcvec.project(dv,fcgeo.vec(edge))
np = (edge.Vertexes[0].Point).add(nv)
return np
# checking if alwaySnap setting is on
extractrl = False
if Draft.getParam("alwaysSnap"):
extractrl = ctrl
ctrl = True
# setting Radius
radius = getScreenDist(Draft.getParam("snapRange"),cursor)
# checking if parallel to one of the edges of the last objects
target.snap.off()
target.extsnap.off()
if (len(target.node) > 0):
for o in [lastObj[1],lastObj[0]]:
if o:
ob = target.doc.getObject(o)
if ob:
edges = ob.Shape.Edges
if len(edges)<10:
for e in edges:
if isinstance(e.Curve,Part.Line):
last = target.node[len(target.node)-1]
de = Part.Line(last,last.add(fcgeo.vec(e))).toShape()
np = getPerpendicular(e,point)
if (np.sub(point)).Length < radius:
target.snap.coords.point.setValue((np.x,np.y,np.z))
target.snap.setMarker("circle")
target.snap.on()
target.extsnap.p1(e.Vertexes[0].Point)
target.extsnap.p2(np)
target.extsnap.on()
point = np
else:
last = target.node[len(target.node)-1]
de = Part.Line(last,last.add(fcgeo.vec(e))).toShape()
np = getPerpendicular(de,point)
if (np.sub(point)).Length < radius:
target.snap.coords.point.setValue((np.x,np.y,np.z))
target.snap.setMarker("circle")
target.snap.on()
point = np
# check if we snapped to something
snapped=target.view.getObjectInfo((cursor[0],cursor[1]))
if (snapped == None):
# nothing has been snapped, check fro grid snap
gpt = getGridSnap(target,point)
if gpt:
if radius != 0:
dv = point.sub(gpt)
if dv.Length <= radius:
target.snap.coords.point.setValue((gpt.x,gpt.y,gpt.z))
target.snap.setMarker("point")
target.snap.on()
return gpt
return point
def msg(text=None,mode=None):
"prints the given message on the FreeCAD status bar"
if not text: FreeCAD.Console.PrintMessage("")
else:
# we have something to snap
obj = target.doc.getObject(snapped['Object'])
if hasattr(obj.ViewObject,"Selectable"):
if not obj.ViewObject.Selectable:
return point
if not ctrl:
# are we in passive snap?
snapArray = [getPassivePoint(snapped)]
if mode == 'warning':
FreeCAD.Console.PrintWarning(text)
elif mode == 'error':
FreeCAD.Console.PrintError(text)
else:
snapArray = []
comp = snapped['Component']
if obj.isDerivedFrom("Part::Feature"):
if "Edge" in comp:
# get the stored objects to calculate intersections
intedges = []
if lastObj[0]:
lo = target.doc.getObject(lastObj[0])
if lo:
if lo.isDerivedFrom("Part::Feature"):
intedges = lo.Shape.Edges
nr = int(comp[4:])-1
edge = obj.Shape.Edges[nr]
for v in edge.Vertexes:
snapArray.append([v.Point,0,v.Point])
if isinstance(edge.Curve,Part.Line):
# the edge is a line
midpoint = fcgeo.findMidpoint(edge)
snapArray.append([midpoint,1,midpoint])
if (len(target.node) > 0):
last = target.node[len(target.node)-1]
snapArray.extend(getConstrainedPoint(edge,last,target.constrain))
np = getPerpendicular(edge,last)
snapArray.append([np,1,np])
elif isinstance (edge.Curve,Part.Circle):
# the edge is an arc
rad = edge.Curve.Radius
pos = edge.Curve.Center
for i in [0,30,45,60,90,120,135,150,180,210,225,240,270,300,315,330]:
ang = math.radians(i)
cur = Vector(math.sin(ang)*rad+pos.x,math.cos(ang)*rad+pos.y,pos.z)
snapArray.append([cur,1,cur])
for i in [15,37.5,52.5,75,105,127.5,142.5,165,195,217.5,232.5,255,285,307.5,322.5,345]:
ang = math.radians(i)
cur = Vector(math.sin(ang)*rad+pos.x,math.cos(ang)*rad+pos.y,pos.z)
snapArray.append([cur,0,pos])
for e in intedges:
# get the intersection points
pt = fcgeo.findIntersection(e,edge)
if pt:
for p in pt:
snapArray.append([p,3,p])
elif "Vertex" in comp:
# directly snapped to a vertex
p = Vector(snapped['x'],snapped['y'],snapped['z'])
snapArray.append([p,0,p])
elif comp == '':
# workaround for the new view provider
p = Vector(snapped['x'],snapped['y'],snapped['z'])
snapArray.append([p,2,p])
else:
snapArray = [getPassivePoint(snapped)]
elif Draft.getType(obj) == "Dimension":
for pt in [obj.Start,obj.End,obj.Dimline]:
snapArray.append([pt,0,pt])
elif Draft.getType(obj) == "Mesh":
for v in obj.Mesh.Points:
snapArray.append([v.Vector,0,v.Vector])
if not lastObj[0]:
lastObj[0] = obj.Name
lastObj[1] = obj.Name
if (lastObj[1] != obj.Name):
lastObj[0] = lastObj[1]
lastObj[1] = obj.Name
# calculating shortest distance
shortest = 1000000000000000000
spt = Vector(snapped['x'],snapped['y'],snapped['z'])
newpoint = [Vector(0,0,0),0,Vector(0,0,0)]
for pt in snapArray:
if pt[0] == None: print "snapPoint: debug 'i[0]' is 'None'"
di = pt[0].sub(spt)
if di.Length < shortest:
shortest = di.Length
newpoint = pt
if radius != 0:
dv = point.sub(newpoint[2])
if (not extractrl) and (dv.Length > radius):
newpoint = getPassivePoint(snapped)
target.snap.coords.point.setValue((newpoint[2].x,newpoint[2].y,newpoint[2].z))
if (newpoint[1] == 1):
target.snap.setMarker("square")
elif (newpoint[1] == 0):
target.snap.setMarker("point")
elif (newpoint[1] == 3):
target.snap.setMarker("square")
else:
target.snap.setMarker("circle")
target.snap.on()
return newpoint[2]
def constrainPoint (target,pt,mobile=False,sym=False):
'''
Constrain function used by the Draft tools
On commands that need to enter several points (currently only line/wire),
you can constrain the next point to be picked to the last drawn point by
pressing SHIFT. The vertical or horizontal constraining depends on the
position of your mouse in relation to last point at the moment you press
SHIFT. if mobile=True, mobile behaviour applies. If sym=True, x alway = y
'''
point = Vector(pt)
if len(target.node) > 0:
last = target.node[-1]
dvec = point.sub(last)
affinity = plane.getClosestAxis(dvec)
if ((target.constrain == None) or mobile):
if affinity == "x":
dv = fcvec.project(dvec,plane.u)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
target.constrain = 0 #x direction
target.ui.xValue.setEnabled(True)
target.ui.yValue.setEnabled(False)
target.ui.zValue.setEnabled(False)
target.ui.xValue.setFocus()
elif affinity == "y":
dv = fcvec.project(dvec,plane.v)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.v) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
target.constrain = 1 #y direction
target.ui.xValue.setEnabled(False)
target.ui.yValue.setEnabled(True)
target.ui.zValue.setEnabled(False)
target.ui.yValue.setFocus()
elif affinity == "z":
dv = fcvec.project(dvec,plane.axis)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.axis) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
target.constrain = 2 #z direction
target.ui.xValue.setEnabled(False)
target.ui.yValue.setEnabled(False)
target.ui.zValue.setEnabled(True)
target.ui.zValue.setFocus()
else: target.constrain = 3
elif (target.constrain == 0):
dv = fcvec.project(dvec,plane.u)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
elif (target.constrain == 1):
dv = fcvec.project(dvec,plane.v)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
elif (target.constrain == 2):
dv = fcvec.project(dvec,plane.axis)
point = last.add(dv)
if sym:
l = dv.Length
if dv.getAngle(plane.u) > 1:
l = -l
point = last.add(plane.getGlobalCoords(Vector(l,l,l)))
return point
FreeCAD.Console.PrintMessage(text)
def selectObject(arg):
'''this is a scene even handler, to be called from the Draft tools
@ -491,592 +190,6 @@ def setMod(args,mod,state):
elif mod == "alt":
args["AltDown"] = state
#---------------------------------------------------------------------------
# Trackers
#---------------------------------------------------------------------------
class Tracker:
"A generic Draft Tracker, to be used by other specific trackers"
def __init__(self,dotted=False,scolor=None,swidth=None,children=[],ontop=False):
self.ontop = ontop
color = coin.SoBaseColor()
color.rgb = scolor or FreeCADGui.draftToolBar.getDefaultColor("ui")
drawstyle = coin.SoDrawStyle()
if swidth:
drawstyle.lineWidth = swidth
if dotted:
drawstyle.style = coin.SoDrawStyle.LINES
drawstyle.lineWeight = 3
drawstyle.linePattern = 0x0f0f #0xaa
node = coin.SoSeparator()
for c in [drawstyle, color] + children:
node.addChild(c)
self.switch = coin.SoSwitch() # this is the on/off switch
self.switch.addChild(node)
self.switch.whichChild = -1
self.Visible = False
todo.delay(self._insertSwitch, self.switch)
def finalize(self):
todo.delay(self._removeSwitch, self.switch)
self.switch = None
def _insertSwitch(self, switch):
'''insert self.switch into the scene graph. Must not be called
from an event handler (or other scene graph traversal).'''
sg=FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
if self.ontop:
sg.insertChild(switch,0)
else:
sg.addChild(switch)
def _removeSwitch(self, switch):
'''remove self.switch from the scene graph. As with _insertSwitch,
must not be called during scene graph traversal).'''
sg=FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
sg.removeChild(switch)
def on(self):
self.switch.whichChild = 0
self.Visible = True
def off(self):
self.switch.whichChild = -1
self.Visible = False
class snapTracker(Tracker):
"A Snap Mark tracker, used by tools that support snapping"
def __init__(self):
color = coin.SoBaseColor()
color.rgb = FreeCADGui.draftToolBar.getDefaultColor("snap")
self.marker = coin.SoMarkerSet() # this is the marker symbol
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValue((0,0,0))
node = coin.SoAnnotation()
node.addChild(self.coords)
node.addChild(color)
node.addChild(self.marker)
Tracker.__init__(self,children=[node])
def setMarker(self,style):
if (style == "point"):
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
elif (style == "square"):
self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9
elif (style == "circle"):
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_LINE_9_9
class lineTracker(Tracker):
"A Line tracker, used by the tools that need to draw temporary lines"
def __init__(self,dotted=False,scolor=None,swidth=None):
line = coin.SoLineSet()
line.numVertices.setValue(2)
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValues(0,2,[[0,0,0],[1,0,0]])
Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
def p1(self,point=None):
"sets or gets the first point of the line"
if point:
self.coords.point.set1Value(0,point.x,point.y,point.z)
else:
return Vector(self.coords.point.getValues()[0].getValue())
def p2(self,point=None):
"sets or gets the second point of the line"
if point:
self.coords.point.set1Value(1,point.x,point.y,point.z)
else:
return Vector(self.coords.point.getValues()[-1].getValue())
def getLength(self):
"returns the length of the line"
p1 = Vector(self.coords.point.getValues()[0].getValue())
p2 = Vector(self.coords.point.getValues()[-1].getValue())
return (p2.sub(p1)).Length
class rectangleTracker(Tracker):
"A Rectangle tracker, used by the rectangle tool"
def __init__(self,dotted=False,scolor=None,swidth=None):
self.origin = Vector(0,0,0)
line = coin.SoLineSet()
line.numVertices.setValue(5)
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValues(0,50,[[0,0,0],[2,0,0],[2,2,0],[0,2,0],[0,0,0]])
Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
self.u = plane.u
self.v = plane.v
def setorigin(self,point):
"sets the base point of the rectangle"
self.coords.point.set1Value(0,point.x,point.y,point.z)
self.coords.point.set1Value(4,point.x,point.y,point.z)
self.origin = point
def update(self,point):
"sets the opposite (diagonal) point of the rectangle"
diagonal = point.sub(self.origin)
inpoint1 = self.origin.add(fcvec.project(diagonal,self.v))
inpoint2 = self.origin.add(fcvec.project(diagonal,self.u))
self.coords.point.set1Value(1,inpoint1.x,inpoint1.y,inpoint1.z)
self.coords.point.set1Value(2,point.x,point.y,point.z)
self.coords.point.set1Value(3,inpoint2.x,inpoint2.y,inpoint2.z)
def setPlane(self,u,v=None):
'''sets given (u,v) vectors as working plane. You can give only u
and v will be deduced automatically given current workplane'''
self.u = u
if v:
self.v = v
else:
norm = plane.u.cross(plane.v)
self.v = self.u.cross(norm)
def p1(self,point=None):
"sets or gets the base point of the rectangle"
if point:
self.setorigin(point)
else:
return Vector(self.coords.point.getValues()[0].getValue())
def p2(self):
"gets the second point (on u axis) of the rectangle"
return Vector(self.coords.point.getValues()[3].getValue())
def p3(self,point=None):
"sets or gets the opposite (diagonal) point of the rectangle"
if point:
self.update(point)
else:
return Vector(self.coords.point.getValues()[2].getValue())
def p4(self):
"gets the fourth point (on v axis) of the rectangle"
return Vector(self.coords.point.getValues()[1].getValue())
def getSize(self):
"returns (length,width) of the rectangle"
p1 = Vector(self.coords.point.getValues()[0].getValue())
p2 = Vector(self.coords.point.getValues()[2].getValue())
diag = p2.sub(p1)
return ((fcvec.project(diag,self.u)).Length,(fcvec.project(diag,self.v)).Length)
def getNormal(self):
"returns the normal of the rectangle"
return (self.u.cross(self.v)).normalize()
class dimTracker(Tracker):
"A Dimension tracker, used by the dimension tool"
def __init__(self,dotted=False,scolor=None,swidth=None):
line = coin.SoLineSet()
line.numVertices.setValue(4)
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValues(0,4,[[0,0,0],[0,0,0],[0,0,0],[0,0,0]])
Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
self.p1 = self.p2 = self.p3 = None
def update(self,pts):
if len(pts) == 1:
self.p3 = pts[0]
else:
self.p1 = pts[0]
self.p2 = pts[1]
if len(pts) > 2:
self.p3 = pts[2]
self.calc()
def calc(self):
if (self.p1 != None) and (self.p2 != None):
points = [fcvec.tup(self.p1,True),fcvec.tup(self.p2,True),\
fcvec.tup(self.p1,True),fcvec.tup(self.p2,True)]
if self.p3 != None:
p1 = self.p1
p4 = self.p2
if fcvec.equals(p1,p4):
proj = None
else:
base = Part.Line(p1,p4).toShape()
proj = fcgeo.findDistance(self.p3,base)
if not proj:
p2 = p1
p3 = p4
else:
p2 = p1.add(fcvec.neg(proj))
p3 = p4.add(fcvec.neg(proj))
points = [fcvec.tup(p1),fcvec.tup(p2),fcvec.tup(p3),fcvec.tup(p4)]
self.coords.point.setValues(0,4,points)
class bsplineTracker(Tracker):
"A bspline tracker"
def __init__(self,dotted=False,scolor=None,swidth=None,points = []):
self.bspline = None
self.points = points
self.trans = coin.SoTransform()
self.sep = coin.SoSeparator()
self.recompute()
Tracker.__init__(self,dotted,scolor,swidth,[self.trans,self.sep])
def update(self, points):
self.points = points
self.recompute()
def recompute(self):
if (len(self.points) >= 2):
if self.bspline: self.sep.removeChild(self.bspline)
self.bspline = None
c = Part.BSplineCurve()
# DNC: allows to close the curve by placing ends close to each other
if ( len(self.points) >= 3 ) and ( (self.points[0] - self.points[-1]).Length < Draft.tolerance() ):
# YVH: Added a try to bypass some hazardous situations
try:
c.interpolate(self.points[:-1], True)
except:
pass
elif self.points:
try:
c.interpolate(self.points, False)
except:
pass
c = c.toShape()
buf=c.writeInventor(2,0.01)
#fp=open("spline.iv","w")
#fp.write(buf)
#fp.close()
ivin = coin.SoInput()
ivin.setBuffer(buf)
ivob = coin.SoDB.readAll(ivin)
# In case reading from buffer failed
if ivob and ivob.getNumChildren() > 1:
self.bspline = ivob.getChild(1).getChild(0)
self.bspline.removeChild(self.bspline.getChild(0))
self.bspline.removeChild(self.bspline.getChild(0))
self.sep.addChild(self.bspline)
else:
FreeCAD.Console.PrintWarning("bsplineTracker.recompute() failed to read-in Inventor string\n")
class arcTracker(Tracker):
"An arc tracker"
def __init__(self,dotted=False,scolor=None,swidth=None,start=0,end=math.pi*2):
self.circle = None
self.startangle = math.degrees(start)
self.endangle = math.degrees(end)
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
self.sep = coin.SoSeparator()
self.recompute()
Tracker.__init__(self,dotted,scolor,swidth,[self.trans, self.sep])
def setCenter(self,cen):
"sets the center point"
self.trans.translation.setValue([cen.x,cen.y,cen.z])
def setRadius(self,rad):
"sets the radius"
self.trans.scaleFactor.setValue([rad,rad,rad])
def getRadius(self):
"returns the current radius"
return self.trans.scaleFactor.getValue()[0]
def setStartAngle(self,ang):
"sets the start angle"
self.startangle = math.degrees(ang)
self.recompute()
def setEndAngle(self,ang):
"sets the end angle"
self.endangle = math.degrees(ang)
self.recompute()
def getAngle(self,pt):
"returns the angle of a given vector"
c = self.trans.translation.getValue()
center = Vector(c[0],c[1],c[2])
base = plane.u
rad = pt.sub(center)
return(fcvec.angle(rad,base,plane.axis))
def getAngles(self):
"returns the start and end angles"
return(self.startangle,self.endangle)
def setStartPoint(self,pt):
"sets the start angle from a point"
self.setStartAngle(-self.getAngle(pt))
def setEndPoint(self,pt):
"sets the end angle from a point"
self.setEndAngle(self.getAngle(pt))
def setApertureAngle(self,ang):
"sets the end angle by giving the aperture angle"
ap = math.degrees(ang)
self.endangle = self.startangle + ap
self.recompute()
def recompute(self):
if self.circle: self.sep.removeChild(self.circle)
self.circle = None
if self.endangle < self.startangle:
c = Part.makeCircle(1,Vector(0,0,0),plane.axis,self.endangle,self.startangle)
else:
c = Part.makeCircle(1,Vector(0,0,0),plane.axis,self.startangle,self.endangle)
buf=c.writeInventor(2,0.01)
ivin = coin.SoInput()
ivin.setBuffer(buf)
ivob = coin.SoDB.readAll(ivin)
# In case reading from buffer failed
if ivob and ivob.getNumChildren() > 1:
self.circle = ivob.getChild(1).getChild(0)
self.circle.removeChild(self.circle.getChild(0))
self.circle.removeChild(self.circle.getChild(0))
self.sep.addChild(self.circle)
else:
FreeCAD.Console.PrintWarning("arcTracker.recompute() failed to read-in Inventor string\n")
class ghostTracker(Tracker):
'''A Ghost tracker, that allows to copy whole object representations.
You can pass it an object or a list of objects, or a shape.'''
def __init__(self,sel):
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
self.children = [self.trans]
self.ivsep = coin.SoSeparator()
try:
if isinstance(sel,Part.Shape):
ivin = coin.SoInput()
ivin.setBuffer(sel.writeInventor())
ivob = coin.SoDB.readAll(ivin)
self.ivsep.addChild(ivob.getChildren()[1])
else:
if not isinstance(sel,list):
sel = [sel]
for obj in sel:
self.ivsep.addChild(obj.ViewObject.RootNode.copy())
except:
print "draft: Couldn't create ghost"
self.children.append(self.ivsep)
Tracker.__init__(self,children=self.children)
def update(self,obj):
obj.ViewObject.show()
self.finalize()
self.ivsep = coin.SoSeparator()
self.ivsep.addChild(obj.ViewObject.RootNode.copy())
Tracker.__init__(self,children=[self.ivsep])
self.on()
obj.ViewObject.hide()
class editTracker(Tracker):
"A node edit tracker"
def __init__(self,pos=Vector(0,0,0),name="None",idx=0,objcol=None):
color = coin.SoBaseColor()
if objcol:
color.rgb = objcol[:3]
else:
color.rgb = FreeCADGui.draftToolBar.getDefaultColor("snap")
self.marker = coin.SoMarkerSet() # this is the marker symbol
self.marker.markerIndex = coin.SoMarkerSet.SQUARE_FILLED_9_9
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValue((pos.x,pos.y,pos.z))
selnode = coin.SoType.fromName("SoFCSelection").createInstance()
selnode.documentName.setValue(FreeCAD.ActiveDocument.Name)
selnode.objectName.setValue(name)
selnode.subElementName.setValue("EditNode"+str(idx))
node = coin.SoAnnotation()
selnode.addChild(self.coords)
selnode.addChild(color)
selnode.addChild(self.marker)
node.addChild(selnode)
Tracker.__init__(self,children=[node],ontop=True)
self.on()
def set(self,pos):
self.coords.point.setValue((pos.x,pos.y,pos.z))
def get(self):
p = self.coords.point.getValues()[0]
return Vector(p[0],p[1],p[2])
def move(self,delta):
self.set(self.get().add(delta))
class PlaneTracker(Tracker):
"A working plane tracker"
def __init__(self):
# getting screen distance
p1 = FreeCADGui.ActiveDocument.ActiveView.getPoint((100,100))
p2 = FreeCADGui.ActiveDocument.ActiveView.getPoint((110,100))
bl = (p2.sub(p1)).Length * (Draft.getParam("snapRange")/2)
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
m1 = coin.SoMaterial()
m1.transparency.setValue(0.8)
m1.diffuseColor.setValue([0.4,0.4,0.6])
c1 = coin.SoCoordinate3()
c1.point.setValues([[-bl,-bl,0],[bl,-bl,0],[bl,bl,0],[-bl,bl,0]])
f = coin.SoIndexedFaceSet()
f.coordIndex.setValues([0,1,2,3])
m2 = coin.SoMaterial()
m2.transparency.setValue(0.7)
m2.diffuseColor.setValue([0.2,0.2,0.3])
c2 = coin.SoCoordinate3()
c2.point.setValues([[0,bl,0],[0,0,0],[bl,0,0],[-.05*bl,.95*bl,0],[0,bl,0],
[.05*bl,.95*bl,0],[.95*bl,.05*bl,0],[bl,0,0],[.95*bl,-.05*bl,0]])
l = coin.SoLineSet()
l.numVertices.setValues([3,3,3])
s = coin.SoSeparator()
s.addChild(self.trans)
s.addChild(m1)
s.addChild(c1)
s.addChild(f)
s.addChild(m2)
s.addChild(c2)
s.addChild(l)
Tracker.__init__(self,children=[s])
def set(self,pos=None):
if pos:
Q = plane.getRotation().Rotation.Q
else:
plm = plane.getPlacement()
Q = plm.Rotation.Q
pos = plm.Base
self.trans.translation.setValue([pos.x,pos.y,pos.z])
self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]])
self.on()
class wireTracker(Tracker):
"A wire tracker"
def __init__(self,wire):
self.line = coin.SoLineSet()
self.closed = fcgeo.isReallyClosed(wire)
if self.closed:
self.line.numVertices.setValue(len(wire.Vertexes)+1)
else:
self.line.numVertices.setValue(len(wire.Vertexes))
self.coords = coin.SoCoordinate3()
self.update(wire)
Tracker.__init__(self,children=[self.coords,self.line])
def update(self,wire):
if wire:
self.line.numVertices.setValue(len(wire.Vertexes))
for i in range(len(wire.Vertexes)):
p=wire.Vertexes[i].Point
self.coords.point.set1Value(i,[p.x,p.y,p.z])
if self.closed:
t = len(wire.Vertexes)
p = wire.Vertexes[0].Point
self.coords.point.set1Value(t,[p.x,p.y,p.z])
class gridTracker(Tracker):
"A grid tracker"
def __init__(self):
# self.space = 1
self.space = Draft.getParam("gridSpacing")
# self.mainlines = 10
self.mainlines = Draft.getParam("gridEvery")
self.numlines = 100
col = [0.2,0.2,0.3]
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
bound = (self.numlines/2)*self.space
pts = []
mpts = []
for i in range(self.numlines+1):
curr = -bound + i*self.space
z = 0
if i/float(self.mainlines) == i/self.mainlines:
mpts.extend([[-bound,curr,z],[bound,curr,z]])
mpts.extend([[curr,-bound,z],[curr,bound,z]])
else:
pts.extend([[-bound,curr,z],[bound,curr,z]])
pts.extend([[curr,-bound,z],[curr,bound,z]])
idx = []
midx = []
for p in range(0,len(pts),2):
idx.append(2)
for mp in range(0,len(mpts),2):
midx.append(2)
mat1 = coin.SoMaterial()
mat1.transparency.setValue(0.7)
mat1.diffuseColor.setValue(col)
self.coords1 = coin.SoCoordinate3()
self.coords1.point.setValues(pts)
lines1 = coin.SoLineSet()
lines1.numVertices.setValues(idx)
mat2 = coin.SoMaterial()
mat2.transparency.setValue(0.3)
mat2.diffuseColor.setValue(col)
self.coords2 = coin.SoCoordinate3()
self.coords2.point.setValues(mpts)
lines2 = coin.SoLineSet()
lines2.numVertices.setValues(midx)
s = coin.SoSeparator()
s.addChild(self.trans)
s.addChild(mat1)
s.addChild(self.coords1)
s.addChild(lines1)
s.addChild(mat2)
s.addChild(self.coords2)
s.addChild(lines2)
Tracker.__init__(self,children=[s])
self.update()
def update(self):
bound = (self.numlines/2)*self.space
pts = []
mpts = []
for i in range(self.numlines+1):
curr = -bound + i*self.space
if i/float(self.mainlines) == i/self.mainlines:
mpts.extend([[-bound,curr,0],[bound,curr,0]])
mpts.extend([[curr,-bound,0],[curr,bound,0]])
else:
pts.extend([[-bound,curr,0],[bound,curr,0]])
pts.extend([[curr,-bound,0],[curr,bound,0]])
self.coords1.point.setValues(pts)
self.coords2.point.setValues(mpts)
def setSpacing(self,space):
self.space = space
self.update()
def setMainlines(self,ml):
self.mainlines = ml
self.update()
def set(self):
Q = plane.getRotation().Rotation.Q
self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]])
self.on()
def getClosestNode(self,point):
"returns the closest node from the given point"
# get the 2D coords.
point = plane.projectPoint(point)
u = fcvec.project(point,plane.u)
lu = u.Length
if u.getAngle(plane.u) > 1.5:
lu = -lu
v = fcvec.project(point,plane.v)
lv = v.Length
if v.getAngle(plane.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))
#---------------------------------------------------------------------------
# Helper tools
#---------------------------------------------------------------------------

View File

@ -0,0 +1,611 @@
#***************************************************************************
#* *
#* Copyright (c) 2011 *
#* Yorik van Havre <yorik@uncreated.net> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU General Public License (GPL) *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="FreeCAD Draft Trackers"
__author__ = "Yorik van Havre"
__url__ = "http://free-cad.sourceforge.net"
from pivy import coin
from DraftGui import todo
class Tracker:
"A generic Draft Tracker, to be used by other specific trackers"
def __init__(self,dotted=False,scolor=None,swidth=None,children=[],ontop=False):
self.ontop = ontop
color = coin.SoBaseColor()
color.rgb = scolor or FreeCADGui.draftToolBar.getDefaultColor("ui")
drawstyle = coin.SoDrawStyle()
if swidth:
drawstyle.lineWidth = swidth
if dotted:
drawstyle.style = coin.SoDrawStyle.LINES
drawstyle.lineWeight = 3
drawstyle.linePattern = 0x0f0f #0xaa
node = coin.SoSeparator()
for c in [drawstyle, color] + children:
node.addChild(c)
self.switch = coin.SoSwitch() # this is the on/off switch
self.switch.addChild(node)
self.switch.whichChild = -1
self.Visible = False
todo.delay(self._insertSwitch, self.switch)
def finalize(self):
todo.delay(self._removeSwitch, self.switch)
self.switch = None
def _insertSwitch(self, switch):
'''insert self.switch into the scene graph. Must not be called
from an event handler (or other scene graph traversal).'''
sg=FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
if self.ontop:
sg.insertChild(switch,0)
else:
sg.addChild(switch)
def _removeSwitch(self, switch):
'''remove self.switch from the scene graph. As with _insertSwitch,
must not be called during scene graph traversal).'''
sg=FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
sg.removeChild(switch)
def on(self):
self.switch.whichChild = 0
self.Visible = True
def off(self):
self.switch.whichChild = -1
self.Visible = False
class snapTracker(Tracker):
"A Snap Mark tracker, used by tools that support snapping"
def __init__(self):
color = coin.SoBaseColor()
color.rgb = FreeCADGui.draftToolBar.getDefaultColor("snap")
self.marker = coin.SoMarkerSet() # this is the marker symbol
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValue((0,0,0))
node = coin.SoAnnotation()
node.addChild(self.coords)
node.addChild(color)
node.addChild(self.marker)
Tracker.__init__(self,children=[node])
def setMarker(self,style):
if (style == "point"):
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
elif (style == "square"):
self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9
elif (style == "circle"):
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_LINE_9_9
class lineTracker(Tracker):
"A Line tracker, used by the tools that need to draw temporary lines"
def __init__(self,dotted=False,scolor=None,swidth=None):
line = coin.SoLineSet()
line.numVertices.setValue(2)
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValues(0,2,[[0,0,0],[1,0,0]])
Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
def p1(self,point=None):
"sets or gets the first point of the line"
if point:
self.coords.point.set1Value(0,point.x,point.y,point.z)
else:
return Vector(self.coords.point.getValues()[0].getValue())
def p2(self,point=None):
"sets or gets the second point of the line"
if point:
self.coords.point.set1Value(1,point.x,point.y,point.z)
else:
return Vector(self.coords.point.getValues()[-1].getValue())
def getLength(self):
"returns the length of the line"
p1 = Vector(self.coords.point.getValues()[0].getValue())
p2 = Vector(self.coords.point.getValues()[-1].getValue())
return (p2.sub(p1)).Length
class rectangleTracker(Tracker):
"A Rectangle tracker, used by the rectangle tool"
def __init__(self,dotted=False,scolor=None,swidth=None):
self.origin = Vector(0,0,0)
line = coin.SoLineSet()
line.numVertices.setValue(5)
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValues(0,50,[[0,0,0],[2,0,0],[2,2,0],[0,2,0],[0,0,0]])
Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
self.u = plane.u
self.v = plane.v
def setorigin(self,point):
"sets the base point of the rectangle"
self.coords.point.set1Value(0,point.x,point.y,point.z)
self.coords.point.set1Value(4,point.x,point.y,point.z)
self.origin = point
def update(self,point):
"sets the opposite (diagonal) point of the rectangle"
diagonal = point.sub(self.origin)
inpoint1 = self.origin.add(fcvec.project(diagonal,self.v))
inpoint2 = self.origin.add(fcvec.project(diagonal,self.u))
self.coords.point.set1Value(1,inpoint1.x,inpoint1.y,inpoint1.z)
self.coords.point.set1Value(2,point.x,point.y,point.z)
self.coords.point.set1Value(3,inpoint2.x,inpoint2.y,inpoint2.z)
def setPlane(self,u,v=None):
'''sets given (u,v) vectors as working plane. You can give only u
and v will be deduced automatically given current workplane'''
self.u = u
if v:
self.v = v
else:
norm = plane.u.cross(plane.v)
self.v = self.u.cross(norm)
def p1(self,point=None):
"sets or gets the base point of the rectangle"
if point:
self.setorigin(point)
else:
return Vector(self.coords.point.getValues()[0].getValue())
def p2(self):
"gets the second point (on u axis) of the rectangle"
return Vector(self.coords.point.getValues()[3].getValue())
def p3(self,point=None):
"sets or gets the opposite (diagonal) point of the rectangle"
if point:
self.update(point)
else:
return Vector(self.coords.point.getValues()[2].getValue())
def p4(self):
"gets the fourth point (on v axis) of the rectangle"
return Vector(self.coords.point.getValues()[1].getValue())
def getSize(self):
"returns (length,width) of the rectangle"
p1 = Vector(self.coords.point.getValues()[0].getValue())
p2 = Vector(self.coords.point.getValues()[2].getValue())
diag = p2.sub(p1)
return ((fcvec.project(diag,self.u)).Length,(fcvec.project(diag,self.v)).Length)
def getNormal(self):
"returns the normal of the rectangle"
return (self.u.cross(self.v)).normalize()
class dimTracker(Tracker):
"A Dimension tracker, used by the dimension tool"
def __init__(self,dotted=False,scolor=None,swidth=None):
line = coin.SoLineSet()
line.numVertices.setValue(4)
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValues(0,4,[[0,0,0],[0,0,0],[0,0,0],[0,0,0]])
Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line])
self.p1 = self.p2 = self.p3 = None
def update(self,pts):
if len(pts) == 1:
self.p3 = pts[0]
else:
self.p1 = pts[0]
self.p2 = pts[1]
if len(pts) > 2:
self.p3 = pts[2]
self.calc()
def calc(self):
if (self.p1 != None) and (self.p2 != None):
points = [fcvec.tup(self.p1,True),fcvec.tup(self.p2,True),\
fcvec.tup(self.p1,True),fcvec.tup(self.p2,True)]
if self.p3 != None:
p1 = self.p1
p4 = self.p2
if fcvec.equals(p1,p4):
proj = None
else:
base = Part.Line(p1,p4).toShape()
proj = fcgeo.findDistance(self.p3,base)
if not proj:
p2 = p1
p3 = p4
else:
p2 = p1.add(fcvec.neg(proj))
p3 = p4.add(fcvec.neg(proj))
points = [fcvec.tup(p1),fcvec.tup(p2),fcvec.tup(p3),fcvec.tup(p4)]
self.coords.point.setValues(0,4,points)
class bsplineTracker(Tracker):
"A bspline tracker"
def __init__(self,dotted=False,scolor=None,swidth=None,points = []):
self.bspline = None
self.points = points
self.trans = coin.SoTransform()
self.sep = coin.SoSeparator()
self.recompute()
Tracker.__init__(self,dotted,scolor,swidth,[self.trans,self.sep])
def update(self, points):
self.points = points
self.recompute()
def recompute(self):
if (len(self.points) >= 2):
if self.bspline: self.sep.removeChild(self.bspline)
self.bspline = None
c = Part.BSplineCurve()
# DNC: allows to close the curve by placing ends close to each other
if ( len(self.points) >= 3 ) and ( (self.points[0] - self.points[-1]).Length < Draft.tolerance() ):
# YVH: Added a try to bypass some hazardous situations
try:
c.interpolate(self.points[:-1], True)
except:
pass
elif self.points:
try:
c.interpolate(self.points, False)
except:
pass
c = c.toShape()
buf=c.writeInventor(2,0.01)
#fp=open("spline.iv","w")
#fp.write(buf)
#fp.close()
ivin = coin.SoInput()
ivin.setBuffer(buf)
ivob = coin.SoDB.readAll(ivin)
# In case reading from buffer failed
if ivob and ivob.getNumChildren() > 1:
self.bspline = ivob.getChild(1).getChild(0)
self.bspline.removeChild(self.bspline.getChild(0))
self.bspline.removeChild(self.bspline.getChild(0))
self.sep.addChild(self.bspline)
else:
FreeCAD.Console.PrintWarning("bsplineTracker.recompute() failed to read-in Inventor string\n")
class arcTracker(Tracker):
"An arc tracker"
def __init__(self,dotted=False,scolor=None,swidth=None,start=0,end=math.pi*2):
self.circle = None
self.startangle = math.degrees(start)
self.endangle = math.degrees(end)
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
self.sep = coin.SoSeparator()
self.recompute()
Tracker.__init__(self,dotted,scolor,swidth,[self.trans, self.sep])
def setCenter(self,cen):
"sets the center point"
self.trans.translation.setValue([cen.x,cen.y,cen.z])
def setRadius(self,rad):
"sets the radius"
self.trans.scaleFactor.setValue([rad,rad,rad])
def getRadius(self):
"returns the current radius"
return self.trans.scaleFactor.getValue()[0]
def setStartAngle(self,ang):
"sets the start angle"
self.startangle = math.degrees(ang)
self.recompute()
def setEndAngle(self,ang):
"sets the end angle"
self.endangle = math.degrees(ang)
self.recompute()
def getAngle(self,pt):
"returns the angle of a given vector"
c = self.trans.translation.getValue()
center = Vector(c[0],c[1],c[2])
base = plane.u
rad = pt.sub(center)
return(fcvec.angle(rad,base,plane.axis))
def getAngles(self):
"returns the start and end angles"
return(self.startangle,self.endangle)
def setStartPoint(self,pt):
"sets the start angle from a point"
self.setStartAngle(-self.getAngle(pt))
def setEndPoint(self,pt):
"sets the end angle from a point"
self.setEndAngle(self.getAngle(pt))
def setApertureAngle(self,ang):
"sets the end angle by giving the aperture angle"
ap = math.degrees(ang)
self.endangle = self.startangle + ap
self.recompute()
def recompute(self):
if self.circle: self.sep.removeChild(self.circle)
self.circle = None
if self.endangle < self.startangle:
c = Part.makeCircle(1,Vector(0,0,0),plane.axis,self.endangle,self.startangle)
else:
c = Part.makeCircle(1,Vector(0,0,0),plane.axis,self.startangle,self.endangle)
buf=c.writeInventor(2,0.01)
ivin = coin.SoInput()
ivin.setBuffer(buf)
ivob = coin.SoDB.readAll(ivin)
# In case reading from buffer failed
if ivob and ivob.getNumChildren() > 1:
self.circle = ivob.getChild(1).getChild(0)
self.circle.removeChild(self.circle.getChild(0))
self.circle.removeChild(self.circle.getChild(0))
self.sep.addChild(self.circle)
else:
FreeCAD.Console.PrintWarning("arcTracker.recompute() failed to read-in Inventor string\n")
class ghostTracker(Tracker):
'''A Ghost tracker, that allows to copy whole object representations.
You can pass it an object or a list of objects, or a shape.'''
def __init__(self,sel):
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
self.children = [self.trans]
self.ivsep = coin.SoSeparator()
try:
if isinstance(sel,Part.Shape):
ivin = coin.SoInput()
ivin.setBuffer(sel.writeInventor())
ivob = coin.SoDB.readAll(ivin)
self.ivsep.addChild(ivob.getChildren()[1])
else:
if not isinstance(sel,list):
sel = [sel]
for obj in sel:
self.ivsep.addChild(obj.ViewObject.RootNode.copy())
except:
print "draft: Couldn't create ghost"
self.children.append(self.ivsep)
Tracker.__init__(self,children=self.children)
def update(self,obj):
obj.ViewObject.show()
self.finalize()
self.ivsep = coin.SoSeparator()
self.ivsep.addChild(obj.ViewObject.RootNode.copy())
Tracker.__init__(self,children=[self.ivsep])
self.on()
obj.ViewObject.hide()
class editTracker(Tracker):
"A node edit tracker"
def __init__(self,pos=Vector(0,0,0),name="None",idx=0,objcol=None):
color = coin.SoBaseColor()
if objcol:
color.rgb = objcol[:3]
else:
color.rgb = FreeCADGui.draftToolBar.getDefaultColor("snap")
self.marker = coin.SoMarkerSet() # this is the marker symbol
self.marker.markerIndex = coin.SoMarkerSet.SQUARE_FILLED_9_9
self.coords = coin.SoCoordinate3() # this is the coordinate
self.coords.point.setValue((pos.x,pos.y,pos.z))
selnode = coin.SoType.fromName("SoFCSelection").createInstance()
selnode.documentName.setValue(FreeCAD.ActiveDocument.Name)
selnode.objectName.setValue(name)
selnode.subElementName.setValue("EditNode"+str(idx))
node = coin.SoAnnotation()
selnode.addChild(self.coords)
selnode.addChild(color)
selnode.addChild(self.marker)
node.addChild(selnode)
Tracker.__init__(self,children=[node],ontop=True)
self.on()
def set(self,pos):
self.coords.point.setValue((pos.x,pos.y,pos.z))
def get(self):
p = self.coords.point.getValues()[0]
return Vector(p[0],p[1],p[2])
def move(self,delta):
self.set(self.get().add(delta))
class PlaneTracker(Tracker):
"A working plane tracker"
def __init__(self):
# getting screen distance
p1 = FreeCADGui.ActiveDocument.ActiveView.getPoint((100,100))
p2 = FreeCADGui.ActiveDocument.ActiveView.getPoint((110,100))
bl = (p2.sub(p1)).Length * (Draft.getParam("snapRange")/2)
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
m1 = coin.SoMaterial()
m1.transparency.setValue(0.8)
m1.diffuseColor.setValue([0.4,0.4,0.6])
c1 = coin.SoCoordinate3()
c1.point.setValues([[-bl,-bl,0],[bl,-bl,0],[bl,bl,0],[-bl,bl,0]])
f = coin.SoIndexedFaceSet()
f.coordIndex.setValues([0,1,2,3])
m2 = coin.SoMaterial()
m2.transparency.setValue(0.7)
m2.diffuseColor.setValue([0.2,0.2,0.3])
c2 = coin.SoCoordinate3()
c2.point.setValues([[0,bl,0],[0,0,0],[bl,0,0],[-.05*bl,.95*bl,0],[0,bl,0],
[.05*bl,.95*bl,0],[.95*bl,.05*bl,0],[bl,0,0],[.95*bl,-.05*bl,0]])
l = coin.SoLineSet()
l.numVertices.setValues([3,3,3])
s = coin.SoSeparator()
s.addChild(self.trans)
s.addChild(m1)
s.addChild(c1)
s.addChild(f)
s.addChild(m2)
s.addChild(c2)
s.addChild(l)
Tracker.__init__(self,children=[s])
def set(self,pos=None):
if pos:
Q = plane.getRotation().Rotation.Q
else:
plm = plane.getPlacement()
Q = plm.Rotation.Q
pos = plm.Base
self.trans.translation.setValue([pos.x,pos.y,pos.z])
self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]])
self.on()
class wireTracker(Tracker):
"A wire tracker"
def __init__(self,wire):
self.line = coin.SoLineSet()
self.closed = fcgeo.isReallyClosed(wire)
if self.closed:
self.line.numVertices.setValue(len(wire.Vertexes)+1)
else:
self.line.numVertices.setValue(len(wire.Vertexes))
self.coords = coin.SoCoordinate3()
self.update(wire)
Tracker.__init__(self,children=[self.coords,self.line])
def update(self,wire):
if wire:
self.line.numVertices.setValue(len(wire.Vertexes))
for i in range(len(wire.Vertexes)):
p=wire.Vertexes[i].Point
self.coords.point.set1Value(i,[p.x,p.y,p.z])
if self.closed:
t = len(wire.Vertexes)
p = wire.Vertexes[0].Point
self.coords.point.set1Value(t,[p.x,p.y,p.z])
class gridTracker(Tracker):
"A grid tracker"
def __init__(self):
# self.space = 1
self.space = Draft.getParam("gridSpacing")
# self.mainlines = 10
self.mainlines = Draft.getParam("gridEvery")
self.numlines = 100
col = [0.2,0.2,0.3]
self.trans = coin.SoTransform()
self.trans.translation.setValue([0,0,0])
bound = (self.numlines/2)*self.space
pts = []
mpts = []
for i in range(self.numlines+1):
curr = -bound + i*self.space
z = 0
if i/float(self.mainlines) == i/self.mainlines:
mpts.extend([[-bound,curr,z],[bound,curr,z]])
mpts.extend([[curr,-bound,z],[curr,bound,z]])
else:
pts.extend([[-bound,curr,z],[bound,curr,z]])
pts.extend([[curr,-bound,z],[curr,bound,z]])
idx = []
midx = []
for p in range(0,len(pts),2):
idx.append(2)
for mp in range(0,len(mpts),2):
midx.append(2)
mat1 = coin.SoMaterial()
mat1.transparency.setValue(0.7)
mat1.diffuseColor.setValue(col)
self.coords1 = coin.SoCoordinate3()
self.coords1.point.setValues(pts)
lines1 = coin.SoLineSet()
lines1.numVertices.setValues(idx)
mat2 = coin.SoMaterial()
mat2.transparency.setValue(0.3)
mat2.diffuseColor.setValue(col)
self.coords2 = coin.SoCoordinate3()
self.coords2.point.setValues(mpts)
lines2 = coin.SoLineSet()
lines2.numVertices.setValues(midx)
s = coin.SoSeparator()
s.addChild(self.trans)
s.addChild(mat1)
s.addChild(self.coords1)
s.addChild(lines1)
s.addChild(mat2)
s.addChild(self.coords2)
s.addChild(lines2)
Tracker.__init__(self,children=[s])
self.update()
def update(self):
bound = (self.numlines/2)*self.space
pts = []
mpts = []
for i in range(self.numlines+1):
curr = -bound + i*self.space
if i/float(self.mainlines) == i/self.mainlines:
mpts.extend([[-bound,curr,0],[bound,curr,0]])
mpts.extend([[curr,-bound,0],[curr,bound,0]])
else:
pts.extend([[-bound,curr,0],[bound,curr,0]])
pts.extend([[curr,-bound,0],[curr,bound,0]])
self.coords1.point.setValues(pts)
self.coords2.point.setValues(mpts)
def setSpacing(self,space):
self.space = space
self.update()
def setMainlines(self,ml):
self.mainlines = ml
self.update()
def set(self):
Q = plane.getRotation().Rotation.Q
self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]])
self.on()
def getClosestNode(self,point):
"returns the closest node from the given point"
# get the 2D coords.
point = plane.projectPoint(point)
u = fcvec.project(point,plane.u)
lu = u.Length
if u.getAngle(plane.u) > 1.5:
lu = -lu
v = fcvec.project(point,plane.v)
lv = v.Length
if v.getAngle(plane.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))

View File

@ -11,6 +11,8 @@ data_DATA = \
Draft.py \
DraftTools.py \
DraftGui.py \
DraftSnap.py \
DraftTrackers.py \
WorkingPlane.py \
importOCA.py \
importDXF.py \

View File

@ -29,6 +29,8 @@
<File Id="DraftPy" Name="Draft.py" DiskId="1" />
<File Id="DraftToolsPy" Name="DraftTools.py" DiskId="1" />
<File Id="DraftGuiPy" Name="DraftGui.py" DiskId="1" />
<File Id="DraftSnapPy" Name="DraftSnap.py" DiskId="1" />
<File Id="DraftTrackersPy" Name="DraftTrackers.py" DiskId="1" />
<File Id="importDXFPy" Name="importDXF.py" DiskId="1" />
<File Id="importOCAPy" Name="importOCA.py" DiskId="1" />
<File Id="importSVGPy" Name="importSVG.py" DiskId="1" />