Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
L'esempio di analisi di base dell'inchiostro mostra come la classe InkAnalyzer divide l'inchiostro in vari segmenti di parole e disegni.
Questo campione è una versione aggiornata del campione Ink Divider . Mentre il campione di divisione dell'inchiostro utilizza la classe Divider , questo esempio utilizza l'API InkAnalysis preferita e più recente. L'API InkAnalysis combina la RecognizerContext e Divider in un'API ed espande la funzionalità di entrambi.
Quando si aggiorna il modulo, l'esempio disegna un rettangolo intorno a ogni unità analizzata: parole, linee, paragrafi, aree di scrittura, disegni e punti elenco. Il modulo utilizza colori diversi per le diverse unità. I rettangoli vengono anche ingranditi con quantità diverse per garantire che nessuno rettangolo sia oscurato da altri.
La tabella seguente specifica il colore e l'ingrandimento per ogni unità analizzata.
| Unità analizzata | Colore del rettangolo | Ingrandimento del rettangolo (pixel) |
|---|---|---|
| Parola |
Verde |
1 |
| Linea |
Magenta |
3 |
| Paragrafo |
Blu |
5 |
| Area di scrittura |
Giallo |
7 |
| Disegno |
Rosso |
1 |
| Pallottola |
Arancia/Arancione |
1 |
È possibile cancellare i tratti nel modulo. Nell'applicazione di esempio, è possibile alternare tra la modalità Scrittura e Cancella per modificare la funzione della penna.
private void miInk_Click(object sender, System.EventArgs e)
{
// Turn on the inking mode
myInkOverlay.EditingMode = InkOverlayEditingMode.Ink;
// Update the state of the Ink and Erase menu items
miInk.Checked = true;
miErase.Checked = false;
// Update the UI
this.Refresh();
}
private void miErase_Click(object sender, System.EventArgs e)
{
// Turn on the ink deletion mode
myInkOverlay.EditingMode = InkOverlayEditingMode.Delete;
// Update the state of the Ink and Erase menu items
miInk.Checked = false;
miErase.Checked = true;
// Update the UI
this.Refresh();
}
Quando si aggiungono o eliminano tratti, gli esempi si aggiornano nel InkAnalyzer.
private void myInkOverlay_Stroke(object sender, InkCollectorStrokeEventArgs e)
{
// Filter out the eraser stroke.
if (InkOverlayEditingMode.Ink == myInkOverlay.EditingMode)
{
// Add the new stroke to the InkAnalyzer's stroke collection
myInkAnalyzer.AddStroke(e.Stroke);
if (miAutomaticLayoutAnalysis.Checked)
{
// Invoke an analysis operation on the background thread
myInkAnalyzer.BackgroundAnalyze();
}
}
}
void myInkOverlay_StrokeDeleting(object sender, InkOverlayStrokesDeletingEventArgs e)
{
// Remove the strokes to be deleted from the InkAnalyzer's stroke collection
myInkAnalyzer.RemoveStrokes(e.StrokesToDelete);
// If automatic layout analysis is turned on, analyze the ink on the background thread
if ( miAutomaticLayoutAnalysis.Checked )
{
// Invoke an analysis operation on the background thread
myInkAnalyzer.BackgroundAnalyze ( );
}
}
Si noti che nel menu Modalità, l'analisi automatica del layout è attivata per impostazione predefinita. Con questa opzione selezionata, i gestori eventi di InkOverlay per Stroke e StrokesDeleting chiamano il metodo BackgroundAnalyze ogni volta che un tratto viene creato o eliminato.
Nota
Chiamare il metodo Analyze dell'oggetto InkAnalyzer con più di pochi tratti presenti crea un ritardo notevole nell'applicazione. Ciò è dovuto al fatto che Analyze è un'operazione di analisi dell'input penna sincrona. In pratica, chiamare il metodo Analyze solo quando è necessario il risultato. In caso contrario, usare il metodo asincrono BackgroundAnalyze, come mostrato nell'esempio.
Gestione dei risultati dell'analisi
L'esempio crea due matrici per contenere i vari rettangoli, orizzontali o ruotati. Utilizzare un rettangolo ruotato per ottenere l'angolo su cui viene scritta una linea di testo. L'esempio mostra le proprietà restituite dal InkAnalyzer e visualizza il raccordo limitatore o il raccordo limitatore ruotato (a seconda della selezione nel menu).
private Rectangle[] GetHorizontalBBoxes(Guid nodeType, int inflate)
{
// Declare the array of rectangles to hold the result
Rectangle[] analysisRects;
// Get the division units from the division result of division type
ContextNodeCollection nodes = myInkAnalyzer.FindNodesOfType(nodeType);
// If there is at least one unit, we construct the rectangles
if ((null != nodes) && (0 < nodes.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())
{
// Construct the rectangles
analysisRects = new Rectangle[nodes.Count];
// InkRenderer.InkSpaceToPixel takes Point as parameter.
// Create two Point objects to point to (Top, Left) and
// (Width, Height) properties of rectangle. (Width, Height)
// is used instead of (Right, Bottom) because (Right, Bottom)
// are read-only properties on Rectangle
Point ptLocation = new Point();
Point ptSize = new Point();
// Index into the bounding boxes
int i = 0;
// Iterate through the collection of division units to obtain the bounding boxes
foreach (ContextNode node in nodes)
{
// Get the bounding box of the strokes of the division unit
analysisRects[i] = node.Location.GetBounds();
// The bounding box is in ink space unit. Convert them into pixel unit.
ptLocation = analysisRects[i].Location;
ptSize.X = analysisRects[i].Width;
ptSize.Y = analysisRects[i].Height;
// Convert the Location from Ink Space to Pixel Space
myInkOverlay.Renderer.InkSpaceToPixel(g, ref ptLocation);
// Convert the Size from Ink Space to Pixel Space
myInkOverlay.Renderer.InkSpaceToPixel(g, ref ptSize);
// Assign the result back to the corresponding properties
analysisRects[i].Location = ptLocation;
analysisRects[i].Width = ptSize.X;
analysisRects[i].Height = ptSize.Y;
// Inflate the rectangle by inflate pixels in both directions
analysisRects[i].Inflate(inflate, inflate);
// Increment the index
++i;
}
} // Relinquish the Graphics object
}
else
{
// Otherwise we return null
analysisRects = null;
}
// Return the Rectangle[] object
return analysisRects;
}
private System.Collections.ArrayList GetRotatedBBoxes(Guid nodeType, int inflate)
{
//Find the correct collection of results nodes.
ContextNodeCollection Nodes = myInkAnalyzer.FindNodesOfType(nodeType);
// Declare the array list to hold the results;
// This array represents the four points of a rectangle, with the first point
// copied again to complete the cycle of points.
ArrayList polygonPoints = new ArrayList(Nodes.Count);
// Cycle through each results node, get and convert the
// rotated bounding box points
foreach (ContextNode node in Nodes)
{
//Declare the point array
Point[] rotatedBoundingBox = null;
//Switch on the type of ContextNode to cast into the
//appropriate type. This is required to access the
//type specific property "RotatedBoundingBox" which
//is not found on all ContextNode types.
if (nodeType == ContextNodeType.InkWord)
{
rotatedBoundingBox = ((InkWordNode)node).GetRotatedBoundingBox();
}
else if (nodeType == ContextNodeType.Line)
{
rotatedBoundingBox = ((LineNode)node).GetRotatedBoundingBox();
}
else if (nodeType == ContextNodeType.Paragraph)
{
rotatedBoundingBox = ((ParagraphNode)node).GetRotatedBoundingBox();
}
else if (nodeType == ContextNodeType.WritingRegion ||
nodeType == ContextNodeType.InkDrawing ||
nodeType == ContextNodeType.InkBullet )
{
// Rotated Bounding Boxes are not a supported option for
// Writing Regions or Drawings. We return the axis aligned
// bounding box instead
Rectangle rect = node.Location.GetBounds();
// We need to create a looped list of 4 points to be consistent
// with the way InkAnalysis represents rotated bounding boxes.
rotatedBoundingBox = new Point[4];
rotatedBoundingBox[0] = new Point(rect.X, rect.Y);
rotatedBoundingBox[1] = new Point(rect.Right, rect.Y);
rotatedBoundingBox[2] = new Point(rect.Right, rect.Bottom);
rotatedBoundingBox[3] = new Point(rect.X, rect.Bottom);
}
if (null != rotatedBoundingBox)
{
// 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())
{
// convert each of the points from ink space to pixel space
for (int i = 0; i < rotatedBoundingBox.Length; i++)
{
myInkOverlay.Renderer.InkSpaceToPixel(g, ref rotatedBoundingBox[i]);
}
//inflate the points by calling helper method
InflateHelperMethod(ref rotatedBoundingBox, inflate);
// increment the node portion of the polygonPoints array
polygonPoints.Add(rotatedBoundingBox);
}
}
}
//Return the results
return polygonPoints;
}
Il parser calcola GetRotatedBoundingBox durante l'analisi. È possibile accedere alle informazioni dai riquadri di selezione ruotati nella vostra applicazione per vari scopi utili:
- Rilevare o disegnare i limiti di una singola riga, paragrafo o altra unità.
- Determinare l'angolo in corrispondenza del quale viene scritta una riga o un paragrafo.
- Implementare funzionalità come la selezione per una riga, un paragrafo o un'altra unità.