How to capture the real point of a line within a block?
I used the following method to captured some entities from a block.
I need the point of the entities, to redesign a dimension.
But if I move the original block, and then run my program. The dimension is redesigned in the original location of the block.
Basically the code looks like this:
private void FilterDimension() { TypedValue[] typedValue = new TypedValue[2]; typedValue.SetValue(new TypedValue((int)DxfCode.Start, "INSERT"), 0); typedValue.SetValue(new TypedValue((int)DxfCode.LayerName, "Dimension"), 1); SelectionFilter selectionFilter = new SelectionFilter(typedValue); ObjectId[] objectIdList = editor.SelectAll(selectionFilter).Value.GetObjectIds(); for (int i = 0; i < objectIdList.Length; i++) { ObjectId objectId = objectIdList[i]; BlockReference blockReference = (BlockReference)objectId.GetObject(OpenMode.ForRead); BlockTableRecord blockTableRecord = (BlockTableRecord)blockReference.BlockTableRecord.GetObject(OpenMode.ForRead); //Listas de objetos List<Line> lineList = new List<Line>(); List<DBText> dBTextList = new List<DBText>(); List<Arc> arcList = new List<Arc>(); List<Hatch> hatchList = new List<Hatch>(); foreach (ObjectId item in blockTableRecord) { DBObject dBObject = (DBObject)item.GetObject(OpenMode.ForRead); if (dBObject.GetType() == typeof(Line)) { lineList.Add((Line)dBObject); } else if (dBObject.GetType() == typeof(DBText)) { dBTextList.Add((DBText)dBObject); } else if (dBObject.GetType() == typeof(Arc)) { arcList.Add((Arc)dBObject); } else if (dBObject.GetType() == typeof(Hatch)) { hatchList.Add((Hatch)dBObject); } } } }
But if I use use the End / Start Point captured in "LineList" for example, he did not return the value if the block was moved.
How could I fix it?
Now, thanks!
Solved! Go to Solution.
Solved by norman.yuan. Go to Solution.
Solved by norman.yuan. Go to Solution.
The entities you collected in the Lists is from Block definition (BlockTablerecord), so the geometric references of these entities are defined in relationship with the Block definition's insertion point as base point (0,0,0). Since you actually want is goemetric reerences of entities in BlockReference, so you need to transform the geometric references obtained from the entities iin BlockTableRecord according to position of BlockReference.
Norman Yuan
It is fairly simple to calculate points based on the diference (Vector3d) between Block definition base point (0,0,0) and BlockReference's insertion point. Here is the code sample I quickly assemled:
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; [assembly: CommandClass(typeof(EntPositionInBlk.MyCommands))] namespace EntPositionInBlk { public class MyCommands { [CommandMethod("EntInBlk")] public void GetEntityPositionInBlock1() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ObjectId blkId = PickBlock(ed); if (blkId == ObjectId.Null) { ed.WriteMessage("\n*cancel*\n"); return; } ShowEntitiesInBlock(blkId, doc); } #region private methods private ObjectId PickBlock(Editor ed) { ObjectId blk = ObjectId.Null; PromptEntityOptions opt = new PromptEntityOptions("\nPick a block: "); opt.SetRejectMessage("Not a block reference!"); opt.AddAllowedClass(typeof(BlockReference), true); PromptEntityResult res = ed.GetEntity(opt); if (res.Status == PromptStatus.OK) { blk = res.ObjectId; } return blk; } private void ShowEntitiesInBlock(ObjectId id, Document dwg) { using (Transaction tran = dwg.Database.TransactionManager.StartTransaction()) { BlockReference bRef = (BlockReference)tran.GetObject(id, OpenMode.ForRead); BlockTableRecord br = (BlockTableRecord)tran.GetObject(bRef.BlockTableRecord, OpenMode.ForRead); //BlockRefernece's insertion point Point3d insPt = bRef.Position; foreach (ObjectId entId in br) { //for smplicity, only list Line entity in the block Line line = tran.GetObject(entId, OpenMode.ForRead) as Line; if (line != null) { //Line's StartPoint in block definition Point3d pt = line.StartPoint; //Block definition base point Point3d p = new Point3d(0.0,0.0,0.0); //Actual StartPoint in the BlockReference Point3d point = pt.Add(p.GetVectorTo(insPt)); dwg.Editor.WriteMessage( "\nStart point: x={0}, y={1}, z={2}", point.X, point.Y, point.Z); } } tran.Commit(); } dwg.Editor.WriteMessage("\n"); } #endregion } }
Pay attention to the comments made in ShowEntitiesInBlock() method.
HTH
Norman Yuan
But that will only provide a translation, ignoring any possible rotation, scaling, or mirroring, so why not skip that and use the BlockTransform from the BlockReference? That's what I would do. (don't really have time at this moment to work up a sample)
You are rignt, using TransformBy would make the code simpler, like this:
private void ShowEntitiesInBlock_A(ObjectId id, Document dwg) { using (Transaction tran = dwg.Database.TransactionManager.StartTransaction()) { BlockReference bRef = (BlockReference)tran.GetObject(id, OpenMode.ForRead); BlockTableRecord br = (BlockTableRecord)tran.GetObject(bRef.BlockTableRecord, OpenMode.ForRead); Matrix3d mat = bRef.BlockTransform; foreach (ObjectId entId in br) { //for smplicity, only list Line entity in the block Line line = tran.GetObject(entId, OpenMode.ForRead) as Line; if (line != null) { Point3d pt = line.StartPoint; Point3d point = pt.TransformBy(mat); dwg.Editor.WriteMessage( "\nStart point: x={0}, y={1}, z={2}", point.X, point.Y, point.Z); } } tran.Commit(); } dwg.Editor.WriteMessage("\n"); }
I was simply show the OP with quick code that the geometric references in BlockTableRecord is not what he wants.
However, if the BlockReference could be rotated/mirrored, the just using BlockReference.BlcokTransform would not enough. One has do Point3d.TransformedBy(Matrix3d) futher according to how the rotating/mirroring was done (rotating/mirroring base point/line, rotating angle... Since BlockReference itself does not keeps track where rotating base point is if it was rotated, it is simply not possible to construct a Matrix3d to transform the points of the line.
This leads to another (more reliable) solution: one can explode the BlockReference (which only create a set entities in the same location as the blockrefeence), then loop through these entities to obtain wanted geometric references, and finally erased those entities from BlockReference explosion.
Norman Yuan
No disrespect Norman, but I disagree. I have a number of functions using the BlockTransform in exactly this manner, and it accounts for everything. The BlockTransform is a combined matrix which covers rotation, displacement, mirroring and scaling, and ultimately the basepoint used for any of those operations is irrelevant. (You could think of it as the BlockTransform that AutoCAD stores in the BlockReference has been Normalized to the Block's Insertion point, though I think that may be over-simplifying what actually happens behind the curtain)
One example that comes to mind is one of my Overrules which adds a second Grip to the "Endpoint" of a block. This particular block can be rotated (even rotated in 3d, Normal.Z < 1), is Always scaled non-uniformly, and could be mirrored, but all I do is transform the known BlockTableRecord Point3d by the BlockTransform and the grip always comes in at the right place.
And, yes, I also thought of the option of exploding the block, but I would only resort to that if it were absolutely necessary, and in this case I don't believe it is. I just don't like creating and erasing all those temporary entities when there is an easier way.
Hi CBC,
You're correct that the BlockTransform matrix is the accumulation of every tranformation applied to the BlockReference. That is all you should need to translate from the block coordinate system to the world coordinate system. And, of course, you need to iteratively apply the block transform matrices for nested blocks.
I'm replying to this post because I have a recollection that you also have to account for the possibility that BlockTableRecord.Origin may not be (0,0,0) - which is an additional translation you may have to apply. Its rare, but I have come across drawings where this is the case.
A good point I had not thought of, Stephen. My Overrules (and pretty much all of my other code) are dealing with blocks that my code creates, so I never encountered that problem. Maybe something I should look at to make some of my base helper functions that much more robust, just in case.
And Norman, regardless of the BlockTransform discussion, thanks for posting the example, I really didn't have time to mess with it today. (which by now was actually yesterday. it's 2:00 AM my time)
Sorry to be posting below, I could not edit my answer.
Thank you! It worked. I had a small problem if the block has been rotated. Why use the angle of the existing text, I use the points of the line parallel to the text to find the slope.
Thank you all helped a lot!