diff --git a/Cad/TemplateDrawingService.cs b/Cad/TemplateDrawingService.cs index 0970113..3c20a25 100644 --- a/Cad/TemplateDrawingService.cs +++ b/Cad/TemplateDrawingService.cs @@ -19,6 +19,16 @@ namespace CadParamPluging.Cad private static readonly Regex MTextFormatRegex = new Regex(@"\\[A-Za-z]+[^;]*;", RegexOptions.Compiled); + // 匹配MText中残留的格式代码片段(如 Xt31.278; 或 qj,tz; 等) + // 匹配模式:1-4个字母开头,后面可以是数字/点/逗号/字母的组合,最后以分号结尾 + private static readonly Regex MTextResidualFormatRegex = new Regex(@"^[A-Za-z]{1,4}[\d.,A-Za-z]*;", RegexOptions.Compiled); + + // 外框图层名称集合 + private static readonly HashSet OuterFrameLayerNames = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "图框", "边框", "外框", "FRAME", "BORDER", "粗实线" + }; + private static readonly Regex CaxaLayerRegex = new Regex(@"^CAXA\d$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly HashSet DimensionLayerNames = new HashSet(StringComparer.OrdinalIgnoreCase) @@ -186,9 +196,46 @@ namespace CadParamPluging.Cad s = s.Replace("{", string.Empty).Replace("}", string.Empty); s = MTextFormatRegex.Replace(s, string.Empty); + + // 处理每行开头可能残留的格式代码片段(如 Xt31.278; 或 qj,tz;) + var lines = s.Split(new[] { '\n' }, StringSplitOptions.None); + for (var i = 0; i < lines.Length; i++) + { + lines[i] = CleanLineResidualFormat(lines[i]); + } + s = string.Join("\n", lines); + return s; } + private static string CleanLineResidualFormat(string line) + { + if (string.IsNullOrEmpty(line)) + { + return line; + } + + // 循环清理行首的格式代码残留,直到没有匹配为止 + var result = line; + while (true) + { + var trimmed = result.TrimStart(); + var match = MTextResidualFormatRegex.Match(trimmed); + if (match.Success && match.Index == 0) + { + // 移除匹配的格式代码 + var leadingSpaces = result.Length - result.TrimStart().Length; + result = new string(' ', leadingSpaces) + trimmed.Substring(match.Length); + } + else + { + break; + } + } + + return result; + } + public sealed class NoteApplyResult { public bool Applied { get; set; } @@ -203,6 +250,7 @@ namespace CadParamPluging.Cad public int Score; public string Kind; public string PlainText; + public string OriginalContents; public Action Apply; } @@ -340,7 +388,7 @@ namespace CadParamPluging.Cad if (ent is MText mt) { var plain = SimplifyMText(mt.Contents); - AddNoteCandidateIfMatch(plain, "MText", mt.ObjectId, tr, candidates); + AddNoteCandidateIfMatch(plain, "MText", mt.ObjectId, tr, candidates, mt.Contents); return; } @@ -391,7 +439,7 @@ namespace CadParamPluging.Cad } } - private static void AddNoteCandidateIfMatch(string plainText, string kind, ObjectId id, Transaction tr, List candidates) + private static void AddNoteCandidateIfMatch(string plainText, string kind, ObjectId id, Transaction tr, List candidates, string originalContents = null) { if (!IsLikelyNoteText(plainText)) { @@ -409,7 +457,8 @@ namespace CadParamPluging.Cad Score = score, Kind = kind, PlainText = plainText, - Apply = rendered => ApplyNoteTextToObject(tr, id, kind, rendered) + OriginalContents = originalContents, + Apply = rendered => ApplyNoteTextToObject(tr, id, kind, rendered, originalContents) }); } @@ -481,7 +530,7 @@ namespace CadParamPluging.Cad .Select(s => (s ?? string.Empty).Trim()); } - private static void ApplyNoteTextToObject(Transaction tr, ObjectId id, string kind, string rendered) + private static void ApplyNoteTextToObject(Transaction tr, ObjectId id, string kind, string rendered, string originalContents = null) { if (tr == null || id.IsNull) { @@ -494,7 +543,15 @@ namespace CadParamPluging.Cad var obj = tr.GetObject(id, OpenMode.ForWrite, false); if (obj is MText mt) { - mt.Contents = ToMTextContents(rendered); + // 如果有原始内容,在原始内容中直接替换占位符以保留格式 + if (!string.IsNullOrEmpty(originalContents)) + { + mt.Contents = ReplaceStarsInOriginalContents(originalContents, rendered); + } + else + { + mt.Contents = ToMTextContents(rendered); + } return; } @@ -510,6 +567,135 @@ namespace CadParamPluging.Cad } } + private static string ReplaceStarsInOriginalContents(string originalContents, string renderedPlainText) + { + if (string.IsNullOrEmpty(originalContents) || string.IsNullOrEmpty(renderedPlainText)) + { + return ToMTextContents(renderedPlainText); + } + + // 从SimplifyMText处理后的纯文本和渲染后的纯文本中提取*被替换成什么 + var plainTemplate = SimplifyMText(originalContents); + + // 提取每个占位符被替换成的值 + var replacements = ExtractPlaceholderReplacements(plainTemplate, renderedPlainText); + if (replacements.Count == 0) + { + return ToMTextContents(renderedPlainText); + } + + // 在原始MText内容中直接替换*占位符 + var result = new System.Text.StringBuilder(); + var replacementIndex = 0; + + for (var i = 0; i < originalContents.Length;) + { + var c = originalContents[i]; + + // 检查是否是占位符* + if (c == '*') + { + // 计算连续的*数量 + var starCount = 0; + var j = i; + while (j < originalContents.Length && originalContents[j] == '*') + { + starCount++; + j++; + } + + if (starCount == 4) + { + // 保留**** + result.Append("****"); + i += 4; + } + else + { + // 替换每个* + for (var k = 0; k < starCount; k++) + { + if (replacementIndex < replacements.Count) + { + result.Append(replacements[replacementIndex]); + replacementIndex++; + } + else + { + result.Append('*'); + } + } + i += starCount; + } + } + else + { + result.Append(c); + i++; + } + } + + return result.ToString(); + } + + private static List ExtractPlaceholderReplacements(string plainTemplate, string renderedText) + { + var replacements = new List(); + + // 将两个文本按相同方式处理 + var template = (plainTemplate ?? string.Empty) + .Replace("\r\n", "\n") + .Replace("\r", "\n"); + var rendered = (renderedText ?? string.Empty) + .Replace("\r\n", "\n") + .Replace("\r", "\n"); + + var ti = 0; // template index + var ri = 0; // rendered index + + while (ti < template.Length && ri < rendered.Length) + { + var tc = template[ti]; + + if (tc == '*') + { + // 检查是否是**** + var starCount = 0; + var j = ti; + while (j < template.Length && template[j] == '*') + { + starCount++; + j++; + } + + if (starCount == 4) + { + // ****保持不变,rendered中也应该有**** + ti += 4; + ri += 4; + } + else + { + // 每个*对应rendered中的一个字符 + for (var k = 0; k < starCount && ri < rendered.Length; k++) + { + replacements.Add(rendered[ri].ToString()); + ti++; + ri++; + } + } + } + else + { + // 非*字符,两边应该相同,直接跳过 + ti++; + ri++; + } + } + + return replacements; + } + private static string ToMTextContents(string plainText) { if (string.IsNullOrEmpty(plainText)) @@ -670,6 +856,7 @@ namespace CadParamPluging.Cad public int CaxaLayerErased { get; set; } public int DimensionLayerErased { get; set; } public int DimensionLayerKept { get; set; } + public int OuterFrameErased { get; set; } public Point3d OriginalCenter { get; set; } public Extents3d? OriginalExtents { get; set; } } @@ -714,6 +901,9 @@ namespace CadParamPluging.Cad // 使用CAXA图层范围作为图形区域,用于判断右上角 var graphicExtents = caxaExtents ?? layoutExtents; + // 先计算红色线框的范围 + var redFrameExtents = ComputeRedFrameExtents(tr, allEntities); + foreach (var ent in allEntities) { if (ent.IsErased) @@ -723,6 +913,18 @@ namespace CadParamPluging.Cad var layerName = GetLayerName(tr, ent.LayerId); + // 删除红色外框线(红色的Line/Polyline,且在红色框的边界上) + if (IsOuterFrameEntity(ent, redFrameExtents, tr)) + { + var entForWrite = tr.GetObject(ent.ObjectId, OpenMode.ForWrite) as Entity; + if (entForWrite != null) + { + entForWrite.Erase(true); + result.OuterFrameErased++; + } + continue; + } + if (IsCaxaLayer(layerName)) { var entForWrite = tr.GetObject(ent.ObjectId, OpenMode.ForWrite) as Entity; @@ -755,6 +957,154 @@ namespace CadParamPluging.Cad return result; } + private static bool IsOuterFrameEntity(Entity ent, Extents3d? layoutExtents, Transaction tr = null) + { + if (ent == null || !layoutExtents.HasValue) + { + return false; + } + + // 必须是Line或Polyline + if (!(ent is Line) && !(ent is Polyline)) + { + return false; + } + + // 检查颜色是否为红色(ColorIndex=1,或者通过图层颜色) + if (!IsRedColor(ent, tr)) + { + return false; + } + + // 检查是否位于最外围(实体的范围接近或等于整体布局范围) + try + { + var ext = ent.GeometricExtents; + var layout = layoutExtents.Value; + + var layoutWidth = layout.MaxPoint.X - layout.MinPoint.X; + var layoutHeight = layout.MaxPoint.Y - layout.MinPoint.Y; + + // 容差值(放宽到5%) + var tolerance = Math.Max(layoutWidth, layoutHeight) * 0.05; + + // 检查Line是否位于布局边界上 + if (ent is Line line) + { + var startPt = line.StartPoint; + var endPt = line.EndPoint; + + // 检查是否是水平线(上边或下边) + var isHorizontal = Math.Abs(startPt.Y - endPt.Y) < tolerance; + if (isHorizontal) + { + // 检查Y坐标是否在布局的上边界或下边界 + var isTopEdge = Math.Abs(startPt.Y - layout.MaxPoint.Y) < tolerance; + var isBottomEdge = Math.Abs(startPt.Y - layout.MinPoint.Y) < tolerance; + if (isTopEdge || isBottomEdge) + { + return true; + } + } + + // 检查是否是垂直线(左边或右边) + var isVertical = Math.Abs(startPt.X - endPt.X) < tolerance; + if (isVertical) + { + // 检查X坐标是否在布局的左边界或右边界 + var isLeftEdge = Math.Abs(startPt.X - layout.MinPoint.X) < tolerance; + var isRightEdge = Math.Abs(startPt.X - layout.MaxPoint.X) < tolerance; + if (isLeftEdge || isRightEdge) + { + return true; + } + } + } + + // Polyline可能是完整的矩形外框 + if (ent is Polyline poly) + { + var entWidth = ext.MaxPoint.X - ext.MinPoint.X; + var entHeight = ext.MaxPoint.Y - ext.MinPoint.Y; + if (entWidth > layoutWidth * 0.90 && entHeight > layoutHeight * 0.90) + { + return true; + } + } + } + catch + { + return false; + } + + return false; + } + + private static bool IsRedColor(Entity ent, Transaction tr) + { + if (ent == null) + { + return false; + } + + // 检查实体自身颜色 + var colorIndex = ent.ColorIndex; + + // ColorIndex=1 是红色 + if (colorIndex == 1) + { + return true; + } + + // ColorIndex=256 表示 ByLayer,需要检查图层颜色 + if (colorIndex == 256 && tr != null) + { + try + { + var layer = tr.GetObject(ent.LayerId, OpenMode.ForRead) as LayerTableRecord; + if (layer != null && layer.Color.ColorIndex == 1) + { + return true; + } + } + catch + { + // 忽略 + } + } + + // 检查Color属性 + try + { + var color = ent.Color; + if (color != null) + { + // 红色的RGB大约是(255, 0, 0)或接近 + if (color.ColorIndex == 1) + { + return true; + } + if (color.ColorMethod == Autodesk.AutoCAD.Colors.ColorMethod.ByColor) + { + var r = color.Red; + var g = color.Green; + var b = color.Blue; + // 红色:R高,G和B低 + if (r > 200 && g < 100 && b < 100) + { + return true; + } + } + } + } + catch + { + // 忽略 + } + + return false; + } + private static string GetLayerName(Transaction tr, ObjectId layerId) { if (layerId.IsNull) @@ -845,6 +1195,57 @@ namespace CadParamPluging.Cad return result; } + private static Extents3d? ComputeRedFrameExtents(Transaction tr, IEnumerable entities) + { + Extents3d? result = null; + + foreach (var ent in entities) + { + if (ent == null || ent.IsErased) + { + continue; + } + + // 只处理红色的Line或Polyline + if (!(ent is Line) && !(ent is Polyline)) + { + continue; + } + + if (!IsRedColor(ent, tr)) + { + continue; + } + + try + { + var ext = ent.GeometricExtents; + if (result == null) + { + result = ext; + } + else + { + result = new Extents3d( + new Point3d( + Math.Min(result.Value.MinPoint.X, ext.MinPoint.X), + Math.Min(result.Value.MinPoint.Y, ext.MinPoint.Y), + Math.Min(result.Value.MinPoint.Z, ext.MinPoint.Z)), + new Point3d( + Math.Max(result.Value.MaxPoint.X, ext.MaxPoint.X), + Math.Max(result.Value.MaxPoint.Y, ext.MaxPoint.Y), + Math.Max(result.Value.MaxPoint.Z, ext.MaxPoint.Z))); + } + } + catch + { + // 忽略 + } + } + + return result; + } + private static Extents3d? ComputeCaxaLayerExtents(Transaction tr, IEnumerable entities) { Extents3d? result = null; diff --git a/UI/ParamDrawingPanel.cs b/UI/ParamDrawingPanel.cs index cc03aa4..b813172 100644 --- a/UI/ParamDrawingPanel.cs +++ b/UI/ParamDrawingPanel.cs @@ -614,7 +614,7 @@ namespace CadParamPluging.UI // 删除模板中原有的图纸图形(CAXA图层和尺寸标注,保留右上角) var removeResult = TemplateDrawingService.RemoveTemplateOriginalDrawing(ctx); - AppendLog($"已删除原有图形: CAXA图层={removeResult.CaxaLayerErased}, 标注={removeResult.DimensionLayerErased}, 保留右上角={removeResult.DimensionLayerKept}"); + AppendLog($"已删除原有图形: 红色外框={removeResult.OuterFrameErased}, CAXA图层={removeResult.CaxaLayerErased}, 标注={removeResult.DimensionLayerErased}, 保留右上角={removeResult.DimensionLayerKept}"); // 根据模板参数绘制半剖视图,居中放置于原图纸位置 HalfSectionDrawer.Draw(