Каждая "иголка" — это линия длиной FirNeedleLength
пикселов, отклоняющаяся от направления прямой на угол FirNeedleAngle
градусов. "Иголки" отклоняются попеременно то в одну, то в другую сторону от прямой. В процедуре Line
сначала рассчитываются смещения координат конца "иголки" относительно начала и результаты помещаются в глобальные переменные DX1
, DY1
, DX2
, DY2
. Переменная Counter
служит для определения номера точки. Перед вызовом LineDDA
она инициализируется нулем. Затем вызывается функция LineDDA
, в качестве одного из параметров которой передается указатель на функцию обратного вызова LineDrawFir
. В результате этого функция LineDrawFir
будет вызвана последовательно для каждого из пикселов, составляющих линию, начиная с (X1, Y1). LineDrawFir
ведет подсчет пикселов, каждый раз увеличивая Counter
на единицу. Если остаток от деления номера точки на 10 равен 0, рисуется "иголка", отклоняющаяся в положительном направлении, если 5 — в отрицательном. В остальных случаях не рисуется ничего. Так получается "елочка".
1.3.4.2. "Резиновая" линия и растровые операции
Теперь нужно дать пользователю возможность рисовать линии. Для этого мы используем стандартную "резиновую" линию: пользователь нажимает левую кнопку мыши и, удерживая ее, передвигает мышь. До тех пор, пока кнопка удерживается, за курсором тянется линия. Как только пользователь отпускает кнопку, линия "впечатывается" в рисунок.
Сама по себе реализация "резиновой" линии очень проста: при наступлении события OnMouseDown
запоминаются координаты начала линии и взводится флаг, показывающий, что включен режим рисования "резиновой" линии. Также запоминаются координаты конца отрезка, который на данный момент совпадает с началом. В обработчике OnMouseMove
, если включен режим рисования "резиновой" линии, стирается линия со старыми координатами конца и рисуется с новыми. При наступлении OnMouseUp
программа выходит из режима рисования "резиновой" линии, рисуя окончательный ее вариант с текущими координатами конца.
Самое сложное в этой последовательности действий — стереть нарисованную ранее линию. Если бы у нас был однородный фон, можно было бы просто нарисовать старую линию еще раз цветом фона — это выглядело бы как ее стирание. Но поскольку фон не однородный, а составлен из нарисованных ранее линий, этот способ мы применить не можем.
Для решения этой задачи мы здесь рассмотрим самый простой метод — инверсное рисование (более сложный метод будет рассмотрен чуть позже). При этом каждая точка, принадлежащая линии, закрашивается не каким-либо фиксированным цветом, а инвертируется (т. е. к текущему цвету точки применяется операция not
). Для стирания линии просто рисуем ее еще раз: двойная инверсия восстанавливает предыдущий цвет точек (not not X = X
для любого X).
При рисовании пером и кистью GDI позволяет использовать различные R2_COPYPEN
, в которой цвет фона игнорируется, а результирующий цвет пиксела совпадает с цветом пера или кисти. Изменить растровую операцию можно с помощью функции SetROP2
(двойка в названии функции показывает, что устанавливаемая растровая операция имеет два аргумента — цвет рисования и цвет фона: при выводе растровых рисунков могут применяться растровые операции с тремя аргументами — см. функцию BitBlt
). Нас будет интересовать операция R2_NOT
, которая инвертирует фоновый цвет, игнорируя цвет пера или кисти.
Растровая операция влияет на все, что рисуется с помощью пера и кисти, т. е. на рисование границ фигур и их заливку. Кроме того, растровая операция влияет также на результат работы функции SetPixel
(и, соответственно, изменение цвета с помощью Canvas.Pixels[X, Y]
), т. к. эта операция выполняется с мощью кистей.
Код, рисующий "резиновую" линию, приведен в листинге 1.59.
procedure TLinesForm.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then begin
OldX:= X;
OldY:= Y;
BegX:= X;
BegY:= Y;
LineDrawing:= True;
end;
end;