code to create 3DPOLY from 3D LINes

code to create 3DPOLY from 3D LINes

autoid374ceb4990
Collaborator Collaborator
1,351 Views
39 Replies
Message 1 of 40

code to create 3DPOLY from 3D LINes

autoid374ceb4990
Collaborator
Collaborator

I have a routine that creates a series of 3d LINEs for the boundary of a group of 3DFACEs.

Does anyone have an autoLisp (not visual lisp, I am using R14) that would join the 3D LINEs into  a 3D POLYINE?  The 3D LINEs are not in any order.

boundary.jpg

0 Likes
Accepted solutions (2)
1,352 Views
39 Replies
Replies (39)
Message 21 of 40

gbattinPH5TG
Advocate
Advocate
Accepted solution

Here is an oldie that I found a long time ago (1990-ish)

Message 22 of 40

autoid374ceb4990
Collaborator
Collaborator

@gbattinPH5TG wrote:

Here is an oldie that I found a long time ago (1990-ish)


That one works in R14.  Thanks.

0 Likes
Message 23 of 40

marko_ribar
Advisor
Advisor
Accepted solution

Looking at that uploaded *.lsp and your comment, I guess that R14 doesn't work well with (function) function...

Please try this mod...

 

(defun c:lins23dpoly-vanilla-foo ( / _reverse _foo _vl-remove ss i lil lll lix liln li lii el )

  (defun _reverse ( lst / r )
    (while lst
      (setq r (cons (car lst) r))
      (setq lst (cdr lst))
    )
    r
  )

  (defun _foo ( li lst / el r )
    (while (setq el (car lst))
      (if (or (equal (cadr li) (car el) 1e-8) (equal (cadr li) (cadr el) 1e-8))
        (setq r el lst nil)
      )
      (setq lst (cdr lst))
    )
    r
  )

  (defun _vl-remove ( el lst / e r )
    (while (setq e (car lst))
      (if (not (equal e el 1e-8))
        (setq r (cons e r))
      )
      (setq lst (cdr lst))
    )
    (_reverse r)
  )

  (prompt "\nSelect LINE entities that are connected in array to join them to 3DPOLYLINE...")
  (if (setq ss (ssget (list (cons 0 "LINE"))))
    (progn
      (repeat (setq i (sslength ss))
        (setq lil (cons (list (trans (cdr (assoc 10 (setq lix (entget (ssname ss (setq i (1- i))))))) 0 1) (trans (cdr (assoc 11 lix)) 0 1)) lil))
      )
      (setq lll lil liln (cons (car lil) liln))
      (while (setq li (car lil))
        (setq lil (cdr lil))
        (setq lii (_foo li lil))
        (cond
          ( (and lii (equal (cadr li) (car lii) 1e-8))
            (setq liln (cons lii liln) lil (cons lii (_vl-remove lii lil)))
          )
          (  lii
            (setq liln (cons (_reverse lii) liln) lil (cons (_reverse lii) (_vl-remove lii lil)))
          )
        )
      )
      (setq el (entlast))
      (command "_.3dpoly")
      (foreach li liln
        (command "_non" (car li))
      )
      (if (or (_foo (list (caar liln) (cadr (last liln))) lll) (_foo (list (cadr (last liln)) (caar liln)) lll))
        (command "_c")
        (command "")
      )
      (if (not (eq el (entlast)))
        (sssetfirst nil (ssadd (entlast)))
      )
    )
  )
  (princ)
)

HTH.

M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 24 of 40

autoid374ceb4990
Collaborator
Collaborator

@marko_ribar wrote:

Looking at that uploaded *.lsp and your comment, I guess that R14 doesn't work well with (function) function...

Please try this mod...

HTH.

M.R.


That fixed it. Thanks.

[edit] I was wrong.  Your last update had a few problems.  see above post.

0 Likes
Message 25 of 40

autoid374ceb4990
Collaborator
Collaborator

@marko_ribar wrote:

Looking at that uploaded *.lsp and your comment, I guess that R14 doesn't work well with (function) function...

