Programming Challenge 11/22

Programming Challenge 11/22

john.uhden
Mentor Mentor
2,415 Views
42 Replies
Message 1 of 43

Programming Challenge 11/22

john.uhden
Mentor
Mentor

I haven't tried this myself, but our challenge is to write code that draws a line tangent to two (2) non-touching non-overlapping non-concentric circles without using object snaps.  If there is a shorter line vs. a longer line, we want the shorter one.  Yes, both circles are at elevation 0 with normal normals and that have radii greater than 0.

I'm guessing it's nothing but trigonometry, but I dunno yet.

The function should not use any ssget but instead take two (2) arguments, one for each of the two circles as enames.  Yes, you may use supporting functions of your own making.

The winner will be the one that gets the most likes.  No, you can't "like" your own.

Any ties will be broken by the decision of @dbroad , with or without bribes.

John F. Uhden

2,416 Views
42 Replies
Replies (42)
Message 21 of 43

Sea-Haven
Mentor
Mentor

Dont know used Line tan tan function to just point out the 4 lines.

 

On my to do list is two circle/arc's add a 3rd circle/arc between long been a FIllet nightmare. Maybe challenge 01/23

0 Likes
Message 22 of 43

dbroad
Mentor
Mentor

I didn't look at the thread until I tried on my own. I did one with both sets of tangents and then read through the thread and culled mine.  I like gile's but a lot of them look good. Here is one with an active x approach and a mirror.

