786 lines
30 KiB
Python
786 lines
30 KiB
Python
#***************************************************************************
|
|
#* *
|
|
#* 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 Lesser General Public License (LGPL) *
|
|
#* 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"
|
|
|
|
import FreeCAD,FreeCADGui,math,Draft, DraftVecUtils
|
|
from FreeCAD import Vector
|
|
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):
|
|
global Part, DraftGeomUtils
|
|
import Part, DraftGeomUtils
|
|
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=Draft.get3DView().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=Draft.get3DView().getSceneGraph()
|
|
sg.removeChild(switch)
|
|
|
|
def on(self):
|
|
self.switch.whichChild = 0
|
|
self.Visible = True
|
|
|
|
def off(self):
|
|
self.switch.whichChild = -1
|
|
self.Visible = False
|
|
|
|
def lowerTracker(self):
|
|
'''lowers the tracker to the bottom of the scenegraph, so
|
|
it doesn't obscure the other objects'''
|
|
if self.switch:
|
|
sg=Draft.get3DView().getSceneGraph()
|
|
sg.removeChild(self.switch)
|
|
sg.addChild(self.switch)
|
|
|
|
def raiseTracker(self):
|
|
'''raises the tracker to the top of the scenegraph, so
|
|
it obscures the other objects'''
|
|
if self.switch:
|
|
sg=Draft.get3DView().getSceneGraph()
|
|
sg.removeChild(self.switch)
|
|
sg.insertChild(self.switch,0)
|
|
|
|
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 == "dot"):
|
|
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
|
|
|
|
def setCoords(self,point):
|
|
self.coords.point.setValue((point.x,point.y,point.z))
|
|
|
|
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 = FreeCAD.DraftWorkingPlane.u
|
|
self.v = FreeCAD.DraftWorkingPlane.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(DraftVecUtils.project(diagonal,self.v))
|
|
inpoint2 = self.origin.add(DraftVecUtils.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 = FreeCAD.DraftWorkingPlane.u.cross(FreeCAD.DraftWorkingPlane.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 ((DraftVecUtils.project(diag,self.u)).Length,(DraftVecUtils.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 not pts:
|
|
return
|
|
elif 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):
|
|
import Part
|
|
if (self.p1 != None) and (self.p2 != None):
|
|
points = [DraftVecUtils.tup(self.p1,True),DraftVecUtils.tup(self.p2,True),\
|
|
DraftVecUtils.tup(self.p1,True),DraftVecUtils.tup(self.p2,True)]
|
|
if self.p3 != None:
|
|
p1 = self.p1
|
|
p4 = self.p2
|
|
if DraftVecUtils.equals(p1,p4):
|
|
proj = None
|
|
else:
|
|
base = Part.Line(p1,p4).toShape()
|
|
proj = DraftGeomUtils.findDistance(self.p3,base)
|
|
if not proj:
|
|
p2 = p1
|
|
p3 = p4
|
|
else:
|
|
p2 = p1.add(DraftVecUtils.neg(proj))
|
|
p3 = p4.add(DraftVecUtils.neg(proj))
|
|
points = [DraftVecUtils.tup(p1),DraftVecUtils.tup(p2),DraftVecUtils.tup(p3),DraftVecUtils.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 = FreeCAD.DraftWorkingPlane.u
|
|
rad = pt.sub(center)
|
|
return(DraftVecUtils.angle(rad,base,FreeCAD.DraftWorkingPlane.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):
|
|
import Part
|
|
if self.circle: self.sep.removeChild(self.circle)
|
|
self.circle = None
|
|
if self.endangle < self.startangle:
|
|
c = Part.makeCircle(1,Vector(0,0,0),FreeCAD.DraftWorkingPlane.axis,self.endangle,self.startangle)
|
|
else:
|
|
c = Part.makeCircle(1,Vector(0,0,0),FreeCAD.DraftWorkingPlane.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]
|
|
rootsep = coin.SoSeparator()
|
|
if not isinstance(sel,list):
|
|
sel = [sel]
|
|
for obj in sel:
|
|
rootsep.addChild(self.getNode(obj))
|
|
self.children.append(rootsep)
|
|
Tracker.__init__(self,children=self.children)
|
|
|
|
def update(self,obj):
|
|
"recreates the ghost from a new object"
|
|
obj.ViewObject.show()
|
|
self.finalize()
|
|
sep = getNode(obj)
|
|
Tracker.__init__(self,children=[self.sep])
|
|
self.on()
|
|
obj.ViewObject.hide()
|
|
|
|
def move(self,delta):
|
|
"moves the ghost to a given position, relative from its start position"
|
|
self.trans.translation.setValue([delta.x,delta.y,delta.z])
|
|
|
|
def rotate(self,axis,angle):
|
|
"rotates the ghost of a given angle"
|
|
self.trans.rotation.setValue(coin.SbVec3f(DraftVecUtils.tup(axis)),angle)
|
|
|
|
def center(self,point):
|
|
"sets the rotation/scale center of the ghost"
|
|
self.trans.center.setValue(point.x,point.y,point.z)
|
|
|
|
def scale(self,delta):
|
|
"scales the ghost by the given factor"
|
|
self.trans.scaleFactor.setValue([delta.x,delta.y,delta.z])
|
|
|
|
def getNode(self,obj):
|
|
"returns a coin node representing the given object"
|
|
if isinstance(obj,Part.Shape):
|
|
return self.getNodeLight(obj)
|
|
elif obj.isDerivedFrom("Part::Feature"):
|
|
return self.getNodeFull(obj)
|
|
else:
|
|
return self.getNodeFull(obj)
|
|
|
|
def getNode(self,obj):
|
|
"gets a coin node which is a full copy of the current representation"
|
|
sep = coin.SoSeparator()
|
|
try:
|
|
sep.addChild(obj.ViewObject.RootNode.copy())
|
|
except:
|
|
pass
|
|
return sep
|
|
|
|
def getNodeLight(self,shape):
|
|
"extract a lighter version directly from a shape"
|
|
# very error-prone, will be obsoleted ASAP
|
|
sep = coin.SoSeparator()
|
|
try:
|
|
inputstr = coin.SoInput()
|
|
inputstr.setBuffer(shape.writeInventor())
|
|
coinobj = coin.SoDB.readAll(inputstr)
|
|
# only add wireframe or full node?
|
|
sep.addChild(coinobj.getChildren()[1])
|
|
# sep.addChild(coinobj)
|
|
except:
|
|
pass
|
|
return sep
|
|
|
|
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 = Draft.get3DView().getPoint((100,100))
|
|
p2 = Draft.get3DView().getPoint((110,100))
|
|
bl = (p2.sub(p1)).Length * (Draft.getParam("snapRange")/2)
|
|
pick = coin.SoPickStyle()
|
|
pick.style.setValue(coin.SoPickStyle.UNPICKABLE)
|
|
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(pick)
|
|
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 = FreeCAD.DraftWorkingPlane.getRotation().Rotation.Q
|
|
else:
|
|
plm = FreeCAD.DraftWorkingPlane.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 = DraftGeomUtils.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,forceclosed=False):
|
|
if wire:
|
|
if self.closed or forceclosed:
|
|
self.line.numVertices.setValue(len(wire.Vertexes)+1)
|
|
else:
|
|
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 or forceclosed:
|
|
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]
|
|
|
|
pick = coin.SoPickStyle()
|
|
pick.style.setValue(coin.SoPickStyle.UNPICKABLE)
|
|
|
|
self.trans = coin.SoTransform()
|
|
self.trans.translation.setValue([0,0,0])
|
|
|
|
bound = (self.numlines/2)*self.space
|
|
pts = []
|
|
mpts = []
|
|
apts = []
|
|
for i in range(self.numlines+1):
|
|
curr = -bound + i*self.space
|
|
z = 0
|
|
if i/float(self.mainlines) == i/self.mainlines:
|
|
if round(curr,4) == 0:
|
|
apts.extend([[-bound,curr,z],[bound,curr,z]])
|
|
apts.extend([[curr,-bound,z],[curr,bound,z]])
|
|
else:
|
|
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 = []
|
|
aidx = []
|
|
for p in range(0,len(pts),2):
|
|
idx.append(2)
|
|
for mp in range(0,len(mpts),2):
|
|
midx.append(2)
|
|
for ap in range(0,len(apts),2):
|
|
aidx.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)
|
|
mat3 = coin.SoMaterial()
|
|
mat3.transparency.setValue(0)
|
|
mat3.diffuseColor.setValue(col)
|
|
self.coords3 = coin.SoCoordinate3()
|
|
self.coords3.point.setValues(apts)
|
|
lines3 = coin.SoLineSet()
|
|
lines3.numVertices.setValues(aidx)
|
|
s = coin.SoSeparator()
|
|
s.addChild(pick)
|
|
s.addChild(self.trans)
|
|
s.addChild(mat1)
|
|
s.addChild(self.coords1)
|
|
s.addChild(lines1)
|
|
s.addChild(mat2)
|
|
s.addChild(self.coords2)
|
|
s.addChild(lines2)
|
|
s.addChild(mat3)
|
|
s.addChild(self.coords3)
|
|
s.addChild(lines3)
|
|
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 = FreeCAD.DraftWorkingPlane.getRotation().Rotation.Q
|
|
P = FreeCAD.DraftWorkingPlane.position
|
|
self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]])
|
|
self.trans.translation.setValue([P.x,P.y,P.z])
|
|
self.on()
|
|
|
|
def getClosestNode(self,point):
|
|
"returns the closest node from the given point"
|
|
# get the 2D coords.
|
|
# point = FreeCAD.DraftWorkingPlane.projectPoint(point)
|
|
pt = FreeCAD.DraftWorkingPlane.getLocalCoords(point)
|
|
pu = (round(pt.x/self.space,0))*self.space
|
|
pv = (round(pt.y/self.space,0))*self.space
|
|
pt = FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(pu,pv,0))
|
|
return pt
|
|
|
|
class boxTracker(Tracker):
|
|
"A box tracker, can be based on a line object"
|
|
def __init__(self,line=None,width=0.1,height=1):
|
|
self.trans = coin.SoTransform()
|
|
m = coin.SoMaterial()
|
|
m.transparency.setValue(0.8)
|
|
m.diffuseColor.setValue([0.4,0.4,0.6])
|
|
self.cube = coin.SoCube()
|
|
self.cube.height.setValue(width)
|
|
self.cube.depth.setValue(height)
|
|
self.baseline = None
|
|
if line:
|
|
self.baseline = line
|
|
self.update()
|
|
Tracker.__init__(self,children=[self.trans,m,self.cube])
|
|
|
|
def update(self,line=None,normal=None):
|
|
import WorkingPlane, DraftGeomUtils
|
|
if not normal:
|
|
normal = FreeCAD.DraftWorkingPlane.axis
|
|
if line:
|
|
if isinstance(line,list):
|
|
bp = line[0]
|
|
lvec = line[1].sub(line[0])
|
|
else:
|
|
lvec = DraftGeomUtils.vec(line.Shape.Edges[0])
|
|
bp = line.Shape.Edges[0].Vertexes[0].Point
|
|
elif self.baseline:
|
|
lvec = DraftGeomUtils.vec(self.baseline.Shape.Edges[0])
|
|
bp = self.baseline.Shape.Edges[0].Vertexes[0].Point
|
|
else:
|
|
return
|
|
right = lvec.cross(normal)
|
|
self.cube.width.setValue(lvec.Length)
|
|
p = WorkingPlane.getPlacementFromPoints([bp,bp.add(lvec),bp.add(right)])
|
|
self.trans.rotation.setValue(p.Rotation.Q)
|
|
bp = bp.add(DraftVecUtils.scale(lvec,0.5))
|
|
bp = bp.add(DraftVecUtils.scaleTo(normal,self.cube.depth.getValue()/2))
|
|
self.pos(bp)
|
|
|
|
def pos(self,p):
|
|
self.trans.translation.setValue(DraftVecUtils.tup(p))
|
|
|
|
def width(self,w=None):
|
|
if w:
|
|
self.cube.height.setValue(w)
|
|
else:
|
|
return self.cube.height.getValue()
|
|
|
|
def length(self,l=None):
|
|
if l:
|
|
self.cube.width.setValue(l)
|
|
else:
|
|
return self.cube.width.getValue()
|
|
|
|
def height(self,h=None):
|
|
if h:
|
|
self.cube.depth.setValue(h)
|
|
self.update()
|
|
else:
|
|
return self.cube.depth.getValue()
|
|
|
|
class radiusTracker(Tracker):
|
|
"A tracker that displays a transparent sphere to inicate a radius"
|
|
def __init__(self,position=FreeCAD.Vector(0,0,0),radius=1):
|
|
self.trans = coin.SoTransform()
|
|
self.trans.translation.setValue([position.x,position.y,position.z])
|
|
m = coin.SoMaterial()
|
|
m.transparency.setValue(0.9)
|
|
m.diffuseColor.setValue([0,1,0])
|
|
self.sphere = coin.SoSphere()
|
|
self.sphere.radius.setValue(radius)
|
|
self.baseline = None
|
|
Tracker.__init__(self,children=[self.trans,m,self.sphere])
|
|
|
|
def update(self,arg1,arg2=None):
|
|
if isinstance(arg1,FreeCAD.Vector):
|
|
self.trans.translation.setValue([arg1.x,arg1.y,arg1.z])
|
|
else:
|
|
self.sphere.radius.setValue(arg1)
|
|
if arg2 != None:
|
|
if isinstance(arg2,FreeCAD.Vector):
|
|
self.trans.translation.setValue([arg2.x,arg2.y,arg2.z])
|
|
else:
|
|
self.sphere.radius.setValue(arg2)
|