Partilhar via


Amostra de apagamento de tinta

Este aplicação baseia-se no exemplo da coleção de tinta, demonstrando a eliminação de traços de tinta. O exemplo fornece ao utilizador um menu com quatro modos à escolha: modo tinta, apagar em pontas, apagar em interseções e apagar traços.

No modo ativar a tinta, o objeto InkCollector coleta tinta conforme mostrado em Ink Collection Sample.

Em um modo de apagamento, segmentos de traços de tinta existentes que o usuário toca com o cursor são apagados. Além disso, as cúspides ou intersecções podem ser marcadas com um círculo vermelho.

As partes mais interessantes deste exemplo estão no manipulador de eventos OnPaint do formulário InkErase e nas funções de apagamento que são chamadas do manipulador de eventos OnMouseMove do formulário.

Circundando as Cúspides e Interseções

O manipulador de eventos OnPaint do formulário primeiro pinta os traçados e, dependendo do modo de aplicativo, pode localizar e marcar todas as cúspides ou interseções com um pequeno círculo vermelho. Uma cúspide marca o ponto onde um curso muda de direção abruptamente. Uma interseção marca um ponto onde um traço se cruza consigo mesmo ou com outro traço.

O evento Paint ocorre sempre que um controle é redesenhado.

Observação

O exemplo força o formulário a redesenhar-se sempre que um traçado é apagado ou quando o modo da aplicação muda, usando o método Refresh do formulário.

 

private void InkErase_OnPaint(object sender, PaintEventArgs e)
{
    Strokes strokesToPaint = myInkCollector.Ink.Strokes;

    myInkCollector.Renderer.Draw(e.Graphics, strokesToPaint);

    switch (mode)
    {
        case ApplicationMode.CuspErase:
            PaintCusps(e.Graphics, strokesToPaint);
            break;
        case ApplicationMode.IntersectErase:
            PaintIntersections(e.Graphics, strokesToPaint);
            break;
    }
}

Em PaintCusps, o código itera através de cada cúspide em cada traçado e desenha um círculo vermelho em torno dela. A propriedade PolylineCusps do traçado retorna os índices dos pontos dentro de um traçado que correspondem a cúspides. Além disso, observe o Renderermétodo de InkSpaceToPixel do objeto, que converte o ponto em coordenadas relevantes para o método DrawEllipse.

private void PaintCusps(Graphics g, Strokes strokesToPaint)
{
    foreach (Stroke currentStroke in strokesToPaint)
    {
        int[] cusps = currentStroke.PolylineCusps;

        foreach (int i in cusps)
        {
            Point pt = currentStroke.GetPoint(i);

            // Convert the X, Y position to Window based pixel coordinates
            myInkCollector.Renderer.InkSpaceToPixel(g, ref pt);

            // Draw a red circle as the cusp position
            g.DrawEllipse(Pens.Red, pt.X-3, pt.Y-3, 6, 6);
        }
    }
}

Em PaintIntersections, o código itera através de cada traçado para encontrar as suas intersecções com todo o conjunto de traçados. Observe que ao método FindIntersections do traçado é passada uma coleção de Strokes e retorna uma matriz de valores de índice de ponto flutuante que representam as interseções. O código então calcula uma coordenada X-Y para cada interseção e desenha um círculo vermelho em torno dela.

private void PaintIntersections(Graphics g, Strokes strokesToPaint)
{
    foreach (Stroke currentStroke in strokesToPaint)
    {
        float[] intersections =            currentStroke.FindIntersections(strokesToPaint);
    }
}

Manuseamento de uma caneta com duas extremidades

Três manipuladores de eventos são definidos para o objeto InkCollector para os eventos CursorDown, NewPacketse Stroke. Cada tratador de eventos verifica a propriedade Invertida do objeto Cursor para ver qual extremidade da caneta está a ser usada. Quando a caneta está invertida:

  • O método myInkCollector_CursorDown torna o traçado transparente.
  • O método myInkCollector_NewPackets apaga traçados.
  • O método myInkCollector_Stroke cancela o evento. NewPackets são gerados antes do evento Stroke.

