Чтение онлайн

ЖАНРЫ

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

 FirNeedleAngle = 30;

 //Длина иголки

 FirNeedleLength = 8;

var

 Counter: Integer; // Счетчик точек линии

 // Вспомогательные переменные для построения "елочки"

 DX1, DY1, DX2, DY2: Integer;

// Линия в виде "елочки"

procedure LineDrawFir(X, Y: Integer; Canvas: TCanvas); stdcall;

begin

 with Canvas do case Counter mod 10 of

0: begin

MoveTo(X, Y);

LineTo(X + DX1, Y + DY1);

end;

5:

begin

MoveTo(X, Y);

LineTo(X + DX2, Y + DY2);

end;

 end;

 Inc(Counter);

end;

procedure TLinesForm.Line(X1, Y1, X2, Y2: Integer);

var

 Angle: Extended;

begin

 case RGroupLine.ItemIndex of

...

4:

 begin

Counter := 0;

Angle := ArcTan2(Y2 - Y1, X2 - X1);

DX1 := Round(FirNeedleLength *

Cos(Angle + Pi / 180 * FirNeedleAngle));

DY1 := Round(FirNeedleLength *

Sin(Angle + Pi / 180 * FirNeedleAngle));

DX2 := Round(FirNeedleLength *

Cos(Angle - Pi / 180 * FirNeedleAngle));

DY2 := Round(FirNeedleLength *

Sin(Angle - Pi / 180 * FirNeedleAngle));

LineDDA(X1, Y1, X2, Y2, @LineDrawFir, Integer(Canvas));

end;

 end;

end;

Каждая "иголка" —

это линия длиной
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.

Листинг 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;

procedure TLinesForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);

begin

 if LineDrawing and ((X <> OldX) or (Y <> OldY)) then

with Canvas do

 begin

SetROP2(Handle, R2_NOT);

Line(BegX, BegY, OldX, OldY); // Стираем старую линию.

Line(BegX, BegY, X, Y); // Рисуем новую.

OldX := X;

OldY := Y;

 end;

end;

procedure TLinesFom.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

 if (Button = mbLeft) and LineDrawing then

 begin

case RGroupLine.ItemIndex of

2: Canvas.Pen.Color := clBlue;

3: begin

Canvas.Brush.Color := clRed;

Canvas.Pen.Color := clRed;

end;

4: Canvas.Pen.Color := clGreen;

end;

Line(BegX, BegY, X, Y);

Поделиться с друзьями: