【CAD算法】【计算机图形学】Bezier贝塞尔曲线生成程序(python/numpy实现)[1]

整个项目,从Bezier曲线的创建,到Coons Patch曲面的实现,再到网格的实现和优化,还有最后对表面的光顺,链接如下:
【CAD算法】【计算机图形学】Bezier贝塞尔曲线生成程序(python/numpy实现)[1]
【CAD算法】【计算机图形学】Coons Patch曲面生成程序(python/numpy实现)[2]
【CAD核心算法】基于Laplacian Smoothing的网格mesh优化算法(python实现)[3]
【CAD算法】【遗传算法】采用遗传算法优化Coons Patch曲面程序(python实现)[4]
【CAD算法】基于最速下降法(梯度下降)的最短路径实现(python实现)[5]

1.内容回顾

首先回顾一下,表示一个曲线,有两种方式,implicit表示法和参数表示法。
implicit表示法:通过XYZ坐标表示曲线。
优点:

  • Compact
  • Easy to check if a point belongs to the curve

缺点:

  • Difficult for curve evaluation
  • Difficult for partial curve definition

参数表示法:Represent the X, Y, Z coordinates as a function of a single parameter
优点:

  • Easy for curve evaluation.
  • Convenient for partial curve definition.
  • Many others such as easy for manipulation, intersection.

很明显,参数表示法在工程领域中被广泛运用。

参数表示法有6种,分别是:

  • Conic sections
  • Polynomial Curves
  • Hermite Curves
  • Bezier Curves
  • B-Splines
  • Non-Uniform Rational B-Splines (NURBS)

Conic sections:适用于描述圆锥曲线(圆/椭圆/双曲线/抛物线)。
Polynomial Curves:P(u) = [x(u) y(u) z(u)]T = a0 + a1u + a2u2 + a3u3 (0<=u<=1),这种表示法不能直观地通过改变a0到a3的值预测到曲线形状,一般不被运用。

Hermite Curves:P(u) = f0(u)P0 + f1(u)P1 + f2(u)P0’+ f3(u)P1’ (0<=u<=1),通过确定一条曲线两端点的坐标和斜率来确定一条曲线的坐标。同样不能直观地通过两端点的信息确定曲线的形状。
B spine:也就是B样条曲线,这种表示法得到最广泛的运用,它克服了Bezier曲线某一个控制点坐标变换会整体上地影响曲线形状这个缺点。
Bezier Curves:通过n+1个控制点的坐标,得到曲线的坐标。它的定义如下:
在这里插入图片描述

一定要注意,参数u的取值范围只能在0-1之间,包括端点。根据公式可知,只要知道了bezier曲线的维度n,也就是控制点数减一,就能计算出每一项的blending function的值,分别和控制点坐标相乘再求和,就能得到Bezier曲线的表达式。一旦确定了一个u的值,自然能得到对应曲线的坐标。如果想通过pyplot画出来,可以通过采样的方式,用很多个点来描述这个曲线。

2.题目描述

现在开始我们的程序。问题如下,用户通过txt或者控制台输入一组坐标,该程序能自动计算出Bezier曲线并通过画图软件画出来。
其中,输入信息格式如下:

Line 1           m          /*  Bezier 曲线的数量 */
Line 2           n1          /* 第一条Bezier曲线有多少个控制点 */
Line 3           0 or 1      /*0意味着该曲线首尾闭合, 1代表着首尾不相连 */
Line 4           第一个控制点坐标  /* 坐标之间通过“,”或空格分开 */
Line 5           第二个控制点坐标
.
.
.
Line n1+3        第n个控制点坐标

可能这么描述还不是很清楚,举一个例子:

2                           #一共需要绘制2条Bezier曲线
4                           #第一条曲线有4个控制点
1                           #第一条曲线首尾不相连
1.5		0.4	-3              #控制点的三维坐标
1.7   	-2.56   	4.78    #控制点的三维坐标
0   		245.6 	10      #控制点的三维坐标
0		0	0               #控制点的三维坐标
3                           #第二条曲线有4个控制点
0                           #第二条曲线首尾相连
3		5	-6              #控制点的三维坐标
-4		3.5	7               #控制点的三维坐标
1.5		-5	-10.5           #控制点的三维坐标

3.解决方案

3.1头文件引用和变量定义

import os
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd

np.set_printoptions(suppress = True)
#database = []
m = np.array([],dtype = np.uint8) # the number of bazier curves
pointsNumber = np.array([],dtype = np.uint8) #for the ith curve, the points number of it
closedOrOpen = np.array([],dtype = np.uint8) #for the ith curve, 0 means it is open, 1 means it is closed
point = np.array([],dtype = np.float64) #the control points for all the curves


解释一下,以上四个变量均采用array数据类型,方便在内存中存放很多数据点并可视化。
其中,m是Bezier曲线数量;pointsNumber依次存放着每一条Bezier曲线的控制点数;closedOrOpen依次存放着每一条Bezier曲线是闭合的还是开放的bool数据;point依次存放着每一条Bezier曲线的全部特征点数,配合pointsNumber[i]指针可以很快定位到每一条Bezier曲线的第一个控制点在point数组中的位置。

3.2实现从txt文件的读取

def inputFromText():
  global filepath
  filepath = os.path.abspath('.') + "/" + raw_input("please input the file name(eg:data.txt):")
  
  with open (filepath) as data:
    line_zero = data.readline()
    line_zero_list = list(line_zero.split())
    global m
    m = np.array(line_zero_list, dtype=np.uint8)

    for i in range(m[0]): 
      pointsNumber_str = data.readline()
      pointsNumber_list = list(pointsNumber_str.split())
      temp1 = np.array(pointsNumber_list,dtype=np.uint8)
      global pointsNumber
      pointsNumber = np.append(pointsNumber,temp1) # the number of control points of the ith Bezier curve
      closedOrOpen_str = data.readline()
      closedOrOpen_list = list(closedOrOpen_str.split())
      temp2 = np.array(closedOrOpen_list,dtype=np.uint8)
      global closedOrOpen
      closedOrOpen = np.append(closedOrOpen,temp2) #0 means the ith curve is closed, 1 means it is open
      
      global point
      for j in range(temp1[0]):
      #u += 1
        point_str = data.readline()
        point_list = list(point_str.split())
        temp3 = np.array(point_list,dtype=np.float64)
        point = np.append(point,temp3) # control points for the ith Bezier Curve
  point = np.reshape(point, (-1,3))

读取txt采用python内置的文件操作函数。为了便于理解,采用循环方式依次读取txt的每一行,代码中没有介绍过的变量都是中间变量。

3.3 实现从控制台读取数据并保存数据到txt

def inputFromKeyboard(): 
  
  m_int = input("the number of Beziercurve:") #the number of Beziercurve
  global m
  m = np.array([m_int],dtype = np.uint8)

  filename = raw_input("please input filename:(example: data.txt):")  #input can only read 1 word!
  
  with open(filename,mode="w") as file_txt:
    file_txt.write(str(m_int)+'\n') #change to string!!

    for i in range(m_int): 
      pointsNumber_str = str(input("numbers of control points of the {}th Bezier curve:".format(i)))
      pointsNumber_list = list(pointsNumber_str.split())
      temp1 = np.array(pointsNumber_list,dtype=np.uint8)
      global pointsNumber
      pointsNumber = np.append(pointsNumber,temp1) # the number of control points of the ith Bezier curve
    #u += 1
      file_txt.write(pointsNumber_str + '\n')

      closedOrOpen_str = str(input("0 or 1: 0 means the ith curve is closed, 1 means it is open:")) #0 means the ith curve is closed, 1 means it is open
      closedOrOpen_list = list(closedOrOpen_str.split())
      temp2 = np.array(closedOrOpen_list,dtype=np.uint8)
      global closedOrOpen
      closedOrOpen = np.append(closedOrOpen,temp2) #0 means the ith curve is closed, 1 means it is open
      file_txt.write(closedOrOpen_str + '\n')

      global point
      for j in range(temp1[0]):
        point_str = str(raw_input("the coordinates of control points for the {}th Bezier Curve (eg: '0 0 0'):".format(i))) # control points for the ith Bezier Curve
        point_list = list(point_str.split())
        temp3 = np.array(point_list,dtype=np.float64)
        point = np.append(point,temp3) # control points for the ith Bezier Curve
        file_txt.write(point_str + '\n')

需要注意的是,保存到txt里的是string类型。

3.4实现Bezier曲线的核心程序