Rastreando o cursor

Se o usuário estiver usando uma caneta ou um mouse, MouseMove eventos são gerados. O manipulador de eventos MouseMove primeiro verifica se o modo atual é um modo de apagamento e se algum botão do mouse é pressionado, e ignora o evento se esses estados não estiverem presentes. Em seguida, o manipulador de eventos converte as coordenadas de pixel do cursor em coordenadas de espaço de tinta usando o Renderer método de PixelToInkSpace do objeto e chama um dos métodos de apagamento do código, dependendo do modo de apagamento atual.

Apagando traços

O método EraseStrokes usa a localização do cursor no espaço de tinta e gera uma coleção de traçados que estão dentro de HitTestRadius unidades. O parâmetro currentStroke especifica um objeto Stroke que não deve ser excluído. Em seguida, a coleção de traçados é excluída do colecionador e a forma é redesenhada.

private void EraseStrokes(Point pt, Stroke currentStroke)
{
    Strokes strokesHit = myInkCollector.Ink.HitTest(pt, HitTestRadius);

    if (null!=currentStroke && strokesHit.Contains(currentStroke))
    {
        strokesHit.Remove(currentStroke);
    }

    myInkCollector.Ink.DeleteStrokes(strokesHit);

    if (strokesHit.Count > 0)
    {
        this.Refresh();
    }
}

Apagar em cruzamentos

O método EraseAtIntersections itera sobre cada traçado que cai dentro do raio de teste e gera uma matriz de interseções entre esse traçado e todos os outros traçados na coleção. Se nenhuma interseção for encontrada, esse traçado inteiro será excluído; caso contrário, localiza-se o ponto mais próximo do traçado ao ponto de ensaio e, a partir daí, localizam-se as intersecções de ambos os lados do ponto, descrevendo o segmento a remover.

O método StrokeSplit do objeto é utilizado para separar o segmento do restante do traçado e depois o segmento é excluído, deixando o restante do traçado intacto. Como em EraseStrokes, o formulário é redesenhado antes que o método retorne.

private void EraseAtIntersections(Point pt)
{
    Strokes strokesHit = myInkCollector.Ink.HitTest(pt, HitTestRadius);

    foreach (Stroke currentStroke in strokesHit)
    {
        float[] intersections = currentStroke.FindIntersections(myInkCollector.Ink.Strokes);
        ...
        float findex = currentStroke.NearestPoint(pt);
        ...
        strokeToDelete = currentStroke.Split(intersections[i]);
        ...
    }
    ...
}

Apagar em Cusps

Para cada traçado que se enquadra no raio de teste, o método EraseAtCusps recupera a matriz de cúspides do Stroke do método PolylineCusps do objeto. Cada extremidade do traçado também é uma cúspide, portanto, se o traço tiver apenas duas cúspides, então todo o traço é excluído; caso contrário, localiza-se o ponto mais próximo do traçado ao ponto de ensaio e, a partir daí, localizam-se as intersecções de ambos os lados do ponto, descrevendo o segmento a remover.

O método StrokeSplit do objeto é usado para separar o segmento do resto do traçado e, em seguida, o segmento é excluído, deixando o restante do traçado intacto. Como em EraseStrokes, o formulário é redesenhado antes que o método retorne.

private void EraseAtCusps(Point pt)
{
    ...
    strokesHit = myInkCollector.Ink.HitTest(pt, HitTestRadius);
    
    foreach (Stroke currentStroke in strokesHit)
    {
        int[] cusps = currentStroke.PolylineCusps;
        ...
        float findex = currentStroke.NearestPoint(pt);
        ...
        strokeToDelete = currentStroke.Split(cusps[i]); 
        myInkCollector.Ink.DeleteStroke(strokeToDelete);
        ...
    }
    ...
}

Fechando o formulário

O método Dispose do formulário elimina o objeto InkCollector, myInkCollector.