(defun tantest (ce1 ce2	/ ANG1 ANG2 ANG3 ASIN BLK C1 C2 CP1 CP2 D OID PANG R1 R2 R3 TL2 )
  (defun asin (y) (atan y (sqrt (- 1 (* y y)))))
  (setq c1 (vlax-ename->vla-object ce1));circle objects
  (setq c2 (vlax-ename->vla-object ce2))
  (setq cp1 (vlax-get c1 'center));circle centers in list form
  (setq cp2 (vlax-get c2 'center))
  (setq r1 (vla-get-radius c1))
  (setq r2 (vla-get-radius c2))
  (setq d (distance cp1 cp2))
  (setq r3 (+ r1 r2)) ;change to (abs (- r1 r2)) for exterior tangents
  (setq ang2 (angle cp1 cp2))
  (setq ang1 (asin (/ r3 d)))
  (setq ang3 (+ ang2 ang1))
  (setq pang (- ang3 (/ pi 2)));change to (+ ang3 (/ pi 2)) for exterior tangents
  (setq oid (vla-get-ownerid c1))
  (setq blk (vla-objectidtoobject (vla-get-document c1) oid))
  (setq	tl2 (vla-addline
	      blk
	      (vlax-3d-point (polar cp1 pang r1))
	      (vlax-3d-point (polar cp2 (+ pang pi) r2));change pi to 0 for exterior tangents
	      )
	)
  (vla-mirror tl2 (vla-get-center c1) (vla-get-center c2))
  (princ)
  )

 Would be slightly shorter if arguments could be objects. Only uses interior tangents with directions about how to convert to exterior tangents. 

Architect, Registered NC, VA, SC, & GA.
Message 23 of 43

john.uhden
Mentor
Mentor

@dbroad 

That works, but which tangent is shorter? 🤣

John F. Uhden

0 Likes
Message 24 of 43

dbroad
Mentor
Mentor

Interior tangents are both the shortest and are of equal length. Both exterior tangents are longer and are equal length.

Architect, Registered NC, VA, SC, & GA.
0 Likes
Message 25 of 43

john.uhden
Mentor
Mentor

@dbroad 

You must be kidding me.  Guess you believe in that jive that @_gile posted.  🤓

John F. Uhden

Message 26 of 43

dbroad
Mentor
Mentor

Of course it would be much easier to just draw 2 circles, manually draw the tangents using osnaps, autoconstrain the geometry and then create a dynamic block.

Architect, Registered NC, VA, SC, & GA.
0 Likes
Message 27 of 43

john.uhden
Mentor
Mentor

@dbroad 

You are such a wise mule.  😵

John F. Uhden

Message 28 of 43

calderg1000
Mentor
Mentor

Regards @_gile 

Here my attempt of demonstration by inequations of its logical deduction.

;Premise:r1>r2
;Option 1: r1-r2>r1+r2 -> r2<0, FALSE
;Option 2: r1-r2<r1+r2 -> r2>0 -> TRUE
;If r1>r2 and r2>0 -> r1+r2>r1-r2 (Gile Conclusion)

 


Carlos Calderon G
EESignature
>Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

0 Likes
Message 29 of 43

_gile
Consultant
Consultant

@calderg1000  a écrit :

Regards @_gile 

Here my attempt of demonstration by inequations of its logical deduction.

;Premise:r1>r2
;Option 1: r1-r2>r1+r2 -> r2<0, FALSE
;Option 2: r1-r2<r1+r2 -> r2>0 -> TRUE
;If r1>r2 and r2>0 -> r1+r2>r1-r2 (Gile Conclusion)

 


Sorry, I should have said: "because r1+r2 is always greater than abs(r1-r2)" or "because sqr (r1+r2)) is always greater than sqr(r1-r2)" (we are using the Pythagorean theorem).

That means we do not have to care if r1 is greater or smaller than r2.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 30 of 43

_gile
Consultant
Consultant

A generic function.

It replies to the challenge when called with flags = 2.

 

 

;; assign
;; Assigns DXF values of the supplied ename to the symbols
;;
;; Arguments
;; ename       : ename of the object
;; codeSymList : list of dotted pairs (dxfGroupCode . symbol)
;;
;; Example;
;; (assign line '((10 . starttPt) (11 . endPt)))
(defun assign (ename codeSymList / l)
  (setq l (entget ename))
  (mapcar
    '(lambda (x) (set (cdr x) (cdr (assoc (car x) l))))
    codeSymList
  )
)

;; circleTangents
;; Draws tangents between two circles (if possible)
;;
;; Arguments
;; circle1 : ename of the first circle
;; circle2 : ename of the second circle
;; flags   : a value indicating which tangents to draw,
;;           sum of the following values:
;;           - 1: outside tangents
;;           - 2: inside tangents
(defun circleTangents
       (circle1 circle2 flags / c1 r1 c2 r2 a1 c b d a2 l)
  (assign circle1 '((10 . c1) (40 . r1)))
  (assign circle2 '((10 . c2) (40 . r2)))
  (setq	a1 (angle c1 c2)
	c  (distance c1 c2)
  )
  (foreach x (list (list 1 - 0) (list 2 + pi))
    (and
      (< 0 (logand (car x) flags))
      (setq b ((cadr x) r1 r2))
      (< b c)
      (setq d (sqrt (- (* c c) (* b b))))
      (setq a2 (atan d b))
      (setq l (vl-list*
		(list (+ a1 a2) (+ a1 a2 (caddr x)))
		(list (- a1 a2) (- a1 a2 (caddr x)))
		l
	      )
      )
    )
  )
  (mapcar
    '(lambda (p)
       (entmakex
	 (list (cons 0 "LINE")
	       (cons 10 (polar c1 (car p) r1))
	       (cons 11 (polar c2 (cadr p) r2))
	 )
       )
     )
    l
  )
)

 

 

 

Testing command:

 

 

(defun c:test (/ c1 c2 kw)
  (if
    (and
      (setq c1 (car (entsel "\nFirst circle: ")))
      (setq c2 (car (entsel "\nSecond circle: ")))
      (progn
	(initget 1 "Inside Outside Both")
	(setq kw (getkword "\nTangents to draw [Inside/Outside/Both]: "))
      )
    )
     (circleTangents
       c1
       c2
       (cond
	 ((= kw "Inside") 2)
	 ((= kw "Outside") 1)
	 (T 3)
       )
     )
  )
  (princ)
)

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 31 of 43

john.uhden
Mentor
Mentor

Here's a condensed version that we hope you will like:

(defun @Anonymous (x) (atan (sqrt (- 1 (* x x))) x)))
(defun @Anonymous (e1 e2 / rp1 r1 rp2 r2 d d1 d2 a p1 p2)
  (mapcar 'set '(rp1 r1)(mapcar 'cdr (mapcar '(lambda (x)(assoc x (entget e1)))'(10 40))))
  (mapcar 'set '(rp2 r2)(mapcar 'cdr (mapcar '(lambda (x)(assoc x (entget e2)))'(10 40))))
  (setq d (distance rp1 rp2) d2 (/ d (1+ (/ r1 r2))) a(@acos (/ r2 d2))
       p1 (polar rp1 (+ (angle rp1 rp2) a) r1)
       p2 (polar rp2 (+ (angle rp2 rp1) a) r2)
  )
  (entmakex (list '(0 . "LINE")(cons 10 p1)(cons 11 p2)'(62 . 1)))
)

John F. Uhden

Message 32 of 43

calderg1000
Mentor
Mentor

Regards.

Here my code applying only plane geometry, by the calculation method of the center of inverse homothety. I hope the architect @dbroad will kindly confirm its validity. I have observed that the other proposals have raised the solution by applying trigonometric functions, which should generate an unexpected margin of error.

(defun tan_int (s1 s2 / spm c1 c2 px1 px2 po pm1 com lspt pt1 pt2 pr1 pr2 pt3 pt4)
  (setq
    spm (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object)))
  )
  (setq c1  (cdr (assoc 10 (entget s1)))
        c2  (cdr (assoc 10 (entget s2)))
        px1 (vlax-curve-getpointatparam s1 (* 1.5 pi))
        px2 (vlax-curve-getpointatparam s2 (* 0.5 pi))
        po  (inters c1 c2 px1 px2)
        pm1 (mapcar '* (mapcar '+ c1 po) '(0.5 0.5 1.))
        com (vla-addcircle spm (vlax-3d-point pm1) (* (distance c1 po) 0.5))
  )
  (setq lspt (vlax-invoke (vlax-ename->vla-object s1)
                          'IntersectWith
                          com
                          acExtendNone
             )
        pt1  (list (nth 0 lspt) (nth 1 lspt) (nth 2 lspt))
        pt2  (list (nth 3 lspt) (nth 4 lspt) (nth 5 lspt))
        pr1  (vlax-curve-getparamatpoint s1 pt1)
        pr2  (vlax-curve-getparamatpoint s1 pt2)
        pt3  (vlax-curve-getpointatparam s2 (+ pr1 pi))
        pt4  (vlax-curve-getpointatparam s2 (+ pr2 pi))
  )
  (entdel (vlax-vla-object->ename com))
  (vla-addline spm (vlax-3d-point pt1) (vlax-3d-point pt3))
  (vla-addline spm (vlax-3d-point pt2) (vlax-3d-point pt4))
)

(tan_int (car(entsel "\nSelect Circle1: "))(car(entsel "\nSelect Circle2: ")))

 


Carlos Calderon G
EESignature
>Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

Message 33 of 43

_gile
Consultant
Consultant

@calderg1000  a écrit :

I have observed that the other proposals have raised the solution by applying trigonometric functions, which should generate an unexpected margin of error.


Could you elaborate about "unexpected margin of error", please ?

 

By the way, I strongly suspect that the IntersectWith, vlax-curve-getParamAtPoint or vlax-curve-getPointAtParam functions used with circles work with trigonometric calculations.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 34 of 43

calderg1000
Mentor
Mentor

Gil thank you for your answer.
I mean that any procedure that obeys a mathematical calculation due to the application of trigonometric formulas have a degree of precision that is limited by the rounding factor. Which should be much less by applying graphic geometric procedures. My solution proposal is for a totally graphic methodology, only that I have ignored the use of the compass and the protractor. Using the functions that allow me to find parameters
and interceptions. Well after all it is true I have been able to verify that the margins of error are negligible...


Carlos Calderon G
EESignature
>Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

0 Likes
Message 35 of 43

_gile
Consultant
Consultant

@calderg1000 

I don't think there is any specific inaccuracy due to the use of trigonometric functions (and, again, they are certainly used internally for geometric calculations with circles).
There is an unavoidable inaccuracy with real numbers in computer science that should be well known to anyone who does programming.

This inaccuracy is due to the binary coding of floating-point numbers. AutoCAD uses the double-precision floating-point format that provides accuracy down to 15 or 16 significant digits.
You can try the following simple non trigonometric operation (a substraction);

 

_$ (= 0.1 (- 1.1 1.0))
nil

 

Why ? We can see these values with 16 digits using the rtos function:

 

_$ (rtos 0.1 2 16)
"0.1000000000000000"
_$ (rtos (- 1.1 1.0) 2 16)
"0.1000000000000001"

 

Also with constant PI, the precision does not exceed 15 decimal places:

 

_$ (rtos pi 2 16)
"3.141592653589793"
_$ (rtos pi 2 17)
"3.141592653589793"
_$ (rtos pi 2 18)
"3.141592653589793"

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 36 of 43

_gile
Consultant
Consultant

@calderg1000  a écrit :

My solution proposal is for a totally graphic methodology,


Your algorithm is indeed the transcription of a graphical method, but it uses functions like IntersectWith or vlax-curve* functions that necessarily use analytic geometry.
A purely graphical method on paper has a precision of the order of the thickness of the pencil line, the equivalent in computer science would have a precision of the order of the pixel.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 37 of 43

john.uhden
Mentor
Mentor

@_gile 

I might agree, but I think that rtos doesn't work past 16 places.

Unfortunately we have no other way of seeing so many places.

John F. Uhden

0 Likes
Message 38 of 43

_gile
Consultant
Consultant

@john.uhden  a écrit :

@_gile 

I might agree, but I think that rtos doesn't work past 16 places.

Unfortunately we have no other way of seeing so many places.


rtos doesn't work past 16 places because the double-precision floating-point format only provides accuracy down to 15 or 16 significant digits.

You can try something like this to see that the 17th decimal place is no longer reliable:

 

_$ (= 0.123456789012345678901234 0.12345678901234568)
T

 

And when we talk about "significant digits" it also takes into account the digits before the decimals.

 

_$ (= 1234567890.1234567890123456 1234567890.1234567)
T
_$ (= 12345678901234567.999999999 12345678901234567)
T

 

As does rtos:

 

_$ (rtos 1234567890.123456789012345678901234 2 24)
"1234567890.123457"

 

This is a phenomenon often encountered by those who work with coordinates far from the origin.

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 39 of 43

john.uhden
Mentor
Mentor

@_gile 

I deal with state plane coordinate systems all the time, eg. 600000,400000 (US feet).

But since we can't measure that closely we typically limit our published analyses to only 4 places after the decimal.

And we always close our polyline areas lest there be a slight difference between the start and the end.

It's funny how we report areas to the nearest square foot but also in acres (43,560 s.f.) to only 3 places.

John F. Uhden

0 Likes
Message 40 of 43

Kent1Cooper
Consultant
Consultant

@john.uhden wrote:

.... I think that rtos doesn't work past 16 places. ....


Be careful of the distinction between decimal places and significant figures.  The (rtos) function can handle more than 16 decimal places, if the figures immediately following the decimal point are zeroes, which are not significant if they precede the first significant figures starting later:

 

Command: (rtos (/ pi 1000) 2 18)
"0.003141592653589793"

 

18 decimal places, 16 sig-figs.

 

And extending that....

 

Command: (rtos (/ pi 100000) 2 20)
"0.00003141592653589793"

 

20 decimal places, 16 sig-figs.

 

Conversely, it can't handle as many as 16 decimal places if some of its allowance of 16 sig-figs is eaten up before the decimal point, as demonstrated in Message 18.

Kent Cooper, AIA