Lisp function for offset a polyline.

Lisp function for offset a polyline.

abbas.baghernezhad
Enthusiast Enthusiast
1,945 Views
14 Replies
Message 1 of 15

Lisp function for offset a polyline.

abbas.baghernezhad
Enthusiast
Enthusiast

Hi

I'm still a lisp newbie. I need a lisp function to:

1) offset a polyline by a fixed value inside. (Somehow it doesn't ask for offset value or which side to pick. )

2) picks a vertex (any vertex is acceptable) of the newly offset polyline to a "getpoint" command that comes after.

here are my code so far that doesn't work right from ChatGPT.

(defun c:offsetpl (/ selSet offsetDist)
  (setq selSet (ssget "X" '((0 . "LWPOLYLINE"))))
  (if selSet
    (progn
      (command "_offset" "1.0" selSet)
      (princ "\nOffsetting polyline completed.")
    )
    (princ "\nNo polyline selected.")
  )
  (princ)
)

 and this for the second part which doesn't work at all :

(defun c:plcoord ()
  (setq selSet (ssget "X" '((0 . "LWPOLYLINE"))))
  
  (if selSet
    (progn
      (setq polylineObj (vlax-ename->vla-object (ssname selSet 0)))
      (setq vertexCount (vlax-get-property polylineObj 'NumberVertex))
      
      (if (> vertexCount 0)
        (progn
          (princ "\nPolyline Coordinates:")
          (setq i 0)
          (while (< i vertexCount)
            (setq vertex (vlax-curve-getPointAtParam polylineObj i))
            (setq x (car vertex))
            (setq y (cadr vertex))
            (setq z (caddr vertex))
            (princ (strcat "\nVertex " (itoa (1+ i)) ": (" (rtos x 2 6) ", " (rtos y 2 6) ", " (rtos z 2 6) ")"))
            (setq i (1+ i))
          )
        )
        (princ "\nThe polyline has no vertices.")
      )
    )
    (princ "\nNo polyline selected.")
  )
  (princ)
)

 How can I get it to work smoothely?

0 Likes
Accepted solutions (1)
1,946 Views
14 Replies
Replies (14)
Message 2 of 15

paullimapa
Mentor
Mentor

Since you're still learning you should look up what each of the lisp functions in each of the line of code does. It's also important to understand the command sequence of a typical AutoCAD command like OFFSET or PLINE in this case.

But to get you started you can also copy+paste practically each of the lines of code into the AutoCAD command prompt & you're see the results.

For example, start a new drawing and draw a couple of plines. One pline can have just 1 segment (which has 2 vertices) and another can have 3 segments (which would have 4 vertices).

Then copy & paste the following line of code onto the command line:

(setq selSet (ssget "X" '((0 . "LWPOLYLINE"))))

This actually selects all the plines using the ssget "X" parameter in your drawing and in this case you have 2.

Then these next two lines of code actually starts the Offset command with the first line already setting the offset distance value given as "1.0" and then the Offset command continues by requesting for which side of the pline to offset before finally executing the second line of code is that shows the operation completed:

      (command "_offset" "1.0" selSet)
      (princ "\nOffsetting polyline completed.")

Now the flaw to this code is that since you have two plines drawn, only one of them will be offset while the other one is skipped because the Offset command only accepts one object at a time and not a collection of objects. This requires you to understand how the Offset command works. So you'll need additional code to loop through and complete the same operation on the second drawn pline.

Or you change the initial line of code to only select a single pline object like this:

(setq selSet (ssget "_+.:E:S" '((0 . "LWPOLYLINE"))))

The ssget "_+.:E:S" parameter allows you to select only a single object that's in this case a pline.

 

Lee Mac has a web link that describes clearly the variety of ssget options available:

ssget Function Reference | Lee Mac Programming (lee-mac.com)

As to your second code, this line doesn't work because the NumberVertex parameter does not exist:

(setq vertexCount (vlax-get-property polylineObj 'NumberVertex))

Doing a search online you can find code that others have written to get number of vertices in pline:

Lisp to count polyline verticies - Autodesk: AutoCAD - Eng-Tips

So applying this into your code then will look like this:

   (setq polylineObj (vlax-ename->vla-object (ssname selSet 0))) ; converts entity to vl object
   (setq vertexCount (vlax-curve-getEndParam polylineObj))       ; gets the last parameter in pline
   (setq vertexCount (+ 1 endParam)) ; add 1 to get total number of vertices

 

 

  •  

Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
Message 3 of 15

abbas.baghernezhad
Enthusiast
Enthusiast

How can we always offset outside of a polyline? (stop asking "specify point on side" ). It is easy in VBA. Just +1 or -1. I don't know how is done in Lisp.

0 Likes
Message 4 of 15

paullimapa
Mentor
Mentor

well if it's not a closed pline how can you tell what's outside vs inside?


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 5 of 15

abbas.baghernezhad
Enthusiast
Enthusiast

Well. It is always closed in my cases.

0 Likes
Message 6 of 15

Kent1Cooper
Consultant
Consultant

@abbas.baghernezhad wrote:

How can we always offset outside of a polyline? (stop asking "specify point on side" ). It is easy in VBA. Just +1 or -1. I don't know how is done in Lisp.


One way is to make a VLA object of it, and use (vla-offset) with a positive distance argument, then check the comparative areas of the original and the result.  If the result is smaller, delete it and use (vla-offset) again with the negative of the distance.  [The routine >here< uses that method to determine which way to go between Dimensioning a Polyline's segments inside or outside.]

 

Another way is to use an OFFSET (command), and at the point where that is asking for the side to Offset to, give it:

(polar (getvar 'extmax) (/ pi 4) 1)

which is a point always guaranteed to be outside the Polyline, because it will be outside the entire extents of the drawing [slightly to the upper right of the upper-right-most corner of the extents].

 

Another way is to use one of the routines you can search for to determine whether the Polyline was drawn in a clockwise or counterclockwise direction, and based on that determination, use (vla-offset) with the appropriate sign on the distance [to go outboard, a positive distance if drawn CCW, negative if CW].

 

Beware the potential complications if a closed Polyline is self-intersecting, in which case "inside" and "outside" are ambiguous, and the first suggested approach can be thrown off if there could be more than one resulting Polyline, the one it sees as the last object possibly not being a good area comparison [red original, green results]:

Kent1Cooper_1-1706559780601.png

Even one that isn't self-intersecting could have that problem, depending on the shape and the Offset distance:

Kent1Cooper_0-1706559321307.png

Kent Cooper, AIA
0 Likes
Message 7 of 15

paullimapa
Mentor
Mentor
Accepted solution

try this which allows you to select multiple closed plines and uses the geocenter objectsnap option to hopefully locate the center of the pline and uses that point as the offset:

; offsetpl offsets a distance of 1 unit to inside of closed selected plines
; OP:
; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/lisp-function-for-offset-a-polyline/m-p/12527410
(defun c:offsetpl (/ en n selSet offsetDist osmode) ; localize variables
 (vl-load-com) ; load vl functions
  (setq selSet (ssget '((0 . "POLYLINE,LWPOLYLINE") (-4 . "&") (70 . 1)))) ; select closed plines
  (if selSet
    (progn
      (setq osmode (getvar "osmode")) ; save current running object snaps
      (setvar "osmode" 0) ; turn off running object snaps
      (setq n 0) ; start loop
      (while (< n (sslength selSet))
       (setq en (vlax-ename->vla-object (ssname selSet n)) ; convert entity to vl object
             pt (osnap (vlax-curve-getStartPoint en) "_gcen") ; get center of closed pline
       )
       (if pt
        (command "_.Offset" "1.0" (ssname selSet n) pt "")
        (progn
         (redraw en 3) ; highlite pline
         (princ"\nCould not locate Pline's inside point.")
        )
       ) ; if
       (setq n (1+ n)) ; next item
      ) ; while
      (setvar "osmode" osmode) ; restore previous running object snaps
      (princ "\nOffsetting polyline completed.")
    ) ; progn
    (princ "\nNo Closed polyline selected.")
  ) ; if 
  (princ)
) ; defun

Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
Message 8 of 15

Kent1Cooper
Consultant
Consultant

@paullimapa wrote:

....

   ....
   (setq vertexCount (vlax-curve-getEndParam polylineObj))       ; gets the last parameter in pline
   (setq vertexCount (+ 1 endParam)) ; add 1 to get total number of vertices

Line 3 is correct if the Polyline is open.  If it's closed, the end parameter is the number of vertices [with (fix) applied if you need it to be an integer, such as for use in a (repeat) function].

 

But if it's always a LWPolyline, the (cdr (assoc 90 EntityDataList)) approach is probably simpler -- no difference whether it's open or closed.

Kent Cooper, AIA
Message 9 of 15

Kent1Cooper
Consultant
Consultant

@paullimapa wrote:

... uses the geocenter objectsnap option to hopefully locate the center of the pline and uses that point as the offset:

; offsetpl offsets a distance of 1 unit to inside of closed selected plines
....

[See Message 3 again -- looking to Offset to the outside.]

Kent Cooper, AIA
0 Likes
Message 10 of 15

paullimapa
Mentor
Mentor

oops I miss the outside part


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 11 of 15

Kent1Cooper
Consultant
Consultant

@abbas.baghernezhad wrote:
....

 

(defun c:plcoord ()
....      
      (if (> vertexCount 0)
        (progn
          ....
        )
        (princ "\nThe polyline has no vertices.")
      )
....

....


ChatGPT is still notoriously bad at AutoLisp.  It just doesn't yet know the language well enough.

 

In addition to other things already pointed out, it is not possible for a Polyline to have no vertices, or even only one.  If that vertexcount variable were set in a valid way, and a Polyline is actually selected, the variable value would always be 2 or more.

Kent Cooper, AIA
Message 12 of 15

abbas.baghernezhad
Enthusiast
Enthusiast

I know here is Lisp Forum but my main code is being written in VBA. I can easily find a vertex coordinate. How can I use it in a lisp command?

Assuming that there is a LispFunction that takes 2 point coordinates and does sth . This line doesn't work! how should I write it?

...
EntVertex = Ent.Coordinates()
ThisDrawing.SendCommand "_LispFunction" & vbCr & Ent.Coordinates(0),Ent.Coordinates(1) & vbCr 

 

0 Likes
Message 13 of 15

Sea-Haven
Mentor
Mentor

The alternative to Geocenter is check is pline CW or CCW and use VLA-offset as a +ve value is always inside, -ve outside. 

0 Likes
Message 14 of 15

Kent1Cooper
Consultant
Consultant

@Sea-Haven wrote:

.... use VLA-offset as a +ve value is always inside, -ve outside. 


Not always.  Positive is always to the right from the point of view of the drawn direction of the object, negative to the left.  That means that As AutoCAD thinks of drawn direction for such things [regardless of how they were actually drawn], for an Arc/Circle/Ellipse, positive always goes outside, negative inside.  For a Polyline or a Spline that has an inside and outside [i.e. typically a closed one], positive goes inside only if it is drawn in the Clockwise direction.

Here, the yellow are the original Polylines, identical except for their drawn directions which are shown by the green arrows, and the red are the results from (vla-offset), both with a positive value -- outside for one of them, inside for the other.

Kent1Cooper_0-1706793485321.png

Kent Cooper, AIA
0 Likes
Message 15 of 15

Sea-Haven
Mentor
Mentor

I agree with " only if it is drawn in the Clockwise direction." that is why I said "needs a CW CCW check". If open and reset to say CW can still offset to what appears to be wrong side, so simplest way may be just offset and add a swap, could be an Enter to accept or this.

SeaHaven_0-1706837280119.png

 

 

 

 

0 Likes