Exemplo n.º 1
0
    def constrain(self,point,basepoint=None,axis=None):
        '''constrain(point,basepoint=None,axis=None: Returns a
        constrained point. Axis can be "x","y" or "z" or a custom vector. If None,
        the closest working plane axis will be picked.
        Basepoint is the base point used to figure out from where the point
        must be constrained. If no basepoint is given, the current point is
        used as basepoint.'''

        # without the Draft module fully loaded, no axes system!"
        if not hasattr(FreeCAD,"DraftWorkingPlane"):
            return point

        point = Vector(point)

        # setup trackers if needed
        if not self.constrainLine:
            self.constrainLine = DraftTrackers.lineTracker(dotted=True)

        # setting basepoint
        if not basepoint:
            if not self.basepoint:
                self.basepoint = point
        else:
            self.basepoint = basepoint
        delta = point.sub(self.basepoint)

        # setting constraint axis
        if not self.affinity:
            self.affinity = FreeCAD.DraftWorkingPlane.getClosestAxis(delta)
        if isinstance(axis,FreeCAD.Vector):
            self.constraintAxis = axis
        elif axis == "x":
            self.constraintAxis = FreeCAD.DraftWorkingPlane.u
        elif axis == "y":
            self.constraintAxis = FreeCAD.DraftWorkingPlane.v
        elif axis == "z":
            self.constraintAxis = FreeCAD.DraftWorkingPlane.axis
        else:
            if self.affinity == "x":
                self.constraintAxis = FreeCAD.DraftWorkingPlane.u
            elif self.affinity == "y":
                self.constraintAxis = FreeCAD.DraftWorkingPlane.v
            else:
                self.constraintAxis = FreeCAD.DraftWorkingPlane.axis
                
        # calculating constrained point
        cdelta = fcvec.project(delta,self.constraintAxis)
        npoint = self.basepoint.add(cdelta)

        # setting constrain line
        if self.constrainLine:
            if point != npoint:
                self.constrainLine.p1(point)
                self.constrainLine.p2(npoint)
                self.constrainLine.on()
            else:
                self.constrainLine.off()
		
        return npoint       
 def setString(self,text=None):
     "sets the dim string to the given value or auto value"
     self.dimnode.param1.setValue(.5)
     p1 = Vector(self.dimnode.pnts.getValues()[0].getValue())
     p2 = Vector(self.dimnode.pnts.getValues()[-1].getValue())
     m = self.dimnode.datumtype.getValue()
     if m == 2:
         self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(1,0,0))).Length
     elif m == 3:
         self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(0,1,0))).Length
     else:
         self.Distance = (p2.sub(p1)).Length 
     if not text:
         text = Draft.getParam("dimPrecision",2)
         text = "%."+str(text)+"f"
         text = (text % self.Distance)
     self.dimnode.string.setValue(text)
Exemplo n.º 3
0
def equals(p1, p2):
    '''returns True if vertexes have same coordinates within precision amount of digits '''
    precision = 12
    p = precision
    u = Vector(p1.X, p1.Y, p1.Z)
    v = Vector(p2.X, p2.Y, p2.Z)
    vector = (u.sub(v))
    isNull = (round(vector.x, p) == 0 and round(vector.y, p) == 0 and round(vector.z, p) == 0)
    return isNull
Exemplo n.º 4
0
def equals(p1, p2):
    '''returns True if vertexes have same coordinates within precision amount of digits '''
    precision = 12  #hardcoded
    p = precision
    u = Vector(p1.X, p1.Y, p1.Z)
    v = Vector(p2.X, p2.Y, p2.Z)
    vector = (u.sub(v))
    isNull = (round(vector.x, p) == 0 and round(vector.y, p) == 0
              and round(vector.z, p) == 0)
    return isNull
Exemplo n.º 5
0
 def setString(self, text=None):
     "sets the dim string to the given value or auto value"
     self.dimnode.param1.setValue(.5)
     p1 = Vector(self.dimnode.pnts.getValues()[0].getValue())
     p2 = Vector(self.dimnode.pnts.getValues()[-1].getValue())
     m = self.dimnode.datumtype.getValue()
     if m == 2:
         self.Distance = (DraftVecUtils.project(p2.sub(p1),
                                                Vector(1, 0, 0))).Length
     elif m == 3:
         self.Distance = (DraftVecUtils.project(p2.sub(p1),
                                                Vector(0, 1, 0))).Length
     else:
         self.Distance = (p2.sub(p1)).Length
     if not text:
         text = Draft.getParam("dimPrecision", 2)
         text = "%." + str(text) + "f"
         text = (text % self.Distance)
     self.dimnode.string.setValue(text)
Exemplo n.º 6
0
 def scaleGhost(self,x,y,z,rel):
     delta = Vector(x,y,z)
     if rel:
         delta = FreeCAD.DraftWorkingPlane.getGlobalCoords(delta)
     self.ghost.scale(delta)
     # calculate a correction factor depending on the scaling center
     corr = Vector(self.node[0].x,self.node[0].y,self.node[0].z)
     corr.scale(delta.x,delta.y,delta.z)
     corr = (corr.sub(self.node[0])).negative()
     self.ghost.move(corr)
     self.ghost.on()
