Search the FAQ Archives

3 - 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 - Internet FAQ Archives

comp.cad.autocad AutoLISP FAQ (part 2/2) - samples, code

( Part1 - Part2 - MultiPage )
[ Usenet FAQs | Web FAQs | Documents | RFC Index | Airports ]
Posted-By: auto-faq
Archive-name: CAD/autolisp-faq/part2
Version: 2.28
Last-modified: 2002-06-25
Posted-By: Reini Urban <>
Posting-Frequency: monthly
Copyright: see Appendix [A]

See reader questions & answers on this topic! - Help others by sharing your knowledge
        Welcome to the comp.cad.autocad AutoLISP FAQ
          by Reini Urban <>

AutoLISP is a scripting language for AutoCAD, a well known CAD package.
This AutoLISP FAQ is posted to comp.cad.autocad, alt.cad.autocad and
the *.answers groups monthly. Some AutoCAD FAQ's are at but not posted to
comp.cad.autocad. The contents and the samples apply to all
releases of AutoLISP since Release 10, including Visual Lisp, Vital Lisp
and ACOMP. There's no special AutoLISP newsgroup.
Best are comp.cad.autocad and autodesk.autocad.customization,
but please don't bother comp.lang.lisp.
Source code of all functions in this FAQ is in FAQ-CODE.LSP
(for location see [A.1]), there's also a Winhelp file.
Thanks to all who have contributed. Corrections and contributions
always welcome. 
Please see

This is part 2/2 of the AutoLISP FAQ v2.28, which consists of:

    AutoLISP FAQ (part 1/2) - General
    AutoLISP FAQ (part 2/2) - Samples, code

| changes, + new in items from this version to the last posted version,
intermediate personal comments and uncertainties in <..>

  Table of Contents
  part 1:   General
     [0] The Future of AutoLISP? Should I learn it or VB instead?
       [0.1] What changed with AutoCAD 2000?
       [0.2] We cannot create ARX anymore?
     [1] Where can I find AutoLISP routines on the Internet?
       [1.1] Are the comp.cad.autocad articles stored somewhere?
       [1.2] Autodesk's SDK
      [2] What are the best books to learn AutoLISP?
       [2.1] Online AutoLISP documents, Winhelp [deleted]
       [2.2] AutoLISP Coding Style
     [3] How do I debug AutoLISP programs?
        [3.1] Native AutoLISP debuggers
        [3.2] Modular style, TRACE
        [3.3] BREAK function, debug-print
     [4] How can I protect my AutoLISP programs? Security
       [4.1] Kelvinate
       [4.2] Protect
       [4.3] Kelvinate and Protect
       [4.4] Convert
       [4.5] ACOMP
       [4.7] Lisp2C
       [4.6] Vital LISP Professional
       [4.8] Visual Lisp by Autodesk
     [5] AutoLISP compilers
       [5.1] ACOMP
       [5.2] Vital LISP Professional
       [5.3] Visual Lisp by Autodesk
       [5.4] Better ones: Common Lisp and Scheme
     [6] AutoLISP editors and other tools
       [6.1] AutoLISP editors
       [6.2] Analyzers, Packager and Parenthesis checkers
       [6.3] Pretty Printers
     [7] AutoLISP problems and bugs
     [8] Sorting with AutoLISP
     [9] Recursion
     [10] Iteration with MAPCAR,...
|    [11] S::STARTUP, My LISPs aren't loading at startup anymore
     [12] How to AUTOLOAD my programs?
     [13] How can I pass a variable number of
          arguments to a LISP function?
|    [14] How can I avoid stack overflows?
     [15] (command "ROTATE3D") does not work! Why?
     [16] Lisp programs operating over multiple drawings
     [17] How to export Visual Lisp functions to AutoLISP/AutoCAD?
     [A] Disclaimer, Notes from the authors
       [A.1] FAQ Locations

  part 2:   Samples, code
     [20] General Helper functions
       [20.1] List manipulation
       [20.2] String manipulation
       [20.3] symbol->string
       [20.4] AutoCAD entity access
     [21] Sample Lisp programs
       [21.1] Globally change text, polylines, layer utils, date stamp
       [21.2] Plot dialog from within LISP. Using DDE or ActiveX
       [21.3] (entmod),(entmake) Layers, without (command "_LAYER"...)
       [21.4] How to select multiple files in LISP? (as in FILES-Unlock)
       [21.5] Replace multiple blocks
       [21.6] (vports), VIEWPORT entity, pixel conversion
       [21.7] Select all visible objects: zoom coordinates
       [21.8] How to write XYZ data of selected objects to a file?
     [22] Block Attributes
       [22.1] How to access block attributes?
       [22.2] How to MODIFY block attributes? DATESTAMP.LSP
       [22.3] How to UPDATE block attributes?
       [22.4] How to ENTMAKE a Block Complex Entity in AutoLISP
     [23] Polylines
       [23.1] How to access polyline VERTICES?
       [23.2] How to JOIN multiple lines to polylines?
       [23.3] Change WIDTH of multiple polylines
       [23.4] Create a polyline or spline: with (ENTMAKE) or (COMMAND)
       [23.5] How to calculate the LENGTH of polylines?
       [23.6] How to revert the polyline direction?
       [23.7] How to get the CENTER of a polyline?
     [24] Circle/Arc Geometry:  BULGE conversion, some trigonometry
     [25] DCL: listboxes with tabs or monotext font
     [26] EED Extended Entity Data: Get and Store
       [26.1] Select objects on their EED with (ssget "X")
       [26.2] Get EED from an object
     [27] How to break a command in LISP?
       [27.1] How to do an unlimited number user prompts?
     [28] How to decode ACIS internal geometry with LISP
     [A] Disclaimer, Notes from the author
+      [A.1] FAQ Locations
     [B] Acknowledgements
     [C] Recent Changes

