Partilhar via


Exemplo de divisor de tinta

Este exemplo é baseado no Exemplo de Coleção de Tintas. Ele mostra como usar o objeto Divider para analisar a entrada de tinta.

Para obter informações conceituais detalhadas sobre o Divisor, consulte O Objeto Divisor.

Quando o formulário é atualizado, o exemplo desenha um retângulo delimitador em torno de cada unidade analisada, dividida em palavras, linhas, parágrafos e desenhos. Além de usar cores diferentes, esses retângulos são ampliados por diferentes quantidades para garantir que nenhum dos retângulos seja obscurecido por outras pessoas. A tabela a seguir especifica a cor e o aumento de cada unidade analisada.

Unidade analisada Cor Aumento de pixels
Word
Verde
1
Linha
Magenta
3
Paragraph
Azul
5
Desenho
Vermelho
1

Configurando o formulário

Quando o formulário é carregado, um objeto Divider é criado. Um objeto InkOverlay é criado e associado a um painel no formulário. Em seguida, os manipuladores de eventos são anexados ao objeto InkOverlay para rastrear quando os traços são adicionados e excluídos. Em seguida, se os reconhecedores estiverem disponíveis, um objeto RecognizerContext para o reconhecedor padrão será atribuído ao Divisor. Em seguida, a propriedade LineHeight do objeto Divider é definida e a coleção Strokes do objeto InkOverlay é atribuída ao Divisor. Por fim, o objeto InkOverlay está habilitado.

// Create the ink overlay and associate it with the form
myInkOverlay = new Microsoft.Ink.InkOverlay(DrawArea.Handle);

// Set the erasing mode to stroke erase.
myInkOverlay.EraserMode = InkOverlayEraserMode.StrokeErase;

// Hook event handler for the Stroke event to myInkOverlay_Stroke.
// This is necessary since the application needs to pass the strokes 
// to the ink divider.
myInkOverlay.Stroke += new InkCollectorStrokeEventHandler(myInkOverlay_Stroke); 

// Hook the event handler for StrokeDeleting event to myInkOverlay_StrokeDeleting.
// This is necessary as the application needs to remove the strokes from 
// ink divider object as well.
myInkOverlay.StrokesDeleting += new InkOverlayStrokesDeletingEventHandler(myInkOverlay_StrokeDeleting);

// Hook the event handler for StrokeDeleted event to myInkOverlay_StrokeDeleted.
// This is necessary to update the layout analysis result when automatic layout analysis
// option is selected.
myInkOverlay.StrokesDeleted += new InkOverlayStrokesDeletedEventHandler(myInkOverlay_StrokeDeleted);

// Create the ink divider object
myInkDivider = new Divider();

// Add a default recognizer context to the divider object
// without adding the recognizer context, the divider would
// not use a recognizer to do its word segmentation and would
// have less accurate results.
// Adding the recognizer context slows down the call to 
// myInkDivider.Divide though.
// It is possible that there is no recognizer installed on the
// machine for this language. In that case the divider does
// not use a recognizer to improve its accuracy.
// Get the default recognizer if any
try
{
    Recognizers recognizers = new Recognizers();
     myInkDivider.RecognizerContext = recognizers.GetDefaultRecognizer().CreateRecognizerContext();
}
catch (InvalidOperationException)
{
     //We are in the case where no default recognizers can be found
}

    // The LineHeight property helps the InkDivider distinguish between 
    // drawing and handwriting. The value should be the expected height 
    // of the user's handwriting in ink space units (0.01mm).
    // Here we set the LineHeight to 840, which is about 1/3 of an inch.
    myInkDivider.LineHeight = 840;

// Assign ink overlay's strokes collection to the ink divider
// This strokes collection is updated in the event handler
myInkDivider.Strokes = myInkOverlay.Ink.Strokes;

// Enable ink collection
myInkOverlay.Enabled = true;

A coleção Strokes do objeto Divider deve ser mantida em sincronia com a coleção Strokes do objeto InkOverlay (acessada por meio da propriedade Ink do objeto InkOverlay). Para garantir que isso aconteça, o manipulador de eventos Stroke para o objeto InkOverlay é gravado da seguinte maneira. Observe que o manipulador de eventos primeiro testa para ver se o EditingMode está definido como Tinta para filtrar traços de borracha. Se o usuário solicitou a análise automática de layout, o aplicativo chama o método DivideInk do formulário e atualiza a área de desenho.

private void myInkOverlay_Stroke(object sender, InkCollectorStrokeEventArgs e )
{
    // Filter out the eraser stroke.
    if(InkOverlayEditingMode.Ink == myInkOverlay.EditingMode)
    {
        // Add the new stroke to the ink divider's strokes collection
        myInkDivider.Strokes.Add(e.Stroke);
        
        if(miAutomaticLayoutAnalysis.Checked)
        {
            // Call DivideInk
            DivideInk();

            // Repaint the screen to reflect the change
            DrawArea.Refresh();
        }
    }
}

