Draft: small fixes

This commit is contained in:
Yorik van Havre 2012-07-17 19:57:52 -03:00
parent 14562ba540
commit 4e9412c0ae
3 changed files with 251 additions and 229 deletions

View File

@ -21,7 +21,7 @@
#* * #* *
#*************************************************************************** #***************************************************************************
import FreeCAD,FreeCADGui,Draft,math,DraftVecUtils import FreeCAD,FreeCADGui,Draft,math,DraftVecUtils,ArchCommands
from FreeCAD import Vector from FreeCAD import Vector
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from pivy import coin from pivy import coin

View File

@ -333,10 +333,15 @@ class SelectPlane(DraftTool):
sel = FreeCADGui.Selection.getSelectionEx() sel = FreeCADGui.Selection.getSelectionEx()
if len(sel) == 1: if len(sel) == 1:
sel = sel[0] sel = sel[0]
if sel.HasSubObjects: self.ui = FreeCADGui.draftToolBar
if Draft.getType(sel.Object) == "Axis":
plane.alignToEdges(sel.Object.Shape.Edges)
self.display(plane.axis)
self.finish()
return
elif sel.HasSubObjects:
if len(sel.SubElementNames) == 1: if len(sel.SubElementNames) == 1:
if "Face" in sel.SubElementNames[0]: if "Face" in sel.SubElementNames[0]:
self.ui = FreeCADGui.draftToolBar
plane.alignToFace(sel.SubObjects[0], self.offset) plane.alignToFace(sel.SubObjects[0], self.offset)
self.display(plane.axis) self.display(plane.axis)
self.finish() self.finish()

View File

