Finding the names of the blockrefs making up an array together with its position

Finding the names of the blockrefs making up an array together with its position

rean.kremer
Contributor Contributor
1,571 Views
6 Replies
Message 1 of 7

Finding the names of the blockrefs making up an array together with its position

rean.kremer
Contributor
Contributor

Hello all,

 

I'm running into the problem that I can't find out what types of blockrefs (by name) are positioned where in an associative array. No matter what I seem to do, I get an anonymous blockref name. However, when I explode the block in the drawing manually, the underlying blocks all have the correct name, so the data should be somewhere. This data is needed to automatically count the number of certain blocks in a drawing with arrays.

 

As a workaround currently I use sourceEntities, but that has as a drawback that I have to assume that the full array consists of a single type of blockref. If one of the entities is replaced this method no longer works accurately. 

 

It would be really great if someone can help me find out how to get the block names. To start off, I created a minimal coding examples shown below and I've also attached an example drawing with the type of array I'm looking to investigate. 

 

Any help is more than welcome.

code:

 

 

[CommandMethod("ArrayTest")]
    public static void ArrayTest()
    {
        Document doc = Application.DocumentManager.MdiActiveDocument;
        using (Transaction transaction = doc.Database.TransactionManager.StartTransaction())
        {
            TypedValue[] filterList = new TypedValue[1];
            filterList[0] = new TypedValue(0, "INSERT");
            SelectionFilter filter = new SelectionFilter(filterList);
            PromptSelectionResult result = doc.Editor.SelectAll(filter);
            if (result.Status == PromptStatus.OK)
            {
                foreach (ObjectId objectId in result.Value.GetObjectIds())
                {
                    if (AssocArray.IsAssociativeArray(objectId))
                    {
                        AssocArray Array = AssocArray.GetAssociativeArray(objectId);
                        Autodesk.AutoCAD.DatabaseServices.Entity arrayEntity = transaction.GetObject(Array.EntityId, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Entity;
                        AssocArrayPathParameters Arrayparams = Array.GetParameters() as AssocArrayPathParameters;
                        Curve3d arrayPath = Arrayparams.Path.Curve;
                        //open assocArray as blockref so you can explode
                        using (BlockReference assocBlockRef = objectId.GetObject(OpenMode.ForWrite) as BlockReference)
                        {
                            using (DBObjectCollection blockRefs = new DBObjectCollection())
                            {
                                assocBlockRef.Explode(blockRefs);
                                if (blockRefs != null && blockRefs.Count > 0)
                                {
                                    foreach (BlockReference blockref in blockRefs)
                                    {
                                        doc.Editor.WriteMessage("\nBlockref.Name: {0}\n", blockref.Name);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            transaction.Commit();
        }
    }

 

 

0 Likes
Accepted solutions (1)
1,572 Views
6 Replies
Replies (6)
Message 2 of 7

norman.yuan
Mentor
Mentor

@rean.kremer wrote:

...

As a workaround currently I use sourceEntities, but that has as a drawback that I have to assume that the full array consists of a single type of blockref. If one of the entities is replaced this method no longer works accurately. 

...


Firstly, I am not sure what exactly you need to find out: the block's name (as subentities in the AssocArray), or the block that has to be at certain position (i.e. I am not sure why you mention "...its position"). It seems the array may have different blocks as subentities. In this case your claim as I quoted above ("... I have to assume that the full array consists of a single type of blockref...") is not correct.

 

The SubEntities is an ObjectIdCollection of element source entity of the array. Say, if your array is made from reference of blockA and reference of blockB, and each of them is repeated 10 times along a curve, so you would see 10 blockA and 10 blockB as the array. However, the AssocArray.SubEntities propery ONLY has 2 objectIds in it: one is BlockReference of blockA and the other is BlockReference of blockB.

So, if you want to know the block name of blockreference or blockreferences used to build the array, SubEntities property is the piece of data for you to use.

 

While exploding could gives you blockreference count, you should be able to avoid exploding and simply use ArrayParameter to work out the count.

 

When array is built, AutoCAD does not simply add the block reference according the array parameters into database (that way, each element would the the a new block reference to the same blockA or blockB and each element would have its own ObjectId). Instead, AutoCAD create a new anonymous block accoring to the array parameters, using the source element. So, no extra entities are added to database because of the array, except for the anonymous block, representing the array. This way, when array is changed by user, new anonymous block is created, and the old anonymous would be cleared automatically when drawing is closed.

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 7

rean.kremer
Contributor
Contributor

Hello norman.yuan,

 

First off, thank you for taking the time to respond to me and trying to help me.

 

I'll try to state my goal a bit more clear: I would like to know the position and name of every blockreference making up the array. 

 

For the example I attached in my previous message, this means I would like to know that the topmost blockreference is located at (100.986, 201.073, 0) and has a name of "testRectangle1". I'm able to find this out manually by exploding the array, but I would like to do this through .Net. Through exploding I can already find the positions of the blockreferences making up the array, but I'm not able to find their corresponding names.

 

Regarding what you said about subentities: I saw no way to find out how to get this. Did you mean SourceEntities perhaps? If so, the problem with that is that if I only know which two names make up the array, it's not possible for me to find out to which location they correspond. Looking at the example, I have 8 rectangles in an array. 2 and 5 are of type B while the others are of type A. If I understand it correctly, SourceEntities only gives me that type A and type B are part of the array, not which number/location in the array they are at. So that gives me less info than I need.

 

Moreover, in the example I attached previous I only get 1 SourceEntity through .Net, even though it is made up from 2 different BlockReferences. I think this is because I made the array using 1 Blockreference and then used replace item to put in the other blockreferences. Either way, this then doesn't consistently tell me which blocks the array is made of even if there is only 1 SourceEntity.

 

Since the Blockreference name is displayed correctly when I explode the array manually I still think it should be possible through .Net also.  Somehow.

0 Likes
Message 4 of 7

norman.yuan
Mentor
Mentor

Ah, yes, I did mean "SourceEntities" but somehow entered as "SubEntities". I do not see there is an attached drawing in your original post. I am curious how an entity could be part of array, but is not source entity (does this non-SourceEntity follow the Array when you do some change, such as drag the associated path?). Can you upload the drawing?

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 7

norman.yuan
Mentor
Mentor
Accepted solution

if only you need to know the location of each element of the array, you can call AssocArray.getItems(true/false) to obtain a list of ItemLocators, and then call AssocArray.GetItemTransform(ItemLocator) to find out the element's location ON THE PATH (!). Depending on the relative position of the source entity (block reference, in your case, its offset from the path, rotation relative to the path, if the array's Alihgnments is set to true or false) to the path (i.e. the block's insertion point could sit on the path, or offset from the path...), you can then calculate its exact/absolute location in the drawing space.

following code find element locations ALONG the path:

 

        public static void LocateArrayItems()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;

            var res = ed.GetEntity("\nSelect an entity array:");
            if (res.Status == PromptStatus.OK)
            {
                if (AssocArray.IsAssociativeArray(res.ObjectId))
                {
                    var array = AssocArray.GetAssociativeArray(res.ObjectId);

                    var arrayParams= array.GetParameters() as AssocArrayPathParameters;
                    var basePt = arrayParams.BasePoint.Point;
                    ed.WriteMessage($"\nArray base point: {basePt.ToString()}");

                    var locators = array.getItems(true);
                    var i = 1;
                    foreach (var loc in locators)
                    {
                        var matrix = array.GetItemTransform(loc);
                        var itemPt = basePt.TransformBy(matrix);
                        ed.WriteMessage($"\nItem {i} location: {itemPt}");
                        i++;
                    }
                }

            }
        }

 

Yes, I can see the calculation is quite complicated. Also, I have not digged out how to find out the replacement information is covered in the various classes related to AssocArray. Maybe, the information on replacement element is not exposed in .NET API?

 

In the end, it looks like, exploding would be much simpler approach. The only thing I can point out in the code of your original post is that you do not explode the AssocArray as BlockReference, because there is a catch: if the array element is a composite emelemt (i.e. more than one entities, thus the SourceEntities.Count would be > 1. In your case, you could have 2 or 3 block references combined as one array element), you would have also recusively explode each element. You should call static AssocAaary.Explode() method. Actually, seeing this method being static, I even think this was the API developers' thought to provide an easier way to get each element's information.

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 6 of 7

rean.kremer
Contributor
Contributor

It works! Thank you so much for your help, I've spend days looking for ways to do this and with your help I've finally done it. 

 

Using the following code allows me to find both the name and the corresponding position of the blockreference for blocks in an array:

 

[CommandMethod("ArrayTest2")]
    public static void ArrayTest2()
    {
        Document doc = Application.DocumentManager.MdiActiveDocument;
        using (Transaction transaction = doc.Database.TransactionManager.StartTransaction())
        {
            TypedValue[] filterList = new TypedValue[1];
            filterList[0] = new TypedValue(0, "INSERT");
            SelectionFilter filter = new SelectionFilter(filterList);
            PromptSelectionResult result = doc.Editor.SelectAll(filter);
            if (result.Status == PromptStatus.OK)
            {
                foreach (ObjectId objectId in result.Value.GetObjectIds())
                {
                    if (AssocArray.IsAssociativeArray(objectId))
                    {
                        AssocArray Array = AssocArray.GetAssociativeArray(objectId);
                        Autodesk.AutoCAD.DatabaseServices.Entity arrayEntity = transaction.GetObject(Array.EntityId, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Entity;
                        AssocArrayPathParameters Arrayparams = Array.GetParameters() as AssocArrayPathParameters;
                        Arrayparams.GetItemCount(out string expression, out string evaluatorId);

                        Curve3d arrayPath = Arrayparams.Path.Curve;
                        ObjectIdCollection oidcol = AssocArray.Explode(objectId);
                        if (oidcol != null && oidcol.Count > 0)
                        {
                            foreach (ObjectId oid in oidcol)
                            {
                                BlockReference blockref = transaction.GetObject(oid, OpenMode.ForRead) as BlockReference;
                                BlockTableRecord btr = transaction.GetObject(blockref.DynamicBlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
                                doc.Editor.WriteMessage("\nBlockref.Name: {0}\n", btr.Name);
                                doc.Editor.WriteMessage("\nBlockref.Position: {0}\n", blockref.Position);
                            }
                        }
                    }
                }
            }
            transaction.Abort();
        }
    }

 

With regards to the SourceEntities being equal to 1 despite there being more, I'm still not sure why that is the case. Running the above code in debug mode still gives tells me that SourceEntities is equal to one on my drawing. It doesn't really matter to me anymore, as I can now do what I need, but it is definitely weird....

 

Speaking of the .dwg: I've done another attempt at adding it to this reply. I'm not sure what happened in the initial post, sorry about that.

 

Thanks again!

 

0 Likes
Message 7 of 7

norman.yuan
Mentor
Mentor

@rean.kremer wrote:

...

With regards to the SourceEntities being equal to 1 despite there being more, I'm still not sure why that is the case. Running the above code in debug mode still gives tells me that SourceEntities is equal to one on my drawing. It doesn't really matter to me anymore, as I can now do what I need, but it is definitely weird....

...


Since the element in your array entity is a single entity (a blockreference), so the SourceEntities.Count=1. The replacement entity (it could be any kind entity, but in your case, it is also a blockreference) is just a replacement, being placed there according to the path parameters. It is not a SourceEntity. In fact, you can replace all elements with different blockreference, yet the array still keeps its original blockreference in SourceEntities. That is why Array can be reset back. As I said, in previous reply: I just could not find where/how the replacement information is stored, and suspect it is not available in Assoc API, thus AssocArray.Explode() is the way you have to go, so far.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes