Author Topic: Iterate all blocks and nested blocks  (Read 3490 times)

0 Members and 1 Guest are viewing this topic.

Woabow

  • Newt
  • Posts: 56
Iterate all blocks and nested blocks
« on: January 23, 2014, 01:17:32 PM »
Sometimes I would like to read the scale of all blocks by iterate through the drawing and read all EffectiveScaleFactor and ScaleFactor properties. But I'm confused, when running this code I only get nested blocks, not blocks that are just inserted in the drawing. Can somebody point me in the right direction?

Code: [Select]

(defun c:dump ( / blocks )

    (defun sub ( blk / subblk  )
    (vlax-for ob blk
     (if  (and
             (= (vla-get-ObjectName ob) "AcDbBlockReference")
             (setq efName (vlax-get ob
                       (if (vlax-property-available-p ob 'EffectiveName)
                   'EffectiveName
                   'Name
                   )
                  )
          )
             (setq subblk (vla-item blocks efName))
             (= (vla-get-IsXref subblk) :vlax-false)
           )
             (progn
               (vlax-dump-object ob)
               (sub subblk)
             )
     nil
     )
    )
    )

(setq blocks (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object))))

(vlax-for b blocks
    (if (and (= (vla-get-IsXref b) :vlax-false)
             (= (vla-get-IsLayout b) :vlax-false)
             (/= (substr (vla-get-Name b) 1 1) "*")
        )
      (progn
       (vlax-dump-object b)
       (sub b)
      )
    )
)

(princ)
)


irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Iterate all blocks and nested blocks
« Reply #1 on: January 23, 2014, 02:41:19 PM »
Iterate through the Blocks collection of the ActiveDocument. Note this collection includes the ModelSpace and all PaperSpace blocks as well - so you'd be going through the actual placed blocks as well as any nested stuff. This way also you need not worry about going into a nested block as you would anyway iterate over its definition (it's also part of the Blocks collection).

Note though that it would iterate over each version of a DB, since a change in a property would make an anonymous block with the same EffectiveName as the original. Thus you might want to check on this.

Thereafter you'd need to check for duplicates as you're looking for blockreferences (INSERT). So I'd suggest keeping a running list of found EffectiveNames and only dumping their info if not already in that list.
« Last Edit: January 23, 2014, 02:46:04 PM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Woabow

  • Newt
  • Posts: 56
Re: Iterate all blocks and nested blocks
« Reply #2 on: January 24, 2014, 12:42:25 AM »
Not that I really grasp what is going on, but I started from scratch and ended up with this:

Code: [Select]
(defun scan ( blk / )
 (vlax-for i blk
    (checkscale i)
    (scan i)
 )
)

(vlax-for itm (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object)))
   (scan itm)
)

Where
Code: [Select]
checkscaletests the scale and modifies it when necessary.

I have to admit that I do not understand how the database works, why is it that I need to go into (with vlax-for in scan) the single blocks collected with vla-get-blocks to see the inserts?


irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Iterate all blocks and nested blocks
« Reply #3 on: January 24, 2014, 01:14:43 AM »
Here's what I meant:
Code - Auto/Visual Lisp: [Select]
  1. (defun c:BlkENameScale  (/ foundNames)
  2.   (princ
  3.     "\nBlock Name\tXScale\tYScale\tZScale\n-----------------------------------------------------")
  4.     (vlax-for NestedObject  BlockDef
  5.       (if (and (eq (vla-get-ObjectName NestedObject) "AcDbBlockReference")
  6.                (not (vl-position (vla-get-EffectiveName NestedObject) foundNames)))
  7.         (progn (princ (strcat "\n" (vla-get-EffectiveName NestedObject) "\t"))
  8.                (princ (vla-get-XEffectiveScaleFactor NestedObject))
  9.                (princ "\t")
  10.                (princ (vla-get-YEffectiveScaleFactor NestedObject))
  11.                (princ "\t")
  12.                (princ (vla-get-ZEffectiveScaleFactor NestedObject))
  13.                (princ "\t")
  14.                (setq foundNames (cons (vla-get-EffectiveName NestedObject) foundNames))))))
  15.   (princ))
If you want to list all block inserts, even if they're duplicate names, then remove the statements checking if in the foundNames list and adding to the foundNames list.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Woabow

  • Newt
  • Posts: 56
Re: Iterate all blocks and nested blocks
« Reply #4 on: January 24, 2014, 02:49:39 AM »
So there is no need for recursion? I think I get it. Only one level deep needs is enough because the nested block, with nested blocks in it, will also be in the vla-get-blocks list.

Thanks.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Iterate all blocks and nested blocks
« Reply #5 on: January 24, 2014, 05:34:20 AM »
So there is no need for recursion? I think I get it. Only one level deep needs is enough because the nested block, with nested blocks in it, will also be in the vla-get-blocks list.

Thanks.
Yes exactly, there's only one list of block definitions in the drawing. Even if a block is placed within another's definition, it doesn't mean the nested block is removed from the drawing's block-list.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Iterate all blocks and nested blocks
« Reply #6 on: January 24, 2014, 05:38:20 AM »
Note though that the blocks collection contains ALL block definitions ever loaded / created in that drawing. Even those which are not inserted anywhere (i.e. the blocks which are purgeable).
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Woabow

  • Newt
  • Posts: 56
Re: Iterate all blocks and nested blocks
« Reply #7 on: January 24, 2014, 06:50:46 AM »
Got it.

But what did you mean by

