feat: add EnvelopeOnly mode for shell optimization
This commit is contained in:
parent
80c5e73ab1
commit
52189448dd
@ -289,6 +289,14 @@ namespace RevitHttpControl.Controllers
|
||||
Description = "删除所有内部元素,减少约80%文件大小",
|
||||
EstimatedReduction = "80%",
|
||||
DeletedElements = new[] { "家具", "设备", "天花板", "配景", "植物", "灯具", "内部装饰" }
|
||||
},
|
||||
new
|
||||
{
|
||||
Mode = ShellOptimizeMode.EnvelopeOnly,
|
||||
Name = "仅保留围护+场地",
|
||||
Description = "严格只保留外壳(外墙/屋顶/外门窗等)与场地地形,删除所有室内与链接模型",
|
||||
EstimatedReduction = "-",
|
||||
DeletedElements = new[] { "所有室内构件", "结构构件", "链接模型" }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -171,7 +171,12 @@ namespace RevitHttpControl.Models
|
||||
/// <summary>
|
||||
/// 激进模式 - 删除所有内部元素(减少约80%)
|
||||
/// </summary>
|
||||
Aggressive
|
||||
Aggressive,
|
||||
|
||||
/// <summary>
|
||||
/// 仅保留围护+场地 - 严格只保留外壳(外墙/屋顶/外门窗等)与场地地形,删除所有室内与链接模型
|
||||
/// </summary>
|
||||
EnvelopeOnly
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -502,4 +502,7 @@ GET http://localhost:9000/api/shell/modes
|
||||
- **智能分析**:25种构件类别支持,外墙外门窗智能识别
|
||||
- **完整统计**:文件大小对比、删除统计、处理时间记录
|
||||
|
||||
项目现已集成完整的薄壳优化功能,代码质量达到生产级标准。
|
||||
### 变更
|
||||
|
||||
• POST /api/shell/execute 或 POST /api/shell/execute/sync
|
||||
• body 里把 mode 设为 EnvelopeOnly
|
||||
@ -12,6 +12,14 @@ namespace RevitHttpControl.Services
|
||||
/// </summary>
|
||||
public class ShellAnalyzer
|
||||
{
|
||||
public ElementAction GetElementAction(Element element, ShellOptimizeMode mode)
|
||||
{
|
||||
if (element?.Category == null)
|
||||
return ElementAction.Keep;
|
||||
|
||||
return DetermineElementAction(element, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分析模型构件,确定保留和删除的元素
|
||||
/// </summary>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否为结构元素(始终保留)
|
||||
/// </summary>
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
/// <summary>
|
||||
/// 根据分析结果获取需要删除的构件
|
||||
/// </summary>
|
||||
private List<ElementId> GetElementsToDelete(Document doc, ShellAnalyzeResponse analysis)
|
||||
private List<ElementId> GetElementsToDelete(Document doc, ShellOptimizeMode mode)
|
||||
{
|
||||
var elementsToDelete = new List<ElementId>();
|
||||
|
||||
// 重新分析获取详细的构件列表(为了安全性)
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断构件是否应该删除
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量删除构件
|
||||
/// </summary>
|
||||
private int DeleteElements(Document doc, List<ElementId> elementIds)
|
||||
private int DeleteElements(Document doc, List<ElementId> 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
|
||||
/// <summary>
|
||||
/// 删除一批构件
|
||||
/// </summary>
|
||||
private int DeleteElementBatch(Document doc, List<ElementId> elementIds)
|
||||
private int DeleteElementBatch(Document doc, List<ElementId> 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
|
||||
/// <summary>
|
||||
/// 检查构件是否可以安全删除
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user