Author Topic: GetBlockLimits (for cadmandu) ...  (Read 4283 times)

0 Members and 1 Guest are viewing this topic.

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
GetBlockLimits (for cadmandu) ...
« on: December 10, 2014, 07:51:34 PM »
Don't know if this is what you want but it's what I can ante up ...

Code: [Select]
(defun _GetBlockLimits ( block min_size / @GetName @GetObjectExtents @IniCollection @Legit @Main @Try @XY @Wazzup )

    ;;========================================================================
    ;;
    ;;  _GetBlockLimits.lsp
    ;;
    ;;      Get the block's limits and object counts.
    ;;
    ;;      2014 Michael Puckett
    ;;
    ;;      Distilled from existing code for my old buddy Steve (cadmandu).
    ;;     
    ;;      As such there may be errors. Find 'em, report 'em or shaddup uface.
    ;;
    ;;------------------------------------------------------------------------
    ;;
    ;;  Args:
    ;;
    ;;      block    ;; valid (activex) block, e.g. modelspace
    ;;      min_size ;; the smallest object size to be considered valid
    ;;
    ;;  Example:
    ;;
    ;;      (_GetBlockLimits
    ;;          (vla-get-modelspace
    ;;              (vla-get-activedocument
    ;;                  (vlax-get-acad-object)
    ;;              )
    ;;          )
    ;;          300 ;; ignore anything smaller than a foot
    ;;      )
    ;;
    ;;      >>  (   (3.44425e+006 1.24654+003) ;; min x & y
    ;;              (3.57655e+006 5.1095e+007) ;; max x & y   
    ;;              43                         ;; objects counted
    ;;              9                          ;; objects ignored
    ;;          )     
    ;;
    ;;      (_GetBlockLimits
    ;;          (vla-get-modelspace
    ;;              (vla-get-activedocument
    ;;                  (vlax-get-acad-object)
    ;;              )
    ;;          )
    ;;          9.99e99 ;; ignore anything smaller than a vogon warship
    ;;      )
    ;;
    ;;      >>  (
    ;;              nil ;; no min     
    ;;              nil ;; or max
    ;;              0   ;; because 0 objects counted
    ;;              52  ;; all objects ignored
    ;;          )
    ;;
    ;;  Other notes:
    ;;
    ;;      The block can be any valid (activex) block.
    ;;      *   It can be modelspace (usually the case).
    ;;      *   Said block can reside within a document opened via ObjectDBX
    ;;          (how we use it).
    ;;      *   It could be paperspace, tho you would want to mod or delete the
    ;;          @Legit function. It's currently set up to reject anything that
    ;;          we wouldn't normally consider a 3D object as we use this to
    ;;          determine the limits of 3D models for automagic x-referenceing
    ;;          for given dwg limits.
    ;;      *   If you want to use this to determine the limits of x-references
    ;;          attached to the current drawing just iterate the active document's
    ;;          blocks collection, and if the block is an xref pass it to the
    ;;          function (and then apply applicable transformation yada to the
    ;;          result for scale, rotation and funky normals).
    ;;
    ;;  /beer
    ;;
    ;;------------------------------------------------------------------------
   
    (defun @Try ( try_statement / try_catch try_result )

        (if
            (vl-catch-all-error-p
                (setq try_catch
                    (vl-catch-all-apply
                        (function
                            (lambda ( )
                                (setq try_result (eval try_statement))
                            )
                        )
                    )
                )
            )
            (setq *try_errors* ;; lexical global
                (cons
                    (list
                        try_statement
                        (vl-catch-all-error-message try_catch)
                    )
                    *try_errors*
                )
            )
        )

        try_result

    )
   
    (defun @GetName ( obj / name )

        (@Try '(setq name (vla-get-name obj)))

        (if name name "")

    )
   
    (defun @XY ( point )
   
        (list (car point) (cadr point))
   
    )
   
    (defun @GetObjectExtents ( object / a b )

        (@Try
           '(progn
                (vlax-invoke-method object 'GetBoundingBox 'a 'b)
                (mapcar 'vlax-safearray->list (list a b))
            )
        )

    )
   
    (defun @IniCollection ( collection )

        (if (null (member collection *ini_collections*)) ;; lexical global
            (progn
                (vlax-for obj collection (princ))
                (setq *ini_collections* (cons collection *ini_collections*))
            )
        )

        collection

    )

    (defun @Legit ( obj min_size / objname )
   
        ;;  filter out non model, non essential, non visible objects
        ;;
        ;;  edit to suit, this works for my applications
       
        (not
            (or
                (member
                    (setq objname (strcase (vla-get-objectname obj)))
                   '(   "ACDBPOINT"
                        "ACDBTEXT"
                        "ACDBMTEXT"
                        "ACDBLEADER"
                        "ACDBWIPEOUT"
                        "ACDBATTRIBUTE"
                        "ACDBHATCH"
                        "ACDBATTRIBUTEDEFINITION"
                        "ACDBOLE2FRAME"
                    )
                )
                (wcmatch objname "ACDB*DIMENSION")
                (and
                    (eq "ACDBBLOCKREFERENCE" objname)
                    (or
                        ;;  it's an AutoPLANT intel node
                        (member
                            (strcase (@GetName obj))
                           '("AT_MASTER_PIPING" "AT_MASTER_EQUIP")
                        )
                        ;;  it's an xref
                        (vlax-property-available-p obj 'path)
                    )
                )
                (and
                    (vlax-property-available-p obj 'length)
                    (@Try '(< (vlax-get obj 'length) min_size))
                )
                (and
                    (vlax-property-available-p obj 'diameter)
                    (@Try '(< (vlax-get obj 'diameter) min_size))
                )
                (and
                    (vlax-property-available-p obj 'visible)
                    (@Try '(eq :vlax-false (vla-get-visible obj)))
                )
            )
        )
    )

    (defun @Wazzup ( obj / bb )
   
        ;;  Note: Uses many lexical globals:
        ;;        min_size, x*, y*, count, ignored
   
        (if
            (and
                (@legit obj min_size)
                (setq bb (@GetObjectExtents obj))
                (< min_size (apply 'distance (mapcar '@XY bb)))
            )
            (setq
                ;;  x* / y* are lexical globals
                x* (append x* (mapcar 'car bb))
                y* (append y* (mapcar 'cadr bb))
            )
            ;;  don't count objects without a bounding box
            (setq
                count   (1- count)
                ignored (1+ ignored)
            )
        )
    )

    (defun @Main ( block min_size / x* y* count ignored )
   
        (setq
            count   (vla-get-count block)
            ignored 0
        )

        (vlax-map-collection (@IniCollection block) '@Wazzup)
       
        (if (zerop count)
            (list
                nil
                nil
                count
                ignored
            )
            (list
                (list (apply 'min x*) (apply 'min y*))
                (list (apply 'max x*) (apply 'max y*))   
                count
                ignored
            )
        )       
    )

    (@Main block min_size)

)

Cheers.
« Last Edit: December 10, 2014, 11:08:48 PM by MP »
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.com • http://cadanalyst.slack.com • http://linkedin.com/in/cadanalyst

cadmandu

  • Guest
Re: GetBlockLimits (for cadmandu) ...
« Reply #1 on: December 11, 2014, 08:02:14 AM »
Thanks very much MP, i'll give it a try here this morning and let you know.
τΏτ
Steve'o

Lee Mac

  • Seagull
  • Posts: 12912
  • London, England
Re: GetBlockLimits (for cadmandu) ...
« Reply #2 on: December 11, 2014, 01:48:17 PM »
Nice coding MP.

I would note that the ActiveX getboundingbox method doesn't work too well where dynamic block references are concerned, since the method doesn't seem to account for invisible objects within the block definition. Hence, you will receive unexpected results when calculating the extents of a modelspace block containing dynamic block references, or if you are processing a block definition containing nested dynamic block references (unlikely, but you never know).

For what its worth, this is the route I would take:
(kudos to gile for his refgeom function)
Code: [Select]
;; Block Reference Bounding Box  -  Lee Mac
;; Returns a WCS point list describing a rectangular frame bounding all geometry of a supplied block reference.
;; Excludes Text, MText & Attribute Definitions.
;; ref - [vla] Block Reference Object

(defun LM:blockreferenceboundingbox ( ref )
    (
        (lambda ( lst )
            (apply
                (function
                    (lambda ( m v )
                        (mapcar (function (lambda ( p ) (mapcar '+ (mxv m p) v))) lst)
                    )
                )
                (refgeom (vlax-vla-object->ename ref))
            )
        )
        (LM:blockdefinitionboundingbox
            (vla-item
                (vla-get-blocks (vla-get-document ref))
                (vla-get-name ref)
            )
        )
    )
)

;; Block Definition Bounding Box  -  Lee Mac
;; Returns a WCS point list describing a rectangular frame bounding all geometry of a supplied block definition.
;; Excludes Text, MText & Attribute Definitions.
;; def - [vla] Block Definition Object

(defun LM:blockdefinitionboundingbox ( def / llp lst urp )
    (vlax-for obj def
        (cond
            (   (= :vlax-false (vla-get-visible obj)))
            (   (= "AcDbBlockReference" (vla-get-objectname obj))
                (setq lst (append lst (LM:blockreferenceboundingbox obj)))
            )
            (   (and (not (wcmatch (vla-get-objectname obj) "AcDbAttributeDefinition,AcDb*Text"))
                     (vlax-method-applicable-p obj 'getboundingbox)
                     (not (vl-catch-all-error-p (vl-catch-all-apply 'vla-getboundingbox (list obj 'llp 'urp))))
                )
                (setq lst (vl-list* (vlax-safearray->list llp) (vlax-safearray->list urp) lst))
            )
        )
    )
    (LM:points->boundingbox lst)
)

;; Point to Bounding Box  -  Lee Mac
;; Returns the rectangular extents of a supplied point list

(defun LM:points->boundingbox ( lst )
    (   (lambda ( l )
            (mapcar '(lambda ( a ) (mapcar '(lambda ( b ) ((eval b) l)) a))
               '(
                    (caar   cadar)
                    (caadr  cadar)
                    (caadr cadadr)
                    (caar  cadadr)
                )
            )
        )
        (mapcar '(lambda ( f ) (apply 'mapcar (cons f lst))) '(min max))
    )
)

;; RefGeom (gile)
;; Returns a list which first item is a 3x3 transformation matrix (rotation, scales, normal)
;; and second item the object insertion point in its parent (xref, block or space)
;; Argument : an ename

(defun refgeom ( ent / ang ang mat ocs )
    (setq enx (entget ent)
          ang (cdr (assoc 050 enx))
          ocs (cdr (assoc 210 enx))
    )
    (list
        (setq mat
            (mxm
                (mapcar '(lambda ( v ) (trans v 0 ocs t))
                   '(
                        (1.0 0.0 0.0)
                        (0.0 1.0 0.0)
                        (0.0 0.0 1.0)
                    )
                )
                (mxm
                    (list
                        (list (cos ang) (- (sin ang)) 0.0)
                        (list (sin ang) (cos ang)     0.0)
                       '(0.0 0.0 1.0)
                    )
                    (list
                        (list (cdr (assoc 41 enx)) 0.0 0.0)
                        (list 0.0 (cdr (assoc 42 enx)) 0.0)
                        (list 0.0 0.0 (cdr (assoc 43 enx)))
                    )
                )
            )
        )
        (mapcar '- (trans (cdr (assoc 10 enx)) ocs 0)
            (mxv mat (cdr (assoc 10 (tblsearch "block" (cdr (assoc 2 enx))))))
        )
    )
)

;; Matrix x Vector - Vladimir Nesterovsky
;; Args: m - nxn matrix, v - vector in R^n

(defun mxv ( m v )
    (mapcar '(lambda ( r ) (apply '+ (mapcar '* r v))) m)
)

;; Matrix Transpose - Doug Wilson
;; Args: m - nxn matrix

(defun trp ( m )
    (apply 'mapcar (cons 'list m))
)

;; Matrix x Matrix - Vladimir Nesterovsky
;; Args: m,n - nxn matrices

(defun mxm ( m n )
    ((lambda ( a ) (mapcar '(lambda ( r ) (mxv a r)) m)) (trp n))
)

A quick test program for convenience:
Code: [Select]
(defun c:test ( / box ent enx )
    (if
        (and
            (setq ent (car (entsel "\nSelect block: ")))
            (setq enx (entget ent))
            (= "INSERT" (cdr (assoc 0 enx)))
        )
        (if (setq box (LM:blockreferenceboundingbox (vlax-ename->vla-object ent)))
            (entmake
                (append
                   '(
                        (000 . "LWPOLYLINE")
                        (100 . "AcDbEntity")
                        (100 . "AcDbPolyline")
                        (090 . 4)
                        (070 . 1)
                    )
                    (list (cons  038 (caddr (trans (car box) 0 ent))))
                    (mapcar '(lambda ( x ) (cons 10 (trans x 0 ent))) box)
                    (list (assoc 210 enx))
                )
            )
        )
    )
    (princ)
)
(vl-load-com) (princ)

Hopefully any added functionality will make up for the distinct lack of comedic comments  :uglystupid2:



Aside, I struggled to determine the advantage of the @IniCollection function in your code - though, there's good chance I'm overlooking something obvious - could you shed some light?

Lee
« Last Edit: December 11, 2014, 01:54:16 PM by Lee Mac »

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: GetBlockLimits (for cadmandu) ...
« Reply #3 on: December 11, 2014, 11:52:34 PM »
A quick thanks for chiming in Lee, much appreciated. Had a brutal day and too weary to play. Will review on Saturday, thanks again for spending the time herein.
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.com • http://cadanalyst.slack.com • http://linkedin.com/in/cadanalyst

Lee Mac

  • Seagull
  • Posts: 12912
  • London, England
Re: GetBlockLimits (for cadmandu) ...
« Reply #4 on: December 12, 2014, 05:11:13 PM »
No worries MP, I know the feeling - take your time, there are no deadlines here  :-)

cadmandu

  • Guest
Re: GetBlockLimits (for cadmandu) ...
« Reply #5 on: December 13, 2014, 10:35:56 AM »
Really appreciate all the help here guys :-D
steve'o

Peter2

  • Swamp Rat
  • Posts: 650
Re: GetBlockLimits (for cadmandu) ...
« Reply #6 on: November 01, 2016, 05:56:45 AM »
Hi

I tried Lees code, and (of course ...) it works fine, but there seem to be a challenge in different
Code - Auto/Visual Lisp: [Select]
  1. insunits / dwgunits
. If the units are set correctly, then AutoCAD scales the blocks correctly. The pure code gives the pure extents of the block, and at the moment I have to think about the units-stuff ...


EDIT:
When I set "dwgunits" from 3 (mm) to 6 (Meter), then I get a huge and not correct box...
« Last Edit: November 01, 2016, 11:36:17 AM by Peter2 »
Peter

AutoCAD Map 3D 2023 German (so some technical terms will be badly retranslated to English)
BricsCAD V23