> **本文回答**：`CircleEnt` / `ArcEnt` / `EllipseEnt` —— 圆、弧、椭圆的参数、角度约定、填充、与 Polyline 互转。

## 一、圆 (CircleEnt)

### 创建

```js
const circle = new vjmapext.CircleEnt([cx, cy], radius);
circle.color = 0x4488ff;
circle.fillColor = 0x4488ff;
circle.fillOpacity = 0.3;
mapcadLayer.addEntity(circle);
```

> ⚠️ **封闭图形默认带填充**：`CircleEnt`、整椭圆 `EllipseEnt`、闭合 `PolylineEnt`/`SplineEnt` 默认 `fillColor = -1`（跟随 `color`）、`fillOpacity = 0.2`——不设也会有一层 20% 半透明面。只要轮廓就显式 `fillOpacity = 0`。

### 属性

| 属性 | 类型 | 说明 |
|---|---|---|
| `center` | `Point2D` | 圆心 |
| `radius` | number | 半径（CAD 单位） |

### 修改

```js
circle.radius = 150;
circle.changed();
```

### 查询

```js
// 实体上没有 area()/perimeter()/length() 这类测量方法；自己按几何算：
const area = Math.PI * circle.radius ** 2;        // π r²
const girth = 2 * Math.PI * circle.radius;        // 2 π r
circle.boundingBox();    // 与半径对齐的外接矩形
// CircleEnt 另有静态构造：fromTwoPoints(直径两点) / fromThreePoints(外接圆,共线返 null) / fromCenterDiameter
```

## 二、弧 (ArcEnt)

### 创建

```js
const arc = new vjmapext.ArcEnt(
  [cx, cy],        // 圆心
  radius,          // 半径
  startAngle,      // 起始角，弧度
  endAngle,        // 终止角，弧度
  isCCW            // 可选，默认 true（逆时针）；false = 顺时针
);
```

**重要：ArcEnt 用弧度**（不是度）：

```js
const arc = new vjmapext.ArcEnt([0, 0], 100, 0, Math.PI);   // 上半圆
```

度 → 弧度：

```js
const deg2rad = d => d * Math.PI / 180;
const quarter = new vjmapext.ArcEnt([0, 0], 100, 0, deg2rad(90));
```

### 方向约定

- `isCCW=true`（默认）逆时针扫、false 顺时针扫；构造第 5 参控制
- 0 弧度 = X 正方向
- π/2 = Y 正方向
- `fromThreePoints` 会按三点转向自动判定 `isCCW`

### 端点和中点

```js
arc.startPoint;   // 只读 getter，按 startAngle 计算
arc.endPoint;
arc.midPoint;     // 弧中点（按 midAngle）
arc.midAngle;     // 中点角度
// 没有 arc.length()；弧长按实际扫掠角算（与渲染同规则，考虑 isCCW 与跨 0 回绕）：
let sweep = arc.endAngle - arc.startAngle;
if (arc.isCCW && sweep < 0) sweep += 2 * Math.PI;
if (!arc.isCCW && sweep > 0) sweep -= 2 * Math.PI;
const arcLen = arc.radius * Math.abs(sweep);
```

### 用三点构造弧

```js
const arc = vjmapext.ArcEnt.fromThreePoints(
  [0, 0], [50, 80], [100, 0]
);
mapcadLayer.addEntity(arc);
```

## 三、椭圆 / 椭圆弧 (EllipseEnt)

### 创建完整椭圆

```js
// new EllipseEnt(center?, majorRadius?, minorRadius?, rotation?)
// 注意：第三参是短半轴长度（不是比例）；起始/终止角是属性不是构造参数
const ell = new vjmapext.EllipseEnt(
  [cx, cy],        // 圆心
  majorRadius,     // 长半轴长度
  minorRadius,     // 短半轴长度（= 长半轴 × ratio）
  rotation         // 旋转（弧度）
);
```

### 创建椭圆弧

