+ 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
|
Draft.py
|
||||||
DraftTools.py
|
DraftTools.py
|
||||||
DraftGui.py
|
DraftGui.py
|
||||||
|
DraftSnap.py
|
||||||
|
DraftTrackers.py
|
||||||
WorkingPlane.py
|
WorkingPlane.py
|
||||||
importDXF.py
|
importDXF.py
|
||||||
importOCA.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,29 +34,16 @@ from functools import partial
|
||||||
from draftlibs import fcvec,fcgeo
|
from draftlibs import fcvec,fcgeo
|
||||||
from FreeCAD import Vector
|
from FreeCAD import Vector
|
||||||
from DraftGui import todo,QtCore,QtGui
|
from DraftGui import todo,QtCore,QtGui
|
||||||
|
from DraftSnap import *
|
||||||
|
from DraftTrackers import *
|
||||||
from pivy import coin
|
from pivy import coin
|
||||||
|
|
||||||
# loads a translation engine
|
#---------------------------------------------------------------------------
|
||||||
#locale = QtCore.QLocale(eval("QtCore.QLocale."+FreeCADGui.getLocale())).name()
|
# Preflight stuff
|
||||||
#translator = QtCore.QTranslator()
|
#---------------------------------------------------------------------------
|
||||||
#translator.load('Draft_'+locale+'.qm',':/translations/')
|
|
||||||
#QtGui.QApplication.installTranslator(translator)
|
|
||||||
FreeCADGui.updateLocale()
|
|
||||||
|
|
||||||
def translate(context,text):
|
# update the translation engine
|
||||||
"convenience function for Qt translator"
|
FreeCADGui.updateLocale()
|
||||||
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
|
# loads the fill patterns
|
||||||
FreeCAD.svgpatterns = importSVG.getContents(Draft_rc.qt_resource_data,'pattern',True)
|
FreeCAD.svgpatterns = importSVG.getContents(Draft_rc.qt_resource_data,'pattern',True)
|
||||||
|
@ -85,312 +72,24 @@ MODSNAP = MODS[Draft.getParam("modsnap")]
|
||||||
MODALT = MODS[Draft.getParam("modalt")]
|
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:
|
|
||||||
|
|
||||||
- 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):
|
def translate(context,text):
|
||||||
"returns a passive snap point"
|
"convenience function for Qt translator"
|
||||||
cur = Vector(info['x'],info['y'],info['z'])
|
return QtGui.QApplication.translate(context, text, None, QtGui.QApplication.UnicodeUTF8).toUtf8()
|
||||||
return [cur,2,cur]
|
|
||||||
|
def msg(text=None,mode=None):
|
||||||
def getScreenDist(dist,cursor):
|
"prints the given message on the FreeCAD status bar"
|
||||||
"returns a 3D distance from a screen pixels distance"
|
if not text: FreeCAD.Console.PrintMessage("")
|
||||||
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:
|
else:
|
||||||
# we have something to snap
|
if mode == 'warning':
|
||||||
obj = target.doc.getObject(snapped['Object'])
|
FreeCAD.Console.PrintWarning(text)
|
||||||
if hasattr(obj.ViewObject,"Selectable"):
|
elif mode == 'error':
|
||||||
if not obj.ViewObject.Selectable:
|
FreeCAD.Console.PrintError(text)
|
||||||
return point
|
|
||||||
if not ctrl:
|
|
||||||
# are we in passive snap?
|
|
||||||
snapArray = [getPassivePoint(snapped)]
|
|
||||||
else:
|
else:
|
||||||
snapArray = []
|
FreeCAD.Console.PrintMessage(text)
|
||||||
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
|
|
||||||
|
|
||||||
def selectObject(arg):
|
def selectObject(arg):
|
||||||
'''this is a scene even handler, to be called from the Draft tools
|
'''this is a scene even handler, to be called from the Draft tools
|
||||||
|
@ -490,592 +189,6 @@ def setMod(args,mod,state):
|
||||||
args["CtrlDown"] = state
|
args["CtrlDown"] = state
|
||||||
elif mod == "alt":
|
elif mod == "alt":
|
||||||
args["AltDown"] = state
|
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
|
# 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 \
|
Draft.py \
|
||||||
DraftTools.py \
|
DraftTools.py \
|
||||||
DraftGui.py \
|
DraftGui.py \
|
||||||
|
DraftSnap.py \
|
||||||
|
DraftTrackers.py \
|
||||||
WorkingPlane.py \
|
WorkingPlane.py \
|
||||||
importOCA.py \
|
importOCA.py \
|
||||||
importDXF.py \
|
importDXF.py \
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
<File Id="DraftPy" Name="Draft.py" DiskId="1" />
|
<File Id="DraftPy" Name="Draft.py" DiskId="1" />
|
||||||
<File Id="DraftToolsPy" Name="DraftTools.py" DiskId="1" />
|
<File Id="DraftToolsPy" Name="DraftTools.py" DiskId="1" />
|
||||||
<File Id="DraftGuiPy" Name="DraftGui.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="importDXFPy" Name="importDXF.py" DiskId="1" />
|
||||||
<File Id="importOCAPy" Name="importOCA.py" DiskId="1" />
|
<File Id="importOCAPy" Name="importOCA.py" DiskId="1" />
|
||||||
<File Id="importSVGPy" Name="importSVG.py" DiskId="1" />
|
<File Id="importSVGPy" Name="importSVG.py" DiskId="1" />
|
||||||
|
|
Loading…
Reference in New Issue
Block a user