点和向量
大约 7 分钟
点和向量
Point2D 是 WebCAD 中最基础的几何类型,表示空间中的点或向量。支持可选的 Z 坐标以处理 3D 数据。
PointInput 类型
PointInput 是一个联合类型,允许使用多种方式传入点坐标,简化代码编写:
type PointInput = Point2D | [number, number] | [number, number, number];支持的输入形式
| 形式 | 示例 | 说明 |
|---|---|---|
| Point2D 对象 | new Point2D(100, 200) | 传统写法 |
| 二维数组 | [100, 200] | 简化写法(推荐) |
| 三维数组 | [100, 200, 50] | 带 Z 坐标 |
使用示例
import { LineEnt, CircleEnt, Point2D } from 'vjcad';
// 以下写法等价:
const line1 = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
const line2 = new LineEnt([0, 0], [100, 100]); // 简化写法
// 实体构造函数支持 PointInput
const circle = new CircleEnt([50, 50], 25);
// 几何变换方法也支持 PointInput
circle.move([0, 0], [100, 100]);
circle.rotate([50, 50], Math.PI / 4);
circle.scale([50, 50], 2);
circle.mirror([0, 0], [100, 0]);推荐使用简化写法
使用数组形式 [x, y] 可以让代码更简洁,特别是在需要传入多个点时:
// 简洁
const pline = new PolylineEnt([[0, 0], [100, 0], [100, 100], [0, 100]]);
// 繁琐
const pline2 = new PolylineEnt([
new Point2D(0, 0), new Point2D(100, 0),
new Point2D(100, 100), new Point2D(0, 100)
]);toPoint2D 函数
将 PointInput 转换为 Point2D 对象:
import { toPoint2D, Point2D } from 'vjcad';
const p1 = toPoint2D([100, 200]); // Point2D(100, 200)
const p2 = toPoint2D([100, 200, 50]); // Point2D(100, 200, 50)
const p3 = toPoint2D(new Point2D(100, 200)); // 克隆原点Point2D 类
创建点
import { Point2D } from 'vjcad';
// 创建指定坐标的点(2D)
const p1 = new Point2D(100, 200);
// 创建带 Z 坐标的点(3D)
const p2 = new Point2D(100, 200, 50);
// 创建默认原点 (0, 0)
const p3 = new Point2D();
// 使用静态方法创建原点
const origin = Point2D.Origin();属性
| 属性 | 类型 | 说明 |
|---|---|---|
x | number | X 坐标 |
y | number | Y 坐标 |
z | number | undefined | Z 坐标(可选,用于 3D 数据) |
const p = new Point2D(100, 200, 50);
console.log(p.x); // 100
console.log(p.y); // 200
console.log(p.z); // 50
// 2D 点的 z 为 undefined
const p2d = new Point2D(100, 200);
console.log(p2d.z); // undefined克隆
创建当前点的副本,修改副本不会影响原点(包括 z 坐标):
const p1 = new Point2D(100, 200, 50);
const p2 = p1.clone();
p2.x = 300; // p1 仍然是 (100, 200, 50)
console.log(p2.z); // 50(z 坐标也被复制)偏移
将点按指定向量进行偏移:
const point = new Point2D(100, 100);
// 偏移 (50, 30)
point.offset(new Point2D(50, 30));
// point 现在是 (150, 130)
// 反向偏移
point.offsetMinus(new Point2D(50, 30));
// point 现在是 (100, 100)移动
将点从一个位置移动到另一个位置(计算位移向量后应用):
const point = new Point2D(100, 100);
// 从 (0, 0) 移动到 (50, 50) 的位移量应用到 point
point.move(new Point2D(0, 0), new Point2D(50, 50));
// point 现在是 (150, 150)旋转
绕指定中心点旋转指定角度(弧度):
const point = new Point2D(100, 0);
// 绕原点旋转 90 度(π/2 弧度)
point.rotate(Math.PI / 2);
// point 现在约为 (0, 100)
// 绕指定中心点旋转
point.rotate(Math.PI / 4, new Point2D(50, 50));缩放
以指定中心点为基准进行缩放:
const point = new Point2D(100, 100);
// 以原点为中心放大 2 倍
point.scale(2);
// point 现在是 (200, 200)
// 以指定中心点为基准缩放
point.scale(0.5, new Point2D(50, 50));序列化
支持与数据库格式的相互转换。当存在非零 z 值时,序列化为 [x, y, z] 格式;否则为 [x, y] 格式:
// 2D 点序列化
const p2d = new Point2D(100, 200);
const db2d = p2d.toDb(); // [100, 200]
// 3D 点序列化(z 非零)
const p3d = new Point2D(100, 200, 50);
const db3d = p3d.toDb(); // [100, 200, 50]
// z 为 0 时不包含在序列化结果中
const pZero = new Point2D(100, 200, 0);
const dbZero = pZero.toDb(); // [100, 200]
// 从数据库格式恢复
const restored2d = new Point2D().fromDb([100, 200]);
console.log(restored2d.z); // undefined
const restored3d = new Point2D().fromDb([100, 200, 50]);
console.log(restored3d.z); // 50
// 从数据库格式恢复(静态方法)
const p2 = Point2D.fromDb([300, 400, 25]);
// 支持旧版对象格式 {x, y}
const p3 = Point2D.fromDb({ x: 500, y: 600 });Point2D 方法汇总
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
clone() | - | Point2D | 克隆点(包括 z 坐标) |
offset(vector) | Point2D | Point2D | 偏移(返回自身) |
offsetMinus(vector) | Point2D | Point2D | 反向偏移(返回自身) |
move(from, to) | Point2D, Point2D | Point2D | 移动(返回自身) |
rotate(angle, center?) | number, Point2D? | Point2D | 旋转(返回自身) |
scale(factor, center?) | number, Point2D? | Point2D | 缩放(返回自身) |
toDb() | - | [x, y] | [x, y, z] | 转换为数组,z 非零时包含 z |
fromDb(data) | array | object | Point2D | 从数据恢复(返回自身) |
所有变换方法都返回 this,支持链式调用:
const point = new Point2D(100, 0)
.rotate(Math.PI / 4)
.scale(2)
.offset(new Point2D(50, 50));ObservablePoint2D 响应式点
ObservablePoint2D 是 Point2D 的响应式版本,当坐标发生变化时会自动触发回调函数。主要用于实体的关键点,实现坐标变化时自动标记实体需要重绘。同样支持可选的 z 坐标。
创建响应式点
import { ObservablePoint2D } from 'vjcad';
// 创建响应式点,回调函数在坐标变化时触发
const point = new ObservablePoint2D(0, 0, () => {
console.log('坐标已变化!');
});
// 修改坐标会触发回调
point.x = 100; // 输出: 坐标已变化!
point.y = 200; // 输出: 坐标已变化!
point.z = 50; // 输出: 坐标已变化!(修改 z 坐标也触发回调)性能优化:批量更新
单独修改 x、y、z 会触发多次回调,使用 set() 方法可以批量更新,只触发一次回调:
const point = new ObservablePoint2D(0, 0, () => {
console.log('更新');
});
// 低效:触发三次回调
point.x = 100; // 触发
point.y = 200; // 触发
point.z = 50; // 触发
// 高效:只触发一次回调
point.set(100, 200, 50); // 只触发一次(第三个参数为可选的 z 值)从其他点复制
使用 copyFrom() 方法从另一个点复制数据,也只触发一次回调:
const source = new Point2D(300, 400);
const observable = new ObservablePoint2D(0, 0, () => {
console.log('已更新');
});
// 高效复制(只触发一次)
observable.copyFrom(source);在实体中的应用
ObservablePoint2D 主要用于实体类的关键点属性,实现坐标变化时自动标记实体需要重绘:
class LineEnt extends EntityBase {
private _startPoint: ObservablePoint2D;
private _endPoint: ObservablePoint2D;
constructor(start: Point2D, end: Point2D) {
super();
// 创建响应式起点,坐标变化时自动标记实体已修改
this._startPoint = new ObservablePoint2D(
start.x,
start.y,
() => this.setModified()
);
// 创建响应式终点
this._endPoint = new ObservablePoint2D(
end.x,
end.y,
() => this.setModified()
);
}
// 获取起点(返回 ObservablePoint2D)
get startPoint(): ObservablePoint2D {
return this._startPoint;
}
// 设置起点
set startPoint(value: Point2D) {
this._startPoint.copyFrom(value); // 使用 copyFrom 避免替换引用
}
}
// 使用示例
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// 直接修改坐标会自动标记实体需要重绘
line.startPoint.x = 50; // 自动调用 setModified()ObservablePoint2D 方法汇总
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
set(x, y, z?) | number, number, number? | ObservablePoint2D | 批量设置坐标(只触发一次回调) |
copyFrom(point) | {x, y, z?} | ObservablePoint2D | 从另一个点复制(只触发一次回调) |
clone() | - | ObservablePoint2D | 克隆(不带回调,包含 z) |
offset(vector) | {x, y} | ObservablePoint2D | 偏移(优化,只触发一次) |
offsetMinus(vector) | {x, y} | ObservablePoint2D | 反向偏移 |
move(from, to) | {x, y}, {x, y} | ObservablePoint2D | 移动 |
rotate(angle, center?) | number, Point2D? | ObservablePoint2D | 旋转 |
scale(factor, center?) | number, Point2D? | ObservablePoint2D | 缩放 |
toDb() | - | [x, y] | [x, y, z] | 转换为数组,z 非零时包含 z |
fromDb(data) | array | object | ObservablePoint2D | 从数据恢复 |
向量运算
Point2D 也可以作为二维向量使用,进行常见的向量运算:
向量加减法
const v1 = new Point2D(3, 4);
const v2 = new Point2D(1, 2);
// 向量加法
const sum = new Point2D(v1.x + v2.x, v1.y + v2.y); // (4, 6)
// 向量减法
const diff = new Point2D(v1.x - v2.x, v1.y - v2.y); // (2, 2)向量长度(模)
const v = new Point2D(3, 4);
const length = Math.sqrt(v.x * v.x + v.y * v.y); // 5向量归一化
const v = new Point2D(3, 4);
const length = Math.sqrt(v.x * v.x + v.y * v.y);
const normalized = new Point2D(v.x / length, v.y / length); // (0.6, 0.8)点积(内积)
点积可用于计算向量夹角、投影等:
const v1 = new Point2D(1, 0);
const v2 = new Point2D(0, 1);
// 点积公式: a·b = ax*bx + ay*by
const dot = v1.x * v2.x + v1.y * v2.y; // 0(垂直)叉积
二维叉积返回标量,表示两向量构成的平行四边形面积,可用于判断旋转方向:
const v1 = new Point2D(1, 0);
const v2 = new Point2D(0, 1);
// 叉积公式: a×b = ax*by - ay*bx
const cross = v1.x * v2.y - v1.y * v2.x; // 1(v2 在 v1 逆时针方向)向量运算示例
// 计算两点间的方向向量(归一化)
function getDirection(from: Point2D, to: Point2D): Point2D {
const dx = to.x - from.x;
const dy = to.y - from.y;
const length = Math.sqrt(dx * dx + dy * dy);
if (length === 0) return new Point2D(0, 0);
return new Point2D(dx / length, dy / length);
}
// 计算点到直线的投影点
function projectPointToLine(point: Point2D, lineStart: Point2D, lineEnd: Point2D): Point2D {
const dx = lineEnd.x - lineStart.x;
const dy = lineEnd.y - lineStart.y;
const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (dx * dx + dy * dy);
return new Point2D(
lineStart.x + t * dx,
lineStart.y + t * dy
);
}