Dividindo a tinta

Quando o usuário clica em Dividir no menu Arquivo, o método Divide é chamado no objeto Divider . O reconhecedor padrão é usado, se disponível.

DivisionResult divResult = myInkDivider.Divide();

O objeto DivisionResult resultante, referenciado pela variável divResult, é passado para uma função de utilitário, getUnitBBBoxes(). A função utilitário retorna uma matriz de retângulos para qualquer tipo de divisão solicitado: segmentos, linhas, parágrafos ou desenhos.

myWordBoundingBoxes = getUnitBBoxes(divResult, InkDivisionType.Segment, 1);
myLineBoundingBoxes = getUnitBBoxes(divResult, InkDivisionType.Line, 3);
myParagraphBoundingBoxes = getUnitBBoxes(divResult, InkDivisionType.Paragraph, 5);
myDrawingBoundingBoxes = getUnitBBoxes(divResult, InkDivisionType.Drawing, 1);

Por fim, o painel de formulários é forçado a redesenhar para que os retângulos delimitador apareçam.

DrawArea.Refresh();

Resultados da Análise de Tinta

Na função de utilitário, o objeto DivisionResult é consultado para seus resultados usando o método ResultByType , com base no tipo de divisão solicitado pelo chamador. O método ResultByType retorna uma coleção DivisionUnits . Cada DivisionUnit na coleção representa um desenho, um único segmento de reconhecimento de manuscrito, uma linha de manuscrito ou um bloco de manuscrito, dependendo do que foi especificado quando a função de utilitário foi chamada.

DivisionUnits units = divResult.ResultByType(divType);

Se houver pelo menos um DivisionUnit, uma matriz de retângulos será criada contendo um retângulo delimitador por unidade. (Os retângulos são inflados por valores diferentes para cada tipo de unidade, mantidos na variável inflada, para evitar a sobreposição.)

// If there is at least one unit, we construct the rectangles
if((null != units) && (0 < units.Count))
{
    // We need to convert rectangles from ink units to
    // pixel units. For that, we need Graphics object
    // to pass to InkRenderer.InkSpaceToPixel method
    using (Graphics g = DrawArea.CreateGraphics())
    {

    // InkSpace to Pixel Space conversion setup done here. 
    // Not shown for brevity.

        // Iterate through the collection of division units to obtain the bounding boxes
        foreach(DivisionUnit unit in units)
        {
            // Get the bounding box of the strokes of the division unit
            divRects[i] = unit.Strokes.GetBoundingBox();

            // Div unit rect Ink space to Pixel space conversion done here. 
            // Not shown for brevity.

            // Inflate the rectangle by inflate pixels in both directions
            divRects[i].Inflate(inflate, inflate);

            // Increment the index
            ++i;
        }

    } // Relinquish the Graphics object
}

Redesenhando o formulário

Quando a redesenhar é forçada acima, o código a seguir é executado para pintar as caixas delimitadoras para cada DivisionUnit no formulário ao redor da tinta.

private void DrawArea_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            // Create the Pen used to draw bounding boxes.
            // First set of bounding boxes drawn here are 
            // the bounding boxes of paragraphs.
            // These boxes are drawn with Blue pen.
            Pen penBox = new Pen(Color.Blue, 2);

            // First, draw the bounding boxes for Paragraphs
            if(null != myParagraphBoundingBoxes)
            {
                // Draw bounding boxes for Paragraphs
                e.Graphics.DrawRectangles(penBox, myParagraphBoundingBoxes);
            }

            // Next, draw the bounding boxes for Lines
            if(null != myLineBoundingBoxes)
            {
                // Color is Magenta pen
                penBox.Color = Color.Magenta;
                // Draw the bounding boxes for Lines
                e.Graphics.DrawRectangles(penBox, myLineBoundingBoxes);
            }
            
            // Then, draw the bounding boxes for Words
            if(null != myWordBoundingBoxes)
            {
                // Color is Green
                penBox.Color = Color.Green;
                // Draw bounding boxes for Words
                e.Graphics.DrawRectangles(penBox, myWordBoundingBoxes);
            }
            
            // Finally, draw the boxes for Drawings
            if(null != myDrawingBoundingBoxes)
            {
                // Color is Red pen
                penBox.Color = Color.Red;
                // Draw bounding boxes for Drawings
                e.Graphics.DrawRectangles(penBox, myDrawingBoundingBoxes);
            }
        }

Fechando o formulário

O método Dispose do formulário descarta os objetos InkOverlay, Divider, RecognizerContext e a coleção Strokes usada no exemplo.