Как сделана кисть с нелинейной прозрачностью

  В предыдущей статье рассказывалось о кисти с нелинейной динамикой прозрачности, о том, как удобно с ней работать и демонстрировался превосходный результат этой работы http://proza.ru/2023/06/29/606
  Теперь мы поговорим о математике и о тех алгоритмах, которые создают нелинейную динамику прозрачности.

  Напомню, как организуется полупрозрачность кисти во время её работы по рисунку. Пусть кисть у нас напитана активным цветом Ca, а цвет пикселя на рисунке, который ей нужно покрасить, имеет цвет Co. Обращение к функции ccRGB поможет нам определить цвет покраски –

  C = ccRGB(Ca, Co, A)     здесь A – плотность кисти, число от 0 до 1,
                а величина 1-A называется прозрачностью кисти

Функция, к которой мы обращаемся, берёт указанную долю A первого цвета и складывает с долей 1-A, взятой от второго цвета.
  Значение плотности кисти можно задать заранее и не менять во время работы. Динамическая кисть ведёт себя иначе – она учитывает заданное значение, но при этом меняет плотность в процессе своей работы. Например, при медленном движении кисть имеет плотность, близкую к заданной, а при большой скорости стилуса плотность кисти уменьшается, и след становится более прозрачным.
  Обычно динамическая кисть создаёт плавное изменение прозрачности следа, если же прозрачность в каком-то диапазоне скоростей меняется резко, то мы говорим о нелинейном изменении прозрачности.

    ИСПОЛЬЗОВАНИЕ ТАЙМЕРА

  Непосредственно узнать о скорости движения стилуса мы не можем, таких стандартных функций не предусмотрено, поэтому приходится включать таймер, на языке программирования он называется Timer, и роль его заключается в том, чтобы генерировать события с тем интервалом времени, который для него установлен. Минимальный интервал, который можно задать это 15 миллисекунд, а поскольку мы фиксируем быстрые события, то задаём этот минимальный интервал.
  О том, как обрабатываются события таймера подробно рассказывалось в этой статье http://proza.ru/2023/06/23/679  В настоящее время эта обработка немного усложнилась и стала более гибкой, но результат её остался прежним – при работе таймера имеется постоянно обновляемая переменная – Vtimer, значение которой является усреднённой величиной и указывает на то, за сколько интервалов времени стилус совершает три перехода от одного пикселя к другому.

  При использовании тач-панели этот «другой» пиксель является соседним, но стилус в своём движении по планшету пиксели пропускает – сообщает не об одном переходе, а о двух таких переходах.
  Наверное, его можно настроить по-другому, но я этого делать не умею, поэтому пользуюсь тем, что доступно.
  Как бы то ни было, но это значит, что при использовании стилуса значение Vtimer нужно делить на два. Учитывая все изложенные обстоятельства, получаем, что величина Vtimer=100 соответствует скорости движения стилуса

  3 / (0.015*Vtimer/2) = 2 пикселя в секунду, и это очень медленная скорость.
  А вот если значение Vtimer равно 20 (подумайте, как это немного, набрать только 20 интервалов за три перемещения), то тогда скорость движения получается равной 20 пикселей в секунду, это даже не самая быстрая, а вполне рабочая, умеренная скорость.
  Ну, в общем вы поняли, определить скорость быстрого движения стилуса с помощью таймера совсем не просто - если напрямую использовать накопленные интервалы за время одного перемещения, то при быстром движении стилуса мы будем иметь дело с последовательностью чередующихся нулей и единиц, причём нулей будет больше, чем единиц. Тем не менее, и такая последовательность программой обрабатывается. Текст программы, обрабатывающей событие перехода с одного пикселя на другой приводится ниже статьи, в приложении.

   ПРАКТИЧЕСКОЕ ИСПОЛЬЗОВАНИЕ ДИНАМИКИ КИСТИ

  Но давайте посмотрим на иллюстрацию, на кнопочки, управляющие характеристиками динамической кисти. Об активном цвете и о кнопке с названием Плотность, уже говорилось. Размер пятна величина условная, обычно эта величина определяет радиус пятна рисования (в пикселях), но в некоторых случаях радиус пятна может быть вдвое большим, или быть ещё большего размера, вплоть до значения 20. Пятно диаметром 40 пикселей, это очень большое пятно.
  Другие кнопки расположены на левой панели. Вы видите там номер кисти, включённую опцию динамического расширения следа, и включённую опцию «В» заглавное на кнопке буквенных опций. Заглавное «В» означает, что включена динамическая зависимость прозрачности следа от скорости движения стилуса.
 
  Движок на кнопке под названием «Плавность» установлен в крайнее левое положение «0». В зависимости от позиции движка прозрачность зависит от скорости по-разному – в том положении, что на иллюстрации, след при быстром движении становится заметно прозрачным, а в позиции «16» прозрачность следа никак не меняется. Крайняя правая позиция «17» не делает след прозрачнее, напротив того, при увеличении скорости движения стилуса немного увеличивается плотность следа.
  Однако давайте посмотрим, какими формулами организована такая зависимость. Пусть A это выставленное значение плотности следа. Получается оно с учётом выставленного размера кисти (в нашем случае это значение 1), и в итоге, если на кнопке стоит цифра 6, то плотность A=0.286, для семи A=0.4, и смотрите, что мы делаем с этим числом далее - 

