TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: roy_043 on January 09, 2018, 10:16:18 AM

Title: FYI: enlast is buggy!
Post by: roy_043 on January 09, 2018, 10:16:18 AM
I do not know if this is common knowledge, I do not remember seeing any posts to this effect, but the entlast function is quite buggy. This applies to both the AutoCAD and the BricsCAD implementation. I have just found out that the BricsCAD team have actually gone out of their way to exactly emulate the quaint AutoCAD behavior.

The problem in a nutshell:
The return value of (entlast) can depend on the layout that was previously active. (There may of course be other issues...)

See the attached test files (Lisp and dwg).

Instead of entlast I advise using something like this:
Code - Auto/Visual Lisp: [Select]
  1. (defun ReliableEntlast ( / ss)
  2.   (if (setq ss (ssget "_L"))
  3.     (ssname ss 0)
  4.   )
  5. )
Title: Re: FYI: enlast is buggy!
Post by: MP on January 09, 2018, 10:34:47 AM
In before John. One issue some programmers may miss is that entlast returns the last non deleted primary entity, so while the last entity may be an attribute entlast would return its owner, a block instance.
Title: Re: FYI: enlast is buggy!
Post by: JohnK on January 09, 2018, 10:35:55 AM
Somewhat related topic, if anyone wants to add/adjust/correct/etc. any section of theSwamp's Autolisp help file they will need to follow some simple HTML editing sytaxs rules so here is some basic info about the process. Here is a section (the HTML source) for the ADD (http://www.theswamp.org/~john/avlisp/#+) function so you can see the HTML tags I added/used.

Below that is the CSS for the file so they can see what classes they can use.

Aside from that Mark or I will have to do the final editing because the file resides in my home directory on TheSwamps server. So, if you provide a code snip like the ADD example below it can be added to the file.

Code - HTML5: [Select]
  1. <h3 class="String" id="+">+ (add)</h3>
  2.  
  3. Returns the sum of all numbers
  4.  
  5. (+ [number number] ...)
  6.  
  7. <span class="ArgumentsIdentifier">Arguments</span>
  8.  
  9. number
  10.  
  11. A number.
  12.  
  13. <span class="ReturnsIdentifier">Return Values</span>
  14.  
  15. The result of the addition. If you supply only one number argument, this
  16. function returns the result of adding it to zero. If you supply no arguments,
  17. the function returns 0.
  18.  
  19. <span class="ExamplesIdentifier">Examples</span>
  20.  
  21. (+ 1 2) returns 3
  22.  
  23. (+ 1 2 3 4.5) returns 10.5
  24.  
  25. (+ 1 2 3 4.0) returns 10.0
  26.  
  27. <span class="PreProc">-------------------------------------------------------------------------------</span>

Code - CSS: [Select]
  1. <style type="text/css">
  2. <!--
  3. pre {
  4.     color:#333333;
  5.     background-color:#fffffc;
  6.     margin-left:7px;
  7.     margin-right:7px;
  8.     margin-top:0;
  9.     margin-bottom:0;
  10.     border:none;
  11.     padding:0;
  12.     text-indent:0;
  13.     font-family:'Courier New', monospace;
  14.     font-size:12px;
  15. }
  16. body {
  17.     color:#333333;
  18.     background-color:#fffffc;
  19. }
  20. .heading {
  21.     color: #000044;
  22.     font-size:22px;
  23.     font-weight:bold;
  24.     font-style: italic;
  25. }
  26. .Ignore {
  27.     color: #202020;
  28. }
  29. .Statement {
  30.     color: #7e8aa2;
  31. }
  32. .Todo {
  33.     color: #C82536;
  34.     font-size:14px;
  35.     font-style: italic;
  36.     font-weight: bold;
  37.     text-decoration: underline;
  38. }
  39. .Special {
  40.     color: #B22222;
  41. }
  42. .Identifier {
  43.     color: #000044;
  44. }
  45. .ArgumentsIdentifier {
  46.     font-family:'Courier New', monospace;
  47.     font-size:14px;
  48.     font-weight:bold;
  49.     font-style:italic;
  50.     text-decoration: underline;
  51. }
  52. .ReturnsIdentifier {
  53.     font-family:'Courier New', monospace;
  54.     font-size:14px;
  55.     font-weight:bold;
  56.     font-style:italic;
  57.     text-decoration: underline;
  58. }
  59. .ExamplesIdentifier {
  60.     font-family:'Courier New', monospace;
  61.     font-size:14px;
  62.     font-weight:bold;
  63.     font-style:italic;
  64.     text-decoration: underline;
  65. }
  66. .PreProc {
  67.     color: #000000;
  68. }
  69. .String {
  70.     color: #000044;
  71.     font-size:22px;
  72.     font-style: italic;
  73. }
  74. -->
  75. </style>

