From 52189448ddf9ce5b90b4305ff23c19438be3ba3d Mon Sep 17 00:00:00 2001 From: sladro Date: Thu, 29 Jan 2026 11:21:58 +0800 Subject: [PATCH] feat: add EnvelopeOnly mode for shell optimization --- Controllers/ShellController.cs | 8 +++ Models/ShellModels.cs | 7 ++- Readme.md | 5 +- Services/ShellAnalyzer.cs | 109 +++++++++++++++++++++++++++++++++ Services/ShellOptimizer.cs | 45 ++++---------- 5 files changed, 138 insertions(+), 36 deletions(-) diff --git a/Controllers/ShellController.cs b/Controllers/ShellController.cs index a18c0a2..6b17506 100644 --- a/Controllers/ShellController.cs +++ b/Controllers/ShellController.cs @@ -289,6 +289,14 @@ namespace RevitHttpControl.Controllers Description = "删除所有内部元素,减少约80%文件大小", EstimatedReduction = "80%", DeletedElements = new[] { "家具", "设备", "天花板", "配景", "植物", "灯具", "内部装饰" } + }, + new + { + Mode = ShellOptimizeMode.EnvelopeOnly, + Name = "仅保留围护+场地", + Description = "严格只保留外壳(外墙/屋顶/外门窗等)与场地地形,删除所有室内与链接模型", + EstimatedReduction = "-", + DeletedElements = new[] { "所有室内构件", "结构构件", "链接模型" } } }; diff --git a/Models/ShellModels.cs b/Models/ShellModels.cs index 5e520f9..9865d11 100644 --- a/Models/ShellModels.cs +++ b/Models/ShellModels.cs @@ -171,7 +171,12 @@ namespace RevitHttpControl.Models /// /// 激进模式 - 删除所有内部元素(减少约80%) /// - Aggressive + Aggressive, + + /// + /// 仅保留围护+场地 - 严格只保留外壳(外墙/屋顶/外门窗等)与场地地形,删除所有室内与链接模型 + /// + EnvelopeOnly } /// diff --git a/Readme.md b/Readme.md index d6fa0fa..7c14f37 100644 --- a/Readme.md +++ b/Readme.md @@ -502,4 +502,7 @@ GET http://localhost:9000/api/shell/modes - **智能分析**:25种构件类别支持,外墙外门窗智能识别 - **完整统计**:文件大小对比、删除统计、处理时间记录 -项目现已集成完整的薄壳优化功能,代码质量达到生产级标准。 \ No newline at end of file +### 变更 + + • POST /api/shell/execute 或 POST /api/shell/execute/sync + • body 里把 mode 设为 EnvelopeOnly \ No newline at end of file diff --git a/Services/ShellAnalyzer.cs b/Services/ShellAnalyzer.cs index e2fdc04..81553ac 100644 --- a/Services/ShellAnalyzer.cs +++ b/Services/ShellAnalyzer.cs @@ -12,6 +12,14 @@ namespace RevitHttpControl.Services /// public class ShellAnalyzer { + public ElementAction GetElementAction(Element element, ShellOptimizeMode mode) + { + if (element?.Category == null) + return ElementAction.Keep; + + return DetermineElementAction(element, mode); + } + /// /// 分析模型构件,确定保留和删除的元素 /// @@ -134,6 +142,17 @@ namespace RevitHttpControl.Services { 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; @@ -156,6 +175,82 @@ namespace RevitHttpControl.Services } } + 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; + } + /// /// 判断是否为结构元素(始终保留) /// @@ -282,6 +377,13 @@ namespace RevitHttpControl.Services if (action == ElementAction.Keep) { + if (mode == ShellOptimizeMode.EnvelopeOnly) + { + if (IsSiteElement(category)) + return "场地/地形构件,必须保留"; + return "围护构件,必须保留"; + } + if (IsStructuralElement(category)) return "结构元素,必须保留"; if (IsExteriorElement(element)) @@ -290,6 +392,13 @@ namespace RevitHttpControl.Services } else { + if (mode == ShellOptimizeMode.EnvelopeOnly) + { + if (element is RevitLinkInstance) + return "链接模型,必须删除"; + return "室内/非围护构件,删除"; + } + switch (mode) { case ShellOptimizeMode.Conservative: diff --git a/Services/ShellOptimizer.cs b/Services/ShellOptimizer.cs index 56db91f..ff68f52 100644 --- a/Services/ShellOptimizer.cs +++ b/Services/ShellOptimizer.cs @@ -39,7 +39,7 @@ namespace RevitHttpControl.Services // 2. 分析模型获取删除列表 var analysis = _analyzer.AnalyzeModel(doc, mode); - var elementsToDelete = GetElementsToDelete(doc, analysis); + var elementsToDelete = GetElementsToDelete(doc, mode); // 3. 创建备份(如果需要) if (backupOriginal) @@ -48,7 +48,7 @@ namespace RevitHttpControl.Services } // 4. 执行删除操作 - var deletedCount = DeleteElements(doc, elementsToDelete); + var deletedCount = DeleteElements(doc, elementsToDelete, mode); result.RemovedCount = deletedCount; // 5. 保存文档并计算优化后大小 @@ -85,14 +85,10 @@ namespace RevitHttpControl.Services /// /// 根据分析结果获取需要删除的构件 /// - private List GetElementsToDelete(Document doc, ShellAnalyzeResponse analysis) + private List GetElementsToDelete(Document doc, ShellOptimizeMode mode) { var elementsToDelete = new List(); - // 重新分析获取详细的构件列表(为了安全性) - var analyzer = new ShellAnalyzer(); - var detailedAnalysis = analyzer.AnalyzeModel(doc, ShellOptimizeMode.Standard); - // 获取所有需要删除的构件 var allElements = new FilteredElementCollector(doc) .WhereElementIsNotElementType() @@ -101,7 +97,7 @@ namespace RevitHttpControl.Services foreach (var element in allElements) { - if (ShouldDeleteElement(element, analysis)) + if (_analyzer.GetElementAction(element, mode) == ElementAction.Remove) { elementsToDelete.Add(element.Id); } @@ -110,30 +106,10 @@ namespace RevitHttpControl.Services return elementsToDelete; } - /// - /// 判断构件是否应该删除 - /// - private bool ShouldDeleteElement(Element element, ShellAnalyzeResponse analysis) - { - if (element.Category == null) - return false; - - var categoryName = element.Category.Name; - var categoryStats = analysis.Categories.FirstOrDefault(c => c.Name == categoryName); - - // 如果该类别没有删除项,则不删除 - if (categoryStats == null || categoryStats.Remove == 0) - return false; - - // 这里可以实现更复杂的逻辑,比如优先删除某些构件 - // 目前简化为按类别统计比例删除 - return true; - } - /// /// 批量删除构件 /// - private int DeleteElements(Document doc, List elementIds) + private int DeleteElements(Document doc, List elementIds, ShellOptimizeMode mode) { if (elementIds == null || elementIds.Count == 0) return 0; @@ -145,7 +121,7 @@ namespace RevitHttpControl.Services for (int i = 0; i < elementIds.Count; i += batchSize) { var batch = elementIds.Skip(i).Take(batchSize).ToList(); - deletedCount += DeleteElementBatch(doc, batch); + deletedCount += DeleteElementBatch(doc, batch, mode); } return deletedCount; @@ -154,7 +130,7 @@ namespace RevitHttpControl.Services /// /// 删除一批构件 /// - private int DeleteElementBatch(Document doc, List elementIds) + private int DeleteElementBatch(Document doc, List elementIds, ShellOptimizeMode mode) { using (var transaction = new Transaction(doc, "薄壳优化删除构件")) { @@ -168,7 +144,7 @@ namespace RevitHttpControl.Services foreach (var id in elementIds) { var element = doc.GetElement(id); - if (element != null && CanDeleteElement(element)) + if (element != null && CanDeleteElement(element, mode)) { deletableIds.Add(id); } @@ -201,7 +177,7 @@ namespace RevitHttpControl.Services /// /// 检查构件是否可以安全删除 /// - private bool CanDeleteElement(Element element) + private bool CanDeleteElement(Element element, ShellOptimizeMode mode) { try { @@ -221,7 +197,8 @@ namespace RevitHttpControl.Services } // 检查是否有依赖关系(如主体构件) - if (HasCriticalDependencies(element)) + // EnvelopeOnly: 允许删除“宿主”类元素(如内墙),由Revit级联删除其托管构件 + if (mode != ShellOptimizeMode.EnvelopeOnly && HasCriticalDependencies(element)) return false; return true;