Exemplo n.º 7
0
    def update(
        self,
        inclination=0.0,
        anchor_idx=0,
        base_snap_vertex=None,
        final_snap_vertex=None,
    ):
        '''Update the tracker.'''

        import DraftGeomUtils

        if base_snap_vertex == None:
            base_snap_vertex = Vector(0.0, 0.0, 0.0)
        if final_snap_vertex == None:
            final_snap_vertex = Vector(1.0, 0.0, 0.0)
        
        bp = base_snap_vertex
        lvec = final_snap_vertex.sub(bp)

        self.cube.width.setValue(lvec.Length)

        inclination = inclination * -1

        p = App.Placement()  # objet Placement
        p.Base = bp  # base est coordonnée bp
        if lvec.x == 0 and lvec.y == 0:  # orientation verticale
            up = Vector(0, -1, 0)
            yaxis = up.cross(lvec)
            xaxis = lvec.cross(yaxis)
            if round(lvec.Length, 3) > 0:
                p.Rotation = App.Rotation(lvec, yaxis, xaxis, "ZXY")
                p.Rotation = App.Rotation(
                    p.Rotation.multVec(Vector(1, 0, 0)), inclination
                ).multiply(p.Rotation)
        else:
            up = Vector(0, 0, 1)  # vector up = Z
            yaxis = up.cross(lvec)  # yaxis = produit vectoriel entre Z et lvec
            xaxis = lvec.cross(yaxis)  # xaxis = produit vectoriel entre lvec et yaxis
            if round(lvec.Length, 3) > 0:
                #p.Rotation = App.Rotation(lvec, xaxis, yaxis, "ZXY")
                p.Rotation = App.Rotation(lvec, yaxis, xaxis, "ZXY")
                p.Rotation = App.Rotation(
                    p.Rotation.multVec(Vector(1, 0, 0)), inclination
                ).multiply(p.Rotation)

        self.setRotation(p.Rotation)
        delta = self.getDelta(anchor_idx)
        delta = p.Rotation.multVec(delta)
        delta = delta.add(lvec.multiply(0.5))
        bp = bp.add(delta)
        self.pos(bp)
Exemplo n.º 8
0
def scale(shape, delta=Vector(1, 1, 1), center=Vector(0, 0, 0), copy=True):
    if copy:
        sh = shape.copy()
    else:
        sh = shape
    if delta == Vector(1, 1, 1):
        return sh

    #if len(obj.Shape.Solids) > 0:
    #    sh.Placement.Base = Vector(0,0,0)
    #    sh.Placement.Rotation.Angle = -obj.Placement.Rotation.Angle
    #    delta = sh.Placement.Rotation.multVec(delta)
    #    sh.Placement.Rotation.Angle = 0

    m = FreeCAD.Matrix()
    m.scale(delta)
    sh = sh.transformGeometry(m)
    corr = Vector(center.x, center.y, center.z)
    corr.scale(delta.x, delta.y, delta.z)
    corr = (corr.sub(center)).negative()
    sh.translate(corr)
    sh.Placement = shape.Placement
    return sh
 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
 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)
Exemplo n.º 11
0
 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
Exemplo n.º 12
0
 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)