Subject: [20] General helper functions For more general AutoLISP functions please see the AutoLISP Standard Library at Other code is also available at some AutoLISP sites [1] or included in the SDK's by AutoDESK [1.2] I included here some very useful helper functions for shorter samples in answers on the net. You could rely on the fact that these functions are in common knowledge such as the famous dxf function, which is defined as (defun dxf (grp ele) (cdr (assoc grp ele))) and the specific counterpart (getval) which works with either an ename or entget list. [20.1] List manipulation See also Useful sample functions for *list manipulation* are: ;;; a not empty list? (defun CONSP (x) (and x (listp x))) ;;; returns the index of the first element in the list, ;;; base 0, or nil if not found ;;; (position 'x '(a b c)) -> nil, (position 'b '(a b c d)) -> 1 (defun POSITION (x lst / ret) (if (not (zerop (setq ret (length (member x lst))))) (- (length lst) ret))) ;;; Removes an item from a list (double elements allowed) ;;; (remove 0 '(0 1 2 3 0)) -> (1 2 3) (defun REMOVE (ele lst) ; by Serge Volkov (apply 'append (subst nil (list ele) (mapcar 'list lst)))) ;;; Conditional remove from flat list, ;;; pred requires exactly 1 arg ;;; (remove-if 'zerop '(0 1 2 3 0)) -> (1 2 3) ;;; (remove-if 'numberp '(0 (0 1) "")) -> ((0 1) "") (defun REMOVE-IF (pred from) (cond ((atom from) from) ;nil or symbol (return that) ((apply pred (list (car from))) (remove-if pred (cdr from))) (t (cons (car from) (remove-if pred (cdr from)))) ) ) ;;; Keeps all elements to which the predicate applies ;;; Say: "keep if", it need not be defined recursively, also like this. ;;; [fixed, thanks to Serge Pashkov, in FAQ-CODE.LSP it was okay] (defun REMOVE-IF-NOT (pred lst) ; by Vladimir Nesterowsky (apply 'append (mapcar '(lambda (e) (if (apply pred (list e)) (list e))) lst))) ;;; Conses ele to list if not already in list ;;; Trick: Accepts quoted lists too, such as ;;; (setq l '(1 2 3) (adjoin 0 'l) ;;; -> !l (0 1 2 3) (defun ADJOIN (ele lst / tmp) (if (= (type lst) 'SYM) (setq tmp lst lst (eval tmp))) (setq lst (cond ((member ele lst) lst) (t (cons ele lst)))) (if tmp (set tmp lst) lst) ) ;;; put the first element to the end, simple version ;;; ("rotate by one") (defun ROT1 (lst) (append (cdr lst) (list (car lst)))) ;;; the list without the last element (defun BUTLAST (lst) (reverse (cdr (reverse lst)))) ------------------------------ [20.2] String manipulation Please check Some useful *string functions* would be: Predicates: (stringp expr) - string predicate, is expr a string? (defun stringp (s) (= (type s) 'STR)) (string-not-emptyp str) - is str a not empty string? (defun string-not-emptyp (s) (and (stringp s) (/= s ""))) Trimming: (str-trim string) - str without any whitespace, to the right and left, defined in AI_UTILS.LSP as well as (str-left-trim string), (str-right-trim string) (str-left-trim-bag string bag), (str-right-trim-bag string bag) - remove all chars in bag (=STR) Search: (strpos string substr) - position of substring in string (1 based) Parsing and gathering functions, (list<->string): (strtok str tokens) - string -> list delimited by tokens (SDK2) (strlcat lst delim) - concat list -> string seperated by delim (string->list str) - string -> list of chars (list->string lst) - list of chars -> string All of them and much more are in the Stdlib (see above). Some are at or in your AI_UTILS.LSP. You'll need them esp. for DCL functions. ------------------------------ [20.3] symbol->string The inverse function to (read) would be (symbol-name). The following is the only general way, but there exist better special methods. ;;; SYMBOL-NAME - returns the name of a symbol as string ;;; converts any valid lisp expression to its printed representation ;;; (symbol-name a) -> "a", (symbol-name '(0 1 2 a)) -> "(0 1 2 A)" (defun SYMBOL-NAME (sym / f str tmp) (setq tmp "$sym.tmp") ;temp. filename, should be deleted (setq f (open tmp "w"))(princ sym f) (close f) (setq f (open tmp "r") str (read-line f) f (close f)) str) For plain symbols exists a better trick explained by Christoph Candido at Vill/VLISP introduced a fast vl-symbol-name. See also ------------------------------ [20.4] AutoCAD entity access [renamed SSAPPLY to SSMAP] See also ;;; returns the first group value of an entity. ;;; like the wellknown (dxf) function but accepts all kinds of ;;; entity representations (ename, entget list, entsel list) ;;; NOTE: For getting 10 groups in LWPOLYLINE's not usable! (defun GETVAL (grp ele) ;"dxf value" of any ent... (cond ((= (type ele) 'ENAME) ;ENAME (cdr (assoc grp (entget ele)))) ((not ele) nil) ;empty value ((not (listp ele)) nil) ;invalid ele ((= (type (car ele)) 'ENAME) ;entsel-list (cdr (assoc grp (entget (car ele))))) (T (cdr (assoc grp ele))))) ;entget-list ;;; Ex: (gettyp pline) => "POLYLINE" (defun GETTYP (ele) ;return type (getval 0 ele)) ;;; assure ENAME ;;; convert the entity to type ENAME (to write shorter code) (defun ENTITY (ele) ;convert to element name (cond ;accepts the following types: ((= (type ele) 'ENAME) ele) ; ENAME ((not (listp ele)) nil) ; error: no list ((= (type (car ele)) 'ENAME) (car ele)) ; entsel-list ((cdr (assoc -1 ele))) ; entget-list or nil ) ) ;and now just: (defun getval (grp ele) (cdr (assoc grp (entget (entity ele))))) ;;; Ex: (istypep ele "TEXT") ;;; is element a "SOLID"? (defun istypep (ele typ) ;check type (= (gettyp ele) typ)) ;;; Ex: (istypep ele '("TEXT" "ATTDEF")) ;;; is element a "TEXT" or a "ATTDEF"? (defun ISTYPEP (ele typ) ;better implementation to accept lists too (cond ((listp typ) (member (gettyp ele) typ)) ;bugfixed ((stringp typ) (= (gettyp ele) typ)) ;assume typ uppercase (T nil))) ;;; Ex: (getpt (entsel)) => ( 0.1 10.0 24) (defun GETPT (ele) ;return the startpoint of any element (getval 10 ele)) ;group 10 ;;; Ex: (getflag pline) => 1 if closed (defun GETFLAG (ele) (getval 70 ele)) ;same with the entity flag ;;; bitvalue val in flag of element set? ;;; Ex: (flagsetp 1 pline) => T if closed ;;; Ex: (flagsetp 16 vertex) => T if spline control point (defun FLAGSETP (val ele) (bitsetp val (getflag ele))) ;;; Ex: (bitsetp 4 12) => T ;bitvalue 4 (=2.Bit) in 12 (=4+8) is set (defun BITSETP (val flag) (= (logand val flag) val)) ;;; convert selection set to list, ;;; Note: it's also wise to use ai_ssget, because some ents could be ;;; on locked layers ;;; Ex: (sslist (ai_ssget (ssget))) => list of selected unlocked ents ;;; or (mapcar 'entupd (sslist (ssget "X" '((8 . "TEMP"))))) ;;; - regens all entities on layer TEMP (defun SSLIST (ss / n lst) (if (= (type ss) 'PICKSET) (repeat (setq n (sslength ss)) (setq n (1- n) lst (cons (ssname ss n) lst))))) ;;; apply a function to each ent in ss, in reversed order ;;; Faster, but not so easy to understand. see [22.2] ;;; [renamed from SSAPPLY to SSMAP to match the stdlib name] ;;; Ex: (ssmap 'entupd (ssget)) ; regenerate only some entities (defun SSMAP (fun ss / n) (if (= 'PICKSET (type ss)) (repeat (setq n (sslength ss)) (apply fun (list (ssname ss (setq n (1- n))))))))
Subject: [21] Sample Lisp Programs: [21.1] Globally change texts, polylines, layer utils, datestamp For globally changing text attributes use CHTEXT.LSP in your sample directory. For globally changing polyline attributes, freeze layers by pick and other similar tasks search for free lisp tools at any AutoLISP site. See "[1]" and some code at "[22]","[23]","[24]" For putting a datestamp and others onto your plots automatically first check out if your plotter supports HPGL/2. Then use the internal HPGL/2 driver and configure the datestamp in HPCONFIG. DATESTAMP.LSP: Change the plot header attributes by yourself as in [22.2]. A profi plotstamp routine is here: ------------------------------ [21.2] Plot dialog from within Lisp. Using DDE or ActiveX or initdia (initdia)(command "_PLOT") Calling the PLOT dialogbox from AutoLISP *before R14* was possible only under Windows i.e. with LISPPLOT by Mike Dickason. This fed the keyboard buffer with keystrokes. files/lispd-l/ or also: Otherwise create a script and call this at the end of your lisp, but this will not show up the dialogbox. Xiang Zhu: You could have used "DDELISP" under Windows. [shortened] ;;; [fixed for all releases] (defun DDECMD (str / tmp acadver ddestr) (if (not (boundp 'initiate)) (cond ((= 14 (setq acadver (atoi (getvar "ACADVER")))) (setq ddestr "AutoCAD.R14.DDE") (arxload "ddelisp")) ((= 13 acadver) (setq ddestr "autocad.r13.dde") (xload "ddelisp")) ((= 12 acadver) (setq ddestr "autocad.dde") (xload "ddelisp")) (T (princ "DDE not supported")(exit)))) (if (not (zerop (setq tmp (initiate ddestr "system")))) (progn (execute tmp (strcat "[" str "]")) (terminate tmp) str))) (ddecmd "_plot ") ; return would be "^13" Beware that Acad accepts only DDE commands if the command line is active, that means no dialogbox must be open. With vlisp/ViLL ActiveX methods can be used to plot, but the dialog can not be called: ;;; vlisp syntax: (setq vlax:ActiveDocument (vla-get-ActiveDocument (vlax-get-Acad-Object))) (setq plt (vla-get-plot vlax:ActiveDocument)) ;=> plot object (vla-PlotWindow plt pt1 pt2) ;WCS pts (vla-PlotPreview plt 1) ;0; partial, 1: full (vla-PlotToDevice plt "Default System Printer") ;if it exists With R14 INITDIA was introduced, which can be applied to most but not all dialogs: (initdia)(command "_PLOT") With A2000 use OLE (VLA- methods) instead of DDE. DDE support is discontinued by Microsoft. OLE provides a better object-oriented interface. ------------------------------ [21.3] (entmod) and (entmake) Layers, without (command "_LAYER"...) ENTMOD a layer I try to change a layer property without calling COMMAND function inside a lisp routine. Since R13, using the following lisp (setq tbl_lst (entget (tblobjname "LAYER" "ANY_LAYER")) clr_grp (assoc 62 tbl_lst) ) (entmod (subst (cons 62 (- (cdr clr_grp))) clr_grp tbl_lst)) you can toggle "ANY_LAYER" On or Off, even it is the current layer. But AutoCAD doesn't know a table entry has been changed until you click the Layer Control on the toolbar or something similar. Besides, you can issue 'DDLMODES to see On/OFf property of "ANY_LAYER" changed. Doing the same way to freeze a layer, you will still see entities on that layer shown on screen, but you can not select them, until you do something related to layer settings, and AutoCAD will hide those entities. ENTMAKE a layer You must get your pattern with entget, using the table object name as argument. This table object name can be retrieved with the TBLOBJNAME function: (entget (tblobjname "LAYER" "Any Layer Name")) ; R2000 can have spaces! ;;; This routine will create a layer with any name you type: (defun C:MLAY () ; by Reinaldo Togores <> (setq laynam (getstring "\nLayer name: ")) (entmake (list '(0 . "LAYER") '(5 . "28") '(100 . "AcDbSymbolTableRecord") '(100 . "AcDbLayerTableRecord") (cons 2 laynam) '(70 . 64) '(62 . 7) '(6 . "CONTINUOUS") ))) ------------------------------ [21.4] How to select multiple files in Lisp? (as in FILES - Unlock) [new] * DOSLIB v4.3 from McNeel contains dos_getfilem, * STDLIB contains std-getfilem * At is another lisp helper routine to select multiple files with DCL. You will also need VLISP, DOSLIB or the STDLIB to access the directory functions. Another lisp version is at ------------------------------ [21.5] Replace multiple blocks A search at the lisp archives yielded those hits: Cadalyst: => 97code.htm and a question for your username which can be obtained free and automatically or xarch: and search for "BLOCK;REPLACE" => also at the Cadalog: OpenSource Freeware Keyword: "Block Replace" => (this one is the best) ------------------------------ [21.6] (vports), VIEWPORT entity, pixel conversion VIEWPORT entity: The answer to "I can do an (entget) on a VIEWPORT and get its lower left (DXF group 10) and upper right (DXF group 11) corner. But it appears that these coordinates are in the paper space system. What I'm interested in finding out is what portion of the "real" drawing (the model space drawing) are currently shown in that viewport." is at has also some tricks. How to change viewports in AutoLISP? with (setvar "CVPORT" vport-id) see With the following functions you convert pixel<->drawing units: ;;; Conversion pixel to drawing units (defun PIX2UNITS (pix) (* pix (/ (getvar "VIEWSIZE") (cadr (getvar "SCREENSIZE"))))) ;;; Conversion drawing units to pixel (defun UNITS2PIX (units) (* units (/ (cadr (getvar "SCREENSIZE"))(getvar "VIEWSIZE")))) Note also the "Pixel Off by One" Errors in AutoCAD, written by Vibrant ------------------------------ [21.7] Select all visible objects: zoom coordinates Beware that with (ssget) you will only get visible objects, because all interface functions (entsel,ssget,osnap) work with pixel, only (ssget "X") will select not visible objects. ;;; returns a list of the actual viewport corners in WCS (defun ZOOMPTS ( / ctr h screen ratio size size_2) (setq ctr (xy-of (getvar "VIEWCTR")) ;3D -> 2D h (getvar "VIEWSIZE") ;real screen (getvar "SCREENSIZE") ;2D: Pixel x,y ratio (/ (float (car screen)) ;aspect ratio (cadr screen)) size (list (* h ratio) h) ;screensize in coords size_2 (mapcar '/ size '(2.0 2.0))) (list (mapcar '- ctr size_2) (mapcar '+ ctr size_2))) (defun XY-OF (pt) (list (car pt)(cadr pt))) ;assure 2D coords Note: The points returned are in WCS but this is ok, because the "CP" "WP" and "P" options of ssget expect WCS points. "W" and "C" require UCS points - why the difference I don't know. ;;; one way to define this function (defun SSALL-VISIBLE (/ l) (ssget "C" (car (setq l (maptrans0-1 (zoompts)))) (cadr l))) ;;; or another (defun SSALL-VISIBLE-1 () ;combine "C" and (p1 p2) to one list (apply 'ssget (append '("C") (maptrans0-1 (zoompts))))) ;;; map some pts from WCS to UCS, easier with just one argument (defun MAPTRANS0-1 (pts) (mapcar '(lambda (pt)(trans pt 0 1)) pts))
Subject: [21.8] How to write XYZ data of selected objects to a file? ;;; CDF - comma delimited string (defun CDF-POINT (pt) (strcat (car pt) ", " (cadr pt) ", " (caddr pt))) ;;; SDF - space delimited, may easier be read back in to AutoCAD (defun SDF-POINT (pt) (strcat (car pt) " " (cadr pt) " " (caddr pt))) ;;; convert this SDF format back to a point with (defun STR->POINT (s) (eval (read (strcat "(" s ")")))) ;;; Write a XYZ file of all selected objects (SDF see below) (defun C:XYZ (/ ss fname f) (if (and (setq ss (ssget)) (setq fname (getfiled "Write XYZ to file" (strcat (getvar "DWGNAME") ".XYZ") "XYZ" 7)) (setq f (open fname "w"))) (foreach ele (sslist ss) ; -> [20.4] (foreach pt (getpts ele) ; -> [23.1] (write-line (cdf-point pt) f)))) (if f (close f))) ;;; => <fname>.xyz ;;; 0.45, 12.3, -34.0 For a ASC file (SDF-format) simply change all XYZ to ASC and cdf-point to sdf-point above. For the other way 'round, creating PLINES from a ascii x,y file best convert the file to a script like: PLINE 300.2,10 350.4,10.4
Subject: [22] Block Attributes [22.1] How to access block attributes? Check all subentities after the INSERT until the attribute is found. See ;;; returns entget-list of attribute attname (STRING) in element ele ;;; or nil if not found (defun ATTELE (ele attname / rslt) (if (and (istypep ele "INSERT") (= (getval 66 ele) 1)) (progn (setq ele (entnext (entity ele))) (while (istypep ele "ATTRIB") (if (= (strcase (getval 2 ele)) (strcase attname)) (setq rslt (entget ele) ele nil) ;break the loop (setq ele (entnext ele)))))) rslt ) ;;;Example: (attele (entsel) "TEST") ; returns entget-list of ; attribute "TEST" if the block has it BTW: Even trickier functions to get entities DXF group codes are GET and EDLGETENT by Vladimir Nesterowsky. ;;;Sample calls: ;;; return list of 2,1 and -1 group values (defun GET-ATTRIBS-LOOK-UP (block-ename) (get '(2 1 -1) (cdr (edlgetent block-ename)))) (defun ALL-VERTICES-AND-BULGES (pline-ename) (get '(10 42) (cdr (edlgetent pline-ename)))) available at ------------------------------ [22.2] How to MODIFY block attributes? DATESTAMP.LSP For a very simple DATESTAMP.LSP simply entmod the entget-list of the DATE attribute in your plotstamp block, retrieved from (attele) as above. ;;; change the attribute value of INSERT ele to new (group 1) (defun ATTCHG (ele attname new / b) (if (setq b (attele ele attname)) (entmod (subst (cons 1 new) (getval 1 b) b)))) ;;; Change all DATESTAMP attributes in all inserted PLOT* blocks (defun C:DATESTAMP () (ssmap ;fixed by Alan Williams, wrong arg order '(lambda (ele) (attchg ele "DATESTAMP" (today)) (entupd ele) ) (ssget "X" '((0 . "INSERT")(2 . "PLOT*"))))) ;;;return todays date, could be a DIESEL or this string conversion (defun TODAY (/ s) (setq s (rtos (getvar "CDATE") 2)) ;gets the julian date (strcat (substr s 5 2) "-" (substr s 7 2)"-"(substr s 3 2))) Automatic datestamps are normally done with either RTEXT (bonus/express tools) or HPCONFIG with a HPGL/2 plotter. (<a2000i) ------------------------------ [22.3] How to UPDATE block attributes? There exists a support/ATTREDEF.LSP to update attribute properties (position, layer, ...) for already inserted blocks. On complex entities you must (entupd) the header entity, to see the update on the screen (forces an element REGEN). ;;; Example: (setq s (getstring "Change Attribute to: ")) (attchg (attele (setq b (entsel "of block: ")) s))) (entupd (car b)) ; the block, not the attribute ;;; some more helper funcs to get the main entity of any attribute ;;; or vertex (defun MAIN-ENTITY (ele) (setq b (entity b)) ;force ENAME (while (istypep b '("ATTRIB" "ATTDEF" "VERTEX")) (setq b (entnext b))) ;loop until no more sub-ents (if (istypep b '("SEQEND" "ENDBLK")) (getval -2 b) ;complex entity -> header b ;normal entity ) ) ------------------------------ [22.4] How to ENTMAKE a Block Complex Entity in AutoLISP See or (broken) There is an example how to use multiple calls to (entmake) to create the block header, the entities, closes the block and finally (entmake) the INSERT. For anonymous blocks beware that only (setq bn (entmake '((0 . "ENDBLK")))) returns the blockname for (entmake (list '(0 . "INSERT")'(70 . 1)(cons 2 bn) ...))
Subject: [23] Polylines Since R14 LWPOLYLINE's store the vertices in one entity as multiple 10 groups. So (assoc) will not work. [23.1] How to access polyline VERTICES? [updated for R14] A polyline VERTEX is a subentity of a POLYLINE (same as an ATTRIBUTE is a subentity of an INSERT element or a ATTDEF of a BLOCK). Therefore the same functions as in [22.1]-[22.3] can be used. ;;; return only some assoc values in the list (for LWPOLYLINE) (defun GROUP-ONLY (grp lst) (mapcar 'cdr (remove-if-not '(lambda(pair)(= grp (car pair))) lst))) ;;; return the vertex list of a polyline or of any other element ;;; Note that with edlgetent mentioned in [22.1] it's a one-liner (defun GETPTS (ele / pts) (setq ele (entity ele)) ;force type ENAME (cond ((istypep ele "POLYLINE") (while (istypep (setq ele (entnext ele)) "VERTEX") ;; omit fit and spline points (conservative style) (if (not (or (flagsetp 1 ele) (flagsetp 8 ele))) ;bugfix! (setq pts (cons (trans (getpt ele) ele 0) pts))) (reverse pts))) ;; Special case: you have to map it, assoc finds only the first. ;; Fix a LWPOLYLINE bug in R14: internally stored as 2d point, ;; (entget) returns fantasy z-values. ((istypep ele "LWPOLYLINE") (mapcar '(lambda(pt)(trans (list (car pt)(cadr pt) 0.0) ele 0)) (group-only 10 (entget ele)))) ;; insert here possible other types, such as ((istypep ele '("TEXT" "CIRCLE")) (list (getpt ele))) ;; more like this (serge's style) (T (apply 'append (mapcar '(lambda (n / p) (if (setq p (getval n ele)) (list p))) '(10 11 12 13))) ) ;; or like this (conservative style) ;;(T (foreach n '(10 11 12 13) ;; (if (setq p (getval n ele)) (setq pts (cons p pts)))) ;; pts ;;) ) ) Suggestions by Vladimir Nesteroswky for a different vertex structure: (defun VERTICES-AND-BULGES (pline-ename) (mapcar 'cdr (remove-if-not '(lambda (ele) (bitsetp 9 x)) (get '(70 10 42) (cdr (edlgetent pline-ename)) => list of (10 42) pairs of the pline See also [23.5] for a different edge structure (segments) of plines. ------------------------------ [23.2] How to JOIN multiple lines to polylines? Simply try to join each element with all selected, but beware that an entity already joined cannot be entget'ed anymore, because it's deleted. ;;; This sample converts all selected elements to polylines and ;;; tries to join as much as possible. (defun C:JOINPOLY (/ ele ss) (foreach ele (sslist (setq ss (ssget))) ;better process lists (if (entget ele) ;not already joined (cond ;(then it would be nil) ((istypep ele '("ARC" "LINE")) ;; in fact you should check Z of lines and UCS here too (command "_PEDIT" ele "_Y" "_J" ss "" ""); convert and JOIN ) ((and (istypep ele '("POLYLINE" "LWPOLYLINE")) ;bugfix (not (flagsetp 1 ele)) ;not closed (< (rem (getflag ele) 128) 8)) ;ignore meshes and such (command "_PEDIT" ele "_J" ss "" "");ucs check omitted ) ) ) ) ) ------------------------------ [23.3] Change WIDTH of multiple polylines With the help of the above defined helper function it's a short one: (defun C:POLYWID (/ wid ele) (initget 5)(setq wid (getdist "New Polyline Width: ")) ;not negative (foreach ele (sslist (ssget '((0 . "*POLYLINE")))) ;only PLINES (command "_PEDIT" ele "_W" wid ""))) ------------------------------ [23.4] Create a polyline or spline: with (ENTMAKE) or (COMMAND) 1. You can create a script-file with a LISP-program and then run it. It seems to be the simpliest way, but I/O errors may occur when reading/writing the script. If your soft is commercial, it must handle such errors. 2. The second way is to create the entities list and use ENTMAKE. Advantage: fast, in WCS, independent of actual osnaps. See 3. The third solution is based on command and mapcar. It works with Polylines, Splines or Lines. Disadvantage: UCS, Osnaps ;;; Draws a POLYLINE entity from a list of points (same with SPLINE, ;;; or LINE), on the actual UCS, with actual OSNAP settings (defun DRAW-PLINE (pts) (command "_PLINE") (mapcar 'command pts) (command "")) (defun DRAW-SPLINE (pts) (command "_SPLINE") (mapcar 'command pts) ; the pts must be the fitpoints then (command "" "" "")) ------------------------------ [23.5] How to calculate the LENGTH of polylines? There are two ways: 1. the obvious, using the AREA command which is quite "noisy" (prints the result), but works even with splines. ;;; add up the LENGTH of all selected objects, NOISY, you can do the ;;; same with AREAs: simply change the last line to (getvar "AREA") (defun C:LEN-OF () (command "_AREA" "_A" "_E") ;add up objects (for R12+13) (ssmap 'command (ssget)) ;pass all elements to AutoCAD (command "" "") ;two returns (getvar "PERIMETER")) ;this is the length 2. Doing some math, but only for simple entities. Here it is best to define some helper functions again. This is also an introduction for the next chapter [24], some bulge trigonometry for curved segments. ;;; calculates length of a pline, quiet (defun POLY-LENGTH (poly / seg) (apply '+ ; the sum of all single segment lengths (mapcar '(lambda (seg) ;length of one segment (if (zerop (car seg)) ;is it straight? (distance (cadr seg) (caddr seg)) ; line segment or (abs (arclen seg)))) ; curved: -> [24] (pline-segs poly)))) ;segment list (bulge p1 p2) ;;; returns all group codes of the complex element ;;; (vertices, attributes) as list, similar to (edlgetent) (defun CPLX-LIST (grp ele / lst) (if (= 1 (getval 66 ele)) (progn (setq ele (entnext (entity ele))) (while (and ele (not (istypep ele "SEQEND"))) (setq lst (cons (getval grp ele) lst) ele (entnext ele))) (reverse lst)))) ;;; Creates a segment list for the polyline pname ;;; as a list of '(bulge p1 p2). A straight line has bulge 0.0 ;;; Compute pts in ECS of pname. Accepts LWPOLYLINE's (defun PLINE-SEGS (pname / pts segs) (setq segs (mapcar 'list (if (istypep pname "LWPOLYLINE") (group-only 42 (entget pname)) (cplx-list 42 pname)) (setq pts (getpts pname)) ; ->[23.1] (rot1 pts))) ; ->[20.1] (if (flagsetp 1 pname) segs ;closed (butlast segs))) ;open: without the last segment, ->[20.1] ;;; Example: (a bit optimized for brevity :) ;;; Add up all the lengths of all selected polylines, quiet ;;; To accept also other entities, add those to pline-segs (defun C:POLYLEN () (apply '+ (ssmap 'poly-length (ssget '((0 . "*POLYLINE")))))) For the sum of areas use either the noisy AREA command or implement Heron's formula for polygon areas (just for simple closed polygons). ------------------------------ [23.6] How to REVERT a polyline direction? Sergei Komarov submitted a REVPOLY.LSP which takes care of bulges and widths too. A short stdlib version is this: ;;; ignoring any width information (defun POLY-REVERSE (segs) (reverse (mapcar '(lambda (seg) (std-make-seg (std-seg-p2 seg) (std-seg-p1 seg) (- (std-seg-bulge-num seg)))) segs))) (defun C:POLYREV (/ ele) (std-require "ENTMAKE") (if (setq ele (car (entsel "\nRevert Poly: "))) (std-entmake-pline (entget ele '("*")) ; keep EED information (poly-reverse (std-pline-segs ele))))) ------------------------------ [23.7] How to get the CENTER of a polyline? The centroid of SOLID's has to be extracted with MASSPROP. You can let it write to a file and analyse this then. The centroid of polylines is different to the mean vector. The 2D geometric mean of some pts is simply: (setq n (float (length pts))) (list (/ (apply '+ (mapcar 'car pts)) n) (/ (apply '+ (mapcar 'cadr pts)) n)) The true centriod is more difficult. The stdlib version is at STD-CENTROID-2D In the stdlib.arx or at (under freebies) are also a better massprop lisp function for solids. Then you do (command "_REGION" ele)...(massprop entlast)...(command "_UNDO" 1) For VLA there's also a massprop property for Acis objects.
Subject: [24] Circle/Arc Geometry: BULGE conversion, some trigonometry What is the *BULGE* in a polyline? The bulge is the tangent of one forth of the included angle of a curved segment. A bulge 0.0 means a straight segment. Together with the start- and endpoint it is sufficient information to quickly calculate all other required information of a curved segment. A negative bulge is a rotation in clockwise direction ("mathematically negative"). arclength = radius*angle bulge = tan( ang/4 ) (CCW: +, CW: -) angle = 4*atan( bulge ) bulge = ( 2*altitude ) / chord (CCW: +, CW: -) See also for a sample program or the book "Maximizing AutoLISP" [2] (Note: The R10/11 book -Vol II- contains a wrong bulge formula.) ;;; converts a bulged segment (bulge pt1 pt2) of a polyline ;;; to a circle (ctr rad), the start- and endpoints are known ;;; therefore the angles too: (angle ctr pt1)(angle ctr pt2) ;;; returns nil on a straight segment! ;;; (bugfixed version. Thanks to Sergei Komarov) (defun SEG2CIR (seg / bulge p1 p2 cot x y rad dummy) (if (zerop (car seg)) ;straight line => invalid circle nil (setq bulge (car seg) p1 (cadr seg) p2 (caddr seg) cot (* 0.5 (- (/ 1.0 bulge) bulge)) x (/ (- (+ (car p1) (car p2)) (* (- (cadr p2) (cadr p1)) cot)) 2.0) y (/ (+ (+ (cadr p1) (cadr p2)) (* (- (car p2) (car p1)) cot)) 2.0) rad (distance (list (car p1) (cadr p1)) (list x y)) dummy (list (list x y) rad)))) ; return this, I hate progn's ;;; inverse conversion ;;; calculates segment (bulge p1 p2) of arc ;;; with given circle (ctr rad), start-angle, end-angle (defun ARC2SEG (cir ang1 ang2 / p1 p2) (setq p1 (polar (car cir) ang1 (cadr cir)) p2 (polar (car cir) ang2 (cadr cir))) (list (arc2bul p1 p2 cir) p1 p2)) ;;; calculates bulge of arc given the arc points and the ;;; circle (ctr rad) [fixed by Serge Pashkov] (defun ARC2BUL (p1 p2 cir / ang) (setq ang (- (angle (car cir) p2) (angle (car cir) p1))) (if (minusp ang) (setq ang (+ (* 2.0 pi) ang))) (tan (/ ang 4.0))) ;;; returns angle of arc (bulge) ;;; The seg format is (bulge p1 p2) (defun BUL2ANG (seg / ctr) (- (angle (setq ctr (car (seg2cir seg))) (cadr seg)) (angle ctr (caddr seg)))) ;;; calculates angle of arc given the chord distance and radius (defun ARC2ANG (chord rad) (* 2.0 (atan (/ chord 2.0 (sqrt (- (expt rad 2) (expt (/ chord 2.0) 2) ) ) ) ) ) ) ;another way in the paren's world ;;; length of arc = radius*angle, ;;; Note: +/-, you'll need (abs (arclen seg)) for the distance (defun ARCLEN (seg) (* (cadr (seg2cir seg)) ;radius 4.0 (atan (car seg)))) ;angle = 4*atan(bulge) (setq *INFINITY* 1.7e308) ; largest double (defun TAN (z / cosz) ; [fixed] (if (zerop (setq cosz (cos z))) *INFINITY* (/ (sin z) cosz))) (defun DTR (ang)(* pi (/ ang 180.0))) ;degree to radian (defun RTD (ang)(/ (* ang 180.0) pi)) ;radian to degree
Subject: [25] DCL: listboxes with tabs or monotext font Under Windows it's difficult to layout texts because of non-monospaced fonts. Try it with the tabs attribute in the list_box tile, such as: tabs = "0 20 40"; and (set_tile "listbox" "Layer:\t0\twhite") try the following to use monospaced characters: : list_box { label = "Drawing"; key = "dwglist"; width = 50; fixed_width_font = true; // <- monotext } Also might want to look at the detab routine (TAB -> Spaces) at or STD-DETAB in
Subject: [26] EED Extended Entity Data: Select, Get and Store [26.1] Select objects on their EED with (ssget "X") ;;; defines your appname header and delimiter (4 char regapp name ;;; according AAIG, AutoDESK Application Interoperation Guidelines) (setq appname "HUBU-") ;;; defines * for all sub types (setq allappnames (strcat appname "*")) ;;; eg: HUBU-LIST1, HUBU-LIST2 ;;; here is how to get the first eed list from one element (defun get-eed-1st (ele) (cdadr (assoc -3 (entget (entity ele) (list allappnames))))) ;;; this gets all elements of appnames typ (wildcards allowed) (defun ssget-app (typ) ;fast (ssget "X" (list (list -3 (list typ)))) ;;; this gets only your elements (defun ssget-hubu (typ) ;fast (ssget "X" (list (list -3 (list (strcat appname typ))))) (ssget-hubu "*") ; will get all your elements ------------------------------ [26.2] Get EED from an object Check any XDATA with: (entget (car (entsel)) '("*")) ;;; GETXDATA - get all XDATA lists from an element ;;; i.e with XDATA: ;;; (-3 ("HUBU-1" (1000 ."ASSHATCH")(1002 ."{") ;;; (1070 . 1)(1002 ."}"))) ;;; =>(("HUBU-1" (1000 ."ASSHATCH")(1002 ."{")(1070 . 1)(1002 ."}"))) (defun getxdata (e apnlst) (cdr (assoc -3 (entget e apnlst)))) ;;; GETXDATA-ALL - all lists without the regapp name ;;; => ((1000 ."ASSHATCH")(1002 ."{")(1070 . 1)(1002 ."}")) (defun getxdata-all (e apnlst) (apply 'append (mapcar 'cdr (getxdata e apnlst)))) The regapp name is stripped here, because it's only used for fast ssget access. The different apps are divided by different (1000 . name) groups as it's used by AutoDESK. For storing XDATA in an element see XDATA.LSP or XED.LSP though those examples are a bit disturbing. For advanced EED tricks, esp. converting the "{" "}" ADS resbuf style to Lisp lists and back, see
Subject: [27] How to break a command in Lisp? Also: "How do I press Break in AutoLISP?" (command) without parameters works just like hitting Ctrl-C under DOS or Esc under Windows at the command prompt. But it does not mimic Esc in a dialog box. And it does not work within SCRIPTS. (command nil) is the same as (command). (command) breaks only the command functions, e.g. if you use the command "DIM" inside AutoLISP, you must interrupt it by (command) after dimensioning. But it doesn't work, if you try to interrupt a lisp loop. There is another function (exit) or (quit) -they do the same-, which immediately break a Lisp program. Example: (while T ; do ; a never ending loop (princ "\nEnter a=") (setq a (getint)) (if (zerop a)(exit)) ; Breaks Lisp and returns to the command mode. ) In this example (command) doesn't work. (exit) works exactly as Ctrl-C. It prints "error: quit / exit abort" and outputs all nested functions. To provide "silent" break you must include this error message to an error handling function, e.g.: (setq *olderr* *error* *error* my-error) (defun MY-ERROR (s) (if (not (member s ; msgs of the english version: '("Function cancelled" "console break" "quit / exit abort"))) (princ (strcat "\nError: " s)) ) (setq *error* *olderr*) ) For scripts use this workaround by defining (cancel) in lisp, simply (defun SCRIPT-CANCEL () (command) (command "resume") ) and in a SCRIPT.SCR: .. [<script commands>] (script-cancel) [<more script commands>] ..
Subject: [27.1] How to do an unlimited number of user prompts? [new] To let the user end any selected command without having to write code for every possible option, just repeat (command PAUSE) until the command is ended. ;; Sample by Owen Wengerd (command "_ARC") (while (= 1 (logand (getvar "CMDACTIVE") 1)) (command PAUSE))
Subject: [28] How to decode ACIS internal geometry with Lisp? All the ACIS objects (3DSOLID) have been documented by Spatial ("SAT Format Description"). However the internal representation by (entget) is still encrypted, but the encryption scheme was hacked. (XOR 95) Samples and code are at:
Subject: [A] Disclaimer, Notes from the authors If you think of questions that are appropriate for this FAQ, or would like to improve an answer, please send email to Reini Urban <> but don't expect an reply. This AutoLISP FAQ is Copyright (c) 1996,97,98,99,2000 by Reini Urban. This FAQ may be freely redistributed in its entirety without modification, provided that this copyright notice is not removed. It may not be sold for profit or incorporated in commercial documents (e.g. published for sale on CD-ROM, floppy disks, books, magazines, or other print form) without the prior written permission of the copyright holder. Permission is expressly granted for this document to be made available for file transfer from installations offering unrestricted anonymous file transfer on the Internet (WWW, FTP) and esp. to be included into the official AutoCAD FAQ. The sample code is, if not otherwise stated, (c) 1996,97 by Reini Urban and may be freely used, but not sold. The basic functions in [20] are, if not otherwise stated, (c) 1991-97 by Reini Urban and may/should be freely used. If this FAQ is reproduced in offline media (e.g., CD-ROM, print form, etc.), a complimentary copy should be sent to Reini Urban, X-RAY, Nibelungeng. 3, 8010 Graz, Austria This article, the contents and the sample code, is provided AS IS without any expressed or implied warranty. ------------------------------ [A.1] FAQ Locations Homepage of the HTML'ified version: + Annotated AcadWiki version: + The posted ascii versions (and always latest versions) are at and The Winhelp version (zipped with faq and code) is at The FAQ usenet archive is at resp. The Lisp code from this FAQ is at A french translation of the FAQ by Roger Rosec A japanese translation of the FAQ by MASAMI Chikahiro A russian translation of the FAQ by Igor Orellana at A german translation by myself at + A new spanish translation by Eduardo Magdalena + Relevant AutoDesk FAQ's and TechSupport AutoDesk news groups news:// or the new WebX interface at
Subject: [B] Acknowledgements This FAQ is based on great efforts of the the news://comp.cad.autocad community, in particular: Tom Berger, Adi Buturovic, Christoph Candido, Mike Clark, Miles Constable, Cara Denko, T.J. DiTullio, Chris Ehly, Jeff Foster, Rusty Gesner, William Kiernan, Paul Kohut, Sergei M. Komarov, Joseph + M. Liston, Lu, Eduardo Magdalena, Masami Chikahiro, Georg Mischler, Desi Moreno, Vladimir Nesterovsky, Roger Rosec, Serge Pashkov, Dennis Shinn, Tony Tanzillo, Eugene Tenenbaum, Reinaldo Togores, Reini Urban, Serge Volkov, Morten Warankov, Owen Wengerd, Alan Williams, Doug Wilson, Ian A. White, David Whynot, Darren Young, Xiang Zhu and others.
Subject: [C] Recent Changes * 25.Jun 2002 new spanish translation [A.1] * 18.Jun 2001 fixed to * 23.Apr 2001, 14.Jun 2001 fixed several links * v2.28 4.Apr 2001 changed rurban@sbox to (defunct in the next year) all parts of the faq are now in the acadwiki. * v2.27 23.Sep 2000 added [23.7] "How to get the CENTER of a polyline?" * 15.Sep 2000 changed stack-overflow [14] to better reflect VL/VLIDE, A2000, new book [2] * 1.Sep 2000 vl-sort warning with duplicate entries [8] * 18.Aug 2000 changed CodeMagic [6.1] from Freeware to Shareware, thanks to Nir Sullam * 1.Aug 2000 changed adesk faq location * v2.26 6.Jun 2000 added LDATA bug [7], fixed DEFUN-Q [11], removed most colored [new/changed] notes * v2.25 17.May 2000 added [27.1], added C:POLYREV [23.6], texinfo versions * 25.Apr 2000 added Point A [1.1], shortened the DDE example in [21.2] * 24.Apr 2000 Vladimir fixed to [1.1] * v2.24 20.Apr 2000 renamed to [1], added [1], added rapidlisp [6.2] * 30.Mar 2000 renamed to * 9.Mar 2000 added CodeMagic editor at [6.1], thanks to Nir Sullam * 29.Feb 2000 Masami Chikahiro fixed numeric range [7]: -32766 => -32768 * 17.Feb 2000 Added the dotsoft buglist url. Mike Tuersley fixed [11] for MNL files. Owen: $600 US of ADN [1.2] * v2.23 14.Feb 2000 Chris Ehly fixed all broken links. * v2.22 13.Jan 2000 additions to numerical precision [7]. adesk techdocs links are broken again. compiled S::STARTUP hooks [11]. * the full history is at -- Reini Urban, Jun 25, 2002

User Contributions:

Mar 17, 2023 @ 5:17 pm
Regardless if you believe in God or not, read this message!!!

Throughout time, we can see how we have been carefully conditioned to come to this point where we are on the verge of a cashless society. Did you know that the Bible foretold of this event almost 2,000 years ago?

In the book of Revelation 13:16-18, we read,

"He (the false prophet who deceives many by his miracles--Revelation 19:20) causes all, both small and great, rich and poor, free and slave, to receive a mark on their right hand or on their foreheads, and that no one may buy or sell except one who has the mark or the name of the beast, or the number of his name.

Here is wisdom. Let him who has understanding calculate the number of the beast, for it is the number of a man: His number is 666."

Referring to the last generation, this could only be speaking of a cashless society. Why so? Revelation 13:17 states that we cannot buy or sell unless we receive the mark of the beast. If physical money was still in use, we could buy or sell with one another without receiving the mark. This would contradict scripture that states we need the mark to buy or sell!

These verses could not be referring to something purely spiritual as scripture references two physical locations (our right hand or forehead) stating the mark will be on one "OR" the other. If this mark was purely spiritual, it would indicate both places, or one--not one OR the other!

This is where it comes together. It is amazing how accurate the Bible is concerning the implantable RFID microchip. Here is information from a man named Carl Sanders who worked with a team of engineers to help develop this RFID chip:

"Carl Sanders sat in seventeen New World Order meetings with heads-of-state officials such as Henry Kissinger and Bob Gates of the C.I.A. to discuss plans on how to bring about this one-world system. The government commissioned Carl Sanders to design a microchip for identifying and controlling the peoples of the world—a microchip that could be inserted under the skin with a hypodermic needle (a quick, convenient method that would be gradually accepted by society).

Carl Sanders, with a team of engineers behind him, with U.S. grant monies supplied by tax dollars, took on this project and designed a microchip that is powered by a lithium battery, rechargeable through the temperature changes in our skin. Without the knowledge of the Bible (Brother Sanders was not a Christian at the time), these engineers spent one-and-a-half-million dollars doing research on the best and most convenient place to have the microchip inserted.

Guess what? These researchers found that the forehead and the back of the hand (the two places the Bible says the mark will go) are not just the most convenient places, but are also the only viable places for rapid, consistent temperature changes in the skin to recharge the lithium battery. The microchip is approximately seven millimeters in length, .75 millimeters in diameter, about the size of a grain of rice. It is capable of storing pages upon pages of information about you. All your general history, work history, criminal record, health history, and financial data can be stored on this chip.

Brother Sanders believes that this microchip, which he regretfully helped design, is the “mark” spoken about in Revelation 13:16–18. The original Greek word for “mark” is “charagma,” which means a “scratch or etching.” It is also interesting to note that the number 666 is actually a word in the original Greek. The word is “chi xi stigma,” with the last part, “stigma,” also meaning “to stick or prick.” Carl believes this is referring to a hypodermic needle when they poke into the skin to inject the microchip."

Mr. Sanders asked a doctor what would happen if the lithium contained within the RFID microchip leaked into the body. The doctor replied by saying a terrible sore would appea (...)

Comment about this article, ask questions, or add new information about this topic:

Part1 - Part2 - MultiPage

[ Usenet FAQs | Web FAQs | Documents | RFC Index ]

Send corrections/additions to the FAQ Maintainer: (Reini Urban)

Last Update March 27 2014 @ 02:11 PM