> **本文回答**：`Point2D` 是基础点 / 向量类，可选 z 坐标，支持链式 `.rotate / .scale / .offset / .move`；`PointInput` 联合类型允许 `Point2D | [x, y] | [x, y, z]` 三种输入；`ObservablePoint2D` 是带 callback 的响应式版本（实体内部用）；`BoundingBox` 是轴对齐包围盒，多框合并 `mergeBoundingBoxes([...])`，从点数组算 `calculateBoundingBoxFromPoints([...])`。

## 一、`Point2D` 基础

```js
import { Point2D, toPoint2D } from 'vjcad';

// 创建
const p1 = new Point2D(100, 200);
const p2 = new Point2D(100, 200, 50);   // 带 z
const o  = Point2D.Origin();             // (0, 0)

// 输入互转
const p3 = toPoint2D([100, 200]);
const p4 = toPoint2D([100, 200, 50]);

// 克隆 / 序列化
const c = p1.clone();
const arr = p1.toDb();                   // [100, 200] 或 [100, 200, 50]
const r = Point2D.fromDb([300, 400, 25]);
```

属性：`x / y / z`（z 可选）。

MCP 参考：`vjcad/doc/geometry/point2d.md`。

## 二、`Point2D` 向量运算 / 链式变换

```js
const p = new Point2D(100, 0)
  .rotate(Math.PI / 4)              // 绕原点旋转 45°
  .scale(2)                         // 以原点为中心缩放 ×2
  .offset(new Point2D(50, 50));     // 偏移

p.move(new Point2D(0, 0), new Point2D(50, 50));  // 计算位移并应用
p.offsetMinus(new Point2D(50, 30));              // 反向偏移
```

| 方法 | 参数 | 说明 |
|---|---|---|
| `clone()` | — | 克隆 |
| `offset(v) / offsetMinus(v)` | Point2D | 偏移 / 反向偏移 |
| `move(from, to)` | Point2D, Point2D | 移动 |
| `rotate(angle, center?)` | rad, Point2D? | 旋转（弧度） |
| `scale(factor, center?)` | num, Point2D? | 缩放 |
| `toDb() / fromDb(d)` | — | 序列化 |

> 所有变换方法都返回 `this`，支持链式调用——⚠️ 是**原地修改后返回 this**（`p.rotate(π/2)` 直接改 `p` 自身），与 vjmap `GeoPoint` 的 clone 风格相反；要保留原点须先 `.clone()`。
>
> ⚠️ `Point2D` 是极简类：**没有** `mag / unit / perp / angleTo / distanceTo / dot / lerp`——那些是 vjmap `GeoPoint` 的面，混用会 `undefined is not a function`。要算长度/角度用顶层 `distance` / `getAngleBetweenPoints` / `getMidPoint` / `polarToCartesian`。

MCP 参考：`vjcad/doc/geometry/point2d.md`。

## 三、`PointInput`（重要）

```ts
type PointInput = Point2D | [number, number] | [number, number, number];
```

vjcad **大量构造函数 / 几何方法** 同时接受这三种形式：

```js
new LineEnt([0, 0], [100, 100]);
new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
new BulgePoint([x, y], bulge);
circle.move([0, 0], [50, 30]);
```

MCP 参考：`vjcad/doc/geometry/point2d.md`、`vjcad/doc/guide/quick-start.md`。

## 四、`ObservablePoint2D`（响应式）

实体内部用，坐标变化触发回调（如 `setModified()`）。业务代码很少直接 new，但读 `line.startPoint.x = 50` 时背后就是它：

```js
const op = new ObservablePoint2D(0, 0, () => console.log('changed'));
op.set(100, 200, 50);          // 一次回调
op.copyFrom(other);            // 一次回调
op.x = 100; op.y = 200;        // 两次回调
```

MCP 参考：`vjcad/doc/geometry/point2d.md`。

## 五、`BoundingBox`（轴对齐包围盒）

```js
import { BoundingBox, Point2D } from 'vjcad';

const bb = new BoundingBox(new Point2D(0, 0), new Point2D(100, 80));
bb.minX; bb.maxX; bb.minY; bb.maxY;
bb.width; bb.height; bb.center;

bb.BL; bb.BR; bb.TL; bb.TR;            // 四个角点
bb.point1; bb.point2; bb.point3; bb.point4;
bb.minPoint; bb.maxPoint;
bb.hasSizeBounds; bb.isSinglePointBounds;

bb.move(new Point2D(0, 0), new Point2D(50, 30));   // 移动
bb.isPointInBox(new Point2D(50, 50));              // true
```

MCP 参考：`vjcad/doc/geometry/bounding-box.md`。

## 六、合并多个 BoundingBox / 从点集算

```js
import { mergeBoundingBoxes, calculateBoundingBoxFromPoints, BoundingBox, Point2D } from 'vjcad';

const merged = mergeBoundingBoxes([bb1, bb2, bb3]);

const points = [new Point2D(10, 20), new Point2D(50, 80), new Point2D(30, 10)];
const bb = calculateBoundingBoxFromPoints(points);
```

MCP 参考：`vjcad/doc/geometry/bounding-box.md`、`vjcad/example/geometry/15mergebounds.js`。

## 七、典型场景

```js
// 缩放视图到选中实体
function zoomToSelection() {
  const ents = Engine.ssGetFirst();
  if (!ents.length) return;
  // 单实体可直接 e.boundingBox()（运行时全实体可用；EntityBase d.ts 未声明）。
  // 多实体聚合边界用引擎级方法：
  const merged = Engine.getBoundsByEntities(ents);   // 返回 BoundingBox
  if (!merged.hasSizeBounds) return;
  Engine.zoomToEntities(ents);                        // 最直接：直接吃实体数组
  // 或手动：Engine.zoomExtents(undefined, { wcsBounds: [merged.minPoint.x, merged.minPoint.y, merged.maxPoint.x, merged.maxPoint.y] });
}

// 快速碰撞过滤（先用 bbox 粗判）
function fastIntersect(a, b) {
  const ba = Engine.getBoundsByEntities([a]), bb = Engine.getBoundsByEntities([b]);
  if (ba.maxPoint.x < bb.minPoint.x || ba.minPoint.x > bb.maxPoint.x) return false;
  if (ba.maxPoint.y < bb.minPoint.y || ba.minPoint.y > bb.maxPoint.y) return false;
  return true;
}
```

MCP 参考：`vjcad/doc/geometry/bounding-box.md`。

## 反例

- `Point2D` 当 mutable 直接修改：业务对象建议先 `.clone()` 再改，避免影响实体内部 `ObservablePoint2D`。
- 用 `Math.atan2` 自己算角度：vjcad 自带 `getAngleBetweenPoints(p1, p2)`。
- 想 "缩放到一个点"：`BoundingBox.isSinglePointBounds=true`，`zoom*` 系列遇到该 bbox 行为不可预期，提前 `expand` 一定 margin。

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/geometry/point2d.md`、`vjcad/doc/geometry/bounding-box.md`、`vjcad/doc/guide/quick-start.md`
- 示例：`vjcad/example/geometry/05boundingbox.js`、`15mergebounds.js`
