Programming Challenge 24-2

Programming Challenge 24-2

john.uhden
Mentor Mentor
2,852 Views
29 Replies
Message 1 of 30

Programming Challenge 24-2

john.uhden
Mentor
Mentor

Here ya go, you challenge hungry carnivores.

Attached is a drawing of a simple closed polyline (red) being intersected at two points by an open polyline (white).  Actually there are 4 different versions of the same thing, the differences being the polyline directions.

Your challenge is to write a function (or more) to split the closed polylines into two (2) pieces at the intersection points so that each end of the two resulting polylines have coincident end points.  No, not necessarily startpoint coincident with startpoint and endpoint coincident with endpoint, but probably more like (equal start1 end2 fuzz) and (equal end1 start2 fuzz), so that they could be easily joined.

Your function must require only the selection of the open polyline, nothing more.

Unless you've programmed this before, you will be quite surprised with your first attempts.

I suggest that you copy the originals over to the right any number of times for testing.

Please include your initials in the name of the function that you post.

John F. Uhden

0 Likes
Accepted solutions (1)
2,853 Views
29 Replies
Replies (29)
Message 2 of 30

Kent1Cooper
Consultant
Consultant

Drawn directions of either do not need to matter, and no calculation of intersecting points is needed, and I was not surprised by anything.  Copy the victim in place, and Trim the original on one side of the cutting one, and the copy on the other side.  On the assumption that there won't be other closed Polylines in the vicinity than the one you want split, this works in your drawing:

 

 (defun C:SplitPL-KC
  (/ cutss cutpl splitss splitpl1 splitpl2 cutmid cutlen outboard1 outboard2)
  (prompt "\nFor cutting open Polyline {singular},")
  (if
    (and
      (setq cutss (ssget "_:S+." '((0 . "LWPOLYLINE") (-4 . "<NOT") (-4 . "&") (70 . 1) (-4 . "NOT>"))))
        ;; single, open only
      (setq
        cutpl (ssname cutss 0)
        splitss
          (ssget "_F"
            (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 10)) (entget cutpl)))
            '((0 . "LWPOLYLINE") (-4 . "&") (70 . 1)); closed only
          ); ssget & splitss
      ); setq
      (= (sslength splitss) 1); only one to be split
    ); and
    (progn ; then
      (setq splitpl1 (ssname splitss 0))
      (command "_.copy" splitpl1 "" '(0 0) '(0 0))
      (setq
        splitpl2 (entlast)
        cutmid ; midway between ends of cutting one
          (mapcar '/
            (mapcar '+
              (setq cutstart (vlax-curve-getStartPoint cutpl))
              (setq cutend (vlax-curve-getEndPoint cutpl))
            ); +
            '(2 2 2)
          ); /
        cutdir (angle cutstart cutend)
        cutlen (vlax-curve-getDistAtPoint cutpl (vlax-curve-getEndPoint cutpl))
        outboard1 (polar cutmid (+ cutdir (/ pi 2)) cutlen)
        outboard2 (polar cutmid (- cutdir (/ pi 2)) cutlen)
      ); setq
      (command "_.trim" cutpl ""
        (list splitpl1 (vlax-curve-getClosestPointTo splitpl1 outboard1))
        (list splitpl2 (vlax-curve-getClosestPointTo splitpl2 outboard2))
        ""
      ); command
    ); progn
  ); if
  (prin1)
)

 

Add a restriction to an unlocked Layer for the one to split if you want, and *error* handling and command-echo suppression and Undo begin-end wrapping and so on....

 

I imagine there could be more convoluted configurations in which it might not give the result you want, but it works in simple situations such as in your drawing.  With minor adjustment, the same approach could work with other kinds of cutting edges [Line, "heavy" Polyline, Xline, Ray, etc.*] and other kinds of objects to be split ["heavy" Polyline, Circle, Ellipse, Spline, Mline, Xline, Ray, etc.].  *There would need to approximations made for the Fence selection if the cutting edge were to contain curvature(s), such as a Polyline with arc segment(s), or an Arc, Spline, or partial Ellipse.

 

Kent Cooper, AIA
0 Likes
Message 3 of 30

john.uhden
Mentor
Mentor

@Kent1Cooper ,

Nice!  I was expecting that most challengers would get hung up on the BREAK command for a while.  How did you know to copy and trim?  It took me forever to figure that out.

Only thing I can criticize is your insatiable use of the command function, whereas a vla-copy is so much easier plus no worries about object snaps.  Yeah, okay, you have to convert ename to object and vice versa.

Anyway, I hope there are more responses, all of which I hope to accept.  Of course if they are following your response and this one, they have a head start.

John F. Uhden

0 Likes
Message 4 of 30

Kent1Cooper
Consultant
Consultant

@john.uhden wrote:

....  I was expecting that most challengers would get hung up on the BREAK command for a while.  How did you know to vla-copy and trim?  ....


I knew from past experience that Breaking a closed Polyline at a single point results in 2 Polylines, not just the 1 you might expect with its start/end at that Break point.  The original start/end point is the other division between the 2 results.  The TRIM approach gets around that, leaving each side as a single Polyline, regardless of where the original start/end was.

 

[And I didn't vla-copy it.  The only (vla...) functions involved are (vlax-curve...) functions, which will happily work with just the entity name, so there's no need to involve VLA object conversion.]

Kent Cooper, AIA
0 Likes
Message 5 of 30

john.uhden
Mentor
Mentor

@Kent1Cooper ,

ya know, i think that if you supply the target as '(ename . pt) that it will trim only the ename provided, not some other proximitous entity.  Hmmm, maybe it's (list ename pt).  Yes, I think it's the latter,

John F. Uhden

0 Likes
Message 6 of 30

Kent1Cooper
Consultant
Consultant

@john.uhden wrote:

... if you supply the target as '(ename . pt) that it will trim only the ename provided, not some other proximitous entity.  .... it's (list ename pt) ....


Exactly.  Supplying that list [the same as what (entsel) returns], instead of just a point, ensures the right thing gets Trimmed if there could be more than one thing at that point [which there will be in this case].  And as a "plain" list, not a dotted-pair list.

Kent Cooper, AIA
0 Likes
Message 7 of 30

dbroad
Mentor
Mentor

I'll play along although @Kent1Cooper 's is just fine:

;;returns a list of values from an association list that have the key
(defun cdrs (key lst / pr)
  (if
    (setq pr (assoc key lst))
    (cons (cdr pr)
	   (cdrs key (cdr (member pr lst))))))
