Search code examples
lispautocadautolisp

How do I access Quoted Variable Data in AutoLisp?


I'm having trouble accessing the information stored in the lists STPT1 and ENDPT1 which are x(0), y(1), and z(2) coordinates.

For instance, after getting a point: (45.4529 21.6384 0.0) when I inspect with Visual LISP (-(NTH 1 STPT1) 0.5) I get a REAL 21.1384, but the following:

(SETQ STPTP2 '((NTH 0 STPT1) (- (NTH 1 STPT1) 0.5) 0))

creates the list:

((NTH 0 STPT1) (- (NTH 1 STPT1) 0.5) 0)

instead of:

(45.4529 21.1384 0.0)

My goal is to simultaneously create two parallel lines that are 0.5 units apart from each other.

How can I access the information in different positions of the lists STPT1 and ENDPT1 and then assign them in lists STPT2 and ENDPT2?

(VL-LOAD-COM)
(DEFUN C:CURBYOURENTHUSIASM ( / STPT1 ENDPT1 STPT2 ENDPT2)
(SETQ STPT1  (GETPOINT "\nSpecify start point: "))
(SETQ ENDPT1 (GETPOINT STPT1 "\nSpecify end point: "))
(SETQ STPT2  '((NTH 0 STPT1)  (-(NTH 1 STPT1) 0.5) 0))
(SETQ ENDPT2 '((NTH 0 ENDPT1) (-(NTH 1 ENDPT1) 0.5) 0))
(SETQ TOP    (ENTMAKE (LIST (CONS 0 "LINE")(CONS 10 STPT1)(CONS 11 ENDPT1)(CONS 8 "CONCRETE"))))
(SETQ BOTTOM (ENTMAKE (LIST (CONS 0 "LINE")(CONS 10 STPT2)(CONS 11 ENDPT2)(CONS 8 "CONCRETE"))))
(PRINC)
)

Solution

  • Current Issues

    There are a number of issues with your current code:

    1. Unbalanced Parentheses

    You have one too many closing parentheses on line 5 of your code:

    (SETQ STPT2  '((NTH 0 STPT1)  (-(NTH 1 STPT1) 0.5) 0)))
    

    The final closing parenthesis at the end of the above expression is closing the defun expression, resulting in the remaining expressions being evaluated on load, rather than when the function is evaluated.

    2. Quoted Variable Data

    You are incorrectly quoting the following expressions as literal expressions:

    (SETQ STPT2  '((NTH 0 STPT1)  (-(NTH 1 STPT1)  0.5) 0))
    (SETQ ENDPT2 '((NTH 0 ENDPT1) (-(NTH 1 ENDPT1) 0.5) 0))
    

    Expressions which follow the single quote will not be evaluated by the AutoLISP interpreter, but will instead be taken at 'face-value'.

    This means that the nth and - functions will not be evaluated, but will instead be interpreted simply as symbols within a nested list structure. For more information on literal expressions, you may wish to refer to my tutorial describing the Apostrophe & Quote Function.

    To construct a list of variable (i.e. non-literal) data, you should use the list function, e.g.:

        (setq stpt2 (list (nth 0 stpt1) (- (nth 1 stpt1) 0.5) 0))
    

    3. Unnecessary ActiveX

    You are unnecessarily loading the Visual LISP ActiveX extensions (using (vl-load-com)), but are not using any functions from this library in your code. This is a relatively minor issue, but worth mentioning nonetheless.


    Correcting the above issues and formatting your code with appropriate indentation, we have the following:

    (defun c:curbyourenthusiasm ( / stpt1 endpt1 stpt2 endpt2 )
        (setq stpt1  (getpoint "\nSpecify start point: "))
        (setq endpt1 (getpoint stpt1 "\nSpecify end point: "))
        (setq stpt2  (list (nth 0 stpt1)  (- (nth 1 stpt1)  0.5) 0))
        (setq endpt2 (list (nth 0 endpt1) (- (nth 1 endpt1) 0.5) 0))
        (setq top    (entmake (list (cons 0 "line") (cons 10 stpt1) (cons 11 endpt1) (cons 8 "concrete"))))
        (setq bottom (entmake (list (cons 0 "line") (cons 10 stpt2) (cons 11 endpt2) (cons 8 "concrete"))))
        (princ)
    )
    

    This code will now run successfully, but there are a number of possible improvements:


    Possible Improvements

    1. User Input Validation

    You should test for valid user input before proceeding to operate on data obtained from the user: if the user dismisses the prompts without supplying a point, any arithmetic operations on the list values will error, as such values will be nil.

    You can avoid such errors by simply using an if statement:

    (defun c:curbyourenthusiasm ( / ep1 sp1 )
        (if
            (and
                (setq sp1 (getpoint "\nSpecify start point: "))
                (setq ep1 (getpoint "\nSpecify end point: " sp1))
            )
            (progn
                ;; Continue with program operations
            )
        )
        (princ)
    )
    

    2. Line Angle Variation

    Currently your code will always offset the second line in the negative y-direction which will result in a variation in the line spacing as the angle of the line changes - when the line angle is vertical, the two lines will overlap.

    To avoid this, you can use the polar function to calculate a point offset a predetermined distance from the specified start & end points, in a direction perpendicular to the line angle, which you can calculate using the angle function:

    (defun c:curbyourenthusiasm ( / ang ep1 ep2 sp1 sp2 )
        (if
            (and
                (setq sp1 (getpoint "\nSpecify start point: "))
                (setq ep1 (getpoint "\nSpecify end point: " sp1))
            )
            (progn
                (setq ang (- (angle sp1 ep1) (/ pi 2))
                      sp2 (polar sp1 ang 0.5)
                      ep2 (polar ep1 ang 0.5)
                )
                ;; Continue with program operations
            )
        )
        (princ)
    )
    

    3. Accounting for UCS

    The getpoint function will return points whose coordinates are expressed relative to the current UCS (User Coordinate System) active at the time that the program is evaluated.

    However, the points associated with DXF groups 10 & 11 for a LINE entity in the drawing database are expected to be expressed relative to the WCS (World Coordinate System).

    We can transform points between the two coordinate systems using the AutoLISP trans function:

    (defun c:curbyourenthusiasm ( / ang ep1 ep2 sp1 sp2 )
        (if
            (and
                (setq sp1 (getpoint "\nSpecify start point: "))
                (setq ep1 (getpoint "\nSpecify end point: " sp1))
            )
            (progn
                (setq ang (- (angle sp1 ep1) (/ pi 2))
                      sp2 (trans (polar sp1 ang 0.5) 1 0)
                      ep2 (trans (polar ep1 ang 0.5) 1 0)
                      sp1 (trans sp1 1 0)
                      ep1 (trans ep1 1 0)
                )
                ;; Continue with program operations
            )
        )
        (princ)
    )
    

    4. Quote Constant Data

    Where you have constant data (e.g. explicit numerical data or strings), you can quote such data as literal data in the code, avoiding the need for the interpreter to evaluate the list and cons functions to construct the data structures:

    For example:

    (cons 0 "line")
    

    Can become:

    '(0 . "line")
    

    Since 0 and "line" are both constant data and may therefore be marked as literals.


    Implementing all of the above, we have the following:

    (defun c:curbyourenthusiasm ( / ang ep1 ep2 sp1 sp2 )
        (if
            (and
                (setq sp1 (getpoint "\nSpecify start point: "))
                (setq ep1 (getpoint "\nSpecify end point: " sp1))
            )
            (progn
                (setq ang (- (angle sp1 ep1) (/ pi 2))
                      sp2 (trans (polar sp1 ang 0.5) 1 0)
                      ep2 (trans (polar ep1 ang 0.5) 1 0)
                      sp1 (trans sp1 1 0)
                      ep1 (trans ep1 1 0)
                )
                (entmake (list '(0 . "LINE") '(8 . "concrete") (cons 10 sp1) (cons 11 ep1)))
                (entmake (list '(0 . "LINE") '(8 . "concrete") (cons 10 sp2) (cons 11 ep2)))
            )
        )
        (princ)
    )