List of layers from nested filter list

List of layers from nested filter list

nathanwheeler27
Advocate Advocate
1,942 Views
13 Replies
Message 1 of 14

List of layers from nested filter list

nathanwheeler27
Advocate
Advocate

Hi, I am struggling to find a way to retrieve a list of layers from a specific filter or group that is nested in another group or filter. See image below.

nathanwheeler27_0-1605835555744.png

I need to retrieve layers from, for example, 2_STRUCTURAL (Group) -> S - FLOOR FRAMING (Filter) -> S - FF - 1 (Filter)

🙃

 

Using the information already provided by everyone in this thread, I figured out how to do it for unested groups/filters.

https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/retrieve-layer-names-from-group-laye...

 

Can this be done?

I have some work arounds, but it will be tedious and it won't automatically find new layers if there added.

 

Thank you

0 Likes
Accepted solutions (2)
1,943 Views
13 Replies
Replies (13)
Message 2 of 14

ВeekeeCZ
Consultant
Consultant
Accepted solution

This lists all filters and groups. 

 

(defun :LayerFilterList (/ :SubFilterList)

  (defun :SubFilterList (dict / ent lst)
    (foreach ent (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 350)) dict))
      (setq lst (append lst
                        (cons ent (if (assoc 360 (entget ent))
                                    (:SubFilterList (entget (cdr (assoc 360 (entget (cdr (assoc 360 (entget ent)))))))))))))
    lst)

  (mapcar '(lambda (x) (cdr (assoc 300 (entget x))))
          (:SubFilterList (dictsearch
                            (vlax-vla-object->ename
                              (vla-getextensiondictionary
                                (vla-get-layers
                                  (vla-get-activedocument
                                    (vlax-get-acad-object)))))
                            "ACLYDICTIONARY"))))

 

Message 3 of 14

nathanwheeler27
Advocate
Advocate

That prints the names of every group and filter which is great. But I am realizing I need to research dictionaries and practice mapcar/lambda more because I barely understand how. 😥

 

From this point, can it add a list of the layers in that group/filter after?

So ("Group name" ("Layer1" "Layer2" ....))

0 Likes
Message 4 of 14

doaiena
Collaborator
Collaborator

"mapcar" applies a function to each element of the supplied list. A few examples will make it easier to understand.


(setq lst (list 1 2 3 4))
(setq lst (mapcar '1+ lst))

Result: (2 3 4 5)

It is the same as writing:
(setq lst (list (1+ (car lst)) (1+ (cadr lst)) (1+ (caddr lst)) (1+ (cadddr lst))))

You can also write your own function, to use with mapcar:
(defun Add2 (x) (+ x 2))

(setq lst (list 1 2 3 4))

(setq lst (mapcar 'Add2 lst))

Result: (3 4 5 6)


This brings us to "lambda". Lambda is an anonymous function. It's a function without a name, that is used in place. Instead of defining an "Add2" function, you can write a lambda function in place:

 

(setq lst (list 1 2 3 4))

(setq lst (mapcar '(lambda (x) (+ x 2)) lst))
Result: (3 4 5 6)


There is a bit more to it, but no need to make things complicated right now. If u get this basic principle down, you can easily expand on your own.

Mapcar - takes each element of the list (one at a time) and runs them through the supplied function.

Lambda - you can look at it as a "defun" with no name. It's a function that is used on the spot.

Message 5 of 14

ВeekeeCZ
Consultant
Consultant

@nathanwheeler27 wrote:

That prints the names of every group and filter which is great. But I am realizing I need to research dictionaries and practice mapcar/lambda more because I barely understand how. 😥

 

From this point, can it add a list of the layers in that group/filter after?

So ("Group name" ("Layer1" "Layer2" ....))


 

Possibly YES, definitely NOT.

 

Possibly you can dig into dictionaries to get a group layer list. But if you do the same for property filters... all you'll find are definitions of property conditions. Something like the lower. Then you will need to translate them to something that LISP understands and then apply them in right order... not worth it. 

 

"NAME==\"_SO 8##\" OR NAME==\"_PS ###\" OR NAME==\"Layer*\""
"OFF==\"False\" AND FROZEN==\"False\""

 

Message 6 of 14

nathanwheeler27
Advocate
Advocate

@doaiena @Thank you for those examples.

 

@ВeekeeCZ hmm, okay. I am probably going to switch all my filters to groups if I can figure out how to get the layers out of them. 
Alternatively, I do have a lisp function that returns the filters conditions which could be used to recreate the layer list in that properties group instead. That just sounds complicated though 🙄

0 Likes
Message 7 of 14

ВeekeeCZ
Consultant
Consultant

What are you going to do with that?

 

BTW, how much these property filters differ from project to project? Do you really need to read them and translate them? I mean, the pattern remains the same, or similar..., right? So the rules could be just hard-coded. Actually that is what I do. 

 
0 Likes
Message 8 of 14

nathanwheeler27
Advocate
Advocate

My project template has 153 total layers in it. Originally I was just going to hard code the groups and all their layers into the routine. But, if a layer name changes down the road or occasionally a drafter adds a layer(s) to a project, I would then have to manually fix that in the code. This seemed so much more convenient if I could get it to work.

 

For the current function I am making, it will turn off all layers and turn on the layers in a specified group.

0 Likes
Message 9 of 14

nathanwheeler27
Advocate
Advocate
Accepted solution

I believe this is it!

The below code will get every group set and make a list of all their layers, including nested groups.

 

 

(defun :LayerFilterList (/ :SubFilterList)

  (defun :SubFilterList (dict / ent lst)
    (foreach ent (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 350)) dict))
        (setq lst 
            (append lst
                (cons ent 
                    (if (assoc 360 (entget ent))
                                    (:SubFilterList (entget (cdr (assoc 360 (entget (cdr (assoc 360 (entget ent))))))))
                    ) ; if
                ) ; cons
            ) ; append
        ) ; setq
    ) ; foreach
    lst)

    (setq dlist (:SubFilterList (dictsearch
                            (vlax-vla-object->ename
                              (vla-getextensiondictionary
                                (vla-get-layers
                                  (vla-get-activedocument
                                    (vlax-get-acad-object)))))
                            "ACLYDICTIONARY")))

    (mapcar 
        '(lambda (x) 
            (cons (setq i (cdr (assoc 300 (entget x))))
                    (mapcar '(lambda (y) (cdr (assoc 2 (entget (cdr y)))))
					    (vl-remove-if '(lambda (z) (/= 330 (car z))) (entget x))
				    ) ; mapcar
            )
        )
        dlist
    )
)

(princ (assoc "S - ROOF - 1" (:LayerFilterList)))

 

 

 

Princ:

 

 

(S - ROOF - 1 (nil nil 0 TB DEFPOINTS TEXT Dimensions 1 - S - Windows 1 - S - TEXT - ROOF 1 - S - Roof 1 - S - Roof Framing 1 - S - Walls - I 1 - S - Walls - E 1 - S - Section 1 - S - Roof Beams 1 - S - Walls - HATCH 1 - S - Roof Posts NOTES 1 - S - Shearwalls Posts))

 

 

 

Message 10 of 14

prountzos509
Enthusiast
Enthusiast

Hi BeekeeCZ,

Great lisp and i am using it for a long time now.

But i was wondering it its possible to make that list having the same tree form as layer filters' tree. Also each filter paired with its name.

Think thats the solution for exporting the tree and importing to another drawing.

0 Likes
Message 11 of 14

prountzos509
Enthusiast
Enthusiast

Hi BeekeeCZ,

Great lisp (as always). Using it for a long time now.

I was wondering if its possible to get the list with the same tree form as Layer Filters have.

Also (in pairs) each filter name along with its properties (dxf 301).

Then create lines with upper filter name, filter name and its properties, so it can be exported to file and then imported to another drawing.

Couldnt reach the first step so i am looking for help from the master.

0 Likes
Message 12 of 14

ВeekeeCZ
Consultant
Consultant

Here's some to play with. 

 

;; returns list of pairs, CAR is a list of nested filter names, CDR is filter definition eg. 

;; ((("Level1b" "All") . "NAME==\"TEst*\"")
;;  (("Level2" "Level1a" "All") . "NAME==\"Le*\" OR NAME==\"t*\" OR FROZEN==\"False\" OR LOCKED==\"False\"")
;;  (("Level1a" "All") . "NAME==\"Layer*\" OR NAME==\"Te**\""))

(defun LayerFilterList2 (/ :FilterPropList out) 
    
  (defun :FilterPropList (lst / nams ent)
    
    (foreach pair lst
      
      (setq ent (cdr pair) 
	    nams (cons (cdr (assoc 300 (entget ent))) (car pair)) 
	    out (cons (cons nams (cdr (assoc 301 (entget ent))))
		      out))
      
      (if (assoc 360 (entget ent))
	(:FilterPropList (mapcar '(lambda (x) (cons nams x))
				 (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 350)) (entget (cdr (assoc 360 (entget (cdr (assoc 360 (entget ent))))))))))))))
  
  (:FilterPropList (mapcar '(lambda (x) (cons (list "All") x))  				; All is language sensitive, must be translated to current loval version.
			   (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 350))
					  (dictsearch (vlax-vla-object->ename (vla-getextensiondictionary (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))))) "ACLYDICTIONARY")))))
  
  out)

 

Note that "All" as a top level filter name could be translated to localized language ACAD version. 

 

image.png

Message 13 of 14

ВeekeeCZ
Consultant
Consultant

Also, here's an example how such import/export routine could work. Older routine from @_gile 

https://forums.augi.com/showthread.php?97438-Can-I-create-frequently-used-layer-filters-in-LISP&p=95...

Message 14 of 14

prountzos509
Enthusiast
Enthusiast

Thanks (again) BeeKeeCZ.  The list is similar to filter tree form, especially if you reverse out. I can make lines with name, parent, and properties. Reverse list lists parents above lower lever filters, which prevents errors when creating multiple filters with tree form.

0 Likes