# 填充图案

HatchEnt 实体、内置图案、自定义图案、边界类型。

---

## 基本用法

```javascript
const { HatchEnt, PolylineEnt, Edge, Edges, EdgeType, Engine } = vjcad;

// 1. 创建闭合边界
const boundary = new PolylineEnt();
boundary.setPoints([[0, 0], [100, 0], [100, 60], [0, 60]]);
boundary.isClosed = true;
boundary.setDefaults();

// 2. 创建填充
const hatch = new HatchEnt();
hatch.patternName = "ANSI31";
hatch.patternScale = 1.0;
hatch.patternAngle = 0;

// 3. 设置边界
const edge = new Edge();
edge.edgeType = EdgeType.Polyline;
edge.bulgePoints = boundary.bulgePoints.clone();

const edges = new Edges();
edges.add(edge);
hatch.setLoops(edges);

hatch.setDefaults();
hatch.color = 4;
Engine.addEntities([boundary, hatch]);
Engine.zoomExtents();
```

---

## 填充属性

| 属性 | 说明 |
|------|------|
| `patternName` | 图案名称（如 `"SOLID"`, `"ANSI31"`） |
| `patternScale` | 图案比例（默认 1） |
| `patternAngle` | 图案角度（**弧度**；`Math.PI/4` = 45°） |
| `color` | 填充颜色 |

---

## 边界类型

### 多段线边界

```javascript
const edge = new Edge();
edge.edgeType = EdgeType.Polyline;
edge.bulgePoints = boundary.bulgePoints.clone();
```

### 圆形边界

```javascript
const edge = new Edge();
edge.edgeType = EdgeType.Circle;
edge.center = circle.center.clone();
edge.radius = circle.radius;
```

### 带孔填充（外边界 + 内边界）

```javascript
const allEdges = new Edges();

// 外边界
const outerEdge = new Edge();
outerEdge.edgeType = EdgeType.Polyline;
outerEdge.bulgePoints = outerRect.bulgePoints.clone();
allEdges.add(outerEdge);

// 内边界（孔）
const innerEdge = new Edge();
innerEdge.edgeType = EdgeType.Circle;
innerEdge.center = innerCircle.center.clone();
innerEdge.radius = innerCircle.radius;
allEdges.add(innerEdge);

hatch.setLoops(allEdges);
```

---

## 图案比例自动计算

```javascript
const { calculateHatchPatternScale } = vjcad;

const bbox = boundary.boundingBox();
const boundsSize = Math.max(bbox.width, bbox.height);
const patternDef = Engine.patternManager.getPattern(patternName);
const autoScale = calculateHatchPatternScale(boundsSize, patternName, patternDef);
hatch.patternScale = autoScale;
```

---

## 内置图案

### 实心填充

| 图案名 | 说明 |
|--------|------|
| `SOLID` | 实心填充 |

### ANSI 图案（美国标准）

| 图案名 | 说明 |
|--------|------|
| `ANSI31` | 铁、砖和石（45° 斜线） |
| `ANSI32` | 钢 |
| `ANSI33` | 青铜、黄铜和紫铜 |
| `ANSI34` | 塑料和橡胶 |
| `ANSI35` | 耐火砖和耐火材质 |
| `ANSI36` | 大理石、板岩和玻璃 |
| `ANSI37` | 铅、锌、镁和绝缘体（交叉斜线） |
| `ANSI38` | 铝 |

### AR 图案（建筑）

| 图案名 | 说明 |
|--------|------|
| `AR-B816` | 8x16 块砖顺砌 |
| `AR-B816C` | 8x16 块砖（带灰泥接缝） |
| `AR-B88` | 8x8 块砖顺砌 |
| `AR-BRELM` | 标准砖块英式堆砌 |
| `AR-BRSTD` | 标准砖块顺砌 |
| `AR-CONC` | 混凝土 |
| `AR-HBONE` | 人字形图案 |
| `AR-PARQ1` | 镶木地板 |
| `AR-RROOF` | 屋顶木瓦 |
| `AR-SAND` | 沙地 |

### 常用材质图案

| 图案名 | 说明 | 图案名 | 说明 |
|--------|------|--------|------|
| `BRICK` | 砖 | `GRASS` | 草地 |
| `CLAY` | 粘土 | `GRAVEL` | 沙砾 |
| `CORK` | 软木 | `HEX` | 六边形 |
| `CROSS` | 十字形 | `HONEY` | 蜂巢 |
| `DOTS` | 点 | `LINE` | 平行水平线 |
| `EARTH` | 地面 | `NET` | 水平/垂直栅格 |
| `STEEL` | 钢 | `SQUARE` | 小方块 |

### 获取所有图案名称

```javascript
const allNames = Engine.patternManager.getAllPatternNames();
```

---

## 自定义图案

### PAT 格式

```
*图案名称,图案描述
角度,X原点,Y原点,deltaX,deltaY[,线型定义...]
```

### 注册自定义图案

```javascript
const patString = `*MY_PATTERN,我的自定义图案
45,0,0,0,5
135,0,0,0,5`;

// 首选：直接吃 .pat 文本（PatternManager 内置解析器，返回注册条数）
const n = Engine.patternManager.loadFromPATDefinitions(patString, 'custom');

// 或手搓解析 + registerPattern（需要自定义解析逻辑时）：
function parsePatternString(patString) {
    const lines = patString.trim().split('\n');
    const headerMatch = lines[0].match(/^\*([^,]+),(.*)$/);
    const name = headerMatch[1].trim();
    const description = headerMatch[2].trim();
    const patternLines = [];
    for (let i = 1; i < lines.length; i++) {
        const line = lines[i].trim();
        if (!line || line.startsWith(';')) continue;
        const parts = line.split(',').map(p => parseFloat(p.trim()));
        patternLines.push({
            angle: parts[0], xOrigin: parts[1], yOrigin: parts[2],
            deltaX: parts[3], deltaY: parts[4], dashes: parts.slice(5)
        });
    }
    return { name, description, lines: patternLines };
}

const patternDef = parsePatternString(patString);
Engine.patternManager.registerPattern(patternDef, "custom");
```

---

## PatternManager API

```javascript
const patMgr = Engine.patternManager;

patMgr.getAllPatternNames();                    // 所有图案名称
patMgr.getAllPatternNames("builtin");           // 按分类获取
patMgr.getPattern("ANSI31");                   // 获取图案定义
patMgr.hasPattern("MY_PATTERN");               // 检查是否存在
patMgr.registerPattern(definition, "custom");  // 注册自定义图案
patMgr.getCategories();                        // 获取分类
patMgr.getStatistics();                        // 统计信息
```

---

## 命令方式

```javascript
await Engine.editor.executerWithOp('HATCH');       // 选择边界并填充
await Engine.editor.executerWithOp('EHATCH');   // 编辑现有填充（注册名 EHATCH，非 HATCHEDIT）
```
