From 8f51b53f5bc6ed7f9c3dc78cead1cd9eb9609f60 Mon Sep 17 00:00:00 2001 From: sladro Date: Tue, 23 Dec 2025 17:34:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=A0=87=E6=B3=A8=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8=E5=85=83?= =?UTF-8?q?=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cad/DrawingStyleManager.cs | 16 ++ Cad/FeatureDrivenDrawer.cs | 99 +++++++- Cad/TemplateDrawingService.cs | 437 ++++++++++++++++++++++++++++++++++ UI/ParamDrawingPanel.cs | 12 + repro.cs | 89 +++++++ 5 files changed, 647 insertions(+), 6 deletions(-) create mode 100644 repro.cs diff --git a/Cad/DrawingStyleManager.cs b/Cad/DrawingStyleManager.cs index 34bfa7d..64f178c 100644 --- a/Cad/DrawingStyleManager.cs +++ b/Cad/DrawingStyleManager.cs @@ -88,6 +88,22 @@ namespace CadParamPluging.Cad var layerTbl = (LayerTable)_tr.GetObject(_db.LayerTableId, OpenMode.ForRead); if (layerTbl.Has(layerName)) { + try + { + var layerId = layerTbl[layerName]; + var existingLayer = (LayerTableRecord)_tr.GetObject(layerId, OpenMode.ForWrite); + if (existingLayer != null) + { + // Make sure the layer is visible in the template. + existingLayer.IsOff = false; + existingLayer.IsFrozen = false; + existingLayer.IsLocked = false; + } + } + catch + { + // ignore + } return; } diff --git a/Cad/FeatureDrivenDrawer.cs b/Cad/FeatureDrivenDrawer.cs index 2760f03..cef1049 100644 --- a/Cad/FeatureDrivenDrawer.cs +++ b/Cad/FeatureDrivenDrawer.cs @@ -230,7 +230,20 @@ namespace CadParamPluging.Cad var db = ctx.Database; var tr = ctx.Transaction; - var btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); + + // Important: templates & cleanup operate on ModelSpace; draw there to avoid PaperSpace-related dimension issues. + var prevDb = HostApplicationServices.WorkingDatabase; + HostApplicationServices.WorkingDatabase = db; + BlockTableRecord btr; + try + { + var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); + btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); + } + finally + { + HostApplicationServices.WorkingDatabase = prevDb; + } var context = new DrawingContext { @@ -1529,11 +1542,36 @@ namespace CadParamPluging.Cad { try { - double rotRad = rotationDeg * Math.PI / 180.0; - var dim = new RotatedDimension(rotRad, pt1, pt2, dimLinePt, textOverride, ctx.Db.Dimstyle); - ctx.Style?.Apply(dim, DrawingStyleManager.Role.Dimension); - ctx.Btr.AppendEntity(dim); - ctx.Tr.AddNewlyCreatedDBObject(dim, true); + var prevDb = HostApplicationServices.WorkingDatabase; + HostApplicationServices.WorkingDatabase = ctx.Db; + try + { + double rotRad = rotationDeg * Math.PI / 180.0; + + // Use current drawing's dimension style (template-controlled). + var dim = new RotatedDimension(rotRad, pt1, pt2, dimLinePt, textOverride, ctx.Db.Dimstyle); + try { dim.SetDatabaseDefaults(); } catch { } + try { dim.Normal = Vector3d.ZAxis; } catch { } + TryApplyDimSizeOverrides(dim); + ctx.Style?.Apply(dim, DrawingStyleManager.Role.Dimension); + ctx.Btr.AppendEntity(dim); + ctx.Tr.AddNewlyCreatedDBObject(dim, true); + + // Ensure dimension graphics (including text) are computed and persisted in the DWG. + // Some CAD viewers may otherwise show '*' placeholders. + try + { + dim.RecomputeDimensionBlock(true); + } + catch + { + // ignore + } + } + finally + { + HostApplicationServices.WorkingDatabase = prevDb; + } } catch { @@ -1541,6 +1579,55 @@ namespace CadParamPluging.Cad } } + private static void TryApplyDimSizeOverrides(Dimension dim) + { + if (dim == null) + { + return; + } + + // Template dimstyle may have very small DIMTXT; apply per-dimension overrides to keep it readable. + // Values are in drawing units (typically mm). + TrySetDimProp(dim, "Dimtxt", 3.5); + TrySetDimProp(dim, "Dimasz", 2.5); + TrySetDimProp(dim, "Dimgap", 1.0); + TrySetDimProp(dim, "Dimexe", 1.0); + TrySetDimProp(dim, "Dimexo", 1.0); + } + + private static void TrySetDimProp(object obj, string propName, double value) + { + if (obj == null || string.IsNullOrWhiteSpace(propName)) + { + return; + } + + try + { + var prop = obj.GetType().GetProperty(propName); + if (prop == null || !prop.CanWrite) + { + return; + } + + if (prop.PropertyType == typeof(double)) + { + prop.SetValue(obj, value, null); + return; + } + + if (prop.PropertyType == typeof(float)) + { + prop.SetValue(obj, (float)value, null); + return; + } + } + catch + { + // ignore + } + } + private static void TryLoadLinetype(Database db, Transaction tr, string linetypeName) { try diff --git a/Cad/TemplateDrawingService.cs b/Cad/TemplateDrawingService.cs index 2b98a26..147832f 100644 --- a/Cad/TemplateDrawingService.cs +++ b/Cad/TemplateDrawingService.cs @@ -861,6 +861,10 @@ namespace CadParamPluging.Cad public Extents3d? OriginalExtents { get; set; } } + private static readonly Regex DimensionPlaceholderRegex = new Regex( + @"(%%c|[Φφ∅Ø]|\\U\+03A6|\\U\+2205|\\U\+00D8)\s*\*", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + /// /// 删除模板中原有的图纸图形,保留右上角区域的内容(如粗糙度标注)。 /// @@ -957,6 +961,439 @@ namespace CadParamPluging.Cad return result; } + /// + /// 删除模板中残留的“尺寸占位符”文本(例如 (Φ*)),避免与新生成的真实尺寸标注混淆。 + /// + /// + /// 仅清理文本类实体(DBText/MText/块属性),不影响真正的 Dimension 实体。 + /// + public static int RemoveTemplateDimensionPlaceholderTexts( + CadContext ctx, + Extents3d? graphicExtents = null, + double topRightThreshold = 0.70, + bool scanBlockDefinitions = false) + { + if (ctx == null) + { + throw new ArgumentNullException(nameof(ctx)); + } + + var db = ctx.Database; + var tr = ctx.Transaction; + + var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); + var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); + + var allEntities = ms.Cast() + .Select(id => tr.GetObject(id, OpenMode.ForRead, false) as Entity) + .Where(e => e != null) + .ToList(); + + var targetExtents = graphicExtents + ?? ComputeWhiteFrameExtentsFromEntities(tr, allEntities) + ?? ComputeLayoutExtents(allEntities); + + var removed = 0; + foreach (ObjectId id in ms) + { + var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity; + if (ent == null || ent.IsErased) + { + continue; + } + + if (targetExtents.HasValue && IsInTopRightCorner(ent, targetExtents.Value, topRightThreshold)) + { + continue; + } + + if (!IsWithin(ent, targetExtents)) + { + continue; + } + + if (ent is DBText t) + { + if (LooksLikeDimensionPlaceholder(t.TextString)) + { + ent.Erase(true); + removed++; + } + continue; + } + + if (ent is MText mt) + { + var plain = SimplifyMText(mt.Contents); + if (LooksLikeDimensionPlaceholder(plain) || LooksLikeDimensionPlaceholder(mt.Contents)) + { + ent.Erase(true); + removed++; + } + continue; + } + + if (ent is BlockReference br) + { + removed += RemovePlaceholderAttributes(tr, br, targetExtents, topRightThreshold); + + // Keep default behavior conservative: do not edit block definitions unless explicitly enabled. + if (scanBlockDefinitions && IsWithin(br, targetExtents)) + { + removed += RemovePlaceholderTextsInBlockDefinition(tr, br.BlockTableRecord, new HashSet()); + } + } + } + + return removed; + } + + private static int RemovePlaceholderAttributes(Transaction tr, BlockReference br, Extents3d? targetExtents, double topRightThreshold) + { + if (tr == null || br == null) + { + return 0; + } + + var removed = 0; + foreach (ObjectId attId in br.AttributeCollection) + { + var att = tr.GetObject(attId, OpenMode.ForWrite, false) as AttributeReference; + if (att == null || att.IsErased) + { + continue; + } + + if (targetExtents.HasValue && IsInTopRightCorner(att, targetExtents.Value, topRightThreshold)) + { + continue; + } + + if (!IsWithin(att, targetExtents)) + { + continue; + } + + if (LooksLikeDimensionPlaceholder(att.TextString)) + { + try + { + att.Erase(true); + removed++; + } + catch + { + // ignore + } + } + } + + return removed; + } + + private static int RemovePlaceholderTextsInBlockDefinition(Transaction tr, ObjectId blockId, HashSet visited) + { + if (tr == null || blockId.IsNull) + { + return 0; + } + + if (visited == null) + { + visited = new HashSet(); + } + + if (visited.Contains(blockId)) + { + return 0; + } + + visited.Add(blockId); + + var btr = tr.GetObject(blockId, OpenMode.ForWrite, false) as BlockTableRecord; + if (btr == null) + { + return 0; + } + + var removed = 0; + foreach (ObjectId childId in btr) + { + var child = tr.GetObject(childId, OpenMode.ForWrite, false) as Entity; + if (child == null || child.IsErased) + { + continue; + } + + if (child is DBText t) + { + if (LooksLikeDimensionPlaceholder(t.TextString)) + { + child.Erase(true); + removed++; + } + continue; + } + + if (child is MText mt) + { + var plain = SimplifyMText(mt.Contents); + if (LooksLikeDimensionPlaceholder(plain) || LooksLikeDimensionPlaceholder(mt.Contents)) + { + child.Erase(true); + removed++; + } + continue; + } + + if (child is BlockReference br) + { + removed += RemovePlaceholderAttributes(tr, br, null, 1.0); + removed += RemovePlaceholderTextsInBlockDefinition(tr, br.BlockTableRecord, visited); + } + } + + return removed; + } + + private static bool LooksLikeDimensionPlaceholder(string raw) + { + if (string.IsNullOrWhiteSpace(raw)) + { + return false; + } + + var s = (raw ?? string.Empty).Replace(" ", string.Empty).Replace(" ", string.Empty); + if (!s.Contains("*")) + { + return false; + } + + // Typical placeholder examples: (Φ*), Φ*, %%c*, (\U+03A6*) + return DimensionPlaceholderRegex.IsMatch(s); + } + + /// + /// 删除白色线框范围内上半部分的所有内容(用于清理模板残留的图形/占位符)。 + /// 保留:右上角区域(如粗糙度标注)与下半部分(附注/表格)。 + /// + /// 保留下半部比例(0.5 表示保留下半部分) + /// 右上角保留阈值,0.70表示X和Y都超过70%的位置 + public static int RemoveWhiteFrameUpperContent(CadContext ctx, double keepBottomRatio = 0.50, double topRightThreshold = 0.70) + { + if (ctx == null) + { + throw new ArgumentNullException(nameof(ctx)); + } + + keepBottomRatio = Math.Max(0.0, Math.Min(1.0, keepBottomRatio)); + + var db = ctx.Database; + var tr = ctx.Transaction; + + var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); + var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); + + var allEntities = ms.Cast() + .Select(id => tr.GetObject(id, OpenMode.ForRead, false) as Entity) + .Where(e => e != null && !e.IsErased) + .ToList(); + + var whiteFrame = ComputeWhiteFrameExtentsFromEntities(tr, allEntities) ?? ComputeLayoutExtents(allEntities); + if (!whiteFrame.HasValue) + { + return 0; + } + + var frame = whiteFrame.Value; + var height = frame.MaxPoint.Y - frame.MinPoint.Y; + if (height <= 0) + { + return 0; + } + + var cutY = frame.MinPoint.Y + height * keepBottomRatio; + + var removed = 0; + foreach (ObjectId id in ms) + { + var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity; + if (ent == null || ent.IsErased) + { + continue; + } + + // Keep white frame boundary itself. + if (IsWhiteFrameEntity(ent, frame, tr)) + { + continue; + } + + if (!IsWithin(ent, frame)) + { + continue; + } + + // Keep top-right corner content. + if (IsInTopRightCorner(ent, frame, topRightThreshold)) + { + continue; + } + + if (!TryGetEntityCenterY(ent, out var centerY)) + { + continue; + } + + // Keep bottom area (notes/table). + if (centerY < cutY) + { + continue; + } + + try + { + ent.Erase(true); + removed++; + } + catch + { + // ignore + } + } + + return removed; + } + + private static bool TryGetEntityCenterY(Entity ent, out double centerY) + { + centerY = 0; + if (ent == null) + { + return false; + } + + try + { + var ext = ent.GeometricExtents; + centerY = (ext.MinPoint.Y + ext.MaxPoint.Y) / 2.0; + return true; + } + catch + { + return false; + } + } + + private static bool IsWithin(Entity ent, Extents3d frame) + { + if (ent == null) + { + return false; + } + + try + { + var ext = ent.GeometricExtents; + return Intersects(ext, frame); + } + catch + { + return true; + } + } + + private static bool IsWhiteFrameEntity(Entity ent, Extents3d frame, Transaction tr) + { + if (ent == null) + { + return false; + } + + if (!(ent is Line) && !(ent is Polyline)) + { + return false; + } + + if (!IsWhiteColor(ent, tr)) + { + return false; + } + + try + { + var ext = ent.GeometricExtents; + var frameW = frame.MaxPoint.X - frame.MinPoint.X; + var frameH = frame.MaxPoint.Y - frame.MinPoint.Y; + var tol = Math.Max(frameW, frameH) * 0.01; // 1% + + // On outer border lines. + var nearLeft = Math.Abs(ext.MinPoint.X - frame.MinPoint.X) < tol; + var nearRight = Math.Abs(ext.MaxPoint.X - frame.MaxPoint.X) < tol; + var nearBottom = Math.Abs(ext.MinPoint.Y - frame.MinPoint.Y) < tol; + var nearTop = Math.Abs(ext.MaxPoint.Y - frame.MaxPoint.Y) < tol; + + // Rough heuristic: if it touches two sides or spans most of a side, treat as frame. + if ((nearLeft && nearBottom) || (nearLeft && nearTop) || (nearRight && nearBottom) || (nearRight && nearTop)) + { + return true; + } + + if (ent is Line) + { + var spansWidth = (ext.MaxPoint.X - ext.MinPoint.X) > frameW * 0.90; + var spansHeight = (ext.MaxPoint.Y - ext.MinPoint.Y) > frameH * 0.90; + if ((nearTop || nearBottom) && spansWidth) + { + return true; + } + if ((nearLeft || nearRight) && spansHeight) + { + return true; + } + } + + if (ent is Polyline) + { + var spansWidth = (ext.MaxPoint.X - ext.MinPoint.X) > frameW * 0.90; + var spansHeight = (ext.MaxPoint.Y - ext.MinPoint.Y) > frameH * 0.90; + if (spansWidth && spansHeight) + { + return true; + } + } + } + catch + { + return false; + } + + return false; + } + + private static bool IsWithin(Entity ent, Extents3d? targetExtents) + { + if (ent == null) + { + return false; + } + + if (!targetExtents.HasValue) + { + return true; + } + + try + { + var ext = ent.GeometricExtents; + return Intersects(ext, targetExtents.Value); + } + catch + { + return true; + } + } + private static bool IsOuterFrameEntity(Entity ent, Extents3d? layoutExtents, Transaction tr = null) { if (ent == null || !layoutExtents.HasValue) diff --git a/UI/ParamDrawingPanel.cs b/UI/ParamDrawingPanel.cs index 2ba0a95..4b6290f 100644 --- a/UI/ParamDrawingPanel.cs +++ b/UI/ParamDrawingPanel.cs @@ -657,6 +657,16 @@ namespace CadParamPluging.UI var removeResult = TemplateDrawingService.RemoveTemplateOriginalDrawing(ctx); AppendLog($"已删除原有图形: 红色外框={removeResult.OuterFrameErased}, CAXA图层={removeResult.CaxaLayerErased}, 标注={removeResult.DimensionLayerErased}, 保留右上角={removeResult.DimensionLayerKept}"); + // 删除白色线框内“上半部分”的所有内容(保留右上角与下半部分附注/表格) + var upperRemoved = TemplateDrawingService.RemoveWhiteFrameUpperContent( + ctx, + keepBottomRatio: 0.50, + topRightThreshold: 0.70); + if (upperRemoved > 0) + { + AppendLog($"已清理白框上半部内容: {upperRemoved} 处"); + } + var scaleFactor = ParseScaleFactor(bag.GetString("DrawingScale")); // 根据模板参数绘制半剖视图,居中放置于原图纸位置 @@ -709,6 +719,8 @@ namespace CadParamPluging.UI } ctx.Commit(); + + try { doc.Editor.Regen(); } catch { } } AppendLog("图纸生成完成。"); diff --git a/repro.cs b/repro.cs new file mode 100644 index 0000000..a51b16a --- /dev/null +++ b/repro.cs @@ -0,0 +1,89 @@ + +using System; +using System.Globalization; + +public class Program +{ + public static void Main() + { + double diameter = 100.5; + string res = FormatDimNumber(diameter); + Console.WriteLine($"Diameter 100.5 -> '{res}'"); + + diameter = 100; + res = FormatDimNumber(diameter); + Console.WriteLine($"Diameter 100 -> '{res}'"); + + diameter = 0; + res = FormatDimNumber(diameter); + Console.WriteLine($"Diameter 0 -> '{res}'"); + + // Test BuildDimensionText + string dimText = BuildDimensionText($"%%c{FormatDimNumber(100)}", 0.1, -0.1); + Console.WriteLine($"Text with tol: '{dimText}'"); + + dimText = BuildDimensionText($"%%c{FormatDimNumber(100)}", null, null); + Console.WriteLine($"Text no tol: '{dimText}'"); + + // Test ScaleParamBag logic (conceptually) + double scale = 0.5; + double val = 123.45; + string scaledStr = (val * scale).ToString("F2"); + Console.WriteLine($"Scaled 123.45 * 0.5 -> '{scaledStr}'"); + + if (double.TryParse(scaledStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var v)) + { + Console.WriteLine($"Parsed back (Invariant): {v}"); + } + else if (double.TryParse(scaledStr, NumberStyles.Float, CultureInfo.CurrentCulture, out v)) + { + Console.WriteLine($"Parsed back (Current): {v}"); + } + else + { + Console.WriteLine("Parse failed"); + } + } + + private static string FormatDimNumber(double value) + { + try + { + return value.ToString("0.###"); + } + catch + { + return value.ToString(); + } + } + + private static string BuildDimensionText(string baseText, double? tolPlus, double? tolMinus) + { + if (!tolPlus.HasValue && !tolMinus.HasValue) + { + return baseText; + } + + var result = baseText; + + // 公差格式: 基本尺寸 +上差/-下差 + if (tolPlus.HasValue && tolMinus.HasValue) + { + var plusStr = tolPlus.Value >= 0 ? $"+{tolPlus.Value}" : $"{tolPlus.Value}"; + var minusStr = tolMinus.Value >= 0 ? $"+{tolMinus.Value}" : $"{tolMinus.Value}"; + result += $"({plusStr}/{minusStr})"; + } + else if (tolPlus.HasValue) + { + var plusStr = tolPlus.Value >= 0 ? $"+{tolPlus.Value}" : $"{tolPlus.Value}"; + result += $"({plusStr})"; + } + else if (tolMinus.HasValue) + { + var minusStr = tolMinus.Value >= 0 ? $"+{tolMinus.Value}" : $"{tolMinus.Value}"; + result += $"({minusStr})"; + } + + return result; + } +}