Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

selection set filter fails with enames and handles

13 REPLIES 13
SOLVED
Reply
Message 1 of 14
serag.hassouna
1699 Views, 13 Replies

selection set filter fails with enames and handles

Hello,
I think I've found a limiting issue with ssget 's filter list argument.
I've created a filter list of some entity names to allow the user to select from them only, its form is like that

((-4 . "<OR") (-1 . <Entity name: 7ff4ca605d90>) (-1 . <Entity name: 7ff4ca605da0>) (-4 . "OR>"))

and it was stored in a symbol called lsp.
however,

(ssget lst)

returns nil immediately and doesn't work.
so I tried using handles instead, the filter list was like this

((-4 . "<OR") (5 . "1E9") (5 . "1EA") (-4 . "OR>"))

and was stored in lst,
Again, (ssget lst) returns nil the same as before.
so, I've made two last trials to validate this,
The first one have been with only one entity name association list (enclosed in a list of course), e.g

((-1 . <Entity name: 7ff4ca605d90>))

it failed,
The second and last trial have been with the handle's association list, e.g

((5 . "1E9"))

Also, it failed.
_______________________
Does that mean that we can't set certain "known before" objects "to-be-selected-from" interactively, regardless of their type, color, layer, and their properties in general?

13 REPLIES 13
Message 2 of 14
_gile
in reply to: serag.hassouna

hi,

 

You cannot use an ename or a handle in a selection filter.

From the developer's documentation:

The ssget function recognizes all group codes except entity names (group code -1), handles (group code 5), and xdata (group codes greater than 1000)

 

On the other hand, you do not need to select entites you already know the ename or handle, just directly use the ename (possibly got from the handle with handent) or add it to an existing selection set with ssadd.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 14
serag.hassouna
in reply to: _gile

Thanks for pointing me to what the documentation says about that.
On the other hand, this means that the interactive feature can't be used in such case, as ssadd doesn't allow the user to select from them interactively the same way as ssget.

Message 4 of 14
_gile
in reply to: serag.hassouna

I still cannot understand the need of an "interactive feature" to prompt the user for an object the program already knows.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 5 of 14
ВeekeeCZ
in reply to: serag.hassouna

You can make a simple (while (ssget ":S") ...) loop where you (sssetfirst) or set 'highlight property to all entities that match your group.

Message 6 of 14
serag.hassouna
in reply to: _gile

OK, this post (with the "interactive feature" issue added to it by me, coming to it later) is the real background for the idea of "selection of what a program really knows before".
He wants to make some certain blocks with certain block attribute values selected on screen to be able to deal with them in the "properties palette", the somehow easy part is to transfer the elements within the ActiveX Selection Set to an AutoLISP one, then applying sssetfirst on it.
Quoting from him from the 2nd message:

((the problem is that object can be added to selection set for
different reasons, for my case, I was addeing block ref to my selection set
based on certain attribute values. I dont think it can be achieved using
available filters))
so, I though in a hurry that a general quick workaround to add them to the AutoLISP selection set would be by selecting them using their entity names within the filter of ssget, not knowing that ssget doesn't accept entity names and handles as valid filtering values.
However, knowing that ssget can be used to let the user select objects interactively and under the filtering criteria, I thought about how to not completely lose this feature.
I've even made a prototype for that with a function ssfiltered, whose only argument is a previously made AutoLISP selection set (the filter selection set), and the function's task is to allow the user to interactively select objects on screen with a crossing window, and it selects only those objects that are elements in the filter selection set and reside in the crossing window.
This crossing window can be created once (by 2 subsequent left clicks on the mouse), and the function will apply sssetfirst on the filter result.

Here's the code of it

