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