Trin/Break line between two points

Trin/Break line between two points

Anonymous
Not applicable
2,480 Views
13 Replies
Message 1 of 14

Trin/Break line between two points

Anonymous
Not applicable

I'm trying to create a simple lisp that breaks or trims a line or polyline between two picked points. I realise that a macro would do the same (^C^C_break \_f ) but I'd like to do it recursively. My code so far:

 

(defun c:BreakatPoints ( / ent pt1 pt2)
    (while (setq ent (car(entsel "\nSelect line or Polyline ")))
	(if (wcmatch (cdr(assoc 0 (entget ent))) "LINE,LWPOLYLINE")
		  (progn
		  	(setq pt1 (getpoint "\nPick First Point"))
		  	(setq pt2 (getpoint "\nPick Second Point"))

			;; BREAK SELECTED LINE/POLYLINE BETWEEN pt1 & pt2

		  );;end progn
	  )
    )  ; while
  (princ)
)

 

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

ВeekeeCZ
Consultant
Consultant
Accepted solution

Maybe like this?

 

(defun c:BreakatPoints ( / ensel)
  (while (setq ensel (entsel "\nSelect line or Polyline <quit>: "))
    (if (wcmatch (cdr (assoc 0 (entget (car ensel)))) "LINE,LWPOLYLINE,ARC")
      (command "_break" ensel "_F" PAUSE PAUSE)))
  (princ)
)

 

btw. some commands does not accept pure entity name but requires list of (<entname> selpoint) given by entsel. BREAK is one of them.

0 Likes
Message 3 of 14

Anonymous
Not applicable
Works well, ta v much
0 Likes
Message 4 of 14

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

I'm trying to create a simple lisp that breaks or trims a line or polyline between two picked points. I realise that a macro would do the same (^C^C_break \_f ) but I'd like to do it recursively. .... 


Are you aware of the automatic-repeat possibility in command macros?  Precede the double-cancel with an asterisk, and it will recall itself continuously:

 

*^C^C_break \_f

 

I've had that in menu items literally for decades, along with some related ones about breaking at a single point without needing to pick it twice.

Kent Cooper, AIA
0 Likes
Message 5 of 14

Anonymous
Not applicable

I didn't know that, nice tip Kent.

 

Howver in this case I needed to turn osmode to 55, to select the break points, then reset osmode to the previous setting

0 Likes
Message 6 of 14

stevor
Collaborator
Collaborator

First, look for similar solutions.

At google: autolisp break osnap.

One is at:

http://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/break-crossing-lines-using-lsp/td-p/2...

Many other exist.

IF you cannot find it the easy way,

post a messagee that indicates your actual problem in the  Subject .

S
0 Likes
Message 7 of 14

Anonymous
Not applicable
Thanks Stevor, but BeekeeCZ has already found the solution, that's why it's marked as "Solved! by BeekeeCZ" in green
0 Likes
Message 8 of 14

Kent1Cooper
Consultant
Consultant
Accepted solution

@ВeekeeCZ wrote:

....

btw. some commands does not accept pure entity name but requires list of (<entname> selpoint) given by entsel. BREAK is one of them.


Actually, the Break command will accept an entity name alone, without an associated selection point.  If you give it only an entity name, it jumps directly to asking for a first point without your needing to ask for the First-point option.  [This also works when done manually -- try BR(eak) and then don't pick anything, but just type L(ast), assuming the last-drawn object is Breakable, and you'll see.]  So you can do this:

 

(defun c:BreakatPoints ( / ensel)
  (while (setq ensel (entsel "\nSelect line or Polyline <quit>: "))
    (if (wcmatch (cdr (assoc 0 (entget (car ensel)))) "LINE,LWPOLYLINE,ARC")
      (command "_break" (car ensel) PAUSE PAUSE))); entity name only, and without "F"
  (princ)
)

which in this particular situation doesn't really save you anything in terms of length of code, but in other circumstances it sometimes does make more of a difference.

Kent Cooper, AIA
Message 9 of 14

ВeekeeCZ
Consultant
Consultant

Oooo. Shame on me! Of course it's possible, and I've used that a couple of times! Somehow I focused on rewriting the macro...

@Anonymous sorry that I have misled you.

@Kent1Cooper thanks for reminding me.

 

@Anonymous... in this case we can go back to your ent variable 🙂 

 

(defun c:BreakatPoints ( / ent)
  (while (setq ent (car (entsel "\nSelect an object to break: ")))
    (if (wcmatch (cdr (assoc 0 (entget ent))) "LINE,LWPOLYLINE,ARC")
      (command "_.break" ent PAUSE PAUSE)))
  (princ)
)
0 Likes
Message 10 of 14

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... in this case I needed to turn osmode to 55, to select the break points, then reset osmode to the previous setting


I think that 55 as an OSMODE value has one unnecessary mode in it -- CENter.  With QUAdrant and MIDpoint and ENDpoint also included, I believe one or another of those will always be closer to the pick point than the CENter of an Arc or Circle or Polyline arc segment.  So I think 51 would be operationally identical [used in suggestions below].

 