Exemplo n.º 13
0
class bimMove(Move):
    "The bimMove command definition"

    def __init__(self, sel_dict):
        super().__init__()
        self.sel_dict = sel_dict

    def Activated(self):
        from bimEdit import hideAttribute
        self.name = translate("draft","bimMove", utf8_decode=True)
        Modifier.Activated(self,self.name)
        self.ghost = {}
        for typ in self.sel_dict:
            if typ not in self.ghost:
                self.ghost.update({typ:[]})
            for o in self.sel_dict[typ]:
                self.ghost[typ].append(o.ghost[typ])

        ## Proceeding
        if self.call:
            self.view.removeEventCallback("SoEvent",self.call)
        self.ui.pointUi(self.name)
        self.ui.modUi()
        if self.copymode:
            self.ui.isCopy.setChecked(True)
        self.ui.xValue.setFocus()
        self.ui.xValue.selectAll()

        self.call = self.view.addEventCallback("SoEvent",self.action)
        msg(translate("draft", "Pick start point:")+"\n")

    def finish(self,closed=False,cont=False):
        if self.ghost:
            for typ in self.ghost:
                for g in [i for i in self.ghost[typ]]:
                    if g.switch:
                        g.off()
                    g.finalize()
        if cont and self.ui:
            if self.ui.continueMode:
                todo.delayAfter(self.Activated,[])
        Modifier.finish(self)

    def move(self,delta,copy=False):
        "moving the real shape's bases"
        FreeCADGui.addModule("Draft")
        sel_to_edit = [o for typ in self.sel_dict \
                for o in self.sel_dict[typ] if typ == 'toEdit']
        if copy:
            obj_to_edit = replica(sel_to_edit)
        else:
            obj_to_edit = [s.obj for s in sel_to_edit]

        sel = '['
        for o in obj_to_edit:
            if len(sel) > 1:
                sel += ','
            sel += 'FreeCAD.ActiveDocument.' + o.Name
        sel += ']'

        self.commit(translate("draft","Move"),
            ['Draft.move('+sel+','+DraftVecUtils.toString(delta)+ \
                ',copy=False)', 'FreeCAD.ActiveDocument.recompute()'])

    def action(self,arg):
        "scene event handler"
        if arg["Type"] == "SoKeyboardEvent":
            if arg["Key"] == "ESCAPE":
                self.finish()
        elif arg["Type"] == "SoLocation2Event": #mouse movement detection
            #if self.ghost:
            #    self.ghost.off()
            self.point,ctrlPoint,info = getPoint(self,arg)
            if (len(self.node) > 0):
                last = self.node[len(self.node)-1]
                delta = self.point.sub(last)
                if self.ghost:
                    for typ in self.ghost:
                        if typ == 'toEdit' or typ == 'dirDeps':
                            for g in [i for i in self.ghost[typ]]:
                                g.move(delta)
                                g.on()
            if self.extendedCopy:
                if not hasMod(arg,MODALT): self.finish()
            redraw3DView()
        elif arg["Type"] == "SoMouseButtonEvent":
            if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
                if self.point:
                    self.ui.redraw()
                    if (self.node == []):
                        self.node.append(self.point)
                        self.ui.isRelative.show()
                        if self.ghost:
                            for typ in self.ghost:
                                if typ == 'toEdit' or typ == 'dirDeps':
                                    for g in [i for i in self.ghost[typ]]:
                                        g.on()
                        msg(translate("draft", "Pick end point:")+"\n")
                        if self.planetrack:
                            self.planetrack.set(self.point)
                    else:
                        last = self.node[0]
                        if self.ui.isCopy.isChecked() or hasMod(arg,MODALT):
                            self.move(self.point.sub(last),True)
                        else:
                            self.move(self.point.sub(last))
                        if hasMod(arg,MODALT):
                            self.extendedCopy = True
                        else:
                            self.finish(cont=True)

    def numericInput(self,numx,numy,numz):
        "this function gets called by the toolbar when valid x, y, and z \
                have been entered there"
        self.point = Vector(numx,numy,numz)
        if not self.node:
            self.node.append(self.point)
            self.ui.isRelative.show()
            self.ui.isCopy.show()
            for typ in self.ghost:
                if typ == 'toEdit' or typ == 'dirDeps':
                    for g in [i for i in self.ghost[typ]]:
                        g.on()
            msg(translate("draft", "Pick end point:")+"\n")
        else:
            last = self.node[-1]
            if self.ui.isCopy.isChecked():
                self.move(self.point.sub(last),True)
            else:
                self.move(self.point.sub(last))
            self.finish()
Exemplo n.º 14
0
def scale(objectslist,delta=Vector(1,1,1),center=Vector(0,0,0),copy=False,legacy=False):
    '''scale(objects,vector,[center,copy,legacy]): Scales the objects contained
    in objects (that can be a list of objects or an object) of the given scale
    factors defined by the given vector (in X, Y and Z directions) around
    given center. If legacy is True, direct (old) mode is used, otherwise
    a parametric copy is made. If copy is True, the actual objects are not moved,
    but copies are created instead. The objects (or their copies) are returned.'''
    if not isinstance(objectslist,list): objectslist = [objectslist]
    if legacy:
        newobjlist = []
        for obj in objectslist:
            if copy:
                newobj = makeCopy(obj)
            else:
                newobj = obj
            if obj.isDerivedFrom("Part::Feature"):
                sh = obj.Shape.copy()
                m = FreeCAD.Matrix()
                m.scale(delta)
                sh = sh.transformGeometry(m)
                corr = Vector(center.x,center.y,center.z)
                corr.scale(delta.x,delta.y,delta.z)
                corr = (corr.sub(center)).negative()
                sh.translate(corr)
            if getType(obj) == "Rectangle":
                p = []
                for v in sh.Vertexes: p.append(v.Point)
                pl = obj.Placement.copy()
                pl.Base = p[0]
                diag = p[2].sub(p[0])
                bb = p[1].sub(p[0])
                bh = p[3].sub(p[0])
                nb = DraftVecUtils.project(diag,bb)
                nh = DraftVecUtils.project(diag,bh)
                if obj.Length < 0: l = -nb.Length
                else: l = nb.Length
                if obj.Height < 0: h = -nh.Length
                else: h = nh.Length
                newobj.Length = l
                newobj.Height = h
                tr = p[0].sub(obj.Shape.Vertexes[0].Point)
                newobj.Placement = pl
            elif getType(obj) == "Wire":
                p = []
                for v in sh.Vertexes: p.append(v.Point)
                #print(p)
                newobj.Points = p
            elif getType(obj) == "BSpline":
                p = []
                for p1 in obj.Points:
                    p2 = p1.sub(center)
                    p2.scale(delta.x,delta.y,delta.z)
                    p.append(p2)
                newobj.Points = p
            elif (obj.isDerivedFrom("Part::Feature")):
                newobj.Shape = sh
            elif (obj.TypeId == "App::Annotation"):
                factor = delta.y * obj.ViewObject.FontSize
                newobj.ViewObject.FontSize = factor
                d = obj.Position.sub(center)
                newobj.Position = center.add(Vector(d.x*delta.x,d.y*delta.y,d.z*delta.z))
            if copy:
                formatObject(newobj,obj)
            newobjlist.append(newobj)
        if copy and getParam("selectBaseObjects",False):
            select(objectslist)
        else:
            select(newobjlist)
        if len(newobjlist) == 1: return newobjlist[0]
        return newobjlist
    else:
        obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Scale")
        _Clone(obj)
        obj.Objects = objectslist
        obj.Scale = delta
        corr = Vector(center.x,center.y,center.z)
        corr.scale(delta.x,delta.y,delta.z)
        corr = (corr.sub(center)).negative()
        p = obj.Placement
        p.move(corr)
        obj.Placement = p
        if not copy:
            for o in objectslist:
                o.ViewObject.hide()
        if gui:
            _ViewProviderClone(obj.ViewObject)
            formatObject(obj,objectslist[-1])
            select(obj)
        return obj
