# 特征、装配与工程图助手

本页覆盖第三轮新增的接口：按几何定位面/边/顶点、圆角、倒角、旋转、线性/圆周阵列、装配体组件与配合、工程图视图与模型尺寸。所有长度为米、角度为弧度；所有助手执行后都会清空选择，失败返回 `null,说明`，成功返回 `特征,错误码,isWarning`（只有错误码 0 才算干净）。

验证版本：SOLIDWORKS 2025（33.4.0）。每个接口都在 `sldworks-library/tests/round3-live.aardio` 中用体积、面数、配合状态等独立证据回读过。

## 1. 用几何定位实体，而不是本地化名称或屏幕坐标

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }
var doc,findError = sw.FindDoc();
if(!doc){ print(findError); return; }

var edges,truncated = sw.GetEdges(doc);        // 每条边：isLine/isCircle、length、start/end/mid、圆边含 center/axis/radius
var faces = sw.GetFaces(doc);                  // 每个面：isPlane、normal、area、point（面上实际点）
var vertical = [];
for i,e in edges { if(e.isLine && math.abs(e.length-0.03)<1e-9) table.push(vertical,e); }
print(#edges,#vertical);

// 面/边/顶点按最近距离定位（默认容差 1e-5 米），不受视角遮挡影响，也不依赖 "边线<1>" 这类本地化名称
var edge,distance = sw.FindEntity(doc,"EDGE",vertical[1].mid);
var missing,why,nearest = sw.FindEntity(doc,"FACE",{x=1;y=1;z=1});
print(distance,why,nearest);
```

`GetEdges` 返回的 `mid` 是边上的真实中点（闭合边取起点），可以直接作为 `Fillet`、`Chamfer`、`LinearPattern.direction1`、`AddMate` 的 `{x,y,z}` 参考。`SelectByPoint` 对 FACE/EDGE/VERTEX 也改为先几何定位再 `Select4`，其他类型仍退回 `SelectByID2` 坐标拾取（只能拾取视图中可见的实体）。

## 2. 圆角与倒角

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }
var doc,findError = sw.FindDoc();
if(!doc){ print(findError); return; }

var edges = sw.GetEdges(doc);
var before = sw.GetMassProperties(doc).volume;

// 半径 5 mm，一次给多条边；propagate 默认 true（沿相切边传播）
var fillet,code,isWarning = sw.Fillet(doc,[edges[1].mid,edges[2].mid],{radius=0.005;name="AA_Fillet"});
if(!fillet){ print(code); return; }

// 等距倒角 4 mm；不等距用 distance2；角度-距离用 angle（弧度），二者不能同时给
var chamfer,chamferCode = sw.Chamfer(doc,edges[3].mid,{distance=0.004;name="AA_Chamfer"});
var chamfer2,chamfer2Code = sw.Chamfer(doc,edges[4].mid,{distance=0.006;angle=math.rad(30);propagate=false});
print(chamferCode,chamfer2Code);

// 用体积差验证：直棱边等距倒角去除 d²/2·L，圆角去除 (1-π/4)·r²·L
print(before - sw.GetMassProperties(doc).volume);
```

实测要点：

- 长方体棱边上 r=5 mm 圆角、4 mm 等距倒角、6 mm@30° 倒角的体积差都精确等于解析值（误差 < 1e-12 m³）。
- 对与圆角相切的边做倒角，即使 `propagate=false`，倒角两端仍会延伸进相邻圆角过渡面，去除体积约为名义值的 1.7 倍。不要用解析公式当验收标准，用回读的体积和面数作证据。
- 半径/距离超过相邻面尺寸时 SOLIDWORKS 不创建特征，助手返回 `null,说明`。

## 3. 旋转与阵列

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }
var template = "%ProgramData%\SOLIDWORKS\SOLIDWORKS 2025\templates\gb_part.prtdot";
var ring,newError = sw.NewDoc(1,template);
if(!ring){ print(newError); return; }
var planes = [];
for i,f in sw.eachFeature(ring){ if(f.GetTypeName2()=="RefPlane") table.push(planes,f); }

// 草图回调的返回值作为 NewSketch 的第 3 个返回值传出：这里返回中心线对象供 Revolve 当轴
var sketch,message,centerline = sw.NewSketch(ring,planes[1],function(sm){
    sm.CreateCornerRectangle(0.01,0,0,0.02,0.03,0);
    return sm.CreateCenterLine(0,-0.01,0,0,0.04,0);
},"AA_RevSketch");
if(!sketch){ print(message); return; }
var revolve,code = sw.Revolve(ring,sketch,{axis=centerline;name="AA_Revolve"}); // angle 默认 2π
if(!revolve){ print(code); return; }
print(sw.GetMassProperties(ring).volume,math.pi*(0.02**2-0.01**2)*0.03);