;Author: Serag Hassouna
;|Purpose:a prototype to mimic the interactive feature of ssget function (with window crossing option)
The user is allowed to inetactively select any listing of objects, and the command will filter them
relative to a previous selection set and returns that filtered selection set.
The feature of adding/removing objects muliple times is not added to this proptotype.
The crossing window here is just a yellow rectangle.|; (defun ssfiltered (ssallelems / i allelems bbelems minext maxext curgr playloop counter pt1 pt2 inbbelems ssret ssretlen) ;;inselbox-p : returns true if the object is in the selection box or intersects with it;; (defun inselbox-p (sbpt1 sbpt2 opt1 opt2 / selboxpts opts yup ydwn) (setq selboxpts (vl-sort (list sbpt1 sbpt2) (function (lambda (a b) (< (car a) (car b)))))) (setq sbpt1 (car selboxpts) sbpt2 (cadr selboxpts)) (if (> (cadr sbpt1) (cadr sbpt2)) (progn (setq yup (cadr sbpt1) ydwn (cadr sbpt2)) (setq sbpt1 (list (car sbpt1) ydwn (last sbpt1)) sbpt2 (list (car sbpt2) yup (last sbpt2))) ) );swap y-coordinates to get the upper rightmost and the lower leftmost points (mandatory) (setq opts (vl-sort (list opt1 opt2) (function (lambda (a b) (< (car a) (car b)))))) (setq opt1 (car opts) opt2 (cadr opts)) (cond ( (or (and (< (car opt1) (car sbpt1)) (< (car opt2) (car sbpt1))) (and (> (car opt1) (car sbpt2)) (> (car opt2) (car sbpt2))) (and (< (cadr opt1) (cadr sbpt1)) (< (cadr opt2) (cadr sbpt1))) (and (> (cadr opt1) (cadr sbpt2)) (> (cadr opt2) (cadr sbpt2))) ) nil ) (t t) ) ) ;;__________________________________________________________________________________;; ;;recvecs : draws a rectangle with vectors from its upper and lower opposite points;; (defun recvecs (pt1 pt2 color / pts) (setq pts (vl-sort (list pt1 pt2) (function (lambda (a b) (< (car a) (car b)))))) (setq pt1 (car pts) pt2 (cadr pts)) (grvecs (list color pt1 (list (car pt2) (cadr pt1) 0))) (grvecs (list color pt1 (list (car pt1) (cadr pt2) 0))) (grvecs (list color (list (car pt1) (cadr pt2) 0) pt2)) (grvecs (list color (list (car pt2) (cadr pt1) 0) pt2)) ) ;;__________________________________________________________________________________;; (setq playloop t) (setq counter 2 i 0) (repeat (sslength ssallelems) (setq allelems (cons (ssname ssallelems i) allelems)) (setq i (1+ i)) ) (setq bbelems (mapcar (function (lambda (x) (vla-getboundingbox (vlax-ename->vla-object x) 'minext 'maxext) (setq minext (vlax-safearray->list minext) maxext (vlax-safearray->list maxext)) (list x minext maxext) ) ) allelems ) ) (princ "\nSelect Objects: ") (while (progn (setq curgr (grread t (+ 1 2 4 8))) (cond ((and (= (car curgr) 3) (/= counter 0)) (progn (setq counter (1- counter)) (if (= counter 1) (setq pt1 (cadr curgr)) (setq pt2 (cadr curgr)) ) (if pt2 (progn (setq inbbelems (mapcar (function (lambda (x) (list (car x) (inselbox-p pt1 pt2 (cadr x) (last x))))) bbelems)) (mapcar (function (lambda (x) (redraw (car x) 4))) inbbelems);remove highlight (setq inbbelems (vl-remove-if (function (lambda (x) (= (cadr x) nil))) inbbelems)) ) ) ) );1st condition [left click on mouse] ((= counter 0) (setq playloop nil));2nd condition [the user clicked on 2 points] ((and (= (car curgr) 5) (/= counter 0)) (progn (if pt1 (progn (redraw) (recvecs pt1 (cadr curgr) 2) ; (setq inbbelems (mapcar (function (lambda (x) (list (car x) (inselbox-p pt1 (cadr curgr) (cadr x) (last x))))) bbelems)) (mapcar (function (lambda (x) (if (null (cadr x)) (redraw (car x) 4) (redraw (car x) 3) ) ) ) inbbelems ) ; ) ) );progn );the cursor moving (t nil);debug condition );cond playloop );progn );while (redraw) (if (not (null inbbelems)) (progn (setq ssret (ssadd)) (mapcar (function (lambda (x) (ssadd (car x) ssret))) inbbelems) (setq ssretlen (sslength ssret)) (princ (strcat (itoa ssretlen) " object" (if (= ssretlen 1) " is" "s are") " selected\n")) ) (princ "\nNo object is selected\n") ) (sssetfirst nil ssret) );end defun


