# Navisworks API 常用模式

## 1. 模型遍历

### 遍历所有模型项

```csharp
// 获取所有模型项
IEnumerable<ModelItem> allItems = 
    Application.ActiveDocument.Models.RootItemDescendantsAndSelf;

// 遍历特定模型
foreach (Model model in document.Models)
{
    foreach (ModelItem item in model.RootItem.DescendantsAndSelf)
    {
        // 处理每个模型项
    }
}
```

### 获取子节点

```csharp
// 获取所有后代（不包括自己）
var childItems = selectedItem.DescendantsAndSelf.Where(x => x != selectedItem);

// 只获取直接子节点
foreach (ModelItem child in parentItem.Children) { }

// 向上遍历祖先
var current = selectedItem.Parent;
while (current != null)
{
    current = current.Parent;
}
```

## 2. 搜索和查询

### 使用 Search 类

```csharp
Search search = new Search();

// 添加搜索条件
search.SearchConditions.Add(
    SearchCondition.HasCategoryByName(PropertyCategoryNames.Geometry));
search.SearchConditions.Add(
    SearchCondition.HasPropertyByName(PropertyCategoryNames.Item, DataPropertyNames.ItemHidden)
    .EqualValue(VariantData.FromBoolean(false)));

// 设置搜索范围
search.Selection.SelectAll();
search.Locations = SearchLocations.DescendantsAndSelf;

// 执行搜索
ModelItemCollection results = search.FindAll(document, false);
```

### 使用 LINQ 查询

```csharp
var results = document.Models.RootItemDescendantsAndSelf
    .Where(x => x.HasGeometry && !x.IsHidden)
    .Where(x => x.ClassDisplayName.ToLower().Contains("wall"));
```

## 3. 选择操作

```csharp
// 获取当前选择
var currentSelection = document.CurrentSelection.SelectedItems;

// 清空选择
document.CurrentSelection.Clear();

// 添加到选择
document.CurrentSelection.Add(modelItem);

// 复制集合到选择
document.CurrentSelection.CopyFrom(modelItems);
```

## 4. 可见性控制

```csharp
// 隐藏项目
ModelItemCollection itemsToHide = new ModelItemCollection();
itemsToHide.Add(modelItem);
document.Models.SetHidden(itemsToHide, true);

// 显示项目
document.Models.SetHidden(itemsToHide, false);

// 检查是否隐藏
if (modelItem.IsHidden) { }
```

## 5. 文件导出

```csharp
// 保存 NWD 文件
document.SaveFile(filePath);

// 使用导出选项
var exportOptions = new NwdExportOptions();
exportOptions.ExcludeHiddenItems = true;
exportOptions.EmbedXrefs = false;
exportOptions.PreventObjectPropertyExport = false;

document.ExportToNwd(saveFilePath, exportOptions);
```

## 6. Transform 变换操作

### 核心概念

- `ModelItem.Transform` - 返回设计文件中的原始变换（只读）
- `OverridePermanentTransform()` - 应用增量变换（累积）
- `ResetPermanentTransform()` - 清除所有增量变换
- `ModelItem.BoundingBox()` - 返回当前实际显示的包围盒

### 应用变换

```csharp
// 获取原始 Transform
Transform3D originalTransform = modelItem.Transform;

// 应用增量变换
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { modelItem };
doc.Models.OverridePermanentTransform(modelItems, newTransform, false);

// 重置到原始位置
doc.Models.ResetPermanentTransform(modelItems);
```

### 绕物体中心旋转（需要补偿）

```csharp
// 关键：API 的旋转总是绕世界原点(0,0,0)
// 要实现绕物体中心旋转，需要计算补偿

double deltaYaw = newYaw - currentYaw;
double cos = Math.Cos(deltaYaw);
double sin = Math.Sin(deltaYaw);

// 计算绕原点旋转后的位置
double rotatedX = currentPosition.X * cos - currentPosition.Y * sin;
double rotatedY = currentPosition.X * sin + currentPosition.Y * cos;

// 计算补偿平移
var compensatedTranslation = new Vector3D(
    newPosition.X - rotatedX,
    newPosition.Y - rotatedY,
    newPosition.Z - currentPosition.Z
);

// 组合变换
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
var components = identity.Factor();
components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
components.Translation = compensatedTranslation;

var incrementalTransform = components.Combine();
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
```