Title: Re: FYI: enlast is buggy!
Post by: Marc'Antonio Alessi on January 09, 2018, 12:22:59 PM
Maybe this can help: https://www.theswamp.org/index.php?topic=45732.msg509031#msg509031
Title: Re: FYI: enlast is buggy!
Post by: roy_043 on January 09, 2018, 01:07:17 PM
@Marc'Antonio Alessi: Thanks for confirming this.

Note that entnext (no doubt a close cousin of entlast) is also buggy.
Title: Re: FYI: enlast is buggy!
Post by: Marc'Antonio Alessi on January 09, 2018, 01:33:28 PM
@Marc'Antonio Alessi: Thanks for confirming this.

Note that entnext (no doubt a close cousin of entlast) is also buggy.
:yes: :)
Title: Re: FYI: enlast is buggy!
Post by: ribarm on January 09, 2018, 01:42:35 PM
Now you got me... Is this OK?

Code - Auto/Visual Lisp: [Select]
  1. (defun c:elast ( / tmp )
  2.   (setq tmp (entlast))
  3.   (if (entnext tmp)
  4.     (while (setq tmp (entnext tmp))
  5.       (setq el tmp)
  6.     )
  7.     (setq el tmp)
  8.   )
  9.   (prompt "\nLast entity name is stored in variable \"el\"... You can call it with !el...")
  10.   (princ)
  11. )
  12.  
:thinking:
Title: Re: FYI: enlast is buggy!
Post by: ribarm on January 09, 2018, 02:53:50 PM
I guess it's wrong... Why simple when it can be more complex... DUH...

