I hate doing things I can make my computer do for me. My adventures in customizing and automating computers. Primarily with AutoCAD and Microsoft Office.

Friday, April 24, 2009

Bulk Rename Utility

I am working on a project that involves renaming many xref drawing files.  To deal with redirecting all of the references to these files I have created a routine that reads a list of old reference drawing names and new reference drawing names from a tab delimited text file.  The lisp routine then renames each reference to each of the redirected files and changes the path to point to the renamed drawing.  The intention is that this routine will be run by the s::startup routine when a drawing opens.  This allows us to simply add an entry to the redirection list and it will be handled the next time any referencing drawings are opened.

Yesterday I was faced with the task of renaming about 50 of these xref drawing files in one shot.  Enter “Bulk Rename Utility”.  Bulk Rename Utility is a free file renaming tool allowing you to easily rename files and entire folders based upon extremely flexible criteria.  A look at a screenshot (here) gives an idea just how many options there are.  This utility not only allowed me to rename these 50 files in one quick shot, but also gave me a tab delimited list of the old name and new name pairs.  This list will go directly into my xref redirection list with very little additional tweaking.

Kudos to a great piece of free software.  Download it here:http://www.bulkrenameutility.co.uk/Main_Intro.php

Wednesday, April 22, 2009

Get all insertions of a block

Note: this process will not correctly identify insertions of dynamic blocks. - mweaver 01/18/2019
Let’s look at a way to get all insertions of a block without using ssget and without iterating through all objects in the drawing.  This method will also get nested block insertions.
Let’s say the block we want is named “Test2”.
We can use tblobjname to get the block definition entity, thus:
(tblobjname "block" "test2")
This will get us an entity name which we can run through entget to get the following:
((-1 . <Entity name: 7ee414c8>)
  (0 . "BLOCK")
  (330 . <Entity name: 7ee414c0>)
  (5 . "181")
  (100 . "AcDbEntity")
  (67 . 0)
  (8 . "0")
  (100 . "AcDbBlockBegin")
  (70 . 0)
  (10 0.0 0.0 0.0)
  (-2 . <Entity name: 7ee414d0>)
  (2 . "test2")
  (1 . "")
)
Here we have the beginning of the block definition – the objects that make up the block.  If we do an entget on the entity name at assoc 330, we get something like the following:
((-1 . <Entity name: 7ee414c0>)
  (0 . "BLOCK_RECORD")
  (330 . <Entity name: 7ee3fc08>)
  (5 . "180")
  (100 . "AcDbSymbolTableRecord")
  (100 . "AcDbBlockTableRecord")
  (2 . "test2")
  (360 . <Entity name: 7ee414c8>)
  (340 . <Entity name: 0>)
  (102 . "{BLKREFS")
  (331 . <Entity name: 7ee414e8>)
  (331 . <Entity name: 7ee41540>)
  (102 . "}")
  (70 . 1)
  (280 . 1)
  (281 . 0)
)
Note the 331 entries.  These are the entity names for each insertion of Test2.
This approach has distinct advantages over ssget, primarily because this will show nested insertions.  Also, with a bit of trickery, this can be used with objectdbx documents – avoiding the need to iterate every object in the drawing looking for block insertions.
For more information see my entry on Getting block insertions with objectdbx.

Saturday, April 18, 2009

Gathering attributes from a block insertion

In an earlier post I showed a routine for gathering attributes from a block insertion object.  I have since found a better way to get the attribute objects.

For years I have been using vla-get-attributes, which returns a variant, which must then be converted to a safearray and then a list.  The “New and Improved” method is to use vlax-invoke.

Below are a couple of test procedures I used to benchmark these two methods.

First, I selected the block insertion and converted the entity name to a vla object.

(setq obj (vlax-ename->vla-object(car (entsel))))

Here are the two functions.  AttTest1 using vla-getattributes, and AttTest2 using vlax-invoke.  Note that the functions are defined using Defun-q rather than Defun.  This is because the benchmarking routine I used (by Vladimir NesterovskyProfiler) requires defun-q.  I typically change the defun to defun-q to run the profiler, then change it back.

(defun-q attTest1
     ()
     (vlax-safearray->list
       (vlax-variant-value
         (vla-getattributes obj)
       )
     )
     (princ)
)

