Programming Challenge 11/22

Programming Challenge 11/22

john.uhden
Mentor Mentor
2,390 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,391 Views
42 Replies
Replies (42)
Message 2 of 43

Sea-Haven
Mentor
Mentor

You forgot to mention there are 2 solutions minimum a simple left and right circles has a solution of a top or bottom line, never mind that you could end up with the bow tie answer.

 

SeaHaven_0-1671863830866.png

 

0 Likes
Message 3 of 43

yangguoshe
Advocate
Advocate

 Code of highflybird

http://bbs.mjtd.com/forum.php?mod=viewthread&tid=100983&highlight=%D4%BD%B7%C9%D4%BD%B8%DF%BD%B2%CC%...

;;;----------------------------------------------------; ;;;两个圆的公切线 ; ;;;输入: 给定两个圆的圆心和半径 ; ;;;(setq ret (cons (list p q) ret)) ) ( (> l 0) (setq u (atan (sqrt l) r)) (setq v (- a u)) (setq u (+ a u)) (setq p (polar C1 u r1)) (setq q (polar C1 v r1)) (if (eq r b) (setq m (polar c2 u r2) ;外公切线 n (polar c2 v r2) ) (setq m (polar c2 u (- r2)) ;内公切线 n (polar c2 v (- r2)) ) ) (setq ret (cons (list p m) ret)) (setq ret (cons (list q n) ret)) ) ) ) Ret ) ) )

0 Likes
Message 4 of 43

