Traçando à limpo
Como desenhar à mouse livre no canvas

Algumas vezes nos deparamos com desafios incríveis, no Delphi: resolver determinadas questões que implicam no uso de recursos simples, modestos e tão óbvios que, exatamente por estas “qualidades”, não constam de nenhuma bíblia, curso, apostila ou mesmo de listas de discussão. A sua simplicidade termina por inibir perguntas, afinal ninguém quer passar atestado de desconhecimento do óbvio.

Pensando nisso, resolvemos reproduzir aqui uma conversa “acontecida” entre dois de nossos mais importantes colaboradores: Batman e Sobrinho. Batman, como todos sabem, é o eterno professor de Sobrinho, que não esconde seu deslumbramento em relação à programação Delphi. Vamos à ela...

Batman, preciso de um favor: não estou conseguindo fazer uma rotina para traçar figuras a mão livre (ou mouse livre), no estilo dos antigos editores gráficos. Algo como o velho Paint do Windows já dava para quebrar um galho. Não sei nem por onde começar.

Ora sobrinho, comece sempre pelo começo. É o melhor jeito de resolver as coisas.

Ta, mas onde é o começo? Que componente eu uso? Qual dos componentes gráficos é melhor?

Sobrinho, de um modo geral, todos os componentes são bons. Para coisas gráficas, você precisa usar algum que tenha canvas. O canvas nada mais é do que a superfície (em bits) gráfica. Antigamente chamávamos de “tela”, então, quem tem um canvas, tem uma tela só para ele (o componente) e que geralmente é do mesmo tamanho que o próprio componente.

O Timage tem canvas, o TPaintBox tem também, o TBItMap idem e o Form1 também tem seu próprio canvas. Tudo o que serve para um canvas, serve para outro, pois todos são derivados do objeto TCanvas.

Então, se você quer traçar uma reta entre a coordenada 0,0 e a posição atual do mouse, use no evento OnMouseDown a seguite linha de programação:
Canvas.MoveTo(0,0); Canvas.LineTo(X, Y);

Santa programação Batman, só isso?

É sobrinho, se só quiser desenhar uma reta entre dois pontos, é tudo o que tem a fazer. Agora, se quer emendar uma linha na outra, concatenando-as a cada clique do mouse, faça o seguinte:

Crie duas constantes globais:
const
  Px: integer = 0;
  Py: integer = 0;

A seguir, mude a programação do evento OnMouseDown para:

  Canvas.MoveTo(Px,Py); Canvas.LineTo(X, Y);
  Px:= x; Py:= y;

Hummm! Entendí. E como faço para mudar a espessura da linha? Heim? Heim? Heim?

Essa é fácil, sobrinho:
  Canvas.Pen.Width:= 2;

Com isso você já pode criar um programa (as crianças vão adorar) como aquelas antigas brincadeiras de emendar pontos numerados em sequência (lembra delas, sobrinho)? Mas, se quer fazer um programa para traço “a mouse livre”, como você disse, então use o evento OnMouseMove, ao invés do OnMouseDown. E para só desenhar enquanto o botão esquerdo do mouse estiver pressionado, faça:

  if ssLeft in Shift then begin
    Canvas.Pen.Width:= 2;
    Canvas.MoveTo(Px,Py); Canvas.LineTo(X, Y);
    Px:= x; Py:= y; 
  end;

Batman, para mudar a cor seria:

  Canvas.Pen.Color:= clBlue;

Isso sobrinho, está “pegando o jeito da coisa”.

E para desenhar um círculo? Como eu faria, heim Batman?

Calma sobrinho, não queira passar o carro adiante dos bois. Você ainda nem sabe desenhar um retângulo e já quer desenhar um círculo? Primeiro aprenda o que é mais fácil. Por exemplo, para preencher uma figura que acabou se desenhar, com uma determinada cor:
  if ssRight in Shift then begin 
    Canvas.Brush.Color:= clGreen;
    Canvas.FloodFill(x,y,clBlue,fsBorder);
  end;

Se colocar esse pedaço de programação num evento OnMouseDown, então acompanhe o que acontecerá: Canvas.Brush.Color será a cor usada para “pintar” e clBlue (dentro do FloodFill, será a cor limite (sua cor azul).

Santos preenchimentos de áreas Batman, já posso fazer meu editor gráfico de última geração...

Calma sobrinho, ainda é cedo para isso. Ainda não aprendeu como fazer uma linha elástica, não é mesmo? Para isso usaremos algo já visto aqui na TILT, que é transferência de áreas gráficas. Com isso podemos montar um buffer de tela. Acompanhe o raciocínio: crie um TBitmap global...
var
  Buffer: TBitmap;

Nos eventos OnCreate e OnDestroy do Form1 coloque:

procedure TForm1.FormCreate(Sender: TObject); 
begin 
  Buffer:= TBitmap.Create;
  Buffer.Width:= Form1.ClientWidth;
  Buffer.Height:= Form1.ClientHeight;
end;                         
procedure TForm1.FormDestroy(Sender: TObject); 
begin 
  Buffer.Free;
end;

No evento OnMouseDown escreva:

  Px:= x; Py:= y; 
  Buffer.Canvas.CopyRect(rect(0,0,Buffer.Width,Buffer.Height),
    Canvas,rect(0,0,Buffer.Width,Buffer.Height));

Isso fará com que uma cópia do desenho seja arquivada no buffer. A seguir faça, no evento OnMouseMove:

  if ssLeft in Shift then begin 
    Canvas.Pen.Width:= 4; Canvas.Pen.Color:= clBlue; 
    Canvas.Draw(0,0,Buffer);
    Canvas.MoveTo(Px,Py); Canvas.LineTo(X, Y); 
  end;

O resultado será uma linha que é “esticada” até seu ponto de “ancoragem”. Fácil, rápido e sem burocracia.

Santas linhas elásticas, Batman, com isso já posso arrasar no editor gráfico.

Mas sobrinho, e o resto?

Ora Batman, alguma coisa eu quero descobrir sozinho como fazer. Senão você faz toda a parte divertida da programação, não é mesmo?

 
online