面积计算
大约 5 分钟
面积计算
本章介绍 WebCAD 中的面积计算功能,包括多边形面积的计算算法和相关辅助类。
概述
WebCAD 提供了基于鞋带公式(Shoelace formula)的多边形面积计算功能。
import {
calculatePolygonArea,
AreaCalculationElement,
AreaCalculationCollection,
sortEdgeByArea
} from 'vjcad';calculatePolygonArea() - 多边形面积计算
使用鞋带公式计算任意简单多边形的面积。
function calculatePolygonArea(polygonVertices: Point[]): number参数:
| 参数 | 类型 | 说明 |
|---|---|---|
polygonVertices | Point[] | 多边形顶点数组(按顺序排列) |
返回值:
返回多边形的双倍面积(带符号),实际面积需要除以 2 并取绝对值。
基本示例
import { calculatePolygonArea } from 'vjcad';
// 计算矩形面积(顶点按逆时针或顺时针顺序)
const rectangle = [
{ x: 0, y: 0 },
{ x: 100, y: 0 },
{ x: 100, y: 50 },
{ x: 0, y: 50 }
];
const doubleArea = calculatePolygonArea(rectangle);
const area = Math.abs(doubleArea) / 2; // 5000三角形面积
const triangle = [
{ x: 0, y: 0 },
{ x: 100, y: 0 },
{ x: 50, y: 80 }
];
const area = Math.abs(calculatePolygonArea(triangle)) / 2; // 4000复杂多边形
// L 形多边形
const lShape = [
{ x: 0, y: 0 },
{ x: 60, y: 0 },
{ x: 60, y: 40 },
{ x: 20, y: 40 },
{ x: 20, y: 100 },
{ x: 0, y: 100 }
];
const area = Math.abs(calculatePolygonArea(lShape)) / 2;鞋带公式原理
鞋带公式(Shoelace formula),也称为高斯面积公式,是一种计算简单多边形面积的方法。
数学公式
对于 n 个顶点的多边形 ((x_1, y_1), (x_2, y_2), ..., (x_n, y_n)):
[ A = \frac{1}{2} \left| \sum_{i=1}^{n} x_i (y_{i+1} - y_{i-1}) \right| ]
其中索引采用循环方式:(y_{n+1} = y_1),(y_0 = y_n)。
公式推导
将公式展开,每个顶点的贡献为:
x_i × (y_{i+1} - y_{i-1})即当前点的 x 坐标乘以(下一个点的 y 坐标减去前一个点的 y 坐标)。
为什么叫"鞋带公式"
如果把坐标对角线相乘的过程画出来,形状类似鞋带的交叉系法,因此得名。
AreaCalculationElement - 面积计算元素
用于存储单个顶点的面积计算数据。
class AreaCalculationElement {
X: number; // 当前点 X 坐标
Y: number; // 当前点 Y 坐标
YnA: number; // 下一个点 Y 坐标
YnB: number; // 前一个点 Y 坐标
get area(): number; // 该点对面积的贡献
}属性说明
| 属性 | 说明 |
|---|---|
X | 当前顶点的 X 坐标 |
Y | 当前顶点的 Y 坐标 |
YnA | 下一个顶点(next)的 Y 坐标 |
YnB | 前一个顶点(before)的 Y 坐标 |
area 计算
get area(): number {
return this.X * (this.YnA - this.YnB);
}示例
import { AreaCalculationElement } from 'vjcad';
// 创建一个面积计算元素
const element = new AreaCalculationElement();
element.X = 100; // 当前点 x
element.Y = 50; // 当前点 y(实际上在 area 计算中不使用)
element.YnA = 100; // 下一个点的 y
element.YnB = 0; // 前一个点的 y
const contribution = element.area; // 100 * (100 - 0) = 10000AreaCalculationCollection - 面积计算集合
管理多个面积计算元素并计算总面积。
class AreaCalculationCollection {
items: AreaCalculationElement[];
get doubleArea(): number; // 双倍面积(带符号)
get area(): number; // 实际面积(双倍面积的一半)
}示例
import { AreaCalculationCollection, AreaCalculationElement } from 'vjcad';
// 计算矩形面积:(0,0), (100,0), (100,50), (0,50)
const collection = new AreaCalculationCollection();
// 顶点 1: (0, 0)
const e1 = new AreaCalculationElement();
e1.X = 0;
e1.YnA = 0; // 下一个点 (100, 0) 的 y
e1.YnB = 50; // 前一个点 (0, 50) 的 y
collection.items.push(e1);
// 顶点 2: (100, 0)
const e2 = new AreaCalculationElement();
e2.X = 100;
e2.YnA = 50; // 下一个点 (100, 50) 的 y
e2.YnB = 0; // 前一个点 (0, 0) 的 y
collection.items.push(e2);
// 顶点 3: (100, 50)
const e3 = new AreaCalculationElement();
e3.X = 100;
e3.YnA = 50; // 下一个点 (0, 50) 的 y
e3.YnB = 0; // 前一个点 (100, 0) 的 y
collection.items.push(e3);
// 顶点 4: (0, 50)
const e4 = new AreaCalculationElement();
e4.X = 0;
e4.YnA = 0; // 下一个点 (0, 0) 的 y
e4.YnB = 50; // 前一个点 (100, 50) 的 y
collection.items.push(e4);
console.log(collection.doubleArea); // 10000 或 -10000(取决于顶点顺序)
console.log(collection.area); // 5000sortEdgeByArea() - 按面积排序
按面积从大到小对边缘数组进行排序。
function sortEdgeByArea<T extends { area: number }>(edgeArray: T[]): T[]示例
import { sortEdgeByArea } from 'vjcad';
const edges = [
{ name: 'small', area: 100 },
{ name: 'large', area: 500 },
{ name: 'medium', area: 250 }
];
const sorted = sortEdgeByArea(edges);
// sorted = [{ name: 'large', area: 500 }, { name: 'medium', area: 250 }, { name: 'small', area: 100 }]使用场景
在填充检测中,通常需要将边界按面积排序,最大的边界作为外边界,较小的作为孤岛(内部孔洞):
import { sortEdgeByArea, Edge } from 'vjcad';
// 检测到的多个边界
const boundaries: Edge[] = detectBoundaries(entities);
// 按面积排序
const sorted = sortEdgeByArea(boundaries);
// 最大的是外边界
const outerBoundary = sorted[0];
// 其余的是孤岛
const islands = sorted.slice(1);实际应用
计算选中实体的面积
import { calculatePolygonArea, Engine, PolylineEnt } from 'vjcad';
function calculateSelectedArea(): number {
const selected = Engine.getSelectedEntities();
let totalArea = 0;
for (const entity of selected) {
if (entity instanceof PolylineEnt && entity.isClosed) {
// 获取多段线顶点
const vertices = entity.bulgePoints.items.map(bp => ({
x: bp.point2d.x,
y: bp.point2d.y
}));
// 计算面积(注意:这是简化计算,不考虑圆弧)
const area = Math.abs(calculatePolygonArea(vertices)) / 2;
totalArea += area;
}
}
return totalArea;
}判断多边形方向
鞋带公式的符号可以用来判断多边形的绑线方向:
function isCounterClockwise(vertices: { x: number; y: number }[]): boolean {
const doubleArea = calculatePolygonArea(vertices);
return doubleArea > 0; // 正值表示逆时针
}计算带孔洞的多边形面积
function calculateAreaWithHoles(
outerBoundary: { x: number; y: number }[],
holes: { x: number; y: number }[][]
): number {
// 计算外边界面积
const outerArea = Math.abs(calculatePolygonArea(outerBoundary)) / 2;
// 减去所有孔洞面积
let holeArea = 0;
for (const hole of holes) {
holeArea += Math.abs(calculatePolygonArea(hole)) / 2;
}
return outerArea - holeArea;
}注意事项
- 顶点顺序:顶点必须按顺序(顺时针或逆时针)排列
- 简单多边形:只适用于简单多边形(边不相交)
- 双倍面积:
calculatePolygonArea返回双倍面积,需要除以 2 - 带符号:结果可能为负,取绝对值获得实际面积
- 圆弧处理:对于包含圆弧的多段线,需要将圆弧离散化为点序列