and I'm really sorry for length of this reply.

Message 7 of 14
serag.hassouna
in reply to: ВeekeeCZ

You're right about highlighting, using the Highlight method is wider in its application range than redraw, as it can be applied with groups and ActiveX selection sets.
However, as for looping with ssget, the fundamental limitation issue with "the filter list" is not addressed.
Hence, the choice goes for ssadd to construct the so called "filter selection set", to be used later, i.e. to be selected from.
Then the dynamic effects can be implemented within a grread loop.

Message 8 of 14
ronjonp
in reply to: serag.hassouna

I might be missing something here, but what does all this code achieve over a simple (ssget) with a filter?

Message 9 of 14


@serag.hassouna wrote:

.... a function ssfiltered, whose only argument is a previously made AutoLISP selection set (the filter selection set), and the function's task is to allow the user to interactively select objects on screen with a crossing window, and it selects only those objects that are elements in the filter selection set and reside in the crossing window.
.... the function will apply sssetfirst on the filter result.

....


 

Will this do what you're after?  It's not limited to using a single crossing window, but allows any kind of selection options, and when done, leaves you with only those selected objects selected/gripped/highlighted that were also in the 'ssbefore'-argument selection set.

(defun ssfiltered (ssbefore / ssnew n ent)
  (prompt "\nTo get only objects from the existing-selection-set argument within a wider selection,")
  (if (setq ssnew (ssget))
    (progn ; then
      (repeat (setq n (sslength ssnew))
        (setq ent (ssname ssnew (setq n (1- n))))
        (if (not (ssmemb ent ssbefore)) (ssdel ent ssnew))
      ); repeat
      (sssetfirst nil ssnew)
    ); progn
  ); if
); defun
Kent Cooper, AIA
Message 10 of 14
serag.hassouna
in reply to: ronjonp

ssget 's filter is limited, to overcome its limitation, the proposed procedure is to fill a "holistic" selection set with all the elements that match the custom filtering criteria, bearing in mind that this custom filtering criteria has no equivalent filter list to express it. I've provided a vivid example in my previous reply.
This holistic selection set is what I call "filter selection set" when it's supplied to ssfiltered (both, my prototype and @Kent1Cooper 's version ).
Then, you can refine and pick any listing of objects from this holistic one.

Message 11 of 14

Yes, with a small difference, it doesn't highlight the valid objects - in real time - when they're in the selection region.
But all in all, it's more compact & simple.
Really shame on me Smiley Very Happy, instead of constructing a new ssget from the ground and thinking a lot about all the internal low level logic, I didn't think about using another ssget.

Message 12 of 14
_gile
in reply to: serag.hassouna

