Author Topic: Counting nested blocks on only highest and lowest level  (Read 4673 times)

0 Members and 2 Guests are viewing this topic.

Woabow

  • Newt
  • Posts: 56
Counting nested blocks on only highest and lowest level
« on: December 07, 2011, 04:23:56 AM »

To count blocks in a drawing I would like to know if the blocks are on lowest or toplevel. For example, I have a block called Dinnerset which contains 1 Dinnertable and 4 Chairs. When I create a list of this I just want to see 4 Chairs and 1 Table. Or, in some situations, I only would like to see the toplevel: 1x Dinnertable.

Now I have found code that creates a "treelist" with all the blocks on the level the appear in the drawing. The output for a block House with 2 Trees looks like this:

(("house" (("couch" ("pillow" "pillow")) ("dinnerset" ("chair" "chair" "chair" "chair" "dinnertable")) ("couch" ("pillow" "pillow")))) "Tree" "Tree")


I found a flatten routine to create a flat list

(defun flatten (l)
  ;;ElpanovEvgeniy, the Swamp
 (cond ((null l) nil)
       ((atom l)(list l))
       ((listp l)(append (flatten (car l)) (flatten (cdr l))))     
 ) ;_  cond
) ;_  defun

And next I count the occurrences with a routine of Lee:

(defun LM:ListOccurrences ( lst )
  ;; © Lee Mac 2010
  (if lst
    (cons
      (cons (car lst)
        (- (length lst) (length (vl-remove (car lst) lst)))
      )
      (LM:ListOccurrences (vl-remove (car lst) lst))
    )
  )
)

The output is:

(("house" . 1) ("couch" . 2) ("pillow" . 4) ("dinnerset" . 1) ("chair" . 4) ("dinnertable" . 1) ("Tree" . 2))

Which is correct, but what if I only want the lowest level? So just the Dinnertable, Chairs, Pillows and Trees? Or just the toplevel, House? I was trying to go through the drawing again to determinate which block is on lowest level, but there must be some way to use the original output (speed can be an issue too as my drawings can contain thousands of blocks).
 






pBe

  • Bull Frog
  • Posts: 402
