Jump to content

Recommended Posts

Posted

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))
   )
)

 

Posted (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 by pkenewell
  • Like 1
Posted
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.

Posted
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)
)

 

  • Like 2
Posted
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!

Posted
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.

Posted
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!

Posted

@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.

Posted

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.

 

Posted

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)
)

 

  • Like 1
Posted (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 by GLAVCVS
Posted

I mean:
Wouldn't it be better...?
 

(setq n -1)
(while (setq ent (ssname s (setq n (1+ n))))
  ... 

 

Posted
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.

Posted
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.

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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  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...