1、Nurbs 样条线算法推导及 python 实现注:在现在的建模软件中 Nurbs 曲线还引入一个跨度的概念,在这里并不体现,这里是有几个控制点就是控制点数-1 阶,最后好像到 36 阶曲线就发散了耶三个控制点:a=(ax, ay)b=(bx, by)c=(cx, cy)0t1e 为 ab 线上一点则:=e=(ex, ey)=(t*(bx-ax)+ax, t*(by-ay)+ay)同理,同一 t 值,f 为 bc 线上一点为:f=(fx, fy)=(t*(cx-bx)+bx, t*(cy-by)+by)g 点为动线 ef 上的动点为:g=(t*(fx-ex)+ex, t*(fy-ey)+ey)
2、=(gx, gy)gx= t2*(cx-2*bx+ax)+2*t*(bx-ax)+axgy= t2*(cy-2*by+ay)+2*t*(by-ay)+ay动点 g 的运动方程入上图所示;0:(0, 0);1:(1, 1);2:(3, 3);=2(221+0)+2(10)+0;=2(221+0)+2(10)+0abc四个控制点0t1a=(ax, ay) (0, 0)b=(bx, by) (1, 1)c=(cx, cy) (2, 2)d=(dx, dy) (3, 3)e=(ex, ey)=(t*(bx-ax)+ax, t*(by-ay)+ay) (10)+0,f = (fx , fy)=(t*(c
3、x-bx)+bx, t*(cy-by)+by)(21)+1,g= (gx, gy)=(t*(dx-cx)+cx, t*(dy-cy)+cy)(32)+2,h=(hx, hy)=(t*(fx-ex)+ex, t*(fy-ey)+ey)2(221+0)+2(10)+0,I = (ix , iy )=(t*(gx-fx)+fx, t*(gy-fy)+fy)abcd2(322+1)+2(21)+1,j = (jx, jy )=(t*(ix-hx)+hx, t*(iy-hy)+hy)3(332+310)+32(221+0)+3(10)+0,3(33(21)0)+32(221+0)+3(10)+0,N 个
4、控制点:def cv(N, t):n= Point(t*( Ni+1.x- Ni.x)+Ni.x, t*( Ni+1.y- Ni.y)+Ni.yfor i in range(len(N)-1) )if len(n)=1:return n0else:return cv(n, t)0t1N=p0, p1, p2pnprint cv(N, t)python 代码实现:(按分段数计算样条线上的点)def cv(N, seg):step=1/segsP=N0eP=N-1p=sPoldN=Nfor i in range(step, 1, step):t=i*stepcount= len(N)while c
5、ount1:n= Point(t*( Ni+1.x- Ni.x)+Ni.x , t*( Ni+1.y- Ni.y)+Ni.y) for i in range(count-1) N=ncount-=1N=oldNp.append(n0)p.append(eP)因为 y 和 x 的运算过程相同的故下面只求 xreturn p用多项式的方法优化运算:各项常数确定:Cx, Cy (0, 0)(1, 1)(2, 2)(3, 3)(4, 4)(10)+0,(21)+1,(32)+2,(43)+3,2(21)(10)+2(10)+02(32)(21)+2(21)+12(43)(32)+2(32)+23(32
6、)(21) (21)(10)+32(21)(10)+3(10)+03(43)(32) (32)(21)+32(32)(21)+3(21)+1;4 ( (43)(32) (32)(21) (32)(21) (21)(10)+43(32)(21) (21)(10)+62(21)(10)+4(10)+00=041=4(10)62=6(21)1)43=4(32)(21)2)4=( (43)(32) (32)(21)3)1=12=(21)3=(32)2)4= (43)(32) 3)2=23=(32)4=(43)3)3=34=(43)4=44=(43)4=3+44=(43)4=3+43=(32)3=2+3
7、4=(43)4=3+43=(32)3=2+32=(21)2=1+24=( 43)4=3+43=( 32)3=2+32=( 21)2=1+21=( 10)1=0+143,4012342,3, 41, 2, 3, 40, 1, 2, 3, 443,42,3, 41, 2, 3, 40, 1, 2, 3, 4各项系数增量:scale1,11,2,11,3,3,11,4,6,4,1各项系数增量 scale:, =1, 1+1, ; (, 0=1, =1)=, def getScales(n):各项常系数的缩放值1,1 n=11,2,1 n=21,3,3,1 n=31,4,6,4,1 n=4. .返回第
8、 n 行数据n 为控制点数目if n, self.eQuit)self.bind(, self.eLeftClick)self.bind(, self.eLeftDrag)self.bind(, self.eLeftRelease)self.bind(, self.eMidClick)self.bind(, self.eRightClick)self.colorNormal=darkorangeself.colorPick=greenself.colorEnd=blueself.colorDashLine=orangeself.colorCurve=darkblueself.stateText
9、=self.create_text(100, 100, text=self.editState)self.pack(side=left)self.segScale=tkinter.Scale(root, command=self.setSeg, width=10, length=480,sliderlength=15, resolution=1)#滑块self.segScale.pack(side=right)def setSeg(self, seg):#设置分段数if self.curLineId:self.lineDictself.curLineId.seg=max(1,int(seg)s
10、elf.need2update=True#print (event)def eQuit(self, event):self.running=0def eLeftClick(self, event):pos=event.x, event.yif self.editState=new:getCtrps=self.getObjs(ctrp, pos, 3)if len(getCtrps):self.curCtrPId=getCtrps0self.modifyPoint(self.curCtrPId, lineColor=self.colorPick)else:self.curCtrPId=self.
11、createPoint(pos, 3, lineColor=self.colorPick)self.curCtrPsDictself.curCtrPId=posself.curCtrPsInd.append(self.curCtrPId)if self.curLineId is None and len(self.curCtrPsInd)1:ctrps=self.sortCtrPs(self.curCtrPsDict, self.curCtrPsInd)curve=nurbs.Nurbs(ctrps)self.curLineId=self.create_line(curve.cvps, tag
12、s=curve, fill=self.colorCurve)self.lineDictself.curLineId=curveself.dotLineId=self.create_line(ctrps, dash=1, fill=self.colorDashLine)elif self.editState=select:getCurve=self.getObjs(curve, pos, 3)if len(getCurve):self.curLineId=getCurve0#self.curCtrPsDict.clear()#self.curCtrPsInd.clear()ctrps=self.
13、lineDictself.curLineId.ctrPointsfor pos in ctrps:point=self.createPoint(pos, 3, lineColor=self.colorNormal)self.curCtrPsDictpoint=posself.curCtrPsInd.append(point)self.modifyPoint(self.curCtrPsInd-1, lineColor=self.colorEnd)self.dotLineId=self.create_line(ctrps, dash=1, fill=self.colorDashLine)self.
14、need2update=Trueself.segScale.set(self.lineDictself.curLineId.seg)#获取分段数self.editState=newself.itemconfig(self.stateText, text=self.editState)def eLeftDrag(self, event):pos=event.x, event.yif not self.curCtrPId is None:self.curCtrPsDictself.curCtrPId=posself.modifyPoint(self.curCtrPId, pos)self.need
15、2update=Truedef eLeftRelease(self, event):pos=event.x, event.yif not self.curCtrPId is None:print (self.curCtrPId)self.curCtrPsDictself.curCtrPId=posself.modifyPoint(self.curCtrPId, pos)if self.curCtrPId=self.curCtrPsInd-1:if len(self.curCtrPsInd)1:self.modifyPoint(self.curCtrPsInd-2, lineColor=self
16、.colorNormal)self.modifyPoint(self.curCtrPId, lineColor=self.colorEnd)else:self.modifyPoint(self.curCtrPId, lineColor=self.colorNormal)self.curCtrPId=Noneself.need2update=Truedef eMidClick(self, event):pos=event.x, event.yif self.curLineId:self.delete(*self.curCtrPsInd)self.delete(self.dotLineId)sel
17、f.curLineId=Noneelse:self.delete(*self.curCtrPsInd)self.curCtrPsDict.clear()self.curCtrPsInd=#.clear()self.curCtrPId=Noneself.editState=selectself.itemconfig(self.stateText, text=self.editState)def eRightClick(self, event):if self.curCtrPId:self.delete(self.curCtrPId)del self.curCtrPsDictself.curCtr
18、PIdself.curCtrPsInd.remove(self.curCtrPId)self.curCtrPId=Noneif len(self.curCtrPsInd)0:self.modifyPoint(self.curCtrPsInd-1, lineColor=self.colorEnd)elif self.curLineId:self.curCtrPsInd.reverse()self.modifyPoint(self.curCtrPsInd0, lineColor=self.colorNormal)self.modifyPoint(self.curCtrPsInd-1, lineCo
19、lor=self.colorEnd)def run(self):while self.running:if self.need2update:if not self.curLineId is None:ctrps=self.sortCtrPs(self.curCtrPsDict, self.curCtrPsInd)self.lineDictself.curLineId.ctrPoints=ctrpscvps=self.lineDictself.curLineId.cvpsself.coords(self.curLineId, *self.unpackCoords(cvps)self.coords(self.dotLineId, *self.unpackCoords(ctrps)need2update=Falsetime.sleep(0.01)self.update()root=tkinter.Tk()gui=Cv(root)gui.run()gui.destroy()root.destroy() 好像到 36 阶曲线就发散了参考文献:Alias 帮助文档