(defun-q attTest2
     ()
     (vlax-invoke obj 'getattributes)
     (princ)
)

Now for the fun part. Profile-Func is a routine in Vladimir’s profiler that registers the function and starts the timer.

(profile-func 'attTest1)
(profile-func 'attTest2)

Below are the timer results after running both routines 10 times.

Routine

Number of runs timed

Total seconds for all runs

Average seconds per run

AttTest1 10 0.4 0.041
AttTest2 10 0.07 0.007

To put this in perspective, the test block I created for this has 10,000 attributes.  That’s 0.00007 milliseconds per attribute!  Not too shabby!  Needless to say, I am going to be using vlax-invoke to get block attributes on all my routines in the future.  In addition I am going to be looking much more closely at the vlax functions.  I am particularly interested in using vlax-invoke with the GetDynamicBlockProperties method.

Wednesday, April 08, 2009

A Free copy of AutoCAD? From Autodesk???

Jimmy Bergmark, over at JTBWorld posted an article pointing out the Autodesk Assistance Program. This program makes a copy of AutoCAD 2010 available to qualified applicants. If you are a recently un-employed AutoCAD user, check this out!

Tuesday, April 07, 2009

AssocOn other than the car

Have you ever wanted to use Assoc to select an item out of a list, but the search term you wanted to use wasn't the car of the elements of the list? Enter AssocOn. This will let you do an "assoc-like" search on any element of the sub-lists. Take a look at the examples below.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  Routine:    AssocON                                
;;;  Purpose:    Similar to Assoc, but uses a user specified function to select    
;;;          the item in the list on which to search instead of        
;;;          searching on the car of the list.                
;;;  Arguments:    SearchTerm, the item for which we are searching.        
;;;        Lst, the list of lists through which we are searching.        
;;;        func, the function we are using to specify the item in the    
;;;          sub-list.                            
;;;  Returns: the sub-list with the first match.                ;;;
;;;=============================================================================
;;;  Examples                                    
;;;  Given:  (setq mylist '((1 2 3 4 5) (7 6 5 4 3 2 1) (12 13 14 15)))        
;;;        (assocon 2 mylist 'cadr)                        
;;;    will return                                
;;;       (1 2 3 4 5)                                
;;;                                        
;;;    and                                    
;;;                                        
;;;       (assocon "2" mylist                            
;;;        (function (lambda(x)(itoa (cadr x))))                
;;;       )                                    
;;;    will return                                
;;;       (1 2 3 4 5)                                
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun AssocOn (SearchTerm Lst func /)
  (
car
    
(vl-member-if
      
(function
    
(lambda (pair) (equal SearchTerm (apply func (list pair))))
      )
      
lst
    
)
  )
)

Wednesday, April 01, 2009

Get block insertions with objectdbx

Routine: GetBlockInserts  
Purpose:   Get a list of all insertions of specified blocks.
Arguments: doc vla-document object (or objectdbx document). If nil, then the active document is used.           
  bnames a list of strings, the block names for the subject blocks.
  ReturnEnames boolean, non-nil returns entity names, nil returns vla-objects.
Returns:   A list of all the insertions of the subject blocks. -1 if the block is not defined. Nil if the block is defined but has no insertions.

Calling GetBlockInserts thus:

(getblockinserts2 nil '("test1" "test2" "test3") nil)

Might return something that looks like this:

(("test1"
   #<VLA-OBJECT IAcadBlockReference 0b4b55c4>       ;These may be enames or vla-objects depending on ReturnEnames
   #<VLA-OBJECT IAcadBlockReference 0b4b52f4>
   #<VLA-OBJECT IAcadBlockReference 0b4b56b4>
   #<VLA-OBJECT IAcadBlockReference 0b4b563c>
   #<VLA-OBJECT IAcadBlockReference 0b4b518c>
   #<VLA-OBJECT IAcadBlockReference 0b4b554c>
   #<VLA-OBJECT IAcadBlockReference 0b4b54d4>
  )
  ("test2"
   #<VLA-OBJECT IAcadBlockReference 0b4b57a4>
   #<VLA-OBJECT IAcadBlockReference 0b4b53e4>
   #<VLA-OBJECT IAcadBlockReference 0b4b536c>
   #<VLA-OBJECT IAcadBlockReference 0b4b545c>
  )
  ("test3"
    #<VLA-OBJECT IAcadBlockReference 0b4b527c>
    #<VLA-OBJECT IAcadBlockReference 0b4b5204>
    #<VLA-OBJECT IAcadBlockReference 0b4b4dcc>
    #<VLA-OBJECT IAcadBlockReference 0b4b4fac>
    #<VLA-OBJECT IAcadBlockReference 0b4b509c>
   )
("test4")                                           ;There are no insertions of this block
("test5" . –1)                                      ;This block is not defined
)

Note that (cdr(assoc “test4” results)) will return nil.

 

(defun GetBlockInserts2    (doc bnames ReturnEnames / objBlockDef InsList Blocks)
  (
setq    doc    (if doc
         doc
        
(vla-get-activedocument (vlax-get-acad-object))
           )
   
blocks (vla-get-blocks doc)
  )
  (
mapcar (function (lambda (bname)
              (
setq inslist nil)
              (
cons bname
               
(if    (vl-catch-all-error-p
                 
(setq    objBlockDef
                    
(vl-catch-all-apply 'vla-item (list blocks bname))
                  )
                )
                 
-1    ; Block doesn't exist
                  (setq InsList (vl-remove-if-not
                     
(function
                       
(lambda (x) (and (= 331 (car x)) (entget (cdr x))))
                          )
                          (
member '(102 . "{BLKREFS")
                              (
entget (vlax-vla-object->ename objBlockDef))
                          )
                        )
                   
inslist (if    ReturnEnames
                         
(mapcar (function (lambda (x) (cdr x))) inslist)
                          (
mapcar
                       
(function (lambda (x) (vlax-ename->vla-object (cdr x)))
                        )
                       
inslist
                         
)
                        )
                  )
                )
              )
            )
      )
     
bnames
 

)
)

Modified 4/24/2009