;; There was once a question on CCA, about How to retrieve ;; the list of all frozen layers in some VIEWPORT. ;; Here're the functions for that, evolving with the time. (defun get-frozen-layers (vp-ename) (last (last (get-xdata vp-ename "ACAD")))) ;; get EED as an arbitrary list of values, w/out any specific codes (1040 etc). ;; 1002 '{' and '}' control codes actually processed as a ;; beginning / ending markers of lists and those sub-lists created, ;; thus converting the one-dimentional EED data stream into a tree-like ;; LISP list of arbitrary structure (defun get-xdata (ename appname) (re-list ;; reconstruct list (mapcar 'cdr ;; of values (cdadr (assoc -3 ;; of EED code-groups (entget ename (list (strcase appname)))))))) ;; Now we need to reconstruct list from it's ;; one-dimentional EED representation. ;; That's what RE-LIST is all about. ;; older version; a lot of stack space consumed (defun re-list0 (l / rest) ;; lexical closure for REST of input stream (re-list0-aux l)) ;; internal function (defun re-list0-aux (l / tok) ;; RECURSIVE ITERATION ! :) :) (setq tok (car l) rest (cdr l)) ;; get first element and advance (cond ((null l) nil) ;; stop recursion! ((= "}" tok) nil) ;; stop recursion! REST contains the rest of stream. ((= "{" tok) (cons (re-list0-aux rest) (re-list0-aux rest))) (T (cons tok (re-list0-aux rest))))) ;; {{I call it Recursive Iteration :) because it builds the list ;; structure by recursion, but scans the input list with iteration, ;; when recursive AUX function changes an OUTER variable REST, ;; and NOT the one it got as its argument.}} ;; the functions below can be used to watch how re-list0 is working (setq *verbose* 'T) ;; set to NIL to suppress output (defun re-list0p (l / inp) ;; with Print Out (re-list0p-aux 2 l) (princ) ) (defun re-list0p-aux (k l / tok);; RECURSIVE ITERATION ! :} :} (prink "TOK: " (setq tok (car l))) (setq inp (cdr l)) (prink "RES: " (cond ((null l) nil) ;; stop recursion! ((= "}" tok) nil) ;; stop recursion! INPut continues. ((= "{" tok) (cons (re-list0p-aux (+ k 2) (prink "FIRST : " inp) ) (re-list0p-aux (+ k 2) (prink "SECOND: " inp)))) (T (cons tok (re-list0p-aux (+ k 2) (prink "REST : " inp))))))) (DEFUN prink (m x) (IF *verbose* (PROGN (PRINC "\n") (REPEAT k (PRINC " ")) (PRINC m) (PRIN1 x)) x)) ;; end of print-out version ;; RE-LIST0 can be further optimized in order to minimize stack space ;; consumption. Actually, REST is a redundant variable. I can just ;; as well use L itself for keeping track of iteration, so AUX function ;; would take no arguments (working on outer variable L), thus saving ;; greatly on stack. TOK variable can be eliminated by using ;; two utility functions, DEL1 and POP, for advancing on list L. ;; delete first element from the list, discarding its value. (defun del1 ( _q_lst ) ;; a quoted SYM of list as argument (set _q_lst (cdr (eval _q_lst)))) ;; delete first element from list, returning its value. (defun pop ( _q_lst / _v ) (setq _v (car (eval _q_lst))) (set _q_lst (cdr (eval _q_lst))) _v ) ;; next version; trying to save on stack space (defun re-list1 (l) ;; working on this L (re-list1-aux)) ;; internal function ;; here it is, with comments and explanations of how recursion works: (defun re-list1-aux () ;; the working function for re-list() (cond ;; WHEN ((null lst) ;; input list ends: nil) ;; stop recursion. ((= "}" (car lst)) ;; "}" is encountered: (del1 'lst) ;; advance on input list, discarding the "}" symbol nil) ;; and stop recursion ((= "{" (car lst)) ;; "{" is encountered: make inner list: (del1 'lst) ;; advance on input list, discarding the "{" symbol (cons ;; and then add (re-list1-aux);; a result of processing up to matching "}" (re-list1-aux);; to the result of processing what's left. )) (T (cons ;; regular case: add (pop 'lst) ;; first element (re-list1-aux) ;; to the result of processing what's left. )))) ;; It may be optimized even further. I tried to save stack ;; space and hence used DEL1 and POP, but I can use TOK as well ;; declaring it as a local var of main function, and not the ;; inner, recursive one. ;; To show a concept in more clear way, -- (defun re-list2 (l / tok) (re-list2-aux)) (defun read-token() (setq tok (car l) l (cdr l)) tok) (defun re-list2-aux() (if l ;; if input ended, return NIL immediately (cond ;; else ((= (read-token) "}") nil) ((= tok "{") (cons (re-list2-aux) (re-list2-aux))) (T (cons tok (re-list2-aux)))))) ;; The COND clause may be regrouped into IFs, and read-token ;; inlined. Here's the simplest, shortest RE-LIST, using the ;; least possible stack space (I hope) :) (DEFUN re-list (lst / tok aux ?) (DEFUN aux () (IF (AND lst (SETQ tok (CAR lst) lst (CDR lst) ? (/= tok "}"))) (CONS (IF (= tok "{") (aux) tok) (aux)))) (aux)) ;; This defines AUX function and calles it immediately. ;; AUX works recursively on its OUTER variable, LST, ;; iterating over it. Return value is being built as a result ;; of recursion process, when each invocation of recursive ;; function iterates over input list. {Wow! \;} :) ;; The reason I am so excited about this little thing is because ;; it's a READER function, meaning that it is used to read ;; some sort of description language, ananlyze it and build ;; some structures from it. It means that similar approach ;; can be used to build another READER functions, for another ;; descriptive languages. In our case the language is very ;; simple, just (1002 . "{") and (1002 . "}") controls, but ;; it can be anything we need. PARSING an input file or ;; READING it is always a process of reading one-dimentional ;; input stream and converting it into some inner representation. ;; It is this principle that makes it possible to build various ;; interpretors and compilers. ;; So in fact RE-LIST is sort of a "compiler". :) ;; It "compiles" one-dimentional EED data stream into ;; a LISP tree-like lists. ;; It's also possible to use a LISP's built-in subr, READ, for ;; that, now that we've got the idea of "reading" -- although ;; to make one yourself is much more fun. :) (defun re-list4 (lst) (read (re-list4-aux lst))) (defun re-list4-aux (lst) ;; original idea by Morten Warankov (strcat "(" (apply 'strcat (mapcar '(lambda(x) (cond ((= x "{") "(") ((= x "}") ")") ((= 'REAL (type x)) (strcat (rtos x 2 20) " ")) ((= 'INT (type x)) (strcat (rtos x 2 0) " ")) ((= 'STR (type x)) (strcat "\"" x "\"")) ((and x (listp x)) (re-list4-aux x)) (T "") )) lst)) ")")) ;; But then, it will fail if the list is not balanced, ;; whereas our RE-LIST will not. On the other hand, ;; re-list is still very stack-hangry, but re-list4 ;; which uses built-in READ, is much more stable. ;; After some tests I found out that stack usage was ;; improved ONLY by 2%. :-( I guess that mainly it's used ;; for storing intermediate results of AUX functions, ;; and eliminating REST etc was very small improvement after all. :) ;; Still it was a nice exercise... (princ)