The .NET API provides the Editor.SelectionAdded event which allows to remove entities from the selction set in real time.

 

        static PromptSelectionResult GetFilteredSelection(Editor ed, HashSet<ObjectId> allowed)
        {
            SelectionAddedEventHandler filter = (_, e) =>
            {
                var objs = e.AddedObjects;
                for (int i = 0; i < objs.Count; i++)
                {
                    if (!allowed.Contains(objs[i]))
                        e.Remove(i);
                }
            };
            ed.SelectionAdded += filter;
            var selection = ed.GetSelection();
            ed.SelectionAdded -= filter;
            return selection;
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 13 of 14
_gile
in reply to: _gile

You can download the attached ZIP file, unblock it, extract the FilteredSelectionLispFunction.dll and NETLOAD it from AutoCAD.

It defines a new ssfiltered LISP function which requires a list of enames as arguments.

 

Testing example:

(while (setq e (car (entsel))) (setq l (cons e l)))
(setq s (ssfiltered l))
(sssetfirst nil s)

 

 

The whole C# code defining the LISP function if you prefer compile it by yourself.

using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Core.Application;

namespace FilteredSelectionLispFunction
{
    public class LispFunctions
    {
        static TypedValue nil = new TypedValue((int)LispDataType.Nil);

        [LispFunction("ssfiltered")]
        public static TypedValue GetFilteredSelection(ResultBuffer resbuf)
        {
            if (resbuf == null) return nil;
            var args = resbuf.AsArray();
            var allowed = new HashSet<ObjectId>();
            for (int i = 1; i < args.Length - 1; i++)
                if (args[i].TypeCode == (int)LispDataType.ObjectId)
                    allowed.Add((ObjectId)args[i].Value);
            if (allowed.Count == 0) return nil;
            var ed = AcAp.DocumentManager.MdiActiveDocument.Editor;
            SelectionAddedEventHandler filter = (_, e) =>
            {
                var objs = e.AddedObjects;
                for (int i = 0; i < objs.Count; i++)
                    if (!allowed.Contains(objs[i].ObjectId))
                        e.Remove(i);
            };
            ed.SelectionAdded += filter;
            var selection = ed.GetSelection();
            ed.SelectionAdded -= filter;
            if (selection.Status == PromptStatus.OK)
                return new TypedValue((int)LispDataType.SelectionSet, selection.Value);
            else
                return nil;
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 14 of 14
serag.hassouna
in reply to: _gile

Well, I ought to thank you twice <here at least>, firstly, because the filtering behavior works well, and secondly because before this replay I had no working background of .NET with AutoCAD, as this replay encouraged me to get my hands on it.
_________
After some trials, I've modified your code a little to enable ssfiltered to take an AutoLISP selection set as a direct argument.
Here it is

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Core.Application;

namespace FilteredSelectionLispFunction
{
    public class LispFunctions
    {
        static TypedValue nil = new TypedValue((int)LispDataType.Nil);

        [LispFunction("ssfiltered")]
        public static TypedValue GetFilteredSelection(ResultBuffer resbuf)
        {
            var ed = AcAp.DocumentManager.MdiActiveDocument.Editor;

            if (resbuf == null) return nil;
            
            var args = resbuf.AsArray(); //selection set
            var allowed = new HashSet<ObjectId>(); //the members
            /*Mod Zone*/
            if (args.Length > 1)
            {
                ed.WriteMessage("\n;error: too many arguments.\n");
                return nil;
            }
            else
            {
                if (args[0].TypeCode == (int)LispDataType.SelectionSet)
                {
                    SelectionSet selset = (SelectionSet)args[0].Value;
                    if (selset.Count == 0) return nil;
                    ObjectId[] objids = selset.GetObjectIds();
                    for (int i = 0; i < selset.Count; i++) allowed.Add(objids[i]);
                    if (allowed.Count == 0) return nil;
                }
                else
                {
                    ed.WriteMessage("\n;error: expected a selection set\n");
                    return nil;
                }
            }
            /*end Mod Zone*/


            SelectionAddedEventHandler filter = (_, e) =>
            {
                var objs = e.AddedObjects;
                for (int i = 0; i < objs.Count; i++)
                    if (!allowed.Contains(objs[i].ObjectId))
                        e.Remove(i);
            };
            ed.SelectionAdded += filter;
            var selection = ed.GetSelection();
            ed.SelectionAdded -= filter;
            if (selection.Status == PromptStatus.OK)
                return new TypedValue((int)LispDataType.SelectionSet, selection.Value);
            else
                return nil;
        }
    }
}

Again, thanks a lot, I really appreciate your informative effort.

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

Post to forums  

Forma Design Contest


Autodesk Design & Make Report