c# cad 合并dwg2

csharp 复制代码
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Interop;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace cad_tools
{
    public class merge_dwg
    {
        /// <summary>合并目标图模型空间实体数缓存,避免每次 Insert 前全表扫描。</summary>
        private static int _destModelSpaceEntityCount;

        internal static void ResetDestEntityCount()
        {
            _destModelSpaceEntityCount = 0;
        }

        internal static void SyncDestEntityCount(Database destDb)
        {
            _destModelSpaceEntityCount = CountModelSpaceEntities(destDb);
        }
        /// <summary>合并前记录的尺寸标注显示参数,合并后写回。</summary>
        private sealed class DimensionSnapshot
        {
            public double Dimtxt;
            public double Dimscale;
            public double Dimasz;
            public double Dimexo;
            public double Dimexe;
            public double Dimgap;
            public double Dimlfac;
            public bool Dimtih;
            public bool Dimtoh;
            public int Dimtad;
            public string DimStyleName;
            public string DimtxstyName;
        }

        internal static int CountModelSpaceEntities(Database db)
        {
            int count = 0;
            using Transaction tr = db.TransactionManager.StartTransaction();
            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
            BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
            foreach (ObjectId id in ms)
            {
                if (id.IsValid && !id.IsErased)
                {
                    count++;
                }
            }
            tr.Commit();
            return count;
        }

        internal static bool TryGetFullModelSpaceExtents(Database db, out Extents3d extents)
        {
            return TryGetModelSpaceExtents(db, out extents);
        }

        /// <summary>
        /// 将源 DWG 模型空间实体插入目标图(非外部参照),返回占用区域右上角 [maxX, maxY]。
        /// </summary>
        static public double[]? run(string sourcePath, double x, double y, bool isDim, AcadDocument? targetDoc = null)
        {
            if (sourcePath.Contains(".dwl2") || sourcePath.Contains(".dwl"))
            {
                return null;
            }

            if (!File.Exists(sourcePath))
            {
                Console.WriteLine($"错误:源文件不存在:{sourcePath}");
                return null;
            }

            AcadApplication? acadApp = CadConnect.GetOrCreateInstance();
            if (acadApp == null)
            {
                Console.WriteLine("错误:无法连接到 AutoCAD 应用程序。");
                return null;
            }

            Document? destDoc = draw_divider.GetMergeManagedDocument(acadApp);
            if (destDoc == null)
            {
                draw_divider.EnsureMergeCanvas(acadApp);
                destDoc = draw_divider.GetMergeManagedDocument(acadApp);
            }

            if (destDoc == null)
            {
                Console.WriteLine("错误:无法绑定合并画布 .NET 文档,实体与标签可能不在同一图纸。");
                return null;
            }

            try
            {
                using DocumentLock docLock = destDoc.LockDocument();
                Database destDb = destDoc.Database;

                using Database sourceDb = new Database(false, true);
                sourceDb.ReadDwgFile(sourcePath, FileShare.Read, true, null);

                string stylePrefix = BuildUniqueStylePrefix(sourcePath, isDim);
                string mergedDimStyleName = PrepareSourceForMerge(sourceDb, stylePrefix);

                List<DimensionSnapshot> dimSnapshots = CollectDimensionSnapshotsOrdered(sourceDb, mergedDimStyleName);

                if (!TryGetModelSpaceExtents(sourceDb, out Extents3d sourceExtents))
                {
                    Console.WriteLine($"警告:无法获取源图边界,仍尝试插入:{sourcePath}");
                    InsertModelSpace(sourceDb, destDb, Matrix3d.Displacement(new Vector3d(x, y, 0)), dimSnapshots);
                    Console.WriteLine($"已插入(无边界):{Path.GetFileName(sourcePath)} @ ({x:F2}, {y:F2})");
                    return new double[] { x, y };
                }

                double offsetX = -sourceExtents.MinPoint.X;
                double offsetY = -sourceExtents.MinPoint.Y;
                Matrix3d xform = Matrix3d.Displacement(new Vector3d(offsetX + x, offsetY + y, 0));

                InsertModelSpace(sourceDb, destDb, xform, dimSnapshots);

                double relativeMaxX = sourceExtents.MaxPoint.X - sourceExtents.MinPoint.X + x;
                double relativeMaxY = sourceExtents.MaxPoint.Y - sourceExtents.MinPoint.Y + y;

                Console.WriteLine($"已插入:{Path.GetFileName(sourcePath)} @ ({x:F2}, {y:F2})");

                return new double[] { relativeMaxX, relativeMaxY };
            }
            catch (Exception ex)
            {
                Console.WriteLine($"插入失败:{ex.Message}, {sourcePath}");
                if (ex.InnerException != null)
                {
                    Console.WriteLine($"内部异常:{ex.InnerException.Message}");
                }
            }

            return null;
        }

        private static string BuildUniqueStylePrefix(string sourcePath, bool isDim)
        {
            string fullPath = Path.GetFullPath(sourcePath);
            string baseName = SanitizeSymbolName(Path.GetFileNameWithoutExtension(fullPath));
            if (string.IsNullOrEmpty(baseName))
            {
                baseName = "DWG";
            }

            if (!isDim)
            {
                return $"{baseName}_";
            }

            return $"{baseName}_";
        }

        private static string SanitizeSymbolName(string raw)
        {
            if (string.IsNullOrWhiteSpace(raw))
            {
                return "DWG";
            }

            var sb = new StringBuilder(raw.Length);
            foreach (char c in raw.Trim())
            {
                if (char.IsLetterOrDigit(c) || c == '_' || c == '-' || c == '$')
                {
                    sb.Append(c);
                }
                else if (c == ' ' || c == '.')
                {
                    sb.Append('_');
                }
            }

            string name = sb.ToString();
            return string.IsNullOrEmpty(name) ? "DWG" : name;
        }

        /// <summary>
        /// 每个源图只保留一个标注样式(+ 对应文字样式),全部尺寸统一指向它,避免合并后符号表膨胀卡顿。
        /// </summary>
        private static string PrepareSourceForMerge(Database db, string prefix)
        {
            string mergedDimStyleName = BuildPrefixedName(prefix, "DIM");
            string mergedTextStyleName = BuildPrefixedName(prefix, "TXT");

            using Transaction tr = db.TransactionManager.StartTransaction();
            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
            BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);

            var styleCounts = new Dictionary<ObjectId, int>();
            var dimensionIds = new List<ObjectId>();
            var layerNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            foreach (ObjectId id in ms)
            {
                if (!id.IsValid || id.IsErased)
                {
                    continue;
                }

                Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);
                if (!string.IsNullOrEmpty(ent.Layer))
                {
                    layerNames.Add(ent.Layer);
                }

                if (ent is Dimension dim && dim.DimensionStyle.IsValid)
                {
                    dimensionIds.Add(id);
                    if (!styleCounts.TryGetValue(dim.DimensionStyle, out int count))
                    {
                        count = 0;
                    }

                    styleCounts[dim.DimensionStyle] = count + 1;
                }
            }

            if (styleCounts.Count > 0)
            {
                ObjectId primaryStyleId = ObjectId.Null;
                int maxCount = 0;
                foreach (KeyValuePair<ObjectId, int> pair in styleCounts)
                {
                    if (pair.Value > maxCount)
                    {
                        maxCount = pair.Value;
                        primaryStyleId = pair.Key;
                    }
                }

                DimStyleTableRecord primaryStyle =
                    (DimStyleTableRecord)tr.GetObject(primaryStyleId, OpenMode.ForWrite);

                if (primaryStyle.Dimtxsty.IsValid)
                {
                    TextStyleTableRecord txtStyle =
                        (TextStyleTableRecord)tr.GetObject(primaryStyle.Dimtxsty, OpenMode.ForWrite);
                    if (!txtStyle.Name.StartsWith("*", StringComparison.Ordinal))
                    {
                        txtStyle.Name = mergedTextStyleName;
                    }
                }

                if (!primaryStyle.Name.StartsWith("*", StringComparison.Ordinal))
                {
                    primaryStyle.Name = mergedDimStyleName;
                }

                foreach (ObjectId dimId in dimensionIds)
                {
                    Dimension dim = (Dimension)tr.GetObject(dimId, OpenMode.ForWrite);
                    dim.DimensionStyle = primaryStyleId;
                }
            }

            LayerTable layerTable = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
            foreach (string layerName in layerNames)
            {
                if (layerName == "0" || layerName.Equals("Defpoints", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                if (!layerTable.Has(layerName))
                {
                    continue;
                }

                LayerTableRecord layer = (LayerTableRecord)tr.GetObject(layerTable[layerName], OpenMode.ForWrite);
                layer.Name = BuildPrefixedName(prefix, layer.Name);
            }

            tr.Commit();
            return mergedDimStyleName;
        }

        private static string BuildPrefixedName(string prefix, string originalName)
        {
            string name = prefix + originalName;
            return name.Length <= 255 ? name : name.Substring(0, 255);
        }

        private static List<DimensionSnapshot> CollectDimensionSnapshotsOrdered(Database db, string mergedDimStyleName)
        {
            var snapshots = new List<DimensionSnapshot>();

            using Transaction tr = db.TransactionManager.StartTransaction();
            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
            BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);

            foreach (ObjectId id in ms)
            {
                if (!id.IsValid || id.IsErased)
                {
                    continue;
                }

                Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);
                if (ent is Dimension dim)
                {
                    snapshots.Add(CaptureDimensionSnapshot(dim, tr, mergedDimStyleName));
                }
            }

            tr.Commit();
            return snapshots;
        }

        private static DimensionSnapshot CaptureDimensionSnapshot(Dimension dim, Transaction tr, string mergedDimStyleName)
        {
            string dimtxstyName = string.Empty;
            if (dim.DimensionStyle.IsValid)
            {
                try
                {
                    DimStyleTableRecord style =
                        (DimStyleTableRecord)tr.GetObject(dim.DimensionStyle, OpenMode.ForRead);
                    if (style.Dimtxsty.IsValid)
                    {
                        TextStyleTableRecord txt =
                            (TextStyleTableRecord)tr.GetObject(style.Dimtxsty, OpenMode.ForRead);
                        dimtxstyName = txt.Name;
                    }
                }
                catch
                {
                    // ignore
                }
            }

            return new DimensionSnapshot
            {
                Dimtxt = dim.Dimtxt,
                Dimscale = dim.Dimscale,
                Dimasz = dim.Dimasz,
                Dimexo = dim.Dimexo,
                Dimexe = dim.Dimexe,
                Dimgap = dim.Dimgap,
                Dimlfac = dim.Dimlfac,
                Dimtih = dim.Dimtih,
                Dimtoh = dim.Dimtoh,
                Dimtad = dim.Dimtad,
                DimStyleName = mergedDimStyleName,
                DimtxstyName = dimtxstyName,
            };
        }

        private static void InsertModelSpace(
            Database sourceDb,
            Database destDb,
            Matrix3d xform,
            List<DimensionSnapshot> dimSnapshots)
        {
            int entityBefore = _destModelSpaceEntityCount;
            int insertedEntityCount = CountModelSpaceEntities(sourceDb);
            destDb.Insert(xform, sourceDb, true);
            _destModelSpaceEntityCount = entityBefore + insertedEntityCount;

            if (dimSnapshots.Count == 0)
            {
                return;
            }

            using Transaction tr = destDb.TransactionManager.StartTransaction();
            BlockTable bt = (BlockTable)tr.GetObject(destDb.BlockTableId, OpenMode.ForRead);
            BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);

            int entityIndex = 0;
            int snapIndex = 0;
            foreach (ObjectId id in ms)
            {
                if (!id.IsValid || id.IsErased)
                {
                    continue;
                }

                if (entityIndex < entityBefore)
                {
                    entityIndex++;
                    continue;
                }

                entityIndex++;
                if (snapIndex >= dimSnapshots.Count)
                {
                    break;
                }

                if (tr.GetObject(id, OpenMode.ForWrite) is not Dimension dim)
                {
                    continue;
                }

                ApplyDimensionSnapshot(dim, dimSnapshots[snapIndex], tr);
                snapIndex++;
            }

            tr.Commit();
        }

        private static void ApplyDimensionSnapshot(Dimension dim, DimensionSnapshot snap, Transaction tr)
        {
            try
            {
                if (!string.IsNullOrEmpty(snap.DimStyleName))
                {
                    DimStyleTable dimTable = (DimStyleTable)tr.GetObject(
                        dim.Database.DimStyleTableId,
                        OpenMode.ForRead);
                    if (dimTable.Has(snap.DimStyleName))
                    {
                        dim.DimensionStyle = dimTable[snap.DimStyleName];
                    }
                }

                dim.Dimtxt = snap.Dimtxt;
                dim.Dimscale = snap.Dimscale;
                dim.Dimasz = snap.Dimasz;
                dim.Dimexo = snap.Dimexo;
                dim.Dimexe = snap.Dimexe;
                dim.Dimgap = snap.Dimgap;
                dim.Dimlfac = snap.Dimlfac;
                dim.Dimtih = snap.Dimtih;
                dim.Dimtoh = snap.Dimtoh;
                dim.Dimtad = snap.Dimtad;

                if (!string.IsNullOrEmpty(snap.DimtxstyName))
                {
                    TextStyleTable textTable = (TextStyleTable)tr.GetObject(
                        dim.Database.TextStyleTableId,
                        OpenMode.ForRead);
                    if (textTable.Has(snap.DimtxstyName))
                    {
                        dim.TextStyleId = textTable[snap.DimtxstyName];
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"恢复标注尺寸失败:{ex.Message}");
            }
        }

        private static bool TryGetModelSpaceExtents(Database db, out Extents3d extents)
        {
            extents = new Extents3d();
            bool hasExtents = false;

            using Transaction tr = db.TransactionManager.StartTransaction();
            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
            BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);

            foreach (ObjectId id in ms)
            {
                if (!id.IsValid || id.IsErased)
                {
                    continue;
                }

                Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);
                try
                {
                    Extents3d entExt = ent.GeometricExtents;
                    if (!hasExtents)
                    {
                        extents = entExt;
                        hasExtents = true;
                    }
                    else
                    {
                        extents.AddExtents(entExt);
                    }
                }
                catch
                {
                    // 部分实体无几何边界
                }
            }

            tr.Commit();
            return hasExtents;
        }
    }
}