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;
+ }
+}