Inserting a preexisting AutoCAD drawing into a current drawing

6.3k views Asked by At

I'm trying to programmatically insert a block from a pre-existing drawing into the current drawing a plugin is running on. To do that, I have a button on my C#.NET form call the following method

public void MakeAndInsertObject() //Method to add all windows and doors templates to drawing database for use later
{
    Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; //Stores the active document
    Editor ed = doc.Editor; //Stores the document's editor
    Database dtb = ed.Document.Database; //Stores the database from the editor

    Transaction tr = dtb.TransactionManager.StartTransaction(); //Start a transaction with the document's database
    DocumentLock docLock = doc.LockDocument();

    using (tr)    
    using (docLock)
    {
        BlockTableRecord btr = (BlockTableRecord)tr.GetObject(dtb.CurrentSpaceId, OpenMode.ForWrite); //Opens the block table record so you can write to it
        BlockTableRecord newBlockDef = new BlockTableRecord(); //Creates a new record in the block table
        BlockTable blockTable = (BlockTable)tr.GetObject(dtb.BlockTableId, OpenMode.ForWrite); //Opens the block table so it can be written to
        //Pointing new block to correct drawing file
        newBlockDef.Name = "Door";
        newBlockDef.PathName = "C:/Users/Administrator/Documents/All Code/clearspan-autocad-tools-development/Templates/locks/DOOR.dwg";
        blockTable.Add(newBlockDef); //Adds the block table record to the block table
        BlockReference newBlock = new BlockReference(new Point3d(0, 0, 0), newBlockDef.ObjectId); //Insert a block reference with the newly created block
        btr.AppendEntity(newBlock); //Inserts the block into the current space (Model or Paper) via the block table record
        //Updates the Transaction with all new database objects
        tr.AddNewlyCreatedDBObject(newBlockDef, true);
        tr.AddNewlyCreatedDBObject(newBlock, true);
        tr.Commit(); //Applies all changes made as part of the current transaction
        tr.Dispose();
    }
}

The code fully executes but the block in my DOOR.dwg file does not appear at location (0, 0, 0) and I do not know why it doesn't

3

There are 3 answers

2
Trae Moore On BEST ANSWER

I took a snippet from Keen Walmsley and edited it to not be so cumbersome and more legible. This is pretty basic. You should read over some of Keen's stuff it is pretty good and he is very descriptive in his notes. see code below for usage.

public void ImportBlocks()
{
    DocumentCollection dm =
    Application.DocumentManager;
    Editor ed = dm.MdiActiveDocument.Editor;
    Database destDb = dm.MdiActiveDocument.Database;
    Database sourceDb = new Database(false, true);
    try
    {
        // Get name of DWG from which to copy blocks
        var sourceFileName = ed.GetString("\nEnter the path of the source drawing: ");

        // Read the DWG into a side database
        sourceDb.ReadDwgFile(sourceFileName.StringResult, System.IO.FileShare.Read, true, "");

        // Create a variable to store the list of block identifiers
        ObjectIdCollection blockIds = new ObjectIdCollection();

        var tm = sourceDb.TransactionManager;

        using (var myT = tm.StartOpenCloseTransaction())
        {
            // Open the block table
            BlockTable bt = (BlockTable)myT.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false);

            // Check each block in the block table
            foreach (ObjectId btrId in bt)
            {
                BlockTableRecord btr =
                (BlockTableRecord)myT.GetObject(btrId, OpenMode.ForRead, false);

                // Only add named & non-layout blocks to the copy list
                if (!btr.IsAnonymous && !btr.IsLayout)
                    blockIds.Add(btrId);
                btr.Dispose();
            }
        }
        // Copy blocks from source to destination database
        var mapping = new IdMapping();
        sourceDb.WblockCloneObjects(blockIds,
            destDb.BlockTableId,
            mapping,
            DuplicateRecordCloning.Replace,
            false);
    }
    catch(Autodesk.AutoCAD.Runtime.Exception ex)
    {
        ed.WriteMessage("\nError during copy: " + ex.Message);
    }
    sourceDb.Dispose();
}

Just out of curiosity, If someone had developed a python API for use in AutoCAD, Would you use it? or give it a shot?

