+ 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:
parent
6c601565e2
commit
f5d2ae2295
|
@ -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
338
src/Mod/Draft/DraftSnap.py
Normal 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
|
|
@ -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
|
||||
def msg(text=None,mode=None):
|
||||
"prints the given message on the FreeCAD status bar"
|
||||
if not text: FreeCAD.Console.PrintMessage("")
|
||||
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
|
||||
if mode == 'warning':
|
||||
FreeCAD.Console.PrintWarning(text)
|
||||
elif mode == 'error':
|
||||
FreeCAD.Console.PrintError(text)
|
||||
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
|
||||
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
|
||||
#---------------------------------------------------------------------------
|
||||
|
|
611
src/Mod/Draft/DraftTrackers.py
Normal file
611
src/Mod/Draft/DraftTrackers.py
Normal 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))
|
|
@ -11,6 +11,8 @@ data_DATA = \
|
|||
Draft.py \
|
||||
DraftTools.py \
|
||||
DraftGui.py \
|
||||
DraftSnap.py \
|
||||
DraftTrackers.py \
|
||||
WorkingPlane.py \
|
||||
importOCA.py \
|
||||
importDXF.py \
|
||||
|
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in New Issue
Block a user