Tablet PC: Generating ink from scratch *and* using the InkAnalyzerBase class

In recent posts, I’ve demonstarted the use of the “Windows Forms” and “COM” layers of the InkAnalysis API. This time we’ll look at the “Base” or “Core” managed layer which is not bound to windows forms *OR* the rest of the Tablet PC Platform SDK (namely the Microsoft.Ink.dll assembly).

In this post, I’ll demonstrate a simple ink generation and rendering class that uses GDI+ to render the custom ink. This custom ink is a List of Lists of points (List<List<Point>>). You can think of this as an extensible two dimensional jagged array of Points. Each List of Points represents one stroke (pen down, move, pen up).

The application consists of a Form with a “Recognize” button. When the user clicks the recognize button, we’ll add the custom stroke data to an InkAnalyzerBase object, peform analysis, and display the recognition result to the user.

Here’s the basic steps (VS.NET 2005):

1. Install the Input Supplement:
https://blogs.msdn.com/gavingear/archive/2006/09/15/756274.aspx

2. Create a new Windows Forms application project

3. Add a reference to “Microsoft Tablet PC Ink Analysis Core Components” (IACore.dll)

4. Add a button and change the text to “Recognize”

5. Double click the button to add a handler

6. Add the InkGeneration class (See example code à SimpleInkGenerator.cs) to the project

7. Instantiate a SimpleInkGenerator object passing a reference to the Form

8. In the button click handler, add the InkAnalysis code

Screenshot from application:

Example code from click handler:

// Create an InkAnalyzerBase object

InkAnalyzerBase analyzer = new InkAnalyzerBase();

// Disable StrokeCacheAutoCleanup so that stroke data is preserved

// between Analyze() calls

analyzer.AnalysisModes &= ~AnalysisModes.StrokeCacheAutoCleanupEnabled;

// For each List<Point>, create an array of int which is a continuous

// sequence of X,Y point data

if (inkGenerator.strokes_.Count > 0)

{

    for (int index = 0; index < this.inkGenerator.strokes_.Count; index++)

    {

        Point [] stroke = new Point[this.inkGenerator.strokes_[index].Count];

        this.inkGenerator.strokes_[index].CopyTo(stroke);

        int[] strokeIntegers = new int[stroke.Length * 2]; // Point has 2 integer values

        // Copy the X, Y values for the points to the int array in sequence

        //

        // **Multiply pixel coordinate data by 26.4 to simulate "InkSpace" coordinates

        // which are in HIMETRIC units

        for (int innerIndex = 0; innerIndex < strokeIntegers.Length; innerIndex += 2)

        {

          strokeIntegers[innerIndex] = (int)(stroke[innerIndex/2].X * 26.4);

            strokeIntegers[innerIndex+1] = (int)(stroke[innerIndex/2].Y * 26.4);

        }

        analyzer.AddStroke(

            index, // Index

            strokeIntegers, // int []

            new Guid[2] {

                new Guid("598a6a8f-52c0-4ba0-93af-af357411a561"), // PacketProperty.X

                new Guid("b53f9f75-04e0-4498-a7ee-c30dbb5a9011") } // PacketProperty.Y

        );

    }

    // Perform synchronous analysis

    AnalysisStatusBase status = analyzer.Analyze();

    // Display the result, or handle the error

    if (status.Successful == true)

    {

        MessageBox.Show(

            "Result from analysis:" + Environment.NewLine + analyzer.GetRecognizedString(),

            "Recognition Result");

    }

    else

    {

        MessageBox.Show("Error calling Analyze()", "Error");

    }

}

// Clean up unmanaged resources

analyzer.Dispose();

We can see that using the Base/Core layer of the InkAnalysis API requires a bit more code (than the corresponding calls in the Windows Forms layer) – but also offers more flexibility with our data.

The biggest differences between the Base/Core layer of InkAnalysis and the Windows Forms layer boil down to two areas:

1. Adding strokes/stroke data

2. Dealing with ContextNodes (Generic in Base/Core layer, more specific types in the Windows Forms layer)

3. By default, the base layer has the AnalysisModes.StrokeCacheAutoCleanupEnabled flag set. This requires stroke data to be updated manually between successive Analyze() calls. In Windows Forms, this flag is not set by default since the InkAnalyzer directly references the strokes that have been added to the InkAnalyzer.
**If you are using the InkAnalyzerBase class with the AnalysisModes.StrokeCacheAutoCleanupEnabled flag set, you should handle the InkAnalyzerBase.UpdateStrokeData event so that you can refresh the stroke data as needed. This will allow your application to avoid duplicated data (flag not set).

Other than the key areas of differences noted above the Base/Core layer and the Windows Forms layers are almost identical.

There you have it! Performing InkAnalysis with your own custom ink! If you are thinking about incorporating the base/core InkAnalysis layer into your application, download the attached sample code for more detail.

See Ya,

Gavin

CustonInkGeneration.zip