Manipulating or changing polyline vertices

Manipulating or changing polyline vertices

Anonymous
Not applicable
5,597 Views
13 Replies
Message 1 of 14

Manipulating or changing polyline vertices

Anonymous
Not applicable

How exactly does one modify a polyline using lisp?

If I entsel a rectange (four vertices obviously) it will return four assoc 10 codes

How can these be differentiated between for manipulating/changing?

If it were a simple line I'd entsel or ssget the line, then use the subst function to change the relevant start/end point (assoc 10 ent) or (assoc 11 ent).

A polyline will only return the first assoc 10, not the other three (for a rectangle)

0 Likes
Accepted solutions (2)
5,598 Views
13 Replies
Replies (13)
Message 2 of 14

Kent1Cooper
Consultant
Consultant
Accepted solution

One thing you can do is to work with the Coordinates VLA property, rather than the entity data.

 

Make it a VLA object stored as a variable:

 

(vl-load-com); [if needed]

(setq obj (vlax-ename->vla-object (car (entsel "\nSelect a Polyline: "))))

 

Then:

(vlax-get obj 'Coordinates)

 

returns, for a [my simple example] 12w x 6h rectangle with its starting lower left corner at 0,0 and drawn counterclockwise:


(0.0 0.0 12.0 0.0 12.0 6.0 0.0 6.0)

 

That is a list of coordinate values, strung out and undifferentiated, but in order.  In the case of a LWPolyline, there are only X and Y coordinates [the Z coordinate is common, and stored as the Elevation property], so there will be two numbers per vertex -- the first two are the X & Y of the first vertex, the next two of the second vertex, etc.  In the case of a "heavy" Polyline [2D or 3D], there are XYZ coordinates, so there will be three numbers per vertex.  It's not difficult to pull the pairs or triads of numbers apart into separate point lists, and you can play with those and (append) them back together into the undifferentiated list again.

 

Say you want to move the upper right corner of that rectangle up 1 unit.  That means the third vertex, represented by the 12.0 6.0 pair, needs to be 12.0 7.0 instead.  You would manipulate the list to change it to:

 

(0.0 0.0 12.0 0.0 12.0 7.0 0.0 6.0)

 

Then you can impose that revised list of coordinates back on the Polyline this way:

 

(vlax-put obj 'Coordinates '(0.0 0.0 12.0 0.0 12.0 7.0 0.0 6.0))

 

[If your Polylines may ever have arc segments, be aware that the results could be peculiar -- all the above does is replace the vertex locations, but it doesn't change things like arc-segment bulge factors.]

 

EDIT:  But you can also do it with entity data.  If 'edata' is a variable holding the entity data, then:

 

(vl-remove-if-not '(lambda (x) (= (car x) 10)) edata)

 

extracts just the vertex location entries into a list, such as [for the same example rectangle]:


((10 0.0 0.0) (10 12.0 0.0) (10 12.0 6.0) (10 0.0 6.0))

 

You can work on the third item so that you get:

 

(10 12.0 7.0)

 

On the assumption that vertex locations will all be unique [they certainly should be, or you've been drawing wrong], you could then (subst) that new one in place of the old one, with no risk of its finding the wrong (assoc 10) value:

 

(entmod (subst '(10 12.0 7.0) '(10 12.0 6.0) edata))

 

[Same caveat about arc segments.]

 

But the advantage of the Coordinates VLA Property approach is that it will work on any variety of Polyline, whereas that entity-data approach will work only on LWPolylines.  [Something entity-data-based could also be done for "heavy" ones, but it would have to work differently, involving (entnext) to step through vertices.]

Kent Cooper, AIA
Message 3 of 14

ВeekeeCZ
Consultant
Consultant

@Kent1Cooper wrote:

 

...

 

EDIT:  But you can also do it with entity data.  If 'edata' is a variable holding the entity data, then:

 

(vl-remove-if-not '(lambda (x) (= (car x) 10)) edata)

 

extracts just the vertex location entries into a list, such as [for the same example rectangle]:


((10 0.0 0.0) (10 12.0 0.0) (10 12.0 6.0) (10 0.0 6.0))

 

You can work on the third item so that you get:

 

(10 12.0 7.0)

 

On the assumption that vertex locations will all be unique [they certainly should be, or you've been drawing wrong], you could then (subst) that new one in place of the old one, with no risk of its finding the wrong (assoc 10) value:

 

(entmod (subst '(10 12.0 7.0) '(10 12.0 6.0) edata))


In addition what Kent says... you need to find which vertex you gonna change. So use (entsel) to select a polyline, then it's point (pt variable in following code) to get a parametr of polyline vertex. Like this:

 

(setq par (fix (vlax-curve-getParamAtPoint en (vlax-curve-getClosestPointTo en pt))))

 

Then you have the integer which you can use for (nth par '((10 0.0 0.0) (10 12.0 0.0) (10 12.0 6.0) (10 0.0 6.0)))

(untested)

 

Message 4 of 14

Kent1Cooper
Consultant
Consultant

@ВeekeeCZ wrote:


... you need to find which vertex you gonna change. So use (entsel) to select a polyline, then it's point (pt variable in following code) to get a parametr of polyline vertex. Like this:

 

(setq par (fix (vlax-curve-getParamAtPoint en (vlax-curve-getClosestPointTo en pt))))

 

Then you have the integer which you can use for (nth par '((10 0.0 0.0) (10 12.0 0.0) (10 12.0 6.0) (10 0.0 6.0)))

.... 


It's not clear to me how they intend to determine the vertex to work on, but if it's by selecting the Polyline at it, the suggested approach would lead you astray if you happen to pick even just slightly to the "upstream" side of the vertex -- the (fix) function will round down and it will return the parameter value of the previous vertex.  I would suggest forcing ENDpoint object snap, so that it gets picked at a vertex and the getClosestPointTo part isn't needed, or using a round-to-the-nearest-whole-number function on the parameter value returned.

Kent Cooper, AIA
0 Likes
Message 5 of 14

ВeekeeCZ
Consultant
Consultant
Ouu.. I took wrong example... but the thing was to show that vertex could be determined by parameter.
Thanks Kent for correction..
0 Likes
Message 6 of 14

Anonymous
Not applicable

My goodness, how easy is that?

 

I never knew it could be so staightforward & very clearly explained Kent

 

Thank you very much indeed

 

 I shall enjoy exploring the possiblities

 

Thank you too BeekeeCZ

0 Likes
Message 7 of 14

Anonymous
Not applicable

Kent, the above works great if the rectangle hasn't been rotated. Ive just tested your code here at work & some of the rectangles I'd like to move their vertices have been rotated.

 

As an example:

 

Non rotated rectangle:

(setq obj (vlax-ename->vla-object (car (entsel "\nSelect a Polyline: "))))
#<VLA-OBJECT IAcadLWPolyline 000000003b679968>
_$ (vlax-get obj 'Coordinates)
(10.0 10.0 30.0 10.0 30.0 30.0 10.0 30.0)

Rotated (same) rectangle:

 

_$ (setq obj (vlax-ename->vla-object (car (entsel "\nSelect a Polyline: "))))
#<VLA-OBJECT IAcadLWPolyline 000000003b679968>
_$ (vlax-get obj 'Coordinates)
(30.0 10.0 30.0 30.0 10.0 30.0 10.0 10.0)

As you can see the first coodinate set [30.0 10.0] is no longer the lower left, but the lower right

 

 

Is there a way to 'force' the function to always return the coordinates in the order Lower Left, Lower Right, Upper Right, Upper Left ?

0 Likes
Message 8 of 14

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

Kent, the above works great if the rectangle hasn't been rotated. Ive just tested your code here at work & some of the rectangles I'd like to move their vertices have been rotated.

.... 

Is there a way to 'force' the function to always return the coordinates in the order Lower Left, Lower Right, Upper Right, Upper Left ?


Yes, that will always return them in the order drawn, which of course might not just start other than at lower left, but also be clockwise rather than counterclockwise.  If they're always rectangles and always orthogonally oriented to begin with, you can get that "sequence" by calculations from the rectangle's Bounding Box.  Would that be the case?

 

If they're ever not rectangular, or rotated at some non-orthogonal angle, that approach won't work, though there is probably a way to sort the vertices, but questions arise, such as:  If one is rotated at 45 degrees, is the vertex you want first the left one or the bottom one?  Would the answer to the similar question be different for one rotated at 30 degrees than for one rotated at 60 degrees?  Etc., etc.

Kent Cooper, AIA
0 Likes
Message 9 of 14

Anonymous
Not applicable

Perhaps a little background would help here.

I'm cleaning up a load of scruffy old block cable diagrams (which are to be reused soon or redrawn completly) as a 'stock' job which I do when things are a little quiet. The original draughtman drew these with little regard to the snap button.

I noticed that a lot of the rectangular polylines in the drawings were not on an exact snap point, or even close. The lines wern't a problem as I already use a lisp to trim/extend orthagonally & I thought I'd add the functionality of putting the vertices of the rectangles on a snap point supplied by the line trim/extend lisp so that a trim/extend rectangle function is added.

My test function (ALPline.lsp) worked great while experimenting at home, It never occured to me that some of the rectangles might be rotated or mirrored. ALPline.lsp works only on the X axis at present

 

(defun c:ALPlineX ( / cor idx lst obj p1 p2 ss)
  (vl-load-com)
  (setvar "Osmode" 16439)
  (setq ss (ssget "_:L" '((0 . "*POLYLINE"))))
  (command "_snap" "on")
    (setq p1 (getpoint"\nPick Left Hand X Alginment Point"))
    (setq p2 (getpoint"\nPick Right Hand X Alginment Point"))
    (command "_snap" "off")
  
   (setq idx 0)
   (if ss
  (repeat (sslength ss)    
  (setq obj (vlax-ename->vla-object (ssname ss idx))); Kents Code
  (setq cor (vlax-get obj 'Coordinates)); Kents Code
   (cond
     ((=(length cor) 4)
     (setq lst (list (car p1) (nth 1 cor) (car p2) (nth 1 cor)))
        (vlax-put obj 'Coordinates lst)
      )
    ((=(length cor) 8)
  (setq lst (list (car p1) (nth 1 cor) (Car p2) (nth 1 cor) (Car p2) (nth 7 cor) (car p1) (nth 7 cor)))
       (vlax-put obj 'Coordinates lst))); Kents Code
	)
     (t
      (princ "\n Invalid Number of Vertices")
      )
    )

    (setq idx (1+ idx))
    ); repeat
     (princ "\nNo Polylines Selected")
     )
  (princ)
)
0 Likes
Message 10 of 14

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... I'm cleaning up a load of scruffy old block cable diagrams .... The original draughtman drew these with little regard to the snap button.

I noticed that a lot of the rectangular polylines in the drawings were not on an exact snap point, or even close. .... 


Search these Forums [and maybe other places like The Swamp and Cadalyst CAD Tips] -- there are routines around that will force the endpoints/vertices/etc. of all selected objects to the nearest Snap location.

Kent Cooper, AIA
0 Likes
Message 11 of 14

Anonymous
Not applicable

OK, thank you Kent

0 Likes
Message 12 of 14

Anonymous
Not applicable

Kent wrote: '.....all selected objects to the nearest Snap location'

 

The problem with the nearest snap location is that the rectangle often ends up scewed because some of the points are nearer to a 'wrong' snap point. I have perservered with the idea of setting the coordinates of the vertices to a point determined by a getpoint & come up with the below:

 

The problem I now have is that the entmod functions only works on one vertice, they actually work on both, but the second entmod undoes the first one!

 

My code so far....

(defun c:test ( / entx oll olr nlr nur pt slst oul our verlst)
  (setvar "Osmode" 16439)
  (command "_snap" "on")
(setq pt (getpoint "\n Select X Alignment Point"))
(setq entx (entget(car(entsel))))
;(setq coords (mapcar 'cdr(vl-remove-if-not '(lambda (x) (= (car x) 10)) entx)))
(setq verLst (vl-remove-if '(lambda(x)(/= 10(car x)))entx)
      sLst (vl-sort (vl-sort verLst '(lambda(a b) (<(car a) (car b)))) 
               '(lambda (a b) (<(cadr a) (cadr b))))
       oll (car sLst)
       olr (caddr sLst)
       oul (caddr sLst)
       our (last sLst)
        )
 (setq  nlr (list 10 (car pt) (caddr olr))
 	nur (list 10 (car pt) (caddr our)))
(entmod (subst nlr olr entx))
(entmod (subst nur our entx))
  (command "_snap" "off")
  (princ)
  )

With thanks to an old post by ASMI in 2008 #6

 

0 Likes
Message 13 of 14

Kent1Cooper
Consultant
Consultant
Accepted solution

@Anonymous wrote:

....

The problem I now have is that the entmod functions only works on one vertice, they actually work on both, but the second entmod undoes the first one!

 

My code so far....

....
(entmod (subst nlr olr entx))
(entmod (subst nur our entx))
....

.... 


Do it this way, so that the second (entmod)'s (subst) function is working on an entity data list that's had the other vertex substituted, not on the original list:

 

....
(entmod (setq entx (subst nlr olr entx)))
(entmod (subst nur our entx))
....

 

You could actually omit the first (entmod), and just do the first substitution, then do the (entmod) only along with the second substitution:

 

....
(setq entx (subst nlr olr entx))
(entmod (subst nur our entx))
....

Kent Cooper, AIA
Message 14 of 14

Anonymous
Not applicable

Excellent Kent

Problem 110% solved

 

It works on any rotation of a polyine rectangle btw

 

Thank you so very much

0 Likes