Author Topic: Delete hatch from selected and nested blocks  (Read 373 times)

0 Members and 1 Guest are viewing this topic.

kasmo

  • Mosquito
  • Posts: 4
Delete hatch from selected and nested blocks
« on: January 24, 2023, 08:54:46 AM »
Hey guys,
I'm trying to write a lisp that would delete all hatches from a selection set of blocks (sometimes nested).
Doing that with all blocks in the drawing would be pretty straight forward, but I have no idea how I can limit that to a selection set.
Can someone point me in the right direction?

dexus

  • Newt
  • Posts: 101
Re: Delete hatch from selected and nested blocks
« Reply #1 on: January 24, 2023, 11:16:39 AM »
The code below will remove all hatches (and wipeouts) from all block defenitions.
If you want it to only work on the selection you can try to add a blockname filter between row 4 and 5.
Code - Auto/Visual Lisp: [Select]
  1.   (and
  2.     (equal (vla-get-IsXref Blk) :vlax-false)
  3.     (equal (vla-get-IsLayout Blk) :vlax-false)
  4.     (vlax-for Obj Blk
  5.       (if
  6.         (or
  7.           (= (vla-get-ObjectName Obj) "AcDbHatch")
  8.           (= (vla-get-ObjectName Obj) "AcDbWipeout")
  9.         )
  10.         (vla-Delete Obj)
  11.       )
  12.     )
  13.   )
  14. )
  15.  

kasmo

  • Mosquito
  • Posts: 4
Re: Delete hatch from selected and nested blocks
« Reply #2 on: January 25, 2023, 03:28:02 AM »
Yes, I got that far, but how would I filter the blocks to only include the ones from a selection set, nested blocks included (if that is even possible)?

dexus

  • Newt
  • Posts: 101
