Тридцать шесть градусов красоты
Сеточные системы координат, в которых плоскость делится на одинаковые симметричные элементы — на квадраты, треугольники, шестиугольники, достаточно известны. Им соответствуют квадратная, треугольная, шестиугольная симметрия. Но еще существует симметрия десятиугольная.
В ней плоскость не делится на десятиугольники, вместо этого все линии расположены под углами кратными 36°. Координаты в этой системе можно записывать целыми числами, по два целых числа на горизонтальное и вертикальное направление.
![gfxz73dxe0-owjktjad1d-ohf3o.png](https://habrastorage.org/webt/gf/xz/73/gfxz73dxe0-owjktjad1d-ohf3o.png)
Расскажу как это нарисовать.
Количество единичных векторов по всем направлениям в этой системе отсчета десять. Если учесть симметрию обратного направления, то пять. Если учесть горизонтальную симметрию, то три. Обозначим их как и выведем для них аналитические выражения.
Обычная формула расчета координат при повороте:
Двойной угол по этой формуле:
Значит
И исходя из того что существует разница координат
Получим квадратное уравнение
Которое решается
Это говорит о том, что существует как положительное так и отрицательное значение координаты , при которых различие координат двойного и одинарного угла
то же самое.
![94cknlncxymcwpihyqexxgplwxc.png](https://habrastorage.org/webt/94/ck/nl/94cknlncxymcwpihyqexxgplwxc.png)
У десятиугольника такая симметрия, что разница горизонтальной координаты между одинарным и двойным углом при увеличении углов в три раза, то есть, между тройным углом и шестикратным, сохраняет точно такую же величину. Абсолютные значения координат m и x в парном решении меняются местами и меняют свой знак, оставляя значение у разницы тем же самым. Так что, можно связать второе решение квадратного уравнения с тройным углом.
Используя что
Получим
И сразу получим остальные значения.
Число это малый коэффициент золотого сечения.
Число это большой коэффициент золотого сечения.
Их основные свойства:
Координатная система в которой координаты целые числа, и при этом можно делать повороты на 36° определяется так:
Используемые константы равны
Это позволяет представить три базовых вектора как
![m950xaltid59a4cus0d6q-cukj8.png](https://habrastorage.org/webt/m9/50/xa/m950xaltid59a4cus0d6q-cukj8.png)
При комбинации единичных векторов групповая чётность координат сохраняется, и может быть только одного из следующих типов: .
То есть, на координаты накладываются ограничения
где — целые числа.
Преобразуя таблицу умножения
* | 1 | φ | K3 | K2 |
---|---|---|---|---|
1 | 1 | φ | K3 | K2 |
φ | φ | 1-φ | K2 | K3-K2 |
K3 | K3 | K2 | 3+φ | 1+2φ |
K2 | K2 | K3-K2 | 1+2φ | 2-φ |
можно получить целочисленную трёхмерную матрицу для умножения векторов.
Используя эту координатную систему мы позиционируем точки с идеальной точностью целых чисел.
Еще немного теории о связи чисел: степень числа фи и последовательность фибоначчи.
При всей схожести формул
они, конечно, различаются.
Функция это степенная функция, которая строго больше нуля. А в последовательности фибоначчи
присутствует ноль, и из-за этого все предшествующие ему числа чередуют знак.
Для решения уравнения подходит не только
, но и
. Причем, они подходят одновременно.
. Если
и
, то коэффициенты
.
Так что, существует формула, которая связывает последовательность фибоначчи и степенную функцию числа фи (формула бине):
Для чисел с положительными номерами значение задается преимущественно левым слагаемым, и правое выравнивает до целого числа. Для чисел с отрицательным номером основной вклад идёт от правого слагаемого.
Обратная формула, получения степени числа из последовательности фибоначчи:
Для числа , которое в высоких степенях приближается к нулю, используется различие знака чисел с отрицательными номерами и примерное равенство соотношения соседних чисел самому числу
.
В системе целых координат это находит выражение в том, что базовые вектора с множителем выражаются в целых коэффициентах, взятых из последовательности фибоначчи:
На такие вектора можно и делить.
Теперь можно попробовать составить треугольники.
Треугольников в котором углы кратны 36° не так много, всего два. Сумма углов в треугольнике 180°, в долях сумма углов должна быть равна пяти. Как 5 поделить на три целых числа? Единица должна быть, потому что без нее даже минимум, три двойки — это уже перебор. Оставшиеся 4 доли можно поделить только как 1 + 3 и 2 + 2. Оба треугольника равнобедренные, имеют в себе пару одинаковых углов.
Обозначим треугольники как T1 и T2, в соответствии размера в долях того угла который повторяется.
![tq_yfmgseikxfzqdjtsy-przpii.png](https://habrastorage.org/webt/tq/_y/fm/tq_yfmgseikxfzqdjtsy-przpii.png)
Теперь можно попробовать треугольники разбить.
Треугольник T1 можно поделить на два треугольника: Т1 и Т2.
![iwpjiiry5w7rqlo0rszo7vphqpq.png](https://habrastorage.org/webt/iw/pj/ii/iwpjiiry5w7rqlo0rszo7vphqpq.png)
Треугольник T2 можно поделить на два треугольника: T1 и T2.
![5zwccooknlvrmhyuyfmdab3prmk.png](https://habrastorage.org/webt/5z/wc/co/5zwccooknlvrmhyuyfmdab3prmk.png)
Треугольник T1 можно поделить еще и на три треугольника: T1, T2, T1. Причем, такого разбиения два: симметричное и антисимметричное.
![0vddjsk5zudhgt6bbvs94c5hcxq.png](https://habrastorage.org/webt/0v/dd/js/0vddjsk5zudhgt6bbvs94c5hcxq.png)
Эти разбиения уменьшают боковые стороны треугольников на один и тот же коэффициент .
Из таких треугольников можно построить мозаику пенроуза.
Для построения можно исходить из следующих правил:
1. Для каждого уровня разбиения все треугольники имеют одинаковую длину боковых сторон, а основанием соединены с таким же треугольником, который на более детальный уровень разбивается симметрично исходному. Именно из-за этого правила мозаику пенроуза можно представлять равносторонними ромбами.
2. Для разбиения треугольника T1 используется только несимметричный вариант.
Именно поэтому мозаику пенроуза можно представлять разбиением на дельтоиды:
У получившегося T1 обязательно есть парный треугольник, который образует с исходным уголок, «дротик» (dart). А у получившегося T2 парный треугольник вместе с соседним образуют выпуклый дельтоид «воздушный змей» (kite).
![mqlmaedyoysgulvp-g5zk4dpkxa.png](https://habrastorage.org/webt/mq/lm/ae/mqlmaedyoysgulvp-g5zk4dpkxa.png)
Хотя сами по себе могут быть получены делением ромба, дельтоиды в мозаике расположены так, что никогда не соединяются в ромб. У сложной формы дельтоидов есть преимущество: ромб при одной форме может иметь два возможных направления, а у дельтоидов оно задано явно.
Если у нас соединены треугольники Т1 и Т2, то дальнейшее построение имеет два варианта: либо они вместе образуют Т2 и значит на общем уровне он имеет свое отражение. Либо Т1 и Т2 составляют вместе часть разбиения Т1, и тогда продолжение выглядит менее симметрично.
![plgbwpwibkgesn2dkg47h5mtaim.png](https://habrastorage.org/webt/pl/gb/wp/plgbwpwibkgesn2dkg47h5mtaim.png)
Поэтому мозаику пенрозуа удобнее строить не достраиванием во вне, а делением внутрь.
Относительно деления треугольники различаются не только по форме, но и по направлению симметрии. «Правый» и «Левый» треугольники режутся противоположным образом. Поэтому надо сразу выяснить, какого типа треугольники получаются при самом делении.
Мы получим правила
![jpzt4lvjr-szkbjouyhqv1qiipg.png](https://habrastorage.org/webt/jp/zt/4l/jpzt4lvjr-szkbjouyhqv1qiipg.png)
Получается, у ромбов четыре вида сторон: те которые при разбиении делятся — со сдвинутым местом разбиения вправо или влево, и те, которые не делятся, а образуют диагональ для Т1 — либо правого, либо левого. Зная какие стороны стыкуются можно раскрашивать плитки мозаики так, чтобы линии рисунка стыковались на границах, и тогда получаются симпатичные узоры.
У мозаики пенроуза несколько представлений, но как универсальный можно использовать вид из шести треугольников, четыре из которых повторяют показанное разбиение из треугольников, а два дополнительных, вида Т1, аналогичны по разбиению первому и второму, но отличаются тем, что на предыдущем уровне разбиения были частью Т1 точно такого же типа — левый из левого, правый из правого.
![sse1l8-eu9h3wusye-dj93llude.png](https://habrastorage.org/webt/ss/e1/l8/sse1l8-eu9h3wusye-dj93llude.png)
Такие треугольники можно объединять в различные виды мозаики: и в ромбы, и в дельтоиды, и в различающиеся стороной треугольники, и в фигуры HBS. А для представления Р1, для представления из прямых линий и для набора четырёхугольников нужно сопоставить двум базовым треугольникам линии разбиения.
![4omyl_gak0rqdephyxgsxio1l_8.png](https://habrastorage.org/webt/4o/my/l_/4omyl_gak0rqdephyxgsxio1l_8.png)
Список представлений:
-
Из двух треугольников (четырёх видов) с одинаковыми боковыми сторонами
-
Из двух треугольников (четырёх видов) с различными боковыми сторонами
-
Из пары ромбов, представление Р3
-
Из ромба и уголка двух видов
-
Из пары дельтоидов, представление Р2: дротик/воздушный змей
-
Представление P1: четыре вида пятиугольников, звезда, лодочка
-
Представление «шестиугольник, лодка, звезда», HBS
-
Особое представление: просто пересекающиеся прямые линии
-
Семь четырёхугольников (Семь видов четырехугольников, количество форм только шесть)
Чтобы изобразить на компьютере мозаику достаточно в текстовом редакторе написать html-страничку с кодом на javascript и открыть эту страничку в браузере.
Весь текст странички без кода:
Получаем «контекст», объектный интерфейс для рисования на плоскости:
var canvas = document.getElementById("a");
var b = canvas.getContext("2d");
Программа будет рассчитывать фигуры, а потом их отрисовывать в зависимости от заданных настроек.
Функции для упрощения команд рисования:
// начать описание контура
function begin(){b.beginPath();}
// начать линию с точки
function from(p) {b.moveTo(s[8] + p[0], s[9] - p[1]);}
// привести линию к точке
function to(p){b.lineTo(s[8] + p[0], s[9] - p[1]);}
// привести линию к начальной точке, не требуется если выполняется только заливка
function close(){b.closePath();}
// заливка
function fill(color){b.fillStyle = color; b.fill();}
// обводка контура линией
function line(){b.strokeStyle = "#444"; b.lineWidth = 0.5; b.stroke();}
function line_white(){b.strokeStyle = "#fff"; b.lineWidth = 1; b.stroke();}
function line_black(){b.strokeStyle = "#444"; b.lineWidth = 1.5; b.stroke();}
Мозаика будет состоять из фигур, они хранятся как массив данных: тип фигуры, от 0 до 5, координаты угла привязки, массив четырех целых чисел, и направление, 0 до 9. В функции отрисовки масштаб координат и размер шага рисования стороны фигуры задаются отдельно.
var s;
function prepare()
{
var sqrt = Math.sqrt;
var fi = (sqrt(5) - 1) / 2;
var fb = (sqrt(5) + 1) / 2;
var f3 = sqrt(3 + fi);
var f2 = sqrt(2 - fi);
//координаты базовых векторов для всех десяти направлений
var vt = [[ 2, 0, 0, 0], [ 1, 1, 0, 1], [ 0, 1, 1, 0], [ 0,-1, 1, 0], [-1,-1, 0, 1],
[-2, 0, 0, 0], [-1,-1, 0,-1], [ 0,-1,-1, 0], [ 0, 1,-1, 0], [ 1, 1, 0,-1]];
// Константы множители координат
var c = [1/2, fi/2, f3/2, f2/2]
// Общий массив констант
// нулевой элемент для контекста
// седьмой для дополнительных данных рисования.
// восьмой и девятый для указания центра рисования
// десятый для размера шага.
var s = [0, vt, c, fi, f3, f2, 0, 0, 0, 0];
return s;
}
s = prepare();
s[0] = b;
s[7] = 1; // доля последнего уровня
s[8] = 500/2; // сдвих координаты x
s[9] = 320/2; // сдвиг координаты y
Сначала зададим базовую фигуру из шести треугольников.
var f = []; // слои разбиения.
f[0] = [];
f[0].push([0,[ 0, 0, 0, 0],0]);
f[0].push([1,[ 0, 0, 0, 0],0]);
f[0].push([2,[ 0, 0, 0, 0],3]);
f[0].push([3,[ 0, 0, 0, 0],3]);
f[0].push([2,[ 0, 0, 0, 0],7]);
f[0].push([3,[ 0, 0, 0, 0],7]);
fi = s[3]; // берем из констант коэффициент
var levels = 3; // количество расчетных уровней
s[7] = 0.1 * 10; // степень проявления уровня
s[10] = 24 * 6 * fi * fi; // длина линии
//s[10] = 24 * 6 * fi
//s[10] = 24 * 6;
// для отображения полного поля нужно изменить размер шага, размер канвы, место центра и поменять местами p[0] и p[1] в функциях from() и to().
//s[10] = 24;
mode = 12; // режим рисования
// разбиение
var n = 0, m;
for(; n < levels; n++)
{ m = n + 1;
f[m] = [];
for(var k = 0; k < f[n].length; k++)
zd(f[n][k], s, f[m]);
}
// отображение
n = m - 1;
// предыдущий уровень
if(s[7] != 1)
for(var i = 0; i < f[n].length; i++) {paint(f[n][i], mode, 1);}
// последний уровень
for(var i = 0; i < f[m].length; i++) {paint(f[m][i], mode, 0);}
// Для 11 режима подчеркиваются линии
if(mode == 11) {d = 3; for(var i = 0; i < f[m-d].length; i++) {paint(f[m-d][i], mode, d);}}
function zd(a, s, f)
{
var t = a[0]; // тип фигуры
var vt = s[1]; // таблица векторов
if (t > 3) t = t - 4; // типы фигур 4 и 5 обрабатываются как 0 и 1
// направление первого шага в зависимости от типа фигуры, в виде смещения направления
sht = [ 1,-1, 2,-2];
var shift = sht[t];
if(t == 0) {t1 = 0; t2 = 3; t3 = 5;} // типы получившихся фигур
else if(t == 1) {t1 = 1; t2 = 2; t3 = 4;}
else if(t == 2) {t1 = 4; t2 = 2;}
else if(t == 3) {t1 = 5; t2 = 3;}
if (t < 2)
{
pos = a[1];
v1 = a[2]; // общее направление
v2 = (v1 + shift + 10) % 10; // направление первого шага
v3 = (v1 - shift + 10) % 10; // направление второго шага
v4 = (v2 + 5) % 10; // обратное направление первому
v5 = (v1 + 5) % 10; // обратное направление общему (не второму)
p1 = add(pos, vt[v2]); // позиция после первого шага
p2 = add(p1, vt[v3]); // позиция после второго шага
p3 = mul(p1,[2,2,0,0]); // масштабирование
p4 = mul(p2,[2,2,0,0]); // масштабирование
f.push([t1, p3, v4]);
f.push([t2, p3, v3]);
f.push([t3, p4, v5]);
}
else
{
pos = a[1];
v1 = a[2];
v2 = (v1 + shift + 10) % 10; // направление первого шага
v3 = (v1 - shift + 10) % 10; // направление второго шага
v4 = (v2 + 5) % 10; // обратное направление первому
v5 = (v3 + 5) % 10; // обратное направление второму
p1 = add(pos, vt[v2]); // позиция после первого шага
p2 = add(p1, vt[v3]); // позиция после второго шага
p3 = mul(p1,[2,2,0,0]); // масштабирование
p4 = mul(p2,[2,2,0,0]); // масштабирование
f.push([t1, p3, v4]);
f.push([t2, p4, v5]);
}
return f;
}
function mul(v1, v2)
{
mt = [[[1, 0, 0, 0],[0, 1, 0, 0],[ 0, 0, 1, 0],[ 0, 0, 0, 1]],
[[0, 1, 0, 0],[1,-1, 0, 0],[ 0, 0, 0, 1],[ 0, 0, 1,-1]],
[[0, 0, 1, 0],[0, 0, 0, 1],[-3, 1, 0, 0],[-1,-2, 0, 0]],
[[0, 0, 0, 1],[0, 0, 1,-1],[-1,-2, 0, 0],[-2, 1, 0, 0]]]
var v3 = [0, 0, 0, 0];
for(var i = 0; i < 4; i++)
for(var j = 0; j < 4; j++)
for(var k = 0; k < 4; k++)
v3[k] = v3[k] + v1[i] * v2[j] * mt[i][j][k];
for(var i = 0; i < 4; i++) v3[i] = v3[i] / 2;
return v3;
}
function add(v1, v2)
{ // нельзя к первому к первому аргументу добавить второй и вернуть,
// потому что аргументы принимаются по ссылке и значит будут изменены.
var v3 = [0, 0, 0, 0];
for(var i = 0; i < 4; i++) v3[i] = v3[i] + v1[i];
for(var i = 0; i < 4; i++) v3[i] = v3[i] + v2[i];
return v3;
}
function mean(p1, p2, d)
{ var p3 = [(p2[0] - p1[0]) * d + p1[0],(p2[1] - p1[1]) * d + p1[1]];
return p3;
}
Осталось задать режимы, как именно фигуры могут отображаться.
function paint(a, mode, level = 0)
{
vt = s[1]; // таблица векторов
c = s[2]; // массив координатных констант
fi = s[3]; // константа фи
pr = s[7]; // доля проявления уровня
b = s[0]; // контекст рисования
var st = s[10];
// шесть цветов на выбор
colors = [["#BCE"],["#BBE"],["#ECE"],["#EBE"],["#CEF"],["#EEF"]];
type = a[0]; // оригинальный тип фигуры
tn = type; // тип свернутый до 4
if(tn > 3) tn = tn - 4;
// цвет
color = colors[type];
// направление первого шага, в виде сдвига направления
sht = [ 1,-1, 2,-2];
var shift = sht[tn];
p = a[1]; // точка привязки
v0 = a[2]; // направление
v0 = (10 + v0 % 10) % 10; // направление выравнено в пределы 0-10
v1 = (10 + (v0 + shift) % 10) % 10; // направление первого шага
v2 = (10 + (v0 - shift) % 10) % 10; // направление второго шага
// коэффициенты масштабирования для позциции и для сторон.
var kop = 0;
var koe = 0;
pr1 = 1 - pr; // доля предыдущего уровня.
if(level == 0) {kop = st; koe = pr;}
if(level == 1) {kop = st / fi; koe = pr1 / fi; } // проступание соседнего уровня
if(level == 3) {kop = st / fi / fi / fi; koe = pr / fi / fi / fi; } // линии на три уровня меньше
st = st * koe; // масштабирование фигур.
// координаты начала линии
p0 = [kop * (p[0] * c[0] + p[1] * c[1]), kop * (p[2] * c[2] + p[3] * c[3])]
// координаты конца первой линии
s1 = vt[v1]; p1 = [p0[0] + st * (s1[0] * c[0] + s1[1] * c[1]), p0[1] + st * (s1[2] * c[2] + s1[3] * c[3])];
// координаты конца второй линии
s2 = vt[v2]; p2 = [p1[0] + st * (s2[0] * c[0] + s2[1] * c[1]), p1[1] + st * (s2[2] * c[2] + s2[3] * c[3])];
// таблица рисовать ли фон
modes = [1, 1,1,1,1,1, 0,0,0,0,1, 1,1,1];
y = modes[mode];
// заливка, сразу можно грани рисовать
if(level < 3) // если сдвинутый на три уровень, то фон не отрисовывается, только линии 11 режима.
if(y || mode == 0)
{ begin(); from(p0); to(p1); to(p2); close();
if(y) {fill(color);}
if(mode == 0) line();
if(mode == 12) line_white();
}
// четырехугольники
if(mode == 1)
{ p3 = mean(p0, p2, 0.5);
begin(); from(p0); to(p2); from(p1); to(p3); line_black();
}
// дальняя сторона, фигуры HBS
if(mode == 2)
{ begin(); from(p1); to(p2); line();
}
if(mode == 6) // ромбы
{ begin();
if(tn == 0 || tn == 2)
{ color = colors[tn * 2];
// четвертый угол ромба
p3 = mean(p0, p2, 0.5);
p4 = mean(p1, p3, 2);
from(p0); to(p1); to(p2); to(p4); close(); fill(color);
}
line();
}
if(mode == 7) // дельтоиды
{ if(type == 0)
{ // расчет дополнительного угла уголка
p3 = mean(p0, p1, 1 + fi);
p4 = mean(p2, p3, 1 + fi);
begin(); from(p0); to(p1); to(p4); to(p2); close(); fill(colors[0]); line();
}
if(type == 2)
{ // расчет углов фигуры воздушный змей
p3 = mean(p0, p2, 2 + fi)
p4 = [p0[0] + (p2[0] - p1[0]), p0[1] + (p2[1] - p1[1])];
begin(); from(p0); to(p1); to(p3); to(p4); close(); fill(colors[4]); line();
}
}
if(mode == 8) // разные треугольники
{ if(type < 2)
{ begin(); from(p0); to(p1); to(p2); close(); fill(colors[0]); line();
}
if(type == 4 || type == 5)
{ p3 = mean(p0, p1, 1 + fi);
begin(); from(p0); to(p3); to(p2); close(); fill(colors[4]); line();
}
}
if(mode == 9) // ромб и уголки
{ if(type == 0)
{ p3 = mean(p0, p1, 1 + fi);
p4 = mean(p2, p3, 1 + fi);
begin(); from(p0); to(p1); to(p4); to(p2); close(); fill(colors[0]); line();
}
if(type == 2)
{ p3 = [p0[0] - p1[0] + p2[0], p0[1] - p1[1] + p2[1]];
begin(); from(p0); to(p1); to(p2); to(p3); close(); fill(colors[4]); line();
}
if(type == 4)
{ p3 = mean(p2, p1, 1 + fi);
p4 = mean(p0, p3, 1 + fi);
begin(); from(p0); to(p4); to(p1); to(p2); close(); fill(colors[0]); line();
}
}
if(mode == 10)
{
p4 = mean(p1, p0, fi);
p5 = mean(p0, p2, fi);
p6 = mean(p2, p0, 1 / 2 + fi / 2);
p7 = mean(p1, p2, 0.5);
begin(); if(tn < 2) {from(p4); to(p5);} else {from(p6); to(p4);} to(p7); line();
}
if(mode == 11)
{
k1 = 1 / 2;
k2 = (fi + 1) / 2;
k3 = (4 - fi) / 4;
k4 = (fi + 1) / 4;
k5 = (3 - 2 * fi) / 2;
k6 = 1 / 4;
if(tn < 2)
{
p3 = mean(p0, p2, k4);
p4 = mean(p0, p1, k2);
p5 = mean(p1, p2, k1);
p6 = mean(p0, p2, k5);
p7 = mean(p1, p2, k3);
begin(); from(p3); to(p4); to(p5); to(p6); to(p7);
}
else
{
p3 = mean(p2, p1, k3);
p4 = mean(p0, p1, k2);
p5 = mean(p1, p2, k1);
p6 = mean(p2, p0, k6);
begin(); from(p3); to(p4); to(p5); to(p6);
}
line();
}
}
Код можно скопировать со статьи в файл фрагмент за фрагментом и он заработает.
Так как каждый треугольник имеет парный треугольник (кроме тех, которые на границе), то для лучшей прорисовки можно добавить режим, в котором один из пары треугольников отображается ромбом, а второй не отображается. Так же можно сделать и с дельтоидами и с треугольником объединенным из двух.
Для разбиения P1 нужно задать по две линии на треугольник.
![p1lytdtnwdzazxt4skivxybse9y.png](https://habrastorage.org/webt/p1/ly/td/p1lytdtnwdzazxt4skivxybse9y.png)
![zu5pgsfxwhfj0i_dhhxojlze5ka.png](https://habrastorage.org/webt/zu/5p/gs/zu5pgsfxwhfj0i_dhhxojlze5ka.png)
![zvmnmoewvnndw6fmw2nkl5i4jdu.png](https://habrastorage.org/webt/zv/mn/mo/zvmnmoewvnndw6fmw2nkl5i4jdu.png)
![vuwq1-n3ihcniorgpcl58zavf_m.png](https://habrastorage.org/webt/vu/wq/1-/vuwq1-n3ihcniorgpcl58zavf_m.png)
Для представления мозаики из линий нужно задать следующее разбиение:
![14hfolotdv-m0bcw5y9lyiemboy.png](https://habrastorage.org/webt/14/hf/ol/14hfolotdv-m0bcw5y9lyiemboy.png)
Через три уровня линии повторяются:
![nr97lcrwpzx1_hw8m9an_epfb74.png](https://habrastorage.org/webt/nr/97/lc/nr97lcrwpzx1_hw8m9an_epfb74.png)
![qifyfify2rkrulfupcyj8vtpnsy.png](https://habrastorage.org/webt/qi/fy/fi/qifyfify2rkrulfupcyj8vtpnsy.png)
![tskbfqqlpjqwctpkjxau7vz-zky.png](https://habrastorage.org/webt/ts/kb/fq/tskbfqqlpjqwctpkjxau7vz-zky.png)
Из этого можно вывести коэффициенты пропорций деления линией сторон в месте пересечения. После смещения на три уровня сторона треугольника становится . Решая уравнение
получим
, и коэффициент будет равен
.
Получаются пропорции деления: — деление стороны посередине.
— деление единичной диагонали.
— проекция диагонали на сторону.
— первая проекция на диагональ широкого ромба.
— вторая проекция на диагональ широкого ромба.
— проекция на диагональ узкого ромба
Оказывается, что треугольники, у которых совпадают углы привязки образуют фигуры HBS. И для отрисовки разбиения на HBS достаточно выводить дальнюю грань. Если треугольники масштабировать без изменения привязки, то вокруг фигур образуется пустое пространство. В этом пустом пространстве можно вывести фигуры предыдущего уровня, смасштабировав так, чтобы они касались фигур этого уровня. Именно так нарисовано первое изображение статьи.
![7w5ietl5ejxgcxxmmjklneu5s4c.png](https://habrastorage.org/webt/7w/5i/et/7w5ietl5ejxgcxxmmjklneu5s4c.png)
![nkrhvcebjvoz9mrngledbg2bwza.png](https://habrastorage.org/webt/nk/rh/vc/nkrhvcebjvoz9mrngledbg2bwza.png)
![1sbdirofowie9hlqcn9tqzvfuyk.png](https://habrastorage.org/webt/1s/bd/ir/1sbdirofowie9hlqcn9tqzvfuyk.png)
![zjwizmpetc4vkz0fpit5mc1zqiy.png](https://habrastorage.org/webt/zj/wi/zm/zjwizmpetc4vkz0fpit5mc1zqiy.png)
![m9kc-9wtlzocrjdkd_kqcbqgi3k.png](https://habrastorage.org/webt/m9/kc/-9/m9kc-9wtlzocrjdkd_kqcbqgi3k.png)
Для вывода отображения из семи видов четырехугольников отрисовывается основание каждого треугольника и высота.
![l0-bjojkqzo0gxb0-4uehd1gfts.png](https://habrastorage.org/webt/l0/-b/jo/l0-bjojkqzo0gxb0-4uehd1gfts.png)
![kvqxb3etury0ez2vheu3rs58k4c.png](https://habrastorage.org/webt/kv/qx/b3/kvqxb3etury0ez2vheu3rs58k4c.png)
![n82ue6-qp1hvbani3yvagfjwrmo.png](https://habrastorage.org/webt/n8/2u/e6/n82ue6-qp1hvbani3yvagfjwrmo.png)
![mst765wr7ccnnrofxxn6vaa6ntu.png](https://habrastorage.org/webt/ms/t7/65/mst765wr7ccnnrofxxn6vaa6ntu.png)
По-моему, очень красиво.