Рисуем морозные узоры на SQL | | ДОСТУПНЫЙ ОТДЫХ
Интересное

Рисуем морозные узоры на SQL

Рисуем морозные узоры на SQL

Немного SQL-магии под катом: математика, рекурсия, псевдографика.

Заодно вспоминаем под Новый год формулу угла между векторами:
Рисуем морозные узоры на SQL

WITH RECURSIVE T AS (
SELECT
0 x
, 0 y
, ‘{«{0,0}»}’::text[] c — растим узор от центра
, 0 i
UNION ALL
(
WITH Z AS (
SELECT
dn.x
, dn.y
, T.c
, T.i
FROM
T
— вбрасываем случайную точку на плоскость
, LATERAL(
SELECT
((random() * 2 — 1) * 100)::integer x
, ((random() * 2 — 1) * 100)::integer y
) p
— из всех уже заполненных точек выбираем у ней ближайшую
, LATERAL(
SELECT
*
FROM
(
SELECT
(unnest::text[])[1]::integer x
, (unnest::text[])[2]::integer y
FROM
unnest(T.c::text[])
) T
ORDER BY
sqrt((x — p.x) ^ 2 + (y — p.y) ^ 2)
LIMIT 1
) n
— из 8 ее «целочисленных» соседей заполняем ближайшую по направлению к вброшенной
, LATERAL (
SELECT
n.x + dx x
, n.y + dy y
FROM
generate_series(-1, 1) dx
, generate_series(-1, 1) dy
WHERE
(dx, dy) <> (0, 0)
ORDER BY
CASE
WHEN (p.x, p.y) = (n.x, n.y) THEN 0
ELSE abs(acos(((p.x — n.x) * dx + (p.y — n.y) * dy) / sqrt((p.x — n.x) ^ 2 + (p.y — n.y) ^ 2) / sqrt(dx ^ 2 + dy ^ 2)))
END
LIMIT 1
) dn
)
SELECT
Z.x
, Z.y
, Z.c || ARRAY[Z.x, Z.y]::text
, Z.i + 1
FROM
Z
WHERE
Z.i < (1 << 10)
)
)
— для каждой точки рисунка вычисляем расстояние до узора
, map AS (
SELECT
gx x
, gy y
, (
SELECT
sqrt((gx — T.x) ^ 2 + (gy — T.y) ^ 2) v
FROM
T
ORDER BY
v
LIMIT 1
) v
FROM
generate_series(-40, 40) gx
, generate_series(-30, 30) gy
)
— формируем алфавит отрисовки
, gr AS (
SELECT
regexp_split_to_array(‘#*+-. ‘, ») s
)
— рисуем картинку
SELECT
string_agg(
coalesce(s[(v * (array_length(s, 1) — 1))::integer + 1], ‘ ‘)
, ‘ ‘
ORDER BY x) frozen
FROM
(
SELECT
x
, y
, v::double precision / max(v) OVER() v — нормируем значения расстояний по максимуму
FROM
map
) T
, gr
GROUP BY
y
ORDER BY
y;

Рисуем морозные узоры на SQL
А можно немного поправить алфавит и размерыРисуем морозные узоры на SQL

WITH RECURSIVE T AS (
SELECT
0 x
, 0 y
, ‘{«{0,0}»}’::text[] c — растим узор от центра
, 0 i
UNION ALL
(
WITH Z AS (
SELECT
dn.x
, dn.y
, T.c
, T.i
FROM
T
— вбрасываем случайную точку на плоскость
, LATERAL(
SELECT
((random() * 2 — 1) * 100)::integer x
, ((random() * 2 — 1) * 100)::integer y
) p
— из всех существующих точек выбираем у ней ближайшую
, LATERAL(
SELECT
*
FROM
(
SELECT
(unnest::text[])[1]::integer x
, (unnest::text[])[2]::integer y
FROM
unnest(T.c::text[])
) T
ORDER BY
sqrt((x — p.x) ^ 2 + (y — p.y) ^ 2)
LIMIT 1
) n
— из 8 ее соседей заполняем ближайшую по направлению к вброшенной
, LATERAL (
SELECT
n.x + dx x
, n.y + dy y
FROM
generate_series(-1, 1) dx
, generate_series(-1, 1) dy
WHERE
(dx, dy) <> (0, 0)
ORDER BY
CASE
WHEN (p.x, p.y) = (n.x, n.y) THEN 0
ELSE abs(acos(((p.x — n.x) * dx + (p.y — n.y) * dy) / sqrt((p.x — n.x) ^ 2 + (p.y — n.y) ^ 2) / sqrt(dx ^ 2 + dy ^ 2)))
END
LIMIT 1
) dn
)
SELECT
Z.x
, Z.y
, Z.c || ARRAY[Z.x, Z.y]::text
, Z.i + 1
FROM
Z
WHERE
Z.i < (1 << 10)
)
)
— для каждой точки рисунка вычисляем расстояние до узора
, map AS (
SELECT
gx x
, gy y
, (
SELECT
sqrt((gx — T.x) ^ 2 + (gy — T.y) ^ 2) v
FROM
T
ORDER BY
v
LIMIT 1
) v
FROM
generate_series(-70, 70) gx
, generate_series(-35, 35) gy
)
— формируем алфавит отрисовки
, gr AS (
SELECT
regexp_split_to_array(‘#****++++—-…. ‘, ») s
)
— рисуем картинку
SELECT
string_agg(
coalesce(s[(v * (array_length(s, 1) — 1))::integer + 1], ‘ ‘)
, ‘ ‘
ORDER BY x) frozen
FROM
(
SELECT
x
, y
, v::double precision / max(v) OVER() v — нормируем значения расстояний по максимуму
FROM
map
) T
, gr
GROUP BY
y
ORDER BY
y;
Источник

Читать  Российская художница Лера Кирьякова изобразила представительниц разных наций в очаровательном мультяшном стиле (10 фото)

I heart FeedBurner