Overkill not working, objects end to end with tiniest possible angle difference

Overkill not working, objects end to end with tiniest possible angle difference

jamieq
Collaborator Collaborator
1,448 Views
15 Replies
Message 1 of 16

Overkill not working, objects end to end with tiniest possible angle difference

jamieq
Collaborator
Collaborator

I was having an issue with overkill not working on lines aligned end to end, so I investigated by using lisp to get the angles of two lines in one typical instance. The angle of one line was 0.0 radians, but the angle of the other was 7.68523e-011 radians, or 0.0000000000768523. I feel like I should be surprised that AutoCAD doesn't treat this as 0 and consider the lines co-linear, but I've used AutoCAD long enough now. So now I'm wondering if there is any way to get these lines to be connected, either by a workaround that allows overkill to work, or another method. Join works, but there are hundreds of instances in my drawing, and I really don't want to spend the time joining them all individually. 

0 Likes
Accepted solutions (1)
1,449 Views
15 Replies
Replies (15)
Message 2 of 16

Alfred.NESWADBA
Consultant
Consultant

Hi,

 

please upload that dwg-file so we can play with these elements.

 

- alfred -

------------------------------------------------------------------------------------
Alfred NESWADBA
ISH-Solutions GmbH / Ingenieur Studio HOLLAUS
www.ish-solutions.at ... blog.ish-solutions.at ... LinkedIn ... CDay 2025
------------------------------------------------------------------------------------

(not an Autodesk consultant)
0 Likes
Message 3 of 16

jamieq
Collaborator
Collaborator

I put one example in this file. Different numbers, but same result.

0 Likes
Message 4 of 16

dlanorh
Advisor
Advisor
When the overkill dialog appears, check you "Tolerance" Setting (Top Edit Box on my version).

I am not one of the robots you're looking for

0 Likes
Message 5 of 16

jamieq
Collaborator
Collaborator

Thank you. I've already tried many different values for tolerance. Nothing works. The issue isn't that the lines are apart. They share endpoints, so tolerance isn't a factor. The issue is their angle is off by a minuscule amount. 

0 Likes
Message 6 of 16

pendean
Community Legend
Community Legend
Regardless of it being large or small, they are different: OVERKILL skips over differences not compensated for in the pop-up.


0 Likes
Message 7 of 16

jamieq
Collaborator
Collaborator

It’s insane, because literally the only way to find out that the angle of the non-zero lines is by using the lisp (angle [pt] [pt]) command. Every other way to determine an angle in AutoCAD shows zero. Also there's no command or function in AutoCAD that shows the difference in Y position from one endpoint to the other because it is so small. And since the Overkill box has the option to  set the tolerance to 6 decimal points, it is rational to think it would ignore an angle difference at 11 decimal points. That's just some poor programming there. 

0 Likes
Message 8 of 16

ВeekeeCZ
Consultant
Consultant

Not really sure what you're after, but since you're saying that the JOIN works, then you probably want to join them.

This routine I wrote a couple of weeks ago... might be useful.

 