;;Main program: Divide a closed polyline into 2 parts with a fence polyline
(defun c:splitp	 ( / ss f pts cp cp2 fo cpo ipts p2 param1)
  (prompt "\n Select open fence polygon to trim intersecting closed polyline by.")
  (and
    ;;Select only one lwpolyline that must not be closed.
    (SETQ SS (ssget ":S+."  '((0 . "lwpolyline") (-4 . "<NOT") (-4 . "&") (70 . 1) (-4 . "NOT>"))))
    (SETQ F (SSNAME SS 0)) ;fence polyline entity
    (SETQ PTS (cdrs 10  (ENTGET F)));Use only verticies.
    ;;Select only lwpolylines that are closed and intersect the fence polyline
    ;;Weakness: The fence polyline may actually have bulges. May need to either exclude
    ;;fence polylines that are bulged or make the PTS list more refined at bulges.
    (SETQ SS (SSGET "F" PTS '((0 . "lwpolyline") (-4 . "&") (70 . 1))))
    (SETQ CP (SSNAME SS 0))
    (NOT (COMMAND "_.COPY" CP "" "_NON" "0,0" ""))
    (SETQ CP2 (ENTLAST));Closed polyline copy entity
    (setq fo (vlax-ename->vla-object F));fence object
    (setq cpo (vlax-ename->vla-object cp));closed polygon object
    (setq ipts (vlax-invoke fo 'intersectwith cpo acextendnone)) ;flat list of intersections
    (setq p2 (cdddr ipts)) ;;Second intersection
    (setq param1 (vlax-curve-getparamatpoint cp p2));parameter of second intersecton
    ;;;mid param between start and INT.
    (NOT (COMMAND "_.TRIM" F ""  CP (vlax-curve-getpointatparam cp (/ param1 2))""))
    ;;;mid param between INT and end param.
    (NOT (COMMAND "_.TRIM" F ""  CP2(vlax-curve-getpointatparam cp2(/ (+ param1 (vlax-curve-getendparam cp2)) 2))""))
    )
  (princ))
Architect, Registered NC, VA, SC, & GA.
0 Likes
Message 8 of 30

CodeDing
Advisor
Advisor

I know it's a hard-enough programming challenge without this extra nonsense. 

But I'm reading these codes and thinking to myself "But what about this shape?!" 😅

image.png

0 Likes
Message 9 of 30

komondormrex
Mentor
Mentor

@john.uhden 

should the split pline's parts be closed with added part of splitter?

Message 10 of 30

Kent1Cooper
Consultant
Consultant

@CodeDing wrote:

.... "But what about this shape?!" 


[Mine handles that situation just fine in certain specific relationships, but fails in others, depending on exactly how they are positioned in relation to each other.  It's all in the Fence selection through vertices -- if the cutting one's horizontals are above the level of the one to split, the selection misses the latter.  See the last sentence in Message 2.]

Kent Cooper, AIA
0 Likes
Message 11 of 30

john.uhden
Mentor
Mentor

@dbroad ,

Thank goodness you decided to join the game.

You forgot to add your initials to your "SPLITP."

T'would be really cute if your initials were "SOUP."

Nevertheless, it worked just fine for the objects I provided,

BUT...

1.  I don't like the ssget prompting "Select objects: " (plural) when you instructed it to select just one. But that's not your fault.  I think that rather than using a filter, it would be better to use entsel with an appropriate prompt and subsequent validation.  The better answer is for Autodesk to allow the coder to provide the prompt string of his own.

2.  No need to worry about a bulged fence, the users will have been instructed to create only straight segments for the fence.  Just like "There's no crying in baseball" "There are no curves in fences."  Just post to post to post.

3.  I as yet haven't figured out why (/ param1 2) works, but I'm getting closer.  I think it's a result of your brilliance.  It's probably the right technique for that with which I have been struggling.

John F. Uhden

0 Likes
Message 12 of 30

john.uhden
Mentor
Mentor

@komondormrex ,

I, too, am wondering about closing the splits along the fence path.  I'll have to ask my colleague who made the request, but he's off all this week.  For now don't bother with closing.

John F. Uhden

0 Likes
Message 13 of 30

john.uhden
Mentor
Mentor

@CodeDing ,

See my response to @dbroad .

John F. Uhden

0 Likes
Message 14 of 30

Kent1Cooper
Consultant
Consultant

@komondormrex wrote:

.... should the split pline's parts be closed with added part of splitter?


For that:  It shouldn't be too difficult to cause BPOLY or BOUNDARY in each half, and delete the original closed Polyline.

Kent Cooper, AIA
Message 15 of 30

dbroad
Mentor
Mentor
  1. True, but, SSGET allows filtering without a loop.  Since you only get one pick and it works, it's not too much of a problem in mine.  Interactive functions, such as SSGET, should've included an argument for a prompt or been intelligent to have been able to interpret it's own arguments.  Entsel would've required a loop if the selected object didn't match the requested specs. After all, how many of us actually look at the command line anyway while picking.
  2. Good to know.
  3. @Kent1Cooper 's method of using the format of (entsel) return values for his trim picks is better(possibly more reliable) than mine.  My method ensures that each copy is selected by choosing a point exactly on each polyline.
    (/ param1 2) returns a parameter half way between the intersection point and the start of the polyline.  The other expression finds a parameter half way between the intersection and the end of the polyline. It wouldn't work for the second polyline if they weren't exact copies.  Using an ename with the point ensures which polyline gets picked but here it doesn't matter. Without the ename, the copy gets trimmed first.

I agree that bpoly would be the easiest way to solve the task if the result should be closed.  It would, of course, loose any extended data associated with the original and the the bpoly results would have to be properties matched to the original and the original would need to be deleted.  Group membership and locked layers could present problems as well.

Architect, Registered NC, VA, SC, & GA.
0 Likes
Message 16 of 30

Kent1Cooper
Consultant
Consultant

Because every vertex of a Polyline has a unit parameter value, the difference in the length represented by a particular difference in parameter value can vary greatly, if segment lengths vary a lot.  That's why I don't think a parameter-based location of pick points for Trimming is right.  If it's to be done by calculating something along the Polyline relative to an intersection point with the cutter, it should be based on distances, not parameters.  Here's a rather extreme example, with everything on one side of the [white] cutting Polyline being within the same segment on the Polyline to be split [red], so that in the SPLITP command, the pick points in Trimming, at halfway in parameter value difference from the start point in both directions from an intersection, both fall on the same side of the cutter:

Kent1Cooper_0-1710333029752.png

 

Kent Cooper, AIA
Message 17 of 30

komondormrex
Mentor
Mentor

@john.uhden 

is it a continuation of the ssnamex topic?

0 Likes
Message 18 of 30

john.uhden
Mentor
Mentor

@komondormrex asked, "is it a continuation of the ssnamex topic?"

Actually, yes, it is related.

John F. Uhden

0 Likes
Message 19 of 30

john.uhden
Mentor
Mentor

@komondormrex ,

I must thank you, thank you, thank you!

For as much as I am dismayed with the programmer who wrote the ssnamex function, I am thrilled with the person who wrote the BOUNDARY command.

Your idea of creating closed plines is working just great!

John F. Uhden

0 Likes
Message 20 of 30

john.uhden
Mentor
Mentor

Doug,

I hate to hurt your feelings, but the reason your method worked is just because I had provided a rather simple condition.  Turns out that you can't average parameters.  You might have 5 vertices that are very near one another and then a 6th way far away.  As Kent pointed out, you have to use distances for your approach to work with polylines that have many vertices.

John F. Uhden

0 Likes