Author Topic: Collect Entities Between / In range / (entnext)  (Read 561 times)

0 Members and 1 Guest are viewing this topic.

Grrr1337

  • Bull Frog
  • Posts: 380
Collect Entities Between / In range / (entnext)
« on: January 27, 2017, 10:29:26 am »
Hi guys,
I was trying to collect the enames between 2 given enames, by their included order in the database.
Just wrote this, and decided to share it:

Code - Auto/Visual Lisp: [Select]
  1. ; Get the list of entitites, included between 2 given enames in the database:
  2. (defun CollectEntitiesBetween ( e1 e2 / e L )
  3.  (and (vl-every '(lambda (x) (eq 'ENAME (type x))) (list e1 e2))
  4.    (or ; Determine which entity e1 or e2 is the last included in the database
  5.      (and ; straight iteration (assume that e1 is included before e2 in the database)
  6.        (setq e (entnext e1)) ; (entnext (entlast)) -> nil
  7.        (setq L (append L (list e1))) ; include the e1 entity in the Lst
  8.        (progn
  9.          (while (and e (not (eq e e2)))
  10.            (setq L (append L (list e)))
  11.            (setq e (entnext e))
  12.          ); while
  13.          (if (not e) (setq L nil) (setq L (append L (list e2)))) ; if (not e) - then the entity database order is incorrect, otherwise include the last entity in the Lst
  14.        ); progn
  15.      ); and
  16.      (and ; reverse the iteration (the above failed, e2 is included before e1 in the database)
  17.        (setq e (entnext e2)) ; (entnext (entlast)) -> nil
  18.        (setq L (append L (list e2))) ; include the e2 entity in the Lst
  19.        (progn
  20.          (while (and e (not (eq e e1)))
  21.            (setq L (append L (list e)))
  22.            (setq e (entnext e))
  23.          ); while
  24.          (if (not e) (setq L nil) (setq L (append L (list e1))))
  25.        ); progn
  26.      ); and
  27.    ); or
  28.  ); and
  29.  L
  30. ); defun CollectEntitiesBetween

Test function:

Code - Auto/Visual Lisp: [Select]
  1. ; Test function for (CollectEntitiesBetween)
  2. (defun C:test ( / e1 e2 Lst SS )
  3.  (and
  4.    (or (and (setq e1 (car (entsel "\nFirst Entity: "))) (setq e2 (car (entsel "\nSecond Entity:" ))) )
  5.      (and
  6.        (setq e1 (entnext)) ; Sets e1 to the name of the first entity in the drawing
  7.        (setq e2 (entlast)) ; Sets e2 to the name of the last entity in the drawing
  8.      ); and
  9.    ); or
  10.    (setq Lst (CollectEntitiesBetween e1 e2))
  11.    (progn (setq SS (ssadd)) (mapcar '(lambda (x) (ssadd x SS)) Lst) (sssetfirst nil SS))
  12.    (alert ; Display the LayerNames, to comfirm the result
  13.      (strcat "\nResult:\n"
  14.        (apply 'strcat (mapcar '(lambda (x) (strcat (cdr (assoc 8 (entget x))) ", ")) Lst))
  15.      )
  16.    ); alert
  17.  ); and
  18.  (princ)
  19. ); defun C:test for (CollectEntitiesBetween)

I first started with this:
Code - Auto/Visual Lisp: [Select]
  1. ; Collect all graphical enames in the drawing:
  2. (defun GetAllEntities ( / e L )
  3.  (setq e (entnext))
  4.  (while e (setq L (append L (list e))) (setq e (entnext e)) )
  5.  L
  6. ); defun GetAllEntities

Then I wondered, about collecting enames within range (using entnext) - so the result was this CollectEntitiesBetween subfunction.
The main issue was to figure out which ename came first in the database (e1 - the chicken or e2 - the egg).

Now my brain is stuck and I have 2 questions:
  • Is there a way to shorten it (the CollectEntitiesBetween subfunction) ? - obviously I did alot of repetition, on the straight and the reverse iteration.
  • Is there some dictionary that has sorted all the enames by their database added order or I should stick to the GetAllEntities subfunction?
EDIT: Oh, heres the test drawing (quickly created text entities, to represent the layers via lisp):
« Last Edit: January 27, 2017, 12:50:04 pm by Grrr1337 »

ronjonp

  • Needs a day job
  • Posts: 6326
Re: Collect Entities Between / In range / (entnext)
« Reply #1 on: January 27, 2017, 10:51:58 am »
Not exactly sure what you're after, but this is what came to mind for getting items between :)
Code - Auto/Visual Lisp: [Select]
  1. (defun betweenfoo (a b l / tmp)
  2.  (if (and (setq tmp (cdr (member a l))) (setq tmp (cdr (member b (reverse tmp)))))
  3.    (reverse tmp)
  4.  )
  5. )
  6. (betweenfoo 6 9 '(1 2 3 4 5 6 "RJP" "FOO" 123 7 8 9))
  7. ;;("RJP" "FOO" 123 7 8)


Windows 10 x64 - AutoCAD /C3D 2018

Custom Build PC

David Bethel

  • Swamp Rat
  • Posts: 613
Re: Collect Entities Between / In range / (entnext)
« Reply #2 on: January 27, 2017, 11:05:39 am »

Can't you use handles to determine the 1st ?
R12 Dos - A2K

Grrr1337

  • Bull Frog
  • Posts: 380
Re: Collect Entities Between / In range / (entnext)
« Reply #3 on: January 27, 2017, 11:23:08 am »
Not exactly sure what you're after, but this is what came to mind for getting items between :)

(betweenfoo 6 9 '(1 2 3 4 5 6 "RJP" "FOO" 123 7 8 9))
I mean, like collecting different sets of entities while performing command calls in the code, like copy/mirror.
Thats a nice subfunction, might work for the (GetAllEntities) but perhaps this approach would be slower. :)



Can't you use handles to determine the 1st ?

Do sorted handles represent the database entity sorting?
I could attempt a check like this to re-sort the e1 and e2, but I'm not sure does it work that way:
Code: [Select]
(mapcar 'handent (acad_strlsort (mapcar '(lambda (x) (cdr (assoc 5 (entget x)))) (list e1 e2))))

