bulk block insertion optimization and using statements
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
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;
}