Exemplo n.º 15
0
class plane:
    '''A WorkPlane object'''
    def __init__(self):
        # keep track of active document.  Reset view when doc changes.
        self.doc = None
        # self.weak is true if the plane has been defined by self.setup or has been reset
        self.weak = True
        # u, v axes and position define plane, perpendicular axis is handy, though redundant.
        self.u = Vector(1, 0, 0)
        self.v = Vector(0, 1, 0)
        self.axis = Vector(0, 0, 1)
        self.position = Vector(0, 0, 0)
        # a placeholder for a stored state
        self.stored = None

    def __repr__(self):
        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):
        '''
		Return the signed distance from p to the plane, such
		that p + offsetToPoint(p)*direction lies on the plane.
		direction defaults to -plane.axis
		'''
        '''
		A picture will help explain the computation:

                                                            p
                                                          //|
                                                        / / |
                              			      /  /  |
						    /  	/   |
						  /    /    |
		-------------------- plane -----c-----x-----a--------

                Here p is the specified point,
                     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
                     a is the perpendicular intercept on the plane (i.e. along plane.axis)

	        Using vertival bars to denote the length operator,
                     |ap| = |cp| * cos(apc) = |xp| * cos(apx)
                so
                     |xp| = |cp| * cos(apc) / cos(apx)
                          = (cp . axis) / (direction . axis)
		'''
        if direction == None: direction = self.axis
        return direction.dot(self.position.sub(p))

    def projectPoint(self, p, direction=None):
        '''project point onto plane, default direction is orthogonal'''
        if not direction:
            direction = self.axis
        lp = self.getLocalCoords(p)
        gp = self.getGlobalCoords(Vector(lp.x, lp.y, 0))
        a = direction.getAngle(gp.sub(p))
        if a > math.pi / 2:
            direction = DraftVecUtils.neg(direction)
            a = math.pi - a
        ld = self.getLocalRot(direction)
        gd = self.getGlobalRot(Vector(ld.x, ld.y, 0))
        hyp = abs(math.tan(a) * lp.z)
        return gp.add(DraftVecUtils.scaleTo(gd, hyp))

    def projectPointOld(self, p, direction=None):
        '''project point onto plane, default direction is orthogonal. Obsolete'''
        if not direction:
            direction = self.axis
        t = Vector(direction)
        #t.normalize()
        a = round(t.getAngle(self.axis), DraftVecUtils.precision())
        pp = round((math.pi) / 2, DraftVecUtils.precision())
        if a == pp:
            return p
        t.multiply(self.offsetToPoint(p, direction))
        return p.add(t)

    def alignToPointAndAxis(self, point, axis, offset, upvec=None):
        self.doc = FreeCAD.ActiveDocument
        self.axis = axis
        self.axis.normalize()
        if (DraftVecUtils.equals(axis, Vector(1, 0, 0))):
            self.u = Vector(0, 1, 0)
            self.v = Vector(0, 0, 1)
        elif (DraftVecUtils.equals(axis, Vector(-1, 0, 0))):
            self.u = Vector(0, -1, 0)
            self.v = Vector(0, 0, 1)
        elif upvec:
            self.v = upvec
            self.v.normalize()
            self.u = self.v.cross(self.axis)
        else:
            self.v = axis.cross(Vector(1, 0, 0))
            self.v.normalize()
            self.u = DraftVecUtils.rotate(self.v, -math.pi / 2, self.axis)
        offsetVector = Vector(axis)
        offsetVector.multiply(offset)
        self.position = point.add(offsetVector)
        self.weak = False
        # 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")

    def alignToCurve(self, shape, offset):
        if shape.ShapeType == 'Edge':
            #??? TODO: process curve here.  look at shape.edges[0].Curve
            return False
        elif shape.ShapeType == 'Wire':
            #??? TODO: determine if edges define a plane
            return False
        else:
            return False

    def alignToFace(self, shape, offset=0):
        # Set face to the unique selected face, if found
        if shape.ShapeType == 'Face':
            #we should really use face.tangentAt to get u and v here, and implement alignToUVPoint
            self.alignToPointAndAxis(shape.Faces[0].CenterOfMass,
                                     shape.Faces[0].normalAt(0, 0), offset)
            return True
        else:
            return False

    def alignToSelection(self, offset):
        '''If selection uniquely defines a plane, align working plane to it.  Return success (bool)'''
        sex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name)
        if len(sex) == 0:
            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 setup(self, direction, point, upvec=None):
        '''If working plane is undefined, define it!'''
        if self.weak:
            self.alignToPointAndAxis(point, direction, 0, upvec)
            self.weak = True

    def reset(self):
        self.doc = None
        self.weak = True

    def getRotation(self):
        "returns a placement describing the working plane orientation ONLY"
        m = DraftVecUtils.getPlaneRotation(self.u, self.v, self.axis)
        return FreeCAD.Placement(m)

    def getPlacement(self):
        "returns the placement of the working plane"
        m = FreeCAD.Matrix(self.u.x, self.v.x, self.axis.x, self.position.x,
                           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):
        "sets the working plane from a placement (rotaton ONLY)"
        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 save(self):
        "stores the current plane state"
        self.stored = [self.u, self.v, self.axis, self.position, self.weak]

    def restore(self):
        "restores a previously saved plane state, if exists"
        if self.stored:
            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):
        "returns the coordinates of a given point on the working plane"
        pt = point.sub(self.position)
        xv = DraftVecUtils.project(pt, self.u)
        x = xv.Length
        if xv.getAngle(self.u) > 1:
            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 getGlobalCoords(self, point):
        "returns the global coordinates of the given point, taken relatively to this working plane"
        vx = DraftVecUtils.scale(self.u, point.x)
        vy = DraftVecUtils.scale(self.v, point.y)
        vz = DraftVecUtils.scale(self.axis, point.z)
        pt = (vx.add(vy)).add(vz)
        return pt.add(self.position)

    def getLocalRot(self, point):
        "Same as getLocalCoords, but discards the WP position"
        xv = DraftVecUtils.project(point, self.u)
        x = xv.Length
        if xv.getAngle(self.u) > 1:
            x = -x
        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 getGlobalRot(self, point):
        "Same as getGlobalCoords, but discards the WP position"
        vx = DraftVecUtils.scale(self.u, point.x)
        vy = DraftVecUtils.scale(self.v, point.y)
        vz = DraftVecUtils.scale(self.axis, point.z)
        pt = (vx.add(vy)).add(vz)
        return pt

    def getClosestAxis(self, point):
        "returns which of the workingplane axes is closest from the given vector"
        ax = point.getAngle(self.u)
        ay = point.getAngle(self.v)
        az = point.getAngle(self.axis)
        bx = point.getAngle(DraftVecUtils.neg(self.u))
        by = point.getAngle(DraftVecUtils.neg(self.v))
        bz = point.getAngle(DraftVecUtils.neg(self.axis))
        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