roy_043

  • Swamp Rat
  • Posts: 1477
  • BricsCAD 16
Re: Collect Entities Between / In range / (entnext)
« Reply #4 on: January 27, 2017, 12:11:45 pm »
@Grrr1337:
Code - Auto/Visual Lisp: [Select]
  1. (setq e (entnext e1)) ; include the first entity in the Lst
Something is wrong here...

David Bethel

  • Swamp Rat
  • Posts: 613
Re: Collect Entities Between / In range / (entnext)
« Reply #5 on: January 27, 2017, 12:47:49 pm »
From what I remember, handles are hexdecimal values created in order.   -David
R12 Dos - A2K

Grrr1337

  • Bull Frog
  • Posts: 380
Re: Collect Entities Between / In range / (entnext)
« Reply #6 on: January 27, 2017, 12:53:57 pm »
@Roy,
Not sure what exactly you mean. Maybe I poorly commented - did not mean the very first entity from the database.
Slightly modified the code in my post. Otherwise I see nothing wrong according to the entnext reference:
Quote
(setq e1 (entnext)) ; Sets e1 to the name of the first entity in  the  drawing
(setq e2 (entnext e1)) ; Sets e2 to the name of the entity following e1

From what I remember, handles are hexdecimal values created in order.   -David
Thanks David, I'll test this.
« Last Edit: January 27, 2017, 12:57:41 pm by Grrr1337 »

Lee Mac

  • Seagull
  • Posts: 11793
  • AutoCAD 2015 Windows 7 London, England
Re: Collect Entities Between / In range / (entnext)
« Reply #7 on: January 27, 2017, 01:09:35 pm »
How about:
Code - Auto/Visual Lisp: [Select]
  1. (defun getentsbetween ( en1 en2 / en0 fl1 fl2 rtn )
  2.    (setq en0 (entnext))
  3.    (while (and en0 (not (or (setq fl1 (eq en1 en0)) (setq fl2 (eq en2 en0)))))
  4.        (setq en0 (entnext en0))
  5.    )
  6.    (if fl2 (mapcar 'set '(en1 en2) (list en2 en1)))
  7.    (if (or fl1 fl2)
  8.        (progn
  9.            (while (and en0 (not (eq en2 en0)))
  10.                (setq rtn (cons en0 rtn)
  11.                      en0 (entnext en0)
  12.                )
  13.            )
  14.            (reverse (if en0 (cons en0 rtn) rtn))
  15.        )
  16.    )
  17. )

You might also be interested in this thread:
https://www.theswamp.org/index.php?topic=45732.0

roy_043

  • Swamp Rat
  • Posts: 1477
  • BricsCAD 16