Please try this mod...

HTH.

M.R.


I spoke too soon.  After further experimentation with "lins23dpoly-vanilla-foo.lsp" I found the following problems:

In the picture below the blue 3DPOLY left out one line segment.  I erased several line segments around the missing line segment and tried again - the red 3DPOLY is the result.  "The 3djoin.lsp" posted earlier works correctly, but erases the selected line segments which should be easy to fix.

lins23dpoly-vanilla-foo.jpg

0 Likes
Message 26 of 40

CADaSchtroumpf
Advisor
Advisor

@autoid374ceb4990 

Hi,

With your sample drawing, this simple code in vanilla lisp seem to work's...

(defun c:foo ( / ss ent dxf_ent lst n l_10 l_11 l_e)
  (setq ss (ssget "_X" '((0 . "LINE") (8 . "0"))))
  (cond
    (ss
      (setq
        ent (ssname ss 0)
        dxf_ent (entget ent)
        lst (list (cdr (assoc 11 dxf_ent)) (cdr (assoc 10 dxf_ent)))
        ss (ssdel ent ss)
        n (sslength ss)
      )
      (entdel ent)
      (repeat (setq n (sslength ss))
        (setq
          ent (ssname ss (setq n (1- n)))
          dxf_ent (entget ent)
          l_10 (cons (cdr (assoc 10 dxf_ent)) l_10)
          l_11 (cons (cdr (assoc 11 dxf_ent)) l_11)
          l_e (cons ent l_e)
        )
      )
      (while (and l_10 l_11)
        (mapcar
          '(lambda (x y e)
            (cond
              ((or (equal x (car lst) 1E-08) (equal y (car lst) 1E-08))
                (if (equal x (car lst) 1E-08)
                  (setq lst (cons y lst))
                  (setq lst (cons x lst))
                )
                (setq
                  l_11 (append (cdr (member x (reverse l_11))) (cdr (member x l_11)))
                  l_10 (append (cdr (member y (reverse l_10))) (cdr (member y l_10)))
                  l_e (append (cdr (member e (reverse l_e))) (cdr (member e l_e)))
                  ss (ssdel e ss)
                )
                (entdel e)
              )
            )
          )
          l_11 l_10 l_e
        )
      )
    )
  )
  (cond
    (lst
      (entmake '((0 . "POLYLINE") (70 . 8) (62 . 1)))
      (foreach pt lst (entmake (list '(0 . "VERTEX") '(62 . 1) '(70 . 32) (cons 10 pt))))
      (entmake '((0 . "SEQEND")))
    )
  )
  (prin1)
)

 

0 Likes
Message 27 of 40

marko_ribar
Advisor
Advisor

Yes, I see... This version also worked with me... I tested on open paths and closed ones...

 

(defun c:lins23dpoly-vanilla-foo ( / _reverse _foo _vl-remove _freept _findli ss i lil lll lix liln li lii el p ll )

  (defun _reverse ( lst / r )
    (while lst
      (setq r (cons (car lst) r))
      (setq lst (cdr lst))
    )
    r
  )

  (defun _foo ( li lst / el r )
    (while (setq el (car lst))
      (if (or (equal (cadr li) (car el) 1e-8) (equal (cadr li) (cadr el) 1e-8))
        (setq r el lst nil)
      )
      (setq lst (cdr lst))
    )
    r
  )

  (defun _vl-remove ( el lst / e r )
    (while (setq e (car lst))
      (if (not (equal e el 1e-8))
        (setq r (cons e r))
      )
      (setq lst (cdr lst))
    )
    (_reverse r)
  )

  (defun _freept ( lst / pts ppp )
    (foreach li lst
      (setq pts (cons (car li) pts) pts (cons (cadr li) pts))
    )
    (setq ppp pts)
    (foreach p1 pts
      (setq pts (cdr pts))
      (foreach p2 pts
        (if (equal p1 p2 1e-8)
          (setq ppp (_vl-remove p1 ppp))
        )
      )
    )
    (if ppp
      (car ppp)
    )
  )

  (defun _findli ( p lst / li r )
    (while (setq li (car lst))
      (if (or (equal p (car li) 1e-8) (equal p (cadr li) 1e-8))
        (setq r li lst nil)
      )
      (setq lst (cdr lst))
    )
    r
  )

  (prompt "\nSelect LINE entities that are connected in array to join them to 3DPOLYLINE...")
  (if (setq ss (ssget (list (cons 0 "LINE"))))
    (progn
      (repeat (setq i (sslength ss))
        (setq lil (cons (list (trans (cdr (assoc 10 (setq lix (entget (ssname ss (setq i (1- i))))))) 0 1) (trans (cdr (assoc 11 lix)) 0 1)) lil))
      )
      (if (setq p (_freept lil))
        (progn
          (setq li (_findli p lil))
          (setq lil (cons li (_vl-remove li lil)))
        )
      )
      (setq lll lil liln (cons (car lil) liln))
      (while (setq li (car lil))
        (setq lil (cdr lil))
        (setq lii (_foo li lil))
        (cond
          ( (and lii (equal (cadr li) (car lii) 1e-8))
            (setq liln (cons lii liln) lil (cons lii (_vl-remove lii lil)))
          )
          (  lii
            (setq liln (cons (_reverse lii) liln) lil (cons (_reverse lii) (_vl-remove lii lil)))
          )
        )
      )
      (if (equal p (car (last liln)) 1e-8)
        (setq liln (_reverse liln))
      )
      (setq el (entlast))
      (command "_.3dpoly")
      (foreach li liln
        (setq ll li)
        (command "_non" (car li))
      )
      (if p
        (command "_non" (cadr ll) "")
        (command "_c")
      )
      (if (not (eq el (entlast)))
        (sssetfirst nil (ssadd (entlast)))
      )
    )
  )
  (princ)
)

HTH.

M.R.

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

autoid374ceb4990
Collaborator
Collaborator

When I try to load your "foo.lsp" into my old R14 I get:   "ACAD error: too few arguments"

0 Likes
Message 29 of 40

CADaSchtroumpf
Advisor
Advisor

@autoid374ceb4990 

Sorry, I have an extra parenthesis after the declaration of the local variable.

I have modified the code, please reload it.

0 Likes
Message 30 of 40

autoid374ceb4990
Collaborator
Collaborator

@CADaSchtroumpf wrote:

@autoid374ceb4990 

Sorry, I have an extra parenthesis after the declaration of the local variable.

I have modified the code, please reload it.


I tried your latest version and it works if you select all the lines with one window or crossing box, but if I use 2 or 3 windows or crossing boxes, I get this:

lins23dpoly-vanilla-foo2.jpg

0 Likes
Message 31 of 40

komondormrex
Mentor
Mentor

@autoid374ceb4990

Is a 3d polyline in question always supposed to be closed?

0 Likes
Message 32 of 40

autoid374ceb4990
Collaborator
Collaborator

My 3D boundary LINEs  are always  created from TINs and are always closed.  I just tried an open set of lines to see what would happen. The latest version of "foo.lsp" works properly with open or closed line segments.

"lins23dpoly-vanilla-foo.lsp"  works with a single selection set, but adds extra line segments if you use 2 or 3 selection sets when selecting the line segments.  I thought this quirk was because of the selection code in R14, but "foo.lsp" works properly with several selection sets. ?????

0 Likes
Message 33 of 40

Kent1Cooper
Consultant
Consultant

How about a different approach?  Jut trace around the Lines, starting from any one of them, adding the far end of each subsequent Line found in relation to the previous one, in a 3DPOLY command, until you get back to the beginning:

(defun C:L23DP ; Lines {to} 3DPolyline
  (/ lineA line1 p1 p2 p3 ss)
  (setq
    lineA (car (entsel "\nSelect one of the Lines to join into a 3D Polyline: ")); starting Line
    line1 lineA; working first Line
  ); setq
  (command "_.3DPOLY" ; start 3DPoly command
    (setq p1 (cdr (assoc 10 (entget line1)))); working first Line's start
    (setq p2 (cdr (assoc 11 (entget line1)))); working first Line's end
  ); leave in 3DPoly command
  (while
    (and
      (setq
        p3 p2 ; previous point for reference
        ss (ssget "_F" (list p2 p1) '((0 . "LINE"))); incl. working Line & Lines meeting both ends
        line1 (ssname ss 0); first in selection [meets p2 end of working Line]
      ); setq
      (not (equal line1 lineA)); not back to beginning yet
    ); and
    (setq
      p1 (cdr (assoc 10 (entget line1)))
      p2 ; far end from previous Line
        (if (equal p1 p3 1e-6) ; is this the next Line's start?
          (cdr (assoc 11 (entget line1))); then -- use its end
          p1 ; else -- use its start
        ); if
      p3 p2
    ); setq
    (command p2); feed to 3DPoly command
  ); while
  (command "_undo" "_close"); back off latest and Close [end 3DPOLY]
  (prin1)
); defun

Comments:

You don't select all the Lines -- just one of them.

No (vl....) or (function) functions.

It doesn't matter what the drawn order of the Lines was, nor in which direction each was drawn. *

It counts on the set of Lines being closed as you have said they always will be.

It works in the WCS, but could probably be enhanced to work in a non-World UCS.

It draws the 3DPolyline on the current Layer with all current property settings.

If you have running Osnap on, as long as ENDpoint is among the modes, it will work without the need to turn them off.  If you might have [for example] MIDpoint but not also ENDpoint or INTersection or NEArest, it will be misled.

It finds three Lines based on a Fence using the "current" one's endpoints, and the setting of line1 [at line 16] seems to be reliable -- the Line off the starting end of the Fence always seems to be the first in the resulting selection -- but I'm not positive that it will always be.  [Some other possible selection approaches could get incorrect results depending on the Pickbox size and/or Zoom level, but this seems unaffected by those.]

In trials in your sample drawing, it sometimes ran into a problem down near the bottom -- some kind of backtracking on itself, always at the same location -- but it didn't always do it, and I haven't been able to figure out what could be causing it [e.g. I couldn't find my suspicions of a non-meeting of Line ends, or an overlap, in the area].

It could easily be enhanced to remove the original Lines.

[And it could use some of the regular enhancements -- command echo suppression, Osnap and blip controls, Layer and other property settings, etc.]

* EDIT:  In further testing, I'm sometimes running into more of those hold-ups, which may be related to drawn direction.  I'll keep trying to figure out what's going on.

Kent Cooper, AIA
0 Likes
Message 34 of 40

autoid374ceb4990
Collaborator
Collaborator

@Kent1Cooper wrote:

How about a different approach?  Jut trace around the Lines, starting from any one of them, adding the far end of each subsequent Line found in relation to the previous one, in a 3DPOLY command, until you get back to the beginning:

Comments:

You don't need to select all the Lines -- just one of them.

No (vl....) or (function) functions.

It doesn't matter what the drawn order of the Lines was, nor in which direction each was drawn.

It counts on the set of Lines being closed as you have said they always will be.

It works in the WCS, but could probably be enhanced to work in a non-World UCS.

It draws the 3DPolyline on the current Layer with all current property settings.

If you have running Osnap on, as long as ENDpoint is among the modes, it will work without the need to turn them off.  If you might have [for example] MIDpoint but not also ENDpoint or INTersection or NEArest, it will be misled.

It finds three Lines based on a Fence using the "current" one's endpoints, and the setting of line1 [at line 16] seems to be reliable -- the Line off the starting end of the Fence always seems to be the first in the resulting selection -- but I'm not positive that it will always be.  [Some other possible selection approaches could get incorrect results depending on the Pickbox size and/or Zoom level, but this seems unaffected by those.]

In trials in your sample drawing, it sometimes ran into a problem down near the bottom -- some kind of backtracking on itself, always at the same location -- but it didn't always do it, and I haven't been able to figure out what could be causing it [e.g. I couldn't find my suspicions of a non-meeting of Line ends, or an overlap, in the area].

It could easily be enhanced to remove the original Lines.

[And it could use some of the regular enhancements -- command echo suppression, Osnap and blip controls, Layer and other property settings, etc.]


It seems to work correctly.  Strange though, I tried it 3 times in the same drawing and got the "ACAD error: invalid argument list" after creating a partial outline.  Worked the 4th time and several times after.   Closed AutoCAD, reopened the same drawing, tried the same group of lines, OSMODE = 0.  On the first trial I got the  "ACAD error: invalid argument list" error, then it worked 4 times in a row, selecting different start lines each time.  Seems that when it fails OSMODE is set to 1 ????? 

0 Likes
Message 35 of 40

komondormrex
Mentor
Mentor

What if a user wants to make several 3dpoly with one selection set?

0 Likes
Message 36 of 40

autoid374ceb4990
Collaborator
Collaborator

@komondormrex wrote:

What if a user wants to make several 3dpoly with one selection set?


foo.lsp will make a single polyline from 2 or 3 different sets of lines, but they are all joined together.

3 groups.jpg

0 Likes
Message 37 of 40

komondormrex
Mentor
Mentor

i meant a slightly different thing

komondormrex_0-1786895294270.png

 

0 Likes
Message 38 of 40

autoid374ceb4990
Collaborator
Collaborator

foo.lsp produces the following results.  I have not tried any of the other solutions.

2 selections.jpg

0 Likes
Message 39 of 40

Kent1Cooper
Consultant
Consultant

@Kent1Cooper wrote:

.... In further testing, I'm sometimes running into more of those hold-ups, which may be related to drawn direction.  I'll keep trying to figure out what's going on.


This seems to have corrected that [again, lightly tested]:

(defun C:L23DP ; Lines {to} 3DPolyline
  (/ lineA line1 p1 p2 p3 ss)
  (setq
    lineA (car (entsel "\nSelect one of the Lines to join into a 3D Polyline: ")); starting Line
    line1 lineA; working first Line
  ); setq
  (command "_.3DPOLY" ; start 3DPoly command
    (setq p1 (cdr (assoc 10 (entget line1)))); working first Line's start
    (setq p2 (cdr (assoc 11 (entget line1)))); working first Line's end
  ); leave in 3DPoly command
  (while
    (and
      (setq
        p3 p2 ; previous point for reference
        ss
          (ssget "_X" ; incl. working Line & Line meeting downstream end
            (list '(0 . "LINE") '(-4 . "<OR") (cons 10 p2) (cons 11 p2) '(-4 . "OR>"))
          ); ssget
        ss (ssdel line1 ss); leave only next one
        line1 (ssname ss 0); first [should be only] in selection [meets p2 end of working Line]
      ); setq
      (not (equal line1 lineA)); not back to beginning yet
    ); and
    (setq
      p1 (cdr (assoc 10 (entget line1)))
      p2 ; far end from previous Line
        (if (equal p1 p3 1e-6) ; is this the next Line's start?
          (cdr (assoc 11 (entget line1))); then -- use its end
          p1 ; else -- use its start
        ); if
      p3 p2
    ); setq
    (command p2); feed to 3DPoly command
  ); while
  (command "_undo" "_close"); back off latest and Close
  (prin1)
); defun

It looks for the next Line only at the downstream end of the previous one, by start/endpoint coordinates rather than by Fence selection.

Kent Cooper, AIA
0 Likes
Message 40 of 40

autoid374ceb4990
Collaborator
Collaborator

@Kent1Cooper wrote:



This seems to have corrected that [again, lightly tested]:

It looks for the next Line only at the downstream end of the previous one, by start/endpoint coordinates rather than by Fence selection

 


Kent:

Seems to work for most line picks, but the blue lines in the attached picture when picked as the start point still cause problems.  I did not check all lines, just picked lines at random.

lines.jpg

0 Likes