Jump to content

Help with Selection Set Indexing


MTodd

Recommended Posts

This is my first attempt at LISP and my first post.  I'm developing a LISP routine to take a selection set of text entities using SSGET and then convert specific text entities within that selection set to attributes in new blocks.  The original text then gets deleted from the drawing and, as I've learned, must also be deleted from the selection set if it's needed to look for additional items.  I'm using an index to step through items in the selection set.  When a text entity is found meeting the requirements, it gets converted to an attribute within a new block.  The tricky part is that each new block also requires another text entity from within the selection set, which must also be added to the block as an attribute and then also deleted from the drawing and the selection set.  So the problem is trying to correctly update the index to point to the next item in the selection set.  The index of the second text entity could be located in the selection set either before or after the currently indexed entity.  If it's located before or after affects how the index gets updated.  There's no function to find the index is of the second text entity.  It seems like the selection set would have to be stepped through item by item to find the location of the second text entity so that the index can be modified correctly to point to the next item.  The following code appears to work but just wanted some assistance if there's a more efficient method or additional comments:

 

    ; previous code to insert block and change attributes to

    ; Primary and Secondary text values

    ; ...

    ; i = index to Primary text entity

    ; ii = index to Secondary text entity

    ; decrement i (index) to Primary text if Secondary text location

    ;  precedes location of Primary text within selection set (ss)

        (setq ii 0)
        (while (and (> i ii) (not (eq obj (ssname ss ii))))
          (setq ii (1+ ii)))
        (if    (> i ii)
          (setq i (1- i)))        

        (ssdel obj primarytext)        ; delete Primary text from ss
        (entdel primarytext)        ; delete Primary text from drawing

        (ssdel obj secondarytext)        ; delete Secondary text from ss
        (entdel secondarytext)        ; delete Secondary text from drawing

    ; if ii > i, then i will point to next entity in selection set

 

 

------------------- full code for reference -----------------

; Primary text = LOE Item No.

; Secondary text = LOE Count

 

