|
Programmer's Notebook |
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 cache.py cards.py constraints.py csql.py english.py getopts.py gizmos.py goals.py improv.py interpolations.py namespaces.py nihongo.py nodes.py octalplus.py patterns.py persist.py physics.py pids.py pieces.py quizzes.py recipes.py relays.py romaji.py ropen.py sheets.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. |
|