```js
const ellArc = new vjmapext.EllipseEnt([100, 100], 80, 40, 0);
ellArc.startAngle = 0;          // 弧度（属性）
ellArc.endAngle = Math.PI;
// ellArc.isFullEllipse → false
```

- `rotation`/`startAngle`/`endAngle` 单位都是**弧度**
- `startAngle`/`endAngle` 是**参数角** t（相对长轴、旋转前：`x = a·cos t, y = b·sin t` 再转 `rotation`），不是从圆心看过去的几何极角——非正圆时两者不等；要从几何角 θ 换算：`t = Math.atan2(a * Math.sin(θ), b * Math.cos(θ))`（θ 同样相对长轴）
- 椭圆弧没有 isCCW 标志：`endAngle < startAngle` 时按顺时针扫；`endAngle - startAngle ≈ 0` 视为整圆

### 典型场景

```js
// 横向椭圆，长半轴 100，短半轴 20
const oval = new vjmapext.EllipseEnt([0, 0], 100, 20, 0);
oval.fillColor = 0xffcc00;
oval.fillOpacity = 0.4;
mapcadLayer.addEntity(oval);
```

## 四、填充效果组合

```js
const c = new vjmapext.CircleEnt([0, 0], 100);

// 空心
c.fillOpacity = 0;         c.borderOpacity = 1;

// 实心
c.fillOpacity = 1;         c.borderOpacity = 0;   c.fillColor = 0xff4488;

// 半透明 + 粗边框
c.fillColor = 0x4488ff;    c.fillOpacity = 0.3;   c.lineWidth = 3;
```

## 五、与 PolylineEnt 互转

圆弧转多段线（近似）：

```js
// vjmap.getCirclePolygonCoordinates(center, radius, points?, startAngle?, endAngle?)
// 平面坐标计算（非球面），返回已闭合点串；startAngle/endAngle 单位是【度】
const poly = new vjmapext.PolylineEnt(
  vjmap.getCirclePolygonCoordinates([cx, cy], radius, 64)
, true);
```

多段线转圆弧（已知三点）：

```js
const arc = vjmapext.ArcEnt.fromThreePoints(p1, p2, p3);
```

## 六、交互编辑夹点

- `CircleEnt`：中心点 + 四向半径点（`quad0..3`）
- `ArcEnt`：圆心（平移）+ `start`/`end` 端点夹点（拖动同时改角度**和**半径）+ `mid` 中点夹点（只改半径，即曲率）
- `EllipseEnt`：中心（平移）+ `major` 长轴夹点（同时改 majorRadius 和 rotation）+ `minor` 短轴夹点（改 minorRadius）

可自定义夹点集合，见 [drawing/grip](../drawing/grip.md)。

## 七、性能建议

| 量级 | 推荐 |
|---|---|
| < 1000 个 | `CircleEnt` / `ArcEnt` / `EllipseEnt` |
| 1000 ~ 50000 | 转 GeoJSON 走 [primitive/circle-arc](../../../vjmap/references/primitive/circle-arc.md) |
| > 10000 点状圆 | [symbol/circle-symbol](../../../vjmap/references/symbol/circle-symbol.md) |

## 八、常见坑

1. **角度单位**：ArcEnt/EllipseEnt 的 `startAngle`/`endAngle`/`rotation` 和 `rotate(center, angle)` **全都用弧度**（SCALE 命令把用户输入的度 × π/180 才传给 rotate）；只有命令交互输入的是度。
2. **EllipseEnt 没有 `ratio` 字段**——构造直接收 `minorRadius` 长度（内部 `Math.abs` 取绝对值）；`minorRadius > majorRadius` 不报错，只是长短轴含义对调，语义上保持 minor ≤ major。
3. **Arc 的 start/end 写反**：想要半圆上半却画出下半 → 方向是逆时针。
4. **想要 Arc 填充**：Arc 本身不填充；用闭合的 Polyline 或 Ellipse（完整）。
5. **大量椭圆导致卡顿**：考虑用 Symbol 或贴图。
