Быстрая штриховка векторных фигур

  Быстрая заливка фигур векторной графики может быть преобразована в штриховку, причём штриховка эта может вестись как горизонтальными, так и вертикальными линиями с различным интервалом.
 
  Штриховка может пригодиться не только для заполнения замкнутых фигур в обычной векторной графике, но и в программах, иллюстрирующих решение задач по стереометрии. В них штриховкой показываются те или иные сечения. И если сплошная заливка должна быть полупрозрачной, с тем, чтобы через неё были видны и другие сечения, и другие части геометрической фигуры, то  от штриховки полупрозрачности не требуется – другие части фигуры будут видны сквозь просветы между линиями штриховки.
  И в этом плане штриховка даже бывает предпочтительнее. Вопрос только в том, как её наиболее простым способом организовать.

  АЛГОРИТМ СПЛОШНОЙ ЗАЛИВКИ ЛИНИЯМИ

  Однако вначале вспомним тот алгоритм, по которому осуществляется быстрая заливка векторных фигур http://proza.ru/2020/10/16/1478

  Для заливки требуется замкнутый контур известного цвета и указание на цвет заливки. И тот, и другой цвета на рисунке должны быть уникальными – только так программа сможет найти контур и успешно его залить, не путая пиксели контура и пиксели заливки с другими окрашенными пикселями.
 
  Заливка начинается с затравки – указываются координаты того пикселя внутри контура, с которого начинается заливка.
  Оператором C = Form1.Picture1.Point(X, Y) определяется цвет этой точки. Если окажется, что начальный пиксель имеет цвет контура, то заливка не производится. Если же с затравочным пикселем всё в порядке, то определяется затравочная горизонтальная линия – она проходит через затравочный пиксель и идёт от контура слева до контура справа, имея координаты концов I1 и I2.
  Эта линия, однако, не рисуется, а заносится в стек. Точнее, в стек она  заносится в двух экземплярах – как (Y I1 I2 1) и как (Y I1 I2 -1).
  1 здесь указание на то, что заливка развивается вниз, а -1 – указание на развитие заливки вверх.
  Далее стек читается и производится основная работа – от линии, прочитанной из стека, ищутся продолжения заливки (вверх и вниз). Продолжения тут же записываются в стек. После того, как все продолжения найдены, линия, прочитанная из стека, прочерчивается.
  Так, мало-помалу, находятся все продолжения заливки и чертятся все линии, находящиеся в стеке, и на этом заливка заканчивается. 

  Обратите внимание, ничто в этом алгоритме не запрещает нам, двигаясь вверх или вниз по вертикали, двигаться не на смежную линию пикселей, а пропускать несколько линий. Единственная существующая опасность, это, сделав шаг, например 3, не попасть на линию контура, а выйти за пределы контура. Чтобы предотвратить такое развитие событий линию контура нужно сделать широкой.
   Только если линия контура будет у нас достаточно широкая (тоже 3 пикселя), то, как оказывается, попадание на контур будет нам гарантировано.
  На этом принципе и делается штриховка, заменяющая заливку – мы  устанавливаем шаг штриховки, и устанавливаем такую же толщину для линии контура. Более того, начальную точку штриховки можно корректировать, устанавливая её по Y на пиксель, кратный трём, или на один пиксель выше или ниже, отделяя таким образом линии штриховок друг от друга. Этот приём позволяет показывать одну штриховку позади другой.

  Все эти нюансы используются в программе «Стереометрические модели». Модуль, осуществляющий штриховку в этой программе, приведён ниже, в Приложении, и вы можете подробно рассмотреть весь этот алгоритм. Там ширина контура задаётся параметром DWo, с этой величиной связывается и интервал между линиями штриховки, и вертикальный сдвиг линий на плюс/минус один пиксель. XX и YY – координаты начального пикселя (до корректировки его положения по вертикали), Cb = CC – цвет контура. Уникальность цвета штриховки C обеспечивается добавлением 4 бита к цвету контура.

  Осталось объяснить, каким образом горизонтальную штриховку можно превратить в вертикальную.
  И это очень просто – при вызове функции на место координаты XX мы посылаем координату Y, а на место YY – координату X, и одновременно переустанавливаем флаг направления штриховки из положения FZAL = 1 в положение FZAL = 2. В операторах в конце подпрограммы видно, что такая переустановка сразу меняет местами X,Y и при определении цвета пикселя, и в направлении линий штриховки.
  При выставленном флаге FlagZ=1 (так же как и флаг FZAL, это - глобальная переменная) заливка делается сплошной. Всего одна строка в приведённом ниже  тексте позволяет сделать это.

   ВЫВОД

  Штриховка графических элементов тонкими линиями может осуществляться по общему быстрому и удобному алгоритму, как с вертикальным, так и с горизонтальным расположением штрихов. Интервал между линиями такой штриховки требует лишь некоторого увеличения толщины контура фигуры.


