曲线基础

1. 贝塞尔曲线

https://pomax.github.io/bezierinfo/zh-CN/index.html
https://zh.javascript.info/bezier-curve

  • 使用n个控制点$P_1,P_2,\cdots ,P_n$来控制曲线的形状
  • 曲线通过起始点$P_1$ 和终止点$P_n$,接近但不通过中间点$P_2\sim P_{n-1} $
  • 曲线的阶次等于控制点的数量减一。对于两个点我们能得到一条线性曲线(直线),三个点对应一条二阶曲线,四个点对应一条三阶曲线。

1.1. Bezier曲线定义

贝塞尔曲线由一组控制点 $P_i \in \mathbb{R}^2, 0 \leq i \leq n$ 构成,其方程为:

$$
X(t) = \sum_{i=0}^{n} \binom{n}{i} t^i (1-t)^{n-i} P_i = \sum_{i=0}^{n} B_i(t) P_i, \quad t \in [0,1]
$$

$$
\binom{n}{i} = \frac{n!}{i! (n-i)!}
$$

其中,$B_i(t)$ 称为伯恩斯坦(Bernstein)基函数。Bezier曲线是控制多边形的控制点关于伯恩斯坦基函数的加权和,Bezier曲线的次数都为 $n$,需要 $n+1$ 个控制点来定义。

1.2. Bernstein基函数的性质

  • 非负性
  • 端点性质
  • 权性
  • 对称性
  • 导函数

1.3. Bezier曲线的性质

  • 端点性质
  • 一阶导数
  • 二阶导数
  • 对称性
  • 凸包性质
  • 几何不变性
  • 仿射不变性
  • 变差缩减性

2. B样条曲线

2.1. B样条曲线定义

为了能描述复杂形状具有局部性质,改用特殊的基函数即 B样条基函数代替 Bernstein 基函数。一条次数为 $j$ 的平面 B样条曲线,由一组控制点 $P_i \in \mathbb{R}^2$ 和一组节点矢量 $t_i$ 组成,其方程为:

$$
X(t) = \sum_{i=0}^{n} B_{i,j}(t) P_i, \quad t \in [t_0, t_1], \quad 1 \leq j \leq n
$$

由考克斯-德布尔递归公式定义的基函数为:
$$
B_{i,0}(t) =
\begin{cases}
1, & t_i \leq t < t_{i+1} \
0, & \text{otherwise}
\end{cases}
$$

$$
B_{i,j}(t) = \frac{(t - t_i) B_{i,j-1}(t)}{t_{i+j-1} - t_i} + \frac{(t_{i+j} - t) B_{i+1,j-1}(t)}{t_{i+j} - t_{i+1}}, \quad 1 \leq j \leq n
$$

其中,约定 $0/0 = 0$;控制点的个数为 $n+1$;节点个数为 $n+j+2$;基函数的阶次为 $j+1$,次数为 $j$。

2.2. B样条基函数的性质

  • 递推性
  • 规范性
  • 局部支撑性
  • 连续性

2.3. B样条曲线的分类

  • 均匀B样条曲线
  • 准均匀B样条曲线
  • 分段Bezier曲线
  • 非均匀B样条曲线

3. Nurbs曲线

Nurbs方法在有理Bezier方法与非有理B样条方法的基础上,引入了权因子和分母。主要优点如下:

  • 将初等曲线曲面与自由曲线曲面的表达方式统一起来;
  • 增加了权因子,有利于对曲线曲面的形状控制和修改;
  • 非有理B样条、有理、非有理Bezier方法是Nurbs的特例;
  • 在比例、旋转、平移、错切以及平行和透视投影变化下是不变的;

3.1. Nurbs曲线的定义

平面非均匀有理B样条曲线 (Non-Uniform Rational B-Splines, 简称 NURBS) 曲线为:
$$
X(t) = \sum_{i=0}^{n} \frac{\omega_i B_{i,j}(t) P_i}{\sum_{k=0}^{n} \omega_k B_{k,j}(t)}, t \in [0,1]
$$
其中,$\omega_i, i = 0, 1, \dots, n$ 为权因子,分别与控制点 $P_i, i = 0, 1, \dots, n$ 相联系。

3.2. Nurbs曲线的性质

  • 曲线过两端点
  • 局部修改性质
  • 变差减小性质
  • 强凸包性
  • 仿射和透视变换不变性
  • 参数连续性
  • 某一个权因子为0,则相应的控制点对曲线没有影响
  • 某一个权因子为无穷大,则在相应的参数范围内该点为控制点
  • 不包含内节点的Nurbs曲线是有理Bezier曲线

4. B-样条曲线与 NURBS 曲线的区别(chatGPT)

4.1. B-样条曲线(B-Splines)

