From 071b657df74f818067c52ff9bb5536aa0b4a3b2f Mon Sep 17 00:00:00 2001 From: yorikvanhavre Date: Fri, 16 Dec 2011 19:54:32 +0000 Subject: [PATCH] + new Draft Snap git-svn-id: https://free-cad.svn.sourceforge.net/svnroot/free-cad/trunk@5316 e8eeb9e2-ec13-0410-a4a9-efa5cf37419d --- src/Mod/Draft/DraftGui.py | 14 +- src/Mod/Draft/DraftSnap.py | 354 +++++++++++++++---------------- src/Mod/Draft/DraftTools.py | 28 +-- src/Mod/Draft/Draft_rc.py | 2 +- src/Mod/Draft/Makefile.am | 1 + src/Mod/Draft/WorkingPlane.py | 2 +- src/Mod/Draft/draftlibs/fcgeo.py | 4 +- 7 files changed, 191 insertions(+), 214 deletions(-) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 48387bc6c..e213122ed 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -969,17 +969,9 @@ class DraftToolBar: return (r,g,b) def cross(self,on=True): - if on: - if not self.crossedViews: - mw = getMainWindow() - self.crossedViews = mw.findChildren(QtGui.QWidget,"QtGLArea") - for w in self.crossedViews: - w.setCursor(QtCore.Qt.CrossCursor) - else: - for w in self.crossedViews: - w.unsetCursor() - self.crossedViews = [] - + "deprecated" + pass + def toggleConstrMode(self,checked): self.baseWidget.setStyleSheet("#constrButton:Checked {background-color: "+self.getDefaultColor("constr",rgb=True)+" }") self.constrMode = checked diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index c0d70f5b3..a255beefe 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -45,6 +45,10 @@ class Snapper: self.views = [] self.maxEdges = 0 self.radius = 0 + self.constraintAxis = None + self.basepoint = None + self.affinity = None + self.cursorMode = None if Draft.getParam("maxSnap"): self.maxEdges = Draft.getParam("maxSnapEdges") @@ -52,7 +56,7 @@ class Snapper: self.tracker = None self.extLine = None self.grid = None - self.constraintAxis = None + self.constrainLine = None # the snapmarker has "dot","circle" and "square" available styles self.mk = {'passive':'circle', @@ -60,119 +64,36 @@ class Snapper: 'parallel':'circle', 'grid':'circle', 'endpoint':'dot', - 'midpoint':'square', - 'perpendicular':'square', - 'angle':'square', + 'midpoint':'dot', + 'perpendicular':'dot', + 'angle':'dot', 'center':'dot', - 'ortho':'square', - 'intersection':'circle'} + 'ortho':'dot', + 'intersection':'dot'} self.cursors = {'passive':None, 'extension':':/icons/Constraint_Parallel.svg', 'parallel':':/icons/Constraint_Parallel.svg', 'grid':':/icons/Constraint_PointOnPoint.svg', - 'endpoint':':/icons/Constraint_PointOnPoint.svg', + 'endpoint':':/icons/Constraint_PointOnEnd.svg', 'midpoint':':/icons/Constraint_PointOnObject.svg', 'perpendicular':':/icons/Constraint_PointToObject.svg', 'angle':':/icons/Constraint_ExternalAngle.svg', 'center':':/icons/Constraint_Concentric.svg', 'ortho':':/icons/Constraint_Perpendicular.svg', 'intersection':':/icons/Constraint_Tangent.svg'} + + def activate(self): + "create the trackers" + # setup trackers if needed + if not self.tracker: + self.tracker = DraftTrackers.snapTracker() + if not self.extLine: + self.extLine = DraftTrackers.lineTracker(dotted=True) + if (not self.grid) and Draft.getParam("grid"): + self.grid = DraftTrackers.gridTracker() + if not self.constrainLine: + self.constrainLine = DraftTrackers.lineTracker(dotted=True) - 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.''' - - point = Vector(point) - dvec = point.sub(basepoint) - - # setting constraint axis - 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: - self.constraintAxis = None - - # setting basepoint - if not basepoint: - pass - - affinity = FreeCAD.DraftWorkingPlane.getClosestAxis(dvec) - if (not self.constraintAxis) or mobile: - if affinity == "x": - dv = fcvec.project(dvec,FreeCAD.DraftWorkingPlane.u) - point = last.add(dv) - if sym: - l = dv.Length - if dv.getAngle(FreeCAD.DraftWorkingPlane.u) > 1: - l = -l - point = last.add(FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(l,l,l))) - target.constrain = 0 #x direction - target.ui.xValue.setEnabled(True) - target.ui.yValue.setEnabled(False) - target.ui.zValue.setEnabled(False) - target.ui.xValue.setFocus() - elif affinity == "y": - dv = fcvec.project(dvec,FreeCAD.DraftWorkingPlane.v) - point = last.add(dv) - if sym: - l = dv.Length - if dv.getAngle(FreeCAD.DraftWorkingPlane.v) > 1: - l = -l - point = last.add(FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(l,l,l))) - target.constrain = 1 #y direction - target.ui.xValue.setEnabled(False) - target.ui.yValue.setEnabled(True) - target.ui.zValue.setEnabled(False) - target.ui.yValue.setFocus() - elif affinity == "z": - dv = fcvec.project(dvec,FreeCAD.DraftWorkingPlane.axis) - point = last.add(dv) - if sym: - l = dv.Length - if dv.getAngle(FreeCAD.DraftWorkingPlane.axis) > 1: - l = -l - point = last.add(FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(l,l,l))) - target.constrain = 2 #z direction - target.ui.xValue.setEnabled(False) - target.ui.yValue.setEnabled(False) - target.ui.zValue.setEnabled(True) - target.ui.zValue.setFocus() - else: target.constrain = 3 - elif (target.constrain == 0): - dv = fcvec.project(dvec,FreeCAD.DraftWorkingPlane.u) - point = last.add(dv) - if sym: - l = dv.Length - if dv.getAngle(FreeCAD.DraftWorkingPlane.u) > 1: - l = -l - point = last.add(FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(l,l,l))) - elif (target.constrain == 1): - dv = fcvec.project(dvec,FreeCAD.DraftWorkingPlane.v) - point = last.add(dv) - if sym: - l = dv.Length - if dv.getAngle(FreeCAD.DraftWorkingPlane.u) > 1: - l = -l - point = last.add(FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(l,l,l))) - elif (target.constrain == 2): - dv = fcvec.project(dvec,FreeCAD.DraftWorkingPlane.axis) - point = last.add(dv) - if sym: - l = dv.Length - if dv.getAngle(FreeCAD.DraftWorkingPlane.u) > 1: - l = -l - point = last.add(FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(l,l,l))) - return point - def snap(self,screenpos,lastpoint=None,active=True,constrain=None): """snap(screenpos,lastpoint=None,active=True,constrain=None): returns a snapped point from the given (x,y) screenpos (the position of the mouse cursor), active is to @@ -197,7 +118,7 @@ class Snapper: self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) # set the grid - if Draft.getParam("grid"): + if self.grid and Draft.getParam("grid"): self.grid.set() # checking if alwaySnap setting is on @@ -207,16 +128,19 @@ class Snapper: active = True self.setCursor('passive') - self.tracker.off() + if self.tracker: + self.tracker.off() + if self.extLine: + self.extLine.off() point = FreeCADGui.ActiveDocument.ActiveView.getPoint(screenpos[0],screenpos[1]) - # checking if parallel to one of the edges of the last objects - point = self.snapToExtensions(point,lastpoint) - # check if we snapped to something info = FreeCADGui.ActiveDocument.ActiveView.getObjectInfo((screenpos[0],screenpos[1])) + # checking if parallel to one of the edges of the last objects + point = self.snapToExtensions(point,lastpoint) + if not info: # nothing has been snapped, check fro grid snap @@ -239,7 +163,7 @@ class Snapper: if not active: # passive snapping - snaps = [snapToVertex(info)] + snaps = [self.snapToVertex(info)] else: @@ -281,11 +205,10 @@ class Snapper: snaps.extend(self.snapToEndpoints(obj.Mesh)) # updating last objects list - if not self.lastObj[0]: - self.lastObj[0] = obj.Name + if not self.lastObj[1]: self.lastObj[1] = obj.Name - if (self.lastObj[1] != obj.Name): - self.lastObj[0] = lastObj[1] + elif self.lastObj[1] != obj.Name: + self.lastObj[0] = self.lastObj[1] self.lastObj[1] = obj.Name # calculating the nearest snap point @@ -298,15 +221,19 @@ class Snapper: if delta.Length < shortest: shortest = delta.Length winner = snap - if self.radius != 0: + + # see if we are out of the max radius, if any + if self.radius: dv = point.sub(winner[2]) - if (not oldActive) and (dv.Length > self.radius): - winner = snapToVertex(info) + if (dv.Length > self.radius): + if not oldActive: + winner = self.snapToVertex(info) # setting the cursors - self.tracker.setCoords(winner[2]) - self.tracker.setMarker(self.mk[winner[1]]) - self.tracker.on() + if self.tracker: + self.tracker.setCoords(winner[2]) + self.tracker.setMarker(self.mk[winner[1]]) + self.tracker.on() self.setCursor(winner[1]) # return the final point @@ -318,30 +245,35 @@ class Snapper: if o: ob = FreeCAD.ActiveDocument.getObject(o) if ob: - edges = ob.Shape.Edges - if (not self.maxEdges) or (len(edges) <= self.maxEdges): - for e in edges: - if isinstance(e.Curve,Part.Line): - np = self.getPerpendicular(e,point) - if (np.sub(point)).Length < self.radius: - self.tracker.setCoords(np) - self.tracker.setMarker(self.mk['extension']) - self.tracker.on() - self.extLine.p1(e.Vertexes[0].Point) - self.extLine.p2(np) - self.extLine.on() - self.setCursor('extension') - return np - else: - if last: - de = Part.Line(last,last.add(fcgeo.vec(e))).toShape() - np = self.getPerpendicular(de,point) + if ob.isDerivedFrom("Part::Feature"): + edges = ob.Shape.Edges + if (not self.maxEdges) or (len(edges) <= self.maxEdges): + for e in edges: + if isinstance(e.Curve,Part.Line): + np = self.getPerpendicular(e,point) + if not fcgeo.isPtOnEdge(np,e): if (np.sub(point)).Length < self.radius: - self.tracker.setCoords(np) - self.tracker.setMarker(self.mk['parallel']) - self.tracker.on() + if self.tracker: + self.tracker.setCoords(np) + self.tracker.setMarker(self.mk['extension']) + self.tracker.on() + if self.extLine: + self.extLine.p1(e.Vertexes[0].Point) + self.extLine.p2(np) + self.extLine.on() self.setCursor('extension') return np + else: + if last: + de = Part.Line(last,last.add(fcgeo.vec(e))).toShape() + np = self.getPerpendicular(de,point) + if (np.sub(point)).Length < self.radius: + if self.tracker: + self.tracker.setCoords(np) + self.tracker.setMarker(self.mk['parallel']) + self.tracker.on() + self.setCursor('extension') + return np return point def snapToGrid(self,point): @@ -352,9 +284,10 @@ class Snapper: if self.radius != 0: dv = point.sub(np) if dv.Length <= self.radius: - self.tracker.setCoords(np) - self.tracker.setMarker(self.mk['grid']) - self.tracker.on() + if self.tracker: + self.tracker.setCoords(np) + self.tracker.setMarker(self.mk['grid']) + self.tracker.on() self.setCursor('grid') return np return point @@ -442,7 +375,6 @@ class Snapper: "returns a list of intersection snap locations" snaps = [] # get the stored objects to calculate intersections - intedges = [] if self.lastObj[0]: obj = FreeCAD.ActiveDocument.getObject(self.lastObj[0]) if obj: @@ -465,7 +397,6 @@ class Snapper: def getScreenDist(self,dist,cursor): "returns a distance in 3D space from a screen pixels distance" - print cursor p1 = FreeCADGui.ActiveDocument.ActiveView.getPoint(cursor) p2 = FreeCADGui.ActiveDocument.ActiveView.getPoint((cursor[0]+dist,cursor[1])) return (p2.sub(p1)).Length @@ -477,28 +408,32 @@ class Snapper: np = (edge.Vertexes[0].Point).add(nv) return np - def setCursor(self,mode=None): + def setCursor(self,mode=None): + "setCursor(self,mode=None): sets or resets the cursor to the given mode or resets" + if not mode: for v in self.views: v.unsetCursor() self.views = [] else: - if not self.views: - mw = DraftGui.getMainWindow() - self.views = mw.findChildren(QtGui.QWidget,"QtGLArea") - baseicon = QtGui.QPixmap(":/icons/Draft_Cursor.svg") - newicon = QtGui.QPixmap(32,24) - newicon.fill(QtCore.Qt.transparent) - qp = QtGui.QPainter() - qp.begin(newicon) - qp.drawPixmap(0,0,baseicon) - if not (mode == 'passive'): - tp = QtGui.QPixmap(self.cursors[mode]).scaledToWidth(16) - qp.drawPixmap(QtCore.QPoint(16, 8), tp); - qp.end() - cur = QtGui.QCursor(newicon,8,8) - for v in self.views: - v.setCursor(cur) + if mode != self.cursorMode: + if not self.views: + mw = DraftGui.getMainWindow() + self.views = mw.findChildren(QtGui.QWidget,"QtGLArea") + baseicon = QtGui.QPixmap(":/icons/Draft_Cursor.svg") + newicon = QtGui.QPixmap(32,24) + newicon.fill(QtCore.Qt.transparent) + qp = QtGui.QPainter() + qp.begin(newicon) + qp.drawPixmap(0,0,baseicon) + if not (mode == 'passive'): + tp = QtGui.QPixmap(self.cursors[mode]).scaledToWidth(16) + qp.drawPixmap(QtCore.QPoint(16, 8), tp); + qp.end() + cur = QtGui.QCursor(newicon,8,8) + for v in self.views: + v.setCursor(cur) + self.cursorMode = mode def off(self): "finishes snapping" @@ -508,11 +443,67 @@ class Snapper: self.extLine.off() if self.grid: self.grid.off() + if self.constrainLine: + self.constrainLine.off() self.radius = 0 self.setCursor() + self.cursorMode = None - def constrainOff(self): - pass + 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.''' + + point = Vector(point) + + # setting basepoint + if not basepoint: + if not self.basepoint: + self.basepoint = point + else: + self.basepoint = basepoint + delta = point.sub(basepoint) + + # setting constraint axis + 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 point != npoint: + self.constrainLine.p1(point) + self.constrainLine.p2(npoint) + self.constrainLine.on() + else: + self.constrainLine.off() + + return npoint + + def unconstrain(self): + self.basepoint = None + self.affinity = None + if self.constrainLine: + self.constrainLine.off() # deprecated ################################################################## @@ -598,30 +589,31 @@ def snapPoint(target,point,cursor,ctrl=False): if o: ob = target.doc.getObject(o) if ob: - edges = ob.Shape.Edges - if len(edges)<10: - for e in edges: - if isinstance(e.Curve,Part.Line): - last = target.node[len(target.node)-1] - de = Part.Line(last,last.add(fcgeo.vec(e))).toShape() - np = getPerpendicular(e,point) - if (np.sub(point)).Length < radius: - target.snap.coords.point.setValue((np.x,np.y,np.z)) - target.snap.setMarker("circle") - target.snap.on() - target.extsnap.p1(e.Vertexes[0].Point) - target.extsnap.p2(np) - target.extsnap.on() - point = np - else: + if ob.isDerivedFrom("Part::Feature"): + edges = ob.Shape.Edges + if len(edges)<10: + for e in edges: + if isinstance(e.Curve,Part.Line): last = target.node[len(target.node)-1] - de = Part.Line(last,last.add(fcgeo.vec(e))).toShape() - np = getPerpendicular(de,point) + de = Part.Line(last,last.add(fcgeo.vec(e))).toShape() + np = getPerpendicular(e,point) if (np.sub(point)).Length < radius: target.snap.coords.point.setValue((np.x,np.y,np.z)) target.snap.setMarker("circle") target.snap.on() + target.extsnap.p1(e.Vertexes[0].Point) + target.extsnap.p2(np) + target.extsnap.on() point = np + else: + last = target.node[len(target.node)-1] + de = Part.Line(last,last.add(fcgeo.vec(e))).toShape() + np = getPerpendicular(de,point) + if (np.sub(point)).Length < radius: + target.snap.coords.point.setValue((np.x,np.y,np.z)) + target.snap.setMarker("circle") + target.snap.on() + point = np # check if we snapped to something snapped=target.view.getObjectInfo((cursor[0],cursor[1])) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 674d07d31..e607bb8f2 100755 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -119,8 +119,13 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True): ''' ui = FreeCADGui.draftToolBar view = FreeCADGui.ActiveDocument.ActiveView - point = view.getPoint(args["Position"][0],args["Position"][1]) - point = snapPoint(target,point,args["Position"],hasMod(args,MODSNAP)) + # point = view.getPoint(args["Position"][0],args["Position"][1]) + # point = snapPoint(target,point,args["Position"],hasMod(args,MODSNAP)) + if target.node: + last = target.node[-1] + else: + last = None + point = FreeCADGui.Snapper.snap(args["Position"],lastpoint=last,active=hasMod(args,MODSNAP)) if (not plane.weak) and workingplane: # working plane was explicitely selected - project onto it @@ -137,7 +142,7 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True): # point = plane.projectPoint(point) else: point = plane.projectPoint(point, viewDirection) - ctrlPoint = Vector(point.x,point.y,point.z) + ctrlPoint = Vector(point) if (hasMod(args,MODCONSTRAIN)): # constraining if mobile and (target.constrain == None): target.node.append(point) @@ -297,7 +302,6 @@ class SelectPlane: if self.ui: self.ui.offUi() - #--------------------------------------------------------------------------- # Geometry constructors #--------------------------------------------------------------------------- @@ -324,7 +328,6 @@ class Creator: else: FreeCAD.activeDraftCommand = self self.ui = FreeCADGui.draftToolBar - self.ui.cross(True) self.ui.sourceCmd = self self.ui.setTitle(name) self.ui.show() @@ -338,11 +341,6 @@ class Creator: self.snap = snapTracker() self.extsnap = lineTracker(dotted=True) self.planetrack = PlaneTracker() - if Draft.getParam("grid"): - self.grid = gridTracker() - self.grid.set() - else: - self.grid = None def IsActive(self): if FreeCADGui.ActiveDocument: @@ -355,12 +353,11 @@ class Creator: self.extsnap.finalize() self.node=[] self.planetrack.finalize() - if self.grid: self.grid.finalize() if self.support: plane.restore() + FreeCADGui.Snapper.off() FreeCAD.activeDraftCommand = None if self.ui: self.ui.offUi() - self.ui.cross(False) self.ui.sourceCmd = None msg("") if self.call: @@ -1671,11 +1668,6 @@ class Modifier: self.snap = snapTracker() self.extsnap = lineTracker(dotted=True) self.planetrack = PlaneTracker() - if Draft.getParam("grid"): - self.grid = gridTracker() - self.grid.set() - else: - self.grid = None def IsActive(self): if FreeCADGui.ActiveDocument: @@ -1694,7 +1686,7 @@ class Modifier: self.ui.cross(False) msg("") self.planetrack.finalize() - if self.grid: self.grid.finalize() + FreeCADGui.Snapper.off() if self.call: self.view.removeEventCallback("SoEvent",self.call) self.call = None diff --git a/src/Mod/Draft/Draft_rc.py b/src/Mod/Draft/Draft_rc.py index bd6c5bcfe..a63a50945 100644 --- a/src/Mod/Draft/Draft_rc.py +++ b/src/Mod/Draft/Draft_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: Thu Dec 15 14:01:48 2011 +# Created: Fri Dec 16 17:53:29 2011 # by: The Resource Compiler for PyQt (Qt v4.7.3) # # WARNING! All changes made in this file will be lost! diff --git a/src/Mod/Draft/Makefile.am b/src/Mod/Draft/Makefile.am index f9b5a99cd..baf95a192 100644 --- a/src/Mod/Draft/Makefile.am +++ b/src/Mod/Draft/Makefile.am @@ -101,6 +101,7 @@ EXTRA_DIST = \ Resources/icons/Draft_Construction.svg \ Resources/icons/Draft_Draft.svg \ Resources/icons/Draft_2DShapeView.svg \ + Resources/icons/Draft_Cursor.svg \ Resources/patterns/concrete.svg \ Resources/patterns/cross.svg \ Resources/patterns/line.svg \ diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index 965d34223..579cfdd07 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -219,7 +219,7 @@ class plane: 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) + b = min(ax,ay,az,bx,by,bz) if b in [ax,bx]: return "x" elif b in [ay,by]: diff --git a/src/Mod/Draft/draftlibs/fcgeo.py b/src/Mod/Draft/draftlibs/fcgeo.py index 500203bff..a451ad17e 100755 --- a/src/Mod/Draft/draftlibs/fcgeo.py +++ b/src/Mod/Draft/draftlibs/fcgeo.py @@ -272,9 +272,9 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F else : # Line isn't on Arc's plane if dirVec.dot(arc.Curve.Axis) != 0 : toPlane = Vector(arc.Curve.Axis) ; toPlane.normalize() - d = vec1.dot(toPlane) + d = pt1.dot(toPlane) dToPlane = center.sub(pt1).dot(toPlane) - toPlane = Vector(vec1) + toPlane = Vector(pt1) toPlane.scale(dToPlane/d,dToPlane/d,dToPlane/d) ptOnPlane = toPlane.add(pt1) if round(ptOnPlane.sub(center).Length - arc.Curve.Radius,precision) == 0 :