# 几何计算

距离、角度、交点、面积、空间索引、坐标系转换。

---

## 距离计算

```javascript
const { distance, Point2D } = vjcad;

const p1 = new Point2D(0, 0);
const p2 = new Point2D(100, 100);
const dist = distance(p1, p2);

// 直线长度
const line = new LineEnt([0, 0], [100, 100]);
console.log('长度:', line.Length);
```

---

## 角度计算

```javascript
const { getAngleBetweenPoints, radiansToDegrees, degreesToRadians, Point2D } = vjcad;

const origin = new Point2D(0, 0);
const target = new Point2D(100, 100);

const angleRad = getAngleBetweenPoints(origin, target);  // 弧度，范围 0 到 2π
const angleDeg = radiansToDegrees(angleRad);             // 转换为角度
const rad = degreesToRadians(45);                        // 45° → π/4
```

---

## 中点计算

```javascript
const { getMidPoint, Point2D } = vjcad;

const mid = getMidPoint(new Point2D(0, 0), new Point2D(100, 100));
// mid: { x: 50, y: 50 }
```

---

## 交点计算

```javascript
const { GeometryCalculator, LineEnt, CircleEnt, ArcEnt, PolylineEnt } = vjcad;

// 直线与直线
const ptsLL = GeometryCalculator.LineToLine(line1, line2);

// 直线与圆（0、1 或 2 个交点）
const ptsLC = GeometryCalculator.LineToCircle(line, circle);

// 圆与圆
const ptsCC = GeometryCalculator.CircleToCircle(circle1, circle2);

// 圆弧与圆弧
const ptsAA = GeometryCalculator.ArcToArc(arc1, arc2);

// 多段线与多段线
const ptsPP = GeometryCalculator.PlineToPline(pline1, pline2);

// 延长线交点
const ptsLLExt = GeometryCalculator.LineExtToLineExt(line1, line2);
const ptsLCExt = GeometryCalculator.LineExtToCircle(line, circle);
```

---

## 面积计算

```javascript
const { calculatePolygonArea, Point2D } = vjcad;

// 多边形面积（鞋带公式）
const points = [
    new Point2D(0, 0),
    new Point2D(100, 0),
    new Point2D(100, 60),
    new Point2D(0, 60)
];
const doubleArea = calculatePolygonArea(points);
const area = Math.abs(doubleArea) / 2;  // 6000

// 圆面积
console.log(circle.area);  // π × r²

// 闭合多段线面积：仅对无凸度、闭合、不自交的多段线有效，否则返回 -1（如圆角矩形）
pline.isClosed = true;
console.log(pline.area);
```

---

## 边界框

```javascript
// 单个实体
const bbox = entity.boundingBox();
// { pt1(最小点), pt2(最大点), width, height, center }

// 多实体组合范围
const { Engine, mergeBoundingBoxes } = vjcad;
const combined = Engine.getBoundsByEntities([e1, e2], "WCS");
const merged = mergeBoundingBoxes([bbox1, bbox2]);
```

---

## 点在多边形内判断

```javascript
const { pointInPolygon, Point2D } = vjcad;

const polygon = [
    new Point2D(0, 0), new Point2D(100, 0),
    new Point2D(100, 100), new Point2D(0, 100)
];
const inside = pointInPolygon(new Point2D(50, 50), polygon);
```

---

## 点与直线的位置关系

```javascript
const { GeometryCalculator, Point2D } = vjcad;

// 返回：1=行进方向左侧, 0=线上, -1=右侧（源码注释写反，以实现为准；本例返回 1）
const side = GeometryCalculator.witchSidePointToLine(
    new Point2D(0, 0), new Point2D(100, 0), new Point2D(50, 50)
);
```

---

## 几何变换

### 偏移

