How to apply a selection filter on a ObjectIDCollection?

How to apply a selection filter on a ObjectIDCollection?

Anonymous
Not applicable
3,585 Views
8 Replies
Message 1 of 9

How to apply a selection filter on a ObjectIDCollection?

Anonymous
Not applicable

Hi there,

I wonder if its possible to apply a filter to a subset of entities (that could be the result of a previous filtering).

0 Likes
Accepted solutions (2)
3,586 Views
8 Replies
Replies (8)
Message 2 of 9

_gile
Consultant
Consultant

Hi,

 

By my side, i think Linq provides many useful extension methods to deal with all types of collection, those which do not implement IEnumerable<T> as ObjectIdcollection may be converted to IEnumerable<ObjectId> with Cast<ObjectId>>().

Look at the Linq extension methods here:

http://msdn.microsoft.com/en-us/library/9eekhta0.aspx

 

To filter a collection the Where extension methode is quite easy to use passing it a function (delegate) returning true for the elements you want to keep in the collection.

 

Example filtering the Line entities in an ObjectIdCollection (called ids):

ids.cast<ObectId>()
    .Where(id => id.ObjectClass.DxfName == "LINE")

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 9

Anonymous
Not applicable

Hi Gilles,

 

I like your Linq approach, but the filtering criterias I need aren't provided by id.ObjectClass

I want to filter for XData, Layer and others.

0 Likes
Message 4 of 9

_gile
Consultant
Consultant
Accepted solution

It was just an example, for xdata, layer, you'll need to Open the objects. This can be done mapping the ObjectIdCollection to Entities with the Select() method, then adapting the Where() argument function as you need.

 

another example to be used within a Transaction (tr):

ids.Cast<ObjectId>()
    .Select(id => (Entity)tr.GetObject(id, OpenMode.ForRead))
    .Where(ent => ent.Layer == layerName)

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 9

Anonymous
Not applicable
Accepted solution

Initially I tried to work with SelectionFilter (TypedValues) only and select the entities via editor.selectAll(SelectionFilter), since its performance is great. But there are some limits, e.g. I can't filter for entities with certain XData.

So I switched to work with Predicates (Expression<Func<EntityBoolean>>) that also can be combined via PredicateBuilder. The mapping from ObjectIDCollection to Entities is as you suggested:

IEnumerable<Entity> ents = objIds.Select(id => tr.GetObject(id, OpenMode.ForRead)).Cast<Entity>();

 The predicate is applied afterwards:

IEnumerable<Entity> appliedEnts = ents.AsQueryable().Where(predicate);

This solution provides for me the most flexibility with only a little lack of performance, since I only need to open one Transaction.

Thanks!

0 Likes
Message 6 of 9

DiningPhilosopher
Collaborator
Collaborator

@Anonymous wrote:

Initially I tried to work with SelectionFilter (TypedValues) only and select the entities via editor.selectAll(SelectionFilter), since its performance is great. But there are some limits, e.g. I can't filter for entities with certain XData.


You can filter on the XData application name to reduce the selection to only entities having one or more application names, and with that, reduce the number of entities that you must open to examine for more specific criteria, and that's probably a faster solution than having to open all objects selected. Remember that the user can type 'All' to select everything and that means in a large drawing, you will have to open every single object in the current space.

0 Likes
Message 7 of 9

Anonymous
Not applicable

I benchmarked this with a large drawing ~ 20.000 entities. The performance loss is not really worth mention it.

~0.07 sec vs ~ 0.17 sec.

For that I can not only filter for XData Regappnames but for other XData formats too.

0 Likes
Message 8 of 9

_gile
Consultant
Consultant

Hi,

 

Another way is to use the Editor.SelectionAdded event. You can call the Editor.SelectAll() method from a Transaction and combinate classical SelectionFilter with SelectionAdded event filtering.

 

Here's a little example (I 'm more comfortable giving example than trying to explain in English) where entities are classicaly filtered with layer and registered application and a SelectionAdded event handler is used to filter on the first xdata value: (1001, 'foo")

 

        [CommandMethod("Test", CommandFlags.Modal)]
        public void Test()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            try
            {
                SelectionFilter filter =
                    new SelectionFilter(new TypedValue[] { new TypedValue(8, "Layer1"), new TypedValue(1001, "RegApp") });
                ed.SelectionAdded += OnSelectionAdded;
                PromptSelectionResult psr = ed.SelectAll(filter);
                if (psr.Status != PromptStatus.OK)
                    return;
                ed.WriteMessage("\n{0} entities selected", psr.Value.Count);
            }
            finally
            {
                ed.SelectionAdded -= OnSelectionAdded;
            }
        }

        void OnSelectionAdded(object sender, SelectionAddedEventArgs e)
        {
            ObjectId[] ids = e.AddedObjects.GetObjectIds();
            for (int i = 0; i < ids.Length; i++)
            {
                using (Entity ent = (Entity)ids[i].Open(OpenMode.ForRead))
                {
                    ResultBuffer xdata = ent.GetXDataForApplication("RegApp");
                    TypedValue[] tvs = xdata.AsArray();
                    if (tvs[0].TypeCode != 1000 || tvs[0].Value != "foo")
                    {
                        e.Remove(i);
                    }
                }
            }
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 9 of 9

DiningPhilosopher
Collaborator
Collaborator

@Anonymous wrote:

I benchmarked this with a large drawing ~ 20.000 entities. The performance loss is not really worth mention it.

~0.07 sec vs ~ 0.17 sec.

For that I can not only filter for XData Regappnames but for other XData formats too.


Using LINQ against the result of SelectionSet.GetObjectIds() is essentially the same process as using it against all objects in a BlockTableRecord, so I usually pre-filter with a selection set/selection filter first and then use LINQ against the resulting ObjectIds to filter for more specific criteria.

 

I've used that approach with drawings containing betwen 250,000 and 500,000 objects with great sucess and there  was a perceptible difference between that and directly filtering the BlockTableRecord.  Of course, if you don't deal with drawings that large, it may not matter.

0 Likes