You can do that in a not-too-complicated command macro, if you always end its automatic repeating with ESCape when it's asking you to pick an object:

 

*^C^C(setq osm (getvar 'osmode)) _.break \'osmode 51 _first \\(setvar 'osmode osm)

 

But if you hit Enter/Space when it's asking you to pick an object, or if you cancel it after picking something but before having picked two break points, you will be left with the OSMODE value of 51 -- it won't reset whatever the value was before.

 

To ensure that OSMODE gets reset, no matter how the User ends it, you would need a lot more code in an AutoLisp routine with *error* handling, rather than a command macro.  Here's a version that extends the allowable objects to all [I think] Breakable types:

 

(defun C:BF (/ *error* osm esel ent etype); = Break with automatic First option
  (defun *error* (errmsg)
    (if (not (wcmatch errmsg "Function cancelled,quit / exit abort,console break"))
      (princ (strcat "\nError: " errmsg))
    ); if
    (setvar 'osmode osm); reset
    (princ)
  ); defun - *error*
  (setq osm (getvar 'osmode))
  (setvar 'osmode 51)
  (while (setq esel (entsel "\nSelect object for First-point Break <exit>: "))
    (setq
      ent (car esel)
      etype (cdr (assoc 0 (entget ent)))
    ); setq
    (if
      (and
        (wcmatch etype "*LINE,ARC,CIRCLE,ELLIPSE,RAY,TRACE")
          ; *LINE accepts Line, any kind of Polyline, Spline, and Xline, but we must disallow:
        (/= etype "MLINE"); can't be Broken
      ); and
      (command "_break" ent pause pause); then
      (prompt "\nCannot Break that object."); else
    ); if
  ); while
  (setvar 'osmode osm); reset
  (princ)
); defun

It could be done with (ssget) and object-type selection filtering, but that always provides its own "Select objects: " prompt, always in the plural, and without the opportunity to display the <exit> default, so I prefer the (entsel) approach in this case.

 

One other little advantage that has over the command macro approach is that it changes OSMODE only once, and resets it only once, surrounding the entire series of however many Breaks the User does, rather than changing and resetting it separately for every Break.

 

EDIT:  Another little feature that could be added would be for it to ask the User to select something again if they miss in trying to pick something, rather than ending it.  As it is, you have the choice to end it by picking where there's nothing, which you may prefer, but if you meant to pick something and missed, it will exit the command -- write back if you'd like to know how to be prompted to pick again instead [and can't figure it out for yourself].

 

Kent Cooper, AIA
0 Likes
Message 11 of 14

Anonymous
Not applicable

Thanks Kent, that looks a little more thorough.

 

I'd prefer ensel to ssget, I'm only selecting one item (usually a line) at a time. It's not a task which requires doing many times in any given drawing, nevertheless it's quite irritating typing in characters many times per day (in different drawings). Although thinking about it I could have used:

(ssget "_+.:E:S" '((0 . "LINE,LWPOLYLINE,ARC,CIRCLE,ELLIPSE,RAY"))))

But to no real advantage

 

As for propting if the item is mis-picked I guess a simple 'if' statement

while (if(setq esel

Would work with something like 'Missed, try again' as an else.

 

 

PS I only suggested OSMODE 55 because it's my default setting for Object Snap on. I was too lazy to find the more appropriate setting of 51 !!

0 Likes
Message 12 of 14

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... 

As for propting if the item is mis-picked I guess a simple 'if' statement

while (if(setq esel

Would work with something like 'Missed, try again' as an else.

 

....

That approach would also not give you a viable <exit> default option, because whether you miss or hit Enter/space, it's going to return nil, so it will ask you to try again, and the only way out is ESCape.  To have a routine distinguish between a missed pick and Enter, so it can ask you to pick again if you miss but will exit the routine if you hit Enter, you need to make use of the ERRNO System Variable.  Do a Search for errno and you'll find a lot of routines that use it for just that kind of purpose.

Kent Cooper, AIA
0 Likes
Message 13 of 14

Anonymous
Not applicable

I'm not sure if I've got the right code within 't cond ' but this seems to work:

(defun C:BF (/ *error* osm esel ent etype); = Break with automatic First option
  (defun *error* (errmsg)
    (if (not (wcmatch errmsg "Function cancelled,quit / exit abort,console break"))
      (princ (strcat "\nError: " errmsg))
    ); if
    (setvar 'osmode osm); get current osnap setting & store it
    (princ)
  ); defun - *error*
  (setq osm (getvar 'osmode))
  (setvar 'osmode 51)  
  (while
	(progn
	  (setvar 'errno 0)
    		(setq esel (entsel "\nSelect object for First-point Break <exit>: "))
	  (cond
	    ((= 7 (getvar 'errno))(princ "\nNothing Selected - try again."))
                ((null esel) nil)
	    (t
	     (progn
		 (setq
		      ent (car esel)
		      etype (cdr (assoc 0 (entget ent)))
		 ); setq
		    (if
		      (and
		        (wcmatch etype "*LINE,ARC,CIRCLE,ELLIPSE,RAY,TRACE"); *LINE accepts Line, any kind of Polyline, Spline, and Xline, but we must disallow multilines
		        (/= etype "MLINE"); can't be Broken
		      ); and
		      (command "_break" ent pause pause); then
		      (prompt "\nCannot Break that object."); else
		    ); if
	 	)
	     t); end cond t
	);; end cond
    );; progn
  ); while  
  (setvar 'osmode osm); reset
  (setvar 'errno 2); reset to default setting
  (princ)
); defun
0 Likes
Message 14 of 14

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

I'm not sure if I've got the right code within 't cond ' but this seems to work:

....  
  (while
	(progn
	  (setvar 'errno 0)
    		(setq esel (entsel "\nSelect object for First-point Break <exit>: "))
	  (cond
	    ((= 7 (getvar 'errno))(princ "\nNothing Selected - try again."))
                ((null esel) nil)
	    (t
	     (progn
....
(if (and (wcmatch etype "*LINE,ARC,CIRCLE,ELLIPSE,RAY,TRACE"); *LINE accepts Line, any kind of Polyline, Spline, and Xline, but we must disallow multilines (/= etype "MLINE"); can't be Broken ); and (command "_break" ent pause pause); then (prompt "\nCannot Break that object."); else ); if ) t); end cond t );; end cond );; progn ); while .... (setvar 'errno 2); reset to default setting (princ) ); defun

That may be viable, though it's a little peculiar in a few ways.  Typically, a (while) function's first argument is some kind of test, and if the test does not return nil, then there's something following for it to do before going back and running that test again.  The way you have it, the (progn) that is the "test" is not followed by anything for it to do, but in the right circumstances contains the thing to do within it.  It returns nil [from the ((null esel) nil) part] and stops the loop if they hit Enter, but it's not testing the value of ERRNO to come to that determination, as it is in other conditions.  And the T condition in which it goes ahead and Breaks the object [if Breakable] is forced to have another T added at the end just to keep the (while) loop going for another selection [without it, the (command) function in there would return nil and stop the loop].  You can avoid that extraneous element, and also eliminate having an (if) function nested inside one of the (cond) conditions [one of the main benefits of (cond) is that it eliminates nested (if) functions], by structuring it differently.  [Also, there is no "default" value for ERRNO to be reset to, but an "initial" value of 0, and its value is changing all the time as you do things, so there's no point in resetting it explicitly -- anything you set it to will soon be overridden anyway.]

 

Here's one way [certainly not the only way] to do this kind of thing, that I have sometimes used:

 

(defun C:BF (/ *error* osm esel ent edata etype done)
; = Break with automatic First option
  (defun *error* (errmsg)
    (if (not (wcmatch errmsg "Function cancelled,quit / exit abort,console break"))
      (princ (strcat "\nError: " errmsg))
    ); if
    (setvar 'osmode osm); get current osnap setting & store it
    (princ)
  ); defun - *error*
  (setq osm (getvar 'osmode))
  (setvar 'osmode 51)  
  (while (not done); check that User hasn't chosen <exit> default
    (setvar 'errno 0)
    (setq esel (entsel "\nSelect object for First-point Break <exit>: "))
    (cond
      ( (and ; selection of Breakable & unlocked object
          (= (getvar 'errno) 0); picked something
          (setq ent (car esel) edata (entget ent) etype (cdr (assoc 0 edata)))
          (wcmatch etype "*LINE,ARC,CIRCLE,ELLIPSE,RAY,TRACE")
            ; *LINE = Line/any kind of Polyline/Spline/Xline, but must disallow:
          (/= etype "MLINE"); can't be Broken
          (= (logand 4 (cdr (assoc 70 (tblsearch "layer" (cdr (assoc 8 edata)))))) 0)
            ; 0 = Unlocked Layer, 4 = Locked
        ); and
        (command "_break" ent pause pause); proceed!
      ); Breakable-object selection condition
      ((= (getvar 'errno) 7) (prompt "\nNothing Selected - try again.")); missed
      ((= (getvar 'errno) 52) (setq done T)); Enter/space at Selection prompt stops loop
      ((prompt "\nNot a Breakable object, or on a locked Layer --"))
; none of the above [failed some test(s) in first condition, didn't meet others] ); cond ); while (setvar 'osmode osm); reset (princ) ); defun

 

I added the locked-Layer check, and in order to facilitate checking both that and the entity type, the edata variable, and the done variable to end it on Enter/space.

 

[The fallback-condition  ((prompt ...  part could be done: (T (prompt ... , but the T is not needed when there's only one thing for it to do.]

 

Another variant would be to eliminate the condition for ERRNO being 7, and change the fallback-condition prompt to the more all-encompassing "\nNothing selected, or not a Breakable object, or on a locked Layer - try again."  Conversely, it could pull those all apart and have separate prompts for each of the individual no-go situations.

Kent Cooper, AIA
0 Likes