program tangents;
uses
Math,
graph;
type
v2 = object
public
x: double;
y: double;
end;
type
Circle = object
ctr: v2;
r: integer;
col: integer;
end;
type
linesegment = object
s, f: v2;
end;
const
same = 0;
cross = 1;
near = 2;
far = 3;
function dot(v1: v2; v3: v2): double;
var
d1, d2, v1x, v1y, v2x, v2y: double;
begin
d1 := Sqrt(v1.x * v1.x + v1.y * v1.y);
d2 := Sqrt(v3.x * v3.x + v3.y * v3.y);
v1x := v1.x / d1;
v1y := v1.y / d1;
v2x := v3.x / d2;
v2y := v3.y / d2;
exit(v1x * v2x + v1y * v2y);
end;
function drawline(x: double; y: double; angle: double; length: double): v2;
var
z: v2;
begin
angle := angle * 0.0174532925199433; //=4*atn(1)/180
z.x := x + length * Cos(angle);
z.y := y - length * Sin(angle);
exit(z);
end;
function isleft(L: linesegment; p: v2): integer;
begin
exit(-Sign((L.s.x - L.f.x) * (p.y - L.f.y) - (p.x - L.f.x) * (L.s.y - L.f.y)));
end;
function segmentintersections(L1: linesegment; L2: linesegment): integer;
begin
if isleft(L2, L1.s) = isleft(L2, L1.f) then
exit(0);
if isleft(L1, L2.s) = isleft(L1, L2.f) then
exit(0);
exit(1);
end;
function distance(a: v2; b: v2): double;
begin
exit(sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
end;
function tangent(C: circle; D: circle; flag: integer; position: integer; ref: v2): linesegment;
var
z: array[0 .. 8] of linesegment;
n, idx, a, b, i: integer;
dst, dd: double;
X, Y, Y2, v, v3: v2;
CL, VL: linesegment;
label
lbl;
begin
n := 0;
idx := 0;
i := 0;
dd := 0.0;
dst := 0.0;
if position = near then
dst := 5000.0
else
dst := -5000.0;
for a := 0 to 360 do
begin
for b := 0 to 360 do
begin
v := drawline(C.ctr.x, C.ctr.y, a, C.r);
v3 := drawline(D.ctr.x, D.ctr.y, b, D.r);
X.x := (v.x - v3.x);
X.y := (v.y - v3.y);
Y.x := (c.ctr.x - v.x);
Y.y := (c.ctr.y - v.y);
Y2.x := (D.ctr.x - v3.x);
Y2.y := (D.ctr.y - v3.y);
if (Abs(dot(Y, X)) < 0.01) and (Abs(dot(Y2, X)) < 0.01) then
begin
CL.s := C.ctr;
CL.f := D.ctr;
VL.s := v;
VL.f := v3;
I := segmentintersections(CL, VL);
if I = flag then
begin
dd := distance(v, ref);
if (position = far) and (dst < dd) then
begin
dst := dd;
idx := n;
end;
if (position = near) and (dst >= dd) then
begin
dst := dd;
idx := n;
end;
z[n].s := v;
z[n].f := v3;
n += 1;
goto lbl;
end;//i
end; //abs
end; // a
lbl: ;
end; // b
exit(z[idx]);
end;//function
var
s: linesegment;
gd, gm: smallint;
c: array of circle;
z, rad, Count, n, nflag, cflag: integer;
ref, d: v2; //reference point as screen centre.
begin
{========== set up graph =========}
gd := D8bit;
gm := m800x600;
InitGraph(gd, gm, '');
if GraphResult <> grok then
halt;
SetTextStyle(SansSerifFont, HorizDir, 2);
{===================================}
ref.x := 400;
ref.y := 300;
Count := 0;
cflag := cross;
nflag := far;
// set up some random circles around the centre
for z := 0 to 360 do
if z mod 30 = 0 then
begin
setlength(c, Count + 1);
rad := 170 + random(120);
d := drawline(ref.x, ref.y, z, rad);
c[Count].ctr.x := d.x;
c[Count].ctr.y := d.y;
c[Count].r := 30;
c[Count].col := Count + 1;
Count += 1;
end;//for z
for n := 0 to high(c) - 1 do // draw the circles
begin
setfillstyle(solidfill, c[n].col);
fillellipse(trunc(c[n].ctr.x), trunc(c[n].ctr.y), c[n].r, c[n].r);
end;
for n := 0 to high(c) - 1 - 1 do // tangents all the way round
begin
if (n mod 2 = 1) then
begin
nflag := far;
cflag := cross;
end;
if (n mod 2 = 0) then
nflag := near;
s := tangent(c[n], c[n + 1], cflag, nflag, ref);
line(trunc(s.s.x), trunc(s.s.y), trunc(s.f.x), trunc(s.f.y));
end;
s := tangent(c[n + 1], c[0], cross, far, ref); //last pair
line(trunc(s.s.x), trunc(s.s.y), trunc(s.f.x), trunc(s.f.y));
writeln('DONE . . . please press return');
readln;
closegraph;
end.