Creating a list of angles based off of vertices

Creating a list of angles based off of vertices

wian.meier
Enthusiast Enthusiast
2,363 Views
25 Replies
Message 1 of 26

Creating a list of angles based off of vertices

wian.meier
Enthusiast
Enthusiast

Good day all,

 

I've been sharpening my ax and I've almost created a functioning program, so close but so far.

 

I'm struggling to create a loop that goes through a vertices list and converts them to angles.

 

What I'm trying to do (visualized, I'm just trying to make a list that is usable in lisp):

 

1.PNG

 

This is what I've done so far:

 

(defun rtd (a)
(/ (* a 180.0) pi)
)

(while
(and
	(setq pt1 (car ptlist) pt2 (cadr ptlist))
);and
	(setq ang1 (angle pt1 pt2))
	(setq AngLst (abs rtd ang1))
	(setq ptlist (cdr ptlist))
);while

 

And this is where the code will be placed, right below

;------------------------------
;Angle list

 

In the place of - (setq AngLst '(180 175 170 163 170 90 63))

 

(defun C:CoordList (/ plent plobj coords num pt Ang1 Ang2 Ang3 AngLst pt1 pt2 pt3 ptlist Dif1 Dif2)
  (setq
    plent (car (entsel "\nSelect Polyline: "))
    plobj (vlax-ename->vla-object plent)
    coords (vlax-get plobj 'Coordinates); un-differentiated list of X Y [& Z if applicable] coordinate values
  ); setq
  (setq num (if (= (cdr (assoc 0 (entget plent))) "LWPOLYLINE") 2 3))

    ; LW Polylines have only X & Y; "heavy" 2D & 3D have X Y & Z
  (repeat (/ (length coords) num)
    (repeat num ; number of coordinates to separate into a point list
      (setq
        pt (append pt (list (car coords)))
        coords (cdr coords)
      )
    ); repeat
    (setq
      ptlist (cons pt ptlist); put that point into list of points
      pt nil ; reset for next point
    ); setq
  ); repeat
  (setq ptlist (reverse ptlist)); list of coordinates divided up into point lists
  ;; The above was originally made by Kent1Cooper https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/pick-a-polyline-and-create-a-list-of-its-coordinates/td-p/4934230
  ;; PS he is a legend
;------------------------------
;Angle list
;It depends on your "Angle" of approach

(setq AngLst '(180 175 170 163 170 90 63))


;------------------------------
;Insert the first pole

(command "insert" "5m 150-175" (car ptlist) "" "")

;------------------------------
;Insert the last pole

(command "insert" "5m 150-175" (last ptlist) "" "")

;------------------------------
;Insert the poles in between
;That's what she said....anyway code is below....the belt

(while
(and
(setq
	Ang1 (car AngLst)
	Ang2 (cadr AngLst)
	Ang3 (caddr AngLst)
);setq

(setq 
	pt1 (car ptlist)
	pt2 (cadr ptlist)
	pt3 (caddr ptlist))

(setq 
	Dif1 (- Ang1 Ang2);This is to determine angle difference between ponit 1&2
	Dif2 (- Ang2 Ang3);This is to determine angle difference between ponit 2&3
);setq
);and
; "IF" there is a better way to do the below script please let me know, sorry "IF" it looks a bit "IF"fy
			(if (>= Dif1 -15);1st angle constriction
				(progn
					(if (<= Dif1 15);2nd angle constriction
						(progn
							(if (<= Dif2 15);3rd angle constriction
								(progn
									(if (>= Dif2 -15);4th angle constriction
										(progn
											(command "insert" "5m 100-125" pt2 "" "")
										);progn
										(progn
											(command "insert" "5m 150-175" pt2 "" "")
										);progn
									);4th layer of IF function					
								);progn
								(progn
									(command "insert" "5m 150-175" pt2 "" "")
								);progn
							);3rd layer IF function
						);progn		
						(progn
							(command "insert" "5m 150-175" pt2 "" "")
						);progn
					);2nd layer IF function
				);progn
				(progn
				(command "insert" "5m 150-175" pt2 "" "")
				);progn
			);1st layer IF function
					
(setq ptlist (cdr ptlist));This will set the next point in the list for the loop
(setq AngLst (cdr AngLst));This will set the next angle in the list for the loop
);while
(princ);Everytime I see this I think of princles, maybe I was born to be fat.
);defun

 

So in short what this routine does, you select a polyline and a block will be on each vertices, the type of block will be determined by the angle of each vertices.

 

So far the whole experience feels like I'm giving birth, long and painful, but in the end it will be worth it! Though I do believe the code can be optimized especially at the "if" functions.

 

I would really appreciate any input! This is my first lisp, though I did use one of @Kent1Cooper  routines to determine the polyline vertices 

0 Likes
Accepted solutions (1)
2,364 Views
25 Replies
Replies (25)
Message 2 of 26

CodeDing
Advisor
Advisor

@wian.meier ,

 

With the way you have it set up, it doesn't look like you'll ever be able to insert a block on your next-to-last point? since you are always needing 2 angles forward, and we will always have 1 less angle than polyline vertices...

 

If the code you originally provided depicts your desired outcome, I believe I was able to accurately provide a version that you can easily interpret what could have been done differently / more efficiently. Hope it helps.

 

 

(defun c:CoordList ( / e eg ptList angList tmp blkName p2 dif1 dif2)
  ;get entity
  (if (not (and (setq e (car (entsel "\nSelect Polyline: ")))
                (eq "LWPOLYLINE" (cdr (assoc 0 (setq eg (entget e)))))))
    (progn (prompt "...Polyline not selected.") (exit))
  );if
  ;helper function
  (defun rtd (r) (/ (* r 180.0) pi))
  ;get point list
  (foreach x eg
    (if (= 10 (car x)) (setq ptList (cons (cdr x) ptList)))
  );foreach
  (setq ptList (reverse ptList))
  (setq tmp ptList)
  ;get angles
  (repeat (1- (length tmp))
    (setq angList (cons (angle (car tmp) (car (setq tmp (cdr tmp)))) angList))
  );repeat
  (setq angList (mapcar 'rtd (reverse angList)))
  ;first pole
  (command "_.INSERT" "5m 150-175" "non" (car ptlist) "" "")
  ;last pole
  (command "_.INSERT" "5m 150-175" "non" (last ptlist) "" "")
  ;main loop
  (repeat (- (length angList) 2)
    ;prep work
    (setq p2 (cadr ptList))
    (setq dif1 (- (car angList) (cadr angList))
          dif2 (- (cadr angList) (caddr angList)))
    ;determine block name
    (if (and (<= -15 dif1 15) (<= -15 dif2 15))
      (setq blkName "5m 100-125")
    ;else
      (setq blkName "5m 150-175")
    );if
    ;insert block
    (command "_.INSERT" blkName "non" p2 "" "")
    ;prep for next loop
    (setq ptList (cdr ptList) angList (cdr angList))
  );repeat
  (prompt "\nComplete.")
  (princ)
);defun

 

 

Best,

~DD

Message 3 of 26

wian.meier
Enthusiast
Enthusiast

Thank you for the response!

 

It seems like that was an oversight from my side, I would need a block placed on the 2nd last vertices, thought I think that problem can be solved by calculating the angle between the last three vertices? 

 

Other than that it's working great!

 

I didn't even know you could use multiple conditions in a "IF" function! This will be a good learning curb for me! Thank you. 

0 Likes
Message 4 of 26

wian.meier
Enthusiast
Enthusiast

While testing it a bit further I've noticed that it will place a wrong block from time to time

 

Capture.PNG

0 Likes
Message 5 of 26

Kent1Cooper
Consultant
Consultant

Your AngLst variable contains seven numbers, but your big nested-(if) function only includes two Block names.  Is that correct?  If some of those are supposed to be other names for other angles, that would explain the wrong-Block situation.  The far better way is to use (cond):

 

(command "_.insert"

  (cond ; for the Block name

    ((< theAngle smallestTestAngle) "smallestAngleBlockName")

    ((< theAngle secondTestAngle) "secondAngleBlockName")

    ((< theAngle thirdTestAngle) "thirdAngleBlockName")

    ((< theAngle fourthTestAngle) "fourthAngleBlockName")

;;   [etc.]

  ) ; cond

  thePoint "" "" ""

); command

 

Maybe those (<) functions should be (<=) instead -- I didn't study your code in great detail.

Kent Cooper, AIA
0 Likes
Message 6 of 26

wian.meier
Enthusiast
Enthusiast

Hi Kent,

 

That is correct, the thinner pole ("5m 100-125") needs to be placed if the angle variance is lower than 15 degrees, if it does exceed 30 degrees a thicker pole ("5m 125-175") needs to be placed. Depending on the situation the angle can go into negatives, depending on which quadrant point 1 and 2 is. Have a look at my beautiful illustration :

Capture 2.PNG

Message 7 of 26

wian.meier
Enthusiast
Enthusiast

Oops I meant to say 15 degrees!

0 Likes
Message 8 of 26

CodeDing
Advisor
Advisor

@wian.meier ,

 


I think that problem can be solved by calculating the angle between the last three vertices? 

Well, the angles created are not referencing 3 vertices at a time, they are using 2 vertices and an ortho direction (east). I am not sure what you are trying to get toward with this. Perhaps you can elaborate?

 


I didn't even know you could use multiple conditions in a "IF" function

An IF statement will only test 1 condition [in the case I provided, the (and ...) function]... The AND function is testing more than 1 condition though.

 


While testing it a bit further I've noticed that it will place a wrong block from time to time

According to the image you posted [and your original code sample], the correct conditions are being met for that block to be inserted..

Maybe the defined conditions are incorrect? Can you describe with words what conditions need to be met for a vertex to receive a specific block?

 

Best,

~DD

0 Likes
Message 9 of 26

wian.meier
Enthusiast
Enthusiast

Well, the angles created are not referencing 3 vertices at a time, they are using 2 vertices and an ortho direction (east). I am not sure what you are trying to get toward with this. Perhaps you can elaborate?


My apologies I might be over complicating things but here it goes:

 

If you reverse the list you can get the angle of the last point like this:

(setq RevPnt ( reverse pntList ))
(setq 
	lastpt (car RevPnt)
	secondlastpt (cadr RevPoints)
)
(setq secondlastAng (angle lastpt secondlastpt))

 

This should give you the angle, but it will be inversed. I'll have to look at how to inverse the result again, but according to my brain that should work.

 


 

0 Likes
Message 10 of 26

wian.meier
Enthusiast
Enthusiast

Alright so if the line bends more than 15 degrees on the point where the pole is placed, then a thicker pole should be placed ("5m 125-175") if the difference is less than 15 degrees, then a thinner pole should be placed ("5m 100-125")

 

As per the image I posted up a comment or two higher

0 Likes
Message 11 of 26

Kent1Cooper
Consultant
Consultant

And what if it's exactly  15 degrees?

Kent Cooper, AIA
0 Likes
Message 12 of 26

wian.meier
Enthusiast
Enthusiast

I see what you're saying, in this case it should be a variance of equal to or bigger than 15.

0 Likes
Message 13 of 26

dlanorh
Advisor
Advisor
Accepted solution

Try this. It makes use of @john.uhden 's @Anonymous function.

 

(defun @Anonymous (a1 a2)
  (cond ( (> a1 (+ a2 pi)) (+ a2 pi pi (- a1)))
        ( (> a2 (+ a1 pi)) (- a2 a1 pi pi))
        (t (- a2 a1))
  )
)

(defun c:test ( / c_doc c_spc ent sp ep pt ang blk lang)
  (setq c_doc (vla-get-activedocument (vlax-get-acad-object))
        c_spc (vlax-get-property c_doc (if (= 1 (getvar 'cvport)) 'paperspace 'modelspace))
        ent (car (entsel "\nSelect Polyline : "))
        sp 0.0
        ep (vlax-curve-getendparam ent)
  )
  (while (<= sp ep)
    (setq pt (vlax-curve-getpointatparam ent sp)
          ang (angle '(0.0 0.0 0.0) (vlax-curve-getfirstderiv ent sp))
    );end_setq
    (cond ( (or (zerop sp) (= sp ep)) (setq blk "5m 125-175"))
          (t (setq blk (if (< (abs (@delta lang ang)) (/ pi 12.0)) "5m 100-125" "5m 125-175")))
    )
    (vlax-invoke c_spc 'insertblock pt blk 1 1 1 0)
    (setq sp (1+ sp) lang ang)
  );end_while
)

 

I know this is a bit advanced as it involves visual lisp and the vlax-curve functions, but it shows you what is possible. 

I am not one of the robots you're looking for

Message 14 of 26

wian.meier
Enthusiast
Enthusiast

This blows my mind a little haha!

 

I tried testing it, but it give me the below error:

22.PNG

0 Likes
Message 15 of 26

wian.meier
Enthusiast
Enthusiast

Alright so this might be able to do the trick:

(defun rtd (r)
(/ (* r 180.0) pi)
)

(setq RevPnt ( reverse ptList ))

(setq 
	lastpt (car RevPnt)
	secondlastpt (cadr RevPnt)
)
(setq lastAng (angle lastpt secondlastpt))
(setq lastAngD (abs rtd lastAng))
(setq lastAng (- lastAng 180))

 

Though when I tried it returned with a too many arguments error

0 Likes
Message 16 of 26

wian.meier
Enthusiast
Enthusiast

I've made a "hybrid code" by combine some of yours and the worst of mine and the results are the same, so I am pretty sure I didn't define the conditions of the "if" function correctly

 

(defun C:CoordList (/ plent plobj coords num pt Ang1 Ang2 Ang3 AngLst pt1 pt2 pt3 ptlist Dif1 Dif2)
  (setq
    plent (car (entsel "\nSelect Polyline: "))
    plobj (vlax-ename->vla-object plent)
    coords (vlax-get plobj 'Coordinates); un-differentiated list of X Y [& Z if applicable] coordinate values
  ); setq
  (setq num (if (= (cdr (assoc 0 (entget plent))) "LWPOLYLINE") 2 3))

    ; LW Polylines have only X & Y; "heavy" 2D & 3D have X Y & Z
  (repeat (/ (length coords) num)
    (repeat num ; number of coordinates to separate into a point list
      (setq
        pt (append pt (list (car coords)))
        coords (cdr coords)
      )
    ); repeat
    (setq
      ptlist (cons pt ptlist); put that point into list of points
      pt nil ; reset for next point
    ); setq
  ); repeat
  (setq ptlist (reverse ptlist)); list of coordinates divided up into point lists
  ;; The above was originally made by Kent1Cooper https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/pick-a-polyline-and-create-a-list-of-its-coordinates/td-p/4934230
  ;; PS he is a legend
;------------------------------
;Angle list
;It depends on your "Angle" of approach

;(setq AngLst '(180 175 170 163 170 90 63))
	(setq tmp ptList)
  (repeat (1- (length tmp))
    (setq AngLst (cons (angle (car tmp) (car (setq tmp (cdr tmp)))) AngLst))
  );repeat
  (setq AngLst (mapcar 'rtd (reverse AngLst)))
;------------------------------
;Insert the first pole

(command "insert" "5m 150-175" (car ptlist) "" "")

;------------------------------
;Insert the last pole

(command "insert" "5m 150-175" (last ptlist) "" "")

;------------------------------
;Insert the poles in between
;That's what she said....anyway code is below....the belt

(while
(and
(setq
	Ang1 (car AngLst)
	Ang2 (cadr AngLst)
	Ang3 (caddr AngLst)
);setq

(setq 
	pt1 (car ptlist)
	pt2 (cadr ptlist)
	pt3 (caddr ptlist))

(setq 
	Dif1 (- Ang1 Ang2);This is to determine angle difference between ponit 1&2
	Dif2 (- Ang2 Ang3);This is to determine angle difference between ponit 2&3
);setq
);and
; "IF" there is a better way to do the below script please let me know, sorry "IF" it looks a bit "IF"fy
			(if (>= Dif1 -15);1st angle constriction
				(progn
					(if (<= Dif1 15);2nd angle constriction
						(progn
							(if (<= Dif2 15);3rd angle constriction
								(progn
									(if (>= Dif2 -15);4th angle constriction
										(progn
											(command "insert" "5m 100-125" pt2 "" "")
										);progn
										(progn
											(command "insert" "5m 150-175" pt2 "" "")
										);progn
									);4th layer of IF function					
								);progn
								(progn
									(command "insert" "5m 150-175" pt2 "" "")
								);progn
							);3rd layer IF function
						);progn		
						(progn
							(command "insert" "5m 150-175" pt2 "" "")
						);progn
					);2nd layer IF function
				);progn
				(progn
				(command "insert" "5m 150-175" pt2 "" "")
				);progn
			);1st layer IF function
					
(setq ptlist (cdr ptlist));This will set the next point in the list for the loop
(setq AngLst (cdr AngLst));This will set the next angle in the list for the loop
);while
(princ);Everytime I see this I think of princles, maybe I was born to be fat.
);defun

 

0 Likes
Message 17 of 26

john.uhden
Mentor
Mentor
Thanks for the reference, but his picture shows all the angles to be
measured from the horizontal (0°), so there should be no mathematics
involved.

John F. Uhden

0 Likes
Message 18 of 26

CodeDing
Advisor
Advisor

@john.uhden ,

 

his picture shows all the angles to be measured from the horizontal (0°), so there should be no mathematics involved.

I disagree. Because the angles are relative to the previous and next points on the line. I was sitting here racking my brain trying to think of a conversion function for a new solution, until dlanorh posted yours. Which is MUCH shorter than I expected. It's great. Kudos.

 

Because we need to account for whether the PLine can go any ANY direction, and account for ANY angle.

This means that an angle of 356 compared to an angle of 10, are in fact (in this case) 14 degrees apart, not 326 degrees apart (as they would be if we use a 0 degree horizontal reference).

 

OP should be mindful that if the PLine were to go in any other direction (or even be REVERSED for that matter) then those scenarios must be accounted for also.

 

Best,

~DD

0 Likes
Message 19 of 26

john.uhden
Mentor
Mentor

After reading more responses that I guess I never saw, it appears that the OP is intending to use larger (diameter) poles where the deflection is greater, which makes sense if he's building a fence or hanging utility lines..  That's where the @Anonymous function is required.  In fact that may be all that's needed.  Then his conditions can look something like (semi-pseudo):

(setq pole

  (cond

    ((<= 0° delta 30°) "SMALL")

    ((<= 30° delta 60°) "MEDIUM")

    ((> delta 60°) "LARGE")

  )

)

Am I close (not that I recommend those particular values)?

 

John F. Uhden

0 Likes
Message 20 of 26

devitg
Advisor
Advisor

MAybe if the OP upload a sample.dwg will be easy to get what he want to . 

And not more guessing .