thatandyward Posted November 14, 2017 Share Posted November 14, 2017 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 Quote Link to comment Share on other sites More sharing options...
Roy_043 Posted November 14, 2017 Share Posted November 14, 2017 Use the getpropertyvalue function to check the objectname. Quote Link to comment Share on other sites More sharing options...
thatandyward Posted November 15, 2017 Author Share Posted November 15, 2017 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.