// 在上视基准面上画孔，双向完全贯穿；再绕外圆柱面圆周阵列 6 个
var holeSketch = sw.NewSketch(ring,planes[2],function(sm){ sm.CreateCircleByRadius(0.015,0,0,0.002); },"AA_Hole");
var hole,holeCode = sw.Extrude(ring,holeSketch,{cut=true;endCondition="throughAllBoth";name="AA_RingHole"});
var outer;
for i,f in sw.GetFaces(ring) { if(f.surfaceTypeName=="cylinder" && f.box[4]>0.019) outer=f; }
var pattern,patternCode = sw.CircularPattern(ring,"AA_RingHole",{axis=outer.point;count=6;name="AA_CircPattern"});
print(holeCode,patternCode,sw.GetMassProperties(ring).volume);

sw.CloseDoc(ring,true); // 示例不保存：显式丢弃
```

线性阵列的方向参考可以是直线边的 `{x,y,z}`、平面/基准面/基准轴特征名或对象。**不要再靠体积回读后手工 flip**：用 `toward1/toward2` 给出期望的方向向量，助手会自己决定 `flip` 并核对。`count` 都包含原始实例。

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }
var plate,findError = sw.FindDoc();
if(!plate){ print(findError); return; }
var dir1,dir2;
for i,e in sw.GetEdges(plate) {
    if(e.isLine && math.abs(e.length-0.08)<1e-9 && e.mid[3]>0.009) dir1 = e;
    if(e.isLine && math.abs(e.length-0.05)<1e-9 && e.mid[3]>0.009) dir2 = e;
}
// toward 只关心正负号：沿 +X 排 3 个、沿 +Y 排 2 个
var linear,code,isWarning,info = sw.LinearPattern(plate,"AA_H1",{
    direction1=dir1.mid;count1=3;spacing1=0.02;toward1=[1,0,0];
    direction2=dir2.mid;count2=2;spacing2=0.015;toward2=[0,1,0];
    name="AA_LinPattern";
});
if(!linear){ print(code); return; }
print(code,info.direction1,info.direction2,info.flip1,info.flip2,info.corrected1,info.corrected2);
print(sw.GetMassProperties(plate).volume);
```

方向规则（SOLIDWORKS 2025 实测，`sldworks-library/research/probe11-report.json`）：

- 直线边作为方向参考时，正方向 = 边的 **起点→终点**（`IEdge.GetCurveParams3().StartPoint/EndPoint`，与 `GetStartVertex/GetEndVertex` 一致，8 条边全部吻合）；平面型面 = 面的法向；基准面 = 法向（`IRefPlane.Transform` 的第 3 行）。助手据此预判 `flip`。
- 创建后用 `ILinearPatternFeatureData.GetTransform(k)` 回读实例平移：实例编号 `k = i1*count2 + i2`（跳过种子），方向 1 的首个实例 `k=count2`，方向 2 的首个实例 `k=1`。平移长度必须等于对应间距，否则助手不猜测方向。
- 回读与 `toward` 相反时，助手改写 `D1ReverseDirection/D2ReverseDirection` 并 `ModifyDefinition`（实测无需 `AccessSelections`），再回读一次；仍不一致则删除阵列并返回 `null,说明`。第 4 个返回值 `info` 即使不传 `toward` 也会给出实际方向、`flip` 与 `corrected` 标志，可直接作为验收证据。

## 4. 装配体：组件与配合

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }
var snapshot = sw.GetDocumentList();
var asm,newError = sw.NewDoc(2,"%ProgramData%\SOLIDWORKS\SOLIDWORKS 2025\templates\gb_assembly.asmdot");
if(!asm){ print(newError); return; }

// 组件文件默认隐藏加载（不弹窗口）；x,y,z 是组件包围盒中心，origin=true 时改为组件原点
var plateComponent,plateInfo = sw.AddComponent(asm,"/models/plate.SLDPRT",{x=0;y=0;z=0});
var blockComponent,blockInfo = sw.AddComponent(asm,"/models/block.SLDPRT",{x=0.1;y=0.08;z=0.05});
if(!plateComponent || !blockComponent){ print(plateInfo,blockInfo); return; }
print(plateInfo.fixed,blockInfo.fixed); // 第一个组件固定，后续浮动

// 配合实体用 {component=组件名,x,y,z}（装配体坐标）按几何定位；面上的点可以从 GetComponents().box 推算
var plate = sw.GetComponents(asm)[1];
var mate,info = sw.AddMate(asm,
    {component=plateInfo.name;x=0;y=0;z=plate.box[6]},          // 板顶面
    {component=blockInfo.name;x=0.12;y=0.09;z=0.05-0.015},      // 块底面（避开中心孔）
    {type="coincident";align="antiAligned";name="AA_Coincident"});