A2 = A: Y = Vtimer
X = (1 + 4 / (Vtimer + 1))
X = X ^ (4 - FC54 / 4)   
A2 = A2 / X: If A2 > 1 Then A2 = 1

К чему такие сложности, спросите вы?  А затем, чтобы на скоростях более 4-х пикселей в секунду плотность следа регулировалась – шла бы к нулю при увеличении скорости, и она себя так и ведёт – смотрите на график с красными линиями в левом углу иллюстрации. Сверху, в уменьшенном в два раза масштабе показан аналогичный график для самой большой плотности, которую можно выставить на кнопке.
  И, казалось бы, выстави подходящую плотность и работай с ней.

  Практика показывает, что это не во всём удобно. Рисуя динамической кистью морские волны я постоянно досадовал на то, что на медленной скорости, когда след становился узким, он был слишком прозрачным. А когда я выставлял большую плотность, то трудно было достичь прозрачности с широким пятном на больших скоростях.
  Потому и появилась идея внести в динамическую кисть опцию нелинейности.

  Опция нелинейности начинает работать, как только скорость движения стилуса станет ниже 20-ти пикселей в секунду. На иллюстрации, на графике слева показана зависимость плотности следа от величины Vtimer и подписаны значения скорости стилуса. Нижние чёрная и серая кривые получены для выставленной плотности 6 и 7 без включения нелинейности, а верхние кривые получаются, когда опцию нелинейности мы включим. При включённой нелинейности след на малых скоростях становится очень и очень плотным.
  Следы кисти, работающей в таком режиме, показаны на иллюстрации. Вначале медленными движениями была проведена плотная линия, а затем я ускорил движения стилуса и сделал вокруг линии полупрозрачный ореол.

  Режим нелинейной плотности следа включается кликом правой кнопки мыши по кнопке Плотность. Работает этот режим на позициях 0-4. На поз.0 он работает так, как описано, а на позициях 2-4 получаются промежуточные значения – плотность узкого следа не очень плотная, а более умеренная.

  Но каким же образом, при включённой опции нелинейной плотности из нижних кривых A(Ftimer) получаются верхние?
  Как только Y (а эта переменная у нас равна Ftimer) окажется выше 20-ти (эта отсечка означает начало наших преобразований) программа делает следующее –

   X = 5: If CV < 8 Then X = (8 - CV) * 2 + 5
   Y = Y - 20: If Y > X * X Then Y = 1 + Y Else Y = 1 + Y * Y / X / X
   If FC54 = 0 Then A2 = A2 ^ (1 / Y)
   If FC54 = 1 Then A2 = A2 ^ (1 / Y) * 0.5 + A2 * 0.5:
   If FC54 = 2 Then A2 = A2 ^ (1 / Y) * 0.3 + A2 * 0.7:
   If FC54 = 3 Then A2 = A2 ^ (1 / Y) * 0.2 + A2 * 0.8:
   If FC54 = 4 Then A2 = A2 ^ (1 / Y) * 0.1 + A2 * 0.9:

  Программа перевычисляет найденные значения плотности, возводя их в степень, меньшую или равную единице. Степень эта равна единице в точке Ftimer=20, следовательно старая и новая кривые сшиваются друг с другом. Примерно так, как сшивается прямая, проведённая из начала координат в точку Ftimer=20 со своим собственным продолжением за эту точку (см. синюю прямую на графике иллюстрации).
  Но брать сразу прямую Y = 1 + Y и приделывать её к точке Ftimer=20 оказалось не совсем хорошо – переход от плотного следа к следу с динамической прозрачностью оказался на практике очень резким и заметным.
  Следовательно, нужно было сделать так, чтобы показатель степени отходил от единицы не сразу, а постепенно и плавно. И для этого в синюю кривую была сделана внутренняя вставка – это парабола, выходящая из точки Ftimer=20, идущая вначале вправо и поднимающаяся затем вверх до пересечения с синей прямой (см. красные фрагменты параболы построенные для разных значений выставленной плотности).

  Вы видите, как много я написал объяснений тому, что заключено в паре коротеньких формул в тексте программы. Мне подобные математические выкрутасы не то, чтобы нравятся, приятно то, что они результаты приносят.
  А результат налицо, и я уже об этом говорил.
  Итак, введение опции нелинейности в кисть, прозрачность которой динамически зависит от скорости движения стилуса, оказалось очень полезным, эффективным, и в конечном итоге удобным.
  Следовательно, не зря я так старался.

  Не зря я так старательно писал эту статью – в процессе этой работы я нашёл в программе пару ошибок и улучшил способ подключения нелинейной опции. Наконец, делая построения разных графиков, я понял, какие недочёты имеются в программе, понял, как их можно исправить, и сделал эти исправления. Написание статьи, это эффективный способ продвижения всякой творческой работы - http://proza.ru/2021/04/20/1121

