bulk block insertion optimization and using statements

bulk block insertion optimization and using statements

jhelouPQYTQ
Participant Participant
387 Views
1 Reply
Message 1 of 2

bulk block insertion optimization and using statements

jhelouPQYTQ
Participant
Participant

Hello there,

 

I'm working with autocad 2019 and have a .NET framework C# plugin that I've inherited.  The plugin I'm working on has different custom blocks and associated properties.

 

I have a transform/visual update function for each archetype/category of block, which reads it's properties(which are just xrecords), then adds a bunch of blocks to a list based on it's properties.  Then, these blocks are all inserted in bulk for each custom block in the drawing.

 

Neither my predecessor nor I really have an incredibly deep understanding of Autocad's inner workings, but the larger our drawings get and the more custom blocks they have, the more of a concern optimization is when refreshing block graphics and their properties.

 

Previously, each custom block would have it's nested blocks inserted one at a time, locking and unlocking the drawing, etc. for every nested block, which was very slow.  The following method has been our replacement(which inserts all the nested blocks in one transaction, instead of several), but I'm wondering if there is further optimization to be done here, and whether all the using statements that are here are necessary or not.

 

1.  I vaguely remember reading that once you're inside a transaction's using statement, you don't need using statements for things inside it, but wasn't quite sure.  I also read another forum post at one point where someone was annoyed that lots of classes in Autocad's API implement Idisposable, despite that you shouldn't really be disposing of them.  Is there information somewhere on this in more detail?  Is there any performance implications to having using statements when I don't need them with certain objects in Autocad?

 

 2. I also am pretty sure I don't need to set BlockReference.RecordGraphicsModified(true) at all, because removing it appears to do nothing at all.  To me, this would indicate that something else here tells autocad it needs to redraw/regen the block visuals.  Is there information somewhere on what actions exactly will cause autocad to regen/redraw block visuals?  Would setting BlockReference.RecordGraphicsModified(false) help with performance?

2a. Is there any way to tell Autocad, "yes, I'm inserting blocks, but until I tell you to do so, don't regen/redraw because I'm not done inserting blocks yet, and redrawing now would be a waste of your time"?  For example, if you were using System.Windows.Forms.ListView, you would call System.Windows.Forms.ListView.BeginUpdate(), add items to the list, then call System.Windows.Forms.ListView.EndUpdate().  Other examples of what I mean would include Excel interop's ScreenUpdating = false, or windows forms "SuspendLayout()" and "ResumeLayout()".

 

3. Are there any other optimizations that could obviously be made for the code below?  For example, I recently changed InsertData to use a character list instead of a string list for it's list of axes to rotate on(which sounds minor, but when you have hundreds of nested blocks per block, and hundreds of blocks, it adds up to thousands of strings).  would it perhaps also be worth it to use a float list instead of a double list?  The most I've ever needed for rotation was 3 decimal places.

 

 

This is the method that inserts all the nested blocks for each block/object in the drawing.  The InsertData class is below as well.  I'm unsure why this method returns a Handle, but that return value isn't used anywhere in code so  your guess is as good as mine.

 

 

 

public static Handle InsertMultipleNestedBlocks(ObjectId id, List<InsertData> blockData) {
            Handle hd = new Handle();
            try {
                if (id.IsErased == false & id.IsEffectivelyErased == false) {
                    using (State.Docu.LockDocument()) {
                        using (Database db = State.Docu.Database) {
                            using (Transaction tr = db.TransactionManager.StartTransaction()) {                            
                                using (BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite)){
                                    for (int i = 0; i < blockData.Count; i++) {
                                        if (System.IO.File.Exists(State.BlockPath(blockData[i].BlockFolder) + blockData[i].BlockName + ".dwg")) {
                                            
                                            using (BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead)) {
                                                //add block to the drawing blocktable if it does not exist (AddBlockToBlockTable function call unwrapped here for faster calls)
                                                if (bt.Has(blockData[i].BlockName) == false) {
                                                    using (Database tempDB = new Database(false, true)) {
                                                        tempDB.ReadDwgFile(State.BlockPath(blockData[i].BlockFolder) + blockData[i].BlockName + ".dwg", System.IO.FileShare.Read, true, "");
                                                        State.Docu.Database.Insert(blockData[i].BlockName, tempDB, false);
                                                    }                                                    
                                                }
                                                //create new reference
                                                using (BlockReference bi = new BlockReference(Autodesk.AutoCAD.Geometry.Point3d.Origin, bt[blockData[i].BlockName])) {

                                                    //rotate
                                                    for (int j = 0; j < blockData[i].RotationAxis.Count; j++) {
                                                        if (blockData[i].RotationAngleDegrees[j] != 0) {
                                                            Vector3d rotationAxisVector = new Vector3d();
                                                            switch (char.ToLower(blockData[i].RotationAxis[j])) {
                                                                case 'y':
                                                                    rotationAxisVector = bi.BlockTransform.CoordinateSystem3d.Yaxis;
                                                                    break;
                                                                case 'z':
                                                                    rotationAxisVector = bi.BlockTransform.CoordinateSystem3d.Zaxis;
                                                                    break;
                                                                case 'x':
                                                                default:
                                                                    rotationAxisVector = bi.BlockTransform.CoordinateSystem3d.Xaxis;
                                                                    break;
                                                            }
                                                            bi.TransformBy(Autodesk.AutoCAD.Geometry.Matrix3d.Rotation(blockData[i].RotationAngleDegrees[j] * Math.PI / 180, rotationAxisVector, new Autodesk.AutoCAD.Geometry.Point3d(0, 0, 0)));
                                                        }
                                                    }
                                                    //add new reference into the parent blocks blocktable and position it correctly, making it a nested block
                                                    using (BlockTableRecord blockSpace = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForWrite)) {
                                                        blockSpace.AppendEntity(bi);
                                                        tr.AddNewlyCreatedDBObject(bi, true);
                                                        bi.LayerId = br.LayerId;
                                                        bi.Position = blockData[i].Position;
                                                        hd = bi.Handle;

                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                tr.Commit();
                            }                            
                        }
                    }
                }
            }
            catch (System.Exception ex) {
                Log.Error(ex);
            }
            finally {
            }
            return hd;
        }

 

 

 

InsertData class:

 

public class InsertData {
        public string BlockName = "";
        public string BlockFolder = "";
        public List<double> RotationAngleDegrees = new List<double>();
        public List<char> RotationAxis = new List<char>();
        public Point3d Position;
    }

 

 

0 Likes
388 Views
1 Reply
Reply (1)
Message 2 of 2

ActivistInvestor
Mentor
Mentor

I cannot get in-depth with your code and other suggested optimizations but I can tell you that BlockReference.RecordGraphicsModified() Is a way to inform AutoCAD that the visual representation of a block reference has changed as a result of something other than a change to the block reference itself. For example, if you modified a block's definition that may affect the visual representation of all references. This API gives you a way to tell AutoCAD that the visual representation of a block reference needs to be updated. If your code modifies a block reference directly, a call to this API is pointless and serves no purpose, because in that case AutoCAD already knows that the block reference needs to be updated. 

0 Likes