CadParamPluging/Cad/TemplateDrawingService.cs

661 lines
21 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;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using CadParamPluging.Common;
using CadParamPluging.Domain.Models;
namespace CadParamPluging.Cad
{
public static class TemplateDrawingService
{
private static readonly Regex MatchFieldRegex = new Regex(
@"^\s*(交付状态|工艺方法|结构特征|特殊条件)\s*[:]\s*(.+?)\s*$",
RegexOptions.Compiled);
private static readonly Regex MTextFormatRegex = new Regex(@"\\[A-Za-z]+[^;]*;", RegexOptions.Compiled);
public static Document CreateDocumentFromTemplate(TemplateInfo template)
{
var doc = Application.DocumentManager.Add(template.FilePath);
Application.DocumentManager.MdiActiveDocument = doc;
return doc;
}
/// <summary>
/// 移除模板中用于“匹配模板/图纸”的参数标注文本(交付状态/工艺方法/结构特征/特殊条件)。
/// 仅清理当前生成图纸中目标空间Layout 或 ModelSpace里的文本实体/块属性。
/// </summary>
public static int RemoveMatchParameterAnnotations(CadContext ctx, string layoutName, bool scanModelSpace)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}
var db = ctx.Database;
var tr = ctx.Transaction;
if (scanModelSpace)
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
return EraseMatchTextEntities(tr, ms);
}
if (string.IsNullOrWhiteSpace(layoutName))
{
return 0;
}
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
ObjectId layoutId = ObjectId.Null;
foreach (DBDictionaryEntry entry in layoutDict)
{
if (string.Equals(entry.Key, layoutName, StringComparison.OrdinalIgnoreCase))
{
layoutId = entry.Value;
break;
}
}
if (layoutId.IsNull)
{
return 0;
}
var layout = (Layout)tr.GetObject(layoutId, OpenMode.ForRead);
var btr = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForWrite);
return EraseMatchTextEntities(tr, btr);
}
private static int EraseMatchTextEntities(Transaction tr, BlockTableRecord btr)
{
if (tr == null || btr == null)
{
return 0;
}
var removed = 0;
var ids = btr.Cast<ObjectId>().ToList();
foreach (var id in ids)
{
var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity;
if (ent == null)
{
continue;
}
if (ent is DBText t)
{
if (ContainsMatchField(t.TextString))
{
ent.Erase(true);
removed++;
}
continue;
}
if (ent is MText mt)
{
var plain = SimplifyMText(mt.Contents);
if (ContainsMatchField(plain))
{
ent.Erase(true);
removed++;
}
continue;
}
if (ent is BlockReference br)
{
foreach (ObjectId attId in br.AttributeCollection)
{
var att = tr.GetObject(attId, OpenMode.ForWrite, false) as AttributeReference;
if (att == null)
{
continue;
}
if (ContainsMatchField(att.TextString))
{
try
{
att.Erase(true);
removed++;
}
catch
{
// ignore
}
}
}
}
}
return removed;
}
private static bool ContainsMatchField(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
{
return false;
}
var lines = raw
.Replace("\r\n", "\n")
.Replace("\r", "\n")
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => (s ?? string.Empty).Trim())
.Where(s => s.Length > 0);
foreach (var line in lines)
{
if (MatchFieldRegex.IsMatch(line))
{
return true;
}
}
return false;
}
private static string SimplifyMText(string contents)
{
if (string.IsNullOrEmpty(contents))
{
return string.Empty;
}
var s = contents
.Replace("\\P", "\n")
.Replace("\\p", "\n")
.Replace("\\~", " ");
s = s.Replace("{", string.Empty).Replace("}", string.Empty);
s = MTextFormatRegex.Replace(s, string.Empty);
return s;
}
public sealed class NoteApplyResult
{
public bool Applied { get; set; }
public string RenderedText { get; set; }
public int PlaceholderCountInDwg { get; set; }
public string TargetKind { get; set; }
public string Message { get; set; }
}
private sealed class NoteCandidate
{
public int Score;
public string Kind;
public string PlainText;
public Action<string> Apply;
}
public static NoteApplyResult ApplyNoteTemplate(CadContext ctx, string layoutName, bool scanModelSpace, TemplateSchemaDefinition schema, ParamBag bag)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}
if (schema == null)
{
return new NoteApplyResult { Applied = false, Message = "Schema is null" };
}
if (bag == null)
{
return new NoteApplyResult { Applied = false, Message = "ParamBag is null" };
}
if (schema.NoteBindings == null || schema.NoteBindings.Count == 0)
{
return new NoteApplyResult { Applied = false, Message = "未配置附注绑定,跳过。" };
}
var tr = ctx.Transaction;
var db = ctx.Database;
var space = GetTargetSpace(tr, db, layoutName, scanModelSpace);
if (space == null)
{
return new NoteApplyResult { Applied = false, Message = "未找到目标空间,跳过附注替换。" };
}
var candidates = new List<NoteCandidate>();
var visitedBlocks = new HashSet<ObjectId>();
foreach (ObjectId id in space)
{
var ent = tr.GetObject(id, OpenMode.ForRead, false) as Entity;
if (ent != null)
{
CollectNoteCandidates(tr, ent, visitedBlocks, candidates);
}
}
if (candidates.Count == 0)
{
return new NoteApplyResult { Applied = false, Message = "未找到附注文本目标(包含‘附注’且包含占位符*),跳过。" };
}
var best = candidates.OrderByDescending(c => c.Score).FirstOrDefault();
if (best == null || best.Apply == null)
{
return new NoteApplyResult { Applied = false, Message = "未找到可替换的附注文本目标,跳过。" };
}
var templateText = best.PlainText ?? string.Empty;
var placeholderCount = NoteTemplateEngine.CountPlaceholders(templateText);
if (placeholderCount <= 0)
{
return new NoteApplyResult
{
Applied = false,
TargetKind = best.Kind,
PlaceholderCountInDwg = placeholderCount,
Message = "附注目标中未检测到有效占位符(*,忽略****),跳过。"
};
}
var effective = NoteTemplateEngine.BuildEffectiveValueKeyBindings(schema.NoteBindings);
var rendered = NoteTemplateEngine.Render(templateText, effective, bag.GetString);
best.Apply(rendered);
return new NoteApplyResult
{
Applied = true,
TargetKind = best.Kind,
PlaceholderCountInDwg = placeholderCount,
RenderedText = rendered,
Message = "附注已替换。"
};
}
private static BlockTableRecord GetTargetSpace(Transaction tr, Database db, string layoutName, bool scanModelSpace)
{
if (tr == null || db == null)
{
return null;
}
if (scanModelSpace)
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
return (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
}
if (string.IsNullOrWhiteSpace(layoutName))
{
return null;
}
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
ObjectId layoutId = ObjectId.Null;
foreach (DBDictionaryEntry entry in layoutDict)
{
if (string.Equals(entry.Key, layoutName, StringComparison.OrdinalIgnoreCase))
{
layoutId = entry.Value;
break;
}
}
if (layoutId.IsNull)
{
return null;
}
var layout = (Layout)tr.GetObject(layoutId, OpenMode.ForRead);
return (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead);
}
private static void CollectNoteCandidates(Transaction tr, Entity ent, HashSet<ObjectId> visitedBlocks, List<NoteCandidate> candidates)
{
if (tr == null || ent == null || candidates == null)
{
return;
}
if (ent is DBText t)
{
AddNoteCandidateIfMatch(t.TextString, "DBText", t.ObjectId, tr, candidates);
return;
}
if (ent is MText mt)
{
var plain = SimplifyMText(mt.Contents);
AddNoteCandidateIfMatch(plain, "MText", mt.ObjectId, tr, candidates);
return;
}
if (ent is BlockReference br)
{
foreach (ObjectId attId in br.AttributeCollection)
{
var att = tr.GetObject(attId, OpenMode.ForRead, false) as AttributeReference;
if (att == null)
{
continue;
}
AddNoteCandidateIfMatch(att.TextString, "Attribute", attId, tr, candidates);
}
var blockId = br.BlockTableRecord;
if (blockId.IsNull)
{
return;
}
if (visitedBlocks == null)
{
visitedBlocks = new HashSet<ObjectId>();
}
if (visitedBlocks.Contains(blockId))
{
return;
}
visitedBlocks.Add(blockId);
var btr = tr.GetObject(blockId, OpenMode.ForRead, false) as BlockTableRecord;
if (btr == null)
{
return;
}
foreach (ObjectId childId in btr)
{
var child = tr.GetObject(childId, OpenMode.ForRead, false) as Entity;
if (child != null)
{
CollectNoteCandidates(tr, child, visitedBlocks, candidates);
}
}
}
}
private static void AddNoteCandidateIfMatch(string plainText, string kind, ObjectId id, Transaction tr, List<NoteCandidate> candidates)
{
if (!IsLikelyNoteText(plainText))
{
return;
}
var score = ComputeNoteScore(plainText);
if (score <= 0)
{
return;
}
candidates.Add(new NoteCandidate
{
Score = score,
Kind = kind,
PlainText = plainText,
Apply = rendered => ApplyNoteTextToObject(tr, id, kind, rendered)
});
}
private static bool IsLikelyNoteText(string plain)
{
if (string.IsNullOrWhiteSpace(plain))
{
return false;
}
if (!plain.Contains("*"))
{
return false;
}
if (plain.Contains("附注"))
{
return true;
}
// fallback: numbered note line
return plain.Contains("1*") || plain.Contains("1 *");
}
private static int ComputeNoteScore(string plain)
{
if (string.IsNullOrWhiteSpace(plain))
{
return 0;
}
var score = 0;
var lines = SplitLines(plain).ToList();
if (plain.Contains("附注"))
{
score += 10;
}
if (lines.Count > 0 && string.Equals(lines[0].Trim(), "附注", StringComparison.OrdinalIgnoreCase))
{
score += 10;
}
if (plain.Contains("*"))
{
score += 5;
}
if (plain.Contains("1*"))
{
score += 3;
}
return score;
}
private static IEnumerable<string> SplitLines(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return Enumerable.Empty<string>();
}
return (text ?? string.Empty)
.Replace("\r\n", "\n")
.Replace("\r", "\n")
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => (s ?? string.Empty).Trim());
}
private static void ApplyNoteTextToObject(Transaction tr, ObjectId id, string kind, string rendered)
{
if (tr == null || id.IsNull)
{
return;
}
rendered = rendered ?? string.Empty;
// We try multiple types regardless of recorded kind to be safe.
var obj = tr.GetObject(id, OpenMode.ForWrite, false);
if (obj is MText mt)
{
mt.Contents = ToMTextContents(rendered);
return;
}
if (obj is DBText t)
{
t.TextString = CollapseToSingleLine(rendered);
return;
}
if (obj is AttributeReference att)
{
att.TextString = CollapseToSingleLine(rendered);
}
}
private static string ToMTextContents(string plainText)
{
if (string.IsNullOrEmpty(plainText))
{
return string.Empty;
}
var s = plainText
.Replace("\r\n", "\n")
.Replace("\r", "\n")
.Replace("\n", "\\P");
return s;
}
private static string CollapseToSingleLine(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return string.Empty;
}
var parts = (text ?? string.Empty)
.Replace("\r\n", "\n")
.Replace("\r", "\n")
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => (s ?? string.Empty).Trim())
.Where(s => s.Length > 0)
.ToArray();
return string.Join(" ", parts);
}
public static void KeepOnlyLayout(Document doc, string layoutName)
{
if (doc == null)
{
throw new ArgumentNullException(nameof(doc));
}
if (string.IsNullOrWhiteSpace(layoutName))
{
return;
}
using (doc.LockDocument())
{
var db = doc.Database;
var prevDb = HostApplicationServices.WorkingDatabase;
HostApplicationServices.WorkingDatabase = db;
try
{
List<string> layoutNames;
using (var tr = db.TransactionManager.StartTransaction())
{
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
layoutNames = layoutDict.Cast<DBDictionaryEntry>().Select(e => e.Key).ToList();
tr.Commit();
}
if (!layoutNames.Any(n => string.Equals(n, layoutName, StringComparison.OrdinalIgnoreCase)))
{
return;
}
var lm = LayoutManager.Current;
lm.CurrentLayout = layoutName;
foreach (var name in layoutNames)
{
if (string.Equals(name, "Model", StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (string.Equals(name, layoutName, StringComparison.OrdinalIgnoreCase))
{
continue;
}
try
{
lm.DeleteLayout(name);
}
catch
{
// ignore
}
}
}
finally
{
HostApplicationServices.WorkingDatabase = prevDb;
}
}
}
public static void KeepOnlyModelWindow(Document doc, Extents3d window)
{
if (doc == null)
{
throw new ArgumentNullException(nameof(doc));
}
using (doc.LockDocument())
{
var db = doc.Database;
using (var tr = db.TransactionManager.StartTransaction())
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
var erased = 0;
var kept = 0;
foreach (ObjectId id in ms)
{
var ent = tr.GetObject(id, OpenMode.ForWrite) as Entity;
if (ent == null)
{
continue;
}
try
{
var ext = ent.GeometricExtents;
if (!Intersects(ext, window))
{
ent.Erase(true);
erased++;
}
else
{
kept++;
}
}
catch
{
// 无法获取范围的对象默认保留,避免误删关键对象
kept++;
}
}
tr.Commit();
}
}
}
private static bool Intersects(Extents3d a, Extents3d b)
{
return a.MinPoint.X <= b.MaxPoint.X
&& a.MaxPoint.X >= b.MinPoint.X
&& a.MinPoint.Y <= b.MaxPoint.Y
&& a.MaxPoint.Y >= b.MinPoint.Y;
}
}
}