class plane:
    '''A WorkPlane object'''

    def __init__(self):
        # keep track of active document.  Reset view when doc changes.
        self.doc = None
        # self.weak is true if the plane has been defined by self.setup or has been reset
        self.weak = True
        # u, v axes and position define plane, perpendicular axis is handy, though redundant.
        self.u = Vector(1,0,0)
        self.v = Vector(0,1,0)
        self.axis = Vector(0,0,1)
        self.position = Vector(0,0,0)
        # a placeholder for a stored state
        self.stored = None

    def __repr__(self):
        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):
        '''
        Return the signed distance from p to the plane, such
        that p + offsetToPoint(p)*direction lies on the plane.
        direction defaults to -plane.axis
        '''

        '''
        A picture will help explain the computation:

                                                            p
                                                          //|
                                                        / / |
                                              /  /  |
                            /   /   |
                          /    /    |
        -------------------- plane -----c-----x-----a--------

                Here p is the specified point,
                     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
                     a is the perpendicular intercept on the plane (i.e. along plane.axis)

            Using vertival bars to denote the length operator,
                     |ap| = |cp| * cos(apc) = |xp| * cos(apx)
                so
                     |xp| = |cp| * cos(apc) / cos(apx)
                          = (cp . axis) / (direction . axis)
        '''
        if direction == None: direction = self.axis
        return direction.dot(self.position.sub(p))

    def projectPoint(self, p, direction=None):
        '''project point onto plane, default direction is orthogonal'''
        if not direction:
            direction = self.axis
        lp = self.getLocalCoords(p)
        gp = self.getGlobalCoords(Vector(lp.x,lp.y,0))
        a = direction.getAngle(gp.sub(p))
        if a > math.pi/2:
            direction = direction.negative()
            a = math.pi - a
        ld = self.getLocalRot(direction)
        gd = self.getGlobalRot(Vector(ld.x,ld.y,0))
        hyp = abs(math.tan(a) * lp.z)
        return gp.add(DraftVecUtils.scaleTo(gd,hyp))
                
    def projectPointOld(self, p, direction=None):
        '''project point onto plane, default direction is orthogonal. Obsolete'''
        if not direction:
            direction = self.axis
        t = Vector(direction)
        #t.normalize()
        a = round(t.getAngle(self.axis),DraftVecUtils.precision())
        pp = round((math.pi)/2,DraftVecUtils.precision())
        if a == pp:
            return p
        t.multiply(self.offsetToPoint(p, direction))
        return p.add(t)

    def alignToPointAndAxis(self, point, axis, offset, upvec=None):
        self.doc = FreeCAD.ActiveDocument
        self.axis = axis;
        self.axis.normalize()
        if (DraftVecUtils.equals(axis, Vector(1,0,0))):
            self.u = Vector(0,1,0)
            self.v = Vector(0,0,1)
        elif (DraftVecUtils.equals(axis, Vector(-1,0,0))):
            self.u = Vector(0,-1,0)
            self.v = Vector(0,0,1)
        elif upvec:
            self.v = upvec
            self.v.normalize()
            self.u = self.v.cross(self.axis)
        else:
            self.v = axis.cross(Vector(1,0,0))
            self.v.normalize()
            self.u = DraftVecUtils.rotate(self.v, -math.pi/2, self.axis)
        offsetVector = Vector(axis); offsetVector.multiply(offset)
        self.position = point.add(offsetVector)
        self.weak = False
        # 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")

    def alignToCurve(self, shape, offset):
        if shape.ShapeType == 'Edge':
            #??? TODO: process curve here.  look at shape.edges[0].Curve
            return False
        elif shape.ShapeType == 'Wire':
            #??? TODO: determine if edges define a plane
            return False
        else:
            return False

    def alignToEdges(self,edges):
        # use a list of edges to find a plane position
        if len(edges) > 2:
            return False
        # for axes systems, we suppose the 2 first edges are parallel
        # ??? TODO: exclude other cases first
        v1 = edges[0].Vertexes[-1].Point.sub(edges[0].Vertexes[0].Point)
        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 alignToFace(self, shape, offset=0):
        # Set face to the unique selected face, if found
        if shape.ShapeType == 'Face':
            #we should really use face.tangentAt to get u and v here, and implement alignToUVPoint
            self.alignToPointAndAxis(shape.Faces[0].CenterOfMass, shape.Faces[0].normalAt(0,0), offset)
            return True
        else:
            return False

    def alignToSelection(self, offset):
        '''If selection uniquely defines a plane, align working plane to it.  Return success (bool)'''
        sex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name)
        if len(sex) == 0:
            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 setup(self, direction=None, point=None, upvec=None):
        '''If working plane is undefined, define it!'''
        if self.weak:
            if direction and point:
                self.alignToPointAndAxis(point, direction, 0, upvec)
            else:
                try:
                    from pivy import coin
                    rot = FreeCADGui.ActiveDocument.ActiveView.getCameraNode().getField("orientation").getValue()
                    upvec = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue())
                    vdir = FreeCADGui.ActiveDocument.ActiveView.getViewDirection()
                    self.alignToPointAndAxis(Vector(0,0,0), vdir.negative(), 0, upvec)
                except:
                    print "Draft: Unable to align the working plane to the current view"
            self.weak = True

    def reset(self):
        self.doc = None
        self.weak = True

    def getRotation(self):
        "returns a placement describing the working plane orientation ONLY"
        m = DraftVecUtils.getPlaneRotation(self.u,self.v,self.axis)
        return FreeCAD.Placement(m)

    def getPlacement(self,rotated=False):
        "returns the placement of the working plane"
        if rotated:
            m = FreeCAD.Matrix(
                self.u.x,self.axis.x,-self.v.x,self.position.x,
                self.u.y,self.axis.y,-self.v.y,self.position.y,
                self.u.z,self.axis.z,-self.v.z,self.position.z,
                0.0,0.0,0.0,1.0)
        else:
            m = FreeCAD.Matrix(
                self.u.x,self.v.x,self.axis.x,self.position.x,
                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):
        "sets the working plane from a placement (rotaton ONLY)"
        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 save(self):
        "stores the current plane state"
        self.stored = [self.u,self.v,self.axis,self.position,self.weak]

    def restore(self):
        "restores a previously saved plane state, if exists"
        if self.stored:
            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):
        "returns the coordinates of a given point on the working plane"
        pt = point.sub(self.position)
        xv = DraftVecUtils.project(pt,self.u)
        x = xv.Length
        if xv.getAngle(self.u) > 1:
                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 getGlobalCoords(self,point):
        "returns the global coordinates of the given point, taken relatively to this working plane"
        vx = Vector(self.u).multiply(point.x)
        vy = Vector(self.v).multiply(point.y)
        vz = Vector(self.axis).multiply(point.z)
        pt = (vx.add(vy)).add(vz)
        return pt.add(self.position)

    def getLocalRot(self,point):
        "Same as getLocalCoords, but discards the WP position"
        xv = DraftVecUtils.project(point,self.u)
        x = xv.Length
        if xv.getAngle(self.u) > 1:
                x = -x
        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 getGlobalRot(self,point):
        "Same as getGlobalCoords, but discards the WP position"
        vx = Vector(self.u).multiply(point.x)
        vy = Vector(self.v).multiply(point.y)
        vz = Vector(self.axis).multiply(point.z)
        pt = (vx.add(vy)).add(vz)
        return pt
        
    def getClosestAxis(self,point):
        "returns which of the workingplane axes is closest from the given vector"
        ax = point.getAngle(self.u)
        ay = point.getAngle(self.v)
        az = point.getAngle(self.axis)
        bx = point.getAngle(self.u.negative())
        by = point.getAngle(self.v.negative())
        bz = point.getAngle(self.axis.negative())
        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 isGlobal(self):
        "returns True if the plane axes are equal to the global axes"
        if self.u != Vector(1,0,0):
            return False
        if self.v != Vector(0,1,0):
            return False
        if self.axis != Vector(0,0,1):
            return False
        return True