## 7. 属性访问

### NET API 方式

```csharp
// 查找属性分类
var category = item.PropertyCategories.FindCategoryByDisplayName("Material");

// 基础属性
string displayName = item.DisplayName;
string className = item.ClassName;
bool hasGeometry = item.HasGeometry;
bool isHidden = item.IsHidden;
```

### COM API 方式（当 NET API 不满足需求时使用）

**参考**: `doc/navisworks_api/NET/examples/Basic Examples/CSharp/APICallsCOMPlugin/`

```csharp
using Autodesk.Navisworks.ComApi;
using Autodesk.Navisworks.Interop.ComApi;

// 获取 COM 状态
ComApi.InwOpState10 state = ComApiBridge.State;

// 获取属性节点
InwOpSelection2 selection = state.CurrentSelection as InwOpSelection2;
InwGUIPropertyNode2 propertyNode = 
    state.GetGUIPropertyNode(selection.Paths()[1], true) as InwGUIPropertyNode2;

// 遍历属性分类
foreach (InwGUIAttribute2 guiAttribute in propertyNode.GUIAttributes())
{
    foreach (InwOaProperty property in guiAttribute.Properties())
    {
        string name = property.name;
        string value = property.value;
    }
}
```

### 添加自定义属性（COM API）

```csharp
InwOaPropertyVec propertyVector = 
    state.ObjectFactory(nwEObjectType.eObjectType_nwOaPropertyVec);

InwOaProperty property = 
    state.ObjectFactory(nwEObjectType.eObjectType_nwOaProperty);
property.name = "CustomProperty1";
property.UserName = "自定义属性1";
property.value = value;

propertyVector.Properties().Add(property);
propertyNode.SetUserDefined(0, categoryName, internalName, propertyVector);
```

## 8. 线程安全 - 关键

**所有 Navisworks API 调用必须在主 UI 线程中执行**

```csharp
// ❌ 错误：在后台线程调用
await Task.Run(() =>
{
    var document = Application.ActiveDocument;  // 可能崩溃
});

// ✅ 正确：使用 Dispatcher.Invoke
await Task.Run(() =>
{
    System.Windows.Application.Current.Dispatcher.Invoke(() =>
    {
        var document = Application.ActiveDocument;
        document.ExportToNwd(path, options);  // 安全执行
    });
});
```

### 检查线程状态

```csharp
var apartmentState = System.Threading.Thread.CurrentThread.GetApartmentState();
bool isMainThread = System.Windows.Application.Current.Dispatcher.CheckAccess();
```

## 9. 碰撞检测 (ClashDetective)

```csharp
// 获取碰撞测试
document.ClashTestsData.Tests;

// 创建碰撞选择
var selectionA = new ClashSelection(modelItemsA, document);
var selectionB = new ClashSelection(modelItemsB, document);

// 创建碰撞测试
var clashTest = new ClashTest();
clashTest.Name = "Test Name";
clashTest.TestType = ClashTestType.Hard;
clashTest.SelectionA = selectionA;
clashTest.SelectionB = selectionB;
```

## 10. TimeLiner 集成

```csharp
// 获取 TimeLiner 数据
var timeliner = document.Timeliner;

// 添加任务
var task = new TimelinerTask();
task.Name = "Task Name";
task.SimulationStatus = SimulationStatus.Active;
task.StartDate = DateTime.Now;
task.EndDate = DateTime.Now.AddDays(7);

timeliner.Tasks.Add(task);

// 关联模型项
task.Selection.CopyFrom(modelItems);
```
