You are on page 1of 6

Working with Environment Variables through AutoLISP

This sample follows that same trend and uses the Shell object to manipulate environment variables that are
maintained by the OS.  Below are three, yes I said three different examples of using the Shell object.  The
first shows how to expand environment variables in a string.  This can be great for building file paths or
getting information about the OS or PC.

;; Shows how to use expanding environment strings


;; Usage: (ExpEnvStr "%TEMP%\\MYDATA")
;; Results of sample: "C:\\DOCUME~1\\Lee\\LOCALS~1\\Temp\\MYDATA"
(defun ExpEnvStr ( strVal / wshShell)
  (vl-load-com)
  (setq wshShell (vlax-create-object "WScript.Shell"))
  (vlax-invoke-method wshShell 'ExpandEnvironmentStrings strVal)
)

;; Retreive the value of the environment variable


;; Usage: (GetEnvStr "SYSTEM" "USERID")
(defun GetEnvStr ( strVarType strVarName / wshShell envVars)
  (vl-load-com)
  (setq wshShell (vlax-create-object "WScript.Shell"))
  (setq envVars (vlax-get-property wshShell 'Environment strVarType))
  (vlax-get-property envVars 'Item strVarName)
)
;; Set the value to an environment variable
;; Usage: (SetEnvStr "SYSTEM" "USERID" "L123")
(defun SetEnvStr ( strVarType strVarName strVarVal / wshShell envVars)
  (vl-load-com)
  (setq wshShell (vlax-create-object "WScript.Shell"))
  (setq envVars (vlax-get-property wshShell 'Environment strVarType))
  (vlax-put-property envVars 'Item strVarName strVarVal))

Creating a New Document in AutoCAD with AutoLISP


Have you ever wanted to create a new document using a macro and tried to use the NEW command to do
this?  Using the NEW command might not be the best approach.  Below is a very basic custom function that
demonstrates how to create a new drawing based on a template through AutoLISP.
;; Written by: Lee Ambrosius
;; Command macro option ^P(createnewdwg "acadiso.dwt")
;; Load the Visual LISP environment
(vl-load-com)
;; Custom routine that allows you to create a new drawing
(defun createNewDWG (templateName)
  (vla-activate (vla-add (vla-get-documents (vlax-get-acad-object)) templateName))
(princ))

Printing a MS Word Doc using AutoLISP


It is possible to automate the printing process of not only your AutoCAD drawings, but also the spces that
might need to go out with them at the same time.  The code can be modified to specifiy a printer if need be,
but it is designed to go to the default printer though.
:: Begin Code
;; Written by: Lee Ambrosius, HyperPics LLC
;; Usage: (PrintMSWordDoc "C:\\test.doc")
;; Open a document in MS Word and Print it out
(defun PrintMSWordDoc ( strWordDoc / wordObj wordDocsObj wordDocObj)
  (vl-load-com)
  ;; Variable to define constant used in the Close and Quit methods
  (setq wdDoNotSaveChanges 0)
  ;; Create a new MS Word object
  (setq wordObj (vlax-invoke-method (vlax-get-acad-object) 'GetInterfaceObject "Word.Application"))
  ;; Get a reference to the Documents collection in Word
  (setq wordDocsObj (vlax-get-property wordObj 'Documents))
  ;; Open the drawing
  (setq wordDocObj (vlax-invoke-method wordDocsObj 'Open strWordDoc))
  ;; Print it out
  (vlax-invoke-method wordDocObj 'PrintOut)
  ;; Close the document and don't save any changes
  (vlax-invoke-method wordDocObj 'Close wdDoNotSaveChanges)
  ;; Close the instance of the MS Word application
  (vlax-invoke-method wordObj 'Quit wdDoNotSaveChanges)
(princ))

.  Which Drives are Available?


From time to time you might want your AutoLISP application to be able to write to a specific drive or drives
in a specific order if you are working with a group of users that might do some travelling.  If you want to test
to see which drives are available through AutoLISP, you could use the function vl-directory-files.  This
would force you to test each letter combination which can take time and it doesn't tell you if the drive is
ready or not.  Below is an example using the FSO object to get all the available drives on the machine, and
test to see what type of drive it is and if it is ready or not.
;; Begin Code
(defun GetDrives ( / wshFSO driveObjs)
  (vl-load-com)
  (if (= wshLibImport nil)
    (progn
      (vlax-import-type-library :tlb-filename "c:\\windows\\system32\\wshom.ocx"
                          :methods-prefix "wshm-"
                          :properties-prefix "wshp-"
                          :constants-prefix "wshk-"
      )
      (setq wshLibImport T)
  ))
  (setq wshFSO (vlax-create-object "Scripting.FileSystemObject"))
  (setq driveObjs (wshp-get-Drives wshFSO))
  (vlax-for driveObj driveObjs
    (progn
      (prompt (strcat "\nDrive Letter: " (vlax-get-property driveObj 'DriveLetter)))
      (prompt "\nDrive Type: ")
      (cond
        ((= (vlax-get-property driveObj 'DriveLetter) wshk-CDRom)(prompt "CDROM"))
        ((= (vlax-get-property driveObj 'DriveLetter) wshk-RamDisk)(prompt "Ram Disk"))
        ((= (vlax-get-property driveObj 'DriveLetter) wshk-Fixed)(prompt "Fixed"))
        ((= (vlax-get-property driveObj 'DriveLetter) wshk-Removable)(prompt "Removeable"))
        ((= (vlax-get-property driveObj 'DriveLetter) wshk-UnknownType)(prompt "Unknown"))
      )
      (prompt (strcat "\nIs Ready: "))
      (if (= (vlax-get-property driveObj 'IsReady) :vlax-true)
        (prompt "True")
        (prompt "False"))))
(princ))
;; End Code

Obtaining a Short File Name through LISP


  The sample shows how to use the File System Object.
;; Begin Code
(defun GetShortName ( strFileName / wshFSO fileObj)
  (vl-load-com)
  (setq wshFSO (vlax-create-object "Scripting.FileSystemObject"))
  (setq fileObj (vlax-invoke-method wshFSO 'GetFile strFileName))
  (vlax-get-property fileObj 'ShortPath)
)
;; End Code

+++++++++++++++++++++++++++++++++
tried writing a routine that creates a dual offset of a line and gives the option of erasing the original, I
would like to get it if you choose not to erase the original that it will change the layer automatically.
:;;MULTIPLE OFFSET OF LINES WITH OPTIONAL REMOVAL OF ORIGINAL LINE
(defun c:moffset (/ ent dist obj kwrd)
(vl-load-com)
(while (not ent)
(if (eq (setq ent (car (entsel "\n Please select line to multi-offset: "))) nil)
(princ "\nTHe object you have chosen is not a line! Please select a line to multi-offset: ")))
(initget (+ 1 2 4 64))
(setq dist (getdist "\nEnter offset distance from centerline: "))
(initget (+ 2 4) "Yes No")
(setq kwrd (getkword "\nDo you wish to put selected line on Centerlines layer? [Yes/No] : "))
(if (/= kwrd "No")(setq kwrd "Yes"))
(setq obj (vlax-ename->vla-object ent))
(vla-offset obj dist)
(vla-offset obj (* dist -1))
(if (eq kwrd "Yes")(vlax-put-property ent 'Layer Centerlines))
(princ)
)

++++++++++++++++++++++++++
i mean by just clicking the POLYLINE & it will automatically gets all the X & Y coordinates (i mean all the corners) &
put it automatically on the TABLE.
(defun c:tabord(/ aCen cAng cCen cPl cRad cReg
fDr it lCnt lLst mSp pCen pT1
pT2 ptLst R tHt tLst vlaPl vlaTab
vLst cTxt oldCol nPl clFlg *error*)

(vl-load-com)
(defun Extract_DXF_Values(Ent Code)
(mapcar 'cdr
(vl-remove-if-not
'(lambda(a)(=(car a)Code))
(entget Ent)))
); end of

(defun *error*(msg)
(setvar "CMDECHO" 1)
(princ)
); end of *error*
(if
(and
(setq cPl(entsel "\nSelect LwPoliline > "))
(= "LWPOLYLINE"(car(Extract_DXF_Values(car cPl)0)))); end and
(progn
(setq vlaPl(vlax-ename->vla-object(car cPl))
ptLst(mapcar 'append
(setq vLst(Extract_DXF_Values(car cPl)10))
(mapcar 'list(Extract_DXF_Values(car cPl)42)))
lLst '("A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
"N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z")
r 2 lCnt 0
tLst '((1 0 "Point")(1 1 "X")(1 2 "Y")(1 3 "Radius"))
mSp(vla-get-ModelSpace
(vla-get-ActiveDocument
(vlax-get-acad-object)))
tHt(getvar "TEXTSIZE")
); end setq
(setvar "CMDECHO" 0)
(foreach vert ptLst
(setq vert(trans vert 0 1)
tLst(append tLst
(list(list r 0 (nth lCnt lLst))
(list r 1(rtos(car vert)2 4))
(list r 2(rtos(cadr vert)2 4))
(list r 3 ""))))
(if(and
(/= 0.0(last vert))
(setq pt1(vlax-curve-GetPointAtParam vlaPl lCnt))
(setq pt2(vlax-curve-GetPointAtParam vlaPl(1+ lCnt)))
); end and
(setq r(1+ r)
cRad(abs(/(distance pt1 pt2)
2(sin(/(* 4(atan(abs(last vert))))2))))
aCen(vlax-curve-GetPointAtParam vlaPl(+ 0.5 lCnt))
fDr(vlax-curve-getFirstDeriv vlaPl
(vlax-curve-getParamAtPoint vlaPl aCen))
pCen(trans
(polar aCen(-(if(minusp(last vert)) pi(* 2 pi))
(atan(/(car fDr)(cadr fDr))))cRad)0 1)
tLst(append tLst(list
(list r 0 "center")
(list r 1(rtos(car pCen)2 4))
(list r 2(rtos(cadr pCen)2 4))
(list r 3(rtos cRad 2 4))))
); end setq
); end if
(setq r(1+ r) lCnt(1+ lCnt))
); end foreach
(setq vlaTab(vla-AddTable mSp (vlax-3D-point '(0 0 0))
(+ 1(/(length tLst)4)) 4 (* 3 tHt)(* 18 tHt)))
(foreach i tLst
(vl-catch-all-apply 'vla-SetText(cons vlaTab i))
(vla-SetCellTextHeight vlaTab(car i)(cadr i)tHt)
(vla-SetCellAlignment vlaTab(car i)(cadr i)acMiddleCenter)
); end foreach
(vla-DeleteRows vlaTab 0 1)
(princ "\n<<< Place Table >>> ")
(command "_.copybase" (trans '(0 0 0)0 1)(entlast) "")
(command "_.erase" (entlast) "")
(command "_.pasteclip" pause)
(if(= :vlax-true(vla-get-Closed vlaPl))
(progn
(setq nPl(vla-Copy vlaPl))
(command "_.region" (entlast) "")
(setq cCen(vlax-get(setq cReg
(vlax-ename->vla-object(entlast)))'Centroid))
(vla-Delete cReg)
(setq clFlg T)
); end progn
); end if
(setq lCnt 0)
(foreach v vLst
(if clFlg
(setq cAng(angle cCen(trans v 0 1))
iPt(polar v cAng (* 2 tHt)))
(setq fDr(vlax-curve-getFirstDeriv vlaPl
(vlax-curve-getParamAtPoint vlaPl v))
iPt(trans
(polar v(-(* 2 pi)(atan(/(car fDr)(cadr fDr))))
(* 2 tHt))0 1)
); end if
); end if
(setq cTxt(vla-AddText mSp(nth lCnt lLst)
(vlax-3d-point iPt) tHt)
lCnt(1+ lCnt)
); end setq
(setq oldCol(getvar "CECOLOR"))
(setvar "CECOLOR" "1")
(command "_.circle" v (/ tHt 3))
(setvar "CECOLOR" oldCol)
); end foreach
(setvar "CMDECHO" 1)
); end progn
(princ "\n<!> It isn't LwPolyline! Quit. <!> ")
); end if
(princ)
); end of c:tabord

Regions don't have normals, alas -- no (210 ...) code, anyway. They seem to be treated as super-thin 3Dsolids, always
in the WCS regardless of 3d rotation, rather than planar objects like polylines or ellipses.
You can get the normal using ActiveX:

(defun get-normal (obj)


(if (vlax-property-available-p obj 'Normal)
(vlax-get obj 'Normal)))

(get-normal (vlax-ename->vla-object (car (entsel))))


Great -- that will do it. I already have a point on the object -- (setq pt (osnap (cadr (entsel)) "_near")), so that plus a
normal defines the UCS I need.

With actDoc already set with (vla-get-activedocument ...), and newOrig set to the WCS coordinates of the point
picked on the region, this sets the current UCS to the plane of the selected region:
( (eq entype "REGION" )
(setq normal (vlax-get obj 'Normal))
(setq regUCS ;;create UCS parallel to the region
(vla-add
(vla-get-usercoordinateSystems actdoc)
(vlax-3D-point (trans '(0 0 0) normal 0))
(vlax-3D-point (trans '(1 0 0) normal 0))
(vlax-3d-point (trans '(0 1 0) normal 0))
"zztemp02"))
(vla-put-origin regUCS (vlax-3d-point newOrig 0 1)) ;;move
UCS origin to point picked on region
(vla-put-activeUCS actDoc regUCS) ;;update UCS
...
+++++++++++++++++++++++
I'm drawing a circle on Y-Z plane.
My questions about this are:
1) How to write it in the code;
2) What is the best way to do this,
for example
a) draw a circle on X-Y plane normally then do 3drotate;
b) rotate the view to Y-Z plane then draw circle;
c) ...

What you will want to study is transformation matrices. http://www.theswamp.org/index.php?


topic=13526.0 ]
If you want to use ActiveX, it's quite the same way with the Normal property except the center point has to
be WCS coordinates. You can easily transform OCS coordinates to WCS coordinates with the trans function
using the normal vector:
(trans '(10. 20. 0.) '(1. 0. 0.) 0) returns (0.0 10.0 20.0)

(vla-put-Normal
(vla-addCircle
(vla-get-ModelSpace
(vla-get-ActiveDocument
(vlax-get-acad-object))))
(vlax-3d-point '(0. 10. 20.)) 5.); WCS coordinates
(vlax-3d-point '(1. 0. 0.))) ; Normal

Another way, shoud be to use a transformation matrix to transform a circle drawn in WCS plane to YZ
plane.

;;; WCS2Norm (Thanks to Doug Broad)


;;; Returns the 4X4 transformation matrix from WCS to an OCS defined by its Normal

(defun WCS2Norm (norm)


(append
(mapcar
(function
(lambda (vec org)
(append (trans vec 1 0 T) (list org))))
(list '(1 0 0) '(0 1 0) '(0 0 1))
(trans '(0 0 0) 0 norm))
(list '(0 0 0 1))))

;;; draw a circle in WCS XY plane

(setq circle
(vla-addCircle
(vla-get-ModelSpace
(vla-get-ActiveDocument
(vlax-get-acad-object)))
(vlax-3d-point '(10. 20. 0.)); WCS point
5.))

;;; Transform the circle to YZ plane


(vla-TransformBy circle (vlax-tmatrix (WCS2Norm '(1. 0. 0.))))

If you don't want to use the command method (rotating UCS), the simpler way is the one told by Tim: using
210 DXF code.
The 210 DXF code is the object 'extrusion direction' IOW: the normal of its plane.
The normal vector of YZ plane is 1.0, 0.0, 0.0 So, using entmake to draw a circle in YZ plane, which center
is 10.0, 20.0, 0.0 (about this plane, IOW: expressed in OCS coordinates) and radius = 5.0, you just have to
specify the 210 DXF code in the entmake list.

(entmake
'((0 . "CIRCLE") (8 . "Calque1")
(10 10. 20. 0.) ; OCS coordinates
(40 . 5.) (210 1. 0. 0.) ; Normal
)

You might also like