Creating pline boundary

Creating pline boundary

dani-perez
Advocate Advocate
4,374 Views
20 Replies
Message 1 of 21

Creating pline boundary

dani-perez
Advocate
Advocate

Hello all,

 

I'd like to be able to create a pline boundary as the example. I have one block made with some plines (Left) and the final result will be to create the red boundary (right) with a code. is it possible?

 

Thanks all.

 

 

 

0 Likes
Accepted solutions (2)
4,375 Views
20 Replies
Replies (20)
Message 2 of 21

ВeekeeCZ
Consultant
Consultant

Try... Assuming you have a block with polylines in the same direction.

 

(vl-load-com)

(defun c:BlockOfLinesBoundary (/ :LWPoly sel i ent ss)
  
  (defun :LWPoly (lst cls)
    (entmakex (append (list (cons 0 "LWPOLYLINE")
			    (cons 100 "AcDbEntity")
			    (cons 100 "AcDbPolyline")
			    (cons 90 (length lst))
			    (cons 70 cls)
			    (cons 8 "Capa1"))
		      lst)))
  
  (if (setq sel (ssget '((0 . "INSERT"))))
    (repeat (setq i (sslength sel))
      (setq ent (ssname sel (setq i (1- i))))
      (command "_.COPY" ent "" "_non" '(0 0 0) "_non" '(0 0 0)
	       "_.EXPLODE" "_L" )
      (and (setq ss (ssget "_P" '((0 . "LWPOLYLINE"))))
	   (:LWPoly (append (mapcar '(lambda (x) (assoc 10 (entget x))) (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss))))
			    (reverse (mapcar '(lambda (x) (assoc 10 (reverse (entget x)))) (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss))))))
		    1)
	   (command "_.ERASE" ss ""))))
  (princ)
  )
0 Likes
Message 3 of 21

dani-perez
Advocate
Advocate

Hello 

 

 

 

0 Likes
Message 4 of 21

ВeekeeCZ
Consultant
Consultant

Glad that solution worked for you.

If you read my assumptions you won't wonder why it's not working for you modification.

You might want to try the routine that danglar found and posted in another today's thread HERE

0 Likes
Message 5 of 21

dani-perez
Advocate
Advocate

Hello 

 

Thanks for replying. I tried danglar's code but it is for background under selected block, isnt it?

 

My issue is to create the external contour of the block, joining the nearest end plines each other.

 

thanks.

0 Likes
Message 6 of 21

Kent1Cooper
Consultant
Consultant

@ВeekeeCZ wrote:

Try... Assuming you have a block with polylines in the same direction. ....


 

... and  assuming that the Polylines' drawing order  matches their positional sequence.  Having some of them "out of order" as well as having one drawn running in the other direction are both  causes of the undesired result in your second drawing.

 

That seems the biggest challenge to me -- figuring out for a set of Polylines, with such wildly different lengths and relative positional shifts, what the sequence should be, and which end is which for each one in relation to the others.   For example, it wouldn't be hard for a routine to determine for each one which is its upper-most end, if that's a reliable standard to use [which I would not assume, but maybe it is for what you're doing].  But then how would it figure out the order to trace along those ends?  It can't do them in either left-to-right or top-to-bottom order, so what standard could be applied that would give a reliable result?

Kent Cooper, AIA
0 Likes
Message 7 of 21

dani-perez
Advocate
Advocate

Hello 

 

 

0 Likes
Message 8 of 21

Kent1Cooper
Consultant
Consultant

@dani-perez wrote:

.…


 

I wouldn't claim it's impossible, but I'm having a hard time imagining how to do it.  Your eye and brain can do it very easily, and could do a heck of a lot of them in the amount of time it would take someone to come up with the code to automate it.  But if I can think of a workable approach, I'll be back, and I hope others will ponder whether there's a way to do it.

Kent Cooper, AIA
0 Likes
Message 9 of 21

marko_ribar
Advisor
Advisor

Picking correct order of entities can't be automated, but everything else I suppose can... Just don't have a time to play with it... Look in attached DWG...

HTH., M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 10 of 21

marko_ribar
Advisor
Advisor

Here is what I've put together to save you from all those steps... Picking can't be automated...

 

(defun c:openlws2closedlwboundary ( / pea f s p stp enp p1 p2 pl sp spp sss li1 li2 rl )
  (setq pea (getvar 'peditaccept))
  (while (null f)
    (if
      (or
        (prompt "\nPick an open polygonal LWPOLYLINE in correct sequence to form closed LWPOLYLINE boundary...")
        (not (setq s (ssget "_+.:E:S" '((0 . "LWPOLYLINE") (-4 . "<not") (-4 . "&=") (70 . 1) (-4 . "not>") (-4 . "<not") (-4 . "<>") (42 . 0.0) (-4 . "not>")))))
      )
      (if (acet-sys-lmouse-down)
        (prompt "\nMissed...")
        (setq f t)
      )
      (progn
        (prompt "\nOK, keep going...")
        (setq p (cadr (grread t)))
      )
    )
    (if (and (null f) s)
      (progn
        (setq stp (cdr (assoc 10 (entget (ssname s 0)))))
        (setq enp (cdr (assoc 10 (reverse (entget (ssname s 0))))))
        (if (< (distance p enp) (distance p stp))
          (setq p1 enp p2 stp)
          (setq p1 stp p2 enp)
        )
        (setq pl (cons (list p1 p2) pl))
        (if (= (length pl) 1)
          (setq sp s)
          (progn
            (vl-cmdf "_.COPY" sp "" "_non" '(0.0 0.0 0.0) "_non" '(0.0 0.0 0.0))
            (setq spp (entlast))
            (vl-cmdf "_.COPY" s "" "_non" '(0.0 0.0 0.0) "_non" '(0.0 0.0 0.0))
            (setq sss (entlast))
            (setq li1 (entmakex (list '(0 . "LINE") (cons 10 (caadr pl)) (cons 11 (caar pl)))))
            (setq li2 (entmakex (list '(0 . "LINE") (cons 10 (cadadr pl)) (cons 11 (cadar pl)))))
            (setvar 'peditaccept 1)
            (vl-cmdf "_.PEDIT" "_M" spp li1 sss li2 "" "_J")
            (while (< 0 (getvar 'cmdactive))
              (vl-cmdf "")
            )
            (setvar 'peditaccept pea)
            (vl-cmdf "_.REGION" "_L" "")
            (setq rl (cons (entlast) rl))
            (if (/= (length rl) 1)
              (progn
                (vl-cmdf "_.UNION" (car rl) (cadr rl) "")
                (setq rl (vl-remove-if '(lambda ( x ) (vlax-erased-p x)) rl))
              )
            )
            (setq sp s)    
          )
        )
      )
    )
  )
  (vl-cmdf "_.EXPLODE" (car rl))
  (while (< 0 (getvar 'cmdactive))
    (vl-cmdf "")
  )
  (setvar 'peditaccept 1)
  (vl-cmdf "_.PEDIT" "_M" (ssget "_P") "" "_J")
  (while (< 0 (getvar 'cmdactive))
    (vl-cmdf "")
  )
  (setvar 'peditaccept pea)
  (sssetfirst nil (ssget "_L"))
  (princ)
)

BTW. You have to have installed Express Tools (acet-sys-lmouse-down) is used...

HTH., M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 11 of 21

dani-perez
Advocate
Advocate

Hello 

 

0 Likes
Message 12 of 21

dani-perez
Advocate
Advocate

Hello marko_ribar,

 

I have watched your drawing and your code. Both are good solutions, especially the code, so thanks.

The drawing I posted is much smaller than the real drawing, thousand of plines in a random distribution. So I guess it's very difficult to join the end of each pline in the correct order, and you said that picking cant be automated, so I will have to pick one by one.

 

thanks for your time!

0 Likes
Message 13 of 21

ВeekeeCZ
Consultant
Consultant

Post at least a picture of some REAL drawing.

0 Likes
Message 14 of 21

marko_ribar
Advisor
Advisor

Hi @dani-perez,

meanwhile, I've modified it further more - there were some minor lacks (for ex. if you pick on opposite side - it may cross lines forming boundary - so it's buggy)... Also I forgot to put in (ssget - method ":L" for selection entities only on unlocked layer(s) - this is necessity as you after use COPY command which can only work correctly on entities that reside on unlocked layer(s)... Also no need to exclude arced open LWPOLYLINES from picking - they can for sure form final boundary with no problems... So here is revision - you may mark it as solution if you wish... Thanks...

 

(defun c:openlws2closedlwboundary ( / pea f s stp enp p1 p2 pl sp spp sss li1 li2 rl )
  (setq pea (getvar 'peditaccept))
  (while (null f)
    (if
      (or
        (prompt "\nPick an open LWPOLYLINE in correct sequence to form closed LWPOLYLINE boundary...")
        (not (setq s (ssget "_+.:E:S:L" '((0 . "LWPOLYLINE") (-4 . "<not") (-4 . "&=") (70 . 1) (-4 . "not>")))))
      )
      (if (acet-sys-lmouse-down)
        (prompt "\nMissed...")
        (setq f t)
      )
      (prompt "\nOK, keep going...")
    )
    (if (and (null f) s)
      (progn
        (setq stp (cdr (assoc 10 (entget (ssname s 0)))))
        (setq enp (cdr (assoc 10 (reverse (entget (ssname s 0))))))
        (setq pl (cons (list stp enp) pl))
        (if (= (length pl) 1)
          (setq sp s)
          (progn
            (vl-cmdf "_.COPY" sp "" "_non" '(0.0 0.0 0.0) "_non" '(0.0 0.0 0.0))
            (setq spp (entlast))
            (vl-cmdf "_.COPY" s "" "_non" '(0.0 0.0 0.0) "_non" '(0.0 0.0 0.0))
            (setq sss (entlast))
            (if (inters (caadr pl) (caar pl) (cadadr pl) (cadar pl))
              (progn
                (setq li1 (entmakex (list '(0 . "LINE") (cons 10 (caadr pl)) (cons 11 (cadar pl)))))
                (setq li2 (entmakex (list '(0 . "LINE") (cons 10 (cadadr pl)) (cons 11 (caar pl)))))
              )
              (progn
                (setq li1 (entmakex (list '(0 . "LINE") (cons 10 (caadr pl)) (cons 11 (caar pl)))))
                (setq li2 (entmakex (list '(0 . "LINE") (cons 10 (cadadr pl)) (cons 11 (cadar pl)))))
              )
            )
            (setvar 'peditaccept 1)
            (vl-cmdf "_.PEDIT" "_M" spp li1 sss li2 "" "_J")
            (while (< 0 (getvar 'cmdactive))
              (vl-cmdf "")
            )
            (setvar 'peditaccept pea)
            (vl-cmdf "_.REGION" "_L" "")
            (setq rl (cons (entlast) rl))
            (if (/= (length rl) 1)
              (progn
                (vl-cmdf "_.UNION" (car rl) (cadr rl) "")
                (setq rl (vl-remove-if '(lambda ( x ) (vlax-erased-p x)) rl))
              )
            )
            (setq sp s)    
          )
        )
      )
    )
  )
  (if sp
    (progn
      (vl-cmdf "_.EXPLODE" (car rl))
      (while (< 0 (getvar 'cmdactive))
        (vl-cmdf "")
      )
      (setvar 'peditaccept 1)
      (vl-cmdf "_.PEDIT" "_M" (ssget "_P") "" "_J")
      (while (< 0 (getvar 'cmdactive))
        (vl-cmdf "")
      )
      (setvar 'peditaccept pea)
      (sssetfirst nil (ssget "_L"))
    )
  )
  (princ)
)

HTH., M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 15 of 21

Kent1Cooper
Consultant
Consultant

@dani-perez wrote:

.... picking cant be automated, so I will have to pick one by one. ....


 

But picking can be greatly reduced  from picking one by one in order:

(prompt "\nUse Fence selection across one-line-segment Polylines in sequence to")
(setq ss
(ssget ; provides its own "Select objects: " prompt
'(
(0 . "LWPOLYLINE")
(-4 . "<not") (-4 . "&=") (70 . 1) (-4 . "not>") ;; not-closed only
(-4 . "<not") (-4 . "<>") (42 . 0.0) (-4 . "not>") ;; no-bulge [i.e. line segment] only
(90 . 2) ;; single-segment only
); filter list
); ssget
); setq

You will need to type in the F to go into Fence selection, as instructed, but then you just draw a Fence through them along the appropriate path, and what that returns is a selection set of them in positional-sequence order, no matter what their drawn  order.  A single  leg of that Fence path will often be able to pass through a whole bunch of the Polylines, sparing you the need to pick each one to determine the sequence.  I haven't followed up with the further processing, stepping through that selection set to work with the Polylines to build the perimeter, but this should give you a big jump on the selection part.

 

EDIT:
It appears it's not even necessary to step through that selection set.  With LOFTNORMALS set to 0 for straight edges instead of splined ones, all you need is to start a LOFT command, and give it ss as the selection, and get your Surface:

(command "_.loft" ss "" "")

and then go on with the Exploding of that into Regions, Unioning them, converting to a Polyline, as in Marko's routine.

 

 

There remains the "problem" that in any case, you need to Explode the Block to get the Polylines, but you could make a copy in-place, Explode that, do this with the results [the selection will ignore the underlying Block], and then delete the ss selection set, and your original Block will still be there.

Kent Cooper, AIA
0 Likes
Message 16 of 21

marko_ribar
Advisor
Advisor
Accepted solution

Here is my mod for "F" - Fence selecting... If this can speed some things, then I think this is it... Please if it satisfies your needs mark it as a solution...

 

(defun c:openlws2closedlwboundary ( / pea ff f ss sss ch i s stp enp p1 p2 pl sp spp ssp li1 li2 rl )
  (setq pea (getvar 'peditaccept))
  (while (null ff)
    (while (null f)
      (if
        (or
          (prompt "\nSelect by fence open LWPOLYLINES in correct sequence to form closed LWPOLYLINE boundary - when asked for selection, TYPE \"F\"...")
          (not (setq ss (ssget "_:L" '((0 . "LWPOLYLINE") (-4 . "<not") (-4 . "&=") (70 . 1) (-4 . "not>")))))
        )
        (setq f t)
        (prompt "\nOK, keep going...")
      )
      (if (null sss)
        (setq sss ss)
        (setq sss (acet-ss-union (list sss ss)))
      )
    )
    (initget "Yes No")
    (setq ch (getkword "\nENTER TO FINISH SELECTION - Finish selecting or keep going [Yes/No] <Yes> : "))
    (if (null ch)
      (setq ch "Yes")
    )
    (if (= ch "Yes")
      (setq ff t)
      (setq f nil)
    )
  )
  (repeat (setq i (sslength sss))
    (setq s (ssadd))
    (ssadd (ssname sss (setq i (1- i))) s)
    (if s
      (progn
        (setq stp (cdr (assoc 10 (entget (ssname s 0)))))
        (setq enp (cdr (assoc 10 (reverse (entget (ssname s 0))))))
        (setq pl (cons (list stp enp) pl))
        (if (= (length pl) 1)
          (setq sp s)
          (progn
            (vl-cmdf "_.COPY" sp "" "_non" '(0.0 0.0 0.0) "_non" '(0.0 0.0 0.0))
            (setq spp (entlast))
            (vl-cmdf "_.COPY" s "" "_non" '(0.0 0.0 0.0) "_non" '(0.0 0.0 0.0))
            (setq ssp (entlast))
            (if (inters (caadr pl) (caar pl) (cadadr pl) (cadar pl))
              (progn
                (setq li1 (entmakex (list '(0 . "LINE") (cons 10 (caadr pl)) (cons 11 (cadar pl)))))
                (setq li2 (entmakex (list '(0 . "LINE") (cons 10 (cadadr pl)) (cons 11 (caar pl)))))
              )
              (progn
                (setq li1 (entmakex (list '(0 . "LINE") (cons 10 (caadr pl)) (cons 11 (caar pl)))))
                (setq li2 (entmakex (list '(0 . "LINE") (cons 10 (cadadr pl)) (cons 11 (cadar pl)))))
              )
            )
            (setvar 'peditaccept 1)
            (vl-cmdf "_.PEDIT" "_M" spp li1 ssp li2 "" "_J")
            (while (< 0 (getvar 'cmdactive))
              (vl-cmdf "")
            )
            (setvar 'peditaccept pea)
            (vl-cmdf "_.REGION" "_L" "")
            (setq rl (cons (entlast) rl))
            (if (/= (length rl) 1)
              (progn
                (vl-cmdf "_.UNION" (car rl) (cadr rl) "")
                (setq rl (vl-remove-if '(lambda ( x ) (vlax-erased-p x)) rl))
              )
            )
            (setq sp s)    
          )
        )
      )
    )
  )
  (if sp
    (progn
      (vl-cmdf "_.EXPLODE" (car rl))
      (while (< 0 (getvar 'cmdactive))
        (vl-cmdf "")
      )
      (setvar 'peditaccept 1)
      (vl-cmdf "_.PEDIT" "_M" (ssget "_P") "" "_J")
      (while (< 0 (getvar 'cmdactive))
        (vl-cmdf "")
      )
      (setvar 'peditaccept pea)
      (sssetfirst nil (ssget "_L"))
    )
  )
  (princ)
)

HTH., M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 17 of 21

danglar
Advocate
Advocate

Very good solution Marko for complicated and non trivial problem

Published here with respect to author

https://lispbox.wordpress.com/2019/01/10/polyline-boundary-creator-closed-lwpolyline-boundary/

 

0 Likes
Message 18 of 21

Kent1Cooper
Consultant
Consultant

@Kent1Cooper wrote:

But picking can be greatly reduced  from picking one by one in order:
(prompt "\nUse Fence selection across one-line-segment Polylines in sequence to")….

You will need to type in the F to go into Fence selection …. what that returns is a selection set of them in positional-sequence order....

….
It appears it's not even necessary to step through that selection set.  …. start a LOFT command, and give it ss as the selection, and get your Surface:

(command "_.loft" ss "" "")

and then go on with the Exploding of that into Regions, Unioning them, ….

…. you could make a copy in-place, Explode that, do this with the results [the selection will ignore the underlying Block], and then delete the ss selection set, and your original Block will still be there.


 

So going back to @marko_ribar's earlier notion of LOFTing and Exploding, etc., with the above Fence-selection approach, it can be done with far less code [minimally tested]:

(defun C:WRAP (/ blksel ss)
  (if
    (and
      (setq blksel (entsel "\nSelect Block of one-line-segment Polylines: "))
      (= (cdr (assoc 0 (entget (car blksel)))) "INSERT")
    ); and
    (progn ; then
      (command
        "_.copy" (car blksel) "" "0,0" ""
        "_.explode" "_last"
      ); command
      (prompt "\nUse Fence selection across one-line-segment Polylines in sequence to")
      (setq ss
        (ssget ; provides its own "Select objects: " prompt
          '(
            (0 . "LWPOLYLINE")
            (-4 . "<not") (-4 . "&=") (70 . 1) (-4 . "not>") ;; not-closed only
            (-4 . "<not") (-4 . "<>") (42 . 0.0) (-4 . "not>") ;; no-bulge [i.e. line segment] only
            (90 . 2) ;; single-segment only
          ); filter list
        ); ssget
      ); setq
      (command
        "_.loft" ss "" ""
        "_.erase" ss ""
        "_.explode" "_last"
        "_.union" "_previous" ""
        "_.explode" "_last"
        "_.pedit" "_multiple" "_previous" "" "_join" 0 ""
      ); command
    ); progn
  ); if
  (princ)
); defun

It does require LOFTNORMALS=0, and that the Block selected is made up of single-line-segment-only Polylines.

Kent Cooper, AIA
0 Likes
Message 19 of 21

marko_ribar
Advisor
Advisor

@Kent1Cooper

I may be wrong, but then again maybe not... I think you are missing setting peditaccept=1 in your version...

@danglar

Thanks...

Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes
Message 20 of 21

Kent1Cooper
Consultant
Consultant
Accepted solution

@marko_ribar wrote:

@Kent1Cooper

I may be wrong, but then again maybe not... I think you are missing setting peditaccept=1 in your version...


 

True -- I always have it set that way, so it was when I tested the routine.  That can certainly be built in, for those who may not always use that setting.  Here's a version that does that, and similarly handles the LOFTNORMALS setting, and suppresses command echoing of all the steps.  Tested on the Block in @dani-perez's second sample drawing in Message 3.

(defun C:WRAP (/ svnames svvals blksel ss)
  (setq
    svnames '(cmdecho loftnormals peditaccept)
    svvals (mapcar 'getvar svnames)
  ); setq
  (mapcar 'setvar svnames '(0 0 1)); turn off command echo, set LOFT, accept PEDIT
  (if
    (and
      (setq blksel (entsel "\nSelect Block of one-line-segment Polylines: "))
      (= (cdr (assoc 0 (entget (car blksel)))) "INSERT")
    ); and
    (progn ; then
      (command
        "_.copy" (car blksel) "" "0,0" ""
        "_.explode" "_last"
      ); command
      (prompt "\nUse Fence selection across one-line-segment Polylines in sequence to")
      (setq ss
        (ssget ; provides its own "Select objects: " prompt
          '(
            (0 . "LWPOLYLINE")
            (-4 . "<not") (-4 . "&=") (70 . 1) (-4 . "not>") ;; not-closed only
            (-4 . "<not") (-4 . "<>") (42 . 0.0) (-4 . "not>") ;; no-bulge [i.e. line segment] only
            (90 . 2) ;; single-segment only
          ); filter list
        ); ssget
      ); setq
      (command
        "_.loft" ss "" ""
        "_.erase" ss ""
        "_.explode" "_last"
        "_.union" "_previous" ""
        "_.explode" "_last"
        "_.pedit" "_multiple" "_previous" "" "_join" 0 ""
      ); command
    ); progn
  ); if
  (mapcar 'setvar svnames svvals); reset
  (princ)
); defun

You do need to be aware enough to notice the instruction to use Fence selection, type the F, draw the Fence, and give it an additional Enter to complete the selection.

 

 

In experimenting, I find that if that Block is of non-uniform scales, when Exploded its Polylines become Lines, so it doesn't work.  It can be of a scale other than 1, and it can be mirrored so the X and Y don't have the same sign, but it must have equal scale absolute values in all 3 coordinate directions.

 

It could also use *error* handling, but that's easily added, if it otherwise does what you want.

Kent Cooper, AIA
0 Likes