Exemplo n.º 17
0
class plane:
	'''A WorkPlane object'''

	def __init__(self):
		# keep track of active document.  Reset view when doc changes.
		self.doc = None
		# self.weak is true if the plane has been defined by self.setup or has been reset
		self.weak = True
		# u, v axes and position define plane, perpendicular axis is handy, though redundant.
		self.u = Vector(1,0,0)
		self.v = Vector(0,1,0)
		self.axis = Vector(0,0,1)
		self.position = Vector(0,0,0)
                # a placeholder for a stored state
                self.stored = None

        def __repr__(self):
                return "Workplane x="+str(fcvec.rounded(self.u))+" y="+str(fcvec.rounded(self.v))+" z="+str(fcvec.rounded(self.axis))

	def offsetToPoint(self, p, direction=None):
		'''
		Return the signed distance from p to the plane, such
		that p + offsetToPoint(p)*direction lies on the plane.
		direction defaults to -plane.axis
		'''

		'''
		A picture will help explain the computation:

                                                            p
                                                          //|
                                                        / / |
                              			      /  /  |
						    /  	/   |
						  /    /    |
		-------------------- plane -----c-----x-----a--------

                Here p is the specified point,
                     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
                     a is the perpendicular intercept on the plane (i.e. along plane.axis)

	        Using vertival bars to denote the length operator,
                     |ap| = |cp| * cos(apc) = |xp| * cos(apx)
                so
                     |xp| = |cp| * cos(apc) / cos(apx)
                          = (cp . axis) / (direction . axis)
		'''
		if direction == None: direction = self.axis
		return direction.dot(self.position.sub(p))

	def projectPoint(self, p, direction=None):
		'''project point onto plane, default direction is orthogonal'''
		if not direction: direction = self.axis
		t = Vector(direction)
		t.multiply(self.offsetToPoint(p, direction))
		return p.add(t)

	def alignToPointAndAxis(self, point, axis, offset, upvec=None):
		self.doc = FreeCAD.ActiveDocument
		self.axis = axis;
		self.axis.normalize()
		if (fcvec.equals(axis, Vector(1,0,0))):
			self.u = Vector(0,1,0)
			self.v = Vector(0,0,1)
                elif (fcvec.equals(axis, Vector(-1,0,0))):
                        self.u = Vector(0,-1,0)
                        self.v = Vector(0,0,1)
                elif upvec:
                        self.v = upvec
                        self.v.normalize()
                        self.u = self.v.cross(self.axis)
		else:
			self.v = axis.cross(Vector(1,0,0))
			self.v.normalize()
			self.u = fcvec.rotate(self.v, -math.pi/2, self.axis)
		offsetVector = Vector(axis); offsetVector.multiply(offset)
		self.position = point.add(offsetVector)
		self.weak = False
		# FreeCAD.Console.PrintMessage("(position = " + str(self.position) + ")\n")
		# FreeCAD.Console.PrintMessage("Current workplane: x="+str(fcvec.rounded(self.u))+" y="+str(fcvec.rounded(self.v))+" z="+str(fcvec.rounded(self.axis))+"\n")

	def alignToCurve(self, shape, offset):
		if shape.ShapeType == 'Edge':
			#??? TODO: process curve here.  look at shape.edges[0].Curve
			return False
		elif shape.ShapeType == 'Wire':
			#??? TODO: determine if edges define a plane
			return False
		else:
			return False

	def alignToFace(self, shape, offset=0):
		# Set face to the unique selected face, if found
		if shape.ShapeType == 'Face':
			#we should really use face.tangentAt to get u and v here, and implement alignToUVPoint
			self.alignToPointAndAxis(shape.Faces[0].CenterOfMass, shape.Faces[0].normalAt(0,0), offset)
			return True
		else:
			return False

	def alignToSelection(self, offset):
		'''If selection uniquely defines a plane, align working plane to it.  Return success (bool)'''
		sex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name)
		if len(sex) == 0:
			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 setup(self, direction, point, upvec=None):
		'''If working plane is undefined, define it!'''
		if self.weak:
			self.alignToPointAndAxis(point, direction, 0, upvec)
			self.weak = True

	def reset(self):
		self.doc = None
                self.weak = True

        def getRotation(self):
                "returns a placement describing the working plane orientation ONLY"
                m = fcvec.getPlaneRotation(self.u,self.v,self.axis)
                return FreeCAD.Placement(m)

        def getPlacement(self):
                "returns the placement of the working plane"
                m = FreeCAD.Matrix(
                        self.u.x,self.v.x,self.axis.x,self.position.x,
                        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):
                "sets the working plane from a placement (rotaton ONLY)"
                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 save(self):
                "stores the current plane state"
                self.stored = [self.u,self.v,self.axis,self.position,self.weak]

        def restore(self):
                "restores a previously saved plane state, if exists"
                if self.stored:
                        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):
                "returns the coordinates of a given point on the working plane"
                xv = fcvec.project(point,self.u)
                x = xv.Length
                if xv.getAngle(self.u) > 1:
                        x = -x
                yv = fcvec.project(point,self.v)
                y = yv.Length
                if yv.getAngle(self.v) > 1:
                        y = -y
                zv = fcvec.project(point,self.axis)
                z = zv.Length
                if zv.getAngle(self.axis) > 1:
                        z = -z
                return Vector(x,y,z)

        def getGlobalCoords(self,point):
                "returns the global coordinates of the given point, taken relatively to this working plane"
                vx = fcvec.scale(self.u,point.x)
                vy = fcvec.scale(self.v,point.y)
                vz = fcvec.scale(self.axis,point.z)
                return (vx.add(vy)).add(vz)

        def getClosestAxis(self,point):
                "returns which of the workingplane axes is closest from the given vector"
                ax = point.getAngle(self.u)
                ay = point.getAngle(self.v)
                az = point.getAngle(self.axis)
                bx = point.getAngle(fcvec.neg(self.u))
                by = point.getAngle(fcvec.neg(self.v))
                bz = point.getAngle(fcvec.neg(self.axis))
                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
