Files
SzmediTools/Szmedi.RevitToolkit.Approval/Commands/ProcessArchiSignCmd.cs
2025-09-16 16:06:41 +08:00

337 lines
14 KiB
C#
Raw 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.ComponentModel;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Markup;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using FuzzySharp;
using JiebaNet.Segmenter;
using Microsoft.Win32;
using Nice3point.Revit.Toolkit.External;
using Szmedi.RevitToolkit.Approval.Controls.ProgressWrapper;
using Szmedi.RevitToolkit.Approval.Models;
namespace Szmedi.RevitToolkit.Approval.Commands
{
/// <summary>
/// 建筑标识预处理
/// </summary>
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class ProcessArchiSignCmd : ExternalCommand
{
private Major major;
private string majorName;
public override void Execute()
{
StringBuilder sb = new StringBuilder();
const string signParam = "深圳构件标识";
var dir = Path.Combine(GlobalAssists.DirAssembly, "mvdlite");
major = Document.GetMajor(out majorName);
var result = MessageBox.Show(
$"""
通过各专业专业独有的构件
(建筑-门窗,结构-梁,暖通-风管,电气-桥架,给排水-无风管有水管)
当前专业为:{majorName}
是 - 继续,否 - 手动选择mvdlite文件
""",
"提示",
MessageBoxButton.YesNoCancel,
MessageBoxImage.Question);
string fileContent;
if (result == MessageBoxResult.Yes)
{
fileContent = Directory.GetFiles(dir, "*.mvdlite")
.FirstOrDefault(f => f.Contains(majorName)) switch
{
string file when !string.IsNullOrEmpty(file) => File.ReadAllText(file),
_ => throw new FileNotFoundException("未找到对应的MVDLite文件请检查目录或文件名。")
};
}
else if (result == MessageBoxResult.No)
{
OpenFileDialog dialog = new OpenFileDialog()
{
Filter = "MVDLite 文件 (*.mvdlite)|*.mvdlite",
Title = "选择 MVDLite 文件",
InitialDirectory = dir,
RestoreDirectory = true
};
if (dialog.ShowDialog() != true)
{
return;
}
fileContent = File.ReadAllText(dialog.FileName);
}
else
{
return;
}
Document.InvokeGroup(
_ =>
{
if (!NeedRemoveParameter(signParam))
{
return;
}
var componentSignSet = UiApplication.Application.Create.NewCategorySet();
var builtInCategories = Enum.GetValues(typeof(BuiltInCategory));
foreach (BuiltInCategory cate in builtInCategories)
{
try
{
var category = Category.GetCategory(Document, cate);
if (category is { CategoryType: CategoryType.Model, AllowsBoundParameters: true })
{
if (cate == BuiltInCategory.OST_Rooms || cate == BuiltInCategory.OST_Areas)
{
continue; //空间和区域不需要标识
}
componentSignSet.Insert(category);
}
}
//有的类别不支持这个方法获取
catch (Exception)
{
//sb.AppendLine($"{ex.Message}:{cate}");
}
}
Document.AddSharedParameter(signParam, componentSignSet, BuiltInParameterGroup.PG_IFC);
var allElements = Document.OfParentModelCollector().ToElements();
try
{
//sb.AppendLine($"标识,优化标识,匹配,比率,");
MvdData data = MvdLiteAssist.Parse(fileContent);
var groups = allElements.GroupBy(e => e.GetTypeId());
Document.Invoke(
_ =>
{
foreach (var g in groups)
{
string mapperName = GetMapperSign(g, data);
//if (elementGroup.Key == ElementId.InvalidElementId)//可能是地形
//{
// string name = string.Empty;
// if (elementGroup.FirstOrDefault().Category != null)
// {
// name = elementGroup.FirstOrDefault().Category.name;
// }
// else
// {
// name = elementGroup.FirstOrDefault().name;
// }
// mapperName = GetMapperSign(name, data);
//}
//else
//{
// mapperName = GetMapperSign(type.name, data);
// if (string.IsNullOrEmpty(mapperName))//如果类型名找不到标识,使用族名称
// {
// mapperName = GetMapperSign(type.FamilyName, data);
// }
//}
foreach (var elem in g)
{
var param = elem.LookupParameter(signParam);
param.SetValue(mapperName);
}
}
}, $"填写标识");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}, $"填写 {majorName} 构件标识");
MessageBox.Show("处理完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
//File.WriteAllText(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\匹配结果" + ".csv",
// sb.ToString(), Encoding.UTF8);
}
class Mapper(string name, string sign, int ratio)
{
public string Name { get; } = name;
public string Sign { get; } = sign;
public int Ratio { get; } = ratio;
public override string ToString()
{
return $"{Name} - {Sign} ({Ratio})";
}
}
//private StringBuilder sb = new StringBuilder();
//private List<Mapper> Mappers { get; componentSignSet; } = new List<Mapper>();
private string GetMapperSign(IGrouping<ElementId, Element> elementGroup, MvdData data)
{
if (elementGroup.Key == ElementId.InvalidElementId)
{
return string.Empty;
}
var definitions = MvdLiteAssist.GetElementSigns(data);
var categoryName = elementGroup.FirstOrDefault().Category?.Name;
//如果没有匹配到,使用类型名进行匹配
var type = Document.GetElement(elementGroup.Key) as ElementType;
var famName = type.FamilyName;
ConfigManager.ConfigFileBaseDir = Path.Combine(RvApp.AddInDirectory, "configs");
var segmenter = new JiebaSegmenter();
var subText = $"{type.Name} {famName} {categoryName}";
var replaceText = subText.ExtractChinese();
var segments = segmenter.Cut(replaceText).Distinct();
var combinedText = string.Join(" ", segments);
string resultName = string.Empty;
double minRatio = 30;
foreach (var defin in definitions)
{
var defText = defin.Replace(majorName, "");
var segmentsDef = segmenter.Cut(defText);
defText = string.Join(" ", segmentsDef);
var tempRatio = Fuzz.TokenSetRatio(defText, combinedText);
//var tempRatio = Fuzz.Ratio(defin, segment);
if (tempRatio >= minRatio)
{
//sb.AppendLine($"{defin},{defText},{combinedText},{tempRatio},");
minRatio = tempRatio;
resultName = defin;
}
}
return resultName;
}
//private static string ExtractChinese(string text)
//{
// if (string.IsNullOrEmpty(text)) return string.Empty;
// // 方法1正则简单常用
// return Regex.Replace(text, @"[^\u4e00-\u9fa5]", "");
// // 方法2遍历可扩展
// // var sb = new StringBuilder();
// // foreach (char c in text)
// // if (c >= 0x4e00 && c <= 0x9fa5)
// // sb.Append(c);
// // return sb.ToString();
//}
/// <summary>
/// 清理字符串中的括号、下划线、减号、加号等符号
/// </summary>
/// <param name="input">原始字符串</param>
/// <returns>清理后的字符串</returns>
public static string CleanSpecialChars(string input, string replacestr = " ")
{
if (string.IsNullOrEmpty(input))
return input;
// 正则模式说明:
// [()\[\]{}<>_-+] 匹配以下符号:
// 圆括号 ()、方括号 []、花括号 {}、尖括号 <>、下划线 _、减号 -、加号 +
// 注意:方括号内的特殊字符(如 [ ] - )需要转义或放在安全位置(如末尾)
string pattern = @"[()\[\]{}<>_+-]";
return Regex.Replace(input, pattern, replacestr);
}
private string GetBestMatchingSign(IGrouping<ElementId, Element> g, HashSet<string> definitions, string categoryName, string famName, ref string resultName)
{
//初步匹配
List<string> strings = [];
if (major == Major.Architecture || major == Major.Structure || major == Major.MasterPlan)
{
int mapperRatio = 30;
foreach (var mapper in definitions)
{
//var sourceIndex = Levenshtein.GetMatchingBlocks(mapper, categoryName).FirstOrDefault().SourcePos;
//var pos = mapper.Length - sourceIndex;
var tempRatio = Fuzz.WeightedRatio(mapper, $"{categoryName}");
if (tempRatio > 30)
{
if (tempRatio > mapperRatio)
{
mapperRatio = tempRatio;
resultName = mapper;
}
strings.Add(mapper);
}
}
}
else
{
strings = [.. definitions];
}
var subText = g.FirstOrDefault().Name;
var replaceText = subText.Replace('-', ' ').Replace('_', ' ').Replace('—', ' ');
var combinedText = $"{famName} {replaceText}";
int minRatio = 20;
foreach (var defin in strings)
{
//var tempRatio = Fuzz.TokenSortRatio(defin, replaceText);
var tempRatio = Fuzz.WeightedRatio(defin, combinedText);
if (tempRatio > minRatio)
{
//Mappers.Add(new(replaceText, defin, tempRatio));
minRatio = tempRatio;
resultName = defin;
}
}
//if (string.IsNullOrEmpty(resultName))
//{
// resultName = "无标识";
//}
return resultName;
}
/// <summary>
/// 需要移除现有的标识参数
/// </summary>
/// <param name="paramName"></param>
/// <returns></returns>
private bool NeedRemoveParameter(string paramName)
{
var dict = SharedParameterAssists.GetParameterElementBindings(Document)
.Where(d => d.Key.Name == paramName);
if (dict.Any())
{
var result = MessageBox.Show(
$"已存在一个或多个{paramName}的参数定义,将全部进行删除,并重新添加,是否继续?",
"警告",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
Document.Invoke(
_ =>
{
Document.Delete(dict.Select(d => d.Key).Select(p => p.Id).ToList());
},
$"删除现有{paramName}参数");
}
else
{
return false;
}
}
return true;
}
}
}