Re: Delete hatch from selected and nested blocks
« Reply #3 on: January 25, 2023, 06:13:41 AM »
Yes it's possible to find all block names in a selection including nested blocks.
Try this by calling (nestedBlocks (ssget))
Code - Auto/Visual Lisp: [Select]
  1. (defun nestedBlocks (ss / blockNames getBlockNames LM:Unique)
  2.  
  3.   (defun getBlockNames (ent / blockNames) ; by dexus
  4.     (if
  5.       (and
  6.         (car (setq blockNames (list (cdr (assoc 2 (entget ent)))))) ; BlockName
  7.         (setq ent (tblobjname "BLOCK" (car blockNames))) ; If blockName is found
  8.       )
  9.       (while (and ent (setq ent (entnext ent))) ; loop though items inside block
  10.         (if (= (cdr (assoc 0 (entget ent))) "INSERT") ; If a nested block is found
  11.           (setq blockNames (append (getBlockNames ent) blockNames)) ; Add blocknames recursively
  12.         )
  13.       )
  14.     )
  15.     blockNames
  16.   )
  17.  
  18.   (defun LM:Unique ( l ) ; Remove duplicates by Lee Mac
  19.     (if l
  20.       (cons (car l)
  21.         (LM:Unique (vl-remove (car l) (cdr l)))
  22.       )
  23.     )
  24.   )
  25.  
  26.   (if ss
  27.     (foreach ent (ssnamex ss)
  28.       (if (= 'ename (type (cadr ent)))
  29.         (setq blockNames (append (getBlockNames (cadr ent)) blockNames)) ; Add blockNames for all items in selection
  30.       )
  31.     )
  32.   )
  33.   (vl-remove nil (LM:Unique blockNames))
  34. )

kasmo

  • Mosquito
  • Posts: 4
Re: Delete hatch from selected and nested blocks
« Reply #4 on: January 26, 2023, 02:14:40 AM »
Works like a charm!

Code - Auto/Visual Lisp: [Select]
  1. (defun c:dhb ( / ss q )
  2.         (if (setq ss (ssget '((0 . "INSERT"))))
  3.                 (progn
  4.                         (setq q (nestedBlocks ss))
  5.                         (vlax-for Blk (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-Acad-Object)))
  6.                           (and
  7.                                 (equal (vla-get-IsXref Blk) :vlax-false)
  8.                                 (equal (vla-get-IsLayout Blk) :vlax-false)
  9.                                 (vlax-for Obj Blk
  10.                                   (if
  11.                                         (and
  12.                                           (= (vla-get-ObjectName Obj) "AcDbHatch")
  13.                                           (member (cdr (assoc 2 (entget (vlax-vla-object->ename blk)))) q)
  14.                                         )
  15.                                         (vla-Delete Obj)
  16.                                   )
  17.                                 )
  18.                           )
  19.                         )
  20.                 )
  21.         )
  22. )

Thank you for your help dexus, also for the comments. Very helpful for me to understand how to deal with nested blocks and the objects inside them.

Lee Mac

  • Seagull
  • Posts: 12752
  • London, England
Re: Delete hatch from selected and nested blocks
« Reply #5 on: January 26, 2023, 12:58:41 PM »
Yes it's possible to find all block names in a selection including nested blocks.
Try this by calling (nestedBlocks (ssget))
Code - Auto/Visual Lisp: [Select]
  1. (defun nestedBlocks (ss / blockNames getBlockNames LM:Unique)
  2.  
  3.   (defun getBlockNames (ent / blockNames) ; by dexus
  4.     (if
  5.       (and
  6.         (car (setq blockNames (list (cdr (assoc 2 (entget ent)))))) ; BlockName
  7.         (setq ent (tblobjname "BLOCK" (car blockNames))) ; If blockName is found
  8.       )
  9.       (while (and ent (setq ent (entnext ent))) ; loop though items inside block
  10.         (if (= (cdr (assoc 0 (entget ent))) "INSERT") ; If a nested block is found
  11.           (setq blockNames (append (getBlockNames ent) blockNames)) ; Add blocknames recursively
  12.         )
  13.       )
  14.     )
  15.     blockNames
  16.   )
  17.  
  18.   (defun LM:Unique ( l ) ; Remove duplicates by Lee Mac
  19.     (if l
  20.       (cons (car l)
  21.         (LM:Unique (vl-remove (car l) (cdr l)))
  22.       )
  23.     )
  24.   )
  25.  
  26.   (if ss
  27.     (foreach ent (ssnamex ss)
  28.       (if (= 'ename (type (cadr ent)))
  29.         (setq blockNames (append (getBlockNames (cadr ent)) blockNames)) ; Add blockNames for all items in selection
  30.       )
  31.     )
  32.   )
  33.   (vl-remove nil (LM:Unique blockNames))
  34. )

It is worth noting that this is a rather inefficient way of obtaining the referenced block names within the selection, as, for each selected block reference, the function is iterating over the components and nested components, regardless of whether the block has already been encountered in the selection. The duplicate block names are then being removed from the resulting list.

The following may present a more efficient alternative:
Code - Auto/Visual Lisp: [Select]
  1. (defun c:dhb ( / blc doc idx lst nme sel )
  2.           blc (vla-get-blocks doc)
  3.     )
  4.     (if (setq sel (ssget '((0 . "INSERT"))))
  5.         (repeat (setq idx (sslength sel))
  6.             (setq idx (1- idx)
  7.                   nme (cdr (assoc 2 (entget (ssname sel idx))))
  8.             )
  9.             (if (not (member nme lst))
  10.                 (setq lst (processblock blc nme lst))
  11.             )
  12.         )
  13.     )
  14.     (vla-regen doc acactiveviewport)
  15.     (princ)
  16. )
  17.  
  18. (defun processblock ( blc blk lst / nme )
  19.     (vlax-for obj (vla-item blc blk)
  20.         (cond
  21.             (   (= "AcDbHatch" (vla-get-objectname obj))
  22.                 (vla-delete obj)
  23.             )
  24.             (   (= "AcDbBlockReference" (vla-get-objectname obj))
  25.                 (if (not (member (setq nme (vla-get-name obj)) lst))
  26.                     (setq lst (processblock blc nme (cons nme lst)))
  27.                 )
  28.             )
  29.         )
  30.     )
  31.     lst
  32. )
  33.  

Works like a charm!

Code - Auto/Visual Lisp: [Select]
  1. (defun c:dhb ( / ss q )
  2.         (if (setq ss (ssget '((0 . "INSERT"))))
  3.                 (progn
  4.                         (setq q (nestedBlocks ss))
  5.                         (vlax-for Blk (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-Acad-Object)))
  6.                           (and
  7.                                 (equal (vla-get-IsXref Blk) :vlax-false)
  8.                                 (equal (vla-get-IsLayout Blk) :vlax-false)
  9.                                 (vlax-for Obj Blk
  10.                                   (if
  11.                                         (and
  12.                                           (= (vla-get-ObjectName Obj) "AcDbHatch")
  13.                                           (member (cdr (assoc 2 (entget (vlax-vla-object->ename blk)))) q)
  14.                                         )
  15.                                         (vla-Delete Obj)
  16.                                   )
  17.                                 )
  18.                           )
  19.                         )
  20.                 )
  21.         )
  22. )

Thank you for your help dexus, also for the comments. Very helpful for me to understand how to deal with nested blocks and the objects inside them.

I'm surprised that your code performs as expected given these lines:
Code - Auto/Visual Lisp: [Select]
  1.                                         (and
  2.                                           (= (vla-get-ObjectName Obj) "AcDbHatch")
  3.                                           (member (cdr (assoc 2 (entget (vlax-vla-object->ename blk)))) q)
  4.                                         )

This is saying, "If the object is a hatch AND it has a block name which is a member of the list..."

(Edit - see subsequent posts)
« Last Edit: January 27, 2023, 09:09:51 AM by Lee Mac »

kasmo

  • Mosquito
  • Posts: 4
Re: Delete hatch from selected and nested blocks
« Reply #6 on: January 27, 2023, 05:23:32 AM »
Quote
The following may present a more efficient alternative:
Code - Auto/Visual Lisp: [Select]
  1.     (defun c:dhb ( / blc doc idx lst nme sel )
  2.               blc (vla-get-blocks doc)
  3.         )
  4.         (if (setq sel (ssget '((0 . "INSERT"))))
  5.             (repeat (setq idx (sslength sel))
  6.                 (setq idx (1- idx)
  7.                       nme (cdr (assoc 2 (entget (ssname sel idx))))
  8.                 )
  9.                 (if (not (member nme lst))
  10.                     (setq lst (processblock blc nme lst))
  11.                 )
  12.             )
  13.         )
  14.         (vla-regen doc acactiveviewport)
  15.         (princ)
  16.     )
  17.      
  18.     (defun processblock ( blc blk lst / nme )
  19.         (vlax-for obj (vla-item blc blk)
  20.             (cond
  21.                 (   (= "AcDbHatch" (vla-get-objectname obj))
  22.                     (vla-delete obj)
  23.                 )
  24.                 (   (= "AcDbBlockReference" (vla-get-objectname obj))
  25.                     (if (not (member (setq nme (vla-get-name obj)) lst))
  26.                         (setq lst (processblock blc nme (cons nme lst)))
  27.                     )
  28.                 )
  29.             )
  30.         )
  31.         lst
  32.     )
  33.      
  34.     (vl-load-com) (princ)
Nice, seems to be a little bit faster on big drawings. Great code to learn from, thank you.

Quote
I'm surprised that your code performs as expected given these lines:
Code - Auto/Visual Lisp: [Select]
  1.                                             (and
  2.                                               (= (vla-get-ObjectName Obj) "AcDbHatch")
  3.                                               (member (cdr (assoc 2 (entget (vlax-vla-object->ename blk)))) q)
  4.                                             )
Why wouldn't it? It checks if that hatch I want to delete is part of the selected blocks. It's just terribly inefficient to do so for every object in every block.

dexus

  • Newt
  • Posts: 101
Re: Delete hatch from selected and nested blocks
« Reply #7 on: January 27, 2023, 07:25:43 AM »
[...]
It is worth noting that this is a rather inefficient way of obtaining the referenced block names within the selection, as, for each selected block reference, the function is iterating over the components and nested components, regardless of whether the block has already been encountered in the selection. The duplicate block names are then being removed from the resulting list.

The following may present a more efficient alternative:
[...]
I didn't do any optimization so it was indeed very inefficient with repeated blocks. Thanks for the improvements.
Clever to remove the hatches while you are processing the block names, saves you from having to loop though all of them twice.

Lee Mac

  • Seagull
  • Posts: 12752
  • London, England
Re: Delete hatch from selected and nested blocks
« Reply #8 on: January 27, 2023, 09:08:59 AM »
Quote
I'm surprised that your code performs as expected given these lines:
Code - Auto/Visual Lisp: [Select]
  1.                                             (and
  2.                                               (= (vla-get-ObjectName Obj) "AcDbHatch")
  3.                                               (member (cdr (assoc 2 (entget (vlax-vla-object->ename blk)))) q)
  4.                                             )
Why wouldn't it? It checks if that hatch I want to delete is part of the selected blocks. It's just terribly inefficient to do so for every object in every block.

Apologies - I see that you are referencing the variable 'blk' in the second line - I initially read the code as both expressions referencing the 'obj' variable.