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.