AutoCAD automation

Written: 2012-06-27 16:22:20

Introduction

As you can check on my CV, I’ve been working as a draughtsman/IT guy/CAD manager for around five years now.

We spend most of our time “drawing” things with Autodesk’s AutoCAD. Be it a P&ID, an electric diagram, civil, structural or piping drafts, a 3D model, you name it!

Being a somewhat small company, in five years we’ve faced different challenges, like getting projects that would require more human hours than we have the means to cover — either by lack of personnel or deadlines.

Sometimes we’ve solved those challenges by getting more people, sometimes by working more hours than usual. Sometimes though those things are just not possible — something else is needed.

Automation in AutoCAD

That something else, would be automation in AutoCAD. That is, using some amount of programming to make tasks easier, faster and less monotonous.

There are plenty of APIs for AutoCAD, the eldest (and my favourite!) being Lisp (more accurately AutoLisp/Visual Lisp).

Other options include VBA, .NET and ObjectARX. VBA for AutoCAD is deprecated though, so that leaves us with either .NET and ObjectARX as alternatives.

I mentioned that my favourite option is Lisp and that it’s the oldest; the thing is that ever since the Visual Lisp extensions, it’s incredibly easy to program in a somewhat object-oriented way and to manipulate graphic objects without having to worry too much about AutoCAD’s inner functioning.

The downside is that it’s quite a mess to have a GUI for the scripts or routines you develop. I mean, sure, it can be done (there’s DCL after all) but it’s not as flexible or easy as one would hope. Luckily, for most use cases a simple custom toolbar and text input will make users happy.

If you really need GUIs or deep control of what’s going on under the hood, I’d strongly recommend learning about the ObjectARX API. It’s well-documented and it’s incredibly powerful — you have to program in C++ though.

Small sample

Here are some bits from what I usually have on my acaddoc.lsp file. That is, the customisations that are loaded with each drawing.

Most are really silly things that happen to save little time per single use but one gets to use quite a lot depending on the kind of work currently at hand.

You may download this file here.

; small-sample.lsp: Is a small sample of things that can be done with
; Visual Lisp. It includes part of Evilham's acaddoc.lsp.
;
; LICENSE:
; Copyright (c) 2012, Evilham (https://evilham.com) 
; BSD 2-Clause copyright and disclaimer apply.
; See: http://www.opensource.org/licenses/bsd-license.php


; This loads the Visual Lisp extensions for AutoLisp
(vl-load-com)

(defun c:``()
; Toggle isometric / orthogonal axis (called snap in AutoCAD)
  (setvar 'SNAPSTYL (* (- (getvar 'SNAPSTYL) 1) -1)))

(defun c:<<(/ z)
; Print the Z coordinate of a given point:
  (setq z (nth 2 (getpoint "\nPoint to get z from: ")))
  (princ (strcat "\nz: " (rtos z 2 2)))
  (princ))

(defun ss->vla-objectlist(ss / lst i lim)
; Convert a selection set to a list of vla-objects
  (setq i -1 lim (sslength ss))
  (while (< (setq i (1+ i)) lim)
    (setq lst (cons (vlax-ename->vla-object (ssname ss i)) lst)))
  (reverse lst))

(defun txtnum(ss prefix suffix nstart nstep ndecimals / i n obj)
; Takes a selection set / vla-object list, two strings (prefix/suffix)
;   and three numbers nstart, nstep, ndecimals.
; It puts in the text/mtext objects' TextString property the following:
;   (strcat prefix NUM suffix)
;   where NUM is nstart + POS_IN_QUEUE * nstep. With ndecimals decimals.

; If ss is a selection set, convert it to a list.
  (if (= (type ss) 'PICKSET)
    (setq ss (ss->vla-objectlist ss)))
; If ss is not a list at this point. Or nstart, nstep, ndecimals are not all
; numbers, set ss to nil. That skips the code and avoids errors.
  (if (not (and (listp ss) (numberp nstart) (numberp nstep)
                (numberp ndecimals) (setq ndecimals (fix ndecimals))))
    (setq ss nil))
  (setq i 0)
  (foreach obj ss
    (if
      (not (vl-catch-all-error-p 
        (vl-catch-all-apply
          'vlax-put-property
          (list obj
                'TextString
                (strcat prefix
                        (rtos (+ nstart (* i nstep)) 2 ndecimals)
                        suffix)))))
      (setq i (1+ i))))
  i)
(defun c:txtnum(/ ss prefix suffix)
; Asks for a prefix and a suffix.
; Calls txtnum with nstart=1 nstep=1 ndecimals=0
  (setq ss (ssget)
    prefix (getstring "Prefix: ")
    suffix (getstring "Suffix: ")
  )
  (txtnum ss prefix suffix 1 1 0)
  (princ))

(defun r:gen(ang / ss po i lim)
; Rotates all selected items by ang 
  (if (and (setq ss (ss->vla-objectlist (ssget)))
           (setq po
                 (vlax-make-variant
                   (vlax-safearray-fill
                     (vlax-make-safearray
                       vlax-vbDouble
                       '(0 . 2))
                     (getpoint "Base point: "))))
           (numberp ang))
      (foreach obj ss
        (vl-catch-all-apply
          'vlax-invoke-method
          (list
            obj
            'Rotate
            po
            ang)))))
(defun deg->rad(ang)
; Conversion from degrees to radians.
  (* (/ ang 180.0) pi))
; The following commands rotate by 90, 180 and 270 degrees respectively:
(defun c:r1() (r:gen (deg->rad 90)) (princ))
(defun c:r2() (r:gen (deg->rad 180)) (princ))
(defun c:r3() (r:gen (deg->rad 270)) (princ))
(princ)

Some of these commands look really silly, like r1, r2 and r3, however once you get used to them it’s much faster to just type r1, select the objects, and click on the base point than having to also type in the degrees or activating ORTHO and clicking on the good direction (also minding the fact that OSNAP may get you a different point and hence a wrong angle).

In others, like txtnum, you’ll notice how a much more generic function is also defined. That’s meant to allow for quick in-place modifications to satisfy slightly different needs.

For example, if you wanted the text “X meters” with X starting at 0 and increasing by 0.5 each time, you could just type:

(txtnum (ssget) "" " meters" 0 0.5 1)

And if it happens to be something you need a lot in a session, just assign that to a temporary alias like this:

(defun c:<1() (txtnum (ssget) "" " meters" 0 0.5 1) (princ))

And if you really use it a lot, just add that line to your acaddoc.lsp and now you have yet another silly command to make your life easier.

Conclusion

There’s much power in AutoCAD’s APIs, basically the limit between what you can and cannot do is in your imagination (albeit it’s cliché, it’s also mostly true).

Any seemingly simple task that is bothering you because it’s too boring (read, easy and mechanical) can most likely be automated in plenty of different ways! Heck, even pretty complex things can be automated (up to a certain point) given enough analysis and understanding of the needs.

If you find yourself in such a case, do drop me a line! If you provide me with a good description of what you have to do (hopefully with a file example; example, I won’t do your job for you!), how you think you can automate it (I reserve the right to do it in a totally different way ;)), and I happen to: find it interesting enough, have the time and disposition to do what you’re asking for; I may as well just do it!

Further reading

It has been a while since last time I needed online help developing for AutoCAD, so I can’t remember that many pages; I’ll be adding some resources that will help you get started with automation in AutoCAD. So far I’ve remembered the following: