Author Topic: Selection by Handle greater than  (Read 9712 times)

0 Members and 1 Guest are viewing this topic.

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Selection by Handle greater than
« Reply #15 on: April 25, 2015, 06:05:22 PM »
Lee Mac, thanks so much for the revised function, which does exactly what I want, and especially for the remarks included with it.

Excellent - you're welcome  :-)

I tried to adapt it to make an equivalent Mirror command, but this always terminates with nothing selected. What did I do wrong?

Note that the MIRROR command has an additional prompt (Erase source objects? [Yes/No] <N>:), hence an additional pause or automated input is required:
Code - Auto/Visual Lisp: [Select]
  1. (command "_.mirror" sel "" "\\" "\\" "")

And I hate to be greedy, but there are a couple of things I still don't understand:

Don't worry - ask away! Sharing knowledge is what this place is all about  :-)

1.
....if the entity returned by (entlast) is a complex entity (such as an attributed block or 3D polyline), the subsequent calls to (entnext) will return the subentities which follow the parent entity (i.e. the attribute references or 3D polyline vertices respectively). For this reason, I suggested the lastent function that I posted above, which will return the last entity in the drawing database for which a call to (entnext) will return nil.
Does that mean that (lastent) returns the last non-complex entity? If so, what if there's a newer entity that is complex? What keeps that newer complex entity from being included in the final selection set? I see where you skip over the sub-entities, but where do you skip over a complex entity that's newer than the last non-complex entity?

Not quite -

(entlast) will return the last visible primary entity in the database, however, in the case of a complex entity such as an attributed block reference or 3D polyline, there will be subentities (attribute references / vertices) which follow the primary entity in the database.

Therefore, if the last object added to the database was complex and we were to step through the database starting from the entity returned by (entlast), we would encounter the subentities which belong to that complex entity before encountering the new objects created by the command(s) invoked following the call to (entlast).

Whereas, my (lastent) function will obtain the last visible primary entity using a call to (entlast) and will then continue to iterate over the drawing database from this entity until there are no further subentities (i.e. when (entnext) returns nil), at which point the function will return the entity occupying the very last index in the database. Of course, if the initial call to (entlast) returns a non-complex entity (such as a LINE, POINT etc.), the call to (entnext) will immediately return nil (as there are no subentities which follow), and the primary entity will be returned by the function as if (entlast) had been used.

2. What's that rtn all by itself at the end of the (lastent) function) It's not part of any expression other than the (defun) function, so I'm surprised it's even allowed in (defun sym ([arguments] [/ variables ...]) expr ...). And what does it do?

When evaluated, a function will return the last evaluated expression within the defun expression; in this case, the last evaluated expression is the symbol 'rtn' which is evaluated to yield the value to which it points - that is, the last entity in the database.

Consider that if the isolated 'rtn' symbol were not present at the end of the function, the function would instead return the value returned by the while expression; and since while returns the value of the last evaluated expression following the test expression, the lastent function would return nil if the entity returned by the initial (entlast) call was non-complex (as the test expression for the while loop would immediately return nil).
« Last Edit: April 25, 2015, 06:09:06 PM by Lee Mac »

qwrtz

  • Newt
  • Posts: 22
Re: Selection by Handle greater than
« Reply #16 on: April 25, 2015, 06:55:49 PM »
Understood! Thanks again.

I left out the third input to the Mirror command because I wanted to be able to specify yes or no at run time. I didn't realize that would mess up the building of the new selection set. I guess I have to prompt for the two clicks and the yes or no and then pass all the input to the Mirror command.

But with the following code, answering Yes suppresses the creation of a new selection set. I only get a selection set on termination if I answer N or Enter. Can you see what I did wrong?

