Outermost line

Outermost line

rajeshpatnaik2001
Advocate Advocate
2,084 Views
25 Replies
Message 1 of 26

Outermost line

rajeshpatnaik2001
Advocate
Advocate

I need to find the outermost lines (example: red lines in the snapshot below )from a bunch of lines. Any help in this regards will be highly appreciated. Thanks.

 

Capture.PNG

0 Likes
Accepted solutions (1)
2,085 Views
25 Replies
Replies (25)
Message 2 of 26

Kent1Cooper
Consultant
Consultant

In that particular set of Lines, sorting by the X coordinate of their midpoints would give the answer, and wouldn't be too hard to make a routine do.  But I don't expect that would always be the right thing to compare.  Can you post some more real-world configurations showing the extremes of the kinds of variability you might encounter?  For example, are they always going to run sort-of in the same general direction like the ones in your image, or sometimes might there be greater angular differences?  Might some of them sometimes intersect?  And if so, and if they could be "outer" ones, what criteria could be used to decide which is considered "outermost"?

 

OuterLines.PNG

 

Etc.

Kent Cooper, AIA
Message 3 of 26

Ranjit_Singh
Advisor
Advisor

There is no clear defined logic as already explained in post 2. There can be quite a few variations based on your image. Need some more details about what the end goal is.

0 Likes
Message 4 of 26

john.uhden
Mentor
Mentor

I think I have something working to obtain one (1) outermost line, but not one on each side (yet)...

All the OKs are for testing only and can be discarded when this thing is working to expectations.

 

(defun c:outermost ( / 2d midp avg ss i e ent ents p1 p2 ang angs mids mid d dmax outer)
  (defun 2d (p)(list (car p)(cadr p)))
  (defun midp (p1 p2)(mapcar '* '(0.5 0.5)(mapcar '+ p1 p2)))
  (defun avg (nums)(/ (apply '+ nums)(length nums)))
  (and
    (setq ss (ssget '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (entget (setq e (ssname ss (setq i (1- i))))))
      (setq p1 (2d (cdr (assoc 10 ent))) p2 (2d (cdr (assoc 11 ent))))
      (setq ents (cons (list e p1 p2) ents))
      (setq ang (angle p1 p2))
      (if (< (* 0.5 pi) ang (* 1.5 pi))
        (setq ang (- ang pi))
      )
      (setq angs (cons ang angs))
      (setq mids (cons (midp p1 p2) mids))
    )
    (setq ok 1)
    (setq mid (list (avg (mapcar 'car mids))(avg (mapcar 'cadr mids))))
    (setq ok 2)
    (setq ang (avg angs))
    (setq ok 3)
    (setq p1 (polar mid (+ ang pi) 1e6) p2 (polar mid ang 1e6))
    (setq ok 4)
    (foreach ent ents
      (setq mid (apply 'midp (cdr ent)))
      (setq ok 4.1)
      (setq d (distance mid (inters mid (polar mid (+ ang (* pi 0.5)) 1e4) p1 p2 nil)))
      (setq ok 4.2)
      (if (or (not dmax)(> d dmax))
        (setq dmax d outer (car ent))
      )
      (setq ok 4.3)
    )
    (setq ok 5)
  )
  (if outer (sssetfirst (ssadd outer)))
  (setq ok 6)
  outer
)

John F. Uhden

Message 5 of 26

john.uhden
Mentor
Mentor

I think this one gets both outermost lines.  I have not added undo or error controls.  Ignore (actually discard) all the OKs.

It's not perfect, probably because of the algorithms I used.  At least if it makes a mistake you will see it and can make a manual correction.

 

(defun c:outermost ( / 2d midp avg ss i e ent ents p1 p2 ang angs
                      mids mid ip1 ip2 d1 d2 d1max d2max outer1 outer2)
  (defun 2d (p)(list (car p)(cadr p)))
  (defun midp (p1 p2)(mapcar '* '(0.5 0.5)(mapcar '+ p1 p2)))
  (defun avg (nums)(/ (apply '+ nums)(length nums)))
  (and
    (setq ss (ssget '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (entget (setq e (ssname ss (setq i (1- i))))))
      (setq p1 (2d (cdr (assoc 10 ent))) p2 (2d (cdr (assoc 11 ent))))
      (setq ents (cons (list e p1 p2) ents))
      (setq ang (angle p1 p2))
      (if (< (* 0.5 pi) ang (* 1.5 pi))
        (setq ang (- ang pi))
      )
      (setq angs (cons ang angs))
      (setq mids (cons (midp p1 p2) mids))
    )
    (setq ok 1)
    (setq mid (list (avg (mapcar 'car mids))(avg (mapcar 'cadr mids))))
    (setq ok 2)
    (setq ang (avg angs))
    (setq ok 3)
    (setq p1 (polar mid (+ ang pi) 1e6) p2 (polar mid ang 1e6))
    (setq ok 4)
    (foreach ent ents
      (setq mid (apply 'midp (cdr ent)))
      (setq ok 4.1)
      (and
        (setq ip1 (inters mid (polar mid (+ ang (* pi 0.5)) 1e8) p1 p2))
        (setq d1 (distance mid ip1))
        (if (or (not d1max)(> d1 d1max))
          (setq d1max d1 outer1 (car ent))
        )
      )
      (setq ok 4.2)
      (and
        (setq ip2 (inters mid (polar mid (+ ang (* pi 1.5)) 1e8) p1 p2))
        (setq d2 (distance mid ip2))
        (if (or (not d2max)(> d2 d2max))
          (setq d2max d2 outer2 (car ent))
        )
      )
      (setq ok 4.3)
    )
    (setq ok 5)
  )
  (cond
    ((and outer1 outer2)
      (sssetfirst (ssadd outer1 (ssadd outer2)))
    )
    (outer1 (sssetfirst (ssadd outer1)))
    (outer2 (sssetfirst (ssadd outer2)))
  )
  (setq ok 6)
)

John F. Uhden

0 Likes
Message 6 of 26

rajeshpatnaik2001
Advocate
Advocate

Thanks, Kent!

 

In most of the cases, the lines will be parallel, either vertical, horizontal or aligned. My posted snapshot was an extreme case. The angular difference would not be more. Lines will never intersect with each other, but their sizes will vary.

 

Sorting by the X coordinate of their midpoints would give the answer if the lines were horizontal. Sorting by the Y coordinate of their midpoints would give the answer if the lines were vertical.

But here the issue is, the lines may be aligned, and their sizes may be different as shown in my snapshot.

 

Thanks

Rajesh

0 Likes
Message 7 of 26

rajeshpatnaik2001
Advocate
Advocate

Thanks, Jon!

I will check your code...

 

Rajesh

0 Likes
Message 8 of 26

john.uhden
Mentor
Mentor
Accepted solution

I think this one is improved.  I changed the angle orientation method.  I also got rid of two ifs that were not necessary, contrary to @ВeekeeCZ's preferences.

(defun c:outermost ( / 2d midp avg ss i e ent ents p1 p2 ang angs
                                          mids mid ip1 ip2 d1 d2 d1max d2max outer1 outer2)
  (defun 2d (p)(list (car p)(cadr p)))
  (defun midp (p1 p2)(mapcar '* '(0.5 0.5)(mapcar '+ p1 p2)))
  (defun avg (nums)(/ (apply '+ nums)(length nums)))
  (and
    (setq ss (ssget '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (entget (setq e (ssname ss (setq i (1- i))))))
      (setq p1 (2d (cdr (assoc 10 ent))) p2 (2d (cdr (assoc 11 ent))))
      (setq ents (cons (list e p1 p2) ents))
      (setq ang (angle p1 p2))
      (if (<=  pi ang (* 2 pi))
        (setq ang (- ang pi))
      )
      (setq angs (cons ang angs))
      (setq mids (cons (midp p1 p2) mids))
    )
    (setq ok 1)
    (setq mid (list (avg (mapcar 'car mids))(avg (mapcar 'cadr mids))))
    (setq ok 2)
    (setq ang (avg angs))
    (setq ok 3)
    (setq p1 (polar mid (+ ang pi) 1e6) p2 (polar mid ang 1e6))
    (setq ok 4)
    (foreach ent ents
      (setq mid (apply 'midp (cdr ent)))
      (setq ok 4.1)
      (and
        (setq ip1 (inters mid (polar mid (+ ang (* pi 0.5)) 1e8) p1 p2))
        (setq d1 (distance mid ip1))
        (or (not d1max)(> d1 d1max))  ;; no more if
        (setq d1max d1 outer1 (car ent))
      )
      (setq ok 4.2)
      (and
        (setq ip2 (inters mid (polar mid (+ ang (* pi 1.5)) 1e8) p1 p2))
        (setq d2 (distance mid ip2))
        (or (not d2max)(> d2 d2max))  ;; no more if
        (setq d2max d2 outer2 (car ent))
      )
      (setq ok 4.3)
    )
    (setq ok 5)
  )
  (cond
    ((and outer1 outer2)
      (sssetfirst (ssadd outer1 (ssadd outer2)))
    )
    (outer1 (sssetfirst (ssadd outer1)))
    (outer2 (sssetfirst (ssadd outer2)))
  )
  (setq ok 6)
)
 

 

John F. Uhden

Message 9 of 26

phanaem
Collaborator
Collaborator

Hi John

 

Your method works only for parallel lines.

In OP, the lines are not quite parallel and, in some condition, your average angle is wrong. See the result of your function.

lines.PNG

 

In the image, some angles are almost 0 or pi. The green line is from p1 to p2 and the red lines are from mid to ip1 or ip2.

Your sorting method is ok, but the average angle is not.

Of course, any attempt must assume parallel lines or almost parallel.

 

This is my offer, pretty much the same. The average angle is better approximated and the sorting method uses trans instead of distance.

 

EDIT: OOPS... Code removed. My average angle is also wrong. 🙂

 

0 Likes
Message 10 of 26

john.uhden
Mentor
Mentor

Your testing is greatly appreciated.

I agree that the angle averaging could be better, but I witnessed nothing as awry as shown in your image.  I tested it with lines varying by as much as 60°. Did you try the last version I posted?

Maybe I will play with it some more, though I doubt there is a crowd of onlookers.  Seems to me that the outermost lines should be obvious and not need any programming to find them, unless the OP wants to incorporate the function into something larger.  Then again, maybe his collection of lines is so close to one another that you can't distinguish them without zooming in tight.

John F. Uhden

0 Likes
Message 11 of 26

phanaem
Collaborator
Collaborator

John

The problem is with the angles near 0 (or 2pi) and near pi.

You can have these almost horizontal lines: 1°, 359°, 181° and 179°. Your angs list will be (1 179 1 179) with an average of 90 deg.

 

here is the test set:

(mapcar
  '(lambda (s f)
     (entmakex (list '(0 . "LINE") (cons 10 s) (cons 11 f)))
   )
  '((0.0 0.0) ( 0.0 0.5) ( 0.0 3.0) (10.0 4.5) (10.0 5.0) (10.0 6.5))
  '((7.0 0.0) (10.0 1.5) (10.0 2.0) ( 0.0 3.5) ( 0.0 6.0) ( 3.0 6.5))
)

 

0 Likes
Message 12 of 26

marko_ribar
Advisor
Advisor

Hi, to jump in...

 

According to @phanaem posted picture, I did some mod in John's code... Nothing special, all the work is by John, but although untested I think that the problem may be not in average angle but in checking part...

 

(defun c:outermost ( / 2d midp avg ss i e ent ents p1 p2 ang angs mids mid ip1 ip2 d1 d2 d1min d2min outer1 outer2)
  (defun 2d (p)(list (car p)(cadr p)))
  (defun midp (p1 p2)(mapcar '* '(0.5 0.5)(mapcar '+ p1 p2)))
  (defun avg (nums)(/ (apply '+ nums)(length nums)))
  (and
    (setq ss (ssget '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (entget (setq e (ssname ss (setq i (1- i))))))
      (setq p1 (2d (cdr (assoc 10 ent))) p2 (2d (cdr (assoc 11 ent))))
      (setq ents (cons (list e p1 p2) ents))
      (setq ang (angle p1 p2))
      (if (<=  pi ang (* 2 pi))
        (setq ang (- ang pi))
      )
      (setq angs (cons ang angs))
      (setq mids (cons (midp p1 p2) mids))
    )
    (setq mid (list (avg (mapcar 'car mids))(avg (mapcar 'cadr mids))))
    (setq ang (avg angs))
    (setq p1 (polar mid (+ ang pi) 1e6) p2 (polar mid ang 1e6))
    (foreach ent ents
      (setq mid (apply 'midp (cdr ent)))
      (and
        (setq ip1 (inters mid (polar mid (+ ang (* pi 0.5)) 1e8) p1 p2 nil))
        (setq d1 (distance p1 ip1))
        (or (not d1min)(< d1 d1min))
        (setq d1min d1 outer1 (car ent))
      )
      (and
        (setq ip2 (inters mid (polar mid (+ ang (* pi 1.5)) 1e8) p1 p2 nil))
        (setq d2 (distance p2 ip2))
        (or (not d2min)(< d2 d2min))
        (setq d2min d2 outer2 (car ent))
      )
    )
  )
  (cond
    ((and outer1 outer2)
      (sssetfirst (ssadd outer1 (ssadd outer2)))
    )
    (outer1 (sssetfirst (ssadd outer1)))
    (outer2 (sssetfirst (ssadd outer2)))
  )
  (princ)
)

My revision is in red color in the code...

Unsure though...

M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 13 of 26

phanaem
Collaborator
Collaborator

Hi Marko

 

It's not that. The average angle must approximate the orientation of all lines. Obviously, the Green line in my image is not.

Your lisp is checking the distances on a "perpendicular" line, which is as hard to find as the average angle. Try your lisp on a bunch of perfectly horizontal lines.

 

lines2.PNG

 

 

Finding the average angle is the key. Then you can apply John's code, or you can use a +-90deg angle and apply your code.

 

The average angle is quite a challenge. I thought I had it but it didn't work. Now I have this new version but I'm not sure is foolproof.

(defun c:test ( / m2p ss i e p1 p2 a l u ang n)
  (defun m2p (a b) (mapcar '(lambda (a b) (/ (+ a b) 2.0)) a b))
  (if
    (or
      (setq s (ssget "_I" '((0 . "LINE"))))
      (setq s (ssget '((0 . "LINE"))))
    )
    (progn
      (sssetfirst nil nil)
      (repeat (setq i (sslength s))
        (setq e  (ssname s (setq i (1- i)))
              p1 (vlax-curve-getstartpoint e)
              p2 (vlax-curve-getendpoint e)
              a  (+ pi (angle p1 p2))
              l  (cons (list (m2p p1 p2) e) l)
              )
        (or u (setq u a))
        (if
          (or
            (equal a u (/ pi 2))
            (equal u (- a pi pi) (/ pi 2))
            (equal a (- u pi pi) (/ pi 2))
          )
          (setq ang (cons (rem a (* pi 2.0)) ang))
          (setq ang (cons (rem (- a pi) (* pi 2.0)) ang))
        )
      )
      (setq u (/ (apply '+ ang) (length ang)))
      (setq n (list (cos u) (sin u) 0.0)
            l (vl-sort l
               '(lambda (a b)
                  (<
                    (car (trans (car a) 0 n))
                    (car (trans (car b) 0 n))
                  )
                )
              )
      )
      (sssetfirst nil
        (ssadd
          (cadr (car l))
          (ssadd (cadr (last l)))
        )
      )
    )
  )
  (princ)
)
0 Likes
Message 14 of 26

rajeshpatnaik2001
Advocate
Advocate

Thank you very much, John. It works fine 🙂

Yes, I will use this function in another larger program. 

0 Likes
Message 15 of 26

rajeshpatnaik2001
Advocate
Advocate

Thank you all 🙂

0 Likes
Message 16 of 26

marko_ribar
Advisor
Advisor

I am afraid that @phanaem is quite right... The key is average angle...

All I could do is try to set solution to a problem that I couldn't solve and that you can rely on...

This is my attempt :

 

(defun avgang ( anglst / a pl p )
  (while (setq a (car anglst))
    (setq anglst (cdr anglst))
    (setq pl (cons (list (abs (sin a)) (cos a)) pl))
  )
  (setq p (list (/ (apply (function +) (mapcar (function car) pl)) (length pl)) (/ (apply (function +) (mapcar (function cadr) pl)) (length pl))))
  (if (equal p (list 0.0 0.0) 0.05)
    0.0
    (atan (car p) (cadr p))
  )
)

;(avgang (list (+ 0.0 (/ pi 100)) (- (* 2 pi) (/ pi 100)) (+ pi (/ pi 100)) (- pi (/ pi 100))))
; passed as 0.0
;(avgang (list (+ (/ pi 2) (/ pi 100)) (- (/ pi 2) (/ pi 100)) (+ (* 1.5 pi) (/ pi 100)) (- (* 1.5 pi) (/ pi 100))))
; passed as 1.5708

And in John's code :

(defun c:outermost ( / 2d midp avg avgang ss i e ent ents p1 p2 ang angs mids mid ip1 ip2 d1 d2 d1min d2min outer1 outer2)
  (defun 2d (p)(list (car p)(cadr p)))
  (defun midp (p1 p2)(mapcar '* '(0.5 0.5)(mapcar '+ p1 p2)))
  (defun avg (nums)(/ (apply '+ nums)(length nums)))
  (defun avgang (anglst / a pl p)
    (while (setq a (car anglst))
      (setq anglst (cdr anglst))
      (setq pl (cons (list (abs (sin a)) (cos a)) pl))
    )
    (setq p (list (/ (apply (function +) (mapcar (function car) pl)) (length pl)) (/ (apply (function +) (mapcar (function cadr) pl)) (length pl))))
    (if (equal p (list 0.0 0.0) 0.05)
      0.0
      (atan (car p) (cadr p))
    )
  )
  (and
    (setq ss (ssget '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (entget (setq e (ssname ss (setq i (1- i))))))
      (setq p1 (2d (cdr (assoc 10 ent))) p2 (2d (cdr (assoc 11 ent))))
      (setq ents (cons (list e p1 p2) ents))
      (setq ang (angle p1 p2))
      (setq angs (cons ang angs))
      (setq mids (cons (midp p1 p2) mids))
    )
    (setq mid (list (avg (mapcar 'car mids))(avg (mapcar 'cadr mids))))
    (setq ang (avgang angs))
    (setq p1 (polar mid (+ ang pi) 1e6) p2 (polar mid ang 1e6))
    (foreach ent ents
      (setq mid (apply 'midp (cdr ent)))
      (and
        (setq ip1 (inters mid (polar mid (+ ang (* pi 0.5)) 1e8) p1 p2 nil))
        (setq d1 (distance p1 ip1))
        (or (not d1min)(< d1 d1min))
        (setq d1min d1 outer1 (car ent))
      )
      (and
        (setq ip2 (inters mid (polar mid (+ ang (* pi 1.5)) 1e8) p1 p2 nil))
        (setq d2 (distance p2 ip2))
        (or (not d2min)(< d2 d2min))
        (setq d2min d2 outer2 (car ent))
      )
    )
  )
  (cond
    ((and outer1 outer2)
      (sssetfirst (ssadd outer1 (ssadd outer2)))
    )
    (outer1 (sssetfirst (ssadd outer1)))
    (outer2 (sssetfirst (ssadd outer2)))
  )
  (princ)
)

Regards for now...

M.R.

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

john.uhden
Mentor
Mentor

Marko:

 

Both you and @phanaem are correct.  The average angle is the key.  I think that the OP mentioned that all the lines are almost parallel, so that means I want to adjust the angles so they are in the general direction as any one of the lines, let's say the first in the selection set, then take the average (watching out for angles hovering around 0 being the same a 2pi).

 

BTW, your version produced only one outermost line.

 

I'll be back.

John F. Uhden

0 Likes
Message 18 of 26

john.uhden
Mentor
Mentor

Following my own advice, this one works much better.

 

(defun c:outermost ( / 2d midp avg ss i e ent ents p1 p2 ang ang1 angs
                       mids mid ip1 ip2 d1 d2 d1max d2max outer1 outer2)
  (defun 2d (p)(list (car p)(cadr p)))
  (defun midp (p1 p2)(mapcar '* '(0.5 0.5)(mapcar '+ p1 p2)))
  (defun avg (nums)(/ (apply '+ nums)(length nums)))
  (and
    (setq ss (ssget '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (entget (setq e (ssname ss (setq i (1- i))))))
      (setq p1 (2d (cdr (assoc 10 ent))) p2 (2d (cdr (assoc 11 ent))))
      (setq ents (cons (list e p1 p2) ents))
      (setq ang (angle p1 p2))
      (if (< (abs (rem ang (* 2 pi)))(/ pi 10)) ;; 18°
        (setq ang (rem (+ ang pi)(* 2 pi)))
      )
      (cond
         ((not ang1)(setq ang1 ang))
         ((equal  ang ang1 (/ pi 10))) ;; 18°
         ((setq ang (rem (+ ang pi)(* 2 pi))))
      )
      (setq angs (cons ang angs))
      (setq mids (cons (midp p1 p2) mids))
    )
    (setq mid (list (avg (mapcar 'car mids))(avg (mapcar 'cadr mids))))
    (setq ang (avg angs))
    (setq p1 (polar mid (+ ang pi) 1e6) p2 (polar mid ang 1e6))
    ;; The following line is just for testing and should be removed...
    (entmakex (list '(0 . "line")(cons 10 p1)(cons 11 p2)'(62 . 1)))
    (foreach ent ents
      (setq mid (apply 'midp (cdr ent)))
      (and
        (setq ip1 (inters mid (polar mid (+ ang (* pi 0.5)) 1e8) p1 p2))
        (setq d1 (distance mid ip1))
        (or (not d1max)(> d1 d1max))  ;; no more if
        (setq d1max d1 outer1 (car ent))
      )
      (setq ok 4.2)
      (and
        (setq ip2 (inters mid (polar mid (+ ang (* pi 1.5)) 1e8) p1 p2))
        (setq d2 (distance mid ip2))
        (or (not d2max)(> d2 d2max))  ;; no more if
        (setq d2max d2 outer2 (car ent))
      )
    )
  )
  (setq ss nil)
  (cond
    ((and outer1 outer2)
      (sssetfirst (setq ss (ssadd outer1 (ssadd outer2))))
    )
    (outer1 (sssetfirst (setq ss (ssadd outer1))))
    (outer2 (sssetfirst (setq ss (ssadd outer2))))
  )
  ss
)

Maybe that 18° could be increased up to say 45°.

 

BTW, my daughter found me a T shirt...

"If at first you don't succeed, call it version 1.0"

John F. Uhden

0 Likes
Message 19 of 26

Kent1Cooper
Consultant
Consultant

@phanaem wrote:

.... 

Finding the average angle is the key. Then you can apply .... Now I have this new version but I'm not sure is foolproof.


Here's a brute-force but I think foolproof [though lightly tested] way to find a kind of "average" angle of sort-of-same-direction Lines, regardless of in which comparative direction each was drawn, and/or whether their directions vary slightly crossing that pesky 0-degree direction, etc.

 

It Copies each Line other than the first one to put the copy's start point at the start point of the first one.  If the copy's endpoint is farther from the first one's endpoint than their common start point is, the copy is "aiming" the wrong way, and it spins it around 180 degrees.  Then it adds its endpoint into a running sum, and deletes the copy.  When it's done that with all of them, it averages the endpoints [divides the coordinates of the accumulated sum by the quantity], and finds the angle from the common start point to there.

 

(setq osm (getvar 'osmode))
(setvar 'osmode 0)
(setq
  ss (ssget '((0 . "LINE")))
  qua (sslength ss)
  start0 (vlax-curve-getStartPoint (setq line1 (ssname ss 0)))
  sumend (setq end0 (vlax-curve-getEndPoint line1))
  length0 (distance start0 end0)
); setq
(repeat (1- (setq n qua))
  (command "_.copy" (setq line (ssname ss (setq n (1- n)))) "" (vlax-curve-getStartPoint line) start0)
  (if
    (> (distance end0 (vlax-curve-getEndPoint (setq line (entlast)))) length0); aims away?
    (command "_rotate" line "" start0 "180"); then -- spin it
  ); if
  (setq sumend (mapcar '+ sumend (vlax-curve-getEndPoint line)))
  (entdel line)
); repeat
(setq angavg (angle start0 (mapcar '(lambda (c) (/ c qua)) sumend)))
  ; angle from common start point to averaged endpoint
(setvar 'osmode osm)

 

The result will not always be the true mathematical average of the directions, but [again, when they're not too far from parallel] should be close enough for purposes of this thread.  It does give a "better" result from one point of view than a true mathematical average of only the angles of the Lines, in that it's "weighted" -- a longer Line has a greater impact on the result than a shorter one has.  It seems to handle a fair degree of angular difference, but certain combinations of a Line's direction being more significantly different than the general direction of the rest, and [because of the distance comparison] its length relative to that of the first Line in the selection, can throw off the result.

 

Because of the Copying, it requires that the selected Lines not be on locked Layers -- that could be worked around if necessary.

Kent Cooper, AIA
0 Likes
Message 20 of 26

marko_ribar
Advisor
Advisor

Based on Kent's comment, maybe something like this :

 

(defun c:outermost ( / 2d midp avg avgang ss i e ent ents p1 p2 ang angs mids mid ip1 ip2 d1 d2 d1min d2min outer1 outer2)
  (defun 2d (p)(list (car p)(cadr p)))
  (defun midp (p1 p2)(mapcar '* '(0.5 0.5)(mapcar '+ p1 p2)))
  (defun avg (nums)(/ (apply '+ nums)(length nums)))
  (defun avgang (ptlstpairs / veclst mainvec mainang a1 a2 avgvec avgang)
    (setq veclst (mapcar '(lambda (x) (mapcar '- (cadr x) (car x))) ptlstpairs))
    (setq mainvec (car (mapcar '(lambda (x) (nth x veclst)) (vl-sort-i veclst '(lambda (a b) (> (distance '(0.0 0.0) a) (distance '(0.0 0.0) b)))))))
    (setq mainang (angle '(0.0 0.0) mainvec))
    (setq a1 (- mainang (/ pi 2)) a2 (+ mainang (/ pi 2)))
    (if (< a2 a1)
      (setq a1 (- a1 (* 2 pi)))
      (setq a1 (- a1 (* 2 pi)) a2 (- a2 (* 2 pi)))
    )
    (setq veclst (mapcar '(lambda (x) (if (not (< a1 (- (angle '(0.0 0.0) x) (* 2 pi)) a2)) (mapcar '- x) x)) veclst))
    (setq avgvec (list (avg (mapcar 'car veclst)) (avg (mapcar 'cadr veclst))))
    (setq avgang (angle '(0.0 0.0) avgvec))
    avgang
  )
  (and
    (setq ss (ssget '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (entget (setq e (ssname ss (setq i (1- i))))))
      (setq p1 (2d (cdr (assoc 10 ent))) p2 (2d (cdr (assoc 11 ent))))
      (setq ents (cons (list e p1 p2) ents))
      (setq ang (angle p1 p2))
      (setq angs (cons ang angs))
      (setq mids (cons (midp p1 p2) mids))
    )
    (setq mid (list (avg (mapcar 'car mids))(avg (mapcar 'cadr mids))))
    (setq ang (avgang (mapcar 'cdr ents)))
    (setq p1 (polar mid (+ ang (/ pi 2)) 1e6) p2 (polar mid (- ang (/ pi 2)) 1e6))
    (foreach ent ents
      (setq mid (apply 'midp (cdr ent)))
      (and
        (setq ip1 (inters mid (polar mid ang 1.0) p1 p2 nil))
        (setq d1 (distance p1 ip1))
        (or (not d1min)(< d1 d1min))
        (setq d1min d1 outer1 (car ent))
      )
      (and
        (setq ip2 (inters mid (polar mid ang 1.0) p1 p2 nil))
        (setq d2 (distance p2 ip2))
        (or (not d2min)(< d2 d2min))
        (setq d2min d2 outer2 (car ent))
      )
    )
  )
  (cond
    ((and outer1 outer2)
      (sssetfirst (ssadd outer1 (ssadd outer2)))
    )
    (outer1 (sssetfirst (ssadd outer1)))
    (outer2 (sssetfirst (ssadd outer2)))
  )
  (princ)
)
Marko Ribar, d.i.a. (graduated engineer of architecture)
0 Likes