定义
B-样条(Basis Splines)是一类分段多项式曲线,由若干控制点和基函数构成。
特性

  • 局部控制
    移动或修改一个控制点只会影响与之相邻的曲线部分。
  • 不经过所有控制点
    B-样条曲线通常不会穿过所有的控制点,而是通过一组点来定义曲线的形状。
  • 无权重
    B-样条曲线的每个控制点对曲线的影响是通过基函数的值来决定的,且没有权重因子。

4.2. NURBS 曲线(Non-Uniform Rational B-Splines)

定义
NURBS 是对 B-样条的扩展,允许每个控制点有一个“权重”值,从而提供更高的灵活性。NURBS 既可以表示简单的曲线(如圆、椭圆),也可以表示复杂的自由曲线。

特性

  • 有权重
    NURBS 允许每个控制点有一个权重(权重不为 1),通过权重对曲线形状进行调整。这使得 NURBS 曲线比 B-样条更灵活。
  • 精确表示几何形状
    NURBS 可以精确表示圆、椭圆、直线等常见的几何形状,而 B-样条曲线无法做到这一点。
  • 包括 B-样条
    NURBS 曲线可以视为 B-样条曲线的一个扩展,所有 B-样条曲线都是 NURBS 曲线,但并非所有的 NURBS 曲线都是 B-样条曲线。

4.3. B-样条与 NURBS 的区别

  • 权重
    B-样条曲线没有权重,而 NURBS 曲线有控制点的权重,可以影响曲线的形状,提供更大的灵活性。
  • 表示能力
    NURBS 曲线可以表示更复杂的几何形状,如圆、椭圆等,而 B-样条曲线只能表示更为一般的自由形状。
  • 数学模型
    NURBS 曲线可以看作是 B-样条曲线的一个推广,通过引入权重来增加曲线的表示能力。
  • 适用范围
    B-样条曲线通常用于工程、计算机图形学中表示平滑曲线;而 NURBS 曲线广泛应用于 CAD(计算机辅助设计)和 3D 建模中,因为它能够表示更多的几何形状和控制。

4.4. 总结

  • B-样条曲线和 NURBS 曲线都用于表示平滑的曲线,但 NURBS 具有更多的灵活性和表达能力,尤其在需要精确表示特定几何形状时。
  • NURBS 曲线是 B-样条的扩展,允许通过控制点的权重来调整曲线形状,提供了更高的控制精度。

这些区别使得 NURBS 曲线在复杂的设计和建模任务中比 B-样条曲线更为常用。

5.NURBS如何退化成B-Spline

看代码发现,nurbs曲线是有理b样条曲线(nurbs曲线的weight值不为空)

  • b样条曲线
logical rational = FALSE;
double* weights = nullptr;
  • nurbs曲线
logical rational = TRUE;
double weights[] = {1, 1, 1, 1, 1};

B-Spline理解节点和节点向量博客参考:
https://zhuanlan.zhihu.com/p/686518292

6. B-Spline如何退化成Bezier

参考博客:https://zhuanlan.zhihu.com/p/686518292

如果B样条曲线的n=p(即,最大控制点下标=阶数),节向量有2(p+1)个knot,并且分别有p+1个knot在首尾固定。这个B样条曲线就退化成贝塞尔曲线。

节点向量均匀化,B-Spline就会退化为Bezier。

在满足控制顶点数N=曲线次数k+1时,此时的准均匀B样条曲线即为BEZIER曲线。节点(knot)总数为N+k+1,分布情况:一半为0,一半为1。

容差

数值容差和几何容差概述

“容差”部分转载自:https://blog.csdn.net/qqQD10/article/details/109199591

1. 数值容差

数值大小:double数值计算精度为16位,4位舍入,所以通常取值为 1.0e-12

建模核心 数值公差 (mm)
OCCT 1.0e-12
Opennurbs 1.0e-12
ACIS 1.0e-11

解决问题

  • 0.1 + 0.2 !== 0.3
  • num1 > num2 + tol !== num1 - num2 > tol
  • Math.sqrt(2) * Math.sqrt(2) !== 2

解释

  • 数值容差主要用于解决数值计算过程中由于浮点数精度限制导致的误差问题。
  • 当处理数值计算时,计算精度和舍入误差可能导致数学表达式不成立,因此需要引入容差来避免这些问题。

注意:几何算法系统的数值容差一般是由系统内部设定,外部用户不可操作。

2. 线性容差/距离容差/全局容差/容差/公差

数值大小:几何上最小的距离尺度(例如:机械领域为0.001mm,建筑领域为1mm),比数值容差大几个量级。通常,机械领域为 1.0e-6,建筑领域为 1.0e-3

建模核心 线性公差 (mm)
Parasoild 1.0e-8
Open CASCADE 1.0e-7
ACIS 1.0e-6

解决问题

  • 数值精度:当两个点距离小于这个容差时,几何系统认为它们是相同的。
  • 引入原因
    • 用户的捕捉或吸附功能没有打开,或者做得不好(系统外部问题)。
    • 第三方数据导入可能导致误差。
    • 为了平衡精度与数据量(效率):例如曲面求交和投影交线的采样点之间的最小距离,如果设置得太小则数据量过大,效率低;如果设置得太大则求解精度不足。