Code - Auto/Visual Lisp: [Select]
  1. (defun c:lmMirror ( / otm ent new sel )
  2.  
  3.     (if ;; If the following expression returns a non-nil value
  4.         ;; (i.e. if the user makes a valid selection)
  5.         (setq sel (ssget)) ;; Prompt the user for a selection
  6.  
  7.         (progn
  8.  
  9.             (setq ent (lastent) ;; Retrieve the last entity added to the database
  10.                   new (ssadd)   ;; Create an empty selection set for the new entities
  11.                   otm (getvar 'orthomode) ;; Store the value of the ORTHOMODE sys var
  12.             ) ;; end setq
  13.  
  14.             (setvar 'orthomode 1)
  15.             ;; Set the ORTHOMODE sys var as appropriate
  16.                        
  17.             (setq p1 (getpoint "First point of mirror line: ")
  18.                   p2 (getpoint p1 "Second point: ")
  19.              )
  20.                        
  21.             (initget "Yes No")
  22.             (setq yn (getkword "Delete the original? <N>") )
  23.             (if (not yn) (setq yn "no") )
  24.  
  25.             (command "_.mirror" sel "" p1 p2 yn) ;; Invoke the MIRROR command
  26.             (setvar 'orthomode otm)
  27.             ;; Reset the ORTHOMODE sys var
  28.             ;; (always store/restore the original sys var value, don't assume that you know what that value should be)
  29.  
  30.             (while (setq ent (entnext ent)) ;; While there is another entity in the database
  31.                 (ssadd ent new) ;; Add it to the new selection set
  32.  
  33.                 ;; If subentities follow this entity
  34.                 (if (= 1 (cdr (assoc 66 (entget ent))))
  35.  
  36.                     ;; Iterate over them until we reach the terminating SEQEND entity:
  37.                     (while (/= "SEQEND" (cdr (assoc 0 (entget ent))))
  38.                         (setq ent (entnext ent))
  39.                     ) ;; end while
  40.                 ) ;; end if
  41.             ) ;; end while
  42.  
  43.             ;; Highlight the new objects
  44.             (sssetfirst nil new)
  45.  
  46.         ) ;; end progn
  47.     ) ;; end if
  48.  
  49.     ;; Supress the return of the last evaluated expression
  50.     (princ)
  51. ) ;; end defun
  52.  
« Last Edit: April 25, 2015, 06:59:23 PM by qwrtz »

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Selection by Handle greater than
« Reply #17 on: April 26, 2015, 07:17:35 AM »
I left out the third input to the Mirror command because I wanted to be able to specify yes or no at run time. I didn't realize that would mess up the building of the new selection set. I guess I have to prompt for the two clicks and the yes or no and then pass all the input to the Mirror command.

Not necessarily - I used the empty string "" (which represents the user pressing Enter) to accept the default option at this prompt, however, you could use another pause ("\\") to pause for user input at the prompt, as used with the point prompts.

But with the following code, answering Yes suppresses the creation of a new selection set. I only get a selection set on termination if I answer N or Enter. Can you see what I did wrong?

I wouldn't say that you've done anything wrong - the issue is that the MIRROR command behaves differently when the user opts to delete the original objects (this is one reason why I tend to avoid using AutoCAD commands in my code, as you become reliant on the behaviour of the command under each condition).

When the user opts to delete the original objects, the mirror transformation is applied to the original entities, rather than to copies of the original entities; therefore, to account for this, I would suggest the following:

Code - Auto/Visual Lisp: [Select]
  1. (defun c:lmMirror ( / otm ent flg new pt1 pt2 sel )
  2.  
  3.     (if ;; If the following expression returns a non-nil value
  4.         ;; (i.e. if the user makes a valid selection)
  5.         (and
  6.             (setq sel (ssget)) ;; Prompt the user for a selection
  7.             (setq pt1 (getpoint "\nSpecify first point of mirror line: ")) ;; Prompt for first point
  8.             (setq pt2 (getpoint "\nSpecify second point of mirror line: " pt1)) ;; Prompt for second point
  9.         )
  10.         (progn
  11.  
  12.             (setq ent (lastent) ;; Retrieve the last entity added to the database
  13.                   new (ssadd)   ;; Create an empty selection set for the new entities
  14.                   otm (getvar 'orthomode) ;; Store the value of the ORTHOMODE sys var
  15.             ) ;; end setq
  16.  
  17.             (setvar 'orthomode 1)
  18.             ;; Set the ORTHOMODE sys var as appropriate
  19.  
  20.             ;; Issue the final MIRROR command prompt separately in order to collect the chosen option
  21.             (initget "Yes No")
  22.             (setq flg (cond ((getkword "\nErase source objects? [Yes/No] <N>: ")) ("No")))
  23.  
  24.             (command "_.mirror" sel "" "_non" pt1 "_non" pt2 flg) ;; Invoke the MIRROR command
  25.  
  26.             (setvar 'orthomode otm)
  27.             ;; Reset the ORTHOMODE sys var
  28.             ;; (always store/restore the original sys var value, don't assume that you know what that value should be)
  29.  
  30.             ;; If the mirror transformation was applied to the original objects
  31.             (if (= "Yes" flg)
  32.                 ;; Highlight the original selection
  33.                 (setq new sel)
  34.                 ;; Else collect the new objects
  35.                 (while (setq ent (entnext ent)) ;; While there is another entity in the database
  36.                     (ssadd ent new) ;; Add it to the new selection set
  37.      
  38.                     ;; If subentities follow this entity
  39.                     (if (= 1 (cdr (assoc 66 (entget ent))))
  40.      
  41.                         ;; Iterate over them until we reach the terminating SEQEND entity:
  42.                         (while (/= "SEQEND" (cdr (assoc 0 (entget ent))))
  43.                             (setq ent (entnext ent))
  44.                         ) ;; end while
  45.                     ) ;; end if
  46.                 ) ;; end while
  47.             ) ;; end if
  48.  
  49.             ;; Highlight the new objects
  50.             (sssetfirst nil new)
  51.  
  52.         ) ;; end progn
  53.     ) ;; end if
  54.  
  55.     ;; Supress the return of the last evaluated expression
  56.     (princ)
  57. ) ;; end defun
  58.  
  59. (defun lastent ( / rtn tmp )
  60.     (setq rtn (entlast))
  61.     (while (setq tmp (entnext rtn)) (setq rtn tmp))
  62.     rtn
  63. )

qwrtz

  • Newt
  • Posts: 22
Re: Selection by Handle greater than
« Reply #18 on: April 27, 2015, 09:36:21 AM »
Yes, that works. I should have realized the old objects were just being moved and re-oriented. Thanks for fixing that.

.... I tend to avoid using AutoCAD commands in my code, as you become reliant on the behaviour of the command under each condition.

Great idea. Then you don't have to revise them each time Autodesk makes a change to the input that a command expects. I should have done that. I've got custom versions of nearly every command I use, but I wrote them all using the (command) function and so I dread each new version and what it's going to do to my custom commands. Maybe I should start re-writing them.

I've been re-reading the earlier discussion between you and MP and I have a question about that:  It sounds like the only problem with MP's (_ssAfter) function is that it might try to add a SEQEND to a selection set, and that would fail and the function would return nil. If that's the only problem, wouldn't it be easy to protect against that possibility by checking to see (if (/= "SEQEND" (cdr (assoc 0 (entget e))))) before adding each entity?
« Last Edit: April 27, 2015, 04:32:26 PM by qwrtz »

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Selection by Handle greater than
« Reply #19 on: April 27, 2015, 05:47:15 PM »
I've been re-reading the earlier discussion between you and MP and I have a question about that:  It sounds like the only problem with MP's (_ssAfter) function is that it might try to add a SEQEND to a selection set, and that would fail and the function would return nil. If that's the only problem, wouldn't it be easy to protect against that possibility by checking to see (if (/= "SEQEND" (cdr (assoc 0 (entget e))))) before adding each entity?

Indeed - that is pretty much the modification that MP has implemented following my earlier comments; except that you also want to exclude the subentities which follow a complex entity since, although such entities can be added to a selection set, they cannot be processed by a command or selected & gripped and so it is pointless to include them.

It sounds like you now have a good grasp of these concepts, well done.

Lee

qwrtz

  • Newt
  • Posts: 22
Re: Selection by Handle greater than
« Reply #20 on: April 27, 2015, 09:03:28 PM »
I was thinking the same thing myself. I'm amazed at how much more I know about this subject and about Autolisp in general than I did just a week ago. Good instruction!

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Selection by Handle greater than
« Reply #21 on: April 28, 2015, 06:20:57 AM »
Thank you - that's great to hear!  :-)