klbr Posted Thursday at 02:19 AM Posted Thursday at 02:19 AM Hello, I'm new to AutoLISP and am trying to write a lsp to change the coordinates of all polylines in a drawing. I'm trying to loop through all polylines using a while loop, but my expression to stop it doesn't seem to be working. I really don't see what i'm doing wrong here, does anyone have an idea? (defun c:tf () (setq enlast (entlast)) (setq en (entnext)) (while (/= en enlast) (setq ed1 (entget en)) (setq ed2 '()) (foreach x ed1 (if (eq (car x) 10) (setq ed2 (append ed2 (list (list 10 (+ 100 (cadr x)) (+ 100 (caddr x)))))) (setq ed2 (append ed2 (list x))) ) ) (entmod ed2) (setq en (entnext en)) ) ) Quote
pkenewell Posted Thursday at 04:04 PM Posted Thursday at 04:04 PM (edited) @klbr I think your going about this the wrong way: 1) in your loop, you have no way of filtering for Polylines, so it will change ANYTHING that has a DXF code 10 (which is most of your geometric entities, text, blocks, etc.). It is much easier to perform the function (ssget "X" '((0 . "LWPOLYLINE"))) to get the selection set of Polylines, then iterate through it. 2) Since your loop is re-arranging the database, en will never equal entlast, which is why the loop doesn't stop. Here is what I would recommend: (defun c:tf (/ el el2 en i ss) (if (setq ss (ssget "X" '((0 . "LWPOLYLINE")))) (repeat (setq i (sslength ss)) (setq en (ssname ss (setq i (1- i))) el (entget en) el2 nil ) (foreach d el (if (= (car d) 10) (setq el2 (append el2 (list (list (car d) (+ 100.0 (cadr d)) (+ 100.0 (caddr d)))))) (setq el2 (append el2 (list d))) ) ) (entmod el2) ) ) (princ) ) Edited Thursday at 08:38 PM by pkenewell 1 Quote
BIGAL Posted Thursday at 10:15 PM Posted Thursday at 10:15 PM If you just want a add to X & Y why not use move with 0,0,0 X,Y,0. 1 Quote
pkenewell Posted Thursday at 10:24 PM Posted Thursday at 10:24 PM 7 minutes ago, BIGAL said: If you just want a add to X & Y why not use move with 0,0,0 X,Y,0. @BIGAL Yes - using the move command is easier, but I interpreted that the OP is trying to learn how to do entity manipulation in AutoLISP. Quote
Lee Mac Posted Thursday at 11:30 PM Posted Thursday at 11:30 PM 7 hours ago, pkenewell said: 2) Since your loop is re-arranging the database, en will never equal entlast, which is why the loop doesn't stop. This is not correct. Entities within the database are being modified, but the database is not being 'rearranged'. The reason that the loop does not terminate is because the equality & inequality operators work with strings & numerical data types, whereas the variables en & enlast are entity names (essentially pointers). As such, when comparing the pointers, the operators are not comparing the data to which they point, but the pointers themselves - this can be confirmed with the following simple test: _$ (setq e2 (entlast)) <Entity name: 2e2aabd68a0> _$ (setq e1 (car (entsel))) <Entity name: 2e2aabd68a0> Observe that variables e1 & e2 point to the same entity data, but have been created separately. When using the equality or inequality operators, the memory addresses of the pointers themselves are being compared, which are not equal: _$ (= e1 e2) nil _$ (/= e1 e2) T If we copy the pointer, the address will now be equal: _$ (setq e3 e1) <Entity name: 2e2aabd68a0> _$ (= e1 e3) T Instead, we should use the eq function to compare the data to which the pointers point: _$ (= e1 e2) nil _$ (eq e1 e2) T As such, the code could potentially be written: (defun c:tf ( / ed1 ed2 en enlast ) (setq enlast (entlast) en (entnext) ) (while (not (eq en enlast)) (setq ed1 (entget en) ed2 nil ) (if (= "LWPOLYLINE" (cdr (assoc 0 ed1))) (progn (foreach x ed1 (if (= 10 (car x)) (setq ed2 (append ed2 (list (list 10 (+ 100 (cadr x)) (+ 100 (caddr x)))))) (setq ed2 (append ed2 (list x))) ) ) (entmod ed2) ) ) (setq en (entnext en)) ) (princ) ) However, note that this will not operate on the last entity added to the database, and so to include it, we could write the code as: (defun c:tf ( / ed1 ed2 en ) (setq en (entnext)) (while en (setq ed1 (entget en) ed2 nil ) (if (= "LWPOLYLINE" (cdr (assoc 0 ed1))) (progn (foreach x ed1 (if (= 10 (car x)) (setq ed2 (append ed2 (list (list 10 (+ 100 (cadr x)) (+ 100 (caddr x)))))) (setq ed2 (append ed2 (list x))) ) ) (entmod ed2) ) ) (setq en (entnext en)) ) (princ) ) 2 Quote
klbr Posted Friday at 12:03 AM Author Posted Friday at 12:03 AM 7 hours ago, pkenewell said: @klbr I think your going about this the wrong way: 1) in your loop, you have no way of filtering for Polylines, so it will change ANYTHING that has a DXF code 10 (which is most of your geometric entities, text, blocks, etc.). It is much easier to perform the function (ssget "X" '((0 . "LWPOLYLINE"))) to get the selection set of Polylines, then iterate through it. 2) Since your loop is re-arranging the database, en will never equal entlast, which is why the loop doesn't stop. Here is what I would recommend: (defun c:tf (/ el el2 en i ss) (if (setq ss (ssget "X" '((0 . "LWPOLYLINE")))) (repeat (setq i (sslength ss)) (setq en (ssname ss (setq i (1- i))) el (entget en) el2 nil ) (foreach d el (if (= (car d) 10) (setq el2 (append el2 (list (list (car d) (+ 100.0 (cadr d)) (+ 100.0 (caddr d)))))) (setq el2 (append el2 (list d))) ) ) (entmod el2) ) ) (princ) ) @pkenewell Thanks a lot for your response! Using repeat as you proposed got it working! Quote
klbr Posted Friday at 12:08 AM Author Posted Friday at 12:08 AM 1 hour ago, pkenewell said: @BIGAL Yes - using the move command is easier, but I interpreted that the OP is trying to learn how to do entity manipulation in AutoLISP. @BIGAL Thanks a lot for the tip, but the + 100 is just a temporarily used function, as I want to manipulate the coordinates more after I got this first step working. Quote
klbr Posted Friday at 12:10 AM Author Posted Friday at 12:10 AM 39 minutes ago, Lee Mac said: This is not correct. Entities within the database are being modified, but the database is not being 'rearranged'. The reason that the loop does not terminate is because the equality & inequality operators work with strings & numerical data types, whereas the variables en & enlast are entity names (essentially pointers). As such, when comparing the pointers, the operators are not comparing the data to which they point, but the pointers themselves - this can be confirmed with the following simple test: _$ (setq e2 (entlast)) <Entity name: 2e2aabd68a0> _$ (setq e1 (car (entsel))) <Entity name: 2e2aabd68a0> Observe that variables e1 & e2 point to the same entity data, but have been created separately. When using the equality or inequality operators, the memory addresses of the pointers themselves are being compared, which are not equal: _$ (= e1 e2) nil _$ (/= e1 e2) T If we copy the pointer, the address will now be equal: _$ (setq e3 e1) <Entity name: 2e2aabd68a0> _$ (= e1 e3) T Instead, we should use the eq function to compare the data to which the pointers point: _$ (= e1 e2) nil _$ (eq e1 e2) T As such, the code could potentially be written: (defun c:tf ( / ed1 ed2 en enlast ) (setq enlast (entlast) en (entnext) ) (while (not (eq en enlast)) (setq ed1 (entget en) ed2 nil ) (if (= "LWPOLYLINE" (cdr (assoc 0 ed1))) (progn (foreach x ed1 (if (= 10 (car x)) (setq ed2 (append ed2 (list (list 10 (+ 100 (cadr x)) (+ 100 (caddr x)))))) (setq ed2 (append ed2 (list x))) ) ) (entmod ed2) ) ) (setq en (entnext en)) ) (princ) ) However, note that this will not operate on the last entity added to the database, and so to include it, we could write the code as: (defun c:tf ( / ed1 ed2 en ) (setq en (entnext)) (while en (setq ed1 (entget en) ed2 nil ) (if (= "LWPOLYLINE" (cdr (assoc 0 ed1))) (progn (foreach x ed1 (if (= 10 (car x)) (setq ed2 (append ed2 (list (list 10 (+ 100 (cadr x)) (+ 100 (caddr x)))))) (setq ed2 (append ed2 (list x))) ) ) (entmod ed2) ) ) (setq en (entnext en)) ) (princ) ) @Lee Mac Thanks a lot for the clarification! Now I get why my code wasn't working! Quote
klbr Posted Friday at 12:12 AM Author Posted Friday at 12:12 AM @Lee Mac @BIGAL @pkenewell Thanks for your responses! This part of the code is at least working now, but I'll open a separate topic for an issue I'm encountering in the next steps of what I'm trying to accomplish. Quote
BIGAL Posted Friday at 04:54 AM Posted Friday at 04:54 AM Lee has provided a great answer as usual, if you look at a VL answer you can use (Vlax-get obj 'coordinates) this will return a list of the vertices in a pline. so you can do your add 100 to the X&Y in a new list then. (Vlax-put obj 'coordinates list2). Object will update. Note though the co-ordinates list produced is (X Y X Y X Y...) a 3dpline will be (X Y Z X Y Z ....) so when changing you use a (nth k lst) for X then (nth (1+ k) lst) for the Y in a loop just use (repeat (/ (length lst) 2) then a (+ k 2) in loop. (setq obj (vlax-ename->vla-object (car (entsel "\nPick pline object ")))) (setq lst (vlax-get obj 'coordinates)) In your request easy as your adding 100 to each X & Y. Quote
Lee Mac Posted Friday at 08:48 AM Posted Friday at 08:48 AM FWIW, I might be inclined to write the program as follows - (defun c:tf ( / i s ) (if (setq s (ssget "_X" '((0 . "LWPOLYLINE")))) (repeat (setq i (sslength s)) (entmod (mapcar '(lambda ( x ) (if (= 10 (car x)) (mapcar '+ x '(0 100 100)) x)) (entget (ssname s (setq i (1- i)))) ) ) ) ) (princ) ) 1 Quote
GLAVCVS Posted Friday at 09:27 AM Posted Friday at 09:27 AM (edited) 2 hours ago, Lee Mac said: FWIW, I might be inclined to write the program as follows - (defun c:tf ( / i s ) (if (setq s (ssget "_X" '((0 . "LWPOLYLINE")))) (repeat (setq i (sslength s)) (entmod (mapcar '(lambda ( x ) (if (= 10 (car x)) (mapcar '+ x '(0 100 100)) x)) (entget (ssname s (setq i (1- i)))) ) ) ) ) (princ) ) @Lee Mac Forgive my ignorance: I see that you tend to prefer using 'repeat' over other language functions. Is there a reason for this?. I always thought 'repeat' was less efficient. But I'm probably wrong. Edited Friday at 10:59 AM by GLAVCVS Quote
GLAVCVS Posted Friday at 10:21 AM Posted Friday at 10:21 AM I mean: Wouldn't it be better...? (setq n -1) (while (setq ent (ssname s (setq n (1+ n)))) ... Quote
pkenewell Posted Friday at 01:43 PM Posted Friday at 01:43 PM 14 hours ago, Lee Mac said: This is not correct. Entities within the database are being modified, but the database is not being 'rearranged'. @Lee Mac You're correct of course. I was a bit rushed yesterday and didn't spend enough time thinking about it. Thanks for the correction and your great explanation. Quote
Lee Mac Posted Friday at 02:26 PM Posted Friday at 02:26 PM 4 hours ago, GLAVCVS said: @Lee Mac Forgive my ignorance: I see that you tend to prefer using 'repeat' over other language functions. Is there a reason for this?. I always thought 'repeat' was less efficient. But I'm probably wrong. 3 hours ago, GLAVCVS said: I mean: Wouldn't it be better...? (setq n -1) (while (setq ent (ssname s (setq n (1+ n)))) ... The difference in performance is negligible, but looking at it from a logical/semantic perspective: if we intend to iterate over the entire set and not modify the content of the set within the loop, we already know how many items are in the set ahead of time and so there needn't be a condition checked for every iteration (although, in practice the difference is negligible because repeat will be implemented as a basic for loop in the underlying C++ implementation of the LISP API, and therefore an integer comparison is still being performed with every iteration). Personally, I prefer the repeat method for a couple of reasons: I find it more readable and indicative of intent; it's faster for optimised code (i.e. optimised .fas); I typically iterate over a selection set in reverse, thus avoiding reindexing when removing items within the loop. Quote
GLAVCVS Posted Friday at 04:42 PM Posted Friday at 04:42 PM Perfect Thanks for your explanation Quote
Recommended Posts
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.