.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to apply a selection filter on a ObjectIDCollection?

8 REPLIES 8
SOLVED
Reply
Message 1 of 9
pymote
2781 Views, 8 Replies

How to apply a selection filter on a ObjectIDCollection?

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).

8 REPLIES 8
Message 2 of 9
_gile
in reply to: pymote

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
pymote
in reply to: _gile

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.

Message 4 of 9
_gile
in reply to: pymote

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

Message 5 of 9
pymote
in reply to: _gile

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!

Message 6 of 9
DiningPhilosopher
in reply to: pymote


@pymote 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.

Message 7 of 9
pymote
in reply to: DiningPhilosopher

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.

Message 8 of 9
_gile
in reply to: pymote

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

Message 9 of 9
DiningPhilosopher
in reply to: pymote


@pymote 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.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost