曲线与曲面的绘制
一、学习目标
(1)掌握常用规则参数曲线与曲面的编程绘制方法。
(2)掌握自由曲线与曲面的编程绘制方法。
(3)了解自由曲面的拼接编程方法。
二、学习内容
(1)编程绘一个规则参数曲线(如抛物线、星开线、心脏线)和规则参数曲面(如球、园柱、圆台、圆环线等)。
(2)编程绘制一个n次Bezier曲线和一个网格状三次Bezier曲面正轴测投影图。
三、实现代码
(1)parametric_curves_surfaces.py,核心算法文件
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# 参数曲线绘制函数
def plot_parametric_curves():
# 创建图形
fig, axs = plt.subplots(2, 2, figsize=(12, 10))
# 抛物线
t = np.linspace(-2, 2, 100)
x_parabola = t
y_parabola = t**2
axs[0, 0].plot(x_parabola, y_parabola)
axs[0, 0].set_title('抛物线')
# 星形线
t = np.linspace(0, 2*np.pi, 100)
x_astroid = np.cos(t)**3
y_astroid = np.sin(t)**3
axs[0, 1].plot(x_astroid, y_astroid)
axs[0, 1].set_title('星形线')
# 心脏线
t = np.linspace(0, 2*np.pi, 100)
x_cardioid = 2*np.cos(t) - np.cos(2*t)
y_cardioid = 2*np.sin(t) - np.sin(2*t)
axs[1, 0].plot(x_cardioid, y_cardioid)
axs[1, 0].set_title('心脏线')
plt.tight_layout()
plt.show()
# 参数曲面绘制函数
def plot_parametric_surfaces():
fig = plt.figure(figsize=(15, 10))
# 球面
ax1 = fig.add_subplot(221, projection='3d')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x_sphere = np.outer(np.cos(u), np.sin(v))
y_sphere = np.outer(np.sin(u), np.sin(v))
z_sphere = np.outer(np.ones(np.size(u)), np.cos(v))
ax1.plot_surface(x_sphere, y_sphere, z_sphere)
ax1.set_title('球面')
# 圆柱面
ax2 = fig.add_subplot(222, projection='3d')
theta = np.linspace(0, 2*np.pi, 100)
z = np.linspace(0, 1, 100)
theta_grid, z_grid = np.meshgrid(theta, z)
x_cylinder = np.cos(theta_grid)
y_cylinder = np.sin(theta_grid)
z_cylinder = z_grid
ax2.plot_surface(x_cylinder, y_cylinder, z_cylinder)
ax2.set_title('圆柱面')
# 圆台面
ax3 = fig.add_subplot(223, projection='3d')
r1, r2 = 1, 0.5
theta = np.linspace(0, 2*np.pi, 100)
z = np.linspace(0, 1, 100)
theta_grid, z_grid = np.meshgrid(theta, z)
r = r1 + (r2 - r1) * z_grid
x_cone = r * np.cos(theta_grid)
y_cone = r * np.sin(theta_grid)
z_cone = z_grid
ax3.plot_surface(x_cone, y_cone, z_cone)
ax3.set_title('圆台面')
# 圆环面
ax4 = fig.add_subplot(224, projection='3d')
R, r = 2, 1
u = np.linspace(0, 2*np.pi, 100)
v = np.linspace(0, 2*np.pi, 100)
u_grid, v_grid = np.meshgrid(u, v)
x_torus = (R + r*np.cos(v_grid)) * np.cos(u_grid)
y_torus = (R + r*np.cos(v_grid)) * np.sin(u_grid)
z_torus = r * np.sin(v_grid)
ax4.plot_surface(x_torus, y_torus, z_torus)
ax4.set_title('圆环面')
plt.tight_layout()
plt.show()
# Bezier曲线和曲面
def bezier_curve(points, t):
n = len(points) - 1
result = np.zeros(2)
for i in range(n + 1):
result += points[i] * comb(n, i) * (1 - t)**(n - i) * t**i
return result
def comb(n, k):
return np.math.factorial(n) / (np.math.factorial(k) * np.math.factorial(n - k))
def plot_bezier_curve():
# 控制点
points = np.array([
[0, 0],
[1, 2],
[2, -1],
[3, 1],
[4, 0]
])
# 生成曲线点
t = np.linspace(0, 1, 100)
curve_points = np.array([bezier_curve(points, ti) for ti in t])
# 绘制
plt.figure(figsize=(8, 6))
plt.plot(points[:, 0], points[:, 1], 'ro-', label='控制点')
plt.plot(curve_points[:, 0], curve_points[:, 1], 'b-', label='Bezier曲线')
plt.legend()
plt.title('5次Bezier曲线')
plt.grid(True)
plt.show()
def bezier_surface(points, u, v):
n, m = points.shape[0] - 1, points.shape[1] - 1
result = np.zeros(3)
for i in range(n + 1):
for j in range(m + 1):
result += points[i, j] * comb(n, i) * comb(m, j) * (1 - u)**(n - i) * u**i * (1 - v)**(m - j) * v**j
return result
def plot_bezier_surface():
# 控制点网格
points = np.array([
[[0, 0, 0], [1, 0, 1], [2, 0, 0]],
[[0, 1, 1], [1, 1, 2], [2, 1, 1]],
[[0, 2, 0], [1, 2, 1], [2, 2, 0]]
])
# 生成曲面点
u = np.linspace(0, 1, 20)
v = np.linspace(0, 1, 20)
u_grid, v_grid = np.meshgrid(u, v)
surface_points = np.zeros((len(u), len(v), 3))
for i in range(len(u)):
for j in range(len(v)):
surface_points[i, j] = bezier_surface(points, u[i], v[j])
# 绘制
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# 绘制控制点网格
for i in range(points.shape[0]):
for j in range(points.shape[1]):
ax.scatter(points[i, j, 0], points[i, j, 1], points[i, j, 2], color='red')
# 绘制曲面
ax.plot_surface(surface_points[:, :, 0], surface_points[:, :, 1], surface_points[:, :, 2], alpha=0.5)
ax.set_title('3x3控制点网格的Bezier曲面')
plt.show()
if __name__ == "__main__":
# 绘制参数曲线
plot_parametric_curves()
# 绘制参数曲面
plot_parametric_surfaces()
# 绘制Bezier曲线
plot_bezier_curve()
# 绘制Bezier曲面
plot_bezier_surface()
(2)app.py,使用Flask框架,提供参数曲线和曲面的Web可视化。
from flask import Flask, render_template, jsonify
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
import io
import base64
app = Flask(__name__)
def get_parametric_curves():
# 创建图形
fig, axs = plt.subplots(2, 2, figsize=(12, 10))
# 抛物线
t = np.linspace(-2, 2, 100)
x_parabola = t
y_parabola = t**2
axs[0, 0].plot(x_parabola, y_parabola, 'b-', linewidth=2)
axs[0, 0].set_title('抛物线', fontsize=12)
axs[0, 0].grid(True)
# 星形线
t = np.linspace(0, 2*np.pi, 100)
x_astroid = np.cos(t)**3
y_astroid = np.sin(t)**3
axs[0, 1].plot(x_astroid, y_astroid, 'r-', linewidth=2)
axs[0, 1].set_title('星形线', fontsize=12)
axs[0, 1].grid(True)
# 心脏线
t = np.linspace(0, 2*np.pi, 100)
x_cardioid = 2*np.cos(t) - np.cos(2*t)
y_cardioid = 2*np.sin(t) - np.sin(2*t)
axs[1, 0].plot(x_cardioid, y_cardioid, 'g-', linewidth=2)
axs[1, 0].set_title('心脏线', fontsize=12)
axs[1, 0].grid(True)
plt.tight_layout()
return fig_to_base64(fig)
def get_parametric_surfaces():
fig = plt.figure(figsize=(15, 10))
# 球面
ax1 = fig.add_subplot(221, projection='3d')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x_sphere = np.outer(np.cos(u), np.sin(v))
y_sphere = np.outer(np.sin(u), np.sin(v))
z_sphere = np.outer(np.ones(np.size(u)), np.cos(v))
ax1.plot_surface(x_sphere, y_sphere, z_sphere, cmap='viridis')
ax1.set_title('球面', fontsize=12)
# 圆柱面
ax2 = fig.add_subplot(222, projection='3d')
theta = np.linspace(0, 2*np.pi, 100)
z = np.linspace(0, 1, 100)
theta_grid, z_grid = np.meshgrid(theta, z)
x_cylinder = np.cos(theta_grid)
y_cylinder = np.sin(theta_grid)
z_cylinder = z_grid
ax2.plot_surface(x_cylinder, y_cylinder, z_cylinder, cmap='plasma')
ax2.set_title('圆柱面', fontsize=12)
# 圆台面
ax3 = fig.add_subplot(223, projection='3d')
r1, r2 = 1, 0.5
theta = np.linspace(0, 2*np.pi, 100)
z = np.linspace(0, 1, 100)
theta_grid, z_grid = np.meshgrid(theta, z)
r = r1 + (r2 - r1) * z_grid
x_cone = r * np.cos(theta_grid)
y_cone = r * np.sin(theta_grid)
z_cone = z_grid
ax3.plot_surface(x_cone, y_cone, z_cone, cmap='inferno')
ax3.set_title('圆台面', fontsize=12)
# 圆环面
ax4 = fig.add_subplot(224, projection='3d')
R, r = 2, 1
u = np.linspace(0, 2*np.pi, 100)
v = np.linspace(0, 2*np.pi, 100)
u_grid, v_grid = np.meshgrid(u, v)
x_torus = (R + r*np.cos(v_grid)) * np.cos(u_grid)
y_torus = (R + r*np.cos(v_grid)) * np.sin(u_grid)
z_torus = r * np.sin(v_grid)
ax4.plot_surface(x_torus, y_torus, z_torus, cmap='magma')
ax4.set_title('圆环面', fontsize=12)
plt.tight_layout()
return fig_to_base64(fig)
def get_bezier_curve():
points = np.array([
[0, 0],
[1, 2],
[2, -1],
[3, 1],
[4, 0]
])
t = np.linspace(0, 1, 100)
curve_points = np.array([bezier_curve(points, ti) for ti in t])
plt.figure(figsize=(8, 6))
plt.plot(points[:, 0], points[:, 1], 'ro-', label='控制点')
plt.plot(curve_points[:, 0], curve_points[:, 1], 'b-', label='Bezier曲线')
plt.legend()
plt.title('5次Bezier曲线', fontsize=12)
plt.grid(True)
return fig_to_base64(plt.gcf())
def get_bezier_surface():
points = np.array([
[[0, 0, 0], [1, 0, 1], [2, 0, 0]],
[[0, 1, 1], [1, 1, 2], [2, 1, 1]],
[[0, 2, 0], [1, 2, 1], [2, 2, 0]]
])
u = np.linspace(0, 1, 20)
v = np.linspace(0, 1, 20)
u_grid, v_grid = np.meshgrid(u, v)
surface_points = np.zeros((len(u), len(v), 3))
for i in range(len(u)):
for j in range(len(v)):
surface_points[i, j] = bezier_surface(points, u[i], v[j])
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
for i in range(points.shape[0]):
for j in range(points.shape[1]):
ax.scatter(points[i, j, 0], points[i, j, 1], points[i, j, 2], color='red')
ax.plot_surface(surface_points[:, :, 0], surface_points[:, :, 1], surface_points[:, :, 2], alpha=0.5, cmap='viridis')
ax.set_title('3x3控制点网格的Bezier曲面', fontsize=12)
return fig_to_base64(fig)
def bezier_curve(points, t):
n = len(points) - 1
result = np.zeros(2)
for i in range(n + 1):
result += points[i] * comb(n, i) * (1 - t)**(n - i) * t**i
return result
def bezier_surface(points, u, v):
n, m = points.shape[0] - 1, points.shape[1] - 1
result = np.zeros(3)
for i in range(n + 1):
for j in range(m + 1):
result += points[i, j] * comb(n, i) * comb(m, j) * (1 - u)**(n - i) * u**i * (1 - v)**(m - j) * v**j
return result
def comb(n, k):
return np.math.factorial(n) / (np.math.factorial(k) * np.math.factorial(n - k))
def fig_to_base64(fig):
canvas = FigureCanvasAgg(fig)
buf = io.BytesIO()
canvas.print_png(buf)
data = base64.b64encode(buf.getvalue()).decode('utf-8')
plt.close(fig)
return data
@app.route('/')
def index():
return render_template('index.html')
@app.route('/get_curves')
def get_curves():
return jsonify({
'parametric_curves': get_parametric_curves(),
'parametric_surfaces': get_parametric_surfaces(),
'bezier_curve': get_bezier_curve(),
'bezier_surface': get_bezier_surface()
})
if __name__ == '__main__':
app.run(debug=True)
(3)HTML页面,可视化展示。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>参数曲线与曲面可视化</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
padding-top: 20px;
}
.container {
max-width: 1200px;
}
.card {
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border: none;
}
.card-header {
background-color: #007bff;
color: white;
font-weight: bold;
}
.loading {
text-align: center;
padding: 20px;
}
.plot-container {
text-align: center;
padding: 10px;
}
.plot-container img {
max-width: 100%;
height: auto;
border-radius: 5px;
}
h1 {
color: #007bff;
margin-bottom: 30px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h1>参数曲线与曲面可视化</h1>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
参数曲线
</div>
<div class="card-body">
<div class="plot-container" id="parametric-curves">
<div class="loading">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
参数曲面
</div>
<div class="card-body">
<div class="plot-container" id="parametric-surfaces">
<div class="loading">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
Bezier曲线
</div>
<div class="card-body">
<div class="plot-container" id="bezier-curve">
<div class="loading">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
Bezier曲面
</div>
<div class="card-body">
<div class="plot-container" id="bezier-surface">
<div class="loading">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 加载所有图形
fetch('/get_curves')
.then(response => response.json())
.then(data => {
// 更新参数曲线
document.getElementById('parametric-curves').innerHTML =
`<img src="data:image/png;base64,${data.parametric_curves}" alt="参数曲线">`;
// 更新参数曲面
document.getElementById('parametric-surfaces').innerHTML =
`<img src="data:image/png;base64,${data.parametric_surfaces}" alt="参数曲面">`;
// 更新Bezier曲线
document.getElementById('bezier-curve').innerHTML =
`<img src="data:image/png;base64,${data.bezier_curve}" alt="Bezier曲线">`;
// 更新Bezier曲面
document.getElementById('bezier-surface').innerHTML =
`<img src="data:image/png;base64,${data.bezier_surface}" alt="Bezier曲面">`;
})
.catch(error => {
console.error('Error:', error);
document.querySelectorAll('.loading').forEach(el => {
el.innerHTML = '<div class="alert alert-danger">加载失败,请刷新页面重试</div>';
});
});
</script>
</body>
</html>
四、运行结果
五、项目简介
# 参数曲线与曲面可视化项目
## 项目概述
本项目使用Python Flask框架和Matplotlib库,实现了多种参数曲线和曲面的可视化展示,包括Bezier曲线/曲面的算法实现。
## 功能模块
### 1. 参数曲线可视化
- 抛物线
- 星形线
- 心脏线
### 2. 参数曲面可视化
- 球面
- 圆柱面
- 圆台面
- 圆环面
### 3. Bezier曲线/曲面
- 5次Bezier曲线
- 3x3控制点网格的Bezier曲面
## 技术特点
- 使用Flask提供Web服务接口
- Matplotlib生成高质量图形
- 参数方程精确计算曲线/曲面
- 响应式前端展示
## 运行方式
```bash
pip install -r requirements.txt
python app.py
```
访问 http://localhost:5000 查看可视化效果