yangguoshe
Advocate
Advocate
;;;两个圆的公切线                                      ;
;;;输入: 给定两个圆的圆心和半径                        ;
;;;输出: 这两个圆的公切线(nil,一个点或者点对集合)     ;
;;;----------------------------------------------------;
(defun CIR:Common_Tangent (c1 r1 c2 r2 / a b d eps p q m n u v ret L)
  (setq d (distance c1 c2))
  (setq a (angle c1 c2))
  (setq eps 1e-8)
  (cond
    ( (equal d 0 eps) nil)                                ;圆心重合,无解或者无穷解
    ( (equal d (abs (setq b (- r1 r2))) eps)                 ;内切
      (setq p (polar c1 a (Math:Sign_reversal r1 b)))         ;切点
      (list (list p (polar p (+ a (* pi 0.5)) 1000)))         ;内切线
    )
    ( (> d b)
      (foreach r (list b (+ r1 r2))                        ;考虑内外公切两种情况
        (setq l (* (+ d r) (- d r)))
        (cond
          ( (equal l 0 eps)                                ;两个圆外切
            (setq p (polar c1 a r1))
            (setq q (polar p (+ a (* pi 0.5)) 1000))   
            (setq ret (cons (list p q) ret))
          )
          ( (> l 0)
            (setq u (atan (sqrt l) r))
            (setq v (- a u))
            (setq u (+ a u))
            (setq p (polar C1 u r1))
            (setq q (polar C1 v r1))
            (if (eq r b)                   
              (setq m (polar c2 u r2)                         ;外公切线
                    n (polar c2 v r2)
              )
              (setq m (polar c2 u (- r2))                 ;内公切线
                    n (polar c2 v (- r2))
              )
            )
            (setq ret (cons (list p m) ret))             
            (setq ret (cons (list q n) ret))
          )
        )
      )
      Ret
    )
  )
  ;外公切线
 (entmake (list '(0 . "line") (cons 10 (car(cadddr ret))) (cons 11 (cadr(cadddr ret))) ))
 (entmake (list '(0 . "line") (cons 10 (car(caddr ret))) (cons 11 (cadr(caddr ret))) ))
 ;内公切线
 (entmake (list '(0 . "line") (cons 10 (car(car ret))) (cons 11 (cadr(car ret))) ))
 (entmake (list '(0 . "line") (cons 10 (car(cadr ret))) (cons 11 (cadr(cadr ret))) ))
)
0 Likes
Message 5 of 43

_gile
Consultant
Consultant

Hi,

My 2 cents.

(defun challenge11 (circle1 circle2 / c1 c2 r1 r2 a1 b c d a2)
  (setq	c1 (getpropertyvalue circle1 "Center")
	r1 (getpropertyvalue circle1 "Radius")
	c2 (getpropertyvalue circle2 "Center")
	r2 (getpropertyvalue circle2 "Radius")
	a1  (angle c1 c2)
	b  (- r1 r2)
	c  (distance c1 c2)
	d  (sqrt (- (* c c) (* b b)))
	a2 (atan d b)
  )
  (list
    ;; first tangent
    (entmakex
      (list (cons 0 "LINE")
	    (cons 10 (polar c1 (+ a1 a2) r1))
	    (cons 11 (polar c2 (+ a1 a2) r2))
      )
    )
    ;; second tangent
    (entmakex
      (list (cons 0 "LINE")
	    (cons 10 (polar c1 (- a1 a2) r1))
	    (cons 11 (polar c2 (- a1 a2) r2))
      )
    )
  )
)

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 43

Sea-Haven
Mentor
Mentor

Wow Gile you make it look so easy, this is for us Bricscad users as there is no getproperty.

 

(defun challenge11 (circle1 circle2 / c1 c2 r1 r2 a1 b c d a2)
(setq circ1 (entget (car circle1))
 circ2 (entget (car circle2)))
  (setq	c1 (cdr (assoc 10 circ1 ))
	r1 (cdr (assoc 40 circ1 ))
	c2 (cdr (assoc 10 circ2 ))
	r2 (cdr (assoc 40 circ2))
	a1  (angle c1 c2)
	b  (- r1 r2)
	c  (distance c1 c2)
	d  (sqrt (- (* c c) (* b b)))
	a2 (atan d b)
  )
  (list
; first tangent
    (entmakex
      (list (cons 0 "LINE")
	    (cons 10 (polar c1 (+ a1 a2) r1))
	    (cons 11 (polar c2 (+ a1 a2) r2))
      )
    )
; second tangent
    (entmakex
      (list (cons 0 "LINE")
	    (cons 10 (polar c1 (- a1 a2) r1))
	    (cons 11 (polar c2 (- a1 a2) r2))
      )
    )
  )
)
(challenge11 (entsel "\nPick 1st circle") (entsel "\nPick 2nd circle"))
Message 7 of 43

john.uhden
Mentor
Mentor

@Sea-Haven 

I didn't forget to mention.   I didn't mention on purpose.

John F. Uhden

0 Likes
Message 8 of 43

_gile
Consultant
Consultant

The same as in reply #5 rewritten in a more DRY (Don't Repeat Yourself) style.

;; 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 . startPt) (11 . endPt)))
(defun assign (ename codeSymList / l)
  (setq l (entget ename))
  (mapcar
    '(lambda (x) (set (cdr x) (cdr (assoc (car x) l))))
    codeSymList
  )
)

(defun challenge11 (circle1 circle2 / c1 r1 c2 r2 a1 b c d a2)
  (assign circle1 '((10 . c1) (40 . r1)))
  (assign circle2 '((10 . c2) (40 . r2)))
  (setq	a1 (angle c1 c2)
	b  (- r1 r2)
	c  (distance c1 c2)
	d  (sqrt (- (* c c) (* b b)))
	a2 (atan d b)
  )
  (mapcar
    '(lambda (a)
       (entmakex
	 (list (cons 0 "LINE")
	       (cons 10 (polar c1 a r1))
	       (cons 11 (polar c2 a r2))
	 )
       )
     )
    (list (+ a1 a2) (- a1 a2))
  )
)


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 9 of 43

_gile
Consultant
Consultant

@john.uhden  a écrit :

If there is a shorter line vs. a longer line, we want the shorter one.


I forgot that. This version draws the "inside" tangents.

(defun assign (ename codeSymList / l)
  (setq l (entget ename))
  (mapcar
    '(lambda (x) (set (cdr x) (cdr (assoc (car x) l))))
    codeSymList
  )
)

(defun insideTangents (circle1 circle2 / c1 r1 c2 r2 a1 b c d a2)
  (assign circle1 '((10 . c1) (40 . r1)))
  (assign circle2 '((10 . c2) (40 . r2)))
  (setq	a1 (angle c1 c2)
	b  (/ r1 r2)
	c  (/ (* (distance c1 c2) b) (+ 1 b))
	d  (sqrt (- (* c c) (* r1 r1)))
	a2 (atan d r1)
  )
  (mapcar
    '(lambda (a)
       (entmakex
	 (list (cons 0 "LINE")
	       (cons 10 (polar c1 a r1))
	       (cons 11 (polar c2 (+ a pi) r2))
	 )
       )
     )
    (list (+ a1 a2) (- a1 a2))
  )
)


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 10 of 43

phanaem
Collaborator
Collaborator

Hi Gile

You could just change (- r1 r2) to (+ r1 r2) in your first code and the angle would be correct.

Here is my version, very similar to yours

 

(defun tan2circ (e1 e2 / c1 r1 c2 r2 a b x)
  (mapcar 'set '(c1 r1) (circle_def e1))
  (mapcar 'set '(c2 r2) (circle_def e2))
  (setq a (angle c1 c2)
        x (/ (+ r1 r2) (distance c1 c2))
        b (atan (sqrt (- 1 (* x x))) x)
  )  
  (mapcar
   '(lambda (f)
      (entmakex
        (list
          '(0 . "LINE")
          (cons 10 (polar c1 (f a b) r1))
          (cons 11 (polar c2 (f a b) (- r2)))
        )
      )
    )
    (list + -)
  )
)

(defun circle_def (e)
  (setq e (entget e))
  (list
    (cdr (assoc 10 e))
    (cdr (assoc 40 e))
  )
)

 

 

Message 11 of 43

_gile
Consultant
Consultant

@phanaem  a écrit :

Hi Gile

You could just change (- r1 r2) to (+ r1 r2) in your first code and the angle would be correct.


Well noted.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 12 of 43

calderg1000
Mentor
Mentor

Regards @john.uhden 

Here my focus...

(defun c:tanC (/ spm smj smin p1 r1 p2 r2 pmid d scn1 scn2 lpt px1 prx1 px2 prx2
               pt1 pt2 pt3 pt4
              )
  (setq
    spm (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object)))
  )
  (setq smj  (car (entsel "Select Greater Circle: "))
        smin (car (entsel "Select smaller circle: "))
        p1   (cdr (assoc 10 (entget smj)))
        r1   (cdr (assoc 40 (entget smj)))
        p2   (cdr (assoc 10 (entget smin)))
        r2   (cdr (assoc 40 (entget smin)))
        pmid (mapcar '* (mapcar '+ p1 p2) '(0.5 0.5 0.))
        d    (distance p1 p2)
  )
                                                  ;circles aux.    
  (setq scn1 (vla-addcircle spm (vlax-3d-point pmid) (* d 0.5))
        scn2 (vla-addcircle spm (vlax-3d-point p1) (- r1 r2))
  )
  (setq lpt (vlax-safearray->list
              (vlax-variant-value (vla-intersectwith scn1 scn2 acextendnone))
            )
  )                                               ;List intersecciones
  (setq px1  (list (nth 0 lpt) (nth 1 lpt) (nth 2 lpt))
        prx1 (vlax-curve-getparamatpoint scn2 px1)
        px2  (list (nth 3 lpt) (nth 4 lpt) (nth 5 lpt))
        prx2 (vlax-curve-getparamatpoint scn2 px2)
        pt1  (vlax-curve-getpointatparam smj prx1)
        pt2  (vlax-curve-getpointatparam smin prx1)
  )
  (vla-addline spm (vlax-3d-point pt1) (vlax-3d-point pt2))
  (setq pt3 (vlax-curve-getpointatparam smj prx2)
        pt4 (vlax-curve-getpointatparam smin prx2)
  )
  (vla-addline spm (vlax-3d-point pt3) (vlax-3d-point pt4))
  (vla-erase scn1)
  (vla-erase scn2)
)

 


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 13 of 43

yangguoshe
Advocate
Advocate
; Function: Draw the common tangent of two separated circles
; yangguoshe 2022.12.27

(defun c:tt(/ A1 A2 A22 B BB C C1 C2 CIR1 CIR2 D DD R1 R2)
 (setq  cir1(car(entsel "\n Click the first circle")) 
        cir2(car(entsel "\n Click the second circle")) 
        c1(trans(cdr(assoc 10(entget cir1)))cir1 0) 
        r1(cdr(assoc 40(entget cir1))) 
        c2(trans(cdr(assoc 10(entget cir2)))cir2 0) 
        r2(cdr(assoc 40(entget cir2))) 
	a1 (angle c1 c2)
	b  (- r1 r2)
	bb  (+ r1 r2)
	c  (distance c1 c2)
	d  (sqrt (abs(- (* c c) (* b b))) )
    	a2 (atan d b)
        dd (sqrt (- (* c c) (* bb bb))) 
        a22 (atan dd bb))
 (DEFUN F(a xs xs2)
  (entmakex(list(cons 0 "LINE")(cons 10(polar c1(+ a1(* XS A)) r1))(cons 11(polar c2(+ XS2 a1 (* XS A))r2)))))
  (f a22 1 pi) 
  (f a22 -1 pi)
  (f a2 1 0)
  (f a2 -1 0)
)
0 Likes
Message 14 of 43

john.uhden
Mentor
Mentor

@calderg1000 

You should focus on the directions...

Create a function that takes two (2) arguments, the enames of each of the circles.

John F. Uhden

0 Likes
Message 15 of 43

john.uhden
Mentor
Mentor

@_gile 

I was about to bust you for omitting the crossing tangents, but you saved yourself.

Nice work!  But you didn't demonstrate which one(s) is/are the shortest.

John F. Uhden

0 Likes
Message 16 of 43

_gile
Consultant
Consultant

@john.uhden  a écrit :

@_gile 

But you didn't demonstrate which one(s) is/are the shortest.


The crossing ones are always shortest.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 17 of 43

john.uhden
Mentor
Mentor

@Anonymous 

This may not be any better than anyone else's, but anyway:
(BTW, I think @_gile is right...  The lines that cross between the two circles are always the shortest.)

(defun @acos (x) (atan (sqrt (- 1 (* x x))) x)))
(defun @tangent (e1 e2 / rp1 r1 rp2 r2 d d1 d2 a p1 p2)
  (setq rp1 (cdr (assoc 10 (entget e1)))
        r1  (cdr (assoc 40 (entget e1)))
        rp2 (cdr (assoc 10 (entget e2)))
        r2  (cdr (assoc 40 (entget e2)))
         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)))
)

I coulda condensed this somewhat, but I wanted to make it clear for both myself and others.

 

 

 

John F. Uhden

Message 18 of 43

_gile
Consultant
Consultant

@john.uhden  a écrit :

@Anonymous 

BTW, I think @_gile is right...  The lines that cross between the two circles are always the shortest.


Yes, because r1+r2 is always greater than r1-r2.

_gile_0-1672181032967.png

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 19 of 43

john.uhden
Mentor
Mentor

@_gile 

You caught me at first (just reading my e-mail), but your graphic presentation makes it crystal clear.  Thanks!

John F. Uhden

0 Likes
Message 20 of 43

john.uhden
Mentor
Mentor

@Sea-Haven 

Alan, are those the shortest tangents?

John F. Uhden

0 Likes