@ -34,267 +34,284 @@ This module provides a class called plane to assist in selecting and maintaining
''' '''
class plane: class plane:
'''A WorkPlane object''' '''A WorkPlane object'''
def __init__(self): def __init__(self):
# keep track of active document. Reset view when doc changes. # keep track of active document. Reset view when doc changes.
self.doc = None self.doc = None
# self.weak is true if the plane has been defined by self.setup or has been reset # self.weak is true if the plane has been defined by self.setup or has been reset
self.weak = True self.weak = True
# u, v axes and position define plane, perpendicular axis is handy, though redundant. # u, v axes and position define plane, perpendicular axis is handy, though redundant.
self.u = Vector(1,0,0) self.u = Vector(1,0,0)
self.v = Vector(0,1,0) self.v = Vector(0,1,0)
self.axis = Vector(0,0,1) self.axis = Vector(0,0,1)
self.position = Vector(0,0,0) self.position = Vector(0,0,0)
# a placeholder for a stored state # a placeholder for a stored state
self.stored = None self.stored = None
def __repr__(self): def __repr__(self):
return "Workplane x="+str(DraftVecUtils.rounded(self.u))+" y="+str(DraftVecUtils.rounded(self.v))+" z="+str(DraftVecUtils.rounded(self.axis)) return "Workplane x="+str(DraftVecUtils.rounded(self.u))+" y="+str(DraftVecUtils.rounded(self.v))+" z="+str(DraftVecUtils.rounded(self.axis))
def offsetToPoint(self, p, direction=None): def offsetToPoint(self, p, direction=None):
''' '''
Return the signed distance from p to the plane, such Return the signed distance from p to the plane, such
that p + offsetToPoint(p)*direction lies on the plane. that p + offsetToPoint(p)*direction lies on the plane.
direction defaults to -plane.axis direction defaults to -plane.axis
''' '''
''' '''
A picture will help explain the computation: A picture will help explain the computation:
p p
//| //|
/ / | / / |
/ / | / / |
/ / | / / |
/ / | / / |
-------------------- plane -----c-----x-----a-------- -------------------- plane -----c-----x-----a--------
Here p is the specified point, Here p is the specified point,
c is a point (in this case plane.position) on the plane c is a point (in this case plane.position) on the plane
x is the intercept on the plane from p in the specified direction, and x is the intercept on the plane from p in the specified direction, and
a is the perpendicular intercept on the plane (i.e. along plane.axis) a is the perpendicular intercept on the plane (i.e. along plane.axis)
Using vertival bars to denote the length operator, Using vertival bars to denote the length operator,
|ap| = |cp| * cos(apc) = |xp| * cos(apx) |ap| = |cp| * cos(apc) = |xp| * cos(apx)
so so
|xp| = |cp| * cos(apc) / cos(apx) |xp| = |cp| * cos(apc) / cos(apx)
= (cp . axis) / (direction . axis) = (cp . axis) / (direction . axis)
''' '''
if direction == None: direction = self.axis if direction == None: direction = self.axis
return direction.dot(self.position.sub(p)) return direction.dot(self.position.sub(p))
def projectPoint(self, p, direction=None): def projectPoint(self, p, direction=None):
'''project point onto plane, default direction is orthogonal''' '''project point onto plane, default direction is orthogonal'''
if not direction: if not direction:
direction = self.axis direction = self.axis
lp = self.getLocalCoords(p) lp = self.getLocalCoords(p)
gp = self.getGlobalCoords(Vector(lp.x,lp.y,0)) gp = self.getGlobalCoords(Vector(lp.x,lp.y,0))
a = direction.getAngle(gp.sub(p)) a = direction.getAngle(gp.sub(p))
if a > math.pi/2: if a > math.pi/2:
direction = DraftVecUtils.neg(direction) direction = DraftVecUtils.neg(direction)
a = math.pi - a a = math.pi - a
ld = self.getLocalRot(direction) ld = self.getLocalRot(direction)
gd = self.getGlobalRot(Vector(ld.x,ld.y,0)) gd = self.getGlobalRot(Vector(ld.x,ld.y,0))
hyp = abs(math.tan(a) * lp.z) hyp = abs(math.tan(a) * lp.z)
return gp.add(DraftVecUtils.scaleTo(gd,hyp)) return gp.add(DraftVecUtils.scaleTo(gd,hyp))
def projectPointOld(self, p, direction=None): def projectPointOld(self, p, direction=None):
'''project point onto plane, default direction is orthogonal. Obsolete''' '''project point onto plane, default direction is orthogonal. Obsolete'''
if not direction: if not direction:
direction = self.axis direction = self.axis
t = Vector(direction) t = Vector(direction)
#t.normalize() #t.normalize()
a = round(t.getAngle(self.axis),DraftVecUtils.precision()) a = round(t.getAngle(self.axis),DraftVecUtils.precision())
pp = round((math.pi)/2,DraftVecUtils.precision()) pp = round((math.pi)/2,DraftVecUtils.precision())
if a == pp: if a == pp:
return p return p
t.multiply(self.offsetToPoint(p, direction)) t.multiply(self.offsetToPoint(p, direction))
return p.add(t) return p.add(t)
def alignToPointAndAxis(self, point, axis, offset, upvec=None): def alignToPointAndAxis(self, point, axis, offset, upvec=None):
self.doc = FreeCAD.ActiveDocument self.doc = FreeCAD.ActiveDocument
self.axis = axis; self.axis = axis;
self.axis.normalize() self.axis.normalize()
if (DraftVecUtils.equals(axis, Vector(1,0,0))): if (DraftVecUtils.equals(axis, Vector(1,0,0))):
self.u = Vector(0,1,0) self.u = Vector(0,1,0)
self.v = Vector(0,0,1) self.v = Vector(0,0,1)
elif (DraftVecUtils.equals(axis, Vector(-1,0,0))): elif (DraftVecUtils.equals(axis, Vector(-1,0,0))):
self.u = Vector(0,-1,0) self.u = Vector(0,-1,0)
self.v = Vector(0,0,1) self.v = Vector(0,0,1)
elif upvec: elif upvec:
self.v = upvec self.v = upvec
self.v.normalize() self.v.normalize()
self.u = self.v.cross(self.axis) self.u = self.v.cross(self.axis)
else: else:
self.v = axis.cross(Vector(1,0,0)) self.v = axis.cross(Vector(1,0,0))
self.v.normalize() self.v.normalize()
self.u = DraftVecUtils.rotate(self.v, -math.pi/2, self.axis) self.u = DraftVecUtils.rotate(self.v, -math.pi/2, self.axis)
offsetVector = Vector(axis); offsetVector.multiply(offset) offsetVector = Vector(axis); offsetVector.multiply(offset)
self.position = point.add(offsetVector) self.position = point.add(offsetVector)
self.weak = False self.weak = False
# FreeCAD.Console.PrintMessage("(position = " + str(self.position) + ")\n") # FreeCAD.Console.PrintMessage("(position = " + str(self.position) + ")\n")
# FreeCAD.Console.PrintMessage("Current workplane: x="+str(DraftVecUtils.rounded(self.u))+" y="+str(DraftVecUtils.rounded(self.v))+" z="+str(DraftVecUtils.rounded(self.axis))+"\n") # FreeCAD.Console.PrintMessage("Current workplane: x="+str(DraftVecUtils.rounded(self.u))+" y="+str(DraftVecUtils.rounded(self.v))+" z="+str(DraftVecUtils.rounded(self.axis))+"\n")
def alignToCurve(self, shape, offset): def alignToCurve(self, shape, offset):
if shape.ShapeType == 'Edge': if shape.ShapeType == 'Edge':
#??? TODO: process curve here. look at shape.edges[0].Curve #??? TODO: process curve here. look at shape.edges[0].Curve
return False return False
elif shape.ShapeType == 'Wire': elif shape.ShapeType == 'Wire':
#??? TODO: determine if edges define a plane #??? TODO: determine if edges define a plane
return False return False
else: else:
return False return False
def alignToFace(self, shape, offset=0): def alignToEdges(self,edges):
# Set face to the unique selected face, if found # use a list of edges to find a plane position
if shape.ShapeType == 'Face': if len(edges) > 2:
#we should really use face.tangentAt to get u and v here, and implement alignToUVPoint return False
self.alignToPointAndAxis(shape.Faces[0].CenterOfMass, shape.Faces[0].normalAt(0,0), offset) # for axes systems, we suppose the 2 first edges are parallel
return True # ??? TODO: exclude other cases first
else: v1 = edges[0].Vertexes[-1].Point.sub(edges[0].Vertexes[0].Point)
return False v2 = edges[1].Vertexes[0].Point.sub(edges[0].Vertexes[0].Point)
v3 = v1.cross(v2)
v1.normalize()
v2.normalize()
v3.normalize()
print v1,v2,v3
self.u = v1
self.v = v2
self.axis = v3
def alignToSelection(self, offset): def alignToFace(self, shape, offset=0):
'''If selection uniquely defines a plane, align working plane to it. Return success (bool)''' # Set face to the unique selected face, if found
sex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name) if shape.ShapeType == 'Face':
if len(sex) == 0: #we should really use face.tangentAt to get u and v here, and implement alignToUVPoint
return False self.alignToPointAndAxis(shape.Faces[0].CenterOfMass, shape.Faces[0].normalAt(0,0), offset)
elif len(sex) == 1: return True
if not sex[0].Object.isDerivedFrom("Part::Shape"): else:
return False return False
return self.alignToCurve(sex[0].Object.Shape, offset) \
or self.alignToFace(sex[0].Object.Shape, offset) \
or (len(sex[0].SubObjects) == 1 and self.alignToFace(sex[0].SubObjects[0], offset))
else:
# len(sex) > 2, look for point and line, three points, etc.
return False
def setup(self, direction, point, upvec=None): def alignToSelection(self, offset):
'''If working plane is undefined, define it!''' '''If selection uniquely defines a plane, align working plane to it. Return success (bool)'''
if self.weak: sex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name)
self.alignToPointAndAxis(point, direction, 0, upvec) if len(sex) == 0:
self.weak = True return False
elif len(sex) == 1:
if not sex[0].Object.isDerivedFrom("Part::Shape"):
return False
return self.alignToCurve(sex[0].Object.Shape, offset) \
or self.alignToFace(sex[0].Object.Shape, offset) \
or (len(sex[0].SubObjects) == 1 and self.alignToFace(sex[0].SubObjects[0], offset))
else:
# len(sex) > 2, look for point and line, three points, etc.
return False
def reset(self): def setup(self, direction, point, upvec=None):
self.doc = None '''If working plane is undefined, define it!'''
self.weak = True if self.weak:
self.alignToPointAndAxis(point, direction, 0, upvec)
self.weak = True
def getRotation(self): def reset(self):
"returns a placement describing the working plane orientation ONLY" self.doc = None
m = DraftVecUtils.getPlaneRotation(self.u,self.v,self.axis) self.weak = True
return FreeCAD.Placement(m)
def getPlacement(self): def getRotation(self):
"returns the placement of the working plane" "returns a placement describing the working plane orientation ONLY"
m = FreeCAD.Matrix( m = DraftVecUtils.getPlaneRotation(self.u,self.v,self.axis)
self.u.x,self.v.x,self.axis.x,self.position.x, return FreeCAD.Placement(m)
self.u.y,self.v.y,self.axis.y,self.position.y,
self.u.z,self.v.z,self.axis.z,self.position.z,
0.0,0.0,0.0,1.0)
return FreeCAD.Placement(m)
def setFromPlacement(self,pl): def getPlacement(self):
"sets the working plane from a placement (rotaton ONLY)" "returns the placement of the working plane"
rot = FreeCAD.Placement(pl).Rotation m = FreeCAD.Matrix(
self.u = rot.multVec(FreeCAD.Vector(1,0,0)) self.u.x,self.v.x,self.axis.x,self.position.x,
self.v = rot.multVec(FreeCAD.Vector(0,1,0)) self.u.y,self.v.y,self.axis.y,self.position.y,
self.axis = rot.multVec(FreeCAD.Vector(0,0,1)) self.u.z,self.v.z,self.axis.z,self.position.z,
0.0,0.0,0.0,1.0)
return FreeCAD.Placement(m)
def save(self): def setFromPlacement(self,pl):
"stores the current plane state" "sets the working plane from a placement (rotaton ONLY)"
self.stored = [self.u,self.v,self.axis,self.position,self.weak] rot = FreeCAD.Placement(pl).Rotation
self.u = rot.multVec(FreeCAD.Vector(1,0,0))
self.v = rot.multVec(FreeCAD.Vector(0,1,0))
self.axis = rot.multVec(FreeCAD.Vector(0,0,1))
def restore(self): def save(self):
"restores a previously saved plane state, if exists" "stores the current plane state"
if self.stored: self.stored = [self.u,self.v,self.axis,self.position,self.weak]
self.u = self.stored[0]
self.v = self.stored[1]
self.axis = self.stored[2]
self.position = self.stored[3]
self.weak = self.stored[4]
self.stored = None
def getLocalCoords(self,point): def restore(self):
"returns the coordinates of a given point on the working plane" "restores a previously saved plane state, if exists"
pt = point.sub(self.position) if self.stored:
xv = DraftVecUtils.project(pt,self.u) self.u = self.stored[0]
x = xv.Length self.v = self.stored[1]
if xv.getAngle(self.u) > 1: self.axis = self.stored[2]
x = -x self.position = self.stored[3]
yv = DraftVecUtils.project(pt,self.v) self.weak = self.stored[4]
y = yv.Length self.stored = None
if yv.getAngle(self.v) > 1:
y = -y
zv = DraftVecUtils.project(pt,self.axis)
z = zv.Length
if zv.getAngle(self.axis) > 1:
z = -z
return Vector(x,y,z)
def getGlobalCoords(self,point): def getLocalCoords(self,point):
"returns the global coordinates of the given point, taken relatively to this working plane" "returns the coordinates of a given point on the working plane"
vx = DraftVecUtils.scale(self.u,point.x) pt = point.sub(self.position)
vy = DraftVecUtils.scale(self.v,point.y) xv = DraftVecUtils.project(pt,self.u)
vz = DraftVecUtils.scale(self.axis,point.z) x = xv.Length
pt = (vx.add(vy)).add(vz) if xv.getAngle(self.u) > 1:
return pt.add(self.position) x = -x
yv = DraftVecUtils.project(pt,self.v)
y = yv.Length
if yv.getAngle(self.v) > 1:
y = -y
zv = DraftVecUtils.project(pt,self.axis)
z = zv.Length
if zv.getAngle(self.axis) > 1:
z = -z
return Vector(x,y,z)
def getLocalRot(self,point): def getGlobalCoords(self,point):
"Same as getLocalCoords, but discards the WP position" "returns the global coordinates of the given point, taken relatively to this working plane"
xv = DraftVecUtils.project(point,self.u) vx = DraftVecUtils.scale(self.u,point.x)
x = xv.Length vy = DraftVecUtils.scale(self.v,point.y)
if xv.getAngle(self.u) > 1: vz = DraftVecUtils.scale(self.axis,point.z)
x = -x pt = (vx.add(vy)).add(vz)
yv = DraftVecUtils.project(point,self.v) return pt.add(self.position)
y = yv.Length
if yv.getAngle(self.v) > 1:
y = -y
zv = DraftVecUtils.project(point,self.axis)
z = zv.Length
if zv.getAngle(self.axis) > 1:
z = -z
return Vector(x,y,z)
def getGlobalRot(self,point): def getLocalRot(self,point):
"Same as getGlobalCoords, but discards the WP position" "Same as getLocalCoords, but discards the WP position"
vx = DraftVecUtils.scale(self.u,point.x) xv = DraftVecUtils.project(point,self.u)
vy = DraftVecUtils.scale(self.v,point.y) x = xv.Length
vz = DraftVecUtils.scale(self.axis,point.z) if xv.getAngle(self.u) > 1:
pt = (vx.add(vy)).add(vz) x = -x
return pt yv = DraftVecUtils.project(point,self.v)
y = yv.Length
if yv.getAngle(self.v) > 1:
y = -y
zv = DraftVecUtils.project(point,self.axis)
z = zv.Length
if zv.getAngle(self.axis) > 1:
z = -z
return Vector(x,y,z)
def getClosestAxis(self,point): def getGlobalRot(self,point):
"returns which of the workingplane axes is closest from the given vector" "Same as getGlobalCoords, but discards the WP position"
ax = point.getAngle(self.u) vx = DraftVecUtils.scale(self.u,point.x)
ay = point.getAngle(self.v) vy = DraftVecUtils.scale(self.v,point.y)
az = point.getAngle(self.axis) vz = DraftVecUtils.scale(self.axis,point.z)
bx = point.getAngle(DraftVecUtils.neg(self.u)) pt = (vx.add(vy)).add(vz)
by = point.getAngle(DraftVecUtils.neg(self.v)) return pt
bz = point.getAngle(DraftVecUtils.neg(self.axis))
b = min(ax,ay,az,bx,by,bz) def getClosestAxis(self,point):
if b in [ax,bx]: "returns which of the workingplane axes is closest from the given vector"
return "x" ax = point.getAngle(self.u)
elif b in [ay,by]: ay = point.getAngle(self.v)
return "y" az = point.getAngle(self.axis)
elif b in [az,bz]: bx = point.getAngle(DraftVecUtils.neg(self.u))
return "z" by = point.getAngle(DraftVecUtils.neg(self.v))
else: bz = point.getAngle(DraftVecUtils.neg(self.axis))
return None b = min(ax,ay,az,bx,by,bz)
if b in [ax,bx]:
return "x"
elif b in [ay,by]:
return "y"
elif b in [az,bz]:
return "z"
else:
return None
def getPlacementFromPoints(points): def getPlacementFromPoints(points):
"returns a placement from a list of 3 or 4 vectors" "returns a placement from a list of 3 or 4 vectors"
pl = plane() pl = plane()
try: try:
pl.position = points[0] pl.position = points[0]
pl.u = (points[1].sub(points[0]).normalize()) pl.u = (points[1].sub(points[0]).normalize())
pl.v = (points[2].sub(points[0]).normalize()) pl.v = (points[2].sub(points[0]).normalize())
if len(points) == 4: if len(points) == 4:
pl.axis = (points[3].sub(points[0]).normalize()) pl.axis = (points[3].sub(points[0]).normalize())
else: else:
pl.axis = ((pl.u).cross(pl.v)).normalize() pl.axis = ((pl.u).cross(pl.v)).normalize()
except: except:
pass pass
p = pl.getPlacement() p = pl.getPlacement()
del pl del pl
return p return p