Author Topic: Breaking unhandy entities  (Read 7441 times)

0 Members and 1 Guest are viewing this topic.

Lee Mac

  • Seagull
  • Posts: 12926
  • London, England
Re: Breaking unhandy entities
« Reply #15 on: July 11, 2013, 10:36:15 AM »
Right now I do it like this:
Code - Auto/Visual Lisp: [Select]
  1. (while (setq LstBlk (tblnext "BLOCK" (not LstBlk)))
  2.         (if (/=(substr(cdr(assoc 2 LstBlk))1 1)"*")
  3.                 (foreach a '("ACDB3DSOLID" "ACDBBODY" "ACDBREGION" "ACDBPOLYLINE" "ACDBPOLYGONMESH" "ACDB3DPOLYLINE")
  4.                         (mapcar
  5.                                 '(lambda (object)
  6.                                         (if (= (strcase (vla-get-objectname object)) a)(progn
  7.                                                 (vla-explode object)
  8.                                                 (vla-erase object)
  9.                                         ))
  10.                                 )
  11.                                 (vla-collection->list
  12.                                         (vla-item
  13.                                                 (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
  14.                                                 (cdr(assoc 2 LstBlk))
  15.                                         )
  16.                                 )
  17.                         )
  18.                 )      
  19.         )
  20. )
  21. ;;; this is supposed to work in the model space
  22. (while (setq LstBlk (tblnext "BLOCK" (not LstBlk)))
  23.         (if (= (cdr(assoc 2 LstBlk)) "*Model_Space") ;; this should only let you into the model space block
  24.                 ;;; buit I never get there
  25.                 (foreach a '("ACDB3DSOLID" "ACDBBODY" "ACDBREGION" "ACDBPOLYLINE" "ACDBPOLYGONMESH" "ACDB3DPOLYLINE")
  26.                         (mapcar
  27.                                 '(lambda (object)
  28.                                         (if (= (strcase (vla-get-objectname object)) a)(progn
  29.                                                 (vla-explode object)
  30.                                                 (vla-erase object)
  31.                                         ))
  32.                                 )
  33.                                 (vla-collection->list
  34.                                         (vla-item
  35.                                                 (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
  36.                                                 "*Model_Space"
  37.                                         )
  38.                                 )
  39.                         )
  40.                 )
  41.         )
  42. )
  43.  

Sorry, but I cannot stress strongly enough how terribly inefficient your code is.

Here is what your code is currently doing (pseudo-code):
Code - Text: [Select]
  1. For every block in the block table
  2.     If the block is not an anonymous block
  3.         For every objectname in the list
  4.             Convert the collection of block components into a list
  5.             Iterate over the list of block components
  6.             If the objectname of the component matches the item currently processed
  7.                 Explode the object
  8.                 Erase the original object
  9.             end if
  10.         end foreach
  11.     end if
  12. end foreach
  13. For every block in the block table (again)
  14.     If the block is the Modelspace block
  15.         For every objectname in the list
  16.             Convert the collection of all objects in Modelspace into a list
  17.             Iterate over the list of every object in Modelspace
  18.             If the objectname of the component matches the item currently processed
  19.                 Explode the object
  20.                 Erase the original object
  21.             end if
  22.         end foreach
  23.     end if
  24. end foreach

Think about what the code is doing for each block processed:
Code - Text: [Select]
  1. Process first block in block table
  2.     Block is not anonymous
  3.         Process "ACDB3DSOLID"
  4.             Convert the collection of every object in the block into a list
  5.             Iterate over this list
  6.             If objectname = "ACDB3DSOLID"
  7.                 Explode object
  8.                 Erase object
  9.             end if
  10.         Process "ACDBBODY"
  11.             Convert the collection of every object in the block into a list (again)
  12.             Iterate over this list (the same list)
  13.             If objectname = "ACDBBODY"
  14.                 Explode object
  15.                 Erase object
  16.             end if
  17.         Process "ACDBREGION"
  18.             Convert the collection of every object in the block into a list (and again)
  19.             Iterate over this list (the same list again)
  20.             If objectname = "ACDBREGION"
  21.                 Explode object
  22.                 Erase object
  23.             end if
  24.         ...
  25.     ...
  26. Process second block in block table...
  27. ...
  28.  

Notice that, for every block, the collection of objects in each block definition (which could potentially be hundreds, if not thousands of objects) is being converted to a list and iterated over... 6 times!

And that's before you do the same thing and iterate over EVERY object in Modelspace another 6 times!  :-o

Furthermore, you are needlessly retrieving the AutoCAD Application object & Block Collection object (an inefficient & slow process in itself) 6 times for EVERY block processed, and another 6 times for the Modelspace block!

Note also that your code will not account for nested blocks (or other explodable nested objects) - if a block reference or block definition were to contain a nested block or explodable object (such as a polyline), or even perhaps multiple blocks nested within each other several levels deep, when the parent block is exploded, the nested blocks & objects will now be present within the containing object (Modelspace / Block Definition) and will remain in the drawing since the code will have already iterated over the block definition.