Quote
Note though that it would iterate over each version of a DB, since a change in a property would make an anonymous block with the same EffectiveName as the original. Thus you might want to check on this.

?

I'm writing a program that will update all blocks with newer versions found on disk, with the "insert a=b" command. When the drawing is in meters and the blocks in mm, the blocks will automatically get a unitfactor of 0.001 (correct) and a scale of 1000 (don't know why). So the program changes all blocks with scale 1000 to 1 with

Code: [Select]
(vlax-put-property obj 'XEffectiveScaleFactor  1.0)
Do I create anonymous copies of the block definitions this way? Do I need to clean up something afterwards?


 

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Iterate all blocks and nested blocks
« Reply #8 on: January 24, 2014, 08:15:55 AM »
With a DB (Dynamic Block) if you change a property (Visibility / Length / Angle / etc) what acad does is it copies the block's definition to a new anonymous block (something with a *U##### name). Then it's seen as just another block inside the Blocks collection. EffectiveScale is not a Dynamic Block property, it's an annotative scale property - so this shouldn't go and make any anonymous blocks.

E.g. I made a block, called it TEST2, and placed a polar length & stretch inside in BEdit. Then I modified the stretch point and listed it:
Code: [Select]
Command: LS
LIST 1 found
                  BLOCK REFERENCE  Layer: "0"
                            Space: Model space
                   Handle = 25d
       Block Name: "test2"
   Anonymous Name: "*U5"
                at point, X=2291.9786  Y=1127.8018  Z=   0.0000
   X scale factor:    1.0000
   Y scale factor:    1.0000
   rotation angle:      0
   Z scale factor:    1.0000
         InsUnits: Millimeters
  Unit conversion:    1.0000
  Scale uniformly: No
  Allow exploding: Yes
        Distance1: 1142.0613
           Angle1:        27
Now I wrote this lisp into the command line:
Code: [Select]
Command: (vlax-for blk (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object))) (vlax-dump-object blk))
; IAcadModelSpace: A special Block object containing all model space entities...
;   Name = "*Model_Space"...
; IAcadPaperSpace: A special Block object containing all the entities in the active paper space layout...
;   Name = "*Paper_Space"...
; IAcadBlock: A block definition containing a name and a set of objects...
;   Name = "*Paper_Space0"...
; IAcadBlock: A block definition containing a name and a set of objects...
;   Name = "test2"[/color]...
; IAcadBlock: A block definition containing a name and a set of objects...
;   Name = "*U5"...
Note the last 2 blocks are actually the same definition, only the 2nd is the version of test2 where the parameter has changed. Also note that the Block Definition object does not have an EffectiveName property, only Block References do.

What you can do to ignore these blocks is to compare their names to stop if it finds one where the name starts with "*U". E.g. not even iterating over a block where its name starts with "*U", since it's nested objects were already iterated over when the original definition was looked at.

Note added line#5 in my previous code:
Code - Auto/Visual Lisp: [Select]
  1. (defun c:BlkENameScale  (/ foundNames)
  2.   (princ
  3.     "\nBlock Name\tXScale\tYScale\tZScale\n-----------------------------------------------------")
  4.     (if (not (wcmatch (strcase (vla-get-Name BlockDef)) "`*U*"))
  5.       (vlax-for NestedObject  BlockDef
  6.         (if (and (eq (vla-get-ObjectName NestedObject) "AcDbBlockReference")
  7.                  (not (vl-position (vla-get-EffectiveName NestedObject) foundNames)))
  8.           (progn (princ (strcat "\n" (vla-get-EffectiveName NestedObject) "\t"))
  9.                  (princ (vla-get-XEffectiveScaleFactor NestedObject))
  10.                  (princ "\t")
  11.                  (princ (vla-get-YEffectiveScaleFactor NestedObject))
  12.                  (princ "\t")
  13.                  (princ (vla-get-ZEffectiveScaleFactor NestedObject))
  14.                  (princ "\t")
  15.                  (setq foundNames (cons (vla-get-EffectiveName NestedObject) foundNames)))))))
  16.   (princ))
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Iterate all blocks and nested blocks
« Reply #9 on: January 24, 2014, 08:26:00 AM »
BTW, if your block is annotative then a new block definition is also made if you add more than 1 scale to it. But changing the scale factor is not adding a scale, it's as if you used the scale command on it. Using X/Y/ZScale changes the litteral scale, and X/Y/ZEffectiveScale changes with including the AnnoScaleFactor of the scale(s) you attached to that block reference.

Just as an afterthought: You know of course that as soon as you made your block annotative, it also sets the block definitions UniformScale property as ON. Thus your XScale=YScale=ZScale and XEffectiveScale=YEffectiveScale=ZEffectiveScale.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Woabow

  • Newt
  • Posts: 56
Re: Iterate all blocks and nested blocks
« Reply #10 on: January 24, 2014, 09:11:43 AM »
Thanks for the explanation, I think I understand how it works now.

I only wish Autodesk did not create a unitless cad system back then....

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Iterate all blocks and nested blocks
« Reply #11 on: January 24, 2014, 12:05:58 PM »
I only wish Autodesk did not create a unitless cad system back then....
Actually, since I'm now in Revit nearly full-time, I'm not too sure if it's a bad thing. Revit internally draws every thin in feet. Even if the project units are set to be mm (or even inches) it's still saved as feet. That means each and every dimension and each time you enter a distance it gets converted from feet to the project units and back. In some cases this causes in-accurate dimensions, due to the floating-point errors involved with saving fractional numbers, small though they may be they compound on top of each other.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.