Jump to content
SwChilly

AutoLISP Routine, HELP!

Recommended Posts

SwChilly

I'm a student. I am learning about LISP routines. What I'm trying to do is write a lsp that has the user key in point (using createpointmanual I'm assuming) and then draw a polyline from one point to the next.

 

So far my program should to do this:

- prompts user to key in point(s)

- draws a polyline that connects the points

 

I'm guessing I have to make a list for the points and keep adding to the list as each point is created. Can I use a counter, let say p1 (point 1) then p2 p3 p4 and so on, and then add it to the list?

 

Example:

 

(setq aa (list p1))

(setq aa (append aa p2)) *I know I have to keep adding to this list.

 

I think I'm just confusing myself

Anyone got any resources or ideas or answers on what code I need to write?

Share this post


Link to post
Share on other sites
jammie

Hi,

 

There are a few loop functions that could perhaps be useful for the first part of your task. Probably the most relevant is the WHILE expression

 

(while <something is true> <keep doing something>)

 

When you select a point using GETPOINT, you may have noticed that if you hit enter the return value is nil. The WHILE expression will keep looping until a nil value is passed to it.

 

(while 
    ;Test expression - select a point.
    (setq point (getpoint "\nPick point or hit enter :"))
  
    ;Arguments 

    ;If a point is picked, a list is returned. 
    ;The WHILE expression accepts this as a positive result. That means the    
    ;following arguments will be evaluated. If enter was hit the following part 
    ;would be ignored

    (setq aa (append aa (list point)))
   
    ;Back to the test expression to see if the arguments will be evaluated again
);end of while

(if
  
  ;if a point was selected, the point list aa will be created 
  aa
   
  ;If the point list exists - Create the polyline 

  ;If the point list does't exist - Possibly tell the user
);end of IF


 

 

Hope this makes some sense. The AUTOLISP help info in AutoCAD is a very good reference as well

Share this post


Link to post
Share on other sites
Lee Mac

Hi SwChilly,

 

I'm a student also - welcome to the club :)

 

Now, onto your question - there are many ways to accomplish your task, some easier than others.

 

By far the simplest way would be to use the in-built PLine command and let AutoCAD do the work for you:

 

Method 1: Using the Pline command

 

(defun c:test ( )
 (command "_.pline")
 (while (= 1 (logand 1 (getvar 'CMDACTIVE))) (command pause))
 (princ)
)

Here, I first call the PLine command using an underscore (_) so that the correct command is called on non-english versions of AutoCAD, and using a dot (.) to ensure the core PLine command is used, since a command can be redefined by the user.

 

The CMDACTIVE System Variable is a bit-coded value that determines whether a command, script, or LISP program etc is active. Hence, in the above program, the test condition for the While Loop checks whether the PLine command is still active, and, if so, pauses for user input.

 

Constructing a List of Points

 

Another way to gather your points would be to use the getpoint function and construct a list of points to use when creating your LWPolyline.

 

Again, there are many ways to go about this, here is one example:

 

(if (setq p1 (getpoint "\nSpecify First Point: "))
 (progn
   (setq lst (cons p1 lst))
   (while (setq p2 (getpoint "\nSpecify Next Point: " p1))
     (setq lst (cons p2 lst)
           p1  p2
     )
   )
   (setq lst (reverse lst))
 )
)

I use cons since it is much faster than the append function, then reverse the final list so that the points are in order.

 

Notice that this construct could be condensed - consider the following:

 

(if (car (setq lst (list (getpoint "\nSpecify First Point: "))))
 (progn
   (while (car (setq lst (cons (getpoint "\nSpecify Next Point: " (car lst)) lst))))
   (setq lst (reverse (cdr lst)))
 )
)

Although, this isn't as readable as the previous example.

 

Now that we have a list of points, there are many routes we can follow to construct the LWPolyline:

 

Method 2: Using the PLine command with a List of Points

 

We could again use the PLine command to create the polyline:

 

(defun c:test ( / p1 p2 lst )
 (if (setq p1 (getpoint "\nSpecify First Point: "))
   (progn
     (setq lst (cons p1 lst))
     (while (setq p2 (getpoint "\nSpecify Next Point: " p1))
       (setq lst (cons p2 lst)
             p1  p2
       )
     )
     (command "_.pline")
     (foreach pt (reverse lst)
       (command "_non" pt)
     )
     (command)
   )
 )
 (princ)
)

A few things to notice with the above code:

 

  1. I have localised the variables at the top of the code - this ensures the variables do not conflict with other variables using the same names. To see why this is important, read this tutorial.
  2. I have used "_non" before submitting each point to the PLine command - this ignores any Object Snap which may be set and which may affect the point supplied to the PLine command.

 

Method 3: Using entmake with a List of Points:

 

Now we are moving onto the more advanced methods of creating the LWPolyline, involving a little more work on our part since we are now modifying the drawing database directly.

 

I won't explain the ins and outs of the DXF code structure, but a reference for 2011 can be found here, describing the purpose for each of the codes.

 

Consider the following code example:

 

(defun c:test ( / p1 p2 lst )
 (if (setq p1 (getpoint "\nSpecify First Point: "))
   (progn
     (setq lst (cons [color=red](cons 10 p1)[/color] lst))
     (while (setq p2 (getpoint "\nSpecify Next Point: " p1))
       (setq lst (cons [color=red](cons 10 p2)[/color] lst)
             p1  p2
       )
     )
     (entmake
       (append
         (list
           (cons 0 "LWPOLYLINE")
           (cons 100 "AcDbEntity")
           (cons 100 "AcDbPolyline")
           (cons 90 (length lst))
           (cons 70 0)
         )
         (reverse lst)
       )
     )
   )
 )
 (princ)
)

Notice the highlighted changes to the method by which we collect the points - this is because the DXF code for a vertex of the LWPolyline is 10, and so we can construct each vertex entry as the list is constructed to save iterating through the whole list again later on.

 

[ Advanced: Accounting for UCS ]

 

Now, the above code will work correctly when the user is picking points in WCS, but, should the user be working in a UCS which differs from the WCS, we have to translate the points from the UCS to the WCS.

 

This can be accomplished using the trans function. Since the vertices of the LWPolyline are defined relative to the OCS of the LWPolyline, the UCS points need to be translated relative to the plane in which the LWPolyline will reside:

 

(defun c:test ( / p1 p2 lst ucsz )

 (setq ucsz (trans '(0. 0. 1.) 1 0 t)) ;; The Normal of the UCS Plane relative to WCS
 
 (if (setq p1 (getpoint "\nSpecify First Point: "))
   (progn
     (setq lst (cons (cons 10 (trans p1 1 ucsz)) lst))
     (while (setq p2 (getpoint "\nSpecify Next Point: " p1))
       (setq lst (cons (cons 10 (trans p2 1 ucsz)) lst)
             p1  p2
       )
     )
     (entmake
       (append
         (list
           (cons 0 "LWPOLYLINE")
           (cons 100 "AcDbEntity")
           (cons 100 "AcDbPolyline")
           (cons 90 (length lst))
           (cons 70 0)
           (cons 210 ucsz)
         )
         (reverse lst)
       )
     )
   )
 )
 (princ)
)

The above code will now work in all UCS/Views.

 

Method 3: Using Visual LISP

 

The LWPolyline can also be created using Visual LISP methods acting on the current space.

 

Consider the following code:

(defun c:test ( / acdoc acspc p1 p2 lst ) (vl-load-com)

 (setq acdoc (vla-get-activedocument (vlax-get-acad-object))
       acspc (vlax-get-property acdoc (if (= 1 (getvar 'CVPORT)) 'Paperspace 'Modelspace))
 )
 
 (if (setq p1 (getpoint "\nSpecify First Point: "))
   (progn
     (setq lst (cons (cadr p1) (cons (car p1) lst)))
     (while (setq p2 (getpoint "\nSpecify Next Point: " p1))
       (setq lst (cons (cadr p2) (cons (car p2) lst))
             p1  p2
       )
     )
     (vla-addlightweightpolyline acspc
       (vlax-make-variant
         (vlax-safearray-fill
           (vlax-make-safearray vlax-vbdouble (cons 0 (1- (length lst))))
           (reverse lst)
         )
       )
     )
   )
 )
 (princ)
)

The above code uses the AddLightWeightPolylinemethod on the active space (Modelspace or Paperspace) to create an LWPolyline. This method is slightly more complex since it uses Safearrays and Variants, and the point list is constructed slightly differently as the safearray requires a list of doubles as opposed to a list of points.

 

Interestingly, we can avoid the use of Safearrays and Variants by using the undocumented vlax-invoke function on the space object:

 

(defun c:test ( / acdoc acspc p1 p2 lst ) (vl-load-com)

 (setq acdoc (vla-get-activedocument (vlax-get-acad-object))
       acspc (vlax-get-property acdoc (if (= 1 (getvar 'CVPORT)) 'Paperspace 'Modelspace))
 )
 
 (if (setq p1 (getpoint "\nSpecify First Point: "))
   (progn
     (setq lst (cons (cadr p1) (cons (car p1) lst)))
     (while (setq p2 (getpoint "\nSpecify Next Point: " p1))
       (setq lst (cons (cadr p2) (cons (car p2) lst))
             p1  p2
       )
     )
     (vlax-invoke acspc 'addlightweightpolyline (reverse lst))
   )
 )
 (princ)
)

Again, with the above Visual LISP code, the programs will only be correct for users working in WCS. The same coordinate transformations must be made to account for changes in the UCS.

 

Well, this post got a lot longer than I thought it would, so I hope I haven't bored you up to this point.

 

I realise some of the concepts introduced here are perhaps not for a beginner, but hopefully this post can benefit many users in the community, and users can read those sections they wish to learn.

 

If you have any questions about the content of this post, just ask and I would be happy to explain further.

 

Regards,

 

Lee

Share this post


Link to post
Share on other sites
SwChilly
Hi,

 

There are a few loop functions that could perhaps be useful for the first part of your task. Probably the most relevant is the WHILE expression

 

(while <something is true> <keep doing something>)

 

When you select a point using GETPOINT, you may have noticed that if you hit enter the return value is nil. The WHILE expression will keep looping until a nil value is passed to it.

 

(while 
    ;Test expression - select a point.
    (setq point (getpoint "\nPick point or hit enter :"))

    ;Arguments 

    ;If a point is picked, a list is returned. 
    ;The WHILE expression accepts this as a positive result. That means the    
    ;following arguments will be evaluated. If enter was hit the following part 
    ;would be ignored

    (setq aa (append aa (list point)))

    ;Back to the test expression to see if the arguments will be evaluated again
);end of while

(if

  ;if a point was selected, the point list aa will be created 
  aa

  ;If the point list exists - Create the polyline 

  ;If the point list does't exist - Possibly tell the user
);end of IF


 

 

Hope this makes some sense. The AUTOLISP help info in AutoCAD is a very good reference as well

 

Thanks so much for the reply!

 

I don't want to use the getpoint command. I want the user to enter in x and y coordinates and then 0 for the z coordinate.

 

Right now I'm working on getting the user to key in points.

Unless there is an easier way, this is my code thus far (it doesn't work tho):

 

 

 
(Setq ans nil)
(setq break T)

(defun c:cpol ()
 (while_test)
)



(defun while_test ()
(while (= break T)
       ;;Key in a point
   (setq x_loc (getreal "\nEnter in X value: "))
   (setq y_loc (getreal "\nEnter in Y value: "))
   (setq coord (x_loc y_loc 0))
   (command "_.createpointmanual" (coord) "")            ;;the error is here
 (prompt "\nDo you want to create another point?")
 (yn)
   (if (= ans "Yes")
     (while_test)
   )
 )
)

 

I've only just started learning lisp so I havent learned any advanced code as of yet. This is pretty basic.

Share this post


Link to post
Share on other sites
SwChilly
Hi SwChilly,

 

I'm a student also - welcome to the club :)

 

Now, onto your question - there are many ways to accomplish your task, some easier than others.

 

By far the simplest way would be to use the in-built PLine command and let AutoCAD do the work for you: . . . .

 

 

Thanks for your reply Lee, this is helpful and I'll definately look save is for later use and see if I can decipher through it haha

Share this post


Link to post
Share on other sites
Lee Mac
I don't want to use the getpoint command. I want the user to enter in x and y coordinates and then 0 for the z coordinate.

 

I would strongly recommend the getpoint function - note that the user can either pick a point, or manually type coordinates at the prompt.

 

Thanks for your reply Lee, this is helpful and I'll definately look save is for later use and see if I can decipher through it haha

 

Hope it helps!

Share this post


Link to post
Share on other sites
SwChilly


(if (setq p1 (getpoint "\nSpecify First Point: "))
(progn
(setq lst (cons (cadr p1) (cons (car p1) lst)))
(while (setq p2 (getpoint "\nSpecify Next Point: " p1))
(setq lst (cons (cadr p2) (cons (car p2) lst))
p1 p2
)
)

 

 

 

What does cons and entmake do?

Share this post


Link to post
Share on other sites
Lee Mac

Hi SwChilly,

 

Perhaps start with my earlier examples to help with the understanding :)

 

If you want to find out what a function does, the best place to look is the Visual LISP IDE Help Documentation, a tutorial on how to query it can be found here, a general introduction to the Visual LISP IDE can be found here.

Share this post


Link to post
Share on other sites
pBe

 

I'm a student also - Lee

 

By the looks of things you're more like a professor :)

 

very nice Lee

Share this post


Link to post
Share on other sites
Lee Mac
By the looks of things you're more like a professor :)

 

very nice Lee

 

Hehe thanks pBe :)

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...