Consider the following alternative:
Code - Auto/Visual Lisp: [Select]
  1. (defun explodeall ( )
  2.         (vlax-map-collection blk 'explode)
  3.     )
  4.     (princ)
  5. )
  6.  
  7. (defun explode ( obj / lst )
  8.     (if (vlax-method-applicable-p obj 'explode)
  9.         (if (not (vl-catch-all-error-p (setq lst (vl-catch-all-apply 'vlax-invoke (list obj 'explode)))))
  10.             (progn
  11.                 (vla-delete obj)
  12.                 (foreach obj lst (explode obj))
  13.             )
  14.         )
  15.     )
  16. )

The above will iterate over all objects in all block definitions (including all drawing layouts), and will attempt to explode every object. To account for the possibility of nested blocks & other nested explodable objects, the function will recursively iterate over the set of exploded objects, repeating the process to ensure every explodable object is exploded.
« Last Edit: July 11, 2013, 10:47:45 AM by Lee Mac »

roy_043

  • Water Moccasin
  • Posts: 1895
  • BricsCAD 18
Re: Breaking unhandy entities
« Reply #16 on: July 11, 2013, 10:57:02 AM »
I never get into the model space case.
Your problem is caused by the second use of:
Code: [Select]
(tblnext "BLOCK" (not LstBlk))at that point (not LstBlk) returns nil instead of T.

I suggest using this approach:
  • Store the ename of the last modelspace entity.
  • Use vla-copyobjects to make a temporary copy of all 'complex' entities from the block definition to modelspace.
  • Delete these entities from the block definition.
  • Use the explode command on the temporary entities in modelspace until all complex entities are gone.
  • Using the stored ename from step 1, select all new entities in modelspace, use vla-copyobjects to copy them to the block definition and then delete them from modelspace.