def drawCurve(closedOrOpen,pointsNumber,point,point_index):

  C = []
  n = pointsNumber - 1 # n is fewer in numbers than the total control points number. According to definition.
  point_show = np.array([],dtype=np.float64)
  for i in range(n+1):
    point_show = np.append(point_show,point[point_index + i])  

  if (closedOrOpen == 0): # if it is closed, means the oth and nth control points are the same.
    n += 1
    point_show = np.append(point_show,point[point_index])
  elif (closedOrOpen == 1):
    pass
  point_show = point_show.reshape((-1,3))
  #根据Bizier曲线定义
  if ((n+1) % 2 == 0):
    for i in range((n+1) / 2):
      up = 1
      down = 1
      j = n
      while (j > i):
        up *= j
        j = j - 1
      j = n - i
      while (j > 0):
        down *= j
        j = j - 1
      C.append(up / down)
  elif ((n+1) % 2 == 1):
    for i in range(n / 2):
      up = 1
      down = 1
      j = n
      while (j > i):
        up *= j
        j = j - 1
      j = n - i
      while (j > 0):
        down *= j
        j = j - 1
      C.append(up / down)
    up = 1
    down = 1
    j = n
    while (j > n/2):
      up *= j
      j = j - 1
    j = n/2
    while (j > 0):
      down *= j
      j = j - 1
    C.append(up / down)
  if (n%2 == 1):
    for i in range(int((n+1)/2)):
      C.append(C[int(n/2-i)])
  if (n%2 == 0):
    for i in range(int((n+1)/2)):
      C.append(C[int(n/2-i-1)])
  print("C",C) 
  f = np.array([],dtype=np.float64)

  u = 0
  fx = 0
  fy = 0 #not this place!!
  fz = 0
  #print(f_unit.shape)
  print("point_show\n",point_show)
  print("n+1",n+1)
  for j in range(100): 
    fx = 0
    fy = 0  #do not forget return 0!
    fz = 0
    for i in range(n+1):#这一条语句和Bezier曲线的表达式对应上了
      fx += C[i] * u**i * (1-u)**(n-i) * point_show[i][0]
      fy += C[i] * u**i * (1-u)**(n-i) * point_show[i][1]
      fz += C[i] * u**i * (1-u)**(n-i) * point_show[i][2]
    list = []
    list.append(fx)
    list.append(fy) 
    list.append(fz)
    array_list = np.array([list],dtype=np.float64) 
    u += 0.01
    f = np.append(f,array_list)
  f_show = f.reshape((-1,3))
  print(f_show.shape)
  #print(f_show)

  #show the control lines!
 ax.plot(f_show[:,0],f_show[:,1],f_show[:,2],'b.',markersize=1,label='open bazier curve')#bezier曲线,把'b.'改为'-'可以得到连续的曲线
  ax.plot(point_show[:,0],point_show[:,1],point_show[:,2],'-',markersize=1, label='control line')#控制点的连线
  ax.plot(point_show[:,0],point_show[:,1],point_show[:,2],'r.',markersize=8, label='control points')#控制点
  print(point_show.shape[0])

注意,当closedOrOpen为0时,也就是说这是一条封闭的Bezier,那么需要在末尾再把这个曲线的第一个控制点push进array里, 这样就能首尾相连。

3.5 main()函数

inputType = input("Please choose data from text/0 or keyboard/1 ?:")
#print(inputType.type())

if ( inputType == 0): # not '0'!!
  inputFromText()#从txt获取数据
elif ( inputType == 1):
  inputFromKeyboard()#从键盘获取数据

point_index = 0
fig = plt.figure()
ax = fig.gca(projection='3d')

for i in range(m[0]):#绘制m条Bezier曲线
  drawCurve(closedOrOpen[i],pointsNumber[i],point,point_index)
  point_index += pointsNumber[i]#获取每一条Bezier曲线第一个控制点在数组中的位置

ax.legend()
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()

4.试验结果

在这里插入图片描述

这个结果是根据样例数据绘制,红色点为各个Bezier曲线的控制点,中间的点为Bezier曲线,如果在plt.plot()中增加"-"参数或者增加点数,j这条曲线就能更好看一些。

在这里插入图片描述

采用Ubuntu16.03进行调试,交互界面如上。下图为一个更加残暴的数据所绘制出来的图像。

在这里插入图片描述
所有代码的链接如下:https://github.com/iwander-all/CAD.git

参考文献
K. TANG, Fundamental Theories and Algorithms of CAD/CAE/CAM

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值