Had some time Trogg, here is how I might approach it.
I've annotated the code as much as possible, and slightly simplified my approach (so intrinsic properties [layer/linetype etc] of the original circles are overlooked for sake of brevity).
Hopefully my explanations are somewhat clear - if there is anything you don't understand, just ask.
Lee
(defun [color=red]c:ms[/color] ( / *error* _Arc _Line _Dxf e1 e2 c1 c2 r1 r2 di dr an tan ang )
[color=green] ;; ------------------------------------------------------- ;;
;; Example Courtesy of Lee Mac 2010 of www.lee-mac.com ;;
;; ;;
;; Simple enough code, but oh well: © Lee Mac 2010 ;;
;; ------------------------------------------------------- ;;
;; Define function, localise variables and
;; local subfunction.[/color]
(defun [color=red]*error*[/color] ( msg )
[color=green] ;; Error Function not really needed, as
;; nothing really needs resetting should the
;; user hit Esc. But we'll add it to demonstrate
;; how to use an error handler, and to suppress the
;; error message when the user hits Esc.[/color]
(or (wcmatch (strcase msg) "*BREAK,*CANCEL*,*EXIT*")
(princ (strcat "\n** Error: " msg " **")))
(princ)
)
(defun [color=red]_Arc[/color] ( centre radius start end )
[color=green] ;; Subfunction to create an Arc
;; (I use the underscore for no other purpose
;; than to indicate that its a subfunction)
;; Arguments:
;; centre - centre of the Arc
;; radius - Arc Radius
;; start - Arc Start Angle
;; end - Arc End Angle[/color]
(entmakex
[color=green] ;; Using entmake*x* to return the entity name
;; instead of the DXF data list.[/color]
(list
(cons 0 "ARC")
(cons 10 centre)
(cons 40 radius)
(cons 50 start)
(cons 51 end)
)
)
[color=green] ;; entmakex function statement is the last
;; expression in the function definition so
;; the return of entmakex (the entity name)
;; will be returned by the subfunction.[/color]
)
(defun [color=red]_Line[/color] ( start end )
[color=green] ;; Subfunction to create a Line
;; Arguments:
;; start - Start Point of the Line
;; end - End Point of the Line[/color]
(entmakex
(list
(cons 0 "LINE")
(cons 10 start)
(cons 11 end)
)
)
)
(defun [color=red]_dxf[/color] ( code entity ) (cdr (assoc code (entget entity))))
[color=green] ;; ^^ Handy little subfunction for returning DXF data[/color]
[color=green]
;; Subs defined, start Main Program...[/color]
(if
[color=green]
;; IF the following expression is *non-nil*
;; (doesn't have to be explicitely TRUE)[/color]
(and
[color=green] ;; All the following expression need to be non-nil
;; for AND to return True[/color]
(setq e1 (car (entsel "\nSelect First Circle: ")))
(eq "CIRCLE" (_dxf 0 e1)) [color=green];; Check that the user has indeed picked a Circle[/color]
(setq e2 (car (entsel "\nSelect Second Circle: ")))
(eq "CIRCLE" (_dxf 0 e2)) [color=green];; Well yes, they could be that stupid..[/color]
) [color=green];; End AND[/color]
(progn
[color=green] ;; Use the progn function to effectively 'wrap'
;; the following expressions so that the SINGLE
;; progn expression may be fed to the IF function.
;; The IF function will only take three arguments:
;; Test expression, then expression, else expression.
;; Hence if we want to evaluate more than one expression
;; for each case, we can use the progn function which will
;; merely evaluate all expressions fed to it, but can
;; be handed to the IF function as a single expression in itself.[/color]
(setq c1 (_dxf 10 e1) c2 (_dxf 10 e2)) [color=green];; Centers of both Circles.[/color]
(setq r1 (_dxf 40 e1) r2 (_dxf 40 e2)) [color=green];; Radii of both Circles.[/color]
(setq di (distance c1 c2) [color=green];; Distance between centers[/color]
dr (- r1 r2) [color=green] ;; Difference in the Radii[/color]
an (angle c1 c2) [color=green] ;; Angle of the centers[/color]
)
(if (< (abs dr) di)
[color=green] ;; If the absolute value of the difference in the
;; radii is less than the distance between the circle
;; centers (else one circle is inside the other)[/color]
(progn
[color=green] ;; ^^ There it is again....[/color]
(setq tan (sqrt (- (* di di) (* dr dr))))
[color=green] ;; A touch of Pythagoras to get the tangent length[/color]
(setq ang (atan tan dr))
[color=green]
;; Trig to get the angle of the normal, measured from
;; the line connecting the two centers.[/color]
(_Line
(polar c1 (+ an ang) r1)
(polar c2 (+ an ang) r2)
)
[color=green] ;; Draw the first tangent line...[/color]
(_Line
(polar c1 (- an ang) r1)
(polar c2 (- an ang) r2)
)
[color=green]
;; ...And the second.
;; Now for the Arcs![/color]
(_Arc c1 r1 (+ an ang) (- an ang))
(_Arc c2 r2 (- an ang) (+ an ang))
[color=green] ;; Finally, delete the original Circles:[/color]
(mapcar 'entdel (list e1 e2))
[color=green] ;; OK, so mapcar wasn't really needed, I could've
;; used:
;;
;; (entdel e1)
;; (entdel e2)
;;
;; But it gives an opportunity for a novice to easily see how
;; mapcar works: applying a supplied function to every item in a supplied
;; list.[/color]
) [color=green];; End PROGN[/color]
(princ "\n** Unable to Construct Tangents **")
[color=green]
;; Warn the user they have attempted the impossible...[/color]
) [color=green];; End IF[/color]
) [color=green];; End PROGN[/color]
(princ "\n*Cancel*")
[color=green] ;; User hasn't selected two Circles[/color]
) [color=green];; End IF[/color]
(princ) [color=green];; Shhh![/color]
) [color=green];; End DEFUN[/color]