(defun c:JoinMultiple (/ ss i a ang lst ent)

  (if (setq ss (ssget "_:L" '((0 . "LINE"))))
    (repeat (setq i (sslength ss))
      (setq ent (ssname ss (setq i (1- i)))
            ang (angle (cdr (assoc 10 (entget ent))) (cdr (assoc 11 (entget ent))))
            ang (rtos (- ang (if (>= ang pi) pi 0.)) 2 6)
            lst (if (setq a (assoc ang lst))
                  (subst (cons ang (cons ent (cdr a)))
                         a
                         lst)
                  (cons (list ang ent) lst)))))
  (foreach l (mapcar 'cdr lst)
    (while (> (length l) 1)
      (setq e (car l)
            l (cdr l))
      (repeat (setq i (length l))
        (command "_.JOIN" e (nth (setq i (1- i)) l) ""))))
  (princ)
 )
0 Likes
Message 9 of 16

jamieq
Collaborator
Collaborator

The issue is I have is that this scenario exists in hundreds of places in my drawing, some of them co-linear to each other. Using your code, they would all be joined together. I want to join only the co-linear lines within each instance. I've attached another file to show what I mean. 

 

As a side note, your code works fine, but I would recommend an undo begin and end. As it is, trying to undo everything it does could mean undoing hundreds of join commands individually depending on how many lines were selected. 

0 Likes
Message 10 of 16

jamieq
Collaborator
Collaborator

I decided to attack this myself, having found no other solution. The end result required some number rounding code from https://www.cadtutor.net/forum/topic/31921-round-up-number-in-autolisp/. Here's what I came up with. It's a little clunky, but it works, which is the important part. 

 

(defun c:JoinCol (/ ang en ent lst p1 p2 p10 p11 ss)

(sssetfirst nil nil)
(command "_undo" "begin")
(if (setq ss (ssget "_:L" '((0 . "LINE"))))
	(progn
		(mapcar '(lambda (x) (if (> (car x) 0) (setq lst (cons (list (cadr x) (setq p1 (cdr (assoc 10 (entget (cadr x))))) (setq p2 (cdr (assoc 11 (entget (cadr x))))) (cond ((>= (setq ang (roundup (angle p1 p2) 0.0001)) (* 2 pi)) (- ang (* 2 pi))) ((>= ang pi) (- ang pi)) (t ang))) lst)))) (ssnamex ss))
		(while lst
			(setq ent (caar lst) lst (cdr lst))
			(while (progn
				(setq en (entget ent) p10 (cdr (assoc 10 en)) p11 (cdr (assoc 11 en)) ang (cond ((>= (setq ang (roundup (angle p10 p11) 0.0001)) (* 2 pi)) (- ang (* 2 pi))) ((>= ang pi) (- ang pi)) (t ang)))
				(if (vl-some '(lambda (x) (if (and (or (equal p10 (cadr x) 0.1)(equal p10 (caddr x) 0.1)(equal p11 (cadr x) 0.1)(equal p11 (caddr x) 0.1))(equal ang (cadddr x) 0.001)) (setq lst (vl-remove x lst) new (car x)))) lst)
					(progn (command "join" ent new "") t)
					nil
				)
			))
		)
	)
)
(command "_undo" "end")
(princ)
)


;;; -------------------------------------------------------------------------------------
;;; Round a number to the closest matching factor
;;; -------------------------------------------------------------------------------------
;;; num : real/integer value to be rounded
;;; fact : real/integer value as factor to be rounded to
;;; Result: real/integer value rounded to the closest matching to fact
;;; -------------------------------------------------------------------------------------
(defun Round (num fact / n1 n2 d1 d2)
 (setq fact (abs fact)
       n1   (RoundDown num fact)
       n2   (+ n1 fact)
       d1   (abs (- num n1))
       d2   (abs (- num n2))
 )
 (if (< d1 d2)
   n1
   n2
 )
)

;;; -------------------------------------------------------------------------------------
;;; Round a number down to the closest matching factor below the value
;;; -------------------------------------------------------------------------------------
;;; num : real/integer value to be rounded
;;; fact : real/integer value as factor to be rounded to
;;; Result: real/integer value rounded to the closest matching to fact
;;; -------------------------------------------------------------------------------------
(defun RoundDown (num fact /)
 (setq num (- num (rem num fact)))
 (if (and (= (type fact) 'INT) (= (type num) 'REAL))
   (fix num)
   num
 )
)

;;; -------------------------------------------------------------------------------------
;;; Round a number up to the closest matching factor above the value
;;; -------------------------------------------------------------------------------------
;;; num : real/integer value to be rounded
;;; fact : real/integer value as factor to be rounded to
;;; Result: real/integer value rounded to the closest matching to fact
;;; -------------------------------------------------------------------------------------
(defun RoundUp (num fact /)
 (+ (RoundDown num fact) (abs fact))
)
0 Likes
Message 11 of 16

jamieq
Collaborator
Collaborator

Now, if anyone has any tips on faster iteration, I'd love to know. I ran this code on a set of 2400 lines, and it rook about 5 seconds to finish. On a set of 4800 lines it took about 18 seconds to finish. On a set of 7000 lines it took about 41 seconds to finish. On a set of 11400 lines I escaped out of the program at two minutes. 

0 Likes
Message 12 of 16

ВeekeeCZ
Consultant
Consultant

@jamieq wrote:

Now, if anyone has any tips on faster ...


 

<blockquote><hr><li-user login="jamieq" uid="99777"></li-user>&nbsp;wrote:<br>
<p>&nbsp;</p>
<pre>(defun c:JoinCol (/ ang en ent lst p1 p2 p10 p11 ss)
...
					(progn (command "join" ent new "") t)
					nil
...</pre>
<hr></blockquote>
<p>&nbsp;</p>

 

... and of course, you can divide that ss into squares and then work with smaller sss. 

0 Likes
Message 13 of 16

jamieq
Collaborator
Collaborator

I don't follow. Can you elaborate?

0 Likes
Message 14 of 16

ВeekeeCZ
Consultant
Consultant

Definitely the slowest part of your code. Replace that with the entmod...

0 Likes
Message 15 of 16

jamieq
Collaborator
Collaborator

I should have clarified. When I ran the code on the selections mentioned above, zero lines were joined. The time to finish was simply from iterating through the code. In fact, it may be quicker if there were any lines joined, because any line found to be co-linear is then removed from the list and not compared to the rest. So in a selection of 2400 lines, the first is compared to the other 2399, then the next is compared to 2398, and so forth. I actually came up with a possible solution by sorting out the list into two, one for horizontal lines and the other for vertical, allowing for angles up to 45 degrees, since I reduce all the angles to be less than 180 degrees anyway. That way horizontal lines aren't compared to vertical lines when iterated through the list. I'm going to add that. 

0 Likes
Message 16 of 16

jamieq
Collaborator
Collaborator
Accepted solution

Added some sorting to the code to speed up the iteration. Now a selection of 4800 finishes in about 9 seconds, and a selection of 7000 in about 20 seconds. Half the time. 

 

(defun c:JoinCol (/ a ang b en ent hor lst p1 p2 p10 p11 ss ver)

(sssetfirst nil nil)
(command "_undo" "begin")
(if (setq ss (ssget "_:L" '((0 . "LINE"))))
	(progn
		(repeat (setq i (sslength ss))
			(setq tmp (list (setq e (ssname ss (setq i (1- i)))) (setq p1 (cdr (assoc 10 (entget e)))) (setq p2 (cdr (assoc 11 (entget e)))) (cond ((>= (setq ang (roundup (angle p1 p2) 0.0001)) (* 2 pi)) (- ang (* 2 pi))) ((>= ang pi) (- ang pi)) (t ang))))
			(if (< (* 0.2 pi) (cadddr tmp) (* 0.8 pi)) (setq ver (cons tmp ver)) (setq hor (cons tmp hor)))
		)
		(setq a 0)
		(foreach x (list hor ver)
			(setq lst x)
			(while lst
				(setq ent (caar lst) lst (cdr lst) b 0)
				(while (progn
					(setq en (entget ent) p10 (cdr (assoc 10 en)) p11 (cdr (assoc 11 en)) ang (cond ((>= (setq ang (roundup (angle p10 p11) 0.0001)) (* 2 pi)) (- ang (* 2 pi))) ((>= ang pi) (- ang pi)) (t ang)))
					(if (vl-some '(lambda (x) (if (and (or (equal p10 (cadr x) fuzz)(equal p10 (caddr x) fuzz)(equal p11 (cadr x) fuzz)(equal p11 (caddr x) fuzz))(equal ang (cadddr x) 0.001)) (setq lst (vl-remove x lst) new (car x)))) lst)
						(progn (command "join" ent new "") (setq b (1+ b)))
						nil
					)
				))
				(if (> b 0) (setq a (+ (1+ b) a)))
			)
		)
	)
)
(princ (strcat "\n" (itoa a) " lines joined."))
(command "_undo" "end")
(princ)
)

;;; -------------------------------------------------------------------------------------
;;; Round a number to the closest matching factor
;;; -------------------------------------------------------------------------------------
;;; num : real/integer value to be rounded
;;; fact : real/integer value as factor to be rounded to
;;; Result: real/integer value rounded to the closest matching to fact
;;; -------------------------------------------------------------------------------------
(defun Round (num fact / n1 n2 d1 d2)
(setq fact (abs fact)
n1 (RoundDown num fact)
n2 (+ n1 fact)
d1 (abs (- num n1))
d2 (abs (- num n2))
)
(if (< d1 d2)
n1
n2
)
)

;;; -------------------------------------------------------------------------------------
;;; Round a number down to the closest matching factor below the value
;;; -------------------------------------------------------------------------------------
;;; num : real/integer value to be rounded
;;; fact : real/integer value as factor to be rounded to
;;; Result: real/integer value rounded to the closest matching to fact
;;; -------------------------------------------------------------------------------------
(defun RoundDown (num fact /)
(setq num (- num (rem num fact)))
(if (and (= (type fact) 'INT) (= (type num) 'REAL))
(fix num)
num
)
)

;;; -------------------------------------------------------------------------------------
;;; Round a number up to the closest matching factor above the value
;;; -------------------------------------------------------------------------------------
;;; num : real/integer value to be rounded
;;; fact : real/integer value as factor to be rounded to
;;; Result: real/integer value rounded to the closest matching to fact
;;; -------------------------------------------------------------------------------------
(defun RoundUp (num fact /)
(+ (RoundDown num fact) (abs fact))
)
0 Likes