Code - Auto/Visual Lisp: [Select]
  1. (defun c:elast ( / tmp )
  2.   (if (and (ssget "_L") (setq tmp (ssname (ssget "_L") 0)))
  3.     (if (and (entnext tmp) (wcmatch (cdr (assoc 0 (entget (entnext tmp)))) "ATTRIB,VERTEX"))
  4.       (progn
  5.         (while (/= (cdr (assoc 0 (entget (setq tmp (entnext tmp))))) "SEQEND")
  6.           (setq el tmp)
  7.         )
  8.         (if (= (cdr (assoc 0 (entget tmp))) "SEQEND")
  9.           (setq el tmp)
  10.         )
  11.       )
  12.       (setq el tmp)
  13.     )
  14.     (progn
  15.       (setq tmp (entlast))
  16.       (if (and tmp (entnext tmp) (wcmatch (cdr (assoc 0 (entget (entnext tmp)))) "ATTRIB,VERTEX"))
  17.         (progn
  18.           (while (/= (cdr (assoc 0 (entget (setq tmp (entnext tmp))))) "SEQEND")
  19.             (setq el tmp)
  20.           )
  21.           (if (= (cdr (assoc 0 (entget tmp))) "SEQEND")
  22.             (setq el tmp)
  23.           )
  24.         )
  25.         (setq el tmp)
  26.       )
  27.     )
  28.   )
  29.   (if el
  30.     (prompt "\nLast entity name is stored in variable \"el\"... You can call it with !el...")
  31.   )
  32.   (princ)
  33. )
  34.  
  35. (defun c:EntlastTest ( / el )
  36.   (setvar 'ctab "Model")
  37.   (c:elast)
  38.   (print (vlax-ename->vla-object el))
  39.   (setvar 'ctab "Layout1")
  40.   (setvar 'ctab "Model")
  41.   (c:elast)
  42.   (print (vlax-ename->vla-object el))
  43.   (setvar 'ctab "Layout2")
  44.   (setvar 'ctab "Model")
  45.   (c:elast)
  46.   (print (vlax-ename->vla-object el))
  47.   (princ)
  48. )
  49.  
Title: Re: FYI: enlast is buggy!
Post by: roy_043 on January 09, 2018, 04:06:19 PM
I have upgraded a library function to this:
Code - Auto/Visual Lisp: [Select]
  1. (defun KGA_Sys_Entlast ( / enm ss tmp)
  2.   (if (setq ss (ssget "_L"))
  3.     (progn
  4.       (setq enm (ssname ss 0))
  5.       (while
  6.         (and
  7.           (setq tmp (entnext enm))
  8.           (not (vlax-erased-p tmp))                                      ; Required: entnext is also buggy.
  9.           (vl-position (vle-entget 0 tmp) '("ATTRIB" "SEQEND" "VERTEX")) ; Required: entnext is also buggy.
  10.         )
  11.         (setq enm tmp)
  12.       )
  13.     )
  14.   )
  15.   enm
  16. )

Vle-entget is a built-in function in BricsCAD. You can probably guess what it returns.

@ribarm: (ssget "_L") can return nil...

EDIT: Improved version of KGA_Sys_Entlast.
Title: Re: FYI: enlast is buggy!
Post by: roy_043 on January 09, 2018, 04:13:28 PM
Maybe this can help: https://www.theswamp.org/index.php?topic=45732.msg509031#msg509031
Thanks for that link. I'll study the topic. I vaguely remember reading only parts of it in the past. :roll:
Title: Re: FYI: enlast is buggy!
Post by: ribarm on January 09, 2018, 04:43:02 PM
@ribarm: (ssget "_L") can return nil...

code corrected above...
Title: Re: FYI: enlast is buggy!
Post by: Marc'Antonio Alessi on January 11, 2018, 04:48:25 AM
Maybe this can help: https://www.theswamp.org/index.php?topic=45732.msg509031#msg509031
Thanks for that link. I'll study the topic. I vaguely remember reading only parts of it in the past. :roll:
This is not related to the subject of the discussion but, since you use Bricscad, there is another strange behavior in the old versions concerning ssget in empty DWG:
Code: [Select]
(defun C:TestEmptyDWG ( / Ss_Set)
  (print (getvar "ACADVER"))
  (if (setq Ss_Set (ssget "_X"))
    (progn
      (print (cdr (assoc 0 (entget (ssname Ss_Set 0)))))
      (princ (strcat " sslength = " (itoa (sslength Ss_Set))))
    )
    (print "no ssget entities in DWG")
  )
  (princ)
)
Code: [Select]
: TESTEMPTYDWG
"20.0 BricsCAD"
"no ssget entities in DWG"

: TESTEMPTYDWG
"19.1 BricsCAD"
"VIEWPORT"  sslength = 1
This is my version of Ss-After:
Code: [Select]
(defun ALE_Ss-After (EntNam / SelSet)
  (cond
    ( (not EntNam) (ssget "_X" '((0 . "~VIEWPORT"))) ); "~VIEWPORT" x Bricscad
    ( (setq EntNam (entnext EntNam))
      (setq SelSet (ssadd EntNam))
      (while (setq EntNam (entnext EntNam))
        (if (entget EntNam) (ssadd EntNam SelSet))
      )
      SelSet
    )
  )
)
Title: Re: FYI: enlast is buggy!
Post by: roy_043 on January 11, 2018, 05:39:35 AM
@Marc'Antonio Alessi:
Are you testing this with the same dwg? Because I cannot reproduce this in BC V14 or V15. Maybe in one case you are testing with a dwg that (still) contains a 'main' paper space viewport. Which is an invisible entity that cannot be erased through normal user actions. To delete it you have to use vla-delete, entdel will not work.

EDIT: 'vla-deleting' a main viewport results in an _Audit issue. So such a VP should not be deleted.
Title: Re: FYI: enlast is buggy!
Post by: roy_043 on January 11, 2018, 05:57:34 AM
@Marc'Antonio Alessi:
Actually I am trying to create a reliable and fast 'Ss-After' function myself. It is while doing so that I was confronted with the aforementioned issues with entlast and entnext.

Your function uses entnext which you have confirmed is buggy...

Here is my current version (maybe slow in large drawings?):
Code - Auto/Visual Lisp: [Select]
  1. ; Return value: Non-empty pickset or nil.
  2. ; Erased objects are not added to the set.
  3. (defun KGA_Conv_ObjectList_To_Pickset (lst / ret)
  4.   (setq ret (ssadd))
  5.   (foreach obj lst (if (not (vlax-erased-p obj)) (ssadd (vlax-vla-object->ename obj) ret)))
  6.   (if (/= 0 (sslength ret)) ret)
  7. )
  8.  
  9. ; (sssetfirst nil (SsAfter (car (entsel))))
  10. (defun SsAfter (enm / fnd tab)
  11.   (setq tab (strcase (vle-entget 410 enm)))
  12.   (while ; Required: entnext is buggy.
  13.     (and
  14.       (not fnd)
  15.       (setq enm (entnext enm))
  16.     )
  17.     (if
  18.       (and
  19.         (not (vlax-erased-p enm))              ; Required: entnext is buggy.
  20.         (= tab (strcase (vle-entget 410 enm))) ; Required: entnext is buggy.
  21.       )
  22.       (setq fnd enm)
  23.     )
  24.   )
  25.   (if fnd
  26.     (KGA_Conv_ObjectList_To_Pickset
  27.       (member
  28.         (vlax-ename->vla-object fnd)
  29.         (vle-collection->list
  30.           (vla-get-block
  31.           )
  32.         )
  33.       )
  34.     )
  35.   )
  36. )

EDIT: Improved version of SsAfter.
Title: Re: FYI: enlast is buggy!
Post by: Marc'Antonio Alessi on January 11, 2018, 08:28:18 AM
@Marc'Antonio Alessi:
Are you testing this with the same dwg? Because I cannot reproduce this in BC V14 or V15. Maybe in one case you are testing with a dwg that (still) contains a 'main' paper space viewport. Which is an invisible entity that cannot be erased through normal user actions. To delete it you have to use vla-delete, entdel will not work.

EDIT: 'vla-deleting' a main viewport results in an _Audit issue. So such a VP should not be deleted.
You're right, unfortunately I can not replicate the problem, I started from an empty metric DWG...
I analyzed my support requests to BricsCAD and I saw that it's been 9 years since the problem was solved by BricsCAD
(SR19947). Time passes very quickly...
Code: [Select]
(defun C:TestEmptyDWGm ( / Ss_Set)
  (print (getvar "ACADVER"))
  (if (setq Ss_Set (ssget "_X"))
    (progn
      (print (cdr (assoc 0 (entget (ssname Ss_Set 0)))))
      (princ (strcat " sslength = " (itoa (sslength Ss_Set))))
    )
    (print "no ssget entities in DWG")
  )
  (princ)
)
(defun C:TestEmptyDWG1 ( / Ss_Set)
  (setvar "CTAB" "Layout1")
  (setvar "CTAB" "Model")
  (if (setq Ss_Set (ssget "_X"))
    (progn
      (print (cdr (assoc 0 (entget (ssname Ss_Set 0)))))
      (princ (strcat " sslength = " (itoa (sslength Ss_Set))))
    )
    (print "no ssget entities in DWG")
  )
  (princ)
)
(defun C:TestEmptyDWG2 ( / Ss_Set)
  (setvar "CTAB" "Layout2")
  (setvar "CTAB" "Model")
  (if (setq Ss_Set (ssget "_X"))
    (progn
      (print (cdr (assoc 0 (entget (ssname Ss_Set 0)))))
      (princ (strcat " sslength = " (itoa (sslength Ss_Set))))
    )
    (print "no ssget entities in DWG")
  )
  (princ)
)
Code: [Select]
;--------------------------
: (C:TESTEMPTYDWGm)
"19.1 BricsCAD"
"no ssget entities in DWG"

: (C:TESTEMPTYDWG1)
"VIEWPORT"  sslength = 1

: (C:TESTEMPTYDWG2)
"VIEWPORT"  sslength = 2
;--------------------------


;--------------------------
: (C:TESTEMPTYDWGm)
"21.0 BricsCAD"
"no ssget entities in DWG"

: (C:TESTEMPTYDWG1)
"VIEWPORT"  sslength = 1

: (C:TESTEMPTYDWG2)
"VIEWPORT"  sslength = 2
;--------------------------


;--------------------------
Comando: (C:TESTEMPTYDWGm)
"19.0s (LMS Tech)"
"no ssget entities in DWG"

Comando: (C:TESTEMPTYDWG1)
"VIEWPORT"  sslength = 1

Comando: (C:TESTEMPTYDWG2)
"VIEWPORT"  sslength = 2
;--------------------------