470 lines
17 KiB
C#
470 lines
17 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Autodesk.Revit.DB;
|
||
using Autodesk.Revit.DB.Architecture;
|
||
using RevitHttpControl.Models;
|
||
|
||
namespace RevitHttpControl.Services
|
||
{
|
||
/// <summary>
|
||
/// 薄壳分析服务
|
||
/// </summary>
|
||
public class ShellAnalyzer
|
||
{
|
||
public ElementAction GetElementAction(Element element, ShellOptimizeMode mode)
|
||
{
|
||
if (element?.Category == null)
|
||
return ElementAction.Keep;
|
||
|
||
return DetermineElementAction(element, mode);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析模型构件,确定保留和删除的元素
|
||
/// </summary>
|
||
public ShellAnalyzeResponse AnalyzeModel(Document doc, ShellOptimizeMode mode)
|
||
{
|
||
if (doc == null)
|
||
throw new ArgumentNullException(nameof(doc));
|
||
|
||
// 获取所有构件
|
||
var allElements = GetAnalyzableElements(doc);
|
||
|
||
// 分析每个构件
|
||
var analysisResults = new List<ElementAnalysisResult>();
|
||
foreach (var element in allElements)
|
||
{
|
||
var result = AnalyzeElement(element, mode);
|
||
analysisResults.Add(result);
|
||
}
|
||
|
||
// 统计结果
|
||
var summary = CreateAnalysisSummary(analysisResults);
|
||
var categories = CreateCategoryStatistics(analysisResults);
|
||
|
||
return new ShellAnalyzeResponse
|
||
{
|
||
Analysis = summary,
|
||
Categories = categories
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取可分析的构件(排除视图、注释等)
|
||
/// </summary>
|
||
private List<Element> GetAnalyzableElements(Document doc)
|
||
{
|
||
var collector = new FilteredElementCollector(doc)
|
||
.WhereElementIsNotElementType()
|
||
.WhereElementIsViewIndependent();
|
||
|
||
var elements = new List<Element>();
|
||
|
||
foreach (Element element in collector)
|
||
{
|
||
// 只分析实体构件,排除视图、注释、标注等
|
||
if (IsAnalyzableElement(element))
|
||
{
|
||
elements.Add(element);
|
||
}
|
||
}
|
||
|
||
return elements;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断构件是否可分析
|
||
/// </summary>
|
||
private bool IsAnalyzableElement(Element element)
|
||
{
|
||
// 排除不需要分析的元素类型
|
||
if (element is View || element is ViewSheet || element is Viewport)
|
||
return false;
|
||
|
||
if (element.Category == null)
|
||
return false;
|
||
|
||
var category = element.Category.Id.IntegerValue;
|
||
|
||
// 包含的主要构件类别
|
||
var includedCategories = new[]
|
||
{
|
||
(int)BuiltInCategory.OST_Walls, // 墙
|
||
(int)BuiltInCategory.OST_Doors, // 门
|
||
(int)BuiltInCategory.OST_Windows, // 窗
|
||
(int)BuiltInCategory.OST_Furniture, // 家具
|
||
(int)BuiltInCategory.OST_FurnitureSystems, // 家具系统
|
||
(int)BuiltInCategory.OST_Casework, // 橱柜
|
||
(int)BuiltInCategory.OST_Columns, // 柱
|
||
(int)BuiltInCategory.OST_StructuralColumns, // 结构柱
|
||
(int)BuiltInCategory.OST_StructuralFraming, // 结构框架
|
||
(int)BuiltInCategory.OST_Floors, // 楼板
|
||
(int)BuiltInCategory.OST_Roofs, // 屋顶
|
||
(int)BuiltInCategory.OST_Ceilings, // 天花板
|
||
(int)BuiltInCategory.OST_Stairs, // 楼梯
|
||
(int)BuiltInCategory.OST_Railings, // 栏杆
|
||
(int)BuiltInCategory.OST_GenericModel, // 常规模型
|
||
(int)BuiltInCategory.OST_Entourage, // 配景
|
||
(int)BuiltInCategory.OST_LightingFixtures, // 灯具
|
||
(int)BuiltInCategory.OST_PlumbingFixtures, // 卫浴装置
|
||
(int)BuiltInCategory.OST_ElectricalFixtures, // 电气装置
|
||
(int)BuiltInCategory.OST_MechanicalEquipment, // 机械设备
|
||
(int)BuiltInCategory.OST_ElectricalEquipment, // 电气设备
|
||
(int)BuiltInCategory.OST_SpecialityEquipment, // 专用设备
|
||
};
|
||
|
||
return includedCategories.Contains(category);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析单个构件
|
||
/// </summary>
|
||
private ElementAnalysisResult AnalyzeElement(Element element, ShellOptimizeMode mode)
|
||
{
|
||
var categoryId = element.Category?.Id?.IntegerValue ?? 0;
|
||
var categoryName = element.Category?.Name ?? "未知类别";
|
||
var action = DetermineElementAction(element, mode);
|
||
var reason = GetActionReason(element, action, mode);
|
||
|
||
return new ElementAnalysisResult
|
||
{
|
||
ElementId = element.Id.IntegerValue,
|
||
CategoryId = categoryId,
|
||
CategoryName = categoryName,
|
||
Action = action,
|
||
Reason = reason
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 确定构件的处理动作(保留或删除)
|
||
/// </summary>
|
||
private ElementAction DetermineElementAction(Element element, ShellOptimizeMode mode)
|
||
{
|
||
var category = element.Category.Id.IntegerValue;
|
||
|
||
if (mode == ShellOptimizeMode.EnvelopeOnly)
|
||
{
|
||
if (IsSiteElement(category))
|
||
return ElementAction.Keep;
|
||
|
||
if (element is RevitLinkInstance)
|
||
return ElementAction.Remove;
|
||
|
||
return IsEnvelopeElement(element) ? ElementAction.Keep : ElementAction.Remove;
|
||
}
|
||
|
||
// 1. 始终保留的结构元素
|
||
if (IsStructuralElement(category))
|
||
return ElementAction.Keep;
|
||
|
||
// 2. 始终保留的外围元素
|
||
if (IsExteriorElement(element))
|
||
return ElementAction.Keep;
|
||
|
||
// 3. 根据模式判断其他元素
|
||
switch (mode)
|
||
{
|
||
case ShellOptimizeMode.Conservative:
|
||
return DetermineConservativeAction(category);
|
||
case ShellOptimizeMode.Standard:
|
||
return DetermineStandardAction(category);
|
||
case ShellOptimizeMode.Aggressive:
|
||
return DetermineAggressiveAction(category);
|
||
default:
|
||
return ElementAction.Keep;
|
||
}
|
||
}
|
||
|
||
private bool IsSiteElement(int categoryId)
|
||
{
|
||
var siteCategories = new[]
|
||
{
|
||
(int)BuiltInCategory.OST_Topography,
|
||
(int)BuiltInCategory.OST_Site,
|
||
(int)BuiltInCategory.OST_Parking,
|
||
(int)BuiltInCategory.OST_Planting,
|
||
};
|
||
|
||
return siteCategories.Contains(categoryId);
|
||
}
|
||
|
||
private bool IsEnvelopeElement(Element element)
|
||
{
|
||
if (element?.Category == null)
|
||
return false;
|
||
|
||
var categoryId = element.Category.Id.IntegerValue;
|
||
|
||
// Curtain系统构件
|
||
if (categoryId == (int)BuiltInCategory.OST_CurtainWallPanels ||
|
||
categoryId == (int)BuiltInCategory.OST_CurtainWallMullions)
|
||
return true;
|
||
|
||
// 外墙
|
||
if (element is Wall wall)
|
||
return IsExteriorWall(wall);
|
||
|
||
// 屋顶
|
||
if (element is RoofBase)
|
||
return true;
|
||
|
||
// 外门窗:Host 为外墙
|
||
if (element is FamilyInstance fi)
|
||
{
|
||
if (categoryId == (int)BuiltInCategory.OST_Doors || categoryId == (int)BuiltInCategory.OST_Windows)
|
||
{
|
||
try
|
||
{
|
||
return fi.Host is Wall hostWall && IsExteriorWall(hostWall);
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private bool IsExteriorWall(Wall wall)
|
||
{
|
||
try
|
||
{
|
||
var wallType = wall.WallType;
|
||
if (wallType != null)
|
||
{
|
||
if (wallType.Function == WallFunction.Exterior)
|
||
return true;
|
||
|
||
// 少量模型不设置Function时的兜底:幕墙通常属于围护
|
||
var typeName = wallType.Name?.ToLower() ?? "";
|
||
if (typeName.Contains("curtain") || typeName.Contains("幕墙"))
|
||
return true;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断是否为结构元素(始终保留)
|
||
/// </summary>
|
||
private bool IsStructuralElement(int categoryId)
|
||
{
|
||
var structuralCategories = new[]
|
||
{
|
||
(int)BuiltInCategory.OST_Columns,
|
||
(int)BuiltInCategory.OST_StructuralColumns,
|
||
(int)BuiltInCategory.OST_StructuralFraming,
|
||
(int)BuiltInCategory.OST_Floors,
|
||
(int)BuiltInCategory.OST_Roofs,
|
||
(int)BuiltInCategory.OST_Stairs,
|
||
(int)BuiltInCategory.OST_Railings
|
||
};
|
||
|
||
return structuralCategories.Contains(categoryId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断是否为外围元素(外墙、外门窗等)
|
||
/// </summary>
|
||
private bool IsExteriorElement(Element element)
|
||
{
|
||
// 简化判断:假定外墙、外门窗通过命名或参数识别
|
||
// 实际项目中可能需要更复杂的空间分析
|
||
|
||
if (element is Wall wall)
|
||
{
|
||
// 检查墙是否为外墙(简化判断)
|
||
try
|
||
{
|
||
var wallType = wall.WallType;
|
||
var typeName = wallType?.Name?.ToLower() ?? "";
|
||
return typeName.Contains("外") || typeName.Contains("exterior") ||
|
||
typeName.Contains("外墙") || typeName.Contains("curtain");
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (element is FamilyInstance familyInstance)
|
||
{
|
||
// 检查门窗是否在外墙上
|
||
try
|
||
{
|
||
var host = familyInstance.Host;
|
||
if (host is Wall hostWall)
|
||
{
|
||
return IsExteriorElement(hostWall);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保守模式的判断逻辑(只删除装饰元素)
|
||
/// </summary>
|
||
private ElementAction DetermineConservativeAction(int categoryId)
|
||
{
|
||
var decorativeCategories = new[]
|
||
{
|
||
(int)BuiltInCategory.OST_Entourage, // 配景
|
||
};
|
||
|
||
return decorativeCategories.Contains(categoryId) ? ElementAction.Remove : ElementAction.Keep;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标准模式的判断逻辑(删除家具和部分内墙)
|
||
/// </summary>
|
||
private ElementAction DetermineStandardAction(int categoryId)
|
||
{
|
||
var removeCategories = new[]
|
||
{
|
||
(int)BuiltInCategory.OST_Furniture, // 家具
|
||
(int)BuiltInCategory.OST_FurnitureSystems, // 家具系统
|
||
(int)BuiltInCategory.OST_Casework, // 橱柜
|
||
(int)BuiltInCategory.OST_Entourage, // 配景
|
||
(int)BuiltInCategory.OST_LightingFixtures, // 灯具(部分)
|
||
};
|
||
|
||
return removeCategories.Contains(categoryId) ? ElementAction.Remove : ElementAction.Keep;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 激进模式的判断逻辑(删除所有内部元素)
|
||
/// </summary>
|
||
private ElementAction DetermineAggressiveAction(int categoryId)
|
||
{
|
||
var removeCategories = new[]
|
||
{
|
||
(int)BuiltInCategory.OST_Furniture, // 家具
|
||
(int)BuiltInCategory.OST_FurnitureSystems, // 家具系统
|
||
(int)BuiltInCategory.OST_Casework, // 橱柜
|
||
(int)BuiltInCategory.OST_Entourage, // 配景
|
||
(int)BuiltInCategory.OST_LightingFixtures, // 灯具
|
||
(int)BuiltInCategory.OST_ElectricalFixtures, // 电气装置
|
||
(int)BuiltInCategory.OST_PlumbingFixtures, // 卫浴装置
|
||
(int)BuiltInCategory.OST_MechanicalEquipment, // 机械设备
|
||
(int)BuiltInCategory.OST_ElectricalEquipment, // 电气设备
|
||
(int)BuiltInCategory.OST_SpecialityEquipment, // 专用设备
|
||
(int)BuiltInCategory.OST_Ceilings, // 天花板
|
||
(int)BuiltInCategory.OST_GenericModel, // 常规模型(部分)
|
||
};
|
||
|
||
return removeCategories.Contains(categoryId) ? ElementAction.Remove : ElementAction.Keep;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取动作的原因说明
|
||
/// </summary>
|
||
private string GetActionReason(Element element, ElementAction action, ShellOptimizeMode mode)
|
||
{
|
||
var category = element.Category.Id.IntegerValue;
|
||
|
||
if (action == ElementAction.Keep)
|
||
{
|
||
if (mode == ShellOptimizeMode.EnvelopeOnly)
|
||
{
|
||
if (IsSiteElement(category))
|
||
return "场地/地形构件,必须保留";
|
||
return "围护构件,必须保留";
|
||
}
|
||
|
||
if (IsStructuralElement(category))
|
||
return "结构元素,必须保留";
|
||
if (IsExteriorElement(element))
|
||
return "外围元素,必须保留";
|
||
return $"{mode}模式下保留";
|
||
}
|
||
else
|
||
{
|
||
if (mode == ShellOptimizeMode.EnvelopeOnly)
|
||
{
|
||
if (element is RevitLinkInstance)
|
||
return "链接模型,必须删除";
|
||
return "室内/非围护构件,删除";
|
||
}
|
||
|
||
switch (mode)
|
||
{
|
||
case ShellOptimizeMode.Conservative:
|
||
return "装饰元素,保守模式下删除";
|
||
case ShellOptimizeMode.Standard:
|
||
return "内部元素,标准模式下删除";
|
||
case ShellOptimizeMode.Aggressive:
|
||
return "非结构元素,激进模式下删除";
|
||
default:
|
||
return "删除";
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建分析摘要
|
||
/// </summary>
|
||
private ShellAnalysisSummary CreateAnalysisSummary(List<ElementAnalysisResult> results)
|
||
{
|
||
var total = results.Count;
|
||
var keep = results.Count(r => r.Action == ElementAction.Keep);
|
||
var remove = results.Count(r => r.Action == ElementAction.Remove);
|
||
|
||
var estimatedReduction = total > 0
|
||
? $"{(remove * 100.0 / total):F1}%"
|
||
: "0%";
|
||
|
||
return new ShellAnalysisSummary
|
||
{
|
||
TotalElements = total,
|
||
KeepElements = keep,
|
||
RemoveElements = remove,
|
||
EstimatedReduction = estimatedReduction,
|
||
OriginalSize = "未知",
|
||
EstimatedOptimizedSize = "未知"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建分类统计
|
||
/// </summary>
|
||
private List<CategoryStatistics> CreateCategoryStatistics(List<ElementAnalysisResult> results)
|
||
{
|
||
var categoryGroups = results.GroupBy(r => new { r.CategoryId, r.CategoryName });
|
||
var statistics = new List<CategoryStatistics>();
|
||
|
||
foreach (var group in categoryGroups.OrderBy(g => g.Key.CategoryName))
|
||
{
|
||
var total = group.Count();
|
||
var keep = group.Count(r => r.Action == ElementAction.Keep);
|
||
var remove = group.Count(r => r.Action == ElementAction.Remove);
|
||
|
||
statistics.Add(new CategoryStatistics
|
||
{
|
||
CategoryId = group.Key.CategoryId,
|
||
Name = group.Key.CategoryName,
|
||
Total = total,
|
||
Keep = keep,
|
||
Remove = remove
|
||
});
|
||
}
|
||
|
||
return statistics;
|
||
}
|
||
}
|
||
}
|