Доброго времени.
Пишу в тетрадке карандашом. Стираю. Снова пишу, пока насквозь не протрётся.
Положу сюда суть геометрического метода, т.к. пора стирать эту страницу.
Чтоб не пропало.
Ну и алгоритм, ежели кто восхочет что-нибудь состряпать.
/// <summary>
/// Вычисляет градусы квадрата 9
/// </summary>
/// <param name="size">размер квадрата включая центральный элемент</param>
/// <returns></returns>
public static double[] CalcSQ9Degrees(int size)
{
int i = 1;//текущее число, в центре 1, если нужно смещение, вычисляйте отдельно
int w = 2 * size - 1;
// градусы. числа нам здесь не нужны, т.к. они соответствуют индексу
double[] degrees = new double[w * w];
// координаты ячейки
int x = size - 1;
int y = size - 1;
// размер текущего квадрата
// каждый последующий больше на 2
int curWidth = 2;
// центр
degrees[i - 1] = 0;
i++;
x--;
// номер квадрата, он же равен высоте треугольника с вершиной в центре
int nSquare = 0;
// для расчёта используем теоремы синусов, косинусов и Пифагора
// например, для левого треугольника (0-315-45):
//
// угол alpha - всегда 45 (верхний)
// угол beta - угол между вертикалью и прямой из центра квадрата к искомой ячейке
// угол gamma - угол в центре
// противоположные им стороны соответственно: a, b, c
for (; ; )
{
if (x < 0)
break;
nSquare++;
// вверх с левой нижней
// на 45, 135, 225 мы поворачиваем,
// а после 315 отсчитывается ещё одна ячейка влево
// это для подсчёта ячеек
double wx = curWidth;
double a; // угол
double c; // сторона c треугольника, уменьшается на каждой ячейке
// вынесем общие
double sin_45 = Math.Sin(45 * Math.PI / 180);
double cos_45 = Math.Cos(45 * Math.PI / 180);
double b = Math.Sqrt(2 * nSquare * nSquare); // сторона b
for (int j = 0; j < curWidth; j++, i++)
{
// первая вылезающая в следующий квадрат ячейка:
// 2, 10, 26, 51, 84, ...
wx--;
c = wx;
a = (c * sin_45) / (Math.Sqrt(b * b + c * c - 2 * b * c * cos_45));
// переводим в градусы
a = Math.Asin(a) * 180 / Math.PI;
// это левый треугольник (0-315-45)
a = (315 + 90 - a) % 360;
degrees[i - 1] = a;
y--;
}
y++;
x++;
wx = curWidth;
// вправо
for (int j = 0; j < curWidth; j++, i++)
{
wx--;
c = wx;
a = (c * sin_45) / (Math.Sqrt(b * b + c * c - 2 * b * c * cos_45));
a = Math.Asin(a) * 180 / Math.PI;
a = (45 + 90 - a) % 360;
degrees[i - 1] = a;
x++;
}
x--;
y++;
wx = curWidth;
// вниз
for (int j = 0; j < curWidth; j++, i++)
{
wx--;
c = wx;
a = (c * sin_45) / (Math.Sqrt(b * b + c * c - 2 * b * c * cos_45));
a = Math.Asin(a) * 180 / Math.PI;
a = (135 + 90 - a) % 360;
degrees[i - 1] = a;
y++;
}
y--;
x--;
wx = curWidth;
// влево
for (int j = 0; j < curWidth; j++, i++)
{
wx--;
c = wx;
a = (c * sin_45) / (Math.Sqrt(b * b + c * c - 2 * b * c * cos_45));
a = Math.Asin(a) * 180 / Math.PI;
a = (225 + 90 - a) % 360;
degrees[i - 1] = a;
x--;
}
curWidth += 2;
}
return degrees;
}
Для автовыделения квадрата любого размера "по требованию", если предыдущий используемый квадрат окажется маловат:
/// <summary>
/// Вычисляет градус указанного числа на квадрате 9 геометрическим методом.
/// Числа квадрата начинаются с nFirst в центре.
/// Если нужны другие числа, преобразуйте их отдельно
/// </summary>
/// <param name="value">искомое число</param>
/// <param name="degrees">массив вычисленных градусов</param>
/// <param name="nFirst">число в центре квадрата</param>
/// <returns>градус числа <paramref name="value"/></returns>
public static double CalcSQ9Degree(double value, ref double[] degrees, int nFirst = 1)
{
if (value <= 0)
return 0;
// отрежем целую часть
int lowValue = (int)value;
int highValue = lowValue + 1;
// найдём градусы
int n = lowValue - nFirst;
if (degrees == null || degrees.Length == 0 || n + 1 >= degrees.Length)
{
int size = 9;
do
{
size++;
} while (n + 1 >= (2 * size - 1) * (2 * size - 1));
degrees = CalcSQ9Degrees(size);
}
double degreeLow = degrees[n];
if (lowValue == value)
return degreeLow;
// есть дробная часть
double degreeHigh = degrees[n + 1];
double delta = degreeHigh - degreeLow;
if (delta < 0) // проход через 0
delta += 360;
// добавим дробную часть и вернём
return degreeLow + (value - lowValue) * delta;
}