Modify all blocks without opening drawing

Modify all blocks without opening drawing

imajar
Advisor Advisor
3,519 Views
10 Replies
Message 1 of 11

Modify all blocks without opening drawing

imajar
Advisor
Advisor

Hello,

 

I wish to to through and perform the same operation to all 73,000 blocks in a drawing.  I have a lisp that successfully does what I want, except that it runs very slow when the full assembly is open (ETA:  73 days) . . . While the lisp is running, it appears that the block operations execute quickly and all the time is spent transitioning from one block to the next as it goes into and back out of the model space.  Is it possible to go in and modify each block completely bypassing the model space?

 

 

 

One approach I am considering is using VLA to skip loading the drawing entirely, then looping through each block individually performing the update and then somehow saving the block.

 

I have been studying a code by Lee Mac that can can extract blocks from a drawing without opening it - but I cannot find much information about how to modify that information and then add it back in. . .  And I am new at Lisp and brand new at VLA . . . so please forgive me if this is simple!

 

Thank You,

 

-Aaron


Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
0 Likes
3,520 Views
10 Replies
Replies (10)
Message 2 of 11

dlanorh
Advisor
Advisor
Can you post a copy of the current lisp.

I am not one of the robots you're looking for

0 Likes
Message 3 of 11

imajar
Advisor
Advisor
;; A quick and dirty lisp to cycle through all of the blocks in an autocad architectural drawing and convert mass elements into space elements then solids
;; Aaron Jarrett
;; 25 Sep 2018

;; Background:  This is to convert an IFC file that is imported into acad architectural from aec geometry to solid geometry.  For the file in question,
;; the standard methods (exporttoautocad, convertto3dsolid, etc) all produced bad geometry in the conversion.  For some reason, converting from mass elements to space elements and then to solid elements
;; seems to work better (though still not perfect).  This lisp goes through each block and sub block to convert the geometry.  This method preserves the block structures.