if(!mate){ print(info); return; }
print(info.statusName,info.mateTypeName,info.name);

var distanceMate,distanceInfo = sw.AddMate(asm,
    {component=plateInfo.name;x=plate.box[4];y=0;z=(plate.box[3]+plate.box[6])/2},
    {component=blockInfo.name;x=0.15;y=0.09;z=0.03},
    {type="distance";distance=0.005;align="antiAligned"});
print(distanceInfo.dimension); // {fullName,value}
print(sw.GetMates(asm));

sw.SetComponentTransform(asm,blockComponent,{position=[0.1,0.08,0.05]}); // 已配合的组件会被重建拉回
sw.CloseNewDocs(snapshot,true);
```

`AddMate` 内部调用 `AddMate5`，只有状态 1（`noError`）才成功；`incorrectSelections`（4）通常表示两个实体类型不匹配或坐标没有落在面上。配合后用 `FindComponent` + `com.sldWorks.getComponentInfo(component,0).box` 回读位置作为证据，不要只看返回对象。

## 5. 工程图：视图、模型尺寸与注释

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }
var snapshot = sw.GetDocumentList();
var drawing,newError = sw.NewDoc(3,"%ProgramData%\SOLIDWORKS\SOLIDWORKS 2025\templates\gb_a4.drwdot");
if(!drawing){ print(newError); return; }

// 三视图：原生接口，参数是模型完整路径，且该模型必须已打开；先隐藏加载（不弹窗口），结束时用 CloseNewDocs 一起清理
var modelPath = io.fullpath("/models/plate.SLDPRT");
var model,loadError,loadWarning,loadMessage = sw.OpenDoc(modelPath,,,,false);
if(!model){ print(loadError,loadMessage); return; }
if(!drawing.Create3rdAngleViews2(modelPath)){ print("不能创建三视图"); return; }
sw.RebuildDoc(drawing);
var views = sw.GetDrawingViews(drawing);   // name、orientation、scale、outline、referencedDocument、dimensionCount
var inserted = sw.InsertModelDimensions(drawing); // 默认插入“为工程图标注”的模型尺寸到所有视图
print(#views,inserted.inserted,inserted.dimensionCount);
var note,noteInfo = sw.AddNote(drawing,"AA round3",{x=0.05;y=0.19;height=0.005});
print(noteInfo.text,noteInfo.position);

sw.SaveAs("/output/plate-" + tostring(time(),"%Y%m%d-%H%M%S") + ".SLDDRW",,,,drawing);
sw.CloseNewDocs(snapshot);
```

工程图的 `GetDocInfo().hasActiveSketch` 可能只是图纸草图，不表示正在编辑草图；`CaptureView(...,"fit")` 对工程图只缩放到适合，不切换三维视角。

实测（`research/probe17/18`）：引用模型**隐藏加载且工程图先于它创建**时，`InsertModelAnnotations3` 首次返回空（即使强制重建、逐视图插入也无效）；先 `OpenDoc(model,,,,false)` 再 `NewDoc(3,...)` 则正常。`InsertModelDimensions` 在 `inserted==0` 时会激活一次视图引用的隐藏模型（其窗口变为可见）再重试；若仍为 0，检查模型尺寸是否“为工程图标注”。

## 6. 与 GetDocInfo/GetFeatures 一起做验收

每一步写操作之后至少读一种独立证据：`GetMassProperties().volume`（特征体积）、`#GetFaces()` 中圆柱面数量（孔数）、`GetMates()` 的 `statusName`、`GetDrawingViews()` 的 `dimensionCount`。`GetFeatures(doc).code` 非零的特征必须处理，图像（`CaptureView`）只作辅助。

## 官方接口依据

- [FeatureFillet3](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeatureManager~FeatureFillet3.html)
- [InsertFeatureChamfer](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeatureManager~InsertFeatureChamfer.html)
- [FeatureRevolve2](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeatureManager~FeatureRevolve2.html)
- [FeatureLinearPattern5](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeatureManager~FeatureLinearPattern5.html)
- [ILinearPatternFeatureData::GetTransform](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.ILinearPatternFeatureData~GetTransform.html)
- [IFeature::ModifyDefinition](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeature~ModifyDefinition.html)
- [FeatureCircularPattern5](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeatureManager~FeatureCircularPattern5.html)
- [AddComponent5](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IAssemblyDoc~AddComponent5.html)
- [AddMate5](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IAssemblyDoc~AddMate5.html)
- [InsertModelAnnotations3](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IDrawingDoc~InsertModelAnnotations3.html)

[返回首页](index.html)
