Penlets.com provides resources for users and developers of the Pulse smart pen from LiveScribe.

 

Subscribe: RSS Feed - Developers Group

Tutorial

Capturing Drawn Shapes

Tutorial by Robert Hanson

Page 1 » Page 2


Capturing Strokes with StrokeListener

So far we have a penlet that asks the user to draw a circle. When the user does this it will trigger the calling of a strokeCreated method that is defined by the StrokeListener interface

 
/* ***********************************************
 * 
 * StrokeListener interface methods.
 * 
 *********************************************** */

/* [1] */
private Polygon targetArea;

public void strokeCreated (long time, Region region, PageInstance pageInstance)
{
    /* [2] */
    if (targetArea == null) {

        /* [3] */
        StrokeStorage strokeStorage = new StrokeStorage(pageInstance);
        Stroke stroke = strokeStorage.getStroke(time);
        
        /* [4] */
        int v = stroke.getNumberofVertices();
        targetArea = new Polygon(v);
        
        /* [5] */
        for (int i = 0; i < v; i++) {
            targetArea.setXY(i, stroke.getX(i), stroke.getY(i));
        }

        /* [6] */
        this.label.draw("Touch the pen to the paper", true);
    }
}

Going back to what our penlet needs to do, recall that the user only gets to draw one circle then we change the behavior. For this we need two things; we need someplace to store the stroke data, and we need some flag to only grab the first stroke.

For the first part we will use a Polygon object to store the circle data, in a variable named targetArea [1]. The Polygon class is a subclass of the abstract Shape class . The Penlet SDK comes with seven different shapes, listed below along with what they represent.

Point Used to store a single x,y coordinate. Has a utility method to calculate the distance between this point and some other point.
LineSegment Represents a line between two points.
PolyLine A line with any number of points.
Stroke Similar to a PolyLine, except that it keeps track of the time. Has two methods to help determine when a particular part of the stoke was created.
Ellipse Holds a center-point along with width and height. It does not allow for a rotation value to be stored.
Rectangle Stores the upper-left coordinates of the shape long with the width and height. Just like the Ellipse it does not allow for rotation.
Polygon Stores a shape with any number of vertices.

Because we expect that the "circle" drawn by the user will not be a perfect circle, we will use the Polygon shape to store the points. The impact of this is that the user could really draw any shape they wanted, like perhaps a cloud shape, and our Polygon will be able to store that.

For the purposes of a flag that allows the user to only draw one circle, we will check to see if targetArea is null [2].

Next, in order to get access to the actual stroke made by the user we need to get a handle to the StrokeStorage for the page, and then lookup the Stroke object based on when it was made [3].

Now that we have the Stroke we can get the number of vertices in the Stroke and initialize our targetArea [4].

We then simply iterate over the vertices in the Shape and copy them over to the targetArea [5].

The reason for copying the stroke at all is because we will need to call the contains method on the shape, and a Stroke object is not a closed object while a Polygon is. At the same time our code is making the assumption that the user really did draw a circle and not just a line. We could add some checking for that, verifying that the start and ending points are in close proximity, but that is a bit overkill for this tutorial.

Now lets implement the PenTipListener interface and code the last part of the penlet where we check to see if the pen is inside out outside of the circle.

Location, Location, Location

The PenTipListener interface has four methods that we need to implement. Take a look at the code below, then we will discuss the one that matters to us, the singleTap method.

r 
/* ***********************************************
 * 
 * PenTipListener interface method.
 * 
 *********************************************** */

public void singleTap (long time, int x, int y)
{
    if (targetArea != null) {
        if (targetArea.contains(x, y)) {
            this.label.draw("inside", true);
        }
        else {
            this.label.draw("outside", true);
        }
    }
}

public void doubleTap (long time, int x, int y)
{
}

public void penDown (long time, Region region, PageInstance pageInstance)
{
}

public void penUp (long time, Region region, PageInstance pageInstance)
{
}

Ok, the logic here is very simple. When the pen touches the paper the singleTap handler is called with the coordinates of the pen's tip. And in order to test that our targetArea contains the point we call the contains method.

If the point is inside the circle we draw "inside" on the OLED display, and if it is outside the circle we display "outside".

Tada!

That concludes the sample application, but the API does offer some additional tools for working with shapes. In particular you can use the method intersectsWith on the shape to find its relationship to another shape. intersectsWith returns an int value that is one of Shape.INTERSECT_CLIP , Shape.INTERSECT_EXTERNAL , Shape.INTERSECT_INTERNAL1 , or Shape.INTERSECT_INTERNAL2 . The reason for two internal intersection constants is that the first is returned when shape "A" is within shape "B", while the second constant is the reverse.

You can also get the union of the shapes by calling Shape.getUnion . This returns a Rectangle that contains both shapes.

Of course if you are the math-enabled type, you could write your own functions to determine the geometric properties of drawn shapes.

You could even turn shape drawing into a game where the goal was to draw a perfect circle. The penlet could determine the center of the drawn circle and calculate a percentage of perfectness, displaying the result to the user. Hmmm... that could be interesting :)

Until next time, happy coding!


Page 1 » Page 2


Comments (View)
blog comments powered by Disqus

Project Information

Tested for use with: PreRelease-SDK

Download the source code for this tutorial.
Download Source Code (zip)

New Tutorials

Using a Shared Library
Learn how to create a library of utils that you can share between projects.

Capturing Drawn Shapes
Learn how to capture shapes drawn by the pen and determine relationships.

Penlets 101
Never written a penlet before? Then start here with Penlets 101!

Creating a Custom Vocabulary
Learn how to create a custom vocabulary for your ICR applications.

Using Properties Files
J2ME lacks a Properties class. In this tutorial we roll our own, along with split and chomp functions.