;;;https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/
;;;txt2blk-changes-the-current-rotation/td-p/8367208
;;;dbhunia
;;;in reply to: Ranksteven.callahan
;;;?10-30-2018 12:41 AM
;;;Re: TXT2BLK changes the current rotation.
;;;
;;;
;;; BBlock (convert LOE and LOM Bubbles to Blocks and copy
;;; existing Item numbers and Counts, if applicable, to
;;; blocks as attributes

(defun
   c:BBlock  (/    savOSmode savAttdia savAttreq ss ssl ssi i ent e text attvalue attnum)
  (vl-load-com)
  (setvar "cmdecho" 0)            ; disable command echo
  (command ".undo" "begin")
  (setq savOSmode (getvar "osmode"))    ; save osnap
  (setq savAttdia (getvar "attdia"))    ; save attdia
  (setq savAttreq (getvar "attreq"))    ; save attreq
  (setvar "attdia" 0)            ; disable attribute dialog
  (setvar "attreq" 0)            ; disable attribute request
  (if (setq ss (ssget (list '(0 . "TEXT")))) ; select only TEXT objects
    (progn
      (setvar "osmode" 0)        ; disable osnap
      (setq ssl (sslength ss))        ; ent = entity name of ss object
      (setq i 0)            ; ent = entity name of ss object
      (while (and (< i ssl) (> ssl 0))    ; while index < selection set length
    (setq ent (ssname ss i))     ; ent = entity name of ss object
    (setq e (entget ent))         ; ent = entity name of ss object
    (setq text (cdr (assoc 1 e)))    ; text = text value
    (setq INS (cdr (assoc 11 e)))    ; INS = insertion pt of text (assume mid-center)
    (if (TestLOEItemNo INS)
      (progn
        (command ".insert" "LOE Bubble Right" (polar INS 0.0 -0.2188) "" "" "")
        ;; Posted September 2, 2010
        ;; NumFix
        ;; Fix number string with leading zeros
        ;; n - Number of characters for final string
        ;; Alan J. Thompson, 10.29.09
        (while (< (strlen text) 3)    ; pad zeros to LOE Item No.
          (setq text (strcat "0" text))
          text)
        (setq attvalue text)
        (setq attnum 1)
        (Change_Att_Val attvalue attnum)    ; call to change attribute to text value
        (BubbleSelectionSet ss ent INS)    ; call to delete objects in rectangle around LOE Item No.
        (ssdel ent ss)        ; delete LOE Item No. from ss
        (entdel ent)        ; delete LOE Item No. from drawing
        )                ; end if then progn TestLOEItemNo
      (setq i (1+ i))        ; end if else TestLOEItemNo, if not LOE Item No., increment
      )                ; end if TestLOEItemNo
    )                ; end of repeat loop for sslength ss
      )                    ; end if then  progn setq ss "TEXT"
    )                    ; end if "TEXT"
  (setvar "osmode" savOSmode)
  (setvar "attdia" savAttdia)        ; restore attdia
  (setvar "attreq" savAttreq)        ; restore attreq
  (command ".undo" "end")
  (setvar "cmdecho" 1)            ; restore command echo
  (princ))                ; defun

(defun
  Change_Att_Val  (attvalue attnum / obj x)
  ; attnum: 0=1st Attribute (DESC), 1=2nd (LOE), 2=3rd (Count)
  (setq obj (vlax-ename->vla-object (entlast)))
  (if (and
    (vlax-property-available-p obj 'hasAttributes)
    (eq (vla-get-HasAttributes obj) :vlax-true))
    (progn
      (setq x 0)
      (foreach
     att  (vlax-invoke Obj 'GetAttributes)
    (if (= x attnum)
      (vla-put-Textstring att attvalue)) ; Change Attribute Value
    (setq x (+ x 1))))))

(defun
   BubbleSelectionSet  (ss ent INS / LL UR SBOX obj ii)
  ;; Define Selection Set around existing bubble based on typical
  ;; LOE Bubble dimensions and delete bubble
  (setq LL (list (- (car INS) 0.2188) (- (cadr INS) 0.125) 0.0))
  (setq UR (list (+ (car INS) 0.5937) (+ (cadr INS) 0.125) 0.0))
  (setq SBOX (ssget "W" LL UR))
  ;; Tharwat
  ;; Posted August 27, 2015
  ;;

 

(defun
   TestLOEItemNo  (INS / LL UR SBOX obj test arc1list arc1elem arc2elem)
  ;; Define Selection Set around existing bubble based on typical
  ;; LOE Bubble dimensions and verify correct geometric shapes
  ;; for valid LOE Item No. Bubble
  (setq LL (list (- (car INS) 0.2188) (- (cadr INS) 0.125) 0.0))
  (setq UR (list (+ (car INS) 0.5937) (+ (cadr INS) 0.125) 0.0))
  (setq SBOX (ssget "W" LL UR))
  (setq arc1list nil)
  (setq test F)                ; initialize flag FALSE
  (if (not (= SBOX nil))
    (progn
      (repeat (sslength SBOX)
    (setq obj (ssname SBOX 0))    ; get object from SBOX selection set
    (setq otype (cdr (assoc 0 (entget obj)))) ; get object type
    (if (equal otype "ARC")
      (progn            ; progn for if "ARC"
        (if    (equal arc1list nil)
          (setq arc1list (entget obj)) ; arc1list is entity of first arc
          (progn            ; start else for if arc1list nil
        (setq arc1elem (caddr (assoc 10 arc1list)))
        (setq arc2elem (caddr (assoc 10 (entget obj))))
        (if (equal (- arc1elem arc2elem) 0.0 0.01)
          (progn        ; then progn
            (setq arc1elem (cadr (assoc 10 arc1list)))
            (setq arc2elem (cadr (assoc 10 (entget obj))))
            (if    (< 0.5 (abs (- arc1elem arc2elem)) 0.6) ; wide enough?
              (progn        ; then progn
            (if (< arc1elem arc2elem)
              (progn
                (setq arc1elem (cdr (assoc 50 arc1list)))
                (setq arc2elem (cdr (assoc 50 (entget obj)))))
              (progn
                (setq arc1elem (cdr (assoc 50 (entget obj))))
                (setq arc2elem (cdr (assoc 50 arc1list))))) ; end if (< arc1elem arc2elem
            (if (and (equal arc1elem (/ pi 2)) (equal arc2elem (* pi 1.5))) ; and
              (setq test T)    ; set flag TRUE if all tests pass
              )        ; end if and (equal
            )        ; end if then progn (< 0.5 (abs (- arc1elem arc2elem
              )            ; end if (< 0.5 (abs (- arc1elem arc2elem
            )            ; end if then progn (equal (- arc1elem arc2elem)
          )            ; end if (equal (- arc1elem arc2elem)
        )            ; end else progn arc1list nil            
          )                ; end if arc1list nil
        )                ; end progn then "ARC"
      )                ; end if "ARC"
    (ssdel obj SBOX)        ; del obj from selection set
    )                ; end repeat
      )                    ; end if then progn (> (sslength SSBOX) 0)
    )                    ; end if progn (> (sslength SSBOX) 0)
  test                    ; provide true/false to calling function
  )                    ; end defun TestLOEItemNo

 

 

 

 

Link to comment
Share on other sites

First, welcome to CadTutor.

 

Although the selection set is a list of sorts, It might be more efficient to convert the Selection Set to a list. Lisp is after all LISt Processing, and has numerous functions for processing lists not immediately available when processing a Selection Set.

 

Add four text items to a new drawing in the following order "a" "test" "of" "text items"

 

Get a selection set of text items (taken from your code)

 

(setq ss (ssget (list '(0 . "TEXT"))))

Depending on the method used their order in the selection set will be reverse order of creation when using crossing or window or order selected if picked individually

(repeat (setq cnt (sslength ss))
	(setq ss_lst (cons (ssname ss (setq cnt (1- cnt))) ss_lst))
);end_repeat

will convert the selection set into a list of entity names in the same order as they were in the selection set, by starting at the end and adding each new item to the front of the list.

 

If you the want the text string of each item you can use the mapcar function along with the anonymous lambda function to process your list of entities extracting the text string

 

(setq txt_lst (mapcar '(lambda (x) (cdr (assoc 1 (entget x)))) ss_lst))

Here a new list is constructed by taking every item in order from ss_lst and feeding it to the lambda where the item becomes x

 

This text list (txt_lst) will be in the same order as the entity list (ss_lst)

 

If you want to find the location of a specific text string you can use one of the visual lisp functions vl-position

 

(setq find "of")
(setq pos (vl-position find txt_lst))

 

This assigns to the variable pos the zero based index number of "of" in the list. If you then want the entity name of the text "of" it can be obtained using the nth function

 

(setq ent (nth pos ss_lst))

 

If you want to remove and item from a list

 

(setq txt_lst (vl-remove-if '(lambda (x) (= (nth pos txt_lst) x)) txt_lst))
;;or
(setq ss_lst (vl-remove-if '(lambda (x) (= (nth pos ss_lst) x)) ss_lst))

;;if you want to delete the entity do it before removing it from the list
;;or store it in another list of items to be deleted later

(entdel (nth pos ss_lst))
(setq ss_lst (vl-remove-if '(lambda (x) (= (nth pos ss_lst) x)) ss_lst))

(setq delete_lst (cons (nth pos ss_lst) delete_lst))
(setq ss_lst (vl-remove-if '(lambda (x) (= (nth pos ss_lst) x)) ss_lst))

;;then later
(foreach itm delete_lst (entdel itm))

 

Edited by dlanorh
  • Like 1
Link to comment
Share on other sites

Thanks for your excellent response and the recommendations for more efficient list processing as well as the ordering of the lists and the detailed list of functions needed.  The suggestions work well and will be incorporated further as development continues.  Functions mapcar and lambda work great together.  Making a list of text insertion points also works, which will probably be used since they are required to identify the context of each text object.

   

Link to comment
Share on other sites

12 hours ago, MTodd said:

Thanks for your excellent response and the recommendations for more efficient list processing as well as the ordering of the lists and the detailed list of functions needed.  The suggestions work well and will be incorporated further as development continues.  Functions mapcar and lambda work great together.  Making a list of text insertion points also works, which will probably be used since they are required to identify the context of each text object.

   

 

Don't forget that text can be justified. The insertion point is good for left, aligned or fit, all the others require the text alignment point which is usually store in code 11. See link below for a detailed list of text dxf codes.

 

Text DXF Group codes

 

Link to comment
Share on other sites

Just a maybe if you make the list (("textstr1" X Y entname)("textstr2" X Y entname)…...

 

Then use vl-sort your list on 1st item will now reflect a list in most cases alphabetic order, note this does have a bit of a disclaimer as alphanumeric text needs a bit more thought.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...