Exemplo n.º 18
0
    def constrain(self, point, basepoint=None, axis=None):
        '''constrain(point,basepoint=None,axis=None: Returns a
        constrained point. Axis can be "x","y" or "z" or a custom vector. If None,
        the closest working plane axis will be picked.
        Basepoint is the base point used to figure out from where the point
        must be constrained. If no basepoint is given, the current point is
        used as basepoint.'''

        # without the Draft module fully loaded, no axes system!"
        if not hasattr(FreeCAD, "DraftWorkingPlane"):
            return point

        point = Vector(point)

        # setup trackers if needed
        if not self.constrainLine:
            self.constrainLine = DraftTrackers.lineTracker(dotted=True)

        # setting basepoint
        if not basepoint:
            if not self.basepoint:
                self.basepoint = point
        else:
            self.basepoint = basepoint
        delta = point.sub(self.basepoint)

        # setting constraint axis
        if self.mask:
            self.affinity = self.mask
        if not self.affinity:
            self.affinity = FreeCAD.DraftWorkingPlane.getClosestAxis(delta)
        if isinstance(axis, FreeCAD.Vector):
            self.constraintAxis = axis
        elif axis == "x":
            self.constraintAxis = FreeCAD.DraftWorkingPlane.u
        elif axis == "y":
            self.constraintAxis = FreeCAD.DraftWorkingPlane.v
        elif axis == "z":
            self.constraintAxis = FreeCAD.DraftWorkingPlane.axis
        else:
            if self.affinity == "x":
                self.constraintAxis = FreeCAD.DraftWorkingPlane.u
            elif self.affinity == "y":
                self.constraintAxis = FreeCAD.DraftWorkingPlane.v
            else:
                self.constraintAxis = FreeCAD.DraftWorkingPlane.axis

        # calculating constrained point
        cdelta = DraftVecUtils.project(delta, self.constraintAxis)
        npoint = self.basepoint.add(cdelta)

        # setting constrain line
        if self.constrainLine:
            if point != npoint:
                self.constrainLine.p1(point)
                self.constrainLine.p2(npoint)
                self.constrainLine.on()
            else:
                self.constrainLine.off()

        return npoint
