TellmeRevitPluging/Services/ShellAnalyzer.cs

470 lines
17 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}