3
Ognyan Dimitrov On
        /// <summary>
        /// the source drawig should be drawn as number of
        /// separate entites with or without attributes.
        /// Throws NotImplementedException if invoked with .dxf file
        /// </summary>
        /// <param name="sourceDrawing"></param>
        /// <param name="insertionPoint"></param>
        /// <returns>ObjectID of the Block Def that was imported.</returns>
        public void ImportDwgAsBlock(string sourceDrawing, Point3d insertionPoint)
        {
            Matrix3d ucs = _ed.CurrentUserCoordinateSystem;

            string blockname = sourceDrawing.Remove(0, sourceDrawing.LastIndexOf("\\", StringComparison.Ordinal) + 1);
            blockname = blockname.Substring(0, blockname.Length - 4); // remove the extension

            try
            {
                using (_doc.LockDocument())
                {
                    using (var inMemoryDb = new Database(false, true))
                    {
                        #region Load the drawing into temporary inmemory database
                        if (sourceDrawing.LastIndexOf(".dwg", StringComparison.Ordinal) > 0)
                        {
                            inMemoryDb.ReadDwgFile(sourceDrawing, System.IO.FileShare.Read, true, "");
                        }
                        else if (sourceDrawing.LastIndexOf(".dxf", StringComparison.Ordinal) > 0)
                        {
                            _logger.Error(MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name + " : Tried to invoke the method with .dxf file.");
                            throw new NotImplementedException("Importing .dxf is not supported in this version.");
                            //inMemoryDb.DxfIn("@" + sourceDrawing, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\log\\import_block_dxf_log.txt");
                        }
                        else
                        {
                            throw new ArgumentException("This is not a valid drawing.");
                        }
                        #endregion

                        using (var transaction =  _db.TransactionManager.StartTransaction())
                        {
                            BlockTable destDbBlockTable = (BlockTable)transaction.GetObject(_db.BlockTableId, OpenMode.ForRead);
                            BlockTableRecord destDbCurrentSpace = (BlockTableRecord)_db.CurrentSpaceId.GetObject(OpenMode.ForWrite);

                            // If the destination DWG already contains this block definition
                            // we will create a block reference and not a copy of the same definition
                            ObjectId sourceBlockId;
                            if (destDbBlockTable.Has(blockname))
                            {

                                //BlockTableRecord destDbBlockDefinition = (BlockTableRecord)transaction.GetObject(destDbBlockTable[blockname], OpenMode.ForRead);
                                //sourceBlockId = destDbBlockDefinition.ObjectId;

                                sourceBlockId = transaction.GetObject(destDbBlockTable[blockname], OpenMode.ForRead).ObjectId;

                                // Create a block reference to the existing block definition
                                using (var blockReference = new BlockReference(insertionPoint, sourceBlockId))
                                {
                                    _ed.CurrentUserCoordinateSystem = Matrix3d.Identity;
                                    blockReference.TransformBy(ucs);
                                    _ed.CurrentUserCoordinateSystem = ucs;
                                    var converter = new MeasurementUnitsConverter();
                                    var scaleFactor = converter.GetScaleRatio(inMemoryDb.Insunits, _db.Insunits);
                                    blockReference.ScaleFactors = new Scale3d(scaleFactor);
                                    destDbCurrentSpace.AppendEntity(blockReference);
                                    transaction.AddNewlyCreatedDBObject(blockReference, true);
                                    _ed.Regen();
                                    transaction.Commit();
                                    // At this point the Bref has become a DBObject and (can be disposed) and will be disposed by the transaction
                                }
                                return;
                            }

                            //else // There is not such block definition, so we are inserting/creating new one

                            sourceBlockId = _db.Insert(blockname, inMemoryDb, true);
                            BlockTableRecord sourceBlock = (BlockTableRecord)sourceBlockId.GetObject(OpenMode.ForRead);
                            sourceBlock.UpgradeOpen();
                            sourceBlock.Name = blockname;
                            destDbCurrentSpace.DowngradeOpen();
                            var sourceBlockMeasurementUnits = inMemoryDb.Insunits;
                            try
                            {
                                CreateBlockReference(sourceBlock.Name, sourceBlockMeasurementUnits,
                                                     insertionPoint,
                                                     destDbCurrentSpace,
                                                     destDbBlockTable);
                            }
                            catch (ArgumentException argumentException)
                            {
                                _logger.Error("Error. Check inner exception.", argumentException);
                            }

                            _ed.Regen();
                            transaction.Commit();
                        }
                    }
                }
            }
            catch (Autodesk.AutoCAD.Runtime.Exception exception)
            {
                _logger.Error("Error in ImportDrawingAsBlock().", exception);

            }
        }

