Думаю, проще всего делать не спираль Архимеда, а её приближение методом Эйлера. Пусть у нас есть некое расстояние dSafe, на котором должны быть кружочки друг от друга.
Чтобы поставить второй кружочек, отойдём от центрального на вектор (dSafe, 0). Для третьего и дальше поступим так.
Найдём угол касательной к спирали Архимеда. Касательный вектор будет a единиц по радиус-вектору (a — коэффициент спирали r=aф) и r единиц по нормали. Таким образом, если мы в точке (x, y), у нас получается касательный вектор вот такой.
t = (a·x + r·y; a·y − r·x), r = sqrt(x² + y²)
Нормируем этот вектор до длины dSafe и добавляем к (x, y).
Штука номер один. Параметр спирали прямо пропорционален безопасному расстоянию (вы это увидите ниже в коде). И штука номер два — только «бесконечно малые» шаги (при нулевом a, т. е. окружность) будут держать нас на окружности, шаги побольше будут выносить нас наружу, для компенсации сделаем a отрицательным.
Вот действующий код (на C++ Builder’е)
namespace {
const double dSafe = 30;
const int rSafe = dSafe / 2;
const double a = -0.3 * dSafe;
int toPixel(double x)
{
return floor(x + 0.5) + 200;
}
}
void TfmMain::drawPoint(double x, double y)
{
int xx = toPixel(x);
int yy = toPixel(y);
Canvas->Ellipse(xx - rSafe, yy - rSafe, xx + rSafe, yy + rSafe);
}
void __fastcall TfmMain::FormPaint(TObject *Sender)
{
Canvas->Brush->Style = bsClear;
Canvas->Pen->Color = clBlack;
double x = 0, y = 0;
drawPoint(x, y);
x += dSafe;
drawPoint(x, y);
for (int i = 0; i < 120; ++i) {
double r = sqrt(x*x + y*y);
double tx = a*x + r*y;
double ty = a*y - r*x;
double tLen = sqrt(tx*tx + ty*ty);
double k = dSafe / tLen;
x += tx * k;
y += ty * k;
drawPoint(x, y);
}
}