Рассмотрим цилиндр из прямоугольного листа бумаги. Лист имеет две стороны, переднюю и заднюю. Когда мы склеиваем лист в цилиндр, мы не можем достичь задней стороны (внутри цилиндра) с передней стороны (снаружи цилиндра). Однако, если мы склеим лист бумаги в полосу Мёбиуса, то сможем. Вот как будет выглядеть сетка на полосе Мёбиуса, если мы разделим две стороны и сгладим ее:
┌────┬────┬────┬────┰────┬────┬────┬────┐
│ a4 │ b4 │ c4 │ d4 ┃ A1 │ B1 │ C1 │ D1 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ a3 │ b3 │ c3 │ d3 ┃ A2 │ B2 │ C2 │ D2 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ a2 │ b2 │ c2 │ d2 ┃ A3 │ B3 │ C3 │ D3 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ a1 │ b1 │ c1 │ d1 ┃ A4 │ B4 │ C4 │ D4 │
└────┴────┴────┴────┸────┴────┴────┴────┘
Обратите внимание, что квадраты слева (т. Е. В нижнем регистре) находятся спереди, а квадраты справа (т. Е. В верхнем регистре) находятся сзади. Квадраты с разницей в регистре - это один и тот же квадрат, только на противоположных сторонах полосы Мебиуса. Следует отметить, что эта сплющенная полоса Мёбиуса очень похожа на цилиндр, за исключением того, что левая и правая стороны совпадают.
Вот как будет выглядеть код для ленты Мебиуса:
const mod = (x, y) => (x % y + y) % y;
const coords = (m, n) => (i, j) => {
j = mod(j, 2 * n); // wrapping around like a cylinder
if (j < n) return [i, j]; // front side
return [m - i - 1, j - n]; // back side, translated to front side
};
Бутылка Кляйна, точно такая же, как полоса Мёбиуса, за исключением того, что она ведет себя как тор, а не как цилиндр. Вот как будет выглядеть код бутылки Кляйна:
const mod = (x, y) => (x % y + y) % y;
const coords = (m, n) => (i, j) => {
i = mod(i, m); // wrapping around
j = mod(j, 2 * n); // like a torus
if (j < n) return [i, j]; // front side
return [m - i - 1, j - n]; // back side, translated to front side
};
Проективная плоскость также ведет себя как тор. Однако каждая из его сторон может иметь две ориентации, правильную и повернутую на 180 °. Вот как будет выглядеть сплющенная проективная плоскость:
┌────┬────┬────┬────┰────┬────┬────┬────┐
│ a4 │ b4 │ c4 │ d4 ┃ A1 │ B1 │ C1 │ D1 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ a3 │ b3 │ c3 │ d3 ┃ A2 │ B2 │ C2 │ D2 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ a2 │ b2 │ c2 │ d2 ┃ A3 │ B3 │ C3 │ D3 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ a1 │ b1 │ c1 │ d1 ┃ A4 │ B4 │ C4 │ D4 │
┝━━━━┿━━━━┿━━━━┿━━━━╋━━━━┿━━━━┿━━━━┿━━━━┥
│ D4 │ C4 │ B4 │ A4 ┃ d1 │ c1 │ b1 │ a1 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ D3 │ C3 │ B3 │ A3 ┃ d2 │ c2 │ b2 │ a2 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ D2 │ C2 │ B2 │ A2 ┃ d3 │ c3 │ b3 │ a3 │
├────┼────┼────┼────╂────┼────┼────┼────┤
│ D1 │ C1 │ B1 │ A1 ┃ d4 │ c4 │ b4 │ a4 │
└────┴────┴────┴────┸────┴────┴────┴────┘
Итак, вот как будет выглядеть код для проективной плоскости:
const mod = (x, y) => (x % y + y) % y;
const coords = (m, n) => (i, j) => {
i = mod(i, 2 * m); // wrapping around
j = mod(j, 2 * n); // like a torus
if (i >= m) { // collapse to Klein bottle topology
i -= m;
j = mod(n - j - 1, 2 * n);
}
if (j < n) return [i, j]; // front side
return [m - i - 1, j - n]; // back side, translated to front side
};
Надеюсь, это поможет.