UPDATE Here is the converter I missed :

#if !bcad
using Autodesk.AutoCAD.DatabaseServices;
#else
using Teigha.DatabaseServices;
#endif
using Castle.Core.Logging;
using System.Collections.Generic;

namespace KojtoCAD.Utilities
{
    public class MeasurementUnitsConverter
    {
        private ILogger _logger = NullLogger.Instance;

    private readonly Dictionary<UnitsValue, double> _linkBetweenDrawingUnitsAndMilimeters;

    public MeasurementUnitsConverter()
    {
        _linkBetweenDrawingUnitsAndMilimeters = new Dictionary<UnitsValue, double>
                                                    {
                                                        {
                                                            UnitsValue.Angstroms,
                                                            0.0000001
                                                        },
                                                        {
                                                            UnitsValue.Astronomical,
                                                            149600000000000
                                                        },
                                                        {
                                                            UnitsValue.Centimeters, 10
                                                        },
                                                        {
                                                            UnitsValue.Decimeters, 100
                                                        },
                                                        {
                                                            UnitsValue.Dekameters,
                                                            10000
                                                        },
                                                        { UnitsValue.Feet, 304.8 },
                                                        {
                                                            UnitsValue.Gigameters,
                                                            1000000000000
                                                        },
                                                        {
                                                            UnitsValue.Hectometers,
                                                            100000
                                                        },
                                                        { UnitsValue.Inches, 25.4 },
                                                        {
                                                            UnitsValue.Kilometers,
                                                            1000000
                                                        },
                                                        {
                                                            UnitsValue.LightYears,
                                                            9460700000000000000
                                                        },
                                                        { UnitsValue.Meters, 1000 },
                                                        {
                                                            UnitsValue.MicroInches,
                                                            0.0000254
                                                        },
                                                        { UnitsValue.Microns, 0.001 },
                                                        {
                                                            UnitsValue.Miles, 1609344.0
                                                        },
                                                        { UnitsValue.Millimeters, 1 },
                                                        { UnitsValue.Mils, 25400 },
                                                        {
                                                            UnitsValue.Nanometers,
                                                            0.000001
                                                        },
                                                        { UnitsValue.Undefined, 1.0 },
                                                        { UnitsValue.Yards, 914.4 }
                                                    };
        //_linkBetweenDrawingUnitsAndMilimeters.Add(UnitsValue.Parsecs, 30857000000000000000);
    }

    public double GetScaleRatio(UnitsValue sourceUnits, UnitsValue targetUnits)
    {
        if (sourceUnits == UnitsValue.Undefined || targetUnits == UnitsValue.Undefined
            || !_linkBetweenDrawingUnitsAndMilimeters.ContainsKey(sourceUnits)
            || !_linkBetweenDrawingUnitsAndMilimeters.ContainsKey(targetUnits))
        {
            return 1;
        }
        return _linkBetweenDrawingUnitsAndMilimeters[sourceUnits]
               / _linkBetweenDrawingUnitsAndMilimeters[targetUnits];
    }
}
}
0
Alain On

You need to use the wblockcloneObjects method to get the block from the source dwg to the target dwg blocktable. Then you can insert it.

in vb code:

Dim doc As Document = Application.DocumentManager.MdiActiveDocument
        Using lock = doc.LockDocument

            Using OpenDb As New Database(False, False)
                OpenDb.ReadDwgFile(PathDwg, System.IO.FileShare.ReadWrite, True, "")
                Dim ids As New ObjectIdCollection()
                Dim bt As BlockTable
                Dim sourceBlockId As ObjectId = ObjectId.Null
                Using tr As Transaction = OpenDb.TransactionManager.StartTransaction()

                    bt = DirectCast(tr.GetObject(OpenDb.BlockTableId, OpenMode.ForRead), BlockTable)
                    If bt.Has(NaamBlok) Then
                        ids.Add(bt(NaamBlok))
                        sourceBlockId = bt(NaamBlok)
                    End If

                    If ids.Count <> 0 Then
                        Dim destdb As Database = doc.Database
                        Dim iMap As New IdMapping()
                        OpenDb.WblockCloneObjects(ids, destdb.BlockTableId, iMap, DuplicateRecordCloning.Replace, False)

                    End If

                    tr.Commit()

                End Using
            End Using
        End Using