Re: Counting nested blocks on only highest and lowest level
« Reply #1 on: December 08, 2011, 08:22:49 AM »
Code: [Select]
(defun foo (lvl lst)
(defun flatten (lst n)
  (cond
         ((null lst) nil)
         ((atom lst) (list lst))
         ((atom (car lst))
          (cons (list n (car lst) ) (Flatten (cdr lst) n))
          )
         ((append
            (Flatten (car lst) (setq n (1+ n)))
            (Flatten (cdr lst) (setq n (1+ n)))
            )
          )
         )
      ) 
  (cadr (assoc
    (apply lvl (mapcar 'car (setq lst (FLATTEN lst 0))))
    lst
    )
    )
  )

(foo 'max lst)
"pillow"
(foo 'min lst)
"house"

<< ???? >>

Woabow

  • Newt
  • Posts: 56
Re: Counting nested blocks on only highest and lowest level
« Reply #2 on: December 08, 2011, 01:04:37 PM »
Thanks for looking into this.

The 'max shows "pillow", but not only "pillow" is on lowest level, also "chair", "dinnertable"  and "tree".

It probably just looks for the deepest object (haven't understand your code yet (if ever  :|)). I would like to have all objects that do not have subobjects.







pBe

  • Bull Frog
  • Posts: 402
Re: Counting nested blocks on only highest and lowest level
« Reply #3 on: December 09, 2011, 12:52:05 AM »
Explain this to me:

I would like to have all objects that do not have subobjects.

Imagine you already have a working lisp code, what will the result look like for:
1. lowest level
2. highest level
3. Other lists






Woabow

  • Newt
  • Posts: 56
Re: Counting nested blocks on only highest and lowest level
« Reply #4 on: December 09, 2011, 05:02:00 AM »
When counting this list:

(("house" (("couch" ("pillow" "pillow")) ("dinnerset" ("chair" "chair" "chair" "chair" "dinnertable")) ("couch" ("pillow" "pillow")))) "Tree" "Tree")

I would like to have this result in the end

(("Tree" . 2) ("pillow" . 4) ("chair" . 4) ("dinnertable" . 1))

because these items do not have subitems.

If I just know which object to process, I can filter out the rest.

So the answer to your question is:

1. "Tree" "pillow" "chair" "dinnertable" (have no subitems)
2. "house"  "Tree" (are not member of another list)
3. not necessary

I hope this explains it.


« Last Edit: December 09, 2011, 05:20:57 AM by Woabow »

pBe

  • Bull Frog
  • Posts: 402
Re: Counting nested blocks on only highest and lowest level
« Reply #5 on: December 10, 2011, 07:44:54 AM »
When counting this list:

(("house" (("couch" ("pillow" "pillow")) ("dinnerset" ("chair" "chair" "chair" "chair" "dinnertable")) ("couch" ("pillow" "pillow")))) "Tree" "Tree")

I would like to have this result in the end

(("Tree" . 2) ("pillow" . 4) ("chair" . 4) ("dinnertable" . 1))

because these items do not have subitems.

If I just know which object to process, I can filter out the rest.

So the answer to your question is:

1. "Tree" "pillow" "chair" "dinnertable" (have no subitems)
2. "house"  "Tree" (are not member of another list)
3. not necessary

I hope this explains it.

I'm lost. how could tree be included in condition 1?
(("house"
              (("couch" ("pillow" "pillow"))
                ("dinnerset"
                  ("chair" "chair" "chair" "chair" "dinnertable")                  )
                ("couch" ("pillow" "pillow"))
                )
              )
             "Tree"
             "Tree"
             )

Hope you dont mind telling us how did you end up with that kind if a list in the first place? I would suggest modifying the source rather rebuilding the list...





Lee Mac

  • Seagull
  • Posts: 12905
  • London, England
Re: Counting nested blocks on only highest and lowest level
« Reply #6 on: December 11, 2011, 06:04:55 PM »
If I have correctly understood the request, try my 'Nested Block Counter' program  :-)

Woabow

  • Newt
  • Posts: 56
Re: Counting nested blocks on only highest and lowest level
« Reply #7 on: December 12, 2011, 05:10:07 AM »

I'm lost. how could tree be included in condition 1?
(("house"
              (("couch" ("pillow" "pillow"))
                ("dinnerset"
                  ("chair" "chair" "chair" "chair" "dinnertable")                  )
                ("couch" ("pillow" "pillow"))
                )
              )
             "Tree"
             "Tree"
             )

Hope you dont mind telling us how did you end up with that kind if a list in the first place? I would suggest modifying the source rather rebuilding the list...


It's not that difficult. The primary goal is to count all items in the list, but when an item does have subitems, the subitem(s) replace the parent. Take "dinnerset" for example. When creating a list with all items I don't want to see this on the list, because I do have a list with the items in it already ("chair" and "dinnertable"). If both are on the order list created from this drawing, I'll order 1 "dinnerset", 4 "chair" and 1 "dinnertable". Which is wrong because I'll end up with 8x "chair" and 2x "dinnertable".

"Tree" is in the list because it does not contain subitems.

I think you are right about not rebuilding the list, but to create the correct list directly. My attempts started with this code of Leeben:

http://www.theswamp.org/index.php?topic=28043.msg336595#msg336595

Which generates the list as described in my first post.

I also tried to modify the code of T.Wiley

http://www.theswamp.org/index.php?topic=31524.15

Where the function Countlist does the job, but it's not perfect because:
1. the code is called multiple times with a "foreach loop" where Clist keeps track of the result, giving the result list back multiple times, but only correct when called the last time. Not very elegant I guess.
2. if an object is on multiple levels (when "chair" is member of "dinnerset", but  member of "house" too) it will appear twice in the list, so another loop is necessary to total the result.

Code: [Select]
;By T.Wiley
;Modified by Woabow
(defun c:Subcalc (/ *error* ActDoc BlkCol Sel EntData BlkName NestList CList)
   
    (defun *error* ( msg ))
       
    ;------------------------------
    (defun GetBlockBlocks ( blkObj doc / BlkCol BlkCntList tempList tempName )
       
        (setq BlkCol (vla-get-Blocks doc))
        (vlax-for obj blkObj
            (if (= (vla-get-ObjectName obj) "AcDbBlockReference")
                (setq BlkCntList
                    (if (setq tempList (assoc (setq tempName (vla-get-Name obj)) BlkCntList))
                        (subst (list tempName (1+ (cadr tempList)) (caddr tempList)) tempList BlkCntList)
                        (cons (list tempName 1(GetBlockBlocks (vla-Item BlkCol tempName) doc)) BlkCntList)
                    )
                )
            )
        )
        BlkCntList
    )
    ;-------------------------------------
    (defun CountList ( lst multipl )
       (if (atom (caddr lst))
        (progn
          (setq CList (cons (cons (car lst) (* (cadr lst) multipl)) CList ))
        )
       )
        (setq multipl (* multipl (cadr lst)))           
        (foreach i (caddr lst)
          (CountList i multipl)
        )
      CList
    )
   
   
    ;-------------------------------------
    (setq ActDoc (vla-get-ActiveDocument (vlax-get-Acad-Object)))
    (setq BlkCol (vla-get-Blocks ActDoc))
    (if
        (and
            (setq Sel (entsel "\n Select block to get nesting block list: "))
            (setq EntData (entget (car Sel)))
            (= (cdr (assoc 0 EntData)) "INSERT")
            (setq BlkName (cdr (assoc 2 EntData)))
            (setq NestList (GetBlockBlocks (vla-Item BlkCol BlkName) ActDoc))
        )
        (progn
            (prompt (strcat "\nReport for: " BlkName))
            (setq p (list))
            (foreach lst NestList
                (setq p (CountList lst 1))
;                 (print "- ")
            )
            (foreach ip p
             (print ip)
            )
        )
    )
    (princ)
)
 

If I have correctly understood the request, try my 'Nested Block Counter' program  :-)

Almost  :-) It does also show items that contain subitems. And when running it on a big drawing with many blocks Bricscad responds with "error: LISP recursion stack limit exceeded " and Autocad with "internal stack limit reached (simulated)".

Here is the drawing.

http://dl.dropbox.com/u/2190289/s6.rar

It's does not have very deep levels of blocks, which will occur often also, but I have no example drawing that I can post right now.

Lee Mac

  • Seagull
  • Posts: 12905
  • London, England
Re: Counting nested blocks on only highest and lowest level
« Reply #8 on: December 12, 2011, 06:41:41 AM »
If I have correctly understood the request, try my 'Nested Block Counter' program  :-)

Almost  :-) It does also show items that contain subitems.

It will count all primary and nested blocks, nothing more, nothing less.

And when running it on a big drawing with many blocks Bricscad responds with "error: LISP recursion stack limit exceeded " and Autocad with "internal stack limit reached (simulated)".

I've updated the code in that post to hopefully fix this issue.  :-)


Woabow

  • Newt
  • Posts: 56
Re: Counting nested blocks on only highest and lowest level
« Reply #9 on: December 12, 2011, 07:57:25 AM »

Thanks Lee, but I still get the Stack error. Does it work with the S6 drawing on your pc?

Lee Mac

  • Seagull
  • Posts: 12905
  • London, England
Re: Counting nested blocks on only highest and lowest level
« Reply #10 on: December 12, 2011, 08:07:57 AM »
Thanks Lee, but I still get the Stack error. Does it work with the S6 drawing on your pc?

I don't have WinRAR and so cannot open the file, is the file too large to attach in a forum post?

Lee Mac

  • Seagull
  • Posts: 12905
  • London, England
Re: Counting nested blocks on only highest and lowest level
« Reply #11 on: December 12, 2011, 08:17:24 AM »
Almost  :-) It does also show items that contain subitems.

So, am I correct in that you wish to count the quantities of:

  • Primary Blocks which do not contain Nested Blocks.
  • Nested Blocks for those Primary Blocks which do contain Nested Blocks.

I might be able to offer a modification to my Nested Block Counter that you could implement.

Woabow

  • Newt
  • Posts: 56
Re: Counting nested blocks on only highest and lowest level
« Reply #12 on: December 12, 2011, 09:34:16 AM »
If possible I would like to know quantities of:

1. Blocks which do not contain Nested Blocks. This can be ordinary blocks, no nesting at all, or the lowest blocks in a nested block (atoms).
2. All top level blocks, forget all nested Blocks. Thisl is not that important, there are many block count programs out there that do not count the nested blocks.

Would be really helpful if you could make this modification. I must admit that I find Lisp difficult to master.

If have uploaded the drawing again:

http://dl.dropbox.com/u/2190289/s6.dwg


Lee Mac

  • Seagull
  • Posts: 12905
  • London, England
Re: Counting nested blocks on only highest and lowest level
« Reply #13 on: December 12, 2011, 10:06:27 AM »
If have uploaded the drawing again:

Thanks, I have updated the code again which should fix the stack error you were receiving.

If possible I would like to know quantities of:

1. Blocks which do not contain Nested Blocks. This can be ordinary blocks, no nesting at all, or the lowest blocks in a nested block (atoms).

Ok, in the updated code (Version 1.2), replace the '_UpdateNestedBlockCount' and '_IterateSelection' subfunctions with the following functions:

Code: [Select]
        (defun _UpdateNestedBlockCount ( name count tree alist / nests )
            (if (setq nests (cdr (assoc name tree)))
                (foreach nest nests
                    (setq alist (_UpdateNestedBlockCount (car nest) (* count (cdr nest)) tree alist))
                )
                (_Assoc++ name count alist)
            )
        )

        (defun _IterateSelection ( selection blocktree / inc name nested primary )
            (if selection
                (repeat (setq inc (sslength selection))
                    (setq name (_EffectiveName (ssname selection (setq inc (1- inc)))))
                    (if (cdr (assoc name blocktree))
                        (setq nested  (_UpdateNestedBlockCount name 1 blocktree nested))
                        (setq primary (_Assoc++ name 1 primary))                         
                    )
                )
            )
            (list primary nested)
        )

However, I would appreciate if you didn't post the modified code, as otherwise I will have two very different versions of the same program on the web.

Lee


Woabow

  • Newt
  • Posts: 56
Re: Counting nested blocks on only highest and lowest level
« Reply #14 on: December 12, 2011, 10:53:25 AM »
Thanks Lee, works great!

It is slower on very big drawings than my attempt posted above, but for some reason I have more confident in your code  :-)