Jump to content

Alternate way(s) to determine block sub-entity type?


thatandyward

Recommended Posts

I am writing a lisp routine which processes user selected (via ssget) blocks and outputs various data (handle, insertion point, rotation, specific attribute values) to a CSV text file. It processes both top-level blocks and nested blocks to any level.

 

This lisp routine needs to run on both AutoCAD for Windows & AutoCAD for Mac, so that limits options a little (no vla-, vlax-, and vlr- functions) and also why I can't just use the data extraction tool.

 

For each unique block definition the code builds an association list with block name as the key and a list of enames for all sub-entities of type INSERT e.g.

 

( (Block Name #1 . ( ...)) (Block Name #2) ...)

 

Subsequent instances of the block then just process the sub-entities in the association list, rather than needing to iterate though the entire block definition again.

 

This whole process is done recursively to handle the nested blocks - relevant section of code below:

 

;; ... code to initialize output file/selection set etc

;; iterate through blocks in selection set
(repeat (setq Idx (sslength Sel))

;; get current entity from Sel, re-set BlkProcessed & Parents
(setq
    Blk (ssname Sel (setq Idx (1- Idx)))
    BlkProcessed nil
    Parents nil
)

;;-------------------------------------------------------------------------
;; ProcessBlk - Process block entity sub-routine
;;		  Initial block added to NstLst as key value with
;;                associated INSERT sub-entities, if found.
;;                All duplicate blocks just process stored sub-entities.
;;                Data for all blocks written as CSV line to output file
;;              Sub-Routine to allow recursive calling
;; Arguments: 2
   	;;    Blk = Block Entity [as entity name]
;;    Parents = Parent Entities (parent grandparent...) [as list]
   	;; Returns: nil
   	;;-------------------------------------------------------------------------
(defun ProcessBlk (Blk Parents / Item EntLst SubEnt BlkName EntData )

    ;; get entity data & block name
    (setq
	EntData (entget Blk)
	BlkName (cdr (assoc 2 EntData))
    )
    
    ;; add Blk to NstLst if not already a memeber
    (if (not (Member BlkName (mapcar 'car NstLst)))

	(Progn

	    ;; get 1st sub-entity from Block definition table & add to EntLst
	    (setq
		SubEnt (cdr (assoc -2 (tblsearch "BLOCK" BlkName)))
		EntLst (cons SubEnt EntLst)
	    )

	    ;; iterate remaning sub-entites & add to EntLst
	    (while
		(setq SubEnt (entnext SubEnt))
		(setq EntLst (cons SubEnt EntLst))
	    )

	    ;; filter EntLst add to NstLst w/BlkName as key
	    (setq
		EntLst (vl-remove-if-not '(lambda(x) (= (cdr (assoc 0 (entget x))) "INSERT")) EntLst)
		NstLst (cons (cons BlkName EntLst) NstLst)
	    )

	)  ;; END progn
	
    )  ;; END if

    ;; write out CSV line data for Blk
    (princ (BuildBlkInfo Blk EntData BlkName Parents) Dest)

    ;; set BlkProcessed to T if currently nil
    (if (not BlkProcessed) (setq BlkProcessed T))

    ;; append current Blk to Parents
    (setq Parents (cons Blk Parents))

    ;; process all nested blocks associated w/BLkName in NstLst
    (foreach Item (cdr (assoc BlkName NstLst))
	(ProcessBlk Item Parents)
    )

) ;; END Defun

;; Process top-level Blk, if not already processed
(if (not BlkProcessed) (ProcessBlk Blk nil))

   ) ;; END repeat
   
;; ... code to close output file & provide feedback to user

 

Whilst this all works fine I have found a couple of specific blocks in my test drawings are taking a long time to process & subsequently slowing down the overall code execution. The 'problem' blocks are taking ±5sec to pre-process & add to NstLst, and whilst I'm only doing this once per unique block definition it still has quite an impact relative to the rest of the code execution.

 

Looking at these 'problem' blocks in more detail, they appear comprise of ±450 sub-entities, the vast majority of which are 3DSOLID entities.

 

Iterating through that many sub-entities does not appear to be the root-cause of slow-down, in fact if I bypass the vl-remove-if-not filtering of EntLst the pre-processing time is comparable to other 'non-problem' blocks in the drawing; however as soon I introduce entget everything slows down.

 

When I examine the returned data from entget I see that the association list has ±3200 items, the majority of which are encrypted model geometry.

 

I have tried various methods of determining the value of group code 0 in the returned list (assoc, nth, cdr, member) in the hope that it might affect how the list is processed and mean that not all 3200 items would need to be iterated through.

 

I have also tried performing the entget/type test as I'm iterating the sub-entities to determine if they get added to NstLst, rather than just adding all and then filtering at the end. Nothing I've tried has had any meaningful impact on the pre-processing time.

 

It seems to me that the actual act of performing the entget is what is slowing things down due to the size of the return data list. I tried performing entget 450 times on a 'regular' entity and had no slow down at all —which leads to my question(s):

 

  • Is there an alternate way to obtain the entity type without using entget, which would be faster?
  • Is it possible to do a 'partial entget' i.e. only get the first 2 items?
  • Would it be possible to create a filtered selection set i.e. like (ssget "X" '((0 . "INSERT"))) but only for block-specific sub-entities?

 

Ideally the solution would work on both AutoCAD for Mac & AutoCAD for Windows although I'm not adverse to having OS-specific sections of code to allow faster implementation for Windows users if there is an ActiveX or similar Windows-only function that will help here.

 

Thanks

Link to comment
Share on other sites

Thanks Roy - that did the trick!

 

I swapped out the line...

 

EntLst (vl-remove-if-not '(lambda(x) (= (cdr (assoc 0 (entget x))) "INSERT")) EntLst)

 

for

 

EntLst (vl-remove-if-not '(lambda(x) (= "Block Reference" (getpropertyvalue x "LocalizedName"))) EntLst)

 

The previous run-time for my test drawing was ±13sec to process (1106) x blocks, 217 of which were nested. By swapping out the entget to getpropertyvalue the run time reduced to ±2sec.

 

Much simpler than I feared it would be, plus works on both AutoCAD for Windows & AutoCAD for Mac.

 

Really appreciate it.

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...