All computer source code presented on this page, unless it includes attribution to another author, is provided by Ed Halley under the Artistic License. Use such code freely and without any expectation of support. I would like to know if you make anything cool with the code, or need questions answered.
python/
    bindings.py
    boards.py
    buzz.py
    caches.py
    cards.py
    constraints.py
    csql.py
    english.py
    getch.py
    getopts.py
    gizmos.py
    goals.py
    improv.py
    interpolations.py
    namespaces.py
    nihongo.py
    nodes.py
    octalplus.py
    patterns.py
    physics.py
    pids.py
    pieces.py
    quizzes.py
    recipes.py
    relays.py
    romaji.py
    ropen.py
    sheets.py
    stores.py
    strokes.py
    subscriptions.py
    svgbuild.py
    testing.py
    things.py
    timing.py
    ucsv.py
    useful.py
    uuid.py
    vectors.py
    weighted.py
java/
    CSVReader.java
    CSVWriter.java
    GlobFilenameFilter.java
    RegexFilenameFilter.java
    StringBufferOutputStream.java
    ThreadSet.java
    Throttle.java
    TracingThread.java
    Utf8ConsoleTest.java
    droid/
        ArrangeViewsTouchListener.java
        DownloadFileTask.java
perl/
    CVQM.pm
    Kana.pm
    Typo.pm
cxx/
    CCache.h
    equalish.cpp
Download strokes.py
# strokes - routines for recognizing handwriting strokes and gestures

'''

Routines for recognizing handwriting strokes and gestures.

SYNOPSIS

    >>> import strokes ; from strokes import *
    >>> import vectors ; from vectors import *

AUTHOR

    Ed Halley (ed@halley.cc) 22 December 2007

'''

__all__ = [ 'Figure', 'Stroke' ]

#----------------------------------------------------------------------------

import math
import vectors ; from vectors import *

def track(v):
    '''Return a simplified cardinal track, from 0 East to 7 SouthEast.'''
    t = vectors.track(v)
    t += math.pi/8.
    t = t * 8. / (math.pi*2.)
    return int(t) % 8

#----------------------------------------------------------------------------

# a figure is an unordered set or an ordered list of strokes

class Figure (object):

    def __init__(self, ordered=False):
        self.strokes = [ ]
        self.ordered = ordered

    def __str__(self):
        return ','.join( [ str(stroke) for stroke in self.strokes ] )

    def __nonzero__(self):
        if self.strokes: return False
        return True

    def cook(self, jitter=3):
        box = self.bounds()
        for stroke in self.strokes:
            stroke.cook(box, jitter)

    def bounds(self, box=None):
        for stroke in self.strokes:
            box = stroke.bounds(box)
        return box

    def start(self, where):
        self.strokes.append( Stroke() )
        self.draw(where)

    def draw(self, where):
        if not self.strokes:
            raise RuntimeError, 'must call start() on figure before draw()'
        self.strokes[-1].draw(where)

    def stop(self):
        pass

#----------------------------------------------------------------------------

class Stroke (object):

    # a raw stroke is an ordered list of V(x,y) or (x,y)
    # a cooked stroke is an ordered list of (V(x,y),area,dir,len)

    def __init__(self, cooked=None):
        self.raw = [ ]
        self.cooked = cooked

    def __str__(self):
        if not self.cooked:
            self.cook()
        string = ''
        for segment in self.cooked:
            string += "%s%d%d" % segment[1:4] 
        return string

    def __eq__(self, other):
        if other is self: return True
        if str(other) == str(self): return True
        return False

    def draw(self, where):
        where = tuple(where[:2])
        self.raw.append(where)

    # [ (2,2), (4,4), (6,6), (8,6) ]
    # [ (V(2,2), -1, -1),
    #   (V(6,6), SOUTHEAST, 2),
    #   (V(8,6), EAST, 0) ]

    def bounds(self, box=None):
        for pen in self.raw:
            if box is None:
                box = [ pen[0], pen[1], pen[0], pen[1] ]
            else:
                box[0] = min(box[0], pen[0])
                box[1] = min(box[1], pen[1])
                box[2] = max(box[2], pen[0])
                box[3] = max(box[3], pen[1])
        return box

    def area(self, pen, box):
        '''Returns a letter code for each of nine sub-areas the pen may be.'''
        wx = box[2]-box[0]
        wy = box[3]-box[1]
        c = V(box[0]+wx/2, box[1]+wy/2)
        d = pen-c
        if d.magnitude() < min(wx,wy)/4.:
            return 'E'
        else:
            return 'FCBADGHI'[track(d)]

    def cook(self, box=None, jitter=3):
        '''Reduces the raw ink data into a format ready for matching.'''
        if not self.raw: return
        if box is None:
            box = self.bounds()
        span = max(box[2]-box[0], box[3]-box[1], 1)
        self.cooked = [ (V(self.raw[0]), 'X', -1, -1) ]
        for i in range(1, len(self.raw)):
            pen = V(self.raw[i])
            way = self.area(pen, box)
            ink = pen - self.cooked[-1][0]
            far = ink.magnitude()
            if far < jitter:
                continue
            far = int(far * 9. / span)
            dir = track(ink)
            if dir == self.cooked[-1][2]:
                way = self.cooked[-1][1]
                ink = pen - self.cooked[-2][0]
                far = int(ink.magnitude() * 9. / span)
                self.cooked[-1] = (pen, way, dir, far)
            else:
                self.cooked.append( (pen, way, dir, far) )
        self.cooked = [ part for part in self.cooked if part[3] > 0 ]
        #self.cooked.pop(0)

    def match(self, other, tail=False):
        if not other.cooked:
            raise ValueError, 'can only match against cooked strokes'
        return False

#----------------------------------------------------------------------------

def __test__():
    from testing import __ok__, __report__
    import vectors ; from vectors import V,equal,zero

    print 'Testing strokes...'

    stroke = Stroke()

    for pen in [ (2,2), (4,4), (6,6), (8,6) ]:
        stroke.draw(pen)
    print 'raw:', repr(stroke.raw)

    stroke.cook()
    print 'cooked:', repr(stroke.cooked)
    print 'string:', str(stroke)

    # [ (V(2,2), -1, -1),
    #   (V(6,6), 1, 1),
    #   (V(8,6), 0, 0) ]

    __report__()

if __name__ == '__main__':
    __test__()
    if False:
        raise Exception, \
            'This module is not a stand-alone script.  Import it in a program.'


Contact Ed Halley by email at ed@halley.cc.
Text, code, layout and artwork are Copyright © 1996-2013 Ed Halley.
Copying in whole or in part, with author attribution, is expressly allowed.
Any references to trademarks are illustrative and are controlled by their respective owners.
Make donations with PayPal - it's fast, free and secure!