_YOUR OWN HANDPRINTING RECOGNITION ENGINE_ by Ron Avitzur [LISTING ONE] /***************************************************************** A Writer-Dependent Hand-printing Recognizer -- by Ron Avitzur, 1991. This is not the complete code. See the accompanying article for a description of other necessary modules. *****************************************************************/ typedef struct { short num_items; void *items[]; } *List; typedef struct { short code; List strokes; } GesturePattern typedef struct { char is_dot; List s[5]; } StrokePattern; typedef struct { Point Ink[MAX_POINTS], P[MAX_N]; short Ink_Num, N, T[MAX_N], IsDot; unsigned long S[5]; double Aspect_Ratio; long Xmax,Xmin, Ymax,Ymin, Height,Width, XmaxT,XminT, YmaxT,YminT, HeightT,WidthT; long start_time,end_time; List matches; } StrokeData, *StrokePtr; ProcPtr HashFunctions[] = { Fxn1,Fxn2,Fxn3,Fxn4,Fxn5 }; char Bits[] = {2,2,3,2,2}; #define DOT_THRESHHOLD (Wacom?80:4) #define PT_SEP_POST (Wacom?40:4) /****************************************************************/ void Analyze(register StrokePtr theStroke) { char i,s[100]; Simplify(); for (i = 0; i < 5; i++) { HashFunction[i](s,theStroke); ConvertStringToLong(s,&S[i],Bits[i]); } theStroke->IsDot = ((theStroke->Height < DOT_THRESHHOLD && theStroke->Width < DOT_THRESHHOLD) || N == 1); } /****************************************************************/ void Simplify(StrokePtr theStroke,Point *Ink,short N) { Point Q[MAX_POINTS]; short min_dx = theStroke->Width / 8, min_dy = theStroke->Height / 8; if (theStroke->Aspect_Ratio < 0.2) min_dy = theStroke->Height; if (theStroke->Aspect_Ratio > 5.0) min_dx = theStroke->Width; theStroke->N = Process3(theStroke->P,Ink,N,min_dx,min_dy); ComputeT(theStroke); } /****************************************************************/ void ComputeT(StrokePtr theStroke) { register short *T = theStroke->T; register Point *P = theStroke->P; register short i,N = theStroke->N; for (i = 0; i < N - 1; i++) T[i] = ATAN2(P[i+1].v-P[i].v,P[i+1].h-P[i].h); } /****************************************************************/ short Process3(register Point *P, register Point *Q, short num,short xd, short yd) { register short i,n; register short dx,dy; n = 0; P[0] = Q[0]; for (i = 1; i < num - 1; i++) { dx = Q[i].h - P[n].h; dx = ABS(dx); dy = Q[i].v - P[n].v; dy = ABS(dy); if (dx + dy < PT_SEP_POST) continue; if (dx < xd && dy < yd) continue; n++; P[n] = Q[i]; } dx = Q[num - 1].h - P[n].h; dy = Q[num - 1].v - P[n].v; if (ABS(dx) + ABS(dy) > PT_SEP_POST) n++; P[n] = Q[num - 1]; return n + 1; } /****************************************************************/ /* These five lines determine what the features actually are. */ #define Feature1(t) ('0' + ((t + 10 + 45 + 180) / 90) % 4) #define Feature2(t) ('0' + ((t + 10 + 00 + 180) / 90) % 4) #define Feature3(t) ('0' + ((t + 10 + 22 + 180) / 45) % 8) #define Feature4(p) ('0' + (4*((p).h - theStroke->XminT) /theStroke->WidthT)) #define Feature5(p) ('0' + (4*((p).v - theStroke->YminT) /theStroke->HeightT)) /****************************************************************/ #define FOO(name,fxn,type,array,end) \ void name(char *s,StrokePtr theStroke) \ { \ register short i,d,n = 0; \ register type *T = theStroke->array; \ s[0] = fxn(*T++); \ i = theStroke->N - end; \ while (i-- > 0) { \ d = fxn(*T++); \ if (s[n] != d) \ s[++n] = d; \ } \ s[++n] = 0; \ } FOO(Fxn1,Feature1,short,T,2) FOO(Fxn2,Feature2,short,T,2) FOO(Fxn3,Feature3,short,T,2) FOO(Fxn4,Feature4,Point,P,1) FOO(Fxn5,Feature5,Point,P,1) /****************************************************************/ void ConvertStringToLong(char *s,unsigned long *np,short bits) { unsigned long n = 0; short i,len = strlen(s); s[len] = s[len-1]; if (len > 32/bits) len = 32/bits; for (i = 0; i <= len; i++) n = (n << bits) + s[len - i] - '0'; *np = n; }