1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-17 14:10:56 +02:00

ru-RU translation (#295)

* ru-RU introduction

Translation of introduction to Russian

* ru-RU whatis

Translation of chapter 2 to Russian

* (fixup) ru-RU intro

fixed missing translations

* ru-RU index.html

Translated header, meta, title and link names for existing chapter's traslations. (will be updated with every new commit)

* ru-RU locale strings

Locale string russian traslations

* locale fixup

* build chapters 1,2

* ru-RU explanation

translation of explanation to Russian.

* ru-RU control

translation of #control to Russian

* ru-RU weightcontrol

translation of #weightcontrol to Russian

* ru-RU derivatives

translation of #derivatives to Russian

* (fixup) ru-RU weightcontrol

* (fixup) ru-RU explanation

* ru-RU extended

Co-authored-by: Mammoth <echo@mammothnotes.com>
This commit is contained in:
ra30r
2021-01-10 13:24:47 -06:00
committed by GitHub
parent 7768431058
commit 402e4fbb03
5 changed files with 515 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
# Контроль кривых Безье
Кривые Безье, как и все отрезки кривых, являются интерполярными функциями. Это значит, что они имеют заданный набор контрольных точек и генерируют значения где-то "между" ними. (Одним следствием этого есть то, что невозможно сгенерировать точки вне этого "каркаса". Полезная информация!). На практике мы можем проиллюстрировать влияние каждой контрольной точки на течение кривой по отдельности и проследить какие из точек имеют наибольший эффект в тех и иных частях кривой.
Следующие зарисовки демонстрируют интерполяции функций для квадратной и кубической кривых, где "S" представляет степень влияния контрольной точки на положение участка кривой по ходу развития t. Потяните ползунки, чтобы отследить проценты интерполяций каждой контрольной точки на конкретном <i>t</i>.
<div class="figure">
<graphics-element title="Квадратная интерполяция" src="./lerp.js" data-degree="3">
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control">
</graphics-element>
<graphics-element title="Кубическая интерполяция" src="./lerp.js" data-degree="4">
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control">
</graphics-element>
<graphics-element title="Интерполяция кривой 15-го порядка" src="./lerp.js" data-degree="15">
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control">
</graphics-element>
</div>
Также представлена интерполяция кривой Безье 15<sup>го</sup> порядка. Как вы можете видеть, начальная и конечная точки влияют на положение кривой значительно больше, чем любая другая контрольная точка.
Желая изменить кривую, нам нужно изменить "вес"-а (* коофициенты) для каждой точки, по сути меняя интерполяцию. Имплементация оного предельно логична: нужно просто перемножить каждую точку на значение, меняющее ее 'силу'. Эти значения, по конвенции называют "весом" и мы можем учесть их запись в нашей оригинальной функции Безье:
\[
Bézier(n,t) = \sum_{i=0}^{n}
\underset{биноминальный~термин}{\underbrace{\binom{n}{i}}}
\cdot\
\underset{полиноминальный~термин}{\underbrace{(1-t)^{n-i} \cdot t^{i}}}
\cdot\
\underset{вес}{\underbrace{w_i}}
\]
Хоть и выглядит заморочено, но, так уж получается, в реальности "веса" просто значения координат на графике, к которым мы бы хотели, чтобы наша функция стремилась. Так, для кривой <i>n-го</i> порядка, w<sub>0</sub> есть начальной координатой, w<sub>n</sub> конечной координатой, а все между ними — контрольными координатами. Например, чтобы кубическая кривая начиналась в (110,150), стремилась к точкам (25,190) и (210,250) заканчиваясь на (210,30), мы запишем это следующим образом:
\[
\left \{ \begin{matrix}
x = DARKRED[110] \cdot (1-t)^3 + DARKGREEN[25] \cdot 3 \cdot (1-t)^2 \cdot t + DARKBLUE[210] \cdot 3 \cdot (1-t) \cdot t^2 + AMBER[210] \cdot t^3 \\
y = DARKRED[150] \cdot (1-t)^3 + DARKGREEN[190] \cdot 3 \cdot (1-t)^2 \cdot t + DARKBLUE[250] \cdot 3 \cdot (1-t) \cdot t^2 + AMBER[30] \cdot t^3
\end{matrix} \right.
\]
Это производит кривую, график которой мы видели в первой главе этой статьи.
<graphics-element title="Наша кубическая кривая Безье" src="../introduction/cubic.js"></graphics-element>
Что еще можно сделать с кривой Безье? На самом деле — вполне не мало. Остальная часть статьи описывает множества доступных операций и задач, которые они решают.
<div class="howtocode">
### Имплементация весовых функций.
Отталкиваясь от того, что мы уже знаем как воплотить базис функции, добавление контрольных точек представляет собой удивительно легкую задачу:
```
function Bezier(n,t,w[]):
sum = 0
for(k=0; k<=n; k++):
sum += w[k] * binomial(n,k) * (1-t)^(n-k) * t^(k)
return sum
```
И оптимизированная версия:
```
function Bezier(2,t,w[]):
t2 = t * t
mt = 1-t
mt2 = mt * mt
return w[0]*mt2 + w[1]*2*mt*t + w[2]*t2
function Bezier(3,t,w[]):
t2 = t * t
t3 = t2 * t
mt = 1-t
mt2 = mt * mt
mt3 = mt2 * mt
return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3
```
И вот, мы знаем как программировать базис функции с весами.
</div>

View File

@@ -0,0 +1,156 @@
# Производные кривых Безье
Есть целый ряд вещей, которые мы можем сотворить с кривыми Безье базируясь на их производных, и одним восхитительным наблюдением на счет первых, является то, что их производные по сути тоже являются кривыми Безье. На практике, дифференциация кривых Безье относительно логична, хотя нам и потребуется немного математики.
Для начала рассмотрим правило производных для кривых Безье:
\[
Bézier'(n,t) = n \cdot \sum_{i=0}^{n-1} (b_{i+1}-b_i) \cdot Bézier(n-1,t)_i
\]
которое мы так же можем записать (отметив что <i>b</i> в этой формуле, то-же что наш <i>w</i> "вес", а <i>n</i> умноженная на функцию сумы — то-же что функция сумы, с каждой составляющей умноженной на <i>n</i>) как:
\[
Bézier'(n,t) = \sum_{i=0}^{n-1} Bézier(n-1,t)_i \cdot n \cdot (w_{i+1}-w_i)
\]
Другими словами, производная кривой Безье n<sup>го</sup> порядка равна кривой Безье на порядок ниже (n-1), соответственно имея на один термин составляющих меньше, где новые веса w'<sub>0</sub>...w'<sub>n-1</sub> произведены из оригинальных по принципу n(w<sub>i+1</sub> - w<sub>i</sub>). Так, для кривой 3<sup>го</sup> порядка, с 4мя весами, производная имеет 3 веса: w'<sub>0</sub> = 3(w<sub>1</sub>-w<sub>0</sub>), w'<sub>1</sub> = 3(w<sub>2</sub>-w<sub>1</sub>) и w'<sub>2</sub> = 3(w<sub>3</sub>-w<sub>2</sub>).
<div class="note">
### "Погодите, а почему это работает?"
Порой, когда говорят "вот она производная", наше счастье приходит немедленно и остается неизменным вне зависимости от следующих аргументов. Но, возможно, вам захочется покопаться в доказательствах производной. Если так, то для начала оговоримся: веса независимы от общей формулы функции, потому производная кривой затрагивает только производные полиноминалов базовой функции. Найдем сперва это:
\[
B_{n,k}(t) \frac{d}{dt} = {n \choose k} t^k (1-t)^{n-k} \frac{d}{dt}
\]
Приминение [правил продукта](https://ru.qaz.wiki/wiki/Product_rule) и [цепного правила](https://ru.qaz.wiki/wiki/Chain_rule) (* в оригинале [другие](https://en.wikipedia.org/wiki/Product_rule) [ссылки](https://en.wikipedia.org/wiki/Chain_rule)) дает нам следующее:
\[
\begin{array}{l}
... = {n \choose k} \left (
k \cdot t^{k-1} (1-t)^{n-k} + t^k \cdot (1-t)^{n-k-1} \cdot (n-k) \cdot -1
\right )
\end{array}
\]
В таком виде с этой формулой работать сложно, потому раскроем:
\[
\begin{array}{l}
... = \frac{kn!}{k!(n-k)!} t^{k-1} (1-t)^{n-k} - \frac{(n-k)n!}{k!(n-k)!} t^k (1-t)^{n-1-k}
\end{array}
\]
Теперь фокус заключается в трансформации этой функции во что-то, что бы имело биноминальные коэффициенты, потому как мы хотим получить выражение в форме вроде "x! деленное на y!(x-y)!". Если нам удастся сделать это так, чтобы учесть в запись термины <i>n-1</i> и <i>k-1</i> — мы на верном пути.
\[
\begin{array}{l}
... = \frac{n!}{(k-1)!(n-k)!} t^{k-1} (1-t)^{n-k} - \frac{(n-k)n!}{k!(n-k)!} t^k (1-t)^{n-1-k} \\
... = n \left (
\frac{(n-1)!}{(k-1)!(n-k)!} t^{k-1} (1-t)^{n-k} - \frac{(n-k)(n-1)!}{k!(n-k)!} t^k (1-t)^{n-1-k}
\right ) \\
... = n \left (
\frac{(n-1)!}{(k-1)!((n-1)-(k-1))!} t^{(k-1)} (1-t)^{(n-1)-(k-1)} - \frac{(n-1)!}{k!((n-1)-k)!} t^k (1-t)^{(n-1)-k}
\right )
\end{array}
\]
Первая часть готова: компоненты в скобках на самом то деле обычные выражения кривых Безье на порядок ниже:
\[\begin{array}{l}
... = n \left (
\frac{x!}{y!(x-y)!} t^{y} (1-t)^{x-y} - \frac{x!}{k!(x-k)!} t^k (1-t)^{x-k}
\right )
~,~где~x=n-1,~y=k-1
\\
... = n \left ( B_{(n-1),(k-1)}(t) - B_{(n-1),k}(t) \right )
\end{array}
\]
Теперь применим это к записям наших формул с "весами". Начнем с формулы кривой Безье приведенной выше и пройдемся по ее производным.
\[\begin{array}{lcl}
Bézier_{n,k}(t) &=& B_{n,0}(t) \cdot w_0 + B_{n,1}(t) \cdot w_1 + B_{n,2}(t) \cdot w_2 + B_{n,3}(t) \cdot w_3 + ... \\
Bézier_{n,k}(t) \frac{d}{dt} &=& n \cdot (B_{n-1,-1}(t) - B_{n-1,0}(t)) \cdot w_0 + \\
& & n \cdot (B_{n-1,0}(t) - B_{n-1,1}(t)) \cdot w_1 + \\
& & n \cdot (B_{n-1,1}(t) - B_{n-1,2}(t)) \cdot w_2 + \\
& & n \cdot (B_{n-1,2}(t) - B_{n-1,3}(t)) \cdot w_3 + \\
& & ...
\end{array}\]
Раскрыв это (немного раскрасив, чтобы подчеркнуть соотношение терминов), и выстроив термины по возрастанию значений, мы увидим следующее:
\[\begin{array}{lclc}
n \cdot B_{n-1,-1}(t) \cdot w_0 &+& & \\
n \cdot B_{n-1,BLUE[0]}(t) \cdot w_1 &-& n \cdot B_{n-1,BLUE[0]}(t) \cdot w_0 & + \\
n \cdot B_{n-1,RED[1]}(t) \cdot w_2 &-& n \cdot B_{n-1,RED[1]}(t) \cdot w_1 & + \\
n \cdot B_{n-1,MAGENTA[2]}(t) \cdot w_3 &-& n \cdot B_{n-1,MAGENTA[2]}(t) \cdot w_2 & + \\
... &-& n \cdot B_{n-1,3}(t) \cdot w_3 & + \\
... & & &
\end{array}\]
Два из приведенных терминов отпадают. Первый — потому что степень <i>-1</i> не присутствует в суме, оттого каждый раз приводя "ничего" в результат. Можем смело игнорировать это значение в нахождении производной функции. Второй — самый последний термин этой записи, затрагивающий <i>B<sub>n-1,n</sub></i>. Этот термин имел-бы биноминальный коэффициент [<i>i</i> указывая на <i>i+1</i>], который не существуюет. И опять — термин ничего не привносит в результат, затем также может быть проигнорирован. Это значит, нам остается:
\[\begin{array}{lclc}
n \cdot B_{n-1,BLUE[0]}(t) \cdot w_1 &-& n \cdot B_{n-1,BLUE[0]}(t) \cdot w_0 &+ \\
n \cdot B_{n-1,RED[1]}(t) \cdot w_2 &-& ~n \cdot B_{n-1,RED[1]}(t) \cdot w_1 &+ \\
n \cdot B_{n-1,MAGENTA[2]}(t) \cdot w_3 &-& n \cdot B_{n-1,MAGENTA[2]}(t) \cdot w_2 &+ \\
...
\end{array}\]
И это по сути формула функции сумы на 1 порядок ниже:
\[
Bézier_{n,k}(t) \frac{d}{dt} = n \cdot B_{(n-1),BLUE[0]}(t) \cdot (w_1 - w_0)
+ n \cdot B_{(n-1),RED[1]}(t) \cdot (w_2 - w_1)
+ n \cdot B_{(n-1),MAGENTA[2]}(t) \cdot (w_3 - w_2)
~+ ~...
\]
Можно переписать по стандартной форме сумы, и готово:
\[
Bézier_{n,k}(t) \frac{d}{dt} = \sum_{k=0}^{n-1} n \cdot B_{n-1,k}(t) \cdot (w_{k+1} - w_k)
= \sum_{k=0}^{n-1} B_{n-1,k}(t) \cdot \underset{вес~производной}
{\underbrace{n \cdot (w_{k+1} - w_k)}}
\]
</div>
Давайте перепишем это по форме схожей с нашей исходной формулой, чтобы легче было разглядеть разницу. Сначала оригинальная формула, за ней производная:
\[
Bézier(n,t) = \sum_{i=0}^{n}
\underset{биноминальный~термин}{\underbrace{\binom{n}{i}}}
\cdot\
\underset{полиноминальный~термин}{\underbrace{(1-t)^{n-i} \cdot t^{i}}}
\cdot\
\underset{вес}{\underbrace{w_i}}
\]
\[
Bézier'(n,t) = \sum_{i=0}^{k}
\underset{биноминальный~термин}{\underbrace{\binom{k}{i}}}
\cdot\
\underset{полиноминальный~термин}{\underbrace{(1-t)^{k-i} \cdot t^{i}}}
\cdot\
\underset{вес~производной}{\underbrace{n \cdot (w_{i+1} - w_i)}}
{~, ~with ~k=n-1}
\]
И в чем же разница? В терминах формулы кривой Безье, по сути, никакой! Мы уменьшили порядок (вместо порядка <i>n</i>, он теперь <i>n-1</i>), но это все та же функция Безье. Единственное отличие в подсчете изменений в "весах" при нахождении производной. К примеру, исходя из 4-х контрольных точек A, B, C и D, первая производная получит 3 точки, вторая — 2, третья — 1:
\[ \begin{array}{llll}
B(n,t), & & w = \{A,B,C,D\} \\
B'(n,t), & n = 3, & w' = \{A',B',C'\} &= \{3 \cdot (B-A), {~} 3 \cdot (C-B), {~} 3 \cdot (D-C)\} \\
B''(n,t), & n = 2, & w'' = \{A'',B''\} &= \{2 \cdot (B'-A'), {~} 2 \cdot (C'-B')\} \\
B'''(n,t), & n = 1, & w''' = \{A'''\} &= \{1 \cdot (B''-A'')\}
\end{array} \]
Можно продолжать производить этот фокус, до тех пор пока у нас имеется более одного веса. Когда же остается один вес, следующим шагом будет <i>k = 0</i>, и результат сложения "функции сумы Безье" будет равен 0, поскольку мы ничего ни с чем не слагаем. По этому у квадратной функций нету второй производной, у кубической — третей, и, обобщая, кривая Безье <i>n<sup>го</sup></i> порядка, имеет <i>n-1</i> (внятных) производных, с каждой следующей производной равной нулю.

View File

@@ -0,0 +1,175 @@
# Математика под кривыми Безье
Кривые Безье по своей форме представляют собой параметрические функции. С точки зрения математики, эти функции хитрят, поскольку разнятся с каноническим определением функции. Последнее есть преобразованием любого количества вводных в <strong>единый</strong> результат. Как бы мы не меняли вводные значения переменных, на выходе всегда получим одно значение.
Параметрические функции хитрят, потому как говоря: "что ж, ладно, нам нужно произвести более одного вывода", заключают, попросту: "давайте использовать более одной функции". Проиллюстрируем: представьте что есть функция, которая преобразует вводную, назовем ее <i>x</i>, в другое значение следуя определенной логике
\[
f(x) = \cos(x)
\]
Нотация <i>f(x)</i> есть стандартным способом записи функции (по конвенции, <i>f</i>, когда представлена всего одна) и ее вывод меняется в зависимости от значения одной переменной (в данном случае, <i>x</i>). Так, изменив <i>x</i> и получим другой результат <i>f(x)</i>.
Пока все логично. Теперь, давайте рассмотрим параметрические функции в действии и разберемся в чем их фокус. И, для начала, примите ко внимаю следующий пример:
\[
\begin{matrix}
f(a) = \cos(a) \\
f(b) = \sin(b)
\end{matrix}
\]
Итак, нам представлены две функции. Ничего особо выдающегося: просто функции синуса и косинуса. Отметим, что, как вы можете видеть, вводные переменные разные. Затем, меняя значение <i>a</i>, мы не влияем на вывод <i>f(b)</i>, поскольку <i>a</i> никак не задействована в этой функции. Параметрические функции хитрят именно с этим: весь набор функций вывода делит между собой одну или более переменную вводную. Как здесь:
\[
\left \{ \begin{matrix}
f_a(t) = \cos(t) \\
f_b(t) = \sin(t)
\end{matrix} \right.
\]
Что же, несколько функций, и всего одна вводная. Меняя значение <i>t</i> — меняем вывод обеих ф-ций, <i>f<sub>a</sub>(t)</i> и <i>f<sub>b</sub>(t)</i>. Возможно вы спросите: "в чем же польза?". Ответ прост и очевиден, если уточнить, что мы имеем ввиду под записью наших функций:
\[
\left \{ \begin{matrix}
x = \cos(t) \\
y = \sin(t)
\end{matrix} \right.
\]
Так-то. Координаты <i>x</i>/<i>y</i>. Связанные мистическим значением <i>t</i>.
Таким образом, мы видим, что параметрические функции не определяют значение <i>y</i> через значение <i>x</i>, как обычные ф-ции, вместо этого они выводят и <i>x</i> и <i>y</i> из мистического значения <i>t</i>. Значит, для каждого значения <i>t</i>, существуют два соответствующих значения <i>x</i> и <i>y</i>, которые мы можем использовать для зарисовки кривой на графике. К примеру, приведенная выше параметрическая функция выводит координаты точки на круге: можно подставить любое значение <i>t</i> (от негативной до позитивной бесконечности), и на выводе мы всегда получим координаты точки на круге с центром в (0,0) и радиусом в 1. Зарисовав точки для <i>t</i> от 0 до 5, получаем следующее:
<graphics-element title=" (Часть) круга: x=sin(t), y=cos(t)" src="./circle.js">
<input type="range" min="0" max="10" step="0.1" value="5" class="slide-control">
</graphics-element>
Кривые Безье — всего один из многих классов параметрических функций. Их главной характеристикой есть использование одной и той же базовой функции для генерации всех выводов. До сих пор, в использованном нами примере, мы производили значения <i>x</i> и <i>y</i> с помощью разных функций (ф-цией синуса и ф-цией косинуса); Безье же использует единый "биноминальный полином" для вывода обоих значений. Но что же такое "биноминальный полином"?
Возможно, вы помните полиномы из школьной программы. Они выглядят следующим образом:
\[
f(x) = a \cdot x^3 + b \cdot x^2 + c \cdot x + d
\]
Так, если наивысшая степень в уравнении составляет <i></i>, мы называем такой полином кубическим, если <i></i> — квадратным. Если это только <i></i> — значит это просто линия (когда же <i>x</i> не упоминается вовсе — это не полином!).
Кривые Безье являются полиномом <i>t</i> (вместо <i>x</i>), где значение <i>t</i> — варъируется между 0 и 1, и коэффициентами <i>a</i>, <i>b</i> и т.д. принимающими "биноминальную" форму. Звучит сложно, но на практике выглядит понятнее:
\[
\begin{aligned}
линейный &= (1-t) + t \\
квадратный &= (1-t)^2 + 2 \cdot (1-t) \cdot t + t^2 \\
кубический &= (1-t)^3 + 3 \cdot (1-t)^2 \cdot t + 3 \cdot (1-t) \cdot t^2 + t^3
\end{aligned}
\]
Я знаю что вы думаете: это не выглядит таким уж простым. Но если мы уберем <i>t</i> и вместо этого поставим *1, внезапно, все становится ясно. Примите ко внимаю такие биномиальные термины:
\[
\begin{aligned}
линийный &= \hspace{2.5em} 1 + 1 \\
квадратый &= \hspace{1.7em} 1 + 2 + 1\\
кубический &= \hspace{0.85em} 1 + 3 + 3 + 1\\
квартический &= 1 + 4 + 6 + 4 + 1
\end{aligned}
\]
Обратите внимание, что 2 это то же, что 1+1, и 3 это 2+1 и 1+2 и 6 это 3+3... Как вы можете видеть, каждый раз поднимаясь на одно разрешение выше, мы начинаем и заканчиваем с единицы, а все промежуточные значения равны суме двух значений над ними в предыдущей строке. Так получаем последовательность цифр, называемой [треугольником Паскаля](https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B5%D1%83%D0%B3%D0%BE%D0%BB%D1%8C%D0%BD%D0%B8%D0%BA_%D0%9F%D0%B0%D1%81%D0%BA%D0%B0%D0%BB%D1%8F) (*в оригинале [другая ссылка](https://en.wikipedia.org/wiki/Pascal%27s_triangle) ).
Так-же существует простой способ для выяснения подоплотной работы полиноминальных терминов: если мы переименуем <i>(1-t)</i> в <i>a</i> и <i>t</i> в <i>b</i>, убрав "веса", получим следующее:
\[
\begin{aligned}
линийный &= BLUE[a] + RED[b] \\
квадратый &= BLUE[a] \cdot BLUE[a] + BLUE[a] \cdot RED[b] + RED[b] \cdot RED[b] \\
кубический &= BLUE[a] \cdot BLUE[a] \cdot BLUE[a] + BLUE[a] \cdot BLUE[a] \cdot RED[b] + BLUE[a] \cdot RED[b] \cdot RED[b] + RED[b] \cdot RED[b] \cdot RED[b]\\
\end{aligned}
\]
В целом это просто сума "каждого сочетания <i>a</i> и <i>b</i>", получаемая прогрессивной заменой <i>a</i> на <i>b</i> по ходу уравнения. Потому, это так-же довольно просто. Итак теперь вы знаете что такое биноминальные полиномы. Для полноты картины, ниже привожу их общую функцию:
\[
Bézier(n,t) = \sum_{i=0}^{n}
\underset{биноминальный~термин}{\underbrace{\binom{n}{i}}}
\cdot\
\underset{полиноминальный~термин}{\underbrace{(1-t)^{n-i} \cdot t^{i}}}
\]
И теперь, это полное объяснение. Σ в этой функции означает, что это серия сум (с использованием переменной приведенной под Σ, со стартовым значением в ...=&lt;value&gt; и максимальным значением представленным над Σ)
<div class="howtocode">
### Имплементация базовых функций
Наивно, мы могли бы воплотить базис этой функции как математическую конструкцию, используя функцию как путеводитель:
```
function Bezier(n,t):
sum = 0
for(k=0; k<n; k++):
sum += n!/(k!*(n-k)!) * (1-t)^(n-k) * t^(k)
return sum
```
Я говорю "мы могли бы", поскольку это не входит в наши планы: факторные функции *невероятно* дорого стоят. И, как мы можем понять из выше приведенного текста, треугольник Паскаля можно получить и без таких ухищрений: попросту начнем с [1], далее [1,1], затем [1,2,1], потом [1,3,3,1] и т.д., с каждым новым рядком, записывая на 1 цифру больше, чем в предыдущем, начиная и заканчивая с единицы, а промежуточные цифры определяя суммируя две над ними в предыдущем рядке.
Такой ряд можно сгенерировать предельно быстро, и с этим нам не придется компилировать биноминальные термины, поскольку можно пользоваться значениями из таблицы:
```
lut = [ [1], // n=0
[1,1], // n=1
[1,2,1], // n=2
[1,3,3,1], // n=3
[1,4,6,4,1], // n=4
[1,5,10,10,5,1], // n=5
[1,6,15,20,15,6,1]] // n=6
function binomial(n,k):
while(n >= lut.length):
s = lut.length
nextRow = new array(size=s+1)
nextRow[0] = 1
for(i=1, prev=s-1; i<s; i++):
nextRow[i] = lut[prev][i-1] + lut[prev][i]
nextRow[s] = 1
lut.add(nextRow)
return lut[n][k]
```
Итак, что же здесь происходит? Сначала мы декларируем таблицу достаточного размера для удовлетворения большинства запросов. Далее мы заявляем функцию вывода необходимого значения, вытаскивая его из таблицы, предварительно убедившись, что значения для запрашиваемых <i>n/k</i> присутствуют в наборе и расширяя набор по необходимости (если не присутствуют). Наша базовая функция теперь выглядит типа этого:
```
function Bezier(n,t):
sum = 0
for(k=0; k<=n; k++):
sum += binomial(n,k) * (1-t)^(n-k) * t^(k)
return sum
```
Отлично. Конечно, мы можем оптимизировать ее и далее. Для большинства задач компьютерной графики нам не потребуются кривые произвольного порядка (хотя мы приводим код для произвольных кривых в этом пособии); зачастую нам нужны квадратные и кубические кривые, а это значит, мы можем значительно упростить весь наш код:
```
function Bezier(2,t):
t2 = t * t
mt = 1-t
mt2 = mt * mt
return mt2 + 2*mt*t + t2
function Bezier(3,t):
t2 = t * t
t3 = t2 * t
mt = 1-t
mt2 = mt * mt
mt3 = mt2 * mt
return mt3 + 3*mt2*t + 3*mt*t2 + t3
```
И вот теперь мы знаем как программировать базовую функцию. Превосходно.
</div>
Итак, зная как выглядят базовые функции, время добавить магию делающую кривые Безье такими особенными: контрольные точки.

View File

@@ -0,0 +1,31 @@
# Интервал Безье [0,1]
В математике кривых Безье, вы могли заметить одну любопытную деталь — кривые Безье всегда считают вдоль одного и того же интервала t, `t=0` to `t=1`. Почему же именно этот интервал?
Последнее обусловленно тем, как мы определяем "начало" и "конец" нашей кривой. Если у нас есть значение, которое представляет собой сочетание двух других значений, тогда общая формула для этого будет:
\[
mixture = a \cdot value_1 + b \cdot value_2
\]
Очевидно, что начальное и конечное значения `a` и `b` должны быть `a=1, b=0`, чтобы в начале получать вывод 100% первого показателя и 0% второго; и `a=0, b=1`, чтобы в конце получать 0% value 1 и 100% value 2. В дополнение, мы не хотим чтобы "a" и "b" были независимыми, в коем случае можно было бы присвоить им любые значения и на выводе получить, например, 100% первого показателя **и** 100% второго. В принципе, с последним все ок, но в случае кривых Безье, мы всегда должны получать значение *между* двух крайностей, потому нельзя присвоить `a` и `b` значения, которые бы вместе составляли суму более 100% на выводе, что можно записать как:
\[
m = a \cdot value_1 + (1 - a) \cdot value_2
\]
С этим у нас есть гарантия, что мы не получим суму значений пропорций более 100%. Мы ограничиваем значение `a` интервалом [0,1], потому всегда получаем вывод из пропорционального смешения двух показателей, с сумой смесителей не превышающей 100%.
Но... что если мы воспользуемся этой формой, предполагающей что мы всегда будем использовать значения а между 0 и 1, для подсчета точек лежащих вне стандартного интервала? Наши вычисления пойдут по откосной? Что ж... не то чтобы, мы попросту увидим больше.
В случае кривой Безье, продление интервала просто продляет развитие кривой. Кривые Безье всего лишь сегменты полиноминальных кривых. Выбирая более широкий отрезок — мы можем проследить дальнейшее развитие кривой. И как же это выглядит?
Ниже представлены графики кривых Безье, зарисованных "обычным путем", по верх зарисовки кривых на которых они лежат, полученных путем расширения интервала значений `t`. Как мы можем наблюдать, кривая Безье есть частью большей формы, скрытой за границами нашего интервала, которую мы так-же можем регулировать путем перемещения контрольных точек.
<div class="figure">
<graphics-element title="Квадратный бесконечный интервал кривой Безье" src="./extended.js" data-type="quadratic"></graphics-element>
<graphics-element title="Кубический бесконечный интервал кривой Безье" src="./extended.js" data-type="cubic"></graphics-element>
</div>
В области компьютерной графики, существуют множество кривых, которые действуют по противоположному кривым Безье принципу: вместо фиксированного интервала и свободного выбора контрольных точек формирующих развитие искривлений, они фиксируют форму кривой, предоставляя возможность выбора интервала. Отличным примером последней есть [кривая "Spiro"](https://levien.com/phd/phd.html), которая частично базируется на [спирали Корню](https://ru.wikipedia.org/wiki/%D0%9A%D0%BB%D0%BE%D1%82%D0%BE%D0%B8%D0%B4%D0%B0), также известной как [спираль Эйлера](https://ru.qaz.wiki/wiki/Euler_spiral) (* в оригинале другая [ссылка на Корню и Эйлера](https://en.wikipedia.org/wiki/Euler_spiral) ). Эту эстетически приятную кривую можно встретить в нескольких графических пакетах: [FontForge](https://fontforge.org/en-US/) и [Inkscape](https://inkscape.org). Ее даже используют в дизайне шрифтов, например в начертания шрифта Inconsolata.

View File

@@ -0,0 +1,72 @@
# Контроль над кривыми Безье, часть 2: Соотносительные Безье
Мы можем расширить степень влияния на кривые Безье, "соотнеся" их составляющие: подразумевается добавление в формулу коэффициента соотношений (* в оригинале "ratio" — соотношение) в довесок к уже используемым весам.
Как и прочее, воплощение этого коэффициента не должно составить нам особого труда. Тогда как обычная функция:
\[
Bézier(n,t) = \sum_{i=0}^{n} \binom{n}{i} \cdot (1-t)^{n-i} \cdot t^{i} \cdot w_i
\]
Функция для соотносительных кривых Безье имеет два дополнительных термина:
\[
Rational~Bézier(n,t) = \frac{ \sum_{i=0}^{n} \binom{n}{i} \cdot (1-t)^{n-i} \cdot t^{i} \cdot w_i \cdot BLUE[ratio_i] }{ BLUE[ \sum_{i=0}^{n} \binom{n}{i} \cdot (1-t)^{n-i} \cdot t^{i} \cdot ratio_i ] }
\]
Первый из добавочных терминов, представляет собой дополнительный "вес" для каждой координаты, Например если наши значения соотношений [1, 0.5, 0.5, 1], тогда <code>частица<sub>0</sub> = 1</code>, <code>частица<sub>1</sub> = 0.5</code> и т.д., и на практике ничем не отличается от использования дополнительного "вес"-а. Пока ничего особо выдающегося.
Тем не менее, второй из добавленных терминов, составляет всю важность разницы. В процессе компиляции точек на кривой, мы компилируем "нормальные" значения Безье, а далее елим_ их на значение Безье с учетом только соотношений (частиц), без учета весов.
Это создает неожиданный эффект: превращает наш полиноминал во что-то, что полиноминалом более не является. Теперь этот тип кривой есть супер классом полиноминала, что позволяет производить крутые вещи, на которые кривые Безье не способны сами по себе. Например обозначить круг посредством такой кривой (что, как мы убедимся чуть погодя, категорически невозможно используя стандартную кривую Безье).
Но лучшим объяснением, как и всегда, является наглядная демонстрация: давайте посмотрим на эффект "соотносительности" нашей кривой Безье на примере интерактивного графика соотнесительной кривой. Следующий график показывает кривую Безье из предыдущей секции, "обогащенную" факторами соотношений для каждой координаты. Чем ближе к нулю мы выставляем значения этих факторов — тем меньше влияния оказывают соответственные контрольные точки на начертания кривой, и, соответственно, чем выше эти показатели — тем больше влияния контрольная точка оказывает на изгиб кривой. Попробуйте сами:
<graphics-element title="Соотносительная кубическая кривая Безье" src="./rational.js">
<input type="range" min="0.01" max="2" value="1" step="0.01" class="ratio-1">
<input type="range" min="0.01" max="2" value="1" step="0.01" class="ratio-2">
<input type="range" min="0.01" max="2" value="1" step="0.01" class="ratio-3">
<input type="range" min="0.01" max="2" value="1" step="0.01" class="ratio-4">
</graphics-element>
Вы можете думать о значениях соотношений, как о показателе силы притяжения соответствующей точки. Чем выше сила притяжения, тем больше наша кривая будет стремится к этой точке. Вы также можете наблюдать, что одинаковое увеличение или уменьшение всех показателей не оказывает никакого эффекта на результат... схоже с гравитацией: если значения остаются одинаковыми относительно друг-друга, на выводе ничего не меняется. Значения соотношений определяют влияние каждой координаты _относительно всех остальных координат_.
<div class="howtocode">
### Имплементация соотносительных кривых
Дополнение кода из предыдущей секции для учета этой функциональности фактически тривиально:
```
function RationalBezier(2,t,w[],r[]):
t2 = t * t
mt = 1-t
mt2 = mt * mt
f = [
r[0] * mt2,
2 * r[1] * mt * t,
r[2] * t2
]
basis = f[0] + f[1] + f[2]
return (f[0] * w[0] + f[1] * w[1] + f[2] * w[2])/basis
function RationalBezier(3,t,w[],r[]):
t2 = t * t
t3 = t2 * t
mt = 1-t
mt2 = mt * mt
mt3 = mt2 * mt
f = [
r[0] * mt3,
3 * r[1] * mt2 * t,
3 * r[2] * mt * t2,
r[3] * t3
]
basis = f[0] + f[1] + f[2] + f[3]
return (f[0] * w[0] + f[1] * w[1] + f[2] * w[2] + f[3] * w[3])/basis
```
Вот и все что требуется.
</div>