parapoly-runtime 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/cli/code3d-build.js +3 -3
- package/cli/code3d-install-instructions.js +5 -3
- package/instructions/code3d-project-workspace.instructions.md +410 -4
- package/instructions/part3d.instructions.md +220 -0
- package/instructions/sketch3d.instructions.md +248 -0
- package/package.json +1 -1
- package/parapoly_runtime.d.ts +75 -6
- package/parapoly_runtime.js +2 -2
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.part3d,**/*.part3d.js,**/Part3dCodeExporter*,**/part3d/**"
|
|
3
|
+
description: "Part3D 编辑器与 .part3d/.part3d.js 导出规范。涵盖 Part3dCodeExporter 架构、特征导出模式、sketch 可见度、颜色格式等。"
|
|
4
|
+
---
|
|
5
|
+
# Part3D & .part3d.js 导出指南
|
|
6
|
+
|
|
7
|
+
## 文件概览
|
|
8
|
+
|
|
9
|
+
| 文件类型 | 说明 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `.part3d` | Part3D 编辑器的项目文件(JSON 格式,Sprite 序列化树) |
|
|
12
|
+
| `.part3d.js` | 从 `.part3d` 导出的可执行 JS 代码,可在 code3d 运行时独立运行 |
|
|
13
|
+
|
|
14
|
+
## 源码位置
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
packages/parapoly-runtime/src/io/Part3dCodeExporter.ts ← 核心导出类(类库)
|
|
18
|
+
packages/parapoly-editor/src/io/Part3dCodeExporter.ts ← 向后兼容 re-export
|
|
19
|
+
packages/parapoly-editor/src/editors/part3d/ ← Part3D 编辑器
|
|
20
|
+
packages/parapoly-runtime/src/part/feature_node/ ← 特征节点定义
|
|
21
|
+
packages/parapoly-runtime/src/part/feature_node/op/ ← 操作型特征的 run() 逻辑
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Part3dCodeExporter API
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Part3dCodeExporter } from "parapoly-runtime";
|
|
28
|
+
|
|
29
|
+
// 导出 FeatureContainerNode 为 .part3d.js 代码
|
|
30
|
+
let code = Part3dCodeExporter.export(feature_container, default_color?);
|
|
31
|
+
// feature_container: FeatureContainerNode 实例
|
|
32
|
+
// default_color: 默认颜色(默认 "#ffffff"),当节点无颜色时使用
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 导出代码结构
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
let main = function() {
|
|
39
|
+
let block3d_doc = new PartBlock3dDocument();
|
|
40
|
+
|
|
41
|
+
// Helpers
|
|
42
|
+
let __id_to_name = {}; // feature_id → 运行时节点名映射
|
|
43
|
+
let __saved_shapes = {}; // Boolean 操作前保存的 shape 引用
|
|
44
|
+
let __record_name = function(feature_id) { ... };
|
|
45
|
+
let __select_feature = function(feature_id) { ... };
|
|
46
|
+
|
|
47
|
+
// Feature summary(注释段,人类可读预览)
|
|
48
|
+
// Feature count: N
|
|
49
|
+
// [0] FeatureNodeSetBox op=union ...
|
|
50
|
+
|
|
51
|
+
// Executable export(可执行段)
|
|
52
|
+
block3d_doc.box("union", 10, 5, 3, "#ff0000");
|
|
53
|
+
__record_name("abc12345-...");
|
|
54
|
+
// ...
|
|
55
|
+
|
|
56
|
+
return block3d_doc;
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 导出模式分类
|
|
61
|
+
|
|
62
|
+
### 模式 A:基础体(直接 PartBlock3dDocument API)
|
|
63
|
+
|
|
64
|
+
适用于:Box, Cylinder, Sphere, Cone, Torus, Prism, Ellipsoid, Wedge, Trapezoid, Step
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
block3d_doc.box(op, x, y, z, color);
|
|
68
|
+
block3d_doc.translate(px, py, pz); // TransformComponent
|
|
69
|
+
block3d_doc.rotate_by_quaternion(rx, ry, rz, rw);
|
|
70
|
+
__record_name(feature_id);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 模式 B:IIFE + ParaPolyShapeMaker(操作型特征)
|
|
74
|
+
|
|
75
|
+
适用于:Extrude, Revolve, Sweep, Helix, Fillet, Chamfer, Shell, Draft, Mirror, Boolean, Transform
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
(function() {
|
|
79
|
+
let target_name = __id_to_name[target_id];
|
|
80
|
+
if (!target_name) { console.warn("..."); return; }
|
|
81
|
+
let root = block3d_doc.get_root_node();
|
|
82
|
+
let node = root.getChildByName(target_name, true);
|
|
83
|
+
// ... 获取 shape / 调用 ParaPolyShapeMaker.xxx() ...
|
|
84
|
+
let result_node = block3d_doc.push_node(op, name, color, false);
|
|
85
|
+
result_node.set_topo_shape(result_shape);
|
|
86
|
+
block3d_doc.pop_node();
|
|
87
|
+
node.set_archived(true);
|
|
88
|
+
node.set_op_feature_node_id(feature_id);
|
|
89
|
+
__record_name(feature_id);
|
|
90
|
+
})();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 关键经验与注意事项
|
|
94
|
+
|
|
95
|
+
### 1. 颜色格式必须带 `#` 前缀
|
|
96
|
+
|
|
97
|
+
`get_color()` 返回的值可能不带 `#`(如 `"ffffff"`),导出时必须补全:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
let color = node.get_color ? node.get_color() : default_color;
|
|
101
|
+
if (color && !color.startsWith("#")) color = "#" + color;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2. Sketch 可见度保持
|
|
105
|
+
|
|
106
|
+
Part3D 中 Sketch 有两种独立的隐藏机制,导出时必须都保留:
|
|
107
|
+
|
|
108
|
+
| 属性 | 含义 | 设置方式 |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `get_archived()` | 被操作消费后系统隐藏 | `update_feature_node_archived()` 计算 |
|
|
111
|
+
| `get_op_feature_node_id()` | 关联的消费操作 ID | 操作 run() 时持久化设置 |
|
|
112
|
+
| `get_visible()` | 用户通过眼睛图标隐藏 | 用户手动切换 |
|
|
113
|
+
|
|
114
|
+
**导出逻辑**:
|
|
115
|
+
```typescript
|
|
116
|
+
// 创建 sketch 后立即检查并保持可见度
|
|
117
|
+
let is_archived = node.get_archived ? node.get_archived() : false;
|
|
118
|
+
let op_id = node.get_op_feature_node_id ? node.get_op_feature_node_id() : null;
|
|
119
|
+
let is_visible = node.get_visible ? node.get_visible() : true;
|
|
120
|
+
if (is_archived || op_id) {
|
|
121
|
+
lines.push(`block3d_doc.selected_node.set_archived(true);`);
|
|
122
|
+
}
|
|
123
|
+
if (!is_visible) {
|
|
124
|
+
lines.push(`block3d_doc.selected_node.set_visible(false);`);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**为什么要检查 `op_id`**:`get_archived()` 依赖编辑器的 `update_feature_node_archived()` 调用,可能不是最新状态。`get_op_feature_node_id()` 是持久化属性,更可靠。
|
|
129
|
+
|
|
130
|
+
**`end_sketch()` 后 `selected_node` 指向**:`PartBlock3dDocument.end_sketch()` 执行 `this.selected_node = node`(sketch 节点),所以 `block3d_doc.selected_node.set_archived(true)` 能正确定位。
|
|
131
|
+
|
|
132
|
+
**渲染过滤**:`SpriteToNativeNode` 和 `Text2CADFacade.build()` 遍历节点时均跳过 `topo_shape_component.get_archived() === true` 的节点。
|
|
133
|
+
|
|
134
|
+
### 3. Sketch 复用与重发射
|
|
135
|
+
|
|
136
|
+
同一 sketch 被多次引用时(如被两个 Extrude 使用),需要在第二次引用前重新发射:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
let consumed_sketches: Set<string> = new Set();
|
|
140
|
+
// 在 Extrude/Revolve 处理时:
|
|
141
|
+
if (target_id && consumed_sketches.has(target_id) && sketch_nodes.has(target_id)) {
|
|
142
|
+
lines.push(...Part3dCodeExporter.export_sketch_lines(sketch_nodes.get(target_id)));
|
|
143
|
+
lines.push(`__record_name(${JSON.stringify(target_id)});`);
|
|
144
|
+
}
|
|
145
|
+
consumed_sketches.add(target_id);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 4. 操作型特征的归档配对
|
|
149
|
+
|
|
150
|
+
Extrude/Revolve/Fillet 等操作在 IIFE 中必须:
|
|
151
|
+
1. 先取消归档:`node.set_archived(false); node.set_op_feature_node_id(null);`
|
|
152
|
+
2. 执行操作
|
|
153
|
+
3. 重新归档:`node.set_archived(true); node.set_op_feature_node_id(feature_id);`
|
|
154
|
+
|
|
155
|
+
如果 `keep_original === true`,跳过步骤 3。
|
|
156
|
+
|
|
157
|
+
### 5. `__saved_shapes` 用于 Boolean 后的引用
|
|
158
|
+
|
|
159
|
+
Boolean 操作会删除原始节点,但后续 Mirror/Fillet 等可能需要引用被删除节点的 shape。Boolean 在删除前保存:
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
__saved_shapes[target_name] = { shape: topo_shape.clone(), color: color };
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
后续操作查找目标时先查节点,再查 `__saved_shapes`:
|
|
166
|
+
```javascript
|
|
167
|
+
let topo_shape;
|
|
168
|
+
if (node && node.get_topo_shape) {
|
|
169
|
+
topo_shape = node.get_topo_shape();
|
|
170
|
+
} else if (__saved_shapes[target_name]) {
|
|
171
|
+
topo_shape = __saved_shapes[target_name].shape;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 6. 变换顺序差异
|
|
176
|
+
|
|
177
|
+
不同特征对 `transform_native_shape` 的调用顺序不同:
|
|
178
|
+
|
|
179
|
+
| 特征 | 顺序 |
|
|
180
|
+
|---|---|
|
|
181
|
+
| Extrude | 先 extrude_shape → 再 transform_native_shape |
|
|
182
|
+
| Helix | 先 helix_shape → 再 transform_native_shape |
|
|
183
|
+
| Revolve | 先 transform_native_shape(wires) → 再 revolve_shape |
|
|
184
|
+
| Sweep | 先 transform(profile+path) → 再 sweep_shape |
|
|
185
|
+
|
|
186
|
+
### 7. STEP 导入体的 base64 编码
|
|
187
|
+
|
|
188
|
+
`FeatureNodeSetStep` 的 content 可能是字符串或 Uint8Array,导出时统一转为 base64:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
private static step_payload_base64(content: any): string {
|
|
192
|
+
if (!content) return "";
|
|
193
|
+
if (content instanceof Uint8Array) {
|
|
194
|
+
return btoa(String.fromCharCode(...content));
|
|
195
|
+
}
|
|
196
|
+
if (typeof content === "string") {
|
|
197
|
+
return btoa(content);
|
|
198
|
+
}
|
|
199
|
+
return "";
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
运行时通过 `block3d_doc.import_step_shape_by_string(op, base64, true, color)` 还原。
|
|
204
|
+
|
|
205
|
+
### 8. 错误日志格式
|
|
206
|
+
|
|
207
|
+
所有 IIFE 中的 warn 日志统一格式:
|
|
208
|
+
```
|
|
209
|
+
[part3d.js #序号 操作类型 特征ID前8位] 消息
|
|
210
|
+
```
|
|
211
|
+
例如:`[part3d.js #3 Extrude abc12345] target_name not found for sketch def67890`
|
|
212
|
+
|
|
213
|
+
## 修改 Exporter 的流程
|
|
214
|
+
|
|
215
|
+
1. 定位源码:`packages/parapoly-runtime/src/part/feature_node/op/FeatureNode*.ts` 的 `run()` 方法
|
|
216
|
+
2. 找到 `ParaPolyShapeMaker.xxx()` 调用及参数来源
|
|
217
|
+
3. 注意变换顺序(transform 在操作前/后)
|
|
218
|
+
4. 更新 `Part3dCodeExporter.ts` 中对应 `class_name.indexOf(...)` 分支
|
|
219
|
+
5. 保持 IIFE 模式和归档配对
|
|
220
|
+
6. 运行 `get_errors` 验证无 TypeScript 错误
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.sketch3d,**/*.sketch3d.js,**/SketchCodeExporter*,**/sketch3d/**,**/sketch_node/**"
|
|
3
|
+
description: "Sketch3D 草图编辑器与 .sketch3d/.sketch3d.js 导出规范。涵盖 SketchCodeExporter 架构、几何导出、约束导出、可见度处理等。"
|
|
4
|
+
---
|
|
5
|
+
# Sketch3D & .sketch3d.js 导出指南
|
|
6
|
+
|
|
7
|
+
## 文件概览
|
|
8
|
+
|
|
9
|
+
| 文件类型 | 说明 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `.sketch3d` | Sketch3D 编辑器的项目文件(JSON 格式,SkContainerNode 序列化) |
|
|
12
|
+
| `.sketch3d.js` / `.code3d.js` | 从 sketch 导出的可执行 JS 代码 |
|
|
13
|
+
|
|
14
|
+
## 源码位置
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
packages/parapoly-runtime/src/io/SketchCodeExporter.ts ← 核心导出类(类库)
|
|
18
|
+
packages/parapoly-editor/src/io/SketchCodeExporter.ts ← 向后兼容 re-export
|
|
19
|
+
packages/parapoly-editor/src/editors/sketch3d/ ← Sketch3D 编辑器
|
|
20
|
+
packages/parapoly-runtime/src/part/sketch_node/ ← 草图节点定义
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## SketchCodeExporter API
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { SketchCodeExporter } from "parapoly-runtime";
|
|
27
|
+
|
|
28
|
+
// 导出 SkContainerNode 为 .code3d.js 代码
|
|
29
|
+
let code = SketchCodeExporter.export(sk_container_node);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 导出代码结构
|
|
33
|
+
|
|
34
|
+
**重要**:每个 `.sketch3d.js` 文件只包含一个有效的 sketch(一对 `start_sketch`/`end_sketch`)。所有几何体和约束都属于这同一个 sketch 上下文。AI 阅读时应将文件内容视为单一草图的完整定义。
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// This .sketch3d.js file contains exactly ONE sketch (start_sketch/end_sketch pair).
|
|
38
|
+
// All geometry and constraints belong to this single sketch context.
|
|
39
|
+
let main = function() {
|
|
40
|
+
let block3d_doc = new PartBlock3dDocument();
|
|
41
|
+
block3d_doc.start_sketch("sketch", [0, 0, 0, 0, 0, 1]);
|
|
42
|
+
|
|
43
|
+
// 几何绘制(返回 geom_id)
|
|
44
|
+
let g0 = block3d_doc.line_segment(0, 0, 0, 10, 0, 0);
|
|
45
|
+
let g1 = block3d_doc.line_segment(10, 0, 0, 10, 5, 0);
|
|
46
|
+
// ...
|
|
47
|
+
|
|
48
|
+
// 约束
|
|
49
|
+
block3d_doc.constraint_horizontal(g0);
|
|
50
|
+
block3d_doc.constraint_coincident(g0, PointPos.end, g1, PointPos.start);
|
|
51
|
+
// ...
|
|
52
|
+
|
|
53
|
+
block3d_doc.end_sketch();
|
|
54
|
+
return block3d_doc;
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### start_sketch 平面方向
|
|
59
|
+
|
|
60
|
+
`start_sketch` 的第二个参数是平面定义,导出时从 `SkContainerNode.get_ax2()` 的 `Ax2Component` 读取 center 和 direction,输出为 6 元素数组 `[cx, cy, cz, dx, dy, dz]`:
|
|
61
|
+
|
|
62
|
+
| 平面 | 数组值 |
|
|
63
|
+
|---|---|
|
|
64
|
+
| XY 平面(Z 方向) | `[0, 0, 0, 0, 0, 1]` |
|
|
65
|
+
| XZ 平面(Y 方向) | `[0, 0, 0, 0, 1, 0]` |
|
|
66
|
+
| YZ 平面(X 方向) | `[0, 0, 0, 1, 0, 0]` |
|
|
67
|
+
| 偏移平面 | `[0, 0, 10, 0, 0, 1]`(Z=10 处的 XY 平面) |
|
|
68
|
+
| 任意方向 | `[cx, cy, cz, dx, dy, dz]` |
|
|
69
|
+
|
|
70
|
+
也支持字符串简写形式(仅限手写代码):`"x"`, `"y"`, `"z"`。
|
|
71
|
+
|
|
72
|
+
## 几何类型映射
|
|
73
|
+
|
|
74
|
+
| 节点类 | 导出方法 | 返回值 |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `SkGeomPointNode` | `block3d_doc.point(x, y, z)` | geom_id |
|
|
77
|
+
| `SkGeomLineNode` | `block3d_doc.line_segment(x1, y1, z1, x2, y2, z2)` | geom_id |
|
|
78
|
+
| `SkGeomCircleNode` | `block3d_doc.circle(cx, cy, cz, r)` | geom_id |
|
|
79
|
+
| `SkGeomEllipseNode` | `block3d_doc.ellipse(cx, cy, cz, major_r, minor_r)` | geom_id |
|
|
80
|
+
| `SkGeomArcNode` | `block3d_doc.arc(cx, cy, cz, r, start_deg, end_deg)` | geom_id |
|
|
81
|
+
| `SkGeomBSplineNode` | `start_bspline(closed)` + `add_pole()` × N + `end_bspline()` | 无返回值 |
|
|
82
|
+
|
|
83
|
+
## 约束类型映射
|
|
84
|
+
|
|
85
|
+
| ConstraintType | 导出方法 | 条件 |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `Coincident` | `constraint_coincident(s, sp, f, fp)` | — |
|
|
88
|
+
| `Horizontal` | `constraint_horizontal(f)` | 单线段 |
|
|
89
|
+
| `Horizontal` | `constraint_horizontal_points(f, fp, s, sp)` | 两点(`second >= 0` 且 `firstPos != none`) |
|
|
90
|
+
| `Vertical` | `constraint_vertical(f)` | 单线段 |
|
|
91
|
+
| `Vertical` | `constraint_vertical_points(f, fp, s, sp)` | 两点 |
|
|
92
|
+
| `Distance` | `constraint_distance(f, value)` | 无 second |
|
|
93
|
+
| `Distance` | `constraint_distance_points(f, fp, s, sp, value)` | 两点 |
|
|
94
|
+
| `Distance` | `constraint_distance_point_to_line(f, fp, s, value)` | 点到线 |
|
|
95
|
+
| `DistanceX` | `constraint_distance_h(f, value)` / `_points(...)` | — |
|
|
96
|
+
| `DistanceY` | `constraint_distance_v(f, value)` / `_points(...)` | — |
|
|
97
|
+
| `Radius` | `constraint_radius(f, value)` | — |
|
|
98
|
+
| `Diameter` | `constraint_diameter(f, value)` | — |
|
|
99
|
+
| `Parallel` | `constraint_parallel(f, s)` | — |
|
|
100
|
+
| `Perpendicular` | `constraint_perpendicular(f, s)` | — |
|
|
101
|
+
| `Equal` | `constraint_equal(f, s)` | — |
|
|
102
|
+
| `Tangent` | `constraint_tangent(f, s)` | — |
|
|
103
|
+
| `Angle` | `constraint_angle(f, value)` | 单线段(与 X 轴夹角) |
|
|
104
|
+
| `Angle` | `constraint_angle_lines(f, s, value)` | 两线夹角 |
|
|
105
|
+
| `PointOnObject` | `constraint_point_on_object(f, fp, s)` | — |
|
|
106
|
+
| `Symmetric` | `constraint_symmetric(f, fp, line, s, sp)` | — |
|
|
107
|
+
|
|
108
|
+
## 关键经验与注意事项
|
|
109
|
+
|
|
110
|
+
### 1. 跳过内部几何体
|
|
111
|
+
|
|
112
|
+
Sketch 中存在内部辅助几何(如坐标轴),必须跳过:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
if (node.get_is_internal && node.get_is_internal()) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. 跳过 InternalAlignment 约束
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
if (type === ConstraintType.InternalAlignment) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3. Arc 角度转换
|
|
129
|
+
|
|
130
|
+
`SkGeomArcNode` 存储弧度制,导出时需转为角度制:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
let sa = node.component.get_start_angle(); // 弧度
|
|
134
|
+
let ea = node.component.get_end_angle(); // 弧度
|
|
135
|
+
// 导出时转为度
|
|
136
|
+
block3d_doc.arc(cx, cy, cz, r, sa * 180 / Math.PI, ea * 180 / Math.PI);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 4. BSpline 无 geom_id 返回
|
|
140
|
+
|
|
141
|
+
`start_bspline` / `end_bspline` 不返回 geom_id,因此 BSpline 不参与约束引用:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
if (className === ClassNames.SkGeomBSplineNode) {
|
|
145
|
+
// ... export poles ...
|
|
146
|
+
geom_id_to_var.delete(geom_id); // 不记录变量映射
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 5. geom_id 变量命名
|
|
151
|
+
|
|
152
|
+
每个几何体的 geom_id 映射为 `g{id}` 变量名,约束中引用这些变量:
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
let g0 = block3d_doc.line_segment(...);
|
|
156
|
+
let g1 = block3d_doc.line_segment(...);
|
|
157
|
+
block3d_doc.constraint_coincident(g0, PointPos.end, g1, PointPos.start);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 6. PointPos 枚举值
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
PointPos.none = 0 // 整条边/线(用于 constraint_horizontal 等)
|
|
164
|
+
PointPos.start = 1 // 起点
|
|
165
|
+
PointPos.end = 2 // 终点
|
|
166
|
+
PointPos.mid = 3 // 中点(圆心等)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 7. Coincident 约束参数顺序
|
|
170
|
+
|
|
171
|
+
注意 Coincident 的参数顺序是 `(second, secondPos, first, firstPos)`,与其他约束不同:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
case ConstraintType.Coincident:
|
|
175
|
+
return `block3d_doc.constraint_coincident(${s}, ${sp}, ${f}, ${fp});`;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 在 Part3dCodeExporter 中的 Sketch 导出
|
|
179
|
+
|
|
180
|
+
Part3dCodeExporter 中嵌入 sketch 时使用简化版(不含约束,不含 geom_id 变量):
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
block3d_doc.start_sketch("sketch_name", [cx, cy, cz, dx, dy, dz]);
|
|
184
|
+
block3d_doc.line_segment(x1, y1, z1, x2, y2, z2);
|
|
185
|
+
block3d_doc.circle(cx, cy, cz, r);
|
|
186
|
+
// ...
|
|
187
|
+
block3d_doc.end_sketch();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**差异**:
|
|
191
|
+
- `start_sketch` 参数为 `[center_x, center_y, center_z, dir_x, dir_y, dir_z]` 数组(Part3d 模式),而非 `"z"` 字符串(独立 sketch 模式)
|
|
192
|
+
- 不导出约束(拉伸/旋转只需要 wires 形状)
|
|
193
|
+
- 不保留 geom_id 变量
|
|
194
|
+
|
|
195
|
+
## Sketch 可见度(在 Part3D 上下文中)
|
|
196
|
+
|
|
197
|
+
当 Sketch 存在于 Part3D 特征树中时,有两种隐藏机制:
|
|
198
|
+
|
|
199
|
+
1. **`archived = true`**:被 Extrude/Revolve 等操作消费后,系统设置
|
|
200
|
+
2. **`visible = false`**:用户通过眼睛图标手动隐藏
|
|
201
|
+
|
|
202
|
+
Part3dCodeExporter 在导出 sketch 后需检查并保持可见度状态:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
if (is_archived || op_id) {
|
|
206
|
+
lines.push(`block3d_doc.selected_node.set_archived(true);`);
|
|
207
|
+
}
|
|
208
|
+
if (!is_visible) {
|
|
209
|
+
lines.push(`block3d_doc.selected_node.set_visible(false);`);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`get_op_feature_node_id()` 是最可靠的"已被消费"指标(持久化属性),优先于 `get_archived()`(计算属性)。
|
|
214
|
+
|
|
215
|
+
## SkContainerNode 关键 API
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// 坐标系
|
|
219
|
+
sk_node.get_ax2() → { center, direction }
|
|
220
|
+
sk_node.set_ax2(center: Vector3, direction: Vector3)
|
|
221
|
+
|
|
222
|
+
// 几何体容器
|
|
223
|
+
sk_node.get_geoms() → Node (children = SkGeom*Node[])
|
|
224
|
+
|
|
225
|
+
// 约束容器
|
|
226
|
+
sk_node.get_constraints() → Node (children = ConstraintNode[])
|
|
227
|
+
|
|
228
|
+
// 生成 wire shape(供 Extrude/Revolve 使用)
|
|
229
|
+
sk_node.to_wires_shape() → TopoDS_Shape
|
|
230
|
+
|
|
231
|
+
// 可见度
|
|
232
|
+
sk_node.get_archived() → boolean
|
|
233
|
+
sk_node.set_archived(v)
|
|
234
|
+
sk_node.get_visible() → boolean
|
|
235
|
+
sk_node.set_visible(v)
|
|
236
|
+
|
|
237
|
+
// 操作关联
|
|
238
|
+
sk_node.get_op_feature_node_id() → string | null
|
|
239
|
+
sk_node.set_op_feature_node_id(id)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## 修改 SketchCodeExporter 的流程
|
|
243
|
+
|
|
244
|
+
1. 确认几何类型:`packages/parapoly-runtime/src/part/sketch_node/SkGeom*.ts`
|
|
245
|
+
2. 确认约束类型:`packages/parapoly-runtime/src/types/Constraint.ts` (`ConstraintType` 枚举)
|
|
246
|
+
3. 确认 API:`packages/parapoly-runtime/src/part/PartBlock3dDocument.ts` 中的 sketch 方法
|
|
247
|
+
4. 更新 `SketchCodeExporter.ts` 中对应分支
|
|
248
|
+
5. 注意约束参数的 `second >= 0` 条件判断(区分单目/双目约束)
|
package/package.json
CHANGED
package/parapoly_runtime.d.ts
CHANGED
|
@@ -1777,6 +1777,12 @@ declare module "parapoly-runtime" {
|
|
|
1777
1777
|
* @param color Optional override color
|
|
1778
1778
|
*/
|
|
1779
1779
|
public static create_doc_from_part3d(json_str: string, recompile?: boolean, color?: string): PartBlock3dDocument;
|
|
1780
|
+
/**
|
|
1781
|
+
* Create a PartBlock3dDocument from a .block3d file's raw JSON string.
|
|
1782
|
+
* Extracts the stored JavaScript code from BlockCodeComponent and executes it.
|
|
1783
|
+
* @param json_str The raw JSON content of the .block3d file
|
|
1784
|
+
*/
|
|
1785
|
+
public static create_doc_from_block3d(json_str: string): PartBlock3dDocument;
|
|
1780
1786
|
public set_root_node(node: Node): void;
|
|
1781
1787
|
public get_root_node(): Node;
|
|
1782
1788
|
public to_json(): any;
|
|
@@ -1822,14 +1828,37 @@ declare module "parapoly-runtime" {
|
|
|
1822
1828
|
public start_sketch(name: string, plane_dir: any): SkContainerNode;
|
|
1823
1829
|
public end_sketch(): void;
|
|
1824
1830
|
public get_sketch_ax2_component(): Ax2Component;
|
|
1825
|
-
public point(x: number, y: number, z: number):
|
|
1826
|
-
public line_segment(start_x: number, start_y: number, start_z: number, end_x: number, end_y: number, end_z: number):
|
|
1827
|
-
public circle(x: number, y: number, z: number, r: number):
|
|
1828
|
-
public ellipse(x: number, y: number, z: number, major_r: number, minor_r: number):
|
|
1829
|
-
public arc(x: number, y: number, z: number, r: number, startAngle: number, endAngle: number, isDegree?: boolean):
|
|
1831
|
+
public point(x: number, y: number, z: number): number;
|
|
1832
|
+
public line_segment(start_x: number, start_y: number, start_z: number, end_x: number, end_y: number, end_z: number): number;
|
|
1833
|
+
public circle(x: number, y: number, z: number, r: number): number;
|
|
1834
|
+
public ellipse(x: number, y: number, z: number, major_r: number, minor_r: number): number;
|
|
1835
|
+
public arc(x: number, y: number, z: number, r: number, startAngle: number, endAngle: number, isDegree?: boolean): number;
|
|
1830
1836
|
public start_bspline(closed: boolean): void;
|
|
1831
1837
|
public end_bspline(): void;
|
|
1832
1838
|
public add_pole(x: number, y: number, z: number): void;
|
|
1839
|
+
private get_sk_container(): SkContainerNode | null;
|
|
1840
|
+
public constraint_coincident(from_geom_id: number, from_pos: PointPos, to_geom_id: number, to_pos: PointPos): void;
|
|
1841
|
+
public constraint_horizontal(geom_id: number): void;
|
|
1842
|
+
public constraint_vertical(geom_id: number): void;
|
|
1843
|
+
public constraint_horizontal_points(from_geom_id: number, from_pos: PointPos, to_geom_id: number, to_pos: PointPos): void;
|
|
1844
|
+
public constraint_vertical_points(from_geom_id: number, from_pos: PointPos, to_geom_id: number, to_pos: PointPos): void;
|
|
1845
|
+
public constraint_distance(geom_id: number, value: number): void;
|
|
1846
|
+
public constraint_distance_points(from_geom_id: number, from_pos: PointPos, to_geom_id: number, to_pos: PointPos, value: number): void;
|
|
1847
|
+
public constraint_distance_h(geom_id: number, value: number): void;
|
|
1848
|
+
public constraint_distance_h_points(from_geom_id: number, from_pos: PointPos, to_geom_id: number, to_pos: PointPos, value: number): void;
|
|
1849
|
+
public constraint_distance_v(geom_id: number, value: number): void;
|
|
1850
|
+
public constraint_distance_v_points(from_geom_id: number, from_pos: PointPos, to_geom_id: number, to_pos: PointPos, value: number): void;
|
|
1851
|
+
public constraint_distance_point_to_line(point_geom_id: number, point_pos: PointPos, line_geom_id: number, value: number): void;
|
|
1852
|
+
public constraint_radius(geom_id: number, value: number): void;
|
|
1853
|
+
public constraint_diameter(geom_id: number, value: number): void;
|
|
1854
|
+
public constraint_parallel(geom_id_1: number, geom_id_2: number): void;
|
|
1855
|
+
public constraint_perpendicular(geom_id_1: number, geom_id_2: number): void;
|
|
1856
|
+
public constraint_equal(geom_id_1: number, geom_id_2: number): void;
|
|
1857
|
+
public constraint_tangent(geom_id_1: number, geom_id_2: number): void;
|
|
1858
|
+
public constraint_angle(geom_id: number, value: number): void;
|
|
1859
|
+
public constraint_angle_lines(geom_id_1: number, geom_id_2: number, value: number): void;
|
|
1860
|
+
public constraint_point_on_object(point_geom_id: number, point_pos: PointPos, obj_geom_id: number): void;
|
|
1861
|
+
public constraint_symmetric(geom_id_1: number, pos_1: PointPos, line_geom_id: number, geom_id_2: number, pos_2: PointPos): void;
|
|
1833
1862
|
public extrude(op: BooleanTypes, length: number, color: string, bSolid: boolean, transform_matrix_arr: Array<number>): void;
|
|
1834
1863
|
public extrude_by_face(op: BooleanTypes, length: number, face_index: number, direction: DirectionTypes, color: string, bSolid: boolean, bAutoCheckDir: boolean): void;
|
|
1835
1864
|
public revolve(op: BooleanTypes, angle: number, axis: DirectionTypes | Array<number>, color: string, bSolid: boolean, transform_matrix_arr: Array<number>): void;
|
|
@@ -2878,6 +2907,7 @@ declare module "parapoly-runtime" {
|
|
|
2878
2907
|
FeatureSetEllipsoid = "FeatureSetEllipsoid",
|
|
2879
2908
|
FeatureSetWedge = "FeatureSetWedge",
|
|
2880
2909
|
FeatureSetTrapezoid = "FeatureSetTrapezoid",
|
|
2910
|
+
FeatureSetStep = "FeatureSetStep",
|
|
2881
2911
|
FeatureBoolean = "FeatureBoolean",
|
|
2882
2912
|
FeatureExtrude = "FeatureExtrude",
|
|
2883
2913
|
FeatureChamfer = "FeatureChamfer",
|
|
@@ -2954,6 +2984,8 @@ declare module "parapoly-runtime" {
|
|
|
2954
2984
|
viewSketchExtrudeScript = "viewSketchExtrudeScript",
|
|
2955
2985
|
copySketchRevolveScript = "copySketchRevolveScript",
|
|
2956
2986
|
viewSketchRevolveScript = "viewSketchRevolveScript",
|
|
2987
|
+
exportSketch3dJs = "exportSketch3dJs",
|
|
2988
|
+
exportPart3dJs = "exportPart3dJs",
|
|
2957
2989
|
}
|
|
2958
2990
|
|
|
2959
2991
|
export enum GeomTypes {
|
|
@@ -3258,7 +3290,7 @@ declare module "parapoly-runtime" {
|
|
|
3258
3290
|
* @param onProgress - Progress callback
|
|
3259
3291
|
* @returns Bundle output string and metadata
|
|
3260
3292
|
*/
|
|
3261
|
-
export function buildCode3dProject(files: Record<string, string>, config: Code3dProjectConfig, onProgress?: BuildProgress): Promise<{
|
|
3293
|
+
export function buildCode3dProject(files: Record<string, string>, config: Code3dProjectConfig, onProgress?: BuildProgress, fileLoader?: FileLoader): Promise<{
|
|
3262
3294
|
output: string;
|
|
3263
3295
|
binOutput: Uint8Array | null;
|
|
3264
3296
|
result: BuildResult;
|
|
@@ -3347,6 +3379,36 @@ declare module "parapoly-runtime" {
|
|
|
3347
3379
|
errors?: Array<string>;
|
|
3348
3380
|
}
|
|
3349
3381
|
|
|
3382
|
+
export class Part3dCodeExporter {
|
|
3383
|
+
public static export(feature_container: FeatureContainerNode, default_color?: string): string;
|
|
3384
|
+
private static export_feature_summary(feature_container: FeatureContainerNode): Array<string>;
|
|
3385
|
+
private static export_executable_calls(feature_container: FeatureContainerNode, default_color?: string): Array<string>;
|
|
3386
|
+
private static export_sketch_lines(node: any): Array<string>;
|
|
3387
|
+
private static append_transform_lines(lines: Array<string>, node: any): void;
|
|
3388
|
+
private static extract_edges_and_target(items: Array<any>): {
|
|
3389
|
+
target_id: string;
|
|
3390
|
+
edges: Array<number>;
|
|
3391
|
+
};
|
|
3392
|
+
private static extract_faces_and_target(items: Array<any>): {
|
|
3393
|
+
target_id: string;
|
|
3394
|
+
faces: Array<number>;
|
|
3395
|
+
};
|
|
3396
|
+
private static step_payload_base64(content: Uint8Array): string;
|
|
3397
|
+
private static n(v: any): string;
|
|
3398
|
+
private static summarize_feature_node(index: number, node: any): string;
|
|
3399
|
+
private static summarize_feature_component(component: any): string;
|
|
3400
|
+
private static push_if_defined(target: Array<string>, key: string, value: any): void;
|
|
3401
|
+
private static to_simple_value(value: any): string;
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
export class SketchCodeExporter {
|
|
3405
|
+
public static export(sk_container_node: SkContainerNode): string;
|
|
3406
|
+
private static n(v: number): string;
|
|
3407
|
+
private static pp(pos: PointPos): string;
|
|
3408
|
+
private static gv(geom_id: number, map: Map<number, string>): string;
|
|
3409
|
+
private static exportConstraint(type: ConstraintType, first: number, firstPos: PointPos, second: number, secondPos: PointPos, value: number, map: Map<number, string>): string | null;
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3350
3412
|
export class EventDispatcher {
|
|
3351
3413
|
private _listeners: Record<string, Array<Func>>;
|
|
3352
3414
|
private _unique_maps: {};
|
|
@@ -4229,6 +4291,13 @@ declare module "parapoly-runtime" {
|
|
|
4229
4291
|
getId(): string;
|
|
4230
4292
|
}
|
|
4231
4293
|
|
|
4294
|
+
/**
|
|
4295
|
+
* Optional file loader callback for on-demand loading.
|
|
4296
|
+
* Called when a file is needed but not yet in the files map.
|
|
4297
|
+
* Should return the file content string, or null if not found.
|
|
4298
|
+
*/
|
|
4299
|
+
export type FileLoader = (filePath: string) => Promise<string | null>;
|
|
4300
|
+
|
|
4232
4301
|
export interface Vector3Like {
|
|
4233
4302
|
readonly x: number;
|
|
4234
4303
|
readonly y: number;
|