_________
1.07.2023
 
ПРИЛОЖЕНИЕ

'расширение следа по таймеру =======
Public Function Ntimer(M As Integer, NN As Integer) As Integer
Static Ft As Integer, N As Integer, F As Integer, Fti As Single, R As Integer
Static K As Integer, Fto As Integer
Dim X As Single, I As Integer
 
  If M = -1 Then 'обращение при опускании стилуса на планшет
    Ftimer = 50: Ft = 0: N = -1: Ntimer = 10: Fti = 50: R = NN: K = 0:
    Vtimer = 50: F = 0: Exit Function '===
  End If '1327 max, 9 min, 665 max для кисти №1
 
  If F55b = 1 And Ftimer > 80 And Fto < 10 Or K = 2 Then
    K = 2: If Ftimer > 8 Then Ftimer = 8:
  End If: Fto = Ftimer:
  If K = 0 Then K = 1: If Ftimer < 56 Then Vtimer = 0: Ftimer = 0:
  If NN > 1300 Then Ft = 0: Ftimer = 0: Ntimer = NN: Exit Function '====
      Ft = Ft + 1: Form1.Text2 = Str(Ftimer)
      Fti = Fti * 0.7 + Ftimer * 0.3
      If Ft > 2 Then ' Ft > 9 - 3 * F53
        X = Fti * 3: 'X = Ftimer:
        If F55b = 1 Then F = 2: Vtimer = X * 0.3 + Vtimer * 0.7: 'прозрачность -B_опция
        If F55c = 1 Then 'цвет -C_опция
          If F = 0 Or F = 3 Then Vtimer = X * 0.3 + Vtimer * 0.7:
        F = 3: End If:
        If F55d = 5 Then ' D_опция
          If F = 0 Or F = 4 Then Vtimer = X * 0.3 + Vtimer * 0.7:
        F = 4: End If:
        If M = 0 Then Ft = 0: Ftimer = 0: Ntimer = 0: Exit Function '===
        If M = 1 Then X = X * X / 6
        I = FC45: If I = 15 Then I = 14:
        If M = 2 Then If FC45 < 15 Then X = X * X / (32 - I ^ 1.3)
        'X = X * X / 6 / M:
        X = NN + (700 - NN) / (1 + X):
        If N < 0 Then N = X Else N = X * 0.3 + N * 0.7 'сглаживание
        Ft = 0: Ftimer = 0:
      End If:
      If N > 0 Then Ntimer = N Else Ntimer = NN / R
End Function
'=====


Рецензии
Дмитрий, здравствуйте!
Интересная, можно сказать научная статья. Бомба!

Понравилось!

С уважением,

Владимир Войновский   04.07.2023 16:43     Заявить о нарушении
Владимир, Вы сильно преувеличиваете )) Это совсем не наука, это самая заурядная практика программирования. С ошибками и с поиском источника этих ошибок. Однако, спасибо!

Дмитрий Маштаков   04.07.2023 21:35   Заявить о нарушении
Это действительно титанический труд!

Владимир Войновский   04.07.2023 22:04   Заявить о нарушении
На это произведение написаны 4 рецензии, здесь отображается последняя, остальные - в полном списке.