Here is a function using this approach:
Code - Auto/Visual Lisp: [Select]
  1. ; Argument:     Object in block definition as vla-object or ename.
  2. ;               The function does not check if the object can be exploded.
  3. ; Return value: List of new objects in the block definition as list of vla-objects.
  4. (defun ExplodeObjectInBlock (object / N_ExplodeCommand N_ExplodeMethod)
  5.  
  6.   (defun N_ExplodeCommand (object / docObject lastEname newEnameList newObjectList ownerObject tempObject)
  7.     (setq ownerObject (vla-objectidtoobject docObject (vla-get-ownerid object)))
  8.     ; Store the ename of the last object in modelspace:
  9.     (setq lastEname (entlast))
  10.     ; Required for 'SEQEND situations':
  11.     (while (entnext lastEname)
  12.       (setq lastEname (entnext lastEname))
  13.     )
  14.     ; Make a temporary copy of the object from the block definition to modelspace:
  15.     (setq tempObject
  16.       (car
  17.         (vlax-invoke
  18.           docObject
  19.           'copyobjects
  20.           (list object)
  21.           (vla-get-modelspace docObject)
  22.         )
  23.       )
  24.     )
  25.     ; Delete the object from the block definition:
  26.     (vla-delete object)
  27.     ; Use the explode command:
  28.     (setvar 'cmdecho 0)
  29.     (command "_.explode" (vlax-vla-object->ename tempObject))
  30.     (setvar 'cmdecho 1)
  31.     ; Get the enames of all new objects in modelspace:
  32.     (while (setq lastEname (entnext lastEname))
  33.       (setq newEnameList (cons lastEname newEnameList))
  34.     )
  35.     ; Copy the new objects to the block definition:
  36.     (if newEnameList
  37.       (setq newObjectList
  38.         (vlax-invoke
  39.           docObject
  40.           'copyobjects
  41.           (mapcar 'vlax-ename->vla-object newEnameList)
  42.           ownerObject
  43.         )
  44.       )
  45.     )
  46.     ; Delete the new entities from modelspace:
  47.     (mapcar 'entdel newEnameList)
  48.     ; Return the new objects in the block definition:
  49.     newObjectList
  50.   )
  51.  
  52.   (defun N_ExplodeMethod (object / newObjectList)
  53.     (setq newObjectList (vlax-invoke object 'explode))
  54.     (vla-delete object)
  55.     ; Return the new objects in the block definition:
  56.     newObjectList
  57.   )
  58.  
  59.   (if (= (type object) 'ename) (setq object (vlax-ename->vla-object object)))
  60.   (if (vlax-method-applicable-p object 'explode)
  61.     (N_ExplodeMethod object)
  62.     (N_ExplodeCommand object)
  63.   )
  64. )

@ Lee: you make a good point. But the OP does not want to explode ALL objects.

Lee Mac

  • Seagull
  • Posts: 12926
  • London, England
Re: Breaking unhandy entities
« Reply #17 on: July 11, 2013, 11:04:49 AM »
@ Lee: you make a good point. But the OP does not want to explode ALL objects.

I see - a conditional could quite easily be added to control which objects are exploded  :-)
BTW, good idea to enable use of the explode command for block components roy.

roy_043

  • Water Moccasin
  • Posts: 1895
  • BricsCAD 18
Re: Breaking unhandy entities
« Reply #18 on: July 11, 2013, 01:10:01 PM »
Your code:           Is it a valid object name?  Does it support the explode method?
ACDB2DPOLYLINE       No

I have to correct this:
Your code:           Is it a valid object name?  Does it support the explode method?
ACDB2DPOLYLINE       Yes                         Yes


Here is an improved version of the function in my previous post:
Code - Auto/Visual Lisp: [Select]
  1. ;;; Argument:     Object in a block definition (can be modelspace) as vla-object or ename.
  2. ;;;               The function does not check if the object can be exploded.
  3. ;;; Return value: List of new objects in the block definition as list of vla-objects.
  4. (defun ExplodeObject  (object
  5.                         / N_ExplodeCommandModelSpace
  6.                           N_ExplodeCommandOther
  7.                           N_ExplodeMethod
  8.                           docObject modelSpaceObject ownerObject
  9.                       )
  10.  
  11.   (defun N_ExplodeCommandModelSpace (object / lastEname newEnameList)
  12.     ; Store the ename of the last object in modelspace:
  13.     (setq lastEname (entlast))
  14.     ; Required for 'SEQEND situations':
  15.     (while (entnext lastEname)
  16.       (setq lastEname (entnext lastEname))
  17.     )
  18.     ; Use the explode command:
  19.     (setvar 'cmdecho 0)
  20.     (command "_.explode" (vlax-vla-object->ename object))
  21.     (setvar 'cmdecho 1)
  22.     ; Get the enames of all new objects in modelspace:
  23.     (while (setq lastEname (entnext lastEname))
  24.       (setq newEnameList (cons lastEname newEnameList))
  25.     )
  26.     (mapcar 'vlax-ename->vla-object newEnameList)
  27.   )
  28.  
  29.   ;; The function uses the following variables from the main function:
  30.   ;;   docObject
  31.   ;;   modelSpaceObject
  32.   ;;   ownerObject
  33.   (defun N_ExplodeCommandOther (object / lastEname newEnameList newObjectList tempObject)
  34.     ; Store the ename of the last object in modelspace:
  35.     (setq lastEname (entlast))
  36.     ; Required for 'SEQEND situations':
  37.     (while (entnext lastEname)
  38.       (setq lastEname (entnext lastEname))
  39.     )
  40.     ; Make a temporary copy of the object from the block definition in modelspace:
  41.     (setq tempObject
  42.       (car
  43.         (vlax-invoke
  44.           docObject
  45.           'copyobjects
  46.           (list object)
  47.           modelSpaceObject
  48.         )
  49.       )
  50.     )
  51.     ; Delete the object from the block definition:
  52.     (vla-delete object)
  53.     ; Use the explode command:
  54.     (setvar 'cmdecho 0)
  55.     (command "_.explode" (vlax-vla-object->ename tempObject))
  56.     (setvar 'cmdecho 1)
  57.     ; Get the enames of all new objects in modelspace:
  58.     (while (setq lastEname (entnext lastEname))
  59.       (setq newEnameList (cons lastEname newEnameList))
  60.     )
  61.     ; Copy the new objects to the block definition:
  62.     (if newEnameList
  63.       (setq newObjectList
  64.         (vlax-invoke
  65.           docObject
  66.           'copyobjects
  67.           (mapcar 'vlax-ename->vla-object newEnameList)
  68.           ownerObject
  69.         )
  70.       )
  71.     )
  72.     ; Delete the new entities from modelspace:
  73.     (mapcar 'entdel newEnameList)
  74.     ; Return the new objects in the block definition:
  75.     newObjectList
  76.   )
  77.  
  78.   (defun N_ExplodeMethod (object / newObjectList)
  79.     (setq newObjectList (vlax-invoke object 'explode))
  80.     (vla-delete object)
  81.     ; Return the new objects in the block definition:
  82.     newObjectList
  83.   )
  84.  
  85.   (if (= (type object) 'ename) (setq object (vlax-ename->vla-object object)))
  86.   (if (vlax-method-applicable-p object 'explode)
  87.     (N_ExplodeMethod object)
  88.     (progn
  89.       ; Make sure that modelspace is current:
  90.       (setvar 'tilemode 1)
  91.       (setq modelSpaceObject (vla-get-modelspace docObject))
  92.       (setq ownerObject (vla-objectidtoobject docObject (vla-get-ownerid object)))
  93.       (if (= ownerObject modelSpaceObject)
  94.         (N_ExplodeCommandModelSpace object)
  95.         (N_ExplodeCommandOther object)
  96.       )
  97.     )
  98.   )
  99. )