ПРИЛОЖЕНИЕ. Подпрограмма линейной штриховки.
'==========
Public Sub ZAL1(XX As Integer, YY As Integer, CC As Long, DWo As Integer) 'заливка внутри контура заданного цвета
Dim Xo As Integer, Yo As Integer, Xm As Integer, Ym As Integer, Ca As Long, Cb As Long, C As Long
Dim Y As Integer, X1 As Integer, X2 As Integer, K As Integer, P As Integer, H As Integer
Dim I As Integer, I1 As Integer, I2 As Integer, DW As Integer, Pm As Integer
Dim V1 As Integer, Vo As Integer, M As Integer

  Cb = CC: Ca = CC + 8: 'If Cb = Ca Then Exit Sub
  DW = Form1.Picture1.DrawWidth: Form1.Picture1.DrawWidth = 1:
  Pm = 5000 'размер стека
  H = 400: Xo = XX - H: Xm = XX + H: Yo = YY - H: Ym = YY + H 'границы области заливки
  Y = YY: I = XX: Y = Y / 3: Y = Y * 3:
  If DWo = 1 Or DWo = 4 Or DWo = 7 Then Y = Y + 1:
  If DWo = 3 Or DWo = 6 Or DWo = 9 Then Y = Y - 1:
  GoSub 50: If C = Cb Then Exit Sub:
  M = 4: If DWo > 6 Then M = 5:
      If DWo < 4 Then M = 3:
  If FlagZ =1 Then M=1 'при установленном флаге заливка делается сплошной

2 I = I - 1: 'делаем линию-затравку ============
  If I < Xo Then Form1.Picture1.DrawWidth = DW: Exit Sub:
  GoSub 50: If C = Cb Then Else GoTo 2
  I1 = I + 1
3 I = XX
4 I = I + 1:
  If I > Xm Then Form1.Picture1.DrawWidth = DW: Exit Sub:
  GoSub 50: If C = Cb Then Else GoTo 4
  I2 = I - 1:
5  P = 2: YXXk(1, 0) = Y: YXXk(1, 1) = I1: YXXk(1, 2) = I2: YXXk(1, 3) = -1:
         YXXk(2, 0) = Y: YXXk(2, 1) = I1: YXXk(2, 2) = I2: YXXk(2, 3) = 1: 'записываем затравку в стек
         GoTo 30
         
10 If P < 1 Then Form1.Picture1.DrawWidth = DW: Exit Sub 'ВЫХОД
   If P > Pm Then Pm = P
   K = YXXk(P, 3): Y = YXXk(P, 0) + M * K: X1 = YXXk(P, 1): X2 = YXXk(P, 2): P = P - 1 'читаем из стека
   If Y < Yo Or Y > Ym Then GoTo 10
   I = X1: GoSub 50:
   
   If C <> Cb Then 'ищем I1 слева =======================
12 I = I - 1: If I < Xo Then GoTo 13
   GoSub 50: If C = Cb Then Else GoTo 12
13 I1 = I + 1: I = X1
14 I = I + 1: If I > Xm Then GoTo 15
   GoSub 50: If C = Cb Then Else GoTo 14
15 I2 = I - 1
   If I1 < X1 - 1 Then P = P + 1: YXXk(P, 0) = Y: YXXk(P, 1) = I1: YXXk(P, 2) = X1 - 2: YXXk(P, 3) = -K:
   GoTo 26: End If
   
22 I = I + 1: If I > X2 Then GoTo 10 'ищем I1 справа ==================
   GoSub 50: If C = Cb Then GoTo 22 Else I1 = I:
23 I = I + 1: If I > Xm Then GoTo 25
   GoSub 50: If C = Cb Then Else GoTo 23
25 I2 = I - 1:
26 If I2 > X2 + 1 Then P = P + 1: YXXk(P, 0) = Y: YXXk(P, 1) = X2 + 2: YXXk(P, 2) = I2: YXXk(P, 3) = -K:
   If X2 > I2 + 1 Then P = P + 1: YXXk(P, 0) = Y - M * K: YXXk(P, 1) = I2 + 1: YXXk(P, 2) = X2: YXXk(P, 3) = K:
   P = P + 1: YXXk(P, 0) = Y: YXXk(P, 1) = I1: YXXk(P, 2) = I2: YXXk(P, 3) = K: 'основной путь
30   If FZAL = 1 Then Form1.Picture1.Line (I1, Y)-(I2 + 1, Y), Ca: GoTo 10 'заливаем линию
     Form1.Picture1.Line (Y, I1)-(Y, I2 + 1), Ca: GoTo 10 'заливаем линию по вертикали
Exit Sub '======
50 If FZAL = 2 Then C = Form1.Picture1.Point(Y, I) Else C = Form1.Picture1.Point(I, Y)
   If C < 0 Then C = Cb
   Return   
End Sub
'==========

Программу "Стереометрические модели" можно скачать тут - http://disk.yandex.ru/d/T5-M4ZNJabZ9yw
Задания и решения задач ЕГЭ по стереометрии - http://disk.yandex.ru/d/OYik3rOqtxP4dQ


Рецензии