样条曲线实体 (SplineEnt)
大约 6 分钟
样条曲线实体 (SplineEnt)
SplineEnt 表示 CAD 中的样条曲线实体,用于创建光滑的自由曲线。
概述
样条曲线(NURBS曲线)支持两种创建方式:
- 控制点模式:通过控制点"吸引"曲线形状,曲线不经过控制点(端点除外)
- 拟合点模式:曲线精确经过所有指定的拟合点,适合需要精确路径的场景
构造函数
import { SplineEnt, SplineControlPoint, Point2D } from 'vjcad';
// 创建样条曲线
const spline = new SplineEnt();
// 传统方式:添加控制点
const cp1 = new SplineControlPoint();
cp1.point2d = new Point2D(0, 0);
cp1.weight = 1;
spline.controlPoints.items.push(cp1);
// ... 添加更多控制点简化接口(推荐)
为了简化样条曲线的创建和操作,提供了更简洁的数组接口。
设置和添加控制点
import { SplineEnt, Engine } from 'vjcad';
const spline = new SplineEnt();
// 方式1: 批量设置控制点(推荐)
// 格式: [x, y] 表示坐标(权重默认为1),[[x, y], weight] 表示带权重的点
spline.setControlPoints([
[0, 0],
[50, 100],
[100, 50],
[[150, 100], 0.5], // 带权重
[200, 0]
]);
// 方式2: 链式添加单个控制点
spline.addControlPoint([0, 0])
.addControlPoint([50, 100])
.addControlPoint([100, 50], 0.5) // 第二参数是权重
.addControlPoint([150, 0]);
// 方式3: 批量追加控制点
spline.addControlPoints([[200, 50], [[250, 100], 0.8], [300, 50]]);
spline.setDefaults();
Engine.addEntities(spline);获取控制点
const spline: SplineEnt = /* ... */;
// 获取单个控制点坐标
const [x, y] = spline.getControlPoint(0);
// 获取单个控制点坐标和权重
const [[px, py], weight] = spline.getControlPointWithWeight(3);
// 获取所有控制点坐标(不含权重)
const coords = spline.getControlPoints(); // [[0,0], [50,100], [100,50], ...]
// 获取所有控制点(含权重,与 setControlPoints 格式一致)
const full = spline.getControlPointsWithWeight();
// [[0,0], [50,100], [100,50], [[150,100], 0.5], ...]
// 复制到另一个样条曲线
const spline2 = new SplineEnt();
spline2.setControlPoints(spline.getControlPointsWithWeight());简化接口 API 参考
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
addControlPoint(coord, weight?) | [x, y], number | this | 添加单个控制点,支持链式调用 |
addControlPoints(points) | 见下方格式 | this | 批量追加控制点 |
setControlPoints(points) | 见下方格式 | this | 设置所有控制点(替换现有) |
getControlPoint(index) | number | [x, y] | 获取指定索引的控制点坐标 |
getControlPoints() | - | [x, y][] | 获取所有控制点坐标 |
getControlPointWithWeight(index) | number | [[x, y], weight] | 获取控制点坐标和权重 |
getControlPointsWithWeight() | - | 见下方格式 | 获取所有控制点(含权重) |
点数组格式:
- 无权重(默认为1):
[x, y] - 有权重:
[[x, y], weight] - 混合:
[[0, 0], [50, 100], [[100, 50], 0.5], [150, 0]]
推荐使用简化接口
简化接口比传统的 SplineControlPoint + Point2D 方式更简洁,代码更易读。getControlPointsWithWeight() 的返回格式与 setControlPoints() 完全兼容,方便复制和序列化操作。
拟合点接口
拟合点模式下,曲线会精确经过所有指定的点。内部通过全局三次 B-spline 插值算法自动计算控制点和节点向量。
设置拟合点
import { SplineEnt, Engine } from 'vjcad';
const spline = new SplineEnt();
// 设置拟合点 — 曲线精确经过所有点
spline.setFitPoints([
[0, 0],
[30, 60],
[70, 20],
[110, 80],
[150, 30]
]);
spline.setDefaults();
Engine.addEntities(spline);参数化选项
// 使用向心参数化(适合急转弯较多的曲线)
spline.setFitPoints(points, { parameterization: 'sqrtChord' });
// 指定端点切线方向
spline.setFitPoints(points, {
startTangent: [1, 0], // 起点水平向右
endTangent: [1, 0] // 终点水平向右
});拟合点 API 参考
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
setFitPoints(points, options?) | [x, y][], FitPointsOptions | this | 设置拟合点(替换现有),自动计算控制点 |
addFitPoint(coord, options?) | [x, y] | this | 追加一个拟合点并重新计算 |
addFitPoints(points, options?) | [x, y][] | this | 批量追加拟合点并重新计算 |
getFitPoint(index) | number | [x, y] | 获取指定拟合点坐标 |
getFitPoints() | - | [x, y][] | 获取所有拟合点坐标 |
参数化选项 (FitPointsOptions):
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
parameterization | 'chord' | 'sqrtChord' | 'uniform' | 'chord' | 参数化方式 |
startTangent | [number, number] | - | 起点切线方向 |
endTangent | [number, number] | - | 终点切线方向 |
控制点 vs 拟合点
- 控制点 (
setControlPoints):控制点像"磁铁"拉动曲线,曲线不经过控制点。适合直接操控曲线形状。 - 拟合点 (
setFitPoints):曲线必须精确经过每个拟合点。适合已知路径点的场景。
属性
基本属性
| 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|
controlPoints | SplineControlPoints | 读写 | 控制点集合 |
fitPoints | SplineFitPoints | 读写 | 拟合点集合 |
degree | number | 读写 | 曲线阶数(通常为3) |
knots | SplineKnots | 读写 | 节点向量 |
计算属性(只读)
| 属性 | 类型 | 说明 |
|---|---|---|
length | number | 样条曲线总长度 |
isClosed | boolean | 是否闭合 |
startPoint | Point2D | 起始点 |
endPoint | Point2D | 结束点 |
numberOfControlPoints | number | 控制点数量 |
numberOfFitPoints | number | 拟合点数量 |
import { SplineEnt } from 'vjcad';
const spline = new SplineEnt();
spline.setControlPoints([
[0, 0], [50, 100], [100, 50], [150, 100], [200, 0]
]);
console.log('控制点数:', spline.numberOfControlPoints); // 5
console.log('阶数:', spline.degree); // 3
console.log('长度:', spline.length);
console.log('是否闭合:', spline.isClosed); // false方法
几何变换
move() - 移动
import { SplineEnt, Point2D } from 'vjcad';
const spline: SplineEnt = /* ... */;
// 简化写法(推荐)
spline.move([0, 0], [50, 50]);rotate() - 旋转
import { SplineEnt, Point2D } from 'vjcad';
const spline: SplineEnt = /* ... */;
// 简化写法(推荐)
spline.rotate([0, 0], Math.PI / 4); // 绕原点旋转 45°scale() - 缩放
import { SplineEnt, Point2D } from 'vjcad';
const spline: SplineEnt = /* ... */;
// 简化写法(推荐)
spline.scale([0, 0], 2); // 以原点为中心放大 2 倍mirror() - 镜像
import { SplineEnt, Point2D } from 'vjcad';
const spline: SplineEnt = /* ... */;
// 简化写法(推荐)
spline.mirror([0, 0], [100, 0]); // 沿 X 轴镜像序列化
import { SplineEnt } from 'vjcad';
const spline = new SplineEnt();
spline.setControlPoints([[0, 0], [50, 100], [100, 50], [150, 0]]);
spline.setDefaults();
// 导出
const dbData = spline.toDb();
console.log(dbData);
// 导入
const newSpline = new SplineEnt();
newSpline.fromDb(dbData);完整示例
创建简单样条曲线
import { Engine, SplineEnt } from 'vjcad';
// 使用简化接口创建样条曲线
const spline = new SplineEnt();
spline.setControlPoints([
[0, 0],
[30, 80],
[70, 20],
[100, 100]
]);
spline.setDefaults();
Engine.addEntities(spline);
Engine.pcanvas.zoomExtents();拟合点样条曲线
import { Engine, SplineEnt } from 'vjcad';
// 拟合点模式:曲线精确经过所有点
const fitSpline = new SplineEnt();
fitSpline.setFitPoints([
[0, 0], [30, 60], [70, 20], [110, 80], [150, 30], [200, 50]
]);
fitSpline.setDefaults();
fitSpline.color = 1;
// 对比:控制点模式,同样的点作为控制点
const ctrlSpline = new SplineEnt();
ctrlSpline.setControlPoints([
[0, 0], [30, 60], [70, 20], [110, 80], [150, 30], [200, 50]
]);
ctrlSpline.setDefaults();
ctrlSpline.color = 3;
Engine.addEntities(fitSpline, ctrlSpline);
// 红色曲线经过所有点,绿色曲线被点"拉向"但不经过创建带权重的样条曲线
import { Engine, SplineEnt } from 'vjcad';
// 权重可以影响曲线对控制点的"吸引力"
const spline = new SplineEnt();
spline.setControlPoints([
[0, 0],
[[50, 100], 2], // 权重为2,曲线更靠近此点
[100, 50],
[[150, 100], 0.5], // 权重为0.5,曲线远离此点
[200, 0]
]);
spline.setDefaults();
Engine.addEntities(spline);链式调用创建样条曲线
import { Engine, SplineEnt } from 'vjcad';
const spline = new SplineEnt();
// 链式添加控制点
spline.addControlPoint([0, 0])
.addControlPoint([25, 50])
.addControlPoint([50, 0])
.addControlPoint([75, 50])
.addControlPoint([100, 0]);
spline.setDefaults();
Engine.addEntities(spline);绘制波浪线
import { Engine, SplineEnt } from 'vjcad';
function drawWave(startX: number, startY: number, amplitude: number, wavelength: number, cycles: number) {
const points: [number, number][] = [];
const pointsPerCycle = 4; // 每个周期4个控制点
for (let i = 0; i <= cycles * pointsPerCycle; i++) {
const x = startX + (i / pointsPerCycle) * wavelength;
const phase = (i % pointsPerCycle) / pointsPerCycle;
const y = startY + amplitude * Math.sin(phase * 2 * Math.PI);
points.push([x, y]);
}
const spline = new SplineEnt();
spline.setControlPoints(points);
spline.setDefaults();
Engine.addEntities(spline);
return spline;
}
// 绘制3个周期的波浪线
drawWave(0, 100, 30, 50, 3);复制和修改样条曲线
import { Engine, SplineEnt, Point2D } from 'vjcad';
// 创建原始样条曲线
const original = new SplineEnt();
original.setControlPoints([[0, 0], [50, 100], [100, 0]]);
original.setDefaults();
// 使用简化接口复制
const copy = new SplineEnt();
copy.setControlPoints(original.getControlPointsWithWeight());
copy.setDefaults();
// 移动副本
copy.move([0, 0], [0, 50]);
Engine.addEntities(original, copy);下一步
- 多段线 (PolylineEnt) - 多段线实体详解
- 圆弧实体 (ArcEnt) - 圆弧实体详解
- 贝塞尔曲线 - 样条曲线几何计算