(defun c:masstosolid ()
  ;; Make a list of all blocks and subblocks in the drawing, similar to the list provided in the bedit command, this little bit taken from a lisp by Lee Mack:
  ;; Not sure how it works, but appears to generate the result I desire!
  (setq lst nil)
  (while (setq def (tblnext "block" (not def)))
    (if (zerop (logand 125 (cdr (assoc 70 def))))
      (setq lst (cons (cdr (assoc 2 def)) lst))
      )
    )
  (setq ntotal (length lst))
  (princ (strcat "Number of blocks: " (itoa ntotal)))
  
  ;; Now, we have the list, next we iterate through each item in this list and perform the converttospacelements command:
  (setq lstlength(length lst)) 								;; Determine how many blocks are in the drawing - to iterate through them
  (setq n 0)										;; initialize the counter
  (cond (lst										;; Verify that we have blocks at all.
	 (repeat lstlength									;; Iterate through each item in the list of blocks.
	   ;;(princ (strcat "Editing block: " (nth n lst)))
	   (command-s "_.bedit" (nth n lst))							;; bedit a block
	   (setq ss1 nil)
	   (setq ss1 (ssget "_X" '((0 . "AEC_MASS_ELEM"))))					;; Create a selection set of eligible items (AEC MASS ELEMENTS)
	   (cond (ss1
		  (command-s "_spaceconvertmasselement" ss1 "" "_Y")				;; If we have elibible geometry, perform the conversion
		  )
		 )
	   (setq ss1 nil)
	   (setq ss2 nil)
	   (setq ss2 (ssget "_X" '((0 . "AEC_SPACE"))))					;; Create a selection set of eligible items (AEC Spaces)
	   (cond (ss2
		  (command-s "-vpoint" "R" 45 45)						;;  The convertto3dsolid command throws a user alert if the view is in plan view, which disrupts the lisp, this goes away if the view is switched to anything but plan view which is the purpose of this line.
		  (command-s "_convertto3dsolids" ss2 "" "_Y")					;; convert to solid
		  )
		 )
	   (setq ss2 nil)
	   (terpri) (princ (strcat "Completed " (itoa (1+ n)) " out of " (itoa ntotal)))	;; progress updates
	   (setq n (1+ n))									;; 1+ on the iterative counter
	   (command-s "_.bsave")								;; save the block
	   )
	 (command-s "_.bclose" "_sav")
	 )
	)
  (terpri)
  )

Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
0 Likes
Message 4 of 11

dlanorh
Advisor
Advisor

@imajar wrote:

Hello,

 

I wish to to through and perform the same operation to all 73,000 blocks in a drawing.  I have a lisp that successfully does what I want, except that it runs very slow when the full assembly is open (ETA:  73 days) . . . While the lisp is running, it appears that the block operations execute quickly and all the time is spent transitioning from one block to the next as it goes into and back out of the model space.  Is it possible to go in and modify each block completely bypassing the model space?

 

 

 

One approach I am considering is using VLA to skip loading the drawing entirely, then looping through each block individually performing the update and then somehow saving the block.

 

I have been studying a code by Lee Mac that can can extract blocks from a drawing without opening it - but I cannot find much information about how to modify that information and then add it back in. . .  And I am new at Lisp and brand new at VLA . . . so please forgive me if this is simple!

 

Thank You,

 

-Aaron


As you surmised the lag is caused by AutoCAD cycling into and out of the Block Editor. You were saving the block twice and closing the block editor. I think you can skip the bclose and just open another block, but i'm not sure what effect this will have on. Anyway I have tidied up the code and put in a few explanations.

 

I have no way of testing this since I don't run Architectural, so please test the below and give me your thoughts

 

;; Make a list of all blocks and subblocks in the drawing, similar to the list provided in the bedit command, this little bit taken from a lisp by Lee Mack:
;; Not sure how it works, but appears to generate the result I desire!

;; THIS ITERATES THE BLOCK DEFINITION TABLE AND GRABS THE NAMES OF ALL THE BLOCKS THAT SATISFY (= 0 (LOGAND 125 FLAG))  
;; ASSOC 70 Block-type flags (bit coded values, may be combined):
;; 1 = This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application.
;; 2 = This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all).
;; 4 = This block is an external reference (xref).
;; 8 = This block is an xref overlay.
;; 16 = This block is externally dependent.
;; 32 = This is a resolved external reference, or dependent of an external reference (ignored on input).
;; 64 = This definition is a referenced external reference (ignored on input).

  (setq lst nil)
  (while (setq def (tblnext "block" (not def)))
    (if (zerop (logand 125 (cdr (assoc 70 def))))
      (setq lst (cons (cdr (assoc 2 def)) lst))
    )
  )
  (setq ntotal (itoa (length lst)))
  (princ (strcat "\nNumber of blocks: " ntotal "\n"))
  
;  ;; Now, we have the list, next we iterate through each item in this list and perform the converttospacelements command:
  (setq n 0)								;; initialize the counter
  (if lst										;; Verify that we have blocks at all.
	  (foreach blk lst 				;; Iterate through each item in the list of blocks. FOREACH IS BETTER HERE AS IT IS A LIST AND NOT A SELECTION SET
	    (command-s "_.bedit" blk)							                      ;; bedit a block
	    (setq ss1 (ssget "_X" '((0 . "AEC_MASS_ELEM"))))					  ;; Create a selection set of eligible items (AEC MASS ELEMENTS)
	    (if ss1 (command-s "_spaceconvertmasselement" ss1 "" "_Y"))	;; If we have elibible geometry, perform the conversion
		  (setq ss2 (ssget "_X" '((0 . "AEC_SPACE"))))					      ;; Create a selection set of eligible items (AEC Spaces)
	    (cond (ss2
              (command-s "-vpoint" "R" 45 45)						          ;;  The convertto3dsolid command throws a user alert if the view is in plan view, which disrupts the lisp, this goes away if the view is switched to anything but plan view which is the purpose of this line.
              (command-s "_convertto3dsolids" ss2 "" "_Y")				;; convert to solid
            )
		  )
	    (command-s "_.bsave")								                        ;; save the block
	    (setq n (1+ n)
            ss1 nil
            ss2 nil
      );end_setq
      (princ (strcat "\rCompleted " (itoa n)) " of " ntotal))	;; progress updates
	  )
	)
  (princ)
)
(princ)

I am not one of the robots you're looking for

0 Likes
Message 5 of 11

imajar
Advisor
Advisor

Thank you @dlanorh.  Unfortunately, the new script does not run much faster, for some reason autocad reverts back into the model space when you bedit from inside the block editor.  You can see this if you open a block and type in bedit, acad will revert to the model space before bringing up the menu of blocks to select from (same if you directly pass an argument with -bedit).  

 

Any other ideas?


Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
0 Likes
Message 6 of 11

dlanorh
Advisor
Advisor

@imajar wrote:

Thank you @dlanorh.  Unfortunately, the new script does not run much faster, for some reason autocad reverts back into the model space when you bedit from inside the block editor.  You can see this if you open a block and type in bedit, acad will revert to the model space before bringing up the menu of blocks to select from (same if you directly pass an argument with -bedit).  

 

Any other ideas?


You can access the individual objects in a block, but I don't know if you can pass these to your commands whilst it is in a block.

 

This may crash AutoCAD so test it on a drawing with only one block in it (Wblock a single block out to a test drawing).

Load the lisp and type test on the command line.

 

If it finds an "AEC_MASS_ELEM" it will print "Found" to the command line. If it errors, you should get an error message that starts "Oops an error occurred  : " or a crash.

 

This is iterating the Block Collection (Block Definition Table) as in the original lisp

 

(defun c:Test (/ *error* c_doc c_blks )
	
  (defun *error* ( msg )
		(if (and c_doc (= 8 (logand 8 (getvar 'UNDOCTL)))) (vla-endundomark c_doc))
    (if (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) (princ (strcat "\nOops an Error occurred : " msg)))
		(princ)
  );end_defun *error*

	(setq c_doc (vla-get-activedocument (vlax-get-acad-object))
        c_blks (vla-get-blocks c_doc)
  )

	(if (and c_doc (= 8 (logand 8 (getvar 'UNDOCTL)))) (vla-endundomark c_doc))
  (vla-startundomark c_doc)
  
  (vlax-for blk c_blks
    (vlax-for obj blk
      (if (= (cdr (assoc 0 (entget (vlax-vla-object->ename obj)))) "AEC_MASS_ELEM")
        (progn
          (princ "\nFound")
          (command-s "_spaceconvertmasselement" (vlax-vla-object->ename obj) "" "_Y")
        );end_progn  
      );end_if    
    );end_for
  );end_for
  (if (and c_doc (= 8 (logand 8 (getvar 'UNDOCTL)))) (vla-endundomark c_doc))
);end_defun

I am not one of the robots you're looking for

0 Likes
Message 7 of 11

imajar
Advisor
Advisor

If I wblock out a single object - it works.  

 

If I execute it on a series of objects nested inside of blocks, it does not work, this is what happens:

 

Found_spaceconvertmasselement
Select mass elements (and/or groups) to convert: <Bad Entity name: E9B1CDB0>

But if I do entget on the name, it says this:

entget 6B16A1F0
#<SUBR @000001b56eb8ff28 ENTGET>
nil

I have no idea how to debug this . . .

 


Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
0 Likes
Message 8 of 11

dlanorh
Advisor
Advisor

@imajar wrote:

If I wblock out a single object - it works.  

 

If I execute it on a series of objects nested inside of blocks, it does not work, this is what happens:

 

Found_spaceconvertmasselement
Select mass elements (and/or groups) to convert: <Bad Entity name: E9B1CDB0>

But if I do entget on the name, it says this:

entget 6B16A1F0
#<SUBR @000001b56eb8ff28 ENTGET>
nil

I have no idea how to debug this . . .

 


The reasoning behind testing in a drawing that contains a single block  was to test whether we could alter an entity that only lives inside a block. If we can't do this, then using the bedit route is all we have; warts and all.

 

Am I right in assuming that your original lisp converts an entity from AEC mass element to space and then space to solid?

 

If so then we should try the converting from space to solid.

Again try this on a single block drawing

 

(defun c:Test (/ *error* c_doc c_blks )
	
  (defun *error* ( msg )
		(if (and c_doc (= 8 (logand 8 (getvar 'UNDOCTL)))) (vla-endundomark c_doc))
    (if (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")) (princ (strcat "\nOops an Error occurred : " msg)))
		(princ)
  );end_defun *error*

	(setq c_doc (vla-get-activedocument (vlax-get-acad-object))
        c_blks (vla-get-blocks c_doc)
  )

	(if (and c_doc (= 8 (logand 8 (getvar 'UNDOCTL)))) (vla-endundomark c_doc))
  (vla-startundomark c_doc)
  
  (vlax-for blk c_blks
    (vlax-for obj blk
      (cond ( (= (cdr (assoc 0 (entget (vlax-vla-object->ename obj)))) "AEC_MASS_ELEM")
              (princ "\nFound AEC ME\n")
              (command-s "_spaceconvertmasselement" (vlax-vla-object->ename obj) "" "_Y") 
              (cond ( (= (cdr (assoc 0 (entget (entlast)))) "AEC_SPACE")
                      (princ "\nFound AEC SPACE\n")
                      (command-s "_convertto3dsolids" (entlast) "" "_Y")
                    )
              );end_cond
            )
      );end_cond    
    );end_for
  );end_for
  (if (and c_doc (= 8 (logand 8 (getvar 'UNDOCTL)))) (vla-endundomark c_doc))
);end_defun

I'm groping in the dark here, but the upper part of the error "<Bad Entity...> suggest that the command is either not finishing completely, repeating when it shouldn't, is expecting something other than a single entity or it isn't possible to do this. The lower part of the error, at a guess, means you tried to access an entity but that the lisp is stuck and needs reseting. If this fails you may need to re-run it in the VLIDE. Have you used the VLIDE before? This could be the blind leading the blind. Robot Frustrated 

I am not one of the robots you're looking for

Message 9 of 11

imajar
Advisor
Advisor

You are correct that my original lisp converts from mass to space, then from space to solid.  It's wierd, but it works for me.

 

I am glad you explained the purpose - the file I tested before was on geometry not embedded in a block (missed that part of the instruction).  If I embed that geometry into a single block - so that my model space is composed of one single block, then this lisp does not work, with the same output as before saying bad entity on the spaceconvertmasselement command.

 

 

I suspect that autocad is not letting it inside of the block like you thinking.

 

Could I extract the block definition from the drawing - edit it - and reinsert that block definition into the drawing?  (without opening the drawing)?


Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
0 Likes
Message 10 of 11

dlanorh
Advisor
Advisor

@imajar wrote:

You are correct that my original lisp converts from mass to space, then from space to solid.  It's wierd, but it works for me.

 

I am glad you explained the purpose - the file I tested before was on geometry not embedded in a block (missed that part of the instruction).  If I embed that geometry into a single block - so that my model space is composed of one single block, then this lisp does not work, with the same output as before saying bad entity on the spaceconvertmasselement command.

 

 

I suspect that autocad is not letting it inside of the block like you thinking.

 

Could I extract the block definition from the drawing - edit it - and reinsert that block definition into the drawing?  (without opening the drawing)?


I was sceptical about it being possible to alter the blocks outside of the block editor, using non activex commands; but i'd never tried. It is possible to open drawings "in the background" using OBDX and altering the drawings, but again this is using both visual lisp and autolisp methods and properties, which won't work, and I'm pretty certain that you can't use the block editor in OBDX.

 

I could be wrong, but I think part of the problem with the lag is the propagation of the changes throughout the model. Once a block is updated via the block editor, Autocad will update this block throught the drawing before moving to edit the next block in the block editor. If multiple blocks contain other nested blocks then they are updated multiple times. The degree of block nesting and the total number of blocks in the drawing with nested blocks would have the lag effect you are seeing. In essence you need to apply a wash/rinse cycle starting with blocks that have no nested blocks or a drawing that only contains the basic blocks that are the constituent parts of complex blocks. Once these have been processed, inserting the original drawing with the complex blocks (as a block) should update all the complex blocks within allowing it to be written out as a block then exploded.

 

Lee Mac has an "Extract Nested Blocks" routine HERE  but I don't know if this can be automated.

 

 

I am not one of the robots you're looking for

Message 11 of 11

imajar
Advisor
Advisor

Hmm. . . your description of propagating updates throughout the model make sense as to why an update would be necessary after bedit.  Thank you for your help.


Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
0 Likes