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 { /// /// 建筑标识预处理 /// [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 Mappers { get; componentSignSet; } = new List(); private string GetMapperSign(IGrouping 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(); //} /// /// 清理字符串中的括号、下划线、减号、加号等符号 /// /// 原始字符串 /// 清理后的字符串 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 g, HashSet definitions, string categoryName, string famName, ref string resultName) { //初步匹配 List 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; } /// /// 需要移除现有的标识参数 /// /// /// 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; } } }