Exemplo n.º 19
0
    def validate_datum(self):
        """
        Ensure the datum is valid, assuming 0+00 / (0,0,0)
        for station and coordinate where none is suplpied and it
        cannot be inferred fromt the starting geometry
        """
        _datum = self.data.get('meta')
        _geo = self.data.get('geometry')[0]

        if not _geo or not _datum:
            print('Unable to validate alignment datum')
            return

        _datum_truth = [not _datum.get('StartStation') is None,
                        not _datum.get('Start') is None]

        _geo_truth = [not _geo.get('StartStation') is None,
                      not _geo.get('Start') is None]

        #----------------------------
        #CASE 0
        #----------------------------
        #both defined?  nothing to do
        if all(_datum_truth):
            return

        #----------------------------
        #Parameter Initialization
        #----------------------------
        _geo_station = 0
        _geo_start = Vector()

        if _geo_truth[0]:
            _geo_station = _geo.get('StartStation')

        if _geo_truth[1]:
            _geo_start = _geo.get('Start')

        #---------------------
        #CASE 1
        #---------------------
        #no datum defined?  use initial geometry or zero defaults
        if not any(_datum_truth):

            _datum['StartStation'] = _geo_station
            _datum['Start'] = _geo_start
            return

        #--------------------
        #CASE 2
        #--------------------
        #station defined?
        #if the geometry has a station and coordinate,
        #project the start coordinate

        if _datum_truth[0]:

            _datum['Start'] = _geo_start

            #assume geometry start if no geometry station
            if not _geo_truth[0]:
                return

            #scale the distance to the system units
            delta = _geo_station - _datum['StartStation']

            #cutoff if error is below tolerance
            if not support.within_tolerance(delta):
                delta *= units.scale_factor()
            else:
                delta = 0.0

            #assume geometry start if station delta is zero
            if delta:

                #calculate the start based on station delta
                _datum['Start'] = _datum.get('Start').sub(
                    support.vector_from_angle(
                        _geo.get('BearingIn')).multiply(delta)
                )

            return

        #---------------------
        #CASE 3
        #---------------------
        #datum start coordinate is defined
        #if the geometry has station and coordinate,
        #project the start station
        _datum['StartStation'] = _geo_station

        #assume geometry station if no geometry start
        if _geo_truth[1]:

            #scale the length to the document units
            delta = \
                _geo_start.sub(_datum.get('Start')).Length/units.scale_factor()

            _datum['StartStation'] -= delta