注意:几何算法系统根据业务场景决定容差值,外部用户无法操作。

3. 局部容差

局部容差是为每个顶点、边缘和面附加的容差,每个拓扑部分有自己的容差。

数值大小

几何算法内部计算出来的局部容差通常不超过几何上最小的距离尺度。若局部容差过大,可能导致拓扑或几何上的问题,导致Brep(边界表示)不合法。

比全局容差的优势

  • 全局容差需要覆盖最坏的情况,这可能会导致过大的容差值。
  • 局部容差可以针对不同的拓扑部分设定,避免在一些情况下发生错误,例如短边缘因容差值重合导致无法正确生成。

注意:几何算法系统内部决定容差设置,外部用户不可操作。

4. 建模容差 / 模糊容差(Fuzzy)

应用场景:不同行业和客户对三维CAD/CAM设计平台的要求差异。

不同CAD平台的建模容差

CAD平台 应用领域 建模公差 (mm)
UG/NX CAD/CAM 0.01
CATIA CAD/CAM 0.001
Solidworks CAD 0.0254

解决问题

建模容差用于处理实际应用中的几何结构精度要求,确保几何元素在一定的距离范围内被视为足够接近,从而解决设计中的精度问题。

5.总结:

不同的行业和应用场景需要根据实际需求设置适当的建模容差,以便平衡精度和效率之间的关系。

用python绘制Bezier曲线

import numpy as np
import matplotlib.pyplot as plt
from math import comb
from scipy.optimize import fsolve

# 定义控制点
points = np.array([[-1, -1], [0, 0], [-1, 1]])

# 定义贝塞尔基函数
def bernstein_poly(i, n, t):
return comb(n, i) * (t ** i) * ((1 - t) ** (n - i))

# 计算贝塞尔曲线点
def bezier_curve(points, num_points=1000):
n = len(points) - 1
t = np.linspace(0, 1, num_points)
curve = np.zeros((num_points, 2))
for i in range(n + 1):
curve += np.outer(bernstein_poly(i, n, t), points[i])
return curve

# 绘制圆的函数
def circle_points(center, radius, num_points=1000):
theta = np.linspace(0, 2 * np.pi, num_points)
x = center[0] + radius * np.cos(theta)
y = center[1] + radius * np.sin(theta)
return np.column_stack((x, y))

# 求解两条曲线的交点
def find_intersections(bezier_points, circle_points):
intersections = []
for i in range(len(bezier_points) - 1):
for j in range(len(circle_points) - 1):
# 线段 (p1, p2) 和 (q1, q2)
p1, p2 = bezier_points[i], bezier_points[i + 1]
q1, q2 = circle_points[j], circle_points[j + 1]

# 参数方程的解
A = np.array([[p2[0] - p1[0], q1[0] - q2[0]], [p2[1] - p1[1], q1[1] - q2[1]]])
b = np.array([q1[0] - p1[0], q1[1] - p1[1]])

try:
t, u = np.linalg.solve(A, b)
if 0 <= t <= 1 and 0 <= u <= 1: # 确保交点在线段范围内
intersection = p1 + t * (p2 - p1)
intersections.append(intersection)
except np.linalg.LinAlgError:
pass # 矩阵不可逆的情况,跳过

return intersections

# 绘制贝塞尔曲线
bezier_points = bezier_curve(points)

# 绘制圆
circle_center = (0, 0)
circle_radius = 1
circle_pts = circle_points(circle_center, circle_radius)

# 计算交点
intersections = find_intersections(bezier_points, circle_pts)

# 创建图形
fig, ax = plt.subplots(figsize=(8, 8))

# 绘制贝塞尔曲线
ax.plot(bezier_points[:, 0], bezier_points[:, 1], label='Bezier Curve', color='blue')

# 绘制圆
ax.plot(circle_pts[:, 0], circle_pts[:, 1], label='Circle', color='green')

# 绘制控制点
ax.scatter(points[:, 0], points[:, 1], color='red', label='Control Points')

# 绘制交点并标注坐标
for idx, point in enumerate(intersections):
ax.scatter(*point, color='purple', zorder=5)
ax.text(point[0], point[1], f'{point}', fontsize=8, color='purple', ha='right')

# 绘制笛卡尔坐标轴
ax.axhline(0, color='black', linewidth=0.8)
ax.axvline(0, color='black', linewidth=0.8)
ax.grid(color='gray', linestyle='--', linewidth=0.5)

# 设置比例、标题和图例
ax.axis("equal")
ax.set_title("Bezier Curve and Circle Intersection")
ax.set_xlabel("X")
ax.set_ylabel("Y")

# 将图例放在右侧
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.tight_layout()
plt.show()