```javascript
const { offsetLine, expandArc } = vjcad;

// 正值偏到行进方向右侧、负值偏到左侧（实现用 angle - 90°；源码注释"正值向左/向上"写反）
const offsetRight = offsetLine(line, 20);   // 从左向右画的线 → 向下偏移
const offsetLeft = offsetLine(line, -20);   // 从左向右画的线 → 向上偏移
const arcOut = expandArc(arc, 10);          // 圆弧向外偏移
const arcIn = expandArc(arc, -10);          // 圆弧向内偏移
```

### 点旋转

```javascript
const { rotatePointAroundCenter, Point2D } = vjcad;

// 签名是 rotatePointAroundCenter(centerPoint, pointToRotate, angle)——中心在前
const rotated = rotatePointAroundCenter(
    new Point2D(0, 0),     // 旋转中心（第 1 参）
    new Point2D(100, 0),   // 要旋转的点（第 2 参）
    Math.PI / 4            // 旋转角度（弧度）
);
// ⚠️ 必须传 Point2D 实例：传裸数组 [x,y] 不报错但返回 {x:null,y:null}，
// 错误静默——结果取 .x/.y 前先判 null。
```

---

## 空间索引

用于大量实体的高效空间查询。

```javascript
const { SpatialIndex } = vjcad;

const spatialIndex = new SpatialIndex();

// 批量加载（性能最优）
const items = [
    { minX: 0, minY: 0, maxX: 50, maxY: 50, id: 1 },
    { minX: 30, minY: 30, maxX: 80, maxY: 80, id: 2 },
];
spatialIndex.load(items);

// 区域查询
const found = spatialIndex.search({ minX: 25, minY: 25, maxX: 75, maxY: 75 });

// 碰撞检测
const hasCollision = spatialIndex.collides({ minX: 0, minY: 0, maxX: 10, maxY: 10 });

// 插入/删除/清空
spatialIndex.insert(item);
spatialIndex.remove(item, (a, b) => a.id === b.id);
spatialIndex.clear();
```

> **两个索引类，别混**：
>
> - `SpatialIndex`：rbush 通用空间索引（d.ts 里声明为 `any`）。项形态是 `{minX,minY,maxX,maxY, ...任意字段}` 纯对象；`load/search/collides/insert/remove(item, eqFn)/clear`。
> - `SpatialIndexManager`（`Engine.spatialIndexManager`）：**按文档维护实体索引**（rbush + 实体映射），吃 `BoundingBox` 实例（`new BoundingBox(pt1: Point2D, pt2: Point2D)`，自动算 minX/maxX…）：`load(entities?, doc?)` / `search(bounds, entityArray?, doc?)` / `collides(bounds, doc?)` / `insert(entity)` / `remove(entity)` / `setEnabled(bool)`。要自己存任意对象用前者；要对图内实体做空间查询用后者。

---

## 坐标系转换

```javascript
const { Engine } = vjcad;

Engine.canvasToWcs(canvasPoint);   // Canvas → 世界坐标
Engine.wcsToUcs(wcsPoint);        // 世界 → 用户坐标
Engine.ucsToWcs(ucsPoint);        // 用户 → 世界坐标
Engine.wcsToDcs(wcsPoint);        // 世界 → 显示坐标
```

| 坐标系 | 说明 |
|--------|------|
| WCS | 世界坐标系（绝对坐标） |
| UCS | 用户坐标系（可自定义原点和旋转） |
| DCS | 显示坐标系（屏幕像素坐标） |
| Canvas | 画布坐标（HTML Canvas 像素） |

---

## 常用数学工具

```javascript
// 注意：这些是 vjcad 顶层导出函数（src/utils/ 展开导出），
// 不是 MathUtils./AngleUtils. 命名空间成员
vjcad.degreesToRadians(45);          // 角度 → 弧度
vjcad.radiansToDegrees(Math.PI);     // 弧度 → 角度
vjcad.randInt(1, 100);               // 随机整数
vjcad.roundToDecimalPlace(3.14159, 2);  // 3.14

vjcad.normalizeAngle(angle);         // 归一化弧度到 [0, 2π)
vjcad.polarToCartesian(centerPt, angle, distance);  // 极坐标 → Point2D（参数顺序：中心、角度、距离）
```
