Exploding and re-joining a polyline in a lisp program

Exploding and re-joining a polyline in a lisp program

jmillerWL6P5
Participant Participant
2,288 Views
10 Replies
Message 1 of 11

Exploding and re-joining a polyline in a lisp program

jmillerWL6P5
Participant
Participant
Hello! I'm fairly new to AutoCAD lisps, and I'm having issues adding to an existing lisp. The attached lisp program is a helpful divide command that will insert the desired block a minimum distance across any line, arc, or polyline. This tool is extremely helpful, but I'm wishing to expand on this command even further. I'm looking to explode all of the selected polylines, then do the divide command, and then rejoin the exploded lines back into a polyline. I don't doubt there is a way of doing this, and it's probably much easier than I'm making it out to be, but the hardest part for me is adding it into this existing lisp. If you have any ideas or solutions, I'd greatly appreciate it!
 
Any portion of the code in Italics is a user inserted variable...
(vl-load-com)
 
(defun c:SampleCommand ( / s b e i l)
(setvar "CLAYER" "SampleLayer")
 
  (if (and (or (tblsearch "BLOCK" "SampleBlock")
       (prompt "Error: 'SampleBlock' block not found in the drawing!"))
   (setq s (ssget '((0 . "LINE,ARC,LWPOLYLINE")))))
    (repeat (setq i (sslength s))
      (setq e (ssname s (setq i (1- i)))
    l (vlax-curve-getdistatparam e (vlax-curve-getendparam e)))
      (command  "_.divide" e "_b" "SampleBlock" "_n" (1+ (fix (/ l VALUE))))))
  (princ)
  )
0 Likes
Accepted solutions (1)
2,289 Views
10 Replies
Replies (10)
Message 2 of 11

Kent1Cooper
Consultant
Consultant

[I think that should be structured differently in regard to the existence of the Block.  It looks like if it doesn't exist, it will tell you that but then still try to proceed with the operation.]

 

You would need to EXPLODE the Polylines, and JOIN them back together later.  I chose to keep any Lines/Arcs in the initial selection as a separate selection set so that JOINing can use the Polyline pieces only.  But it could also be done with one selection set to apply Divide to, by stepping through the Explode-results set and adding each object in it to the original set.  Then JOIN could be done on everything, and the Lines/Arcs that don't connect to the Polyline pieces will be ignored.  But that involves the risk that some initial Lines/Arcs might meet the ends of some initial Polylines, in which case they would become parts of them.

 

Try something like this [untested]:

 

....
(setvar 'peditaccept 1); for PEDIT later
(if (tblsearch "BLOCK" "SampleBlock")
  (progn ; then
    (if
      (setq ss (ssget '((0 . "LINE,ARC,LWPOLYLINE"))))
      (progn ; then
        (setq ssp (ssget "_P" '((0 . "LWPOLYLINE"))); only Polylines from overall
        (repeat (setq n (sslength ssp)); remove Polylines from overall
          (ssdel (ssname ssp (setq n (1- n))) ss)
        ); repeat
        (initcommandversion); to make next line work if more than one
        (command "_.explode" ssp "")
        (setq ssp (ssget "_P")); resulting Lines/Arcs from Exploding Polylines
        (foreach ss (list ss ssp)
          (repeat (setq i (sslength ss))
            (setq
              e (ssname ss (setq i (1- i)))
              l (vlax-curve-getdistatparam e (vlax-curve-getendparam e))
            )
            (command "_.divide" e "_b" "SampleBlock" "_n" (1+ (fix (/ l VALUE))))
          ); repeat
        ); foreach
        (command "_.join" ssp ""); put Polylines back together
      ); progn
    ); if [selection
  ); progn [Block exists]
  (prompt "Error: 'SampleBlock' block not found in the drawing!"); else
); if
....

 

Save the PEDITACCEPT System Variable value beforehand to restore later, if you want.  And you should probably do the same with the current Layer setting.

 

Also, if the initial Polylines include any non-zero width(s), that will all be lost.  If that's an issue, there is another way that it could be approached, though it would be more complicated.

Kent Cooper, AIA
0 Likes
Message 3 of 11

jmillerWL6P5
Participant
Participant

Kent,

 

I am attempting to test this program, but there is no command defined for this lisp to run. I know enough to be dangerous, but not enough to add onto your code without getting errors in AutoCAD. Can you please adjust the code and test it to ensure it's functioning before it reaches my hands? Then if it doesn't work I know it's my mistakes.

 

I also am less concerned with the error message if the block is not found.

0 Likes
Message 4 of 11

Kent1Cooper
Consultant
Consultant
Accepted solution

Try this [minimally tested]:

(defun c:SampleCommand (/ ss ssp n s i e l)
  (setvar "CLAYER" "SampleLayer")
  (setvar 'peditaccept 1); for PEDIT later
  (if (tblsearch "BLOCK" "SampleBlock")
    (progn ; then
      (if (setq ss (ssget '((0 . "LINE,ARC,LWPOLYLINE")))); valid selection
        (progn ; then
          (if (setq ssp (ssget "_P" '((0 . "LWPOLYLINE")))); only Polylines from overall, if any
            (progn ; then
              (repeat (setq n (sslength ssp)); remove Polylines from overall
                (ssdel (ssname ssp (setq n (1- n))) ss)
              ); repeat
              (initcommandversion); to make next line work if more than one
              (command "_.explode" ssp "")
              (setq ssp (ssget "_P")); resulting Lines/Arcs from Exploding Polylines
            ); progn
          ); if [get pieces from Exploded Polylines]
          (foreach s (list ss ssp)
            (repeat (setq i (sslength s))
              (setq
                e (ssname s (setq i (1- i)))
                l (vlax-curve-getdistatparam e (vlax-curve-getendparam e))
              ); setq
              (command "_.divide" e "_b" "SampleBlock" "_n" (1+ (fix (/ l VALUE))))
            ); repeat
          ); foreach
          (command "_.pedit" "_multiple" ssp "" "_join" "" ""); put Polylines back together
        ); progn
      ); if [selection]
    ); progn [then -- Block exists]
    (prompt "Error: 'SampleBlock' block not found in the drawing!"); else
  ); if
  (princ)
)
Kent Cooper, AIA
0 Likes
Message 5 of 11

jmillerWL6P5
Participant
Participant

Hello Kent,

 

The code you provided works wonders, but I want to make a few adjustments. Before I make those adjustments, I want to get a better understanding of what is occurring in the lisp. I've typed out my understanding of what is occurring in each line of code, but there are certain areas I'm lost in. If it's not too much of a pain, could you look over my crude explanation of the code and correct/explain to me what's occurring? I'm using this lisp as a building point on my lisp skills, and want to understand this code in and out.

 

1. Defines the function's alias and variables( I'm trying to deduce what each variable corelates to.)

2. Sets the current layer to whatever desired.

3. Sets the PEDIT value to 1.

4.Starts an IF statement by looking for the desired block.

5. Starts the THEN statement.

6. An If statement that sets a variable to the selected objects.

7. States a THEN statement.

8. Starts an IF statement that pulls from the previous selection, but only pulls the Polylines from it, and then sets those selected Polylines to a variable.

9. THEN statement.

10. Starts a REPEAT statement that pulls the number of selected polylines, and sets that value to a variable.

11. This one is confusing, but it looks like you're taking the number of polylines and decreasing the value by one, and then setting that as the new value for the number of polylines. Then pulling the name of the objects, to then delete the new number of polylines from the selected objects. Am I on the right track?

12. End of REPEAT function.

13. Does this command make the explode command work when the previous command runs multiple times?

14. Explodes the selected polylines.

15. Selects the exploded lines from the polylines and assigns them to a variable.

16. End of the THEN statement.

17. End of IF statement.

18. Combines the Selected objects with the selected polylines and assigns them to a new variable.

19. Starts a REPEAT statement that pulls the number of objects from the previous variable and sets that number to a new variable.

20. Start of a set variable function.

21. Takes the value associated to the number of selected objects and selected polylines and decreases that value, only to then assign the new value to the same variable. Then pulls the type of entity and assigns that to a new variable?

22. Pulls the length/distance from the two end points of a line and assigns that to a variable.

23. End of variable function.

24. Start of a COMMAND line that selects the individual line to divide from, then follows the command I provided earlier in this chain.

25. End of the REPEAT function.

26. End of the FOREACH statement. I don't quite understand this function. Can you explain?

27. Starts the COMMAND function that rejoins the exploded polylines.

28. End of THEN statement.

29. End of IF statement.

30. End of THEN statement.

31. starts a PROMPT function that displays an error message regarding the block not being found.

32. End of IF statement.

31. Clears command bar.

32. End of Function.

 

If you made it this far I applaud you. I'd appreciate any help!

0 Likes
Message 6 of 11

Kent1Cooper
Consultant
Consultant

Help is your friend.  Start by reading about the functions involved in the >AutoLisp Reference<.  It will tell you what arguments are required and/or optional, what is returned, etc.  That will explain a lot of what you find confusing.  If after looking there, you still don't understand what something is doing, write back.

Kent Cooper, AIA
0 Likes
Message 7 of 11

jmillerWL6P5
Participant
Participant

I looked there and have studied a few other sources, but am still stumped on a few things. I can reverse engineer the lisp, but the issue is that I can't quite place my finger on what each of the variables is in this instance. If you can identify the 7 variables, then I can take that and figure out the process of what the lisp is doing. Again I appreciate any help you provide.

0 Likes
Message 8 of 11

komondormrex
Mentor
Mentor

@jmillerWL6P5,

what is it all about? explode plines, divide them with certain block, join exploded segments? do you need to divide with certain block every segment of pline into some divisions. and do that to lines and arcs also?

0 Likes
Message 9 of 11

jmillerWL6P5
Participant
Participant

The goal was to create a lisp that placed blocks on a straight line a maximum distance from one another. When you select a polyline to do this command, the division applies through the entire line as opposed to between each vertex. The work around was to explode the polyline, and then do the command for each segment of the polyline. With this new lisp it recognizes the polylines and explodes them, then does the divide command before rejoining the exploded lines back into a polyline. Does this answer your question?

0 Likes
Message 10 of 11

Kent1Cooper
Consultant
Consultant

I would change some terminology -- you're using "statement" for some distinguishable categories of things [functions vs. arguments].  See interspersed things:

 


1. Defines the function's alias and variables( I'm trying to deduce what each variable corelates to.)

Not an alias, but the command name per se [an alias is like L giving you the LINE command without entering all of it.]

....

3. Sets the PEDITACCEPT value to 1. so that in the PEDIT command later, the question will not be asked whether to convert Lines/Arcs [if there are any in the selection] to Polylines.

4.Starts an IF statement function by looking for the existence in the drawing of the definition of the desired block nameUsually "looking for a Block" would mean looking for an insertion of it, a Block reference, but that's not applicable here.

5. Starts the THEN statement argument to the (if) functionThe (progn) function is a "wrapper" so that multiple operations together constitute one argument for the 'then' expression, which is what to do if the 'test' expression returns anything other than nil.  See Help for (if) for what they call arguments testexpr, thenexpr and elseexpr.

6. An If statement function that within its 'test'-expression argument sets a variable to the selected objects.

7. States [starts?] a THEN statement argument -- what to do if the 'test' expression returns anything other than nil, in this case if any qualifying objects were selected.

8. Starts an IF statement function that within its 'test' expression pulls from the previous selection, but only pulls the Polylines from it, and then sets those selected Polylines to a variable which will be nil if there are no Polylines in the original selection.

....

11. This one is confusing, but it looks like you're taking the number of polylines and decreasing the value by one, and then setting that as the new value for the number of polylines. Then pulling the name of the objects, to then delete the new number of polylines from the selected objects. Am I on the right track?

It removes each Polyline from the original overall selection [putting them into their own variable does not remove them from the original].  When DIVIDE happens later, they will not exist, having been EXPLODEd, which would cause a problem stepping through that original selection to DIVIDE the Lines/Arcs in it.  It takes away one at a time, counting down not the number of Polylines [that remains the same], but the index number used by (ssname) for the item within the selection of Polylines-only to be removed from the larger selection.

....

13. Does this command make the explode command work when the previous command runs multiple times?

For some reason, EXPLODE within a (command) function can EXPLODE only one object even if given a multiple-object selection, unless you do something to get around that.  There are other ways to do it [Search for QAFLAGS], but preceding it with (initcommandversion) is probably the simplest.

....

15. Selects the exploded lines and/or Arcs from the polylines and assigns them to a variable.

....

18. Combines the Selected objects with the selected polylines and assigns them to a new variable.

The 's' is not a variable, really [and could be omitted from the localized-variables list at the top].  It's a stand-in within the (foreach) function for the items in the supplied list argument.  So the (repeat) function below is applied to what's in the ss variable, and then to what's in the ssp variable.

19. Starts a REPEAT statement that pulls the number of objects from the previous variable item in the list represented by the 's' for this round, in this case what's in the ss variable first, then what's in ssp the second time around and sets that number to a new variable.  

....

21. Takes the value associated to the number of selected objects and selected polylines and decreases that value, only to then assign the new value to the same variable. Then pulls the type of entity and assigns that to a new variable?

This is again stepping through what's in the selection set represented by the 's' [ss the first time through, ssp the second], incrementing the index number downward to get each object in succession.

22. Pulls the length/distance from the two end points of a line and assigns that to a variable.

This parameter-based way can get the length from any of the different vlax-curve-class entity types, and is a common way when more than one type could be involved.  It's universal, better than pulling VLA object-length properties, because those have different names [for a Line, it's called Length, for an Arc, it's called ArcLength, for a Circle, it's called Circumference].  And it's better than a way one might be tempted to use, namely to get the distance along the object at its end, with (vlax-curve-getDistAtPoint entityname (vlax-curve-getEndPoint entityname), because for a Circle or a closed Polyline [where the end is at the same place as the start], that will return zero.

....

26. End of the FOREACH statement. I don't quite understand this function. Can you explain?

FOR EACH item that the stand-in 's' represents in the supplied list argument, do as instructed.  In this case, for the ss variable [first thing in the list], do the (repeat) function that DIVIDEs everything in that selection set, then for the ssp variable, do the same.

....

31. starts a PROMPT function that displays an error message regarding the block not being found.

The 'else' argument to the top-most (if) function -- what to do if its 'test' expression returns nil, i.e. if the Block name is not defined in the drawing.

....

31. Clears command bar.  Typically referred to as "exiting quietly" so you don't have nil appear at the Command line.  It can also be (prin1) [which is the function whose Help entry actually describes this as exiting quietly] or (print), in all cases with no arguments.

....


 

Kent Cooper, AIA
0 Likes
Message 11 of 11

komondormrex
Mentor
Mentor

very funny) so you certainly need to divide every segment of selected pline into desired number of  subsegments with desired block and do similarly that for every arc and line as well.

0 Likes