【CAD算法】【計算機圖形學】Coons Patch曲面生成程序(python/numpy實現)[2]

1.內容回顧

關於Bezier曲線的定義和生成程序,請參考https://blog.csdn.net/iwanderu/article/details/103604863, 本節內容會部分沿用上一節代碼。

和曲線在空間的表示法一樣,參數表示法來描述曲面更加方便,它的分類如下:

  • Bi-linear Patch
  • Ruled Patch
  • Coons Patch
  • Bi-cubic Patch
  • Hermite Patch
  • Coons Patch with tangents
  • Bezier Patch
  • B-Spline Patch
  • Non-Uniform Rational B-Splines (NURBS)
  • Other types of surfaces

本次討論的內容是通過pyplot實現coons patch的繪製。
在這裏插入圖片描述
我們想描述的曲面爲S(u,v),它由四個邊界曲線P0,P1,Q0,Q1包圍而成,注意,u和v的正方向爲向上和向右爲正,這個細節十分重要,因爲繪圖的時候使用一系列的點,放在array數組中來對應計算的,如果順序錯了,繪製的曲線就是錯的。Coons Patch的參數化表達式如下:
在這裏插入圖片描述

其中alpha和beta是blending function,他們能調節曲面的起伏和光滑程度。默認alpha = 1 - u(對應上圖的alpha0), belta = 1 - v(對應上圖的alpha1),u和v分別是兩個方向的座標,注意他們的取值範圍是0-1之間的,包括0和1.編程實現的基礎就是這個公式。

2.題目描述

Based on your programming lab of curves, you are required in this lab to define and display Coons patches with different alpha(u) and belta(v) blending functions. You program should have the following functionalities.

(1) The user can define the four boundary curves of the Coons patch. The format of the curves is Bezier. For your convenience, you can assume that the number of control points of any Bezier curve is 6.

(2) You should allow the user to define different alpha(u) and belta(v). One suggestion is that you can define the alpha(u) (and belta(v)) as a Bezier curve with its two end control points on (0, 1) and (1, 0) respectively. For your convenience, you can fix the degree to 3 (i.e., 4 control points).

(3) Your program should be able to display the Coons patch by sampling the iso-curves and drawing their linear segments.

Note: when your program is examined, it will be viewed in the XY view; try to define 4 boundary curves that do not cross each other in the XY view.

大概意思就是說,題目要求通過4條Bezier曲線作爲邊界,他們由6個控制點確定位置和形狀。Blending function,也就是alpha和beta,由3個控制點確定,函數形式和Bezier曲線表達式是一樣的。

3.解決方案

3.1頭文件引用和變量定義

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

N = 100
step = 0.01 
#print(step)
value_type = np.float64
alpha_u = np.array([],dtype=value_type) #shape (N,1)
beta_v = np.array([],dtype=value_type)
f = np.array([],dtype=value_type) #shape (-1,3) all the boundary points
surface = np.array([],dtype=value_type) #shape (-1,3) all the surface points

matplotlib.use("TkAgg")

N是採樣點的數量,step是循環步長。f存放着四條邊界線上的點,每條線存有N個點的數據。surface是Coons Patch曲面上的點的座標,有N^2個。

3.2 Blending Function的實現

def buildBlendingFunction(control_point,u):
  #u is current step
  #control_point is a np.array,the shape should be (2,2)=>2 points, x-y(or called u-alpha) coordinates
  #return value is a scaler => alpha(u)
  P = np.array([0,1],dtype=value_type)
  P = np.append(P,control_point)
  P = np.append(P,[1,0])
  P = P.reshape((-1,2))
  #print('P\n',P)
  #global alpha
  alpha = np.array([],dtype=value_type) #shape should be (1,2)
  alpha = (1-u)**3 * P[0] + 3 * (1-u)**2 * u * P[1] + 3 * (1-u) * u**2 * P[2] + u**3 * P[3]
  #print("alpha\n",alpha)
  #print("alpha\n",alpha[0],alpha[1])
  #print(P[0],P[1])

  #plt.scatter(alpha[0],alpha[1],markersize=1)
  return alpha[0]

Blending function有4個控制點,是二維平面上的函數,第一個點和最後一個點的座標是確定的,分別是(0,1)和(1,0),通過改變中間控制點的座標來控制Blending function的形狀,再從而控制Coons Patch曲面的起伏程度。
這個函數只用傳入2個參數,中間控制點的座標和當前u或者v的值,返回值爲它的x座標。
在這裏插入圖片描述

3.3 邊界Bezier曲線的獲取

處理傳入參數。

# get 4 boundary bazier curves 
def getCurve(closedOrOpen,pointsNumber,point,point_index,u):

  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))

根據Bezier曲線的定義獲取曲線上點的座標。https://blog.csdn.net/iwanderu/article/details/103604863

  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)])

  global f

  fx = 0
  fy = 0 #not this place!!
  fz = 0

  for i in range(n+1):
    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) 
  f = np.append(f,array_list)
  #f = f.reshape((-1,3))

注意,根據main函數的調用方式,這裏的f依次存放着4條曲線上的點的座標,例如f[0]是邊界曲線1上的第一個點的座標,f[1]是邊界曲線2上的第一個點的座標,以此類推。

3.4main()函數

第一步,輸入Blending function控制點的座標。裏面的數值是可以用戶自定義的。

#1.control point for blending function alpha(u) and beta(v)
control_point1 = np.array([1.1,0.2,0.4,-0.5],dtype=value_type)
control_point1 = control_point1.reshape((2,2))
control_point2 = np.array([1.1,0.2,0.4,-0.5],dtype=value_type)
control_point2 = control_point1.reshape((2,2))

第二步,四條邊界曲線控制點的輸入,輸入順序一定要是向右和向上爲正,表格內數據可以用戶自定義。

#2.control point for boundary curves(they should share 4 edge points!)
#according to the definition in the class, Q1 is curve1,Q0 is curve3,P1 is curve2,P0 is curve4
point_curve1 = np.array([-10,10,10,-6,7,9,-2,7,5,2,8,9,6,11,11,10,10,10],dtype=value_type)
point_curve1 = point_curve1.reshape((-1,3))
#point_curve2 = np.array([10,10,10,13,6,0,7,2,5,9,-2,3,6,6,13,10,-10,10],dtype=value_type)
point_curve2 = np.array([10,-10,10,6,6,13,9,-2,3,7,2,5,13,6,0,10,10,10],dtype=value_type)
point_curve2 = point_curve2.reshape((-1,3))
#point_curve3 = np.array([10,-10,10,6,-7,9,2,-7,5,-2,-8,9,-6,-11,11,-10,-10,10],dtype=value_type)
point_curve3 = np.array([-10,-10,10,-6,-11,11,-2,-8,9,2,-7,5,6,-7,9,10,-10,10],dtype=value_type)
point_curve3 = point_curve3.reshape((-1,3))
point_curve4 = np.array([-10,-10,10,-13,3,0,-7,-2,-5,-9,2,3,-6,6,9,-10,10,10],dtype=value_type)
point_curve4 = point_curve4.reshape((-1,3))
#remember,you can choose your own points here,and when you put the points, do not put them randomly but with certain order.
#they should share 4 commom edge points

第三步,獲取Blending Function

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

u = 0
for i in range(N):
#3. get blending function
  alpha_u_item = np.array([buildBlendingFunction(control_point1,u)],dtype=value_type)
  #print(alpha_u_item)
  alpha_u = np.append(alpha_u,alpha_u_item)
  beta_v_item = np.array([buildBlendingFunction(control_point2,u)],dtype=value_type)
  beta_v = np.append(beta_v,beta_v_item)

第四步,獲得邊界曲線。

#4.get boundary curves, all the boundary points will array in f repeatedly.  
  getCurve(1,6,point_curve1,0,u)
  getCurve(1,6,point_curve2,0,u)
  getCurve(1,6,point_curve3,0,u)
  getCurve(1,6,point_curve4,0,u)

  u += step
  #plt.plot(alpha[0],alpha[1],markersize=1)

f = f.reshape((-1,3))
print(f.shape)
print(f)
# in f, f[0] is from curve 1,f[1] is from curve 2,f[2] is from curve 3,f[3] is from curve 4 and so on, and cycle for 1000 rounds!

第五步,繪製曲線

#5.show the boundary
plt.plot(f[:,0],f[:,1],f[:,2],'b.',markersize=1,label='open bazier curve')
plt.plot(point_curve1[:,0],point_curve1[:,1],point_curve1[:,2],'r.',markersize=8, label='control point1')
plt.plot(point_curve2[:,0],point_curve2[:,1],point_curve2[:,2],'r.',markersize=8, label='control point2')
plt.plot(point_curve3[:,0],point_curve3[:,1],point_curve3[:,2],'r.',markersize=8, label='control point3')
plt.plot(point_curve4[:,0],point_curve4[:,1],point_curve4[:,2],'r.',markersize=8, label='control point4')
plt.plot(point_curve1[:,0],point_curve1[:,1],point_curve1[:,2],'-',markersize=1)
plt.plot(point_curve2[:,0],point_curve2[:,1],point_curve2[:,2],'-',markersize=1)
plt.plot(point_curve3[:,0],point_curve3[:,1],point_curve3[:,2],'-',markersize=1)
plt.plot(point_curve4[:,0],point_curve4[:,1],point_curve4[:,2],'-',markersize=1)

#6.show the surface
surface_item = np.array([],dtype=np.float64)
Q00 = point_curve3[0]
Q01 = point_curve3[-1]
Q10 = point_curve1[0]
Q11 = point_curve1[-1]
for u in range(N):
  for v in range(N):
    surface_item = alpha_u[u]*f[4*v+3]+(1-alpha_u[u])*f[4*v+1] + beta_v[v]*f[4*u+2]+(1-beta_v[v])*f[4*u] - (beta_v[v]*(alpha_u[u]*Q00+(1-alpha_u[u])*Q01)+(1-beta_v[v])*(alpha_u[u]*Q10+(1-alpha_u[u])*Q11))
    #surface_item = u*f[4*v]+(1-u)*f[4*v+2] + v*f[4*v+1]+(1-v)*f[4*v+3] - (v*(u*Q00+(1-u)*Q01)+(1-v)*(u*f[4*v+1]+(1-u)*Q11))
    #plt.plot(surface_item[0],surface_item[1],surface_item[2],'y.',markersize=1)
    surface = np.append(surface,surface_item)

當然,如果也想顯示出用戶根據u,v的值選定的部分曲面的話,可以通過以下函數實現。

#7.show the selected part of the coon face
u1 = input("please input the begin of u(0~u2):") * N
u2 = input("please input the end of u(u1~1):") * N
v1 = input("please input the begin of v(0~v2):") * N
v2 = input("please input the end of v(v1~1):") * N
surface_selected = np.array([],dtype=np.float64)
for u in range(N):
  for v in range(N):
    if (u>=u1 and u<=u2 and v>=v1 and v<=v2):
      surface_item = alpha_u[u]*f[4*v+3]+(1-alpha_u[u])*f[4*v+1] + beta_v[v]*f[4*u+2]+(1-beta_v[v])*f[4*u] - (beta_v[v]*(alpha_u[u]*Q00+(1-alpha_u[u])*Q01)+(1-beta_v[v])*(alpha_u[u]*Q10+(1-alpha_u[u])*Q11))
      surface_selected = np.append(surface_selected,surface_item)    

最後,注意數據格式是可以被pyplot所接受的。把plt.plot(surface[:,0],surface[:,1],surface[:,2],‘y.’,markersize=1, label=‘surface’)中的’y.‘改爲’-'可以顯示連續的曲面。

surface = surface.reshape((-1,3))
surface_selected = surface_selected.reshape((-1,3))
plt.plot(surface[:,0],surface[:,1],surface[:,2],'y.',markersize=1, label='surface')
plt.plot(surface_selected[:,0],surface_selected[:,1],surface_selected[:,2],'g.',markersize=1)

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

4.試驗結果

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
所有代碼的鏈接如下:https://github.com/iwander-all/CAD.git

參考文獻
K. TANG, Fundamental Theories and Algorithms of CAD/CAE/CAM

發佈了32 篇原創文章 · 獲贊 55 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章