Re: Collect Entities Between / In range / (entnext)
« Reply #8 on: January 27, 2017, 01:16:53 pm »
@Roy,
Not sure what exactly you mean. Maybe I poorly commented...
Ignore my previous post, I was indeed confused by the comment.

Grrr1337

  • Bull Frog
  • Posts: 380
Re: Collect Entities Between / In range / (entnext)
« Reply #9 on: January 27, 2017, 01:48:57 pm »
Lee,
The first (while) loop is very impressive!
Looks like it works similairly to vl-some, so overall your suggestion I think will average of 1.5 iteration, while mine either 1 either 2.
That thread looks interesting indeed.

However David was right, so I think this initial comparsion might be faster (and after that iterate once) :
Code - Auto/Visual Lisp: [Select]
  1. (defun SortEnamesByHex (e1 e2)
  2.  (mapcar '(lambda (x) (cdr (assoc -1 x)))
  3.    (vl-sort
  4.      (mapcar 'entget (list e1 e2))
  5.      '(lambda (a b) (apply '< (mapcar 'hex2int (list (cdr (assoc 5 a)) (cdr (assoc 5 b))))))
  6.    )
  7.  )
  8. ); defun SortEnamesByHex
  9.  
  10. ; Select 2 entities:
  11. _$ (setq e1 (car (entsel)))
  12. <Entity name: 7ff6cd5073e0>
  13. _$ (setq e2 (car (entsel)))
  14. <Entity name: 7ff6cd507440>
  15. _$ (SortEnamesByHex e1 e2)
  16. (<Entity name: 7ff6cd5073e0> <Entity name: 7ff6cd507440>) ; <- Result
  17. ; Now select the same 2 entities, but reverse:
  18. _$ (setq e1 (car (entsel)))
  19. <Entity name: 7ff6cd507440>
  20. _$ (setq e2 (car (entsel)))
  21. <Entity name: 7ff6cd5073e0>
  22. _$ (SortEnamesByHex e1 e2)
  23. (<Entity name: 7ff6cd5073e0> <Entity name: 7ff6cd507440>) ; <- Result
  24. _$
  25.  
Where these are from Kent Cooper:
Code - Auto/Visual Lisp: [Select]
  1. ; http://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/decimal-to-hexadecimal/td-p/2873916
  2. ;; int2hex.lsp
  3. ;; Base-10 Integer to Hexadecimal converter.
  4. ;; Accepts positive or negative integer argument, including 0.
  5. ;; Rejects non-integer argument with Alert.
  6. ;; Kent Cooper, January 2011
  7. (defun int2hex (int / power neg int result div remain posval)
  8.  (if (/= (type int) 'INT) (progn (alert "Requires integer argument.") (quit) ) )
  9.  (setq power 1 neg (minusp int) int (abs int) result "" )
  10.  (while (> int 0)
  11.    (setq div (expt 16 power) remain (rem int div) posval (/ remain (expt 16 (1- power))) int (- int remain)
  12.      result (strcat (if (< posval 10) (itoa posval) (chr (+ 55 posval)) ) result) power (1+ power)
  13.    ); end setq
  14.  ); end while
  15.  (strcat (if neg "-" "") (if (= result "") "0" result) )
  16. ); end defun - int2hex
  17.  
  18.  
  19. ;; hex2int.lsp
  20. ;; Hexadecimal to base-10 Integer converter [e.g. for entity handles]
  21. ;; Modified/simplified from Frank Oquendo's (basetodecimal) function.
  22. ;; Added negative-argument capability and argument validity controls.
  23. ;; Kent Cooper, January 2011
  24. (defun hex2int (hndl / result neg power tmp)
  25.  (if (/= (type hndl) 'STR) (progn (alert "Requires string argument.") (quit) ) )
  26.  (setq hndl (strcase hndl) result 0 )
  27.  (if (= (substr hndl 1 1) "-") (setq neg T hndl (substr hndl 2))  )
  28.  (if (/= (vl-string-trim "0123456789ABCDEF" hndl) "")
  29.    (progn (alert "Invalid hexadecimal string.") (quit) )
  30.  ); end if
  31.  (repeat (setq power (strlen hndl))
  32.    (setq
  33.      result (+ result (* (- (setq tmp (ascii (substr hndl 1 1))) (if (> tmp 64) 55 48) ) (expt 16 (setq power (1- power))) ) )
  34.      hndl (substr hndl 2)
  35.    ); end setq
  36.  ); end while
  37.  (if neg (- result) result)
  38. ); end defun - hex2int
But I prefer yours, since I don't like overwhelming the main code with too many subfunctions - leaving it as an alternative for someone.  :roll:


Ignore my previous post, I was indeed confused by the comment.
It was my fault Roy, sorry.

Lee Mac

  • Seagull
  • Posts: 11793
  • AutoCAD 2015 Windows 7 London, England
Re: Collect Entities Between / In range / (entnext)
« Reply #10 on: January 27, 2017, 04:38:55 pm »
Thanks Grrr1337  :-)

Since there are only two items, there is no need to sort them  :wink:

Code - Auto/Visual Lisp: [Select]
  1. (defun getentsbetween ( en1 en2 / ent rtn )
  2.    (if (apply '< (mapcar '(lambda ( x ) (LM:hex->dec (cdr (assoc 5 (entget x))))) (list en2 en1)))
  3.        (mapcar 'set '(en1 en2) (list en2 en1))
  4.    )
  5.    (setq ent (entnext en1))
  6.    (while (and ent (not (eq en2 ent)))
  7.        (setq rtn (cons ent rtn)
  8.              ent (entnext ent)
  9.        )
  10.    )
  11.    (cons en1 (reverse (cons en2 rtn)))
  12. )
  13.  
  14. ;; Hex to Decimal  -  Lee Mac
  15. ;; Converts a hexadecimal string to an integer
  16. ;; n - [str] string representing number to convert
  17. ;; Returns: [int] Decimal representation of supplied number
  18.  
  19. (defun LM:hex->dec ( n )
  20.    (   (lambda ( f ) (f (mapcar '(lambda ( x ) (- x (if (< x 65) 48 55))) (reverse (vl-string->list n)))))
  21.        (lambda ( c ) (if c (+ (* 16 (f (cdr c))) (car c)) 0))
  22.    )
  23. )

Grrr1337

  • Bull Frog
  • Posts: 380
Re: Collect Entities Between / In range / (entnext)
« Reply #11 on: January 28, 2017, 04:51:46 am »
Lee, this seems to be the shortest and fastest solution!
Thanks alot for your help! :)

David Bethel

  • Swamp Rat
  • Posts: 613
Re: Collect Entities Between / In range / (entnext)
« Reply #12 on: January 28, 2017, 06:27:03 am »
I prefer dealing with picksets.

Maybe :
Code - Auto/Visual Lisp: [Select]
  1. (defun make_set (fe le)   ;first_entity last_entity
  2.   (setq nw_set (ssadd))
  3.   (ssadd fe nw_set)
  4.   (while (not (eq fe le))
  5.          (setq fe (entnext fe))
  6.          (ssadd fe nw_set)))
  7.  

Lots of room for crashes.

What if:
  • 1st or last are sequential entities
  • 1st / last order is incorrect

To name a few

-David
R12 Dos - A2K

David Bethel

  • Swamp Rat
  • Posts: 613
Re: Collect Entities Between / In range / (entnext)
« Reply #13 on: January 28, 2017, 07:46:15 am »

;; Hex to Decimal  -  Lee Mac
;; Converts a hexadecimal string to an integer

LOL Mine is so  .... elementary in comparison

Code - Auto/Visual Lisp: [Select]
  1. ;++++++++++++ HEX2DECimal +++++++++++++++++++++++++
  2. (defun h2d (s / sl c p x)
  3.  (setq c 1 p 0)
  4.  (repeat (strlen s)
  5.      (setq sl (cons (ascii (substr (strcase s) c 1)) sl)
  6.             c (1+ c)))
  7.  (foreach a sl
  8.     (setq p (max 1 (* p 16))
  9.           x (cons (* (if (< a 58) (- a 48) (- a 55)) p) x)))
  10.  (apply '+ x))

-David
« Last Edit: January 28, 2017, 07:53:54 am by David Bethel »
R12 Dos - A2K

Grrr1337

  • Bull Frog
  • Posts: 380
Re: Collect Entities Between / In range / (entnext)
« Reply #14 on: January 28, 2017, 09:15:14 am »
What if:
  • 1st or last are sequential entities
  • 1st / last order is incorrect

1. Just include them in a list, like: (<Entity name: 7ff7d1419e60> <Entity name: 7ff7d1419e70>)
2. I think that the order doesn't matter, since the main goal is to deal with multiple command calls like copy/mirror - which can generate new entities.

Heres some practical example (kinda buggy) :

Code - Auto/Visual Lisp: [Select]
  1. ; Copy - Array with erase toggle
  2. ; Entirely Vanilla
  3. (defun C:test ( / GetAngDist SS ad e d i cm stop grr k LstCopies e n itm )
  4.  
  5.  (defun GetAngDist ( msg1 msg2 / p1 p2 r )
  6.    (and
  7.      (setq p1 (getpoint (strcat "\n" (if (eq 'STR (type msg1)) msg1 "Base point") " <exit>: ")))
  8.      (setq p2 (getpoint p1 (strcat "\n" (if (eq 'STR (type msg2)) msg2 "Destination point") " <exit>: ")))
  9.      (setq r (list p1 (angle p1 p2) (distance p1 p2)))
  10.    ); and
  11.    r
  12.  ); defun GetAngDist
  13.  
  14.  (and
  15.    GetEntsBetween
  16.    (setq SS (ssget "_:L-I"))
  17.    (setq ad (GetAngDist nil nil))
  18.    (setq e (entlast))
  19.    (setq d (caddr ad))
  20.    (setq i 0)
  21.    (progn
  22.      (setq cm (getvar 'cmdecho)) (setvar 'cmdecho 0)
  23.      (princ "\nPress [+/-] to increase/decrease the amout of copies, [0-9] to toggle erasing, [RMB] if done: ")
  24.      (while (not stop)
  25.        (setq grr (grread T))
  26.        (cond
  27.          ( (= (car grr) 2)
  28.            (setq k (cadr grr))
  29.            (cond
  30.              ( (= 43 k)
  31.                (command "_.COPY" SS "" "_non" (car ad) "_non" (apply 'polar ad))
  32.                (if (and (entget e) (entget (entlast))) ; check if some of these is not erased
  33.                  (setq LstCopies (cons (GetEntsBetween e (entlast)) LstCopies))
  34.                )
  35.                (setq e (entlast))
  36.                (setq ad (list (car ad) (cadr ad) (+ (caddr ad) d)))
  37.              )
  38.              ( (= 45 k)
  39.                (if LstCopies
  40.                  (progn
  41.                    (setq e (car (car LstCopies)))
  42.                    (and
  43.                      (mapcar 'entget (cdr (car LstCopies)))
  44.                      (mapcar 'entdel (cdr (car LstCopies)))
  45.                    ); and
  46.                    (setq LstCopies (cdr LstCopies))
  47.                    (setq ad (list (car ad) (cadr ad) (- (caddr ad) d)))
  48.                  ); progn
  49.                ); if
  50.              )
  51.              ( ; This block toggles the erase status
  52.                (and
  53.                  LstCopies
  54.                  (setq n (atoi (chr k)))
  55.                  (if (= 0 n) (= (read (chr k)) n) T)
  56.                  (setq itm (nth n (reverse LstCopies)))
  57.                ); and
  58.                (mapcar 'entdel (cdr itm)) ; if attempting to (entdel) an erased entity, its recreated (works like toggle)
  59.              )
  60.            ); cond
  61.          )
  62.          ( (= (car grr) 25) (setq stop T) )
  63.          (T nil)
  64.        ); cond
  65.      ); while
  66.      (and cm (setvar 'cmdecho cm))
  67.    ); progn
  68.  ); and
  69.  (princ)
  70. ); defun C:test
  71.  
  72. (defun GetEntsBetween ( en1 en2 / ent rtn )
  73.  (if (apply '< (mapcar '(lambda ( x ) (LM:hex->dec (cdr (assoc 5 (entget x))))) (list en2 en1)))
  74.    (mapcar 'set '(en1 en2) (list en2 en1))
  75.  )
  76.  (setq ent (entnext en1))
  77.  (while (and ent (not (eq en2 ent)))
  78.    (setq rtn (cons ent rtn)
  79.      ent (entnext ent)
  80.    )
  81.  )
  82.  (cons en1 (reverse (cons en2 rtn)))
  83. )
  84.  
  85. ;; Hex to Decimal  -  Lee Mac
  86. ;; Converts a hexadecimal string to an integer
  87. ;; n - [str] string representing number to convert
  88. ;; Returns: [int] Decimal representation of supplied number
  89.  
  90. (defun LM:hex->dec ( n )
  91.  (   (lambda ( f ) (f (mapcar '(lambda ( x ) (- x (if (< x 65) 48 55))) (reverse (vl-string->list n)))))
  92.    (lambda ( c ) (if c (+ (* 16 (f (cdr c))) (car c)) 0))
  93.  )
  94. )