xref: /plan9/sys/src/libscribble/li_recognizer.c (revision 80ee5cbfe36716af62da8896207e9763b8e3d760)
1*80ee5cbfSDavid du Colombier /*
2*80ee5cbfSDavid du Colombier  *  li_recognizer.c
3*80ee5cbfSDavid du Colombier  *
4*80ee5cbfSDavid du Colombier  *	Copyright 2000 Compaq Computer Corporation.
5*80ee5cbfSDavid du Colombier  *	Copying or modifying this code for any purpose is permitted,
6*80ee5cbfSDavid du Colombier  *	provided that this copyright notice is preserved in its entirety
7*80ee5cbfSDavid du Colombier  *	in all copies or modifications.
8*80ee5cbfSDavid du Colombier  *	COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR
9*80ee5cbfSDavid du Colombier  *	IMPLIED, AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR
10*80ee5cbfSDavid du Colombier  *
11*80ee5cbfSDavid du Colombier  *
12*80ee5cbfSDavid du Colombier  *  Adapted from cmu_recognizer.c by Jay Kistler.
13*80ee5cbfSDavid du Colombier  *
14*80ee5cbfSDavid du Colombier  *  Where is the CMU copyright???? Gotta track it down - Jim Gettys
15*80ee5cbfSDavid du Colombier  *
16*80ee5cbfSDavid du Colombier  *  Credit to Dean Rubine, Jim Kempf, and Ari Rapkin.
17*80ee5cbfSDavid du Colombier  */
18*80ee5cbfSDavid du Colombier 
19*80ee5cbfSDavid du Colombier #include <u.h>
20*80ee5cbfSDavid du Colombier #include <libc.h>
21*80ee5cbfSDavid du Colombier #include <stdio.h>
22*80ee5cbfSDavid du Colombier #include <draw.h>
23*80ee5cbfSDavid du Colombier #include <scribble.h>
24*80ee5cbfSDavid du Colombier #include "scribbleimpl.h"
25*80ee5cbfSDavid du Colombier 
26*80ee5cbfSDavid du Colombier #include "hre_internal.h"
27*80ee5cbfSDavid du Colombier #include "li_recognizer_internal.h"
28*80ee5cbfSDavid du Colombier 
29*80ee5cbfSDavid du Colombier int lidebug = 0;
30*80ee5cbfSDavid du Colombier 
31*80ee5cbfSDavid du Colombier #define LI_MAGIC 0xACCBADDD
32*80ee5cbfSDavid du Colombier 
33*80ee5cbfSDavid du Colombier #define CHECK_LI_MAGIC(_a) \
34*80ee5cbfSDavid du Colombier   ((_a) != nil && ((li_recognizer*)(_a))->li_magic == LI_MAGIC)
35*80ee5cbfSDavid du Colombier 
36*80ee5cbfSDavid du Colombier 
37*80ee5cbfSDavid du Colombier static void lialg_initialize(rClassifier *);
38*80ee5cbfSDavid du Colombier static int lialg_read_classifier_digest(rClassifier *);
39*80ee5cbfSDavid du Colombier static int lialg_canonicalize_examples(rClassifier *);
40*80ee5cbfSDavid du Colombier static char *lialg_recognize_stroke(rClassifier *, point_list *);
41*80ee5cbfSDavid du Colombier static void lialg_compute_lpf_parameters(void);
42*80ee5cbfSDavid du Colombier 
43*80ee5cbfSDavid du Colombier 
44*80ee5cbfSDavid du Colombier char* li_err_msg = nil;
45*80ee5cbfSDavid du Colombier 
46*80ee5cbfSDavid du Colombier #define bcopy(s1,s2,n) memmove(s2,s1,n)
47*80ee5cbfSDavid du Colombier 
48*80ee5cbfSDavid du Colombier /*Freeing classifier*/
49*80ee5cbfSDavid du Colombier 
50*80ee5cbfSDavid du Colombier static void
51*80ee5cbfSDavid du Colombier free_rClassifier(rClassifier* rc);
52*80ee5cbfSDavid du Colombier 
53*80ee5cbfSDavid du Colombier /*
54*80ee5cbfSDavid du Colombier  * Point List Support
55*80ee5cbfSDavid du Colombier */
56*80ee5cbfSDavid du Colombier 
57*80ee5cbfSDavid du Colombier static point_list*
add_example(point_list * l,int npts,pen_point * pts)58*80ee5cbfSDavid du Colombier add_example(point_list* l,int npts,pen_point* pts)
59*80ee5cbfSDavid du Colombier {
60*80ee5cbfSDavid du Colombier 	pen_point* lpts = mallocz(npts*sizeof(pen_point), 1);
61*80ee5cbfSDavid du Colombier 	point_list *p = malloc(sizeof(point_list));
62*80ee5cbfSDavid du Colombier 
63*80ee5cbfSDavid du Colombier 	p->npts = npts;
64*80ee5cbfSDavid du Colombier 	p->pts = lpts;
65*80ee5cbfSDavid du Colombier 	p->next = l;       /*Order doesn't matter, so we stick on end.*/
66*80ee5cbfSDavid du Colombier 
67*80ee5cbfSDavid du Colombier 	/*Copy points.*/
68*80ee5cbfSDavid du Colombier 
69*80ee5cbfSDavid du Colombier 	bcopy(pts, lpts, npts * sizeof(pen_point));
70*80ee5cbfSDavid du Colombier 
71*80ee5cbfSDavid du Colombier 	return(p);
72*80ee5cbfSDavid du Colombier }
73*80ee5cbfSDavid du Colombier 
74*80ee5cbfSDavid du Colombier 
75*80ee5cbfSDavid du Colombier static void
delete_examples(point_list * l)76*80ee5cbfSDavid du Colombier delete_examples(point_list* l)
77*80ee5cbfSDavid du Colombier {
78*80ee5cbfSDavid du Colombier 	point_list* p;
79*80ee5cbfSDavid du Colombier 
80*80ee5cbfSDavid du Colombier 	for( ; l != nil; l = p ) {
81*80ee5cbfSDavid du Colombier 		p = l->next;
82*80ee5cbfSDavid du Colombier 		free(l->pts);
83*80ee5cbfSDavid du Colombier 		free(l);
84*80ee5cbfSDavid du Colombier 	}
85*80ee5cbfSDavid du Colombier }
86*80ee5cbfSDavid du Colombier 
87*80ee5cbfSDavid du Colombier /*
88*80ee5cbfSDavid du Colombier  * Local functions
89*80ee5cbfSDavid du Colombier  */
90*80ee5cbfSDavid du Colombier 
91*80ee5cbfSDavid du Colombier /*
92*80ee5cbfSDavid du Colombier  * recognize_internal-Form Vector, use Classifier to classify, return char.
93*80ee5cbfSDavid du Colombier  */
94*80ee5cbfSDavid du Colombier 
95*80ee5cbfSDavid du Colombier static char*
recognize_internal(rClassifier * rec,Stroke * str,int *)96*80ee5cbfSDavid du Colombier recognize_internal(rClassifier* rec, Stroke* str, int*)
97*80ee5cbfSDavid du Colombier {
98*80ee5cbfSDavid du Colombier 	char *res;
99*80ee5cbfSDavid du Colombier 	point_list *stroke;
100*80ee5cbfSDavid du Colombier 
101*80ee5cbfSDavid du Colombier 	stroke = add_example(nil, str->npts, str->pts);
102*80ee5cbfSDavid du Colombier 	if (stroke == nil) return(nil);
103*80ee5cbfSDavid du Colombier 
104*80ee5cbfSDavid du Colombier 	res = lialg_recognize_stroke(rec, stroke);
105*80ee5cbfSDavid du Colombier 
106*80ee5cbfSDavid du Colombier 	delete_examples(stroke);
107*80ee5cbfSDavid du Colombier 	return(res);
108*80ee5cbfSDavid du Colombier }
109*80ee5cbfSDavid du Colombier 
110*80ee5cbfSDavid du Colombier /*
111*80ee5cbfSDavid du Colombier  * file_path-Construct pathname, check for proper extension.
112*80ee5cbfSDavid du Colombier  */
113*80ee5cbfSDavid du Colombier 
114*80ee5cbfSDavid du Colombier static int
file_path(char * dir,char * filename,char * pathname)115*80ee5cbfSDavid du Colombier   file_path(char* dir,char* filename,char* pathname)
116*80ee5cbfSDavid du Colombier {
117*80ee5cbfSDavid du Colombier 	char* dot;
118*80ee5cbfSDavid du Colombier 
119*80ee5cbfSDavid du Colombier 	/*Check for proper extension on file name.*/
120*80ee5cbfSDavid du Colombier 
121*80ee5cbfSDavid du Colombier 	dot = strrchr(filename,'.');
122*80ee5cbfSDavid du Colombier 
123*80ee5cbfSDavid du Colombier 	if( dot == nil ) {
124*80ee5cbfSDavid du Colombier 		return(-1);
125*80ee5cbfSDavid du Colombier 	}
126*80ee5cbfSDavid du Colombier 
127*80ee5cbfSDavid du Colombier 	/*Determine whether a gesture or character classifier.*/
128*80ee5cbfSDavid du Colombier 
129*80ee5cbfSDavid du Colombier 	if( strcmp(dot,LI_CLASSIFIER_EXTENSION) != 0 ) {
130*80ee5cbfSDavid du Colombier 		return(-1);
131*80ee5cbfSDavid du Colombier 	}
132*80ee5cbfSDavid du Colombier 
133*80ee5cbfSDavid du Colombier 	/*Concatenate directory and filename into pathname.*/
134*80ee5cbfSDavid du Colombier 
135*80ee5cbfSDavid du Colombier 	strcpy(pathname,dir);
136*80ee5cbfSDavid du Colombier 	strcat(pathname,"/");
137*80ee5cbfSDavid du Colombier 	strcat(pathname,filename);
138*80ee5cbfSDavid du Colombier 
139*80ee5cbfSDavid du Colombier 	return(0);
140*80ee5cbfSDavid du Colombier }
141*80ee5cbfSDavid du Colombier 
142*80ee5cbfSDavid du Colombier /*read_classifier_points-Read points so classifier can be extended.*/
143*80ee5cbfSDavid du Colombier 
144*80ee5cbfSDavid du Colombier static int
read_classifier_points(FILE * fd,int nclss,point_list ** ex,char ** cnames)145*80ee5cbfSDavid du Colombier read_classifier_points(FILE* fd,int nclss,point_list** ex,char** cnames)
146*80ee5cbfSDavid du Colombier {
147*80ee5cbfSDavid du Colombier 	int i,j,k;
148*80ee5cbfSDavid du Colombier 	char buf[BUFSIZ];
149*80ee5cbfSDavid du Colombier 	int nex = 0;
150*80ee5cbfSDavid du Colombier 	char* names[MAXSCLASSES];
151*80ee5cbfSDavid du Colombier 	point_list* examples[MAXSCLASSES];
152*80ee5cbfSDavid du Colombier 	pen_point* pts;
153*80ee5cbfSDavid du Colombier 	int npts;
154*80ee5cbfSDavid du Colombier 
155*80ee5cbfSDavid du Colombier 	/*Initialize*/
156*80ee5cbfSDavid du Colombier 
157*80ee5cbfSDavid du Colombier 		for( i = 0; i < MAXSCLASSES; i++ ) {
158*80ee5cbfSDavid du Colombier 				names[i] = nil;
159*80ee5cbfSDavid du Colombier 				examples[i] = nil;
160*80ee5cbfSDavid du Colombier 		}
161*80ee5cbfSDavid du Colombier 
162*80ee5cbfSDavid du Colombier 		/*Go thru classes.*/
163*80ee5cbfSDavid du Colombier 
164*80ee5cbfSDavid du Colombier 		for( k = 0; k < nclss; k++ ) {
165*80ee5cbfSDavid du Colombier 
166*80ee5cbfSDavid du Colombier 				/*Read class name and number of examples.*/
167*80ee5cbfSDavid du Colombier 
168*80ee5cbfSDavid du Colombier 				if( fscanf(fd,"%d %s",&nex,buf) != 2 )
169*80ee5cbfSDavid du Colombier 					goto unallocate;
170*80ee5cbfSDavid du Colombier 
171*80ee5cbfSDavid du Colombier 				/*Save class name*/
172*80ee5cbfSDavid du Colombier 
173*80ee5cbfSDavid du Colombier 				names[k] = strdup(buf);
174*80ee5cbfSDavid du Colombier 
175*80ee5cbfSDavid du Colombier 				/*Read examples.*/
176*80ee5cbfSDavid du Colombier 
177*80ee5cbfSDavid du Colombier 				for( i = 0; i < nex; i++ ) {
178*80ee5cbfSDavid du Colombier 
179*80ee5cbfSDavid du Colombier 					/*Read number of points.*/
180*80ee5cbfSDavid du Colombier 
181*80ee5cbfSDavid du Colombier 					if( fscanf(fd,"%d",&npts) != 1 )
182*80ee5cbfSDavid du Colombier 								goto unallocate; /*Boy would I like exceptions!*/
183*80ee5cbfSDavid du Colombier 
184*80ee5cbfSDavid du Colombier 					/*Allocate array for points.*/
185*80ee5cbfSDavid du Colombier 
186*80ee5cbfSDavid du Colombier 					if( (pts = mallocz(npts*sizeof(pen_point), 1)) == nil )
187*80ee5cbfSDavid du Colombier 								goto unallocate;
188*80ee5cbfSDavid du Colombier 
189*80ee5cbfSDavid du Colombier 					/*Read in points.*/
190*80ee5cbfSDavid du Colombier 
191*80ee5cbfSDavid du Colombier 					for( j = 0; j < npts; j++ ) {
192*80ee5cbfSDavid du Colombier 								int x,y;
193*80ee5cbfSDavid du Colombier 								if( fscanf(fd,"%d %d",&x,&y) != 2 ) {
194*80ee5cbfSDavid du Colombier 									delete_pen_point_array(pts);
195*80ee5cbfSDavid du Colombier 									goto unallocate;
196*80ee5cbfSDavid du Colombier 								}
197*80ee5cbfSDavid du Colombier 								pts[j].Point = Pt(x, y);
198*80ee5cbfSDavid du Colombier 					}
199*80ee5cbfSDavid du Colombier 
200*80ee5cbfSDavid du Colombier 					/*Add example*/
201*80ee5cbfSDavid du Colombier 
202*80ee5cbfSDavid du Colombier 					if( (examples[k] = add_example(examples[k],npts,pts)) == nil ) {
203*80ee5cbfSDavid du Colombier 								delete_pen_point_array(pts);
204*80ee5cbfSDavid du Colombier 								goto unallocate;
205*80ee5cbfSDavid du Colombier 					}
206*80ee5cbfSDavid du Colombier 
207*80ee5cbfSDavid du Colombier 					delete_pen_point_array(pts);
208*80ee5cbfSDavid du Colombier 
209*80ee5cbfSDavid du Colombier 				}
210*80ee5cbfSDavid du Colombier 		}
211*80ee5cbfSDavid du Colombier 
212*80ee5cbfSDavid du Colombier /* ari -- end of list of classes */
213*80ee5cbfSDavid du Colombier /* fprint(2, "]\n"); */
214*80ee5cbfSDavid du Colombier 
215*80ee5cbfSDavid du Colombier 		/*Transfer to recognizer.*/
216*80ee5cbfSDavid du Colombier 
217*80ee5cbfSDavid du Colombier 		bcopy(examples,ex,sizeof(examples));
218*80ee5cbfSDavid du Colombier 		bcopy(names,cnames,sizeof(names));
219*80ee5cbfSDavid du Colombier 
220*80ee5cbfSDavid du Colombier 		return(0);
221*80ee5cbfSDavid du Colombier 
222*80ee5cbfSDavid du Colombier 		/*Error. Deallocate memory and return.*/
223*80ee5cbfSDavid du Colombier 
224*80ee5cbfSDavid du Colombier unallocate:
225*80ee5cbfSDavid du Colombier 		for( ; k >= 0; k-- ) {
226*80ee5cbfSDavid du Colombier 				delete_examples(examples[k]);
227*80ee5cbfSDavid du Colombier 				free(names[k]);
228*80ee5cbfSDavid du Colombier 		}
229*80ee5cbfSDavid du Colombier 
230*80ee5cbfSDavid du Colombier 		return(-1);
231*80ee5cbfSDavid du Colombier }
232*80ee5cbfSDavid du Colombier 
233*80ee5cbfSDavid du Colombier /*read_classifier-Read a classifier file.*/
234*80ee5cbfSDavid du Colombier 
read_classifier(FILE * fd,rClassifier * rc)235*80ee5cbfSDavid du Colombier static int read_classifier(FILE* fd,rClassifier* rc)
236*80ee5cbfSDavid du Colombier {
237*80ee5cbfSDavid du Colombier 
238*80ee5cbfSDavid du Colombier 	li_err_msg = nil;
239*80ee5cbfSDavid du Colombier 
240*80ee5cbfSDavid du Colombier 	/*Read in classifier file.*/
241*80ee5cbfSDavid du Colombier 
242*80ee5cbfSDavid du Colombier 	if(fscanf(fd, "%d", &rc->nclasses) != 1)
243*80ee5cbfSDavid du Colombier 		return -1;
244*80ee5cbfSDavid du Colombier 
245*80ee5cbfSDavid du Colombier 	/*Read in the example points, so classifier can be extended.*/
246*80ee5cbfSDavid du Colombier 
247*80ee5cbfSDavid du Colombier 	if( read_classifier_points(fd,rc->nclasses,rc->ex,rc->cnames) != 0 )
248*80ee5cbfSDavid du Colombier 		return -1;
249*80ee5cbfSDavid du Colombier 
250*80ee5cbfSDavid du Colombier 	return(0);
251*80ee5cbfSDavid du Colombier }
252*80ee5cbfSDavid du Colombier 
253*80ee5cbfSDavid du Colombier /*
254*80ee5cbfSDavid du Colombier  * Extension Functions
255*80ee5cbfSDavid du Colombier */
256*80ee5cbfSDavid du Colombier 
257*80ee5cbfSDavid du Colombier /* getClasses and clearState are by Ari */
258*80ee5cbfSDavid du Colombier 
259*80ee5cbfSDavid du Colombier static int
recognizer_getClasses(recognizer r,char *** list,int * nc)260*80ee5cbfSDavid du Colombier recognizer_getClasses (recognizer r, char ***list, int *nc)
261*80ee5cbfSDavid du Colombier {
262*80ee5cbfSDavid du Colombier 	int i, nclasses;
263*80ee5cbfSDavid du Colombier 	li_recognizer* rec;
264*80ee5cbfSDavid du Colombier 	char **ret;
265*80ee5cbfSDavid du Colombier 
266*80ee5cbfSDavid du Colombier 		rec = (li_recognizer*)r->recognizer_specific;
267*80ee5cbfSDavid du Colombier 
268*80ee5cbfSDavid du Colombier 		/*Check for LI recognizer.*/
269*80ee5cbfSDavid du Colombier 
270*80ee5cbfSDavid du Colombier 		if( !CHECK_LI_MAGIC(rec) ) {
271*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
272*80ee5cbfSDavid du Colombier 				return(-1);
273*80ee5cbfSDavid du Colombier 	}
274*80ee5cbfSDavid du Colombier 
275*80ee5cbfSDavid du Colombier 	*nc = nclasses = rec->li_rc.nclasses;
276*80ee5cbfSDavid du Colombier 	ret = malloc(nclasses*sizeof(char*));
277*80ee5cbfSDavid du Colombier 
278*80ee5cbfSDavid du Colombier 	for (i = 0; i < nclasses; i++)
279*80ee5cbfSDavid du Colombier 				ret[i] = rec->li_rc.cnames[i];   /* only the 1st char of the cname */
280*80ee5cbfSDavid du Colombier 	*list = ret;
281*80ee5cbfSDavid du Colombier 		return 0;
282*80ee5cbfSDavid du Colombier }
283*80ee5cbfSDavid du Colombier 
284*80ee5cbfSDavid du Colombier static int
recognizer_clearState(recognizer)285*80ee5cbfSDavid du Colombier recognizer_clearState (recognizer)
286*80ee5cbfSDavid du Colombier {
287*80ee5cbfSDavid du Colombier   /*This operation isn't supported by the LI recognizer.*/
288*80ee5cbfSDavid du Colombier 
289*80ee5cbfSDavid du Colombier   li_err_msg = "Clearing state is not supported by the LI recognizer";
290*80ee5cbfSDavid du Colombier 
291*80ee5cbfSDavid du Colombier   return(-1);
292*80ee5cbfSDavid du Colombier }
293*80ee5cbfSDavid du Colombier 
isa_li(recognizer r)294*80ee5cbfSDavid du Colombier static bool isa_li(recognizer r)
295*80ee5cbfSDavid du Colombier { return(CHECK_LI_MAGIC(r)); }
296*80ee5cbfSDavid du Colombier 
297*80ee5cbfSDavid du Colombier static int
recognizer_train(recognizer,rc *,uint,Stroke *,rec_element *,bool)298*80ee5cbfSDavid du Colombier recognizer_train(recognizer, rc*, uint, Stroke*, rec_element*, bool)
299*80ee5cbfSDavid du Colombier {
300*80ee5cbfSDavid du Colombier   /*This operation isn't supported by the LI recognizer.*/
301*80ee5cbfSDavid du Colombier 
302*80ee5cbfSDavid du Colombier   li_err_msg = "Training is not supported by the LI recognizer";
303*80ee5cbfSDavid du Colombier 
304*80ee5cbfSDavid du Colombier   return(-1);
305*80ee5cbfSDavid du Colombier }
306*80ee5cbfSDavid du Colombier 
307*80ee5cbfSDavid du Colombier int
li_recognizer_get_example(recognizer r,int class,int instance,char ** name,pen_point ** points,int * npts)308*80ee5cbfSDavid du Colombier li_recognizer_get_example (recognizer r,
309*80ee5cbfSDavid du Colombier 						   int		class,
310*80ee5cbfSDavid du Colombier 						   int		instance,
311*80ee5cbfSDavid du Colombier 						   char		**name,
312*80ee5cbfSDavid du Colombier 						   pen_point	**points,
313*80ee5cbfSDavid du Colombier 						   int		*npts)
314*80ee5cbfSDavid du Colombier {
315*80ee5cbfSDavid du Colombier 	li_recognizer   *rec = (li_recognizer*)r->recognizer_specific;
316*80ee5cbfSDavid du Colombier 	int nclasses = rec->li_rc.nclasses;
317*80ee5cbfSDavid du Colombier 	point_list	    *pl;
318*80ee5cbfSDavid du Colombier 
319*80ee5cbfSDavid du Colombier 		if( !CHECK_LI_MAGIC(rec) ) {
320*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
321*80ee5cbfSDavid du Colombier 				return(-1);
322*80ee5cbfSDavid du Colombier 		}
323*80ee5cbfSDavid du Colombier 		if (class > nclasses)
324*80ee5cbfSDavid du Colombier 				return -1;
325*80ee5cbfSDavid du Colombier 		pl = rec->li_rc.canonex[class];
326*80ee5cbfSDavid du Colombier 		while (instance && pl)
327*80ee5cbfSDavid du Colombier 		{
328*80ee5cbfSDavid du Colombier 				pl = pl->next;
329*80ee5cbfSDavid du Colombier 				instance--;
330*80ee5cbfSDavid du Colombier 		}
331*80ee5cbfSDavid du Colombier 		if (!pl)
332*80ee5cbfSDavid du Colombier 				return -1;
333*80ee5cbfSDavid du Colombier 		*name = rec->li_rc.cnames[class];
334*80ee5cbfSDavid du Colombier 		*points = pl->pts;
335*80ee5cbfSDavid du Colombier 		*npts = pl->npts;
336*80ee5cbfSDavid du Colombier 		return pl->npts; /* I hope [sjm] */
337*80ee5cbfSDavid du Colombier }
338*80ee5cbfSDavid du Colombier 
339*80ee5cbfSDavid du Colombier /*
340*80ee5cbfSDavid du Colombier  * API Functions
341*80ee5cbfSDavid du Colombier  */
342*80ee5cbfSDavid du Colombier 
343*80ee5cbfSDavid du Colombier 
344*80ee5cbfSDavid du Colombier /*li_recognizer_load-Load a classifier file.*/
345*80ee5cbfSDavid du Colombier 
li_recognizer_load(recognizer r,char * dir,char * filename)346*80ee5cbfSDavid du Colombier static int li_recognizer_load(recognizer r, char* dir, char* filename)
347*80ee5cbfSDavid du Colombier {
348*80ee5cbfSDavid du Colombier 		FILE *fd;
349*80ee5cbfSDavid du Colombier 		char* pathname;
350*80ee5cbfSDavid du Colombier 		li_recognizer* rec;
351*80ee5cbfSDavid du Colombier 		rClassifier* rc;
352*80ee5cbfSDavid du Colombier 
353*80ee5cbfSDavid du Colombier 		rec = (li_recognizer*)r->recognizer_specific;
354*80ee5cbfSDavid du Colombier 
355*80ee5cbfSDavid du Colombier 		/*Make sure recognizer's OK*/
356*80ee5cbfSDavid du Colombier 
357*80ee5cbfSDavid du Colombier 		if( !CHECK_LI_MAGIC(rec) ) {
358*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
359*80ee5cbfSDavid du Colombier 				return(-1);
360*80ee5cbfSDavid du Colombier 		}
361*80ee5cbfSDavid du Colombier 
362*80ee5cbfSDavid du Colombier 		rc = &(rec->li_rc);
363*80ee5cbfSDavid du Colombier 
364*80ee5cbfSDavid du Colombier 		/*Check parameters.*/
365*80ee5cbfSDavid du Colombier 
366*80ee5cbfSDavid du Colombier 		if( filename == nil ) {
367*80ee5cbfSDavid du Colombier 				li_err_msg = "Invalid parameters";
368*80ee5cbfSDavid du Colombier 				return(-1);
369*80ee5cbfSDavid du Colombier 		}
370*80ee5cbfSDavid du Colombier 
371*80ee5cbfSDavid du Colombier 		/*We let the directory be null.*/
372*80ee5cbfSDavid du Colombier 
373*80ee5cbfSDavid du Colombier 		if( dir == nil || (int)strlen(dir) <= 0 ) {
374*80ee5cbfSDavid du Colombier 				dir = ".";
375*80ee5cbfSDavid du Colombier 		}
376*80ee5cbfSDavid du Colombier 
377*80ee5cbfSDavid du Colombier 		if(0)fprint(2, "dir = %s, filename = %s\n",
378*80ee5cbfSDavid du Colombier 				dir, filename);
379*80ee5cbfSDavid du Colombier 
380*80ee5cbfSDavid du Colombier 		/*Make full pathname and check filename*/
381*80ee5cbfSDavid du Colombier 
382*80ee5cbfSDavid du Colombier 		pathname = malloc((strlen(dir) + strlen(filename) + 2)*sizeof(char));
383*80ee5cbfSDavid du Colombier 		if( file_path(dir, filename, pathname) == -1 ) {
384*80ee5cbfSDavid du Colombier 				free(pathname);
385*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer classifier file";
386*80ee5cbfSDavid du Colombier 				return -1;
387*80ee5cbfSDavid du Colombier 		}
388*80ee5cbfSDavid du Colombier 
389*80ee5cbfSDavid du Colombier 		/* Try to short-circuit the full classifier-file processing. */
390*80ee5cbfSDavid du Colombier 		rc->file_name = pathname;
391*80ee5cbfSDavid du Colombier 		if (lialg_read_classifier_digest(rc) == 0)
392*80ee5cbfSDavid du Colombier 				return(0);
393*80ee5cbfSDavid du Colombier 		rc->file_name = nil;
394*80ee5cbfSDavid du Colombier 
395*80ee5cbfSDavid du Colombier 		/*Open the file*/
396*80ee5cbfSDavid du Colombier 
397*80ee5cbfSDavid du Colombier 		if( (fd = fopen(pathname,"r")) == nil ) {
398*80ee5cbfSDavid du Colombier 				li_err_msg = "Can't open classifier file";
399*80ee5cbfSDavid du Colombier 				if(0)fprint(2, "Can't open %s.\n", pathname);
400*80ee5cbfSDavid du Colombier 				free(pathname);
401*80ee5cbfSDavid du Colombier 				return(-1);
402*80ee5cbfSDavid du Colombier 		}
403*80ee5cbfSDavid du Colombier 
404*80ee5cbfSDavid du Colombier 		/*If rClassifier is OK, then delete it first.*/
405*80ee5cbfSDavid du Colombier 
406*80ee5cbfSDavid du Colombier 		if( rc->file_name != nil ) {
407*80ee5cbfSDavid du Colombier 				free_rClassifier(rc);
408*80ee5cbfSDavid du Colombier 		}
409*80ee5cbfSDavid du Colombier 
410*80ee5cbfSDavid du Colombier 		/*Read classifier.*/
411*80ee5cbfSDavid du Colombier 
412*80ee5cbfSDavid du Colombier 		if( read_classifier(fd,rc) < 0 ) {
413*80ee5cbfSDavid du Colombier 				free(pathname);
414*80ee5cbfSDavid du Colombier 				return(-1);
415*80ee5cbfSDavid du Colombier 		}
416*80ee5cbfSDavid du Colombier 
417*80ee5cbfSDavid du Colombier 		/*Close file.*/
418*80ee5cbfSDavid du Colombier 
419*80ee5cbfSDavid du Colombier 		fclose(fd);
420*80ee5cbfSDavid du Colombier 
421*80ee5cbfSDavid du Colombier 		/*Add classifier name.*/
422*80ee5cbfSDavid du Colombier 
423*80ee5cbfSDavid du Colombier 		rc->file_name = pathname;
424*80ee5cbfSDavid du Colombier 
425*80ee5cbfSDavid du Colombier 		/* Canonicalize examples. */
426*80ee5cbfSDavid du Colombier 		if (lialg_canonicalize_examples(rc) != 0) {
427*80ee5cbfSDavid du Colombier 				free(pathname);
428*80ee5cbfSDavid du Colombier 				rc->file_name = nil;
429*80ee5cbfSDavid du Colombier 				return -1;
430*80ee5cbfSDavid du Colombier 		}
431*80ee5cbfSDavid du Colombier 
432*80ee5cbfSDavid du Colombier 		return(0);
433*80ee5cbfSDavid du Colombier }
434*80ee5cbfSDavid du Colombier 
435*80ee5cbfSDavid du Colombier /*li_recognizer_save-Save a classifier file.*/
436*80ee5cbfSDavid du Colombier 
li_recognizer_save(recognizer,char *,char *)437*80ee5cbfSDavid du Colombier static int li_recognizer_save(recognizer, char*, char*)
438*80ee5cbfSDavid du Colombier {
439*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
440*80ee5cbfSDavid du Colombier 
441*80ee5cbfSDavid du Colombier 		li_err_msg = "Saving is not supported by the LI recognizer";
442*80ee5cbfSDavid du Colombier 		return -1;
443*80ee5cbfSDavid du Colombier }
444*80ee5cbfSDavid du Colombier 
445*80ee5cbfSDavid du Colombier static wordset
li_recognizer_load_dictionary(recognizer,char *,char *)446*80ee5cbfSDavid du Colombier li_recognizer_load_dictionary(recognizer, char*, char*)
447*80ee5cbfSDavid du Colombier {
448*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
449*80ee5cbfSDavid du Colombier 
450*80ee5cbfSDavid du Colombier 		li_err_msg = "Dictionaries are not supported by the LI recognizer";
451*80ee5cbfSDavid du Colombier 		return nil;
452*80ee5cbfSDavid du Colombier }
453*80ee5cbfSDavid du Colombier 
454*80ee5cbfSDavid du Colombier static int
li_recognizer_save_dictionary(recognizer,char *,char *,wordset)455*80ee5cbfSDavid du Colombier li_recognizer_save_dictionary(recognizer, char*, char*, wordset)
456*80ee5cbfSDavid du Colombier {
457*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
458*80ee5cbfSDavid du Colombier 		li_err_msg = "Dictionaries are not supported by the LI recognizer";
459*80ee5cbfSDavid du Colombier 		return -1;
460*80ee5cbfSDavid du Colombier }
461*80ee5cbfSDavid du Colombier 
462*80ee5cbfSDavid du Colombier static int
li_recognizer_free_dictionary(recognizer,wordset)463*80ee5cbfSDavid du Colombier li_recognizer_free_dictionary(recognizer, wordset)
464*80ee5cbfSDavid du Colombier {
465*80ee5cbfSDavid du Colombier   /*This operation isn't supported by the LI recognizer.*/
466*80ee5cbfSDavid du Colombier 
467*80ee5cbfSDavid du Colombier   li_err_msg = "Dictionaries are not supported by the LI recognizer";
468*80ee5cbfSDavid du Colombier 
469*80ee5cbfSDavid du Colombier   return -1;
470*80ee5cbfSDavid du Colombier 
471*80ee5cbfSDavid du Colombier }
472*80ee5cbfSDavid du Colombier 
473*80ee5cbfSDavid du Colombier static int
li_recognizer_add_to_dictionary(recognizer,letterset *,wordset)474*80ee5cbfSDavid du Colombier li_recognizer_add_to_dictionary(recognizer, letterset*, wordset)
475*80ee5cbfSDavid du Colombier {
476*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
477*80ee5cbfSDavid du Colombier 		li_err_msg = "Dictionaries are not supported by the LI recognizer";
478*80ee5cbfSDavid du Colombier 		return -1;
479*80ee5cbfSDavid du Colombier }
480*80ee5cbfSDavid du Colombier 
481*80ee5cbfSDavid du Colombier static int
li_recognizer_delete_from_dictionary(recognizer,letterset *,wordset)482*80ee5cbfSDavid du Colombier li_recognizer_delete_from_dictionary(recognizer, letterset*, wordset)
483*80ee5cbfSDavid du Colombier {
484*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
485*80ee5cbfSDavid du Colombier 		li_err_msg = "Dictionaries are not supported by the LI recognizer";
486*80ee5cbfSDavid du Colombier 		return -1;
487*80ee5cbfSDavid du Colombier }
488*80ee5cbfSDavid du Colombier 
489*80ee5cbfSDavid du Colombier static char*
li_recognizer_error(recognizer rec)490*80ee5cbfSDavid du Colombier li_recognizer_error(recognizer rec)
491*80ee5cbfSDavid du Colombier {
492*80ee5cbfSDavid du Colombier 	char* ret = li_err_msg;
493*80ee5cbfSDavid du Colombier 
494*80ee5cbfSDavid du Colombier 	/*Check for LI recognizer.*/
495*80ee5cbfSDavid du Colombier 
496*80ee5cbfSDavid du Colombier 	if( !CHECK_LI_MAGIC(rec->recognizer_specific) ) {
497*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
498*80ee5cbfSDavid du Colombier 				return nil;
499*80ee5cbfSDavid du Colombier 	}
500*80ee5cbfSDavid du Colombier 	li_err_msg = nil;
501*80ee5cbfSDavid du Colombier 	return ret;
502*80ee5cbfSDavid du Colombier }
503*80ee5cbfSDavid du Colombier 
504*80ee5cbfSDavid du Colombier static int
li_recognizer_clear(recognizer r,bool)505*80ee5cbfSDavid du Colombier li_recognizer_clear(recognizer r, bool)
506*80ee5cbfSDavid du Colombier {
507*80ee5cbfSDavid du Colombier 		li_recognizer* rec;
508*80ee5cbfSDavid du Colombier 
509*80ee5cbfSDavid du Colombier 		rec = (li_recognizer*)r->recognizer_specific;
510*80ee5cbfSDavid du Colombier 		/*Check for LI recognizer.*/
511*80ee5cbfSDavid du Colombier 		if( !CHECK_LI_MAGIC(rec) ) {
512*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
513*80ee5cbfSDavid du Colombier 				return 0;
514*80ee5cbfSDavid du Colombier 		}
515*80ee5cbfSDavid du Colombier 		return 0;
516*80ee5cbfSDavid du Colombier }
517*80ee5cbfSDavid du Colombier 
518*80ee5cbfSDavid du Colombier static int
li_recognizer_set_context(recognizer,rc *)519*80ee5cbfSDavid du Colombier li_recognizer_set_context(recognizer, rc*)
520*80ee5cbfSDavid du Colombier {
521*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
522*80ee5cbfSDavid du Colombier 		li_err_msg = "Contexts are not supported by the LI recognizer";
523*80ee5cbfSDavid du Colombier 		return -1;
524*80ee5cbfSDavid du Colombier }
525*80ee5cbfSDavid du Colombier 
526*80ee5cbfSDavid du Colombier static rc*
li_recognizer_get_context(recognizer)527*80ee5cbfSDavid du Colombier li_recognizer_get_context(recognizer)
528*80ee5cbfSDavid du Colombier {
529*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
530*80ee5cbfSDavid du Colombier 		li_err_msg = "Contexts are not supported by the LI recognizer";
531*80ee5cbfSDavid du Colombier 		return nil;
532*80ee5cbfSDavid du Colombier }
533*80ee5cbfSDavid du Colombier 
534*80ee5cbfSDavid du Colombier static int
li_recognizer_get_buffer(recognizer,uint *,Stroke **)535*80ee5cbfSDavid du Colombier li_recognizer_get_buffer(recognizer, uint*, Stroke**)
536*80ee5cbfSDavid du Colombier {
537*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
538*80ee5cbfSDavid du Colombier 		li_err_msg = "Buffer get/set are not supported by the LI recognizer";
539*80ee5cbfSDavid du Colombier 		return -1;
540*80ee5cbfSDavid du Colombier }
541*80ee5cbfSDavid du Colombier 
542*80ee5cbfSDavid du Colombier static int
li_recognizer_set_buffer(recognizer,uint,Stroke *)543*80ee5cbfSDavid du Colombier li_recognizer_set_buffer(recognizer, uint, Stroke*)
544*80ee5cbfSDavid du Colombier {
545*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
546*80ee5cbfSDavid du Colombier 		li_err_msg = "Buffer get/set are not supported by the LI recognizer";
547*80ee5cbfSDavid du Colombier 		return -1;
548*80ee5cbfSDavid du Colombier }
549*80ee5cbfSDavid du Colombier 
550*80ee5cbfSDavid du Colombier static int
li_recognizer_translate(recognizer r,uint ncs,Stroke * tps,bool,int * nret,rec_alternative ** ret)551*80ee5cbfSDavid du Colombier li_recognizer_translate(recognizer r, uint ncs, Stroke* tps, bool, int* nret, rec_alternative** ret)
552*80ee5cbfSDavid du Colombier {
553*80ee5cbfSDavid du Colombier 	char* clss;
554*80ee5cbfSDavid du Colombier 	li_recognizer* rec;
555*80ee5cbfSDavid du Colombier 	int conf;
556*80ee5cbfSDavid du Colombier 	rClassifier* rc;
557*80ee5cbfSDavid du Colombier 
558*80ee5cbfSDavid du Colombier 	rec = (li_recognizer*)r->recognizer_specific;
559*80ee5cbfSDavid du Colombier 
560*80ee5cbfSDavid du Colombier 	*nret = 0;
561*80ee5cbfSDavid du Colombier 	*ret = nil;
562*80ee5cbfSDavid du Colombier 
563*80ee5cbfSDavid du Colombier 	/*Check for LI recognizer.*/
564*80ee5cbfSDavid du Colombier 
565*80ee5cbfSDavid du Colombier 	if( !CHECK_LI_MAGIC(rec) ) {
566*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
567*80ee5cbfSDavid du Colombier 				return(-1);
568*80ee5cbfSDavid du Colombier 	}
569*80ee5cbfSDavid du Colombier 
570*80ee5cbfSDavid du Colombier 		rc = &(rec->li_rc);
571*80ee5cbfSDavid du Colombier 
572*80ee5cbfSDavid du Colombier 	/*Check for valid parameters.*/
573*80ee5cbfSDavid du Colombier 	if (ncs < 1) {
574*80ee5cbfSDavid du Colombier 				li_err_msg = "Invalid parameters: ncs";
575*80ee5cbfSDavid du Colombier 				return(-1);
576*80ee5cbfSDavid du Colombier 	}
577*80ee5cbfSDavid du Colombier 	if( tps == nil) {
578*80ee5cbfSDavid du Colombier 				li_err_msg = "Invalid parameters: tps";
579*80ee5cbfSDavid du Colombier 				return(-1);
580*80ee5cbfSDavid du Colombier 	}
581*80ee5cbfSDavid du Colombier 	if( nret == nil) {
582*80ee5cbfSDavid du Colombier 				li_err_msg = "Invalid parameters: nret";
583*80ee5cbfSDavid du Colombier 				return(-1);
584*80ee5cbfSDavid du Colombier 	}
585*80ee5cbfSDavid du Colombier 	if( ret == nil) {
586*80ee5cbfSDavid du Colombier 				li_err_msg = "Invalid parameters: ret";
587*80ee5cbfSDavid du Colombier 				return(-1);
588*80ee5cbfSDavid du Colombier 	}
589*80ee5cbfSDavid du Colombier 
590*80ee5cbfSDavid du Colombier 	/*
591*80ee5cbfSDavid du Colombier 	 * Go through the stroke array and recognize. Since this is a single
592*80ee5cbfSDavid du Colombier 	 *   stroke recognizer, each stroke is treated as a separate
593*80ee5cbfSDavid du Colombier 	 *   character or gesture. We allow only characters or gestures
594*80ee5cbfSDavid du Colombier 	 *   to be recognized at one time, since otherwise, handling
595*80ee5cbfSDavid du Colombier 	 *   the display of segmentation would be difficult.
596*80ee5cbfSDavid du Colombier 	*/
597*80ee5cbfSDavid du Colombier 	clss = recognize_internal(rc,tps,&conf);
598*80ee5cbfSDavid du Colombier 	if (clss == nil) {
599*80ee5cbfSDavid du Colombier 				*nret = 1;
600*80ee5cbfSDavid du Colombier 				return(0);
601*80ee5cbfSDavid du Colombier 	}
602*80ee5cbfSDavid du Colombier 
603*80ee5cbfSDavid du Colombier 	/*Return values.*/
604*80ee5cbfSDavid du Colombier 	*nret = 1;
605*80ee5cbfSDavid du Colombier 	return(*clss);
606*80ee5cbfSDavid du Colombier }
607*80ee5cbfSDavid du Colombier 
608*80ee5cbfSDavid du Colombier 
609*80ee5cbfSDavid du Colombier static rec_fn*
li_recognizer_get_extension_functions(recognizer rec)610*80ee5cbfSDavid du Colombier li_recognizer_get_extension_functions(recognizer rec)
611*80ee5cbfSDavid du Colombier {
612*80ee5cbfSDavid du Colombier 	rec_fn* ret;
613*80ee5cbfSDavid du Colombier 
614*80ee5cbfSDavid du Colombier 	/*Check for LI recognizer.*/
615*80ee5cbfSDavid du Colombier 
616*80ee5cbfSDavid du Colombier 	if( !CHECK_LI_MAGIC(rec->recognizer_specific) ) {
617*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
618*80ee5cbfSDavid du Colombier 				return(nil);
619*80ee5cbfSDavid du Colombier 	}
620*80ee5cbfSDavid du Colombier 
621*80ee5cbfSDavid du Colombier 	ret = make_rec_fn_array(LI_NUM_EX_FNS);
622*80ee5cbfSDavid du Colombier 
623*80ee5cbfSDavid du Colombier /* ari -- clearState & getClasses are mine */
624*80ee5cbfSDavid du Colombier 	ret[LI_GET_CLASSES] =	(rec_fn)recognizer_getClasses;
625*80ee5cbfSDavid du Colombier 	ret[LI_CLEAR] =			(rec_fn)recognizer_clearState;
626*80ee5cbfSDavid du Colombier 	ret[LI_ISA_LI] =		(rec_fn)isa_li;
627*80ee5cbfSDavid du Colombier 	ret[LI_TRAIN] =			(rec_fn)recognizer_train;
628*80ee5cbfSDavid du Colombier 
629*80ee5cbfSDavid du Colombier 	return(ret);
630*80ee5cbfSDavid du Colombier }
631*80ee5cbfSDavid du Colombier 
632*80ee5cbfSDavid du Colombier static char**
li_recognizer_get_gesture_names(recognizer)633*80ee5cbfSDavid du Colombier li_recognizer_get_gesture_names(recognizer)
634*80ee5cbfSDavid du Colombier {
635*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
636*80ee5cbfSDavid du Colombier 		li_err_msg = "Gestures are not supported by the LI recognizer";
637*80ee5cbfSDavid du Colombier 		return nil;
638*80ee5cbfSDavid du Colombier }
639*80ee5cbfSDavid du Colombier 
640*80ee5cbfSDavid du Colombier static xgesture
li_recognizer_set_gesture_action(recognizer,char *,xgesture,void *)641*80ee5cbfSDavid du Colombier li_recognizer_set_gesture_action(recognizer, char*, xgesture, void*)
642*80ee5cbfSDavid du Colombier {
643*80ee5cbfSDavid du Colombier 		/*This operation isn't supported by the LI recognizer.*/
644*80ee5cbfSDavid du Colombier 		li_err_msg = "Gestures are not supported by the LI recognizer";
645*80ee5cbfSDavid du Colombier 		return nil;
646*80ee5cbfSDavid du Colombier }
647*80ee5cbfSDavid du Colombier 
648*80ee5cbfSDavid du Colombier /*
649*80ee5cbfSDavid du Colombier  * Exported Functions
650*80ee5cbfSDavid du Colombier */
651*80ee5cbfSDavid du Colombier 
652*80ee5cbfSDavid du Colombier /*RECOGNIZER_INITIALIZE-Initialize the recognizer.*/
653*80ee5cbfSDavid du Colombier 
654*80ee5cbfSDavid du Colombier /* note from ari:  this expands via pre-processor to
655*80ee5cbfSDavid du Colombier  *
656*80ee5cbfSDavid du Colombier  * recognizer __recognizer_internal_initialize(rec_info* ri)
657*80ee5cbfSDavid du Colombier  */
658*80ee5cbfSDavid du Colombier 
RECOGNIZER_INITIALIZE(ri)659*80ee5cbfSDavid du Colombier RECOGNIZER_INITIALIZE(ri)
660*80ee5cbfSDavid du Colombier {
661*80ee5cbfSDavid du Colombier 	recognizer r;
662*80ee5cbfSDavid du Colombier 	li_recognizer* rec;
663*80ee5cbfSDavid du Colombier 	int i;
664*80ee5cbfSDavid du Colombier 
665*80ee5cbfSDavid du Colombier 	/*Check that locale matches.*/
666*80ee5cbfSDavid du Colombier 
667*80ee5cbfSDavid du Colombier 	if( strcmp(ri->ri_locale,LI_SUPPORTED_LOCALE) != 0 ) {
668*80ee5cbfSDavid du Colombier 		li_err_msg = "Not a supported locale";
669*80ee5cbfSDavid du Colombier /* fprint(2, "Locale error.\n");*/
670*80ee5cbfSDavid du Colombier 		return(nil);
671*80ee5cbfSDavid du Colombier 	}
672*80ee5cbfSDavid du Colombier 
673*80ee5cbfSDavid du Colombier 	/*
674*80ee5cbfSDavid du Colombier 	 * Check that character sets match. Note that this is only approximate,
675*80ee5cbfSDavid du Colombier 	 * since the classifier file will have more information.
676*80ee5cbfSDavid du Colombier 	*/
677*80ee5cbfSDavid du Colombier 
678*80ee5cbfSDavid du Colombier 	if( ri->ri_subset != nil ) {
679*80ee5cbfSDavid du Colombier 	  for(i = 0; ri->ri_subset[i] != nil; i++ ) {
680*80ee5cbfSDavid du Colombier 
681*80ee5cbfSDavid du Colombier 		if( strcmp(ri->ri_subset[i],UPPERCASE) != 0 &&
682*80ee5cbfSDavid du Colombier 			strcmp(ri->ri_subset[i],LOWERCASE) != 0  &&
683*80ee5cbfSDavid du Colombier 			strcmp(ri->ri_subset[i],DIGITS) != 0 &&
684*80ee5cbfSDavid du Colombier 			strcmp(ri->ri_subset[i],GESTURE) != 0 ) {
685*80ee5cbfSDavid du Colombier 		  li_err_msg = "Not a supported character set";
686*80ee5cbfSDavid du Colombier /* fprint(2, "charset error.\n"); */
687*80ee5cbfSDavid du Colombier 
688*80ee5cbfSDavid du Colombier 		  return(nil);
689*80ee5cbfSDavid du Colombier 		}
690*80ee5cbfSDavid du Colombier 	  }
691*80ee5cbfSDavid du Colombier 	}
692*80ee5cbfSDavid du Colombier 
693*80ee5cbfSDavid du Colombier /* ari */
694*80ee5cbfSDavid du Colombier 	r = make_recognizer(ri);
695*80ee5cbfSDavid du Colombier /* fprint(2, "past make_recognizer.\n"); */
696*80ee5cbfSDavid du Colombier 
697*80ee5cbfSDavid du Colombier 	if( r == nil ) {
698*80ee5cbfSDavid du Colombier 		li_err_msg = "Can't allocate storage";
699*80ee5cbfSDavid du Colombier 
700*80ee5cbfSDavid du Colombier 		return(nil);
701*80ee5cbfSDavid du Colombier 	}
702*80ee5cbfSDavid du Colombier 
703*80ee5cbfSDavid du Colombier 	/*Make a LI recognizer structure.*/
704*80ee5cbfSDavid du Colombier 
705*80ee5cbfSDavid du Colombier 
706*80ee5cbfSDavid du Colombier 	/* rec = (li_recognizer*)safe_malloc(sizeof(li_recognizer))) == nil ); */
707*80ee5cbfSDavid du Colombier 
708*80ee5cbfSDavid du Colombier 	rec = malloc(sizeof(li_recognizer));
709*80ee5cbfSDavid du Colombier 
710*80ee5cbfSDavid du Colombier 	r->recognizer_specific = rec;
711*80ee5cbfSDavid du Colombier 
712*80ee5cbfSDavid du Colombier 	rec->li_rc.file_name = nil;
713*80ee5cbfSDavid du Colombier 	rec->li_rc.nclasses = 0;
714*80ee5cbfSDavid du Colombier 
715*80ee5cbfSDavid du Colombier 	/*Initialize the recognizer struct.*/
716*80ee5cbfSDavid du Colombier 
717*80ee5cbfSDavid du Colombier 	r->recognizer_load_state = li_recognizer_load;
718*80ee5cbfSDavid du Colombier 	r->recognizer_save_state = li_recognizer_save;
719*80ee5cbfSDavid du Colombier 	r->recognizer_load_dictionary = li_recognizer_load_dictionary;
720*80ee5cbfSDavid du Colombier 	r->recognizer_save_dictionary = li_recognizer_save_dictionary;
721*80ee5cbfSDavid du Colombier 	r->recognizer_free_dictionary = li_recognizer_free_dictionary;
722*80ee5cbfSDavid du Colombier 	r->recognizer_add_to_dictionary = li_recognizer_add_to_dictionary;
723*80ee5cbfSDavid du Colombier 	r->recognizer_delete_from_dictionary = li_recognizer_delete_from_dictionary;
724*80ee5cbfSDavid du Colombier 	r->recognizer_error = li_recognizer_error;
725*80ee5cbfSDavid du Colombier 	r->recognizer_translate = li_recognizer_translate;
726*80ee5cbfSDavid du Colombier 	r->recognizer_get_context = li_recognizer_get_context;
727*80ee5cbfSDavid du Colombier 	r->recognizer_set_context = li_recognizer_set_context;
728*80ee5cbfSDavid du Colombier 	r->recognizer_get_buffer = li_recognizer_get_buffer;
729*80ee5cbfSDavid du Colombier 	r->recognizer_set_buffer = li_recognizer_set_buffer;
730*80ee5cbfSDavid du Colombier 	r->recognizer_clear = li_recognizer_clear;
731*80ee5cbfSDavid du Colombier 	r->recognizer_get_extension_functions =
732*80ee5cbfSDavid du Colombier 	  li_recognizer_get_extension_functions;
733*80ee5cbfSDavid du Colombier 	r->recognizer_get_gesture_names = li_recognizer_get_gesture_names;
734*80ee5cbfSDavid du Colombier 	r->recognizer_set_gesture_action =
735*80ee5cbfSDavid du Colombier 	  li_recognizer_set_gesture_action;
736*80ee5cbfSDavid du Colombier 
737*80ee5cbfSDavid du Colombier 	/*Initialize LI Magic Number.*/
738*80ee5cbfSDavid du Colombier 
739*80ee5cbfSDavid du Colombier 	rec->li_magic = LI_MAGIC;
740*80ee5cbfSDavid du Colombier 
741*80ee5cbfSDavid du Colombier 	/*Initialize rClassifier.*/
742*80ee5cbfSDavid du Colombier 
743*80ee5cbfSDavid du Colombier 	rec->li_rc.file_name = nil;
744*80ee5cbfSDavid du Colombier 
745*80ee5cbfSDavid du Colombier 	for( i = 0; i < MAXSCLASSES; i++ ) {
746*80ee5cbfSDavid du Colombier 		rec->li_rc.ex[i] = nil;
747*80ee5cbfSDavid du Colombier 		rec->li_rc.cnames[i] = nil;
748*80ee5cbfSDavid du Colombier 	}
749*80ee5cbfSDavid du Colombier 
750*80ee5cbfSDavid du Colombier 	lialg_initialize(&rec->li_rc);
751*80ee5cbfSDavid du Colombier 
752*80ee5cbfSDavid du Colombier 	/*Get rid of error message. Not needed here.*/
753*80ee5cbfSDavid du Colombier 	li_err_msg = nil;
754*80ee5cbfSDavid du Colombier 
755*80ee5cbfSDavid du Colombier 	return(r);
756*80ee5cbfSDavid du Colombier }
757*80ee5cbfSDavid du Colombier 
758*80ee5cbfSDavid du Colombier /*free_rClassifier-Free the rClassifier.*/
759*80ee5cbfSDavid du Colombier 
760*80ee5cbfSDavid du Colombier static void
free_rClassifier(rClassifier * rc)761*80ee5cbfSDavid du Colombier free_rClassifier(rClassifier* rc)
762*80ee5cbfSDavid du Colombier {
763*80ee5cbfSDavid du Colombier 	int i;
764*80ee5cbfSDavid du Colombier 
765*80ee5cbfSDavid du Colombier 	if( rc->file_name != nil) {
766*80ee5cbfSDavid du Colombier 		free(rc->file_name);
767*80ee5cbfSDavid du Colombier 	}
768*80ee5cbfSDavid du Colombier 
769*80ee5cbfSDavid du Colombier 	for( i = 0; rc->ex[i] != nil; i++) {
770*80ee5cbfSDavid du Colombier 		delete_examples(rc->ex[i]);
771*80ee5cbfSDavid du Colombier 		free(rc->cnames[i]);
772*80ee5cbfSDavid du Colombier 	}
773*80ee5cbfSDavid du Colombier 
774*80ee5cbfSDavid du Colombier }
775*80ee5cbfSDavid du Colombier 
776*80ee5cbfSDavid du Colombier /*RECOGNIZER_FINALIZE-Deallocate the recognizer, finalize.*/
777*80ee5cbfSDavid du Colombier 
RECOGNIZER_FINALIZE(r)778*80ee5cbfSDavid du Colombier RECOGNIZER_FINALIZE(r)
779*80ee5cbfSDavid du Colombier {
780*80ee5cbfSDavid du Colombier 		li_recognizer* rec = (li_recognizer*)r->recognizer_specific;
781*80ee5cbfSDavid du Colombier 
782*80ee5cbfSDavid du Colombier 		/*Make sure this is a li_recognizer first*/
783*80ee5cbfSDavid du Colombier 		if( !CHECK_LI_MAGIC(rec) ) {
784*80ee5cbfSDavid du Colombier 				li_err_msg = "Not a LI recognizer";
785*80ee5cbfSDavid du Colombier 				return(-1);
786*80ee5cbfSDavid du Colombier 	}
787*80ee5cbfSDavid du Colombier 
788*80ee5cbfSDavid du Colombier 		/*Deallocate rClassifier resources.*/
789*80ee5cbfSDavid du Colombier 
790*80ee5cbfSDavid du Colombier 		free_rClassifier(&(rec->li_rc));
791*80ee5cbfSDavid du Colombier 
792*80ee5cbfSDavid du Colombier 		/*Deallocate the li_recognizer struct.*/
793*80ee5cbfSDavid du Colombier 		free(rec);
794*80ee5cbfSDavid du Colombier 
795*80ee5cbfSDavid du Colombier 		/*Deallocate the recognizer*/
796*80ee5cbfSDavid du Colombier 		delete_recognizer(r);
797*80ee5cbfSDavid du Colombier 
798*80ee5cbfSDavid du Colombier 		return(0);
799*80ee5cbfSDavid du Colombier }
800*80ee5cbfSDavid du Colombier 
801*80ee5cbfSDavid du Colombier 
802*80ee5cbfSDavid du Colombier /* **************************************************
803*80ee5cbfSDavid du Colombier 
804*80ee5cbfSDavid du Colombier   Implementation of the Li/Yeung recognition algorithm
805*80ee5cbfSDavid du Colombier 
806*80ee5cbfSDavid du Colombier ************************************************** */
807*80ee5cbfSDavid du Colombier 
808*80ee5cbfSDavid du Colombier #define	WORST_SCORE	0x7fffffff
809*80ee5cbfSDavid du Colombier 
810*80ee5cbfSDavid du Colombier /* Dynamic programming parameters */
811*80ee5cbfSDavid du Colombier #define	DP_BAND		3
812*80ee5cbfSDavid du Colombier #define	MIN_SIM		0
813*80ee5cbfSDavid du Colombier #define	MAX_DIST	0x7fffffff
814*80ee5cbfSDavid du Colombier #define	SIM_THLD	60	/* x 100 */
815*80ee5cbfSDavid du Colombier #define	DIST_THLD	3200	/* x 100 */
816*80ee5cbfSDavid du Colombier 
817*80ee5cbfSDavid du Colombier /* Low-pass filter parameters -- empirically derived */
818*80ee5cbfSDavid du Colombier #define	LP_FILTER_WIDTH	6
819*80ee5cbfSDavid du Colombier #define	LP_FILTER_ITERS	8
820*80ee5cbfSDavid du Colombier #define	LP_FILTER_THLD	250	/* x 100 */
821*80ee5cbfSDavid du Colombier #define	LP_FILTER_MIN	5
822*80ee5cbfSDavid du Colombier 
823*80ee5cbfSDavid du Colombier /* Pseudo-extrema parameters -- empirically derived */
824*80ee5cbfSDavid du Colombier #define	PE_AL_THLD	1500	/* x 100 */
825*80ee5cbfSDavid du Colombier #define	PE_ATCR_THLD	135	/* x 100 */
826*80ee5cbfSDavid du Colombier 
827*80ee5cbfSDavid du Colombier /* Contour-angle derivation parameters */
828*80ee5cbfSDavid du Colombier #define	T_ONE		1
829*80ee5cbfSDavid du Colombier #define	T_TWO		20
830*80ee5cbfSDavid du Colombier 
831*80ee5cbfSDavid du Colombier /* Pre-processing and canonicalization parameters */
832*80ee5cbfSDavid du Colombier #define	CANONICAL_X	108
833*80ee5cbfSDavid du Colombier #define	CANONICAL_Y	128
834*80ee5cbfSDavid du Colombier #define	DIST_SQ_THRESHOLD   (3*3)	/* copied from fv.h */
835*80ee5cbfSDavid du Colombier #define	NCANONICAL	50
836*80ee5cbfSDavid du Colombier 
837*80ee5cbfSDavid du Colombier /* Tap-handling parameters */
838*80ee5cbfSDavid du Colombier #define	TAP_CHAR	"."
839*80ee5cbfSDavid du Colombier #define	TAP_TIME_THLD	150	    /* msec */
840*80ee5cbfSDavid du Colombier #define	TAP_DIST_THLD	75	    /* dx * dx + dy * dy */
841*80ee5cbfSDavid du Colombier #define	TAP_PATHLEN	1000	    /* x 100 */
842*80ee5cbfSDavid du Colombier 
843*80ee5cbfSDavid du Colombier 
844*80ee5cbfSDavid du Colombier /* region types */
845*80ee5cbfSDavid du Colombier #define	RGN_CONVEX  0
846*80ee5cbfSDavid du Colombier #define	RGN_CONCAVE 1
847*80ee5cbfSDavid du Colombier #define	RGN_PLAIN   2
848*80ee5cbfSDavid du Colombier #define	RGN_PSEUDO  3
849*80ee5cbfSDavid du Colombier 
850*80ee5cbfSDavid du Colombier 
851*80ee5cbfSDavid du Colombier typedef struct RegionList {
852*80ee5cbfSDavid du Colombier 	int start;
853*80ee5cbfSDavid du Colombier 	int end;
854*80ee5cbfSDavid du Colombier 	int type;
855*80ee5cbfSDavid du Colombier 	struct RegionList *next;
856*80ee5cbfSDavid du Colombier } region_list;
857*80ee5cbfSDavid du Colombier 
858*80ee5cbfSDavid du Colombier 
859*80ee5cbfSDavid du Colombier /* direction-code table; indexed by dx, dy */
860*80ee5cbfSDavid du Colombier static int lialg_dctbl[3][3] = {{1, 0, 7}, {2, 0x7FFFFFFF, 6}, {3, 4, 5}};
861*80ee5cbfSDavid du Colombier 
862*80ee5cbfSDavid du Colombier /* low-pass filter weights */
863*80ee5cbfSDavid du Colombier static int lialg_lpfwts[2 * LP_FILTER_WIDTH + 1];
864*80ee5cbfSDavid du Colombier static int lialg_lpfconst = -1;
865*80ee5cbfSDavid du Colombier 
866*80ee5cbfSDavid du Colombier 
867*80ee5cbfSDavid du Colombier static int lialg_preprocess_stroke(point_list *);
868*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dominant_points(point_list *);
869*80ee5cbfSDavid du Colombier static point_list *lialg_interpolate_points(point_list *);
870*80ee5cbfSDavid du Colombier static void lialg_bresline(pen_point *, pen_point *, point_list *, int *);
871*80ee5cbfSDavid du Colombier static void lialg_compute_chain_code(point_list *);
872*80ee5cbfSDavid du Colombier static void lialg_compute_unit_chain_code(point_list *);
873*80ee5cbfSDavid du Colombier static region_list *lialg_compute_regions(point_list *);
874*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dompts(point_list *, region_list *);
875*80ee5cbfSDavid du Colombier static int *lialg_compute_contour_angle_set(point_list *, region_list *);
876*80ee5cbfSDavid du Colombier static void lialg_score_stroke(point_list *, point_list *, int *, int *);
877*80ee5cbfSDavid du Colombier static int lialg_compute_similarity(point_list *, point_list *);
878*80ee5cbfSDavid du Colombier static int lialg_compute_distance(point_list *, point_list *);
879*80ee5cbfSDavid du Colombier 
880*80ee5cbfSDavid du Colombier static int lialg_read_classifier_digest(rClassifier *);
881*80ee5cbfSDavid du Colombier 
882*80ee5cbfSDavid du Colombier static int lialg_canonicalize_examples(rClassifier *);
883*80ee5cbfSDavid du Colombier static int lialg_canonicalize_example_stroke(point_list *);
884*80ee5cbfSDavid du Colombier static int lialg_compute_equipoints(point_list *);
885*80ee5cbfSDavid du Colombier 
886*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen(point_list *);
887*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen_subset(point_list *, int, int);
888*80ee5cbfSDavid du Colombier static int lialg_filter_points(point_list *);
889*80ee5cbfSDavid du Colombier static int lialg_translate_points(point_list *, int, int, int, int);
890*80ee5cbfSDavid du Colombier static void lialg_get_bounding_box(point_list *, int *, int *, int *, int *);
891*80ee5cbfSDavid du Colombier static void lialg_compute_lpf_parameters();
892*80ee5cbfSDavid du Colombier static int isqrt(int);
893*80ee5cbfSDavid du Colombier static int likeatan(int, int);
894*80ee5cbfSDavid du Colombier static int quadr(int);
895*80ee5cbfSDavid du Colombier 
896*80ee5cbfSDavid du Colombier 
897*80ee5cbfSDavid du Colombier /*************************************************************
898*80ee5cbfSDavid du Colombier 
899*80ee5cbfSDavid du Colombier   Core routines for the Li/Yeung recognition algorithm
900*80ee5cbfSDavid du Colombier 
901*80ee5cbfSDavid du Colombier  *************************************************************/
902*80ee5cbfSDavid du Colombier 
lialg_initialize(rClassifier * rec)903*80ee5cbfSDavid du Colombier static void lialg_initialize(rClassifier *rec) {
904*80ee5cbfSDavid du Colombier 	int i;
905*80ee5cbfSDavid du Colombier 
906*80ee5cbfSDavid du Colombier 	/* Initialize the dompts arrays. */
907*80ee5cbfSDavid du Colombier 	for (i = 0; i < MAXSCLASSES; i++) {
908*80ee5cbfSDavid du Colombier 		rec->dompts[i] = nil;
909*80ee5cbfSDavid du Colombier 	}
910*80ee5cbfSDavid du Colombier }
911*80ee5cbfSDavid du Colombier 
912*80ee5cbfSDavid du Colombier 
913*80ee5cbfSDavid du Colombier /*
914*80ee5cbfSDavid du Colombier  *  Main recognition routine -- called by HRE API.
915*80ee5cbfSDavid du Colombier  */
lialg_recognize_stroke(rClassifier * rec,point_list * stroke)916*80ee5cbfSDavid du Colombier static char *lialg_recognize_stroke(rClassifier *rec, point_list *stroke) {
917*80ee5cbfSDavid du Colombier 		int i;
918*80ee5cbfSDavid du Colombier 		char *name = nil;
919*80ee5cbfSDavid du Colombier 		point_list *input_dompts = nil;
920*80ee5cbfSDavid du Colombier 		char *best_name = nil;
921*80ee5cbfSDavid du Colombier 		int best_score = WORST_SCORE;
922*80ee5cbfSDavid du Colombier 		char *curr_name;
923*80ee5cbfSDavid du Colombier 		point_list *curr_dompts;
924*80ee5cbfSDavid du Colombier 
925*80ee5cbfSDavid du Colombier 		/*    (void)gettimeofday(&stv, nil);*/
926*80ee5cbfSDavid du Colombier 
927*80ee5cbfSDavid du Colombier 		if (stroke->npts < 1) goto done;
928*80ee5cbfSDavid du Colombier 
929*80ee5cbfSDavid du Colombier 		/* Check for tap. */
930*80ee5cbfSDavid du Colombier 
931*80ee5cbfSDavid du Colombier 		/* First thing is to filter out ``close points.'' */
932*80ee5cbfSDavid du Colombier 		if (lialg_filter_points(stroke) != 0) return(nil);
933*80ee5cbfSDavid du Colombier 
934*80ee5cbfSDavid du Colombier 		/* Unfortunately, we don't have the actual time that each point */
935*80ee5cbfSDavid du Colombier 		/* was recorded (i.e., dt is invalid).  Hence, we have to use a */
936*80ee5cbfSDavid du Colombier 		/* heuristic based on total distance and the number of points. */
937*80ee5cbfSDavid du Colombier 		if (stroke->npts == 1 || lialg_compute_pathlen(stroke) < TAP_PATHLEN)
938*80ee5cbfSDavid du Colombier 			return(TAP_CHAR);
939*80ee5cbfSDavid du Colombier 
940*80ee5cbfSDavid du Colombier 		/* Pre-process input stroke. */
941*80ee5cbfSDavid du Colombier 		if (lialg_preprocess_stroke(stroke) != 0) goto done;
942*80ee5cbfSDavid du Colombier 
943*80ee5cbfSDavid du Colombier 		/* Compute its dominant points. */
944*80ee5cbfSDavid du Colombier 		input_dompts = lialg_compute_dominant_points(stroke);
945*80ee5cbfSDavid du Colombier 		if (input_dompts == nil) goto done;
946*80ee5cbfSDavid du Colombier 
947*80ee5cbfSDavid du Colombier 		/* Score input stroke against every class in classifier. */
948*80ee5cbfSDavid du Colombier 		for (i = 0, curr_name = rec->cnames[i], curr_dompts = rec->dompts[i];
949*80ee5cbfSDavid du Colombier 				i < MAXSCLASSES && curr_name != nil && curr_dompts != nil;
950*80ee5cbfSDavid du Colombier 				i++, curr_name = rec->cnames[i], curr_dompts = rec->dompts[i]) {
951*80ee5cbfSDavid du Colombier 				int sim;
952*80ee5cbfSDavid du Colombier 				int dist;
953*80ee5cbfSDavid du Colombier 				int curr_score;
954*80ee5cbfSDavid du Colombier 
955*80ee5cbfSDavid du Colombier 				lialg_score_stroke(input_dompts, curr_dompts, &sim, &dist);
956*80ee5cbfSDavid du Colombier 				curr_score = dist;
957*80ee5cbfSDavid du Colombier 
958*80ee5cbfSDavid du Colombier 				if (lidebug && curr_score < DIST_THLD)
959*80ee5cbfSDavid du Colombier 				fprint(2, "(%s, %d, %d) ", curr_name, sim, dist);
960*80ee5cbfSDavid du Colombier 
961*80ee5cbfSDavid du Colombier 				/* Is it the best so far? */
962*80ee5cbfSDavid du Colombier 				if (curr_score < best_score && curr_score <= DIST_THLD) {
963*80ee5cbfSDavid du Colombier 				best_score = curr_score;
964*80ee5cbfSDavid du Colombier 				best_name = curr_name;
965*80ee5cbfSDavid du Colombier 				}
966*80ee5cbfSDavid du Colombier 		}
967*80ee5cbfSDavid du Colombier 
968*80ee5cbfSDavid du Colombier 		if (lidebug)
969*80ee5cbfSDavid du Colombier 				fprint(2, "\n");
970*80ee5cbfSDavid du Colombier 
971*80ee5cbfSDavid du Colombier 		/* No errors. */
972*80ee5cbfSDavid du Colombier 		name = best_name;
973*80ee5cbfSDavid du Colombier 
974*80ee5cbfSDavid du Colombier done:
975*80ee5cbfSDavid du Colombier 		delete_examples(input_dompts);
976*80ee5cbfSDavid du Colombier 		return(name);
977*80ee5cbfSDavid du Colombier }
978*80ee5cbfSDavid du Colombier 
979*80ee5cbfSDavid du Colombier 
lialg_preprocess_stroke(point_list * points)980*80ee5cbfSDavid du Colombier static int lialg_preprocess_stroke(point_list *points) {
981*80ee5cbfSDavid du Colombier 	int minx, miny, maxx, maxy, xrange, yrange, scale, xoff, yoff;
982*80ee5cbfSDavid du Colombier 
983*80ee5cbfSDavid du Colombier 	/* Filter out points that are too close. */
984*80ee5cbfSDavid du Colombier 	/* We did this earlier, when we checked for a tap. */
985*80ee5cbfSDavid du Colombier /*
986*80ee5cbfSDavid du Colombier 	if (lialg_filter_points(points) != 0) return(-1);
987*80ee5cbfSDavid du Colombier */
988*80ee5cbfSDavid du Colombier 
989*80ee5cbfSDavid du Colombier /*    assert(points->npts > 0);*/
990*80ee5cbfSDavid du Colombier 
991*80ee5cbfSDavid du Colombier 	/* Scale up to avoid conversion errors. */
992*80ee5cbfSDavid du Colombier 	lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
993*80ee5cbfSDavid du Colombier 	xrange = maxx - minx;
994*80ee5cbfSDavid du Colombier 	yrange = maxy - miny;
995*80ee5cbfSDavid du Colombier 	scale = ( ((100 * xrange + CANONICAL_X / 2) / CANONICAL_X) >
996*80ee5cbfSDavid du Colombier 			  ((100 * yrange + CANONICAL_Y / 2) / CANONICAL_Y))
997*80ee5cbfSDavid du Colombier 	  ? (100 * CANONICAL_X + xrange / 2) / xrange
998*80ee5cbfSDavid du Colombier 	  : (100 * CANONICAL_Y + yrange / 2) / yrange;
999*80ee5cbfSDavid du Colombier 	if (lialg_translate_points(points, minx, miny, scale, scale) != 0)
1000*80ee5cbfSDavid du Colombier 		return(-1);
1001*80ee5cbfSDavid du Colombier 
1002*80ee5cbfSDavid du Colombier 	/* Center the stroke. */
1003*80ee5cbfSDavid du Colombier 	lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
1004*80ee5cbfSDavid du Colombier 	xrange = maxx - minx;
1005*80ee5cbfSDavid du Colombier 	yrange = maxy - miny;
1006*80ee5cbfSDavid du Colombier 	xoff = -((CANONICAL_X - xrange + 1) / 2);
1007*80ee5cbfSDavid du Colombier 	yoff = -((CANONICAL_Y - yrange + 1) / 2);
1008*80ee5cbfSDavid du Colombier 	if (lialg_translate_points(points, xoff, yoff, 100, 100) != 0) return(-1);
1009*80ee5cbfSDavid du Colombier 
1010*80ee5cbfSDavid du Colombier 	/* Store the x and y ranges in the point list. */
1011*80ee5cbfSDavid du Colombier 	xrange = maxx - minx;
1012*80ee5cbfSDavid du Colombier 	yrange = maxy - miny;
1013*80ee5cbfSDavid du Colombier 	points->xrange = xrange;
1014*80ee5cbfSDavid du Colombier 	points->yrange = yrange;
1015*80ee5cbfSDavid du Colombier 
1016*80ee5cbfSDavid du Colombier 	if (lidebug) {
1017*80ee5cbfSDavid du Colombier 		int i;
1018*80ee5cbfSDavid du Colombier 		fprint(2, "After pre-processing:   %d %d %d %d\n",
1019*80ee5cbfSDavid du Colombier 				minx, miny, maxx, maxy);
1020*80ee5cbfSDavid du Colombier 		for (i = 0; i < points->npts; i++)
1021*80ee5cbfSDavid du Colombier 			fprint(2, "      (%P)\n", points->pts[i].Point);
1022*80ee5cbfSDavid du Colombier 		fflush(stderr);
1023*80ee5cbfSDavid du Colombier 	}
1024*80ee5cbfSDavid du Colombier 
1025*80ee5cbfSDavid du Colombier 	return(0);
1026*80ee5cbfSDavid du Colombier }
1027*80ee5cbfSDavid du Colombier 
1028*80ee5cbfSDavid du Colombier 
lialg_compute_dominant_points(point_list * points)1029*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dominant_points(point_list *points) {
1030*80ee5cbfSDavid du Colombier 	point_list *ipts;
1031*80ee5cbfSDavid du Colombier 	region_list *regions;
1032*80ee5cbfSDavid du Colombier 	point_list *dpts;
1033*80ee5cbfSDavid du Colombier 
1034*80ee5cbfSDavid du Colombier 	/* Interpolate points. */
1035*80ee5cbfSDavid du Colombier 	ipts = lialg_interpolate_points(points);
1036*80ee5cbfSDavid du Colombier 	if (ipts == nil) return(nil);
1037*80ee5cbfSDavid du Colombier 	if (lidebug) {
1038*80ee5cbfSDavid du Colombier 				int j;
1039*80ee5cbfSDavid du Colombier 				fprint(2, "After interpolation:  %d ipts\n", ipts->npts);
1040*80ee5cbfSDavid du Colombier 				for (j = 0; j < ipts->npts; j++) {
1041*80ee5cbfSDavid du Colombier 					fprint(2, "  (%P), %lud\n", ipts->pts[j].Point, ipts->pts[j].chaincode);
1042*80ee5cbfSDavid du Colombier 				}
1043*80ee5cbfSDavid du Colombier 				fflush(stderr);
1044*80ee5cbfSDavid du Colombier 	}
1045*80ee5cbfSDavid du Colombier 
1046*80ee5cbfSDavid du Colombier 	/* Compute regions. */
1047*80ee5cbfSDavid du Colombier 	regions = lialg_compute_regions(ipts);
1048*80ee5cbfSDavid du Colombier /*    assert(regions != nil);*/
1049*80ee5cbfSDavid du Colombier 
1050*80ee5cbfSDavid du Colombier 	/* Compute dominant points. */
1051*80ee5cbfSDavid du Colombier 	dpts = lialg_compute_dompts(ipts, regions);
1052*80ee5cbfSDavid du Colombier 	if (lidebug) {
1053*80ee5cbfSDavid du Colombier 				int j;
1054*80ee5cbfSDavid du Colombier 				fprint(2, "Dominant points:  ");
1055*80ee5cbfSDavid du Colombier 				for (j = 0; j < dpts->npts; j++) {
1056*80ee5cbfSDavid du Colombier 					fprint(2, "%P (%lud)  ", dpts->pts[j].Point, dpts->pts[j].chaincode);
1057*80ee5cbfSDavid du Colombier 				}
1058*80ee5cbfSDavid du Colombier 				fprint(2, "\n");
1059*80ee5cbfSDavid du Colombier 				fflush(stderr);
1060*80ee5cbfSDavid du Colombier 	}
1061*80ee5cbfSDavid du Colombier 
1062*80ee5cbfSDavid du Colombier 	/* Delete region data structure. */
1063*80ee5cbfSDavid du Colombier 	{
1064*80ee5cbfSDavid du Colombier 				region_list *curr, *next;
1065*80ee5cbfSDavid du Colombier 				for (curr = regions; curr != nil; ) {
1066*80ee5cbfSDavid du Colombier 					next = curr->next;
1067*80ee5cbfSDavid du Colombier 					free(curr);
1068*80ee5cbfSDavid du Colombier 					curr = next;
1069*80ee5cbfSDavid du Colombier 				}
1070*80ee5cbfSDavid du Colombier 	}
1071*80ee5cbfSDavid du Colombier 	delete_examples(ipts);
1072*80ee5cbfSDavid du Colombier 	return(dpts);
1073*80ee5cbfSDavid du Colombier }
1074*80ee5cbfSDavid du Colombier 
1075*80ee5cbfSDavid du Colombier /* Input points are assumed to be integer-valued! */
lialg_interpolate_points(point_list * points)1076*80ee5cbfSDavid du Colombier static point_list *lialg_interpolate_points(point_list *points) {
1077*80ee5cbfSDavid du Colombier 	int i, j;
1078*80ee5cbfSDavid du Colombier 	int maxpts;
1079*80ee5cbfSDavid du Colombier 	point_list *newpts;
1080*80ee5cbfSDavid du Colombier 
1081*80ee5cbfSDavid du Colombier 	/* Compute an upper-bound on the number of interpolated points. */
1082*80ee5cbfSDavid du Colombier 	maxpts = 0;
1083*80ee5cbfSDavid du Colombier 	for (i = 0; i < (points->npts - 1); i++) {
1084*80ee5cbfSDavid du Colombier 				pen_point *pta = &(points->pts[i]);
1085*80ee5cbfSDavid du Colombier 				pen_point *ptb = &(points->pts[i+1]);
1086*80ee5cbfSDavid du Colombier 				maxpts += abs(pta->x - ptb->x) + abs(pta->y - ptb->y);
1087*80ee5cbfSDavid du Colombier 	}
1088*80ee5cbfSDavid du Colombier 
1089*80ee5cbfSDavid du Colombier 	/* Allocate an array of the requisite size. */
1090*80ee5cbfSDavid du Colombier 	maxpts += points->npts;
1091*80ee5cbfSDavid du Colombier 	/* newpts = (point_list *)safe_malloc(sizeof(point_list)); */
1092*80ee5cbfSDavid du Colombier 	newpts = malloc(sizeof(point_list));
1093*80ee5cbfSDavid du Colombier 	newpts->pts = mallocz(maxpts*sizeof(pen_point), 1);
1094*80ee5cbfSDavid du Colombier 	if (newpts->pts == nil) {
1095*80ee5cbfSDavid du Colombier 				free(newpts);
1096*80ee5cbfSDavid du Colombier 				return(nil);
1097*80ee5cbfSDavid du Colombier 	}
1098*80ee5cbfSDavid du Colombier 	newpts->npts = maxpts;
1099*80ee5cbfSDavid du Colombier 	newpts->next = nil;
1100*80ee5cbfSDavid du Colombier 
1101*80ee5cbfSDavid du Colombier 	/* Interpolate each of the segments. */
1102*80ee5cbfSDavid du Colombier 	j = 0;
1103*80ee5cbfSDavid du Colombier 	for (i = 0; i < (points->npts - 1); i++) {
1104*80ee5cbfSDavid du Colombier 				pen_point *startpt = &(points->pts[i]);
1105*80ee5cbfSDavid du Colombier 				pen_point *endpt = &(points->pts[i+1]);
1106*80ee5cbfSDavid du Colombier 
1107*80ee5cbfSDavid du Colombier 				lialg_bresline(startpt, endpt, newpts, &j);
1108*80ee5cbfSDavid du Colombier 
1109*80ee5cbfSDavid du Colombier 				j--;	/* end point gets recorded as start point of next segment! */
1110*80ee5cbfSDavid du Colombier 	}
1111*80ee5cbfSDavid du Colombier 
1112*80ee5cbfSDavid du Colombier 	/* Add-in last point. */
1113*80ee5cbfSDavid du Colombier 	newpts->pts[j++] = points->pts[points->npts - 1];
1114*80ee5cbfSDavid du Colombier 	newpts->npts = j;
1115*80ee5cbfSDavid du Colombier 
1116*80ee5cbfSDavid du Colombier 	/* Compute the chain code for P (the list of points). */
1117*80ee5cbfSDavid du Colombier 	lialg_compute_unit_chain_code(newpts);
1118*80ee5cbfSDavid du Colombier 
1119*80ee5cbfSDavid du Colombier 	return(newpts);
1120*80ee5cbfSDavid du Colombier }
1121*80ee5cbfSDavid du Colombier 
1122*80ee5cbfSDavid du Colombier 
1123*80ee5cbfSDavid du Colombier /* This implementation is due to Kenny Hoff. */
lialg_bresline(pen_point * startpt,pen_point * endpt,point_list * newpts,int * j)1124*80ee5cbfSDavid du Colombier static void lialg_bresline(pen_point *startpt, pen_point *endpt,
1125*80ee5cbfSDavid du Colombier 							point_list *newpts, int *j) {
1126*80ee5cbfSDavid du Colombier 	int Ax, Ay, Bx, By, dX, dY, Xincr, Yincr;
1127*80ee5cbfSDavid du Colombier 
1128*80ee5cbfSDavid du Colombier 	Ax = startpt->x;
1129*80ee5cbfSDavid du Colombier 	Ay = startpt->y;
1130*80ee5cbfSDavid du Colombier 	Bx = endpt->x;
1131*80ee5cbfSDavid du Colombier 	By = endpt->y;
1132*80ee5cbfSDavid du Colombier 
1133*80ee5cbfSDavid du Colombier 	/* INITIALIZE THE COMPONENTS OF THE ALGORITHM THAT ARE NOT AFFECTED */
1134*80ee5cbfSDavid du Colombier 	/* BY THE SLOPE OR DIRECTION OF THE	LINE */
1135*80ee5cbfSDavid du Colombier 	dX = abs(Bx-Ax);	/* store the change in X and Y of the line endpoints */
1136*80ee5cbfSDavid du Colombier 	dY = abs(By-Ay);
1137*80ee5cbfSDavid du Colombier 
1138*80ee5cbfSDavid du Colombier 	/* DETERMINE "DIRECTIONS" TO INCREMENT X AND Y (REGARDLESS OF DECISION) */
1139*80ee5cbfSDavid du Colombier 	if (Ax > Bx) { Xincr=-1; } else { Xincr=1; }    /* which direction in X? */
1140*80ee5cbfSDavid du Colombier 	if (Ay > By) { Yincr=-1; } else { Yincr=1; }    /* which direction in Y? */
1141*80ee5cbfSDavid du Colombier 
1142*80ee5cbfSDavid du Colombier 	/* DETERMINE INDEPENDENT VARIABLE (ONE THAT ALWAYS INCREMENTS BY 1 (OR -1) ) */
1143*80ee5cbfSDavid du Colombier 	/* AND INITIATE APPROPRIATE LINE DRAWING ROUTINE (BASED ON FIRST OCTANT */
1144*80ee5cbfSDavid du Colombier 	/* ALWAYS). THE X AND Y'S MAY BE FLIPPED IF Y IS THE INDEPENDENT VARIABLE. */
1145*80ee5cbfSDavid du Colombier 	if (dX >= dY) {	    /* if X is the independent variable */
1146*80ee5cbfSDavid du Colombier 				int dPr	= dY<<1;	    /* amount to increment decision if right is chosen (always) */
1147*80ee5cbfSDavid du Colombier 				int dPru = dPr - (dX<<1);   /* amount to increment decision if up is chosen */
1148*80ee5cbfSDavid du Colombier 				int P =	dPr - dX;	    /* decision variable start value */
1149*80ee5cbfSDavid du Colombier 
1150*80ee5cbfSDavid du Colombier 				/* process each point in the line one at a time (just use dX) */
1151*80ee5cbfSDavid du Colombier 				for (; dX>=0; dX--) {
1152*80ee5cbfSDavid du Colombier 					newpts->pts[*j].x = Ax;
1153*80ee5cbfSDavid du Colombier 					newpts->pts[*j].y = Ay;
1154*80ee5cbfSDavid du Colombier 					(*j)++;
1155*80ee5cbfSDavid du Colombier 
1156*80ee5cbfSDavid du Colombier 					if (P > 0) {	/* is the pixel	going right AND	up? */
1157*80ee5cbfSDavid du Colombier 								Ax+=Xincr;	/* increment independent variable */
1158*80ee5cbfSDavid du Colombier 								Ay+=Yincr;	/* increment dependent variable */
1159*80ee5cbfSDavid du Colombier 								P+=dPru;	/* increment decision (for up) */
1160*80ee5cbfSDavid du Colombier 					} else {		/* is the pixel just going right? */
1161*80ee5cbfSDavid du Colombier 								Ax+=Xincr;	/* increment independent variable */
1162*80ee5cbfSDavid du Colombier 								P+=dPr;		/* increment decision (for right) */
1163*80ee5cbfSDavid du Colombier 					}
1164*80ee5cbfSDavid du Colombier 				}
1165*80ee5cbfSDavid du Colombier 	} else {		    /* if Y is the independent variable */
1166*80ee5cbfSDavid du Colombier 				int dPr	= dX<<1;	    /* amount to increment decision if right is chosen (always) */
1167*80ee5cbfSDavid du Colombier 				int dPru = dPr - (dY<<1);   /* amount to increment decision if up is chosen */
1168*80ee5cbfSDavid du Colombier 				int P  = dPr - dY;	    /* decision variable start value */
1169*80ee5cbfSDavid du Colombier 
1170*80ee5cbfSDavid du Colombier 				/* process each point in the line one at a time (just use dY) */
1171*80ee5cbfSDavid du Colombier 				for (; dY>=0; dY--) {
1172*80ee5cbfSDavid du Colombier 					newpts->pts[*j].x = Ax;
1173*80ee5cbfSDavid du Colombier 					newpts->pts[*j].y = Ay;
1174*80ee5cbfSDavid du Colombier 					(*j)++;
1175*80ee5cbfSDavid du Colombier 
1176*80ee5cbfSDavid du Colombier 					if (P > 0) {	/* is the pixel going up AND right? */
1177*80ee5cbfSDavid du Colombier 								Ax+=Xincr;	/* increment dependent variable */
1178*80ee5cbfSDavid du Colombier 								Ay+=Yincr;	/* increment independent variable */
1179*80ee5cbfSDavid du Colombier 								P+=dPru;	/* increment decision (for up) */
1180*80ee5cbfSDavid du Colombier 					} else {		/* is the pixel just going up? */
1181*80ee5cbfSDavid du Colombier 								Ay+=Yincr;	/* increment independent variable */
1182*80ee5cbfSDavid du Colombier 								P+=dPr;		/* increment decision (for right) */
1183*80ee5cbfSDavid du Colombier 					}
1184*80ee5cbfSDavid du Colombier 				}
1185*80ee5cbfSDavid du Colombier 	}
1186*80ee5cbfSDavid du Colombier }
1187*80ee5cbfSDavid du Colombier 
lialg_compute_chain_code(point_list * pts)1188*80ee5cbfSDavid du Colombier static void lialg_compute_chain_code(point_list *pts) {
1189*80ee5cbfSDavid du Colombier 	int i;
1190*80ee5cbfSDavid du Colombier 
1191*80ee5cbfSDavid du Colombier 	for (i = 0; i < (pts->npts - 1); i++) {
1192*80ee5cbfSDavid du Colombier 				pen_point *startpt = &(pts->pts[i]);
1193*80ee5cbfSDavid du Colombier 				pen_point *endpt = &(pts->pts[i+1]);
1194*80ee5cbfSDavid du Colombier 				int dx = endpt->x - startpt->x;
1195*80ee5cbfSDavid du Colombier 				int dy = endpt->y - startpt->y;
1196*80ee5cbfSDavid du Colombier 				int tmp = quadr(likeatan(dy, dx));
1197*80ee5cbfSDavid du Colombier 				int dircode = (12 - tmp) % 8;
1198*80ee5cbfSDavid du Colombier 
1199*80ee5cbfSDavid du Colombier 				startpt->chaincode = dircode;
1200*80ee5cbfSDavid du Colombier 	}
1201*80ee5cbfSDavid du Colombier }
1202*80ee5cbfSDavid du Colombier 
1203*80ee5cbfSDavid du Colombier 
lialg_compute_unit_chain_code(point_list * pts)1204*80ee5cbfSDavid du Colombier static void lialg_compute_unit_chain_code(point_list *pts) {
1205*80ee5cbfSDavid du Colombier 	int i;
1206*80ee5cbfSDavid du Colombier 
1207*80ee5cbfSDavid du Colombier 	for (i = 0; i < (pts->npts - 1); i++) {
1208*80ee5cbfSDavid du Colombier 				pen_point *startpt = &(pts->pts[i]);
1209*80ee5cbfSDavid du Colombier 				pen_point *endpt = &(pts->pts[i+1]);
1210*80ee5cbfSDavid du Colombier 				int dx = endpt->x - startpt->x;
1211*80ee5cbfSDavid du Colombier 				int dy = endpt->y - startpt->y;
1212*80ee5cbfSDavid du Colombier 				int dircode = lialg_dctbl[dx+1][dy+1];
1213*80ee5cbfSDavid du Colombier 
1214*80ee5cbfSDavid du Colombier 				startpt->chaincode = dircode;
1215*80ee5cbfSDavid du Colombier 	}
1216*80ee5cbfSDavid du Colombier }
1217*80ee5cbfSDavid du Colombier 
1218*80ee5cbfSDavid du Colombier 
lialg_compute_regions(point_list * pts)1219*80ee5cbfSDavid du Colombier static region_list *lialg_compute_regions(point_list *pts) {
1220*80ee5cbfSDavid du Colombier 		region_list *regions;
1221*80ee5cbfSDavid du Colombier 		region_list *curr_reg;
1222*80ee5cbfSDavid du Colombier 		int *R[2 + LP_FILTER_ITERS];
1223*80ee5cbfSDavid du Colombier 		int *junk;
1224*80ee5cbfSDavid du Colombier 		int *curr, *next;
1225*80ee5cbfSDavid du Colombier 		int i, j;
1226*80ee5cbfSDavid du Colombier 
1227*80ee5cbfSDavid du Colombier 		/* Initialize low-pass filter parameters if necessary. */
1228*80ee5cbfSDavid du Colombier 		if (lialg_lpfconst == -1)
1229*80ee5cbfSDavid du Colombier 				lialg_compute_lpf_parameters();
1230*80ee5cbfSDavid du Colombier 
1231*80ee5cbfSDavid du Colombier 	/* Allocate a 2 x pts->npts array for use in computing the (filtered) Angle set, A_n. */
1232*80ee5cbfSDavid du Colombier 	junk = malloc((2 + LP_FILTER_ITERS) * pts->npts*sizeof(int));
1233*80ee5cbfSDavid du Colombier 	for (i = 0; i < (2 + LP_FILTER_ITERS); i++)
1234*80ee5cbfSDavid du Colombier 		R[i] = junk + (i * pts->npts);
1235*80ee5cbfSDavid du Colombier 	curr = R[0];
1236*80ee5cbfSDavid du Colombier 
1237*80ee5cbfSDavid du Colombier 	/* Compute the Angle set, A, in the first element of array R. */
1238*80ee5cbfSDavid du Colombier 	/* Values in R are in degrees, x 100. */
1239*80ee5cbfSDavid du Colombier 	curr[0] = 18000;				/* a_0 */
1240*80ee5cbfSDavid du Colombier 	for (i = 1; i < (pts->npts - 1); i++) {
1241*80ee5cbfSDavid du Colombier 				int d_i = pts->pts[i].chaincode;
1242*80ee5cbfSDavid du Colombier 				int d_iminusone = pts->pts[i-1].chaincode;
1243*80ee5cbfSDavid du Colombier 				int a_i;
1244*80ee5cbfSDavid du Colombier 
1245*80ee5cbfSDavid du Colombier 				if (d_iminusone < d_i)
1246*80ee5cbfSDavid du Colombier 					d_iminusone += 8;
1247*80ee5cbfSDavid du Colombier 
1248*80ee5cbfSDavid du Colombier 				a_i = (d_iminusone - d_i) % 8;
1249*80ee5cbfSDavid du Colombier 
1250*80ee5cbfSDavid du Colombier 				/* convert to degrees, x 100 */
1251*80ee5cbfSDavid du Colombier 				curr[i] = ((12 - a_i) % 8) * 45 * 100;
1252*80ee5cbfSDavid du Colombier 	}
1253*80ee5cbfSDavid du Colombier 	curr[pts->npts - 1]	= 18000;		/* a_L-1 */
1254*80ee5cbfSDavid du Colombier 
1255*80ee5cbfSDavid du Colombier 	/* Perform a number of filtering iterations. */
1256*80ee5cbfSDavid du Colombier 	next = R[1];
1257*80ee5cbfSDavid du Colombier 	for (j = 0; j < LP_FILTER_ITERS; j++, curr = R[j], next = R[j+1]) {
1258*80ee5cbfSDavid du Colombier 				for (i = 0; i < pts->npts; i++) {
1259*80ee5cbfSDavid du Colombier 					int k;
1260*80ee5cbfSDavid du Colombier 
1261*80ee5cbfSDavid du Colombier 					next[i] = 0;
1262*80ee5cbfSDavid du Colombier 
1263*80ee5cbfSDavid du Colombier 					for (k = i - LP_FILTER_WIDTH; k <= i + LP_FILTER_WIDTH; k++) {
1264*80ee5cbfSDavid du Colombier 						int oldval = (k < 0 || k >= pts->npts) ? 18000 : curr[k];
1265*80ee5cbfSDavid du Colombier 						next[i]	+= oldval * lialg_lpfwts[k - (i	- LP_FILTER_WIDTH)];	/* overflow? */
1266*80ee5cbfSDavid du Colombier 					}
1267*80ee5cbfSDavid du Colombier 
1268*80ee5cbfSDavid du Colombier 					next[i] /= lialg_lpfconst;
1269*80ee5cbfSDavid du Colombier 				}
1270*80ee5cbfSDavid du Colombier 	}
1271*80ee5cbfSDavid du Colombier 
1272*80ee5cbfSDavid du Colombier 	/* Do final thresholding around PI. */
1273*80ee5cbfSDavid du Colombier 	/* curr and next are set-up correctly at end of previous loop! */
1274*80ee5cbfSDavid du Colombier 	for (i = 0; i < pts->npts; i++)
1275*80ee5cbfSDavid du Colombier 				next[i] = (abs(curr[i] - 18000) < LP_FILTER_THLD) ? 18000 : curr[i];
1276*80ee5cbfSDavid du Colombier 	curr = next;
1277*80ee5cbfSDavid du Colombier 
1278*80ee5cbfSDavid du Colombier 	/* Debugging. */
1279*80ee5cbfSDavid du Colombier 	if (lidebug > 1) {
1280*80ee5cbfSDavid du Colombier 				for (i = 0; i < pts->npts; i++) {
1281*80ee5cbfSDavid du Colombier 					fprint(2, "%3d:  (%P)  %lud  ",
1282*80ee5cbfSDavid du Colombier 								i, pts->pts[i].Point, pts->pts[i].chaincode);
1283*80ee5cbfSDavid du Colombier 					for (j = 0; j < 2 + LP_FILTER_ITERS; j++)
1284*80ee5cbfSDavid du Colombier 						fprint(2, "%d  ", R[j][i]);
1285*80ee5cbfSDavid du Colombier 					fprint(2, "\n");
1286*80ee5cbfSDavid du Colombier 				}
1287*80ee5cbfSDavid du Colombier 	}
1288*80ee5cbfSDavid du Colombier 
1289*80ee5cbfSDavid du Colombier 	/* Do the region segmentation. */
1290*80ee5cbfSDavid du Colombier 	{
1291*80ee5cbfSDavid du Colombier 				int start, end;
1292*80ee5cbfSDavid du Colombier 				int currtype;
1293*80ee5cbfSDavid du Colombier 
1294*80ee5cbfSDavid du Colombier #define	RGN_TYPE(val) (((val)==18000)?RGN_PLAIN:((val)<18000?RGN_CONCAVE:RGN_CONVEX))
1295*80ee5cbfSDavid du Colombier 
1296*80ee5cbfSDavid du Colombier 				start = 0;
1297*80ee5cbfSDavid du Colombier 				currtype = RGN_TYPE(curr[0]);
1298*80ee5cbfSDavid du Colombier 				regions = malloc(sizeof(region_list));
1299*80ee5cbfSDavid du Colombier 				curr_reg = regions;
1300*80ee5cbfSDavid du Colombier 				curr_reg->start = start;
1301*80ee5cbfSDavid du Colombier 				curr_reg->end = 0;
1302*80ee5cbfSDavid du Colombier 				curr_reg->type = currtype;
1303*80ee5cbfSDavid du Colombier 				curr_reg->next = nil;
1304*80ee5cbfSDavid du Colombier 				for (i = 1; i < pts->npts; i++) {
1305*80ee5cbfSDavid du Colombier 					int nexttype = RGN_TYPE(curr[i]);
1306*80ee5cbfSDavid du Colombier 
1307*80ee5cbfSDavid du Colombier 					if (nexttype != currtype) {
1308*80ee5cbfSDavid du Colombier 								region_list *next_reg;
1309*80ee5cbfSDavid du Colombier 
1310*80ee5cbfSDavid du Colombier 								end = i - 1;
1311*80ee5cbfSDavid du Colombier 								curr_reg->end = end;
1312*80ee5cbfSDavid du Colombier 								if (lidebug > 1)
1313*80ee5cbfSDavid du Colombier 									fprint(2, "  (%d, %d), %d\n", start, end, currtype);
1314*80ee5cbfSDavid du Colombier 
1315*80ee5cbfSDavid du Colombier 								start = i;
1316*80ee5cbfSDavid du Colombier 								currtype = nexttype;
1317*80ee5cbfSDavid du Colombier 								next_reg = malloc(sizeof(region_list));
1318*80ee5cbfSDavid du Colombier 								next_reg->start = start;
1319*80ee5cbfSDavid du Colombier 								next_reg->end = 0;
1320*80ee5cbfSDavid du Colombier 								next_reg->type = nexttype;
1321*80ee5cbfSDavid du Colombier 								next_reg->next = nil;
1322*80ee5cbfSDavid du Colombier 
1323*80ee5cbfSDavid du Colombier 								curr_reg->next = next_reg;
1324*80ee5cbfSDavid du Colombier 								curr_reg = next_reg;
1325*80ee5cbfSDavid du Colombier 					}
1326*80ee5cbfSDavid du Colombier 				}
1327*80ee5cbfSDavid du Colombier 				end = i - 1;
1328*80ee5cbfSDavid du Colombier 				curr_reg->end = end;
1329*80ee5cbfSDavid du Colombier 				if (lidebug > 1)
1330*80ee5cbfSDavid du Colombier 					fprint(2, "  (%d, %d), %d\n", start, end, currtype);
1331*80ee5cbfSDavid du Colombier 
1332*80ee5cbfSDavid du Colombier 				/* Filter out convex/concave regions that are too short. */
1333*80ee5cbfSDavid du Colombier 				for (curr_reg = regions; curr_reg; curr_reg = curr_reg->next)
1334*80ee5cbfSDavid du Colombier 					if (curr_reg->type == RGN_PLAIN) {
1335*80ee5cbfSDavid du Colombier 								region_list *next_reg;
1336*80ee5cbfSDavid du Colombier 
1337*80ee5cbfSDavid du Colombier 								for (next_reg = curr_reg->next;
1338*80ee5cbfSDavid du Colombier 									 next_reg != nil &&
1339*80ee5cbfSDavid du Colombier 									   (next_reg->end - next_reg->start) < LP_FILTER_MIN;
1340*80ee5cbfSDavid du Colombier 									 next_reg = curr_reg->next) {
1341*80ee5cbfSDavid du Colombier 									/* next_reg must not be plain, and it must be followed by a plain */
1342*80ee5cbfSDavid du Colombier 									/* assert(next_reg->type != RGN_PLAIN); */
1343*80ee5cbfSDavid du Colombier 									/* assert(next_reg->next != nil && (next_reg->next)->type == RGN_PLAIN); */
1344*80ee5cbfSDavid du Colombier 
1345*80ee5cbfSDavid du Colombier 									curr_reg->next = (next_reg->next)->next;
1346*80ee5cbfSDavid du Colombier 									curr_reg->end = (next_reg->next)->end;
1347*80ee5cbfSDavid du Colombier 
1348*80ee5cbfSDavid du Colombier 									free(next_reg->next);
1349*80ee5cbfSDavid du Colombier 									free(next_reg);
1350*80ee5cbfSDavid du Colombier 								}
1351*80ee5cbfSDavid du Colombier 					}
1352*80ee5cbfSDavid du Colombier 
1353*80ee5cbfSDavid du Colombier 				/* Add-in pseudo-extremes. */
1354*80ee5cbfSDavid du Colombier 				{
1355*80ee5cbfSDavid du Colombier 					region_list *tmp, *prev_reg;
1356*80ee5cbfSDavid du Colombier 
1357*80ee5cbfSDavid du Colombier 					tmp = regions;
1358*80ee5cbfSDavid du Colombier 					regions = nil;
1359*80ee5cbfSDavid du Colombier 					prev_reg = nil;
1360*80ee5cbfSDavid du Colombier 					for (curr_reg = tmp; curr_reg; curr_reg = curr_reg->next) {
1361*80ee5cbfSDavid du Colombier 						if (curr_reg->type == RGN_PLAIN) {
1362*80ee5cbfSDavid du Colombier 							int arclen = lialg_compute_pathlen_subset(pts,
1363*80ee5cbfSDavid du Colombier 																		curr_reg->start,
1364*80ee5cbfSDavid du Colombier 																		curr_reg->end);
1365*80ee5cbfSDavid du Colombier 							int dx = pts->pts[curr_reg->end].x -
1366*80ee5cbfSDavid du Colombier 							  pts->pts[curr_reg->start].x;
1367*80ee5cbfSDavid du Colombier 							int dy = pts->pts[curr_reg->end].y -
1368*80ee5cbfSDavid du Colombier 							  pts->pts[curr_reg->start].y;
1369*80ee5cbfSDavid du Colombier 							int chordlen = isqrt(10000 * (dx * dx + dy * dy));
1370*80ee5cbfSDavid du Colombier 							int atcr = (chordlen == 0) ? 0 : (100 * arclen + chordlen / 2) / chordlen;
1371*80ee5cbfSDavid du Colombier 
1372*80ee5cbfSDavid du Colombier 							if (lidebug)
1373*80ee5cbfSDavid du Colombier 								fprint(2, "%d, %d, %d\n", arclen, chordlen, atcr);
1374*80ee5cbfSDavid du Colombier 
1375*80ee5cbfSDavid du Colombier 							/* Split region if necessary. */
1376*80ee5cbfSDavid du Colombier 							if (arclen >= PE_AL_THLD && atcr >= PE_ATCR_THLD) {
1377*80ee5cbfSDavid du Colombier 								int mid = curr_reg->start + (curr_reg->end - curr_reg->start) / 2;
1378*80ee5cbfSDavid du Colombier 								int end = curr_reg->end;
1379*80ee5cbfSDavid du Colombier 								region_list *saved_next = curr_reg->next;
1380*80ee5cbfSDavid du Colombier 
1381*80ee5cbfSDavid du Colombier 								curr_reg->end = mid - 1;
1382*80ee5cbfSDavid du Colombier 								if (prev_reg == nil)
1383*80ee5cbfSDavid du Colombier 									regions = curr_reg;
1384*80ee5cbfSDavid du Colombier 								else
1385*80ee5cbfSDavid du Colombier 									prev_reg->next = curr_reg;
1386*80ee5cbfSDavid du Colombier 								prev_reg = curr_reg;
1387*80ee5cbfSDavid du Colombier 
1388*80ee5cbfSDavid du Colombier 								/* curr_reg = (region_list *)safe_malloc(sizeof(region_list));*/
1389*80ee5cbfSDavid du Colombier 								curr_reg = malloc(sizeof(region_list));
1390*80ee5cbfSDavid du Colombier 								curr_reg->start = mid;
1391*80ee5cbfSDavid du Colombier 								curr_reg->end = mid;
1392*80ee5cbfSDavid du Colombier 								curr_reg->type = RGN_PSEUDO;
1393*80ee5cbfSDavid du Colombier 								curr_reg->next = nil;
1394*80ee5cbfSDavid du Colombier 								prev_reg->next = curr_reg;
1395*80ee5cbfSDavid du Colombier 								prev_reg = curr_reg;
1396*80ee5cbfSDavid du Colombier 
1397*80ee5cbfSDavid du Colombier 								/* curr_reg = (region_list *)malloc(sizeof(region_list)); */
1398*80ee5cbfSDavid du Colombier 								curr_reg = malloc(sizeof(region_list));
1399*80ee5cbfSDavid du Colombier 								curr_reg->start = mid + 1;
1400*80ee5cbfSDavid du Colombier 								curr_reg->end = end;
1401*80ee5cbfSDavid du Colombier 								curr_reg->type = RGN_PLAIN;
1402*80ee5cbfSDavid du Colombier 								curr_reg->next = nil;
1403*80ee5cbfSDavid du Colombier 								prev_reg->next = curr_reg;
1404*80ee5cbfSDavid du Colombier 								prev_reg = curr_reg;
1405*80ee5cbfSDavid du Colombier 
1406*80ee5cbfSDavid du Colombier 								curr_reg->next = saved_next;
1407*80ee5cbfSDavid du Colombier 								continue;
1408*80ee5cbfSDavid du Colombier 							}
1409*80ee5cbfSDavid du Colombier 						}
1410*80ee5cbfSDavid du Colombier 
1411*80ee5cbfSDavid du Colombier 						if (prev_reg == nil)
1412*80ee5cbfSDavid du Colombier 							regions = curr_reg;
1413*80ee5cbfSDavid du Colombier 						else
1414*80ee5cbfSDavid du Colombier 							prev_reg->next = curr_reg;
1415*80ee5cbfSDavid du Colombier 						prev_reg = curr_reg;
1416*80ee5cbfSDavid du Colombier 					}
1417*80ee5cbfSDavid du Colombier 				}
1418*80ee5cbfSDavid du Colombier 		}
1419*80ee5cbfSDavid du Colombier 
1420*80ee5cbfSDavid du Colombier 	free(junk);
1421*80ee5cbfSDavid du Colombier 	return(regions);
1422*80ee5cbfSDavid du Colombier }
1423*80ee5cbfSDavid du Colombier 
1424*80ee5cbfSDavid du Colombier 
lialg_compute_dompts(point_list * pts,region_list * regions)1425*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dompts(point_list *pts, region_list *regions) {
1426*80ee5cbfSDavid du Colombier 	point_list *dpts;
1427*80ee5cbfSDavid du Colombier 	int ndpts;
1428*80ee5cbfSDavid du Colombier 	int *cas;
1429*80ee5cbfSDavid du Colombier 	int nonplain;
1430*80ee5cbfSDavid du Colombier 	region_list *r;
1431*80ee5cbfSDavid du Colombier 		region_list *curr;
1432*80ee5cbfSDavid du Colombier 		int dp;
1433*80ee5cbfSDavid du Colombier 		int previx;
1434*80ee5cbfSDavid du Colombier 		int currix;
1435*80ee5cbfSDavid du Colombier 
1436*80ee5cbfSDavid du Colombier 	/* Compute contour angle set. */
1437*80ee5cbfSDavid du Colombier 	cas = lialg_compute_contour_angle_set(pts, regions);
1438*80ee5cbfSDavid du Colombier 
1439*80ee5cbfSDavid du Colombier 	/* Dominant points include:  start_pt, end_pt, extrema_of_non_plain_regions, midpts of the preceding. */
1440*80ee5cbfSDavid du Colombier 	nonplain = 0;
1441*80ee5cbfSDavid du Colombier 	for (r = regions; r != nil; r = r->next)
1442*80ee5cbfSDavid du Colombier 				if (r->type != RGN_PLAIN)
1443*80ee5cbfSDavid du Colombier 						nonplain++;
1444*80ee5cbfSDavid du Colombier 	ndpts = 2 * (2 + nonplain) - 1;
1445*80ee5cbfSDavid du Colombier 	/* dpts = (point_list *)safe_malloc(sizeof(point_list)); */
1446*80ee5cbfSDavid du Colombier 	dpts = malloc(sizeof(point_list));
1447*80ee5cbfSDavid du Colombier 	dpts->pts = mallocz(ndpts*sizeof(pen_point), 1);
1448*80ee5cbfSDavid du Colombier 	if (dpts->pts == nil) {
1449*80ee5cbfSDavid du Colombier 				free(dpts);
1450*80ee5cbfSDavid du Colombier 				return(nil);
1451*80ee5cbfSDavid du Colombier 	}
1452*80ee5cbfSDavid du Colombier 	dpts->npts = ndpts;
1453*80ee5cbfSDavid du Colombier 	dpts->next = nil;
1454*80ee5cbfSDavid du Colombier 
1455*80ee5cbfSDavid du Colombier 	/* Pick out dominant points. */
1456*80ee5cbfSDavid du Colombier 
1457*80ee5cbfSDavid du Colombier 		/* Record start point. */
1458*80ee5cbfSDavid du Colombier 		dp = 0;
1459*80ee5cbfSDavid du Colombier 		previx = 0;
1460*80ee5cbfSDavid du Colombier 		dpts->pts[dp++] = pts->pts[previx];
1461*80ee5cbfSDavid du Colombier 
1462*80ee5cbfSDavid du Colombier 		for (curr = regions; curr != nil; curr = curr->next)
1463*80ee5cbfSDavid du Colombier 			if (curr->type != RGN_PLAIN) {
1464*80ee5cbfSDavid du Colombier 						int max_v = 0;
1465*80ee5cbfSDavid du Colombier 						int min_v = 0x7fffffff;	/* maxint */
1466*80ee5cbfSDavid du Colombier 						int max_ix = -1;
1467*80ee5cbfSDavid du Colombier 						int min_ix = -1;
1468*80ee5cbfSDavid du Colombier 						int i;
1469*80ee5cbfSDavid du Colombier 
1470*80ee5cbfSDavid du Colombier 						for (i = curr->start; i <= curr->end; i++) {
1471*80ee5cbfSDavid du Colombier 							int v = cas[i];
1472*80ee5cbfSDavid du Colombier 							if (v > max_v) { max_v = v; max_ix = i; }
1473*80ee5cbfSDavid du Colombier 							if (v < min_v) { min_v = v; min_ix = i; }
1474*80ee5cbfSDavid du Colombier 							if (lidebug > 1)
1475*80ee5cbfSDavid du Colombier 								fprint(2, "  %d\n", v);
1476*80ee5cbfSDavid du Colombier 						}
1477*80ee5cbfSDavid du Colombier 
1478*80ee5cbfSDavid du Colombier 						currix = (curr->type == RGN_CONVEX ? max_ix : min_ix);
1479*80ee5cbfSDavid du Colombier 
1480*80ee5cbfSDavid du Colombier 						/* Record midpoint. */
1481*80ee5cbfSDavid du Colombier 						dpts->pts[dp++] = pts->pts[previx + (currix - previx) / 2];
1482*80ee5cbfSDavid du Colombier 
1483*80ee5cbfSDavid du Colombier 						/* Record extreme point. */
1484*80ee5cbfSDavid du Colombier 						dpts->pts[dp++] = pts->pts[currix];
1485*80ee5cbfSDavid du Colombier 
1486*80ee5cbfSDavid du Colombier 						previx = currix;
1487*80ee5cbfSDavid du Colombier 			}
1488*80ee5cbfSDavid du Colombier 
1489*80ee5cbfSDavid du Colombier 		/* Record last mid-point and end point. */
1490*80ee5cbfSDavid du Colombier 		currix = pts->npts - 1;
1491*80ee5cbfSDavid du Colombier 		dpts->pts[dp++] = pts->pts[previx + (currix - previx) / 2];
1492*80ee5cbfSDavid du Colombier 		dpts->pts[dp] = pts->pts[currix];
1493*80ee5cbfSDavid du Colombier 
1494*80ee5cbfSDavid du Colombier 	/* Compute chain-code. */
1495*80ee5cbfSDavid du Colombier 	lialg_compute_chain_code(dpts);
1496*80ee5cbfSDavid du Colombier 
1497*80ee5cbfSDavid du Colombier 	free(cas);
1498*80ee5cbfSDavid du Colombier 	return(dpts);
1499*80ee5cbfSDavid du Colombier }
1500*80ee5cbfSDavid du Colombier 
1501*80ee5cbfSDavid du Colombier 
lialg_compute_contour_angle_set(point_list * pts,region_list * regions)1502*80ee5cbfSDavid du Colombier static int *lialg_compute_contour_angle_set(point_list *pts,
1503*80ee5cbfSDavid du Colombier 											   region_list *regions) {
1504*80ee5cbfSDavid du Colombier 		int *V;
1505*80ee5cbfSDavid du Colombier 		region_list *curr_reg;
1506*80ee5cbfSDavid du Colombier 		int i;
1507*80ee5cbfSDavid du Colombier 
1508*80ee5cbfSDavid du Colombier 		V = malloc(pts->npts*sizeof(int));
1509*80ee5cbfSDavid du Colombier 
1510*80ee5cbfSDavid du Colombier 		V[0] = 18000;
1511*80ee5cbfSDavid du Colombier 		for (curr_reg = regions; curr_reg != nil; curr_reg = curr_reg->next) {
1512*80ee5cbfSDavid du Colombier 				for (i = curr_reg->start; i <= curr_reg->end; i++) {
1513*80ee5cbfSDavid du Colombier 					if (curr_reg->type == RGN_PLAIN) {
1514*80ee5cbfSDavid du Colombier 								V[i] = 18000;
1515*80ee5cbfSDavid du Colombier 					} else {
1516*80ee5cbfSDavid du Colombier 								/* For now, simply choose the mid-point. */
1517*80ee5cbfSDavid du Colombier 								int isMidPt = i == (curr_reg->start +
1518*80ee5cbfSDavid du Colombier 													 (curr_reg->end - curr_reg->start) / 2);
1519*80ee5cbfSDavid du Colombier 								V[i] = (curr_reg->type == RGN_CONVEX)
1520*80ee5cbfSDavid du Colombier 								  ? (isMidPt ? 18000 : 0)
1521*80ee5cbfSDavid du Colombier 								  : (isMidPt ? 0 : 18000);
1522*80ee5cbfSDavid du Colombier 					}
1523*80ee5cbfSDavid du Colombier 				}
1524*80ee5cbfSDavid du Colombier 	}
1525*80ee5cbfSDavid du Colombier 	V[pts->npts - 1] = 18000;
1526*80ee5cbfSDavid du Colombier 
1527*80ee5cbfSDavid du Colombier 	return(V);
1528*80ee5cbfSDavid du Colombier }
1529*80ee5cbfSDavid du Colombier 
1530*80ee5cbfSDavid du Colombier 
1531*80ee5cbfSDavid du Colombier /*
1532*80ee5cbfSDavid du Colombier  *  First compute the similarity between the two strings.
1533*80ee5cbfSDavid du Colombier  *  If it's above a threshold, compute the distance between
1534*80ee5cbfSDavid du Colombier  *  the two and return it as the ``score.''
1535*80ee5cbfSDavid du Colombier  *  Otherwise, return the constant WORST_SCORE.
1536*80ee5cbfSDavid du Colombier  *
1537*80ee5cbfSDavid du Colombier  */
lialg_score_stroke(point_list * input_dompts,point_list * curr_dompts,int * sim,int * dist)1538*80ee5cbfSDavid du Colombier static void lialg_score_stroke(point_list *input_dompts, point_list *curr_dompts, int *sim, int *dist) {
1539*80ee5cbfSDavid du Colombier 	*sim = MIN_SIM;
1540*80ee5cbfSDavid du Colombier 	*dist = MAX_DIST;
1541*80ee5cbfSDavid du Colombier 
1542*80ee5cbfSDavid du Colombier 	*sim = lialg_compute_similarity(input_dompts, curr_dompts);
1543*80ee5cbfSDavid du Colombier 	if (*sim < SIM_THLD) goto done;
1544*80ee5cbfSDavid du Colombier 
1545*80ee5cbfSDavid du Colombier 	*dist = lialg_compute_distance(input_dompts, curr_dompts);
1546*80ee5cbfSDavid du Colombier 
1547*80ee5cbfSDavid du Colombier done:
1548*80ee5cbfSDavid du Colombier 	if (lidebug)
1549*80ee5cbfSDavid du Colombier 		fprint(2, "%d, %d\n", *sim, *dist);
1550*80ee5cbfSDavid du Colombier }
1551*80ee5cbfSDavid du Colombier 
1552*80ee5cbfSDavid du Colombier 
lialg_compute_similarity(point_list * input_dompts,point_list * curr_dompts)1553*80ee5cbfSDavid du Colombier static int lialg_compute_similarity(point_list *input_dompts, point_list *curr_dompts) {
1554*80ee5cbfSDavid du Colombier 	int sim;
1555*80ee5cbfSDavid du Colombier 	point_list *A, *B;
1556*80ee5cbfSDavid du Colombier 	int N, M;
1557*80ee5cbfSDavid du Colombier 	int **G;
1558*80ee5cbfSDavid du Colombier 	int *junk;
1559*80ee5cbfSDavid du Colombier 	int i, j;
1560*80ee5cbfSDavid du Colombier 
1561*80ee5cbfSDavid du Colombier 	/* A is the	longer sequence, length	N. */
1562*80ee5cbfSDavid du Colombier 	/* B is the shorter sequence, length M. */
1563*80ee5cbfSDavid du Colombier 		if (input_dompts->npts >= curr_dompts->npts) {
1564*80ee5cbfSDavid du Colombier 				A = input_dompts;
1565*80ee5cbfSDavid du Colombier 				N = input_dompts->npts;
1566*80ee5cbfSDavid du Colombier 				B = curr_dompts;
1567*80ee5cbfSDavid du Colombier 				M = curr_dompts->npts;
1568*80ee5cbfSDavid du Colombier 	} else {
1569*80ee5cbfSDavid du Colombier 				A = curr_dompts;
1570*80ee5cbfSDavid du Colombier 				N = curr_dompts->npts;
1571*80ee5cbfSDavid du Colombier 				B = input_dompts;
1572*80ee5cbfSDavid du Colombier 				M = input_dompts->npts;
1573*80ee5cbfSDavid du Colombier 		}
1574*80ee5cbfSDavid du Colombier 
1575*80ee5cbfSDavid du Colombier 		/* Allocate and initialize the Gain matrix, G. */
1576*80ee5cbfSDavid du Colombier 		/* The size of G is M x (N + 1). */
1577*80ee5cbfSDavid du Colombier 		/* Note that row 0 is unused. */
1578*80ee5cbfSDavid du Colombier 		/* Similarities are x 10. */
1579*80ee5cbfSDavid du Colombier 		G = malloc(M*sizeof(int *));
1580*80ee5cbfSDavid du Colombier 		junk = malloc(M * (N + 1) * sizeof(int));
1581*80ee5cbfSDavid du Colombier 		for (i = 0; i < M; i++)
1582*80ee5cbfSDavid du Colombier 			G[i] = junk + (i * (N + 1));
1583*80ee5cbfSDavid du Colombier 
1584*80ee5cbfSDavid du Colombier 		for (i = 1; i < M; i++) {
1585*80ee5cbfSDavid du Colombier 			int bval = B->pts[i-1].chaincode;
1586*80ee5cbfSDavid du Colombier 
1587*80ee5cbfSDavid du Colombier 			/* Source column. */
1588*80ee5cbfSDavid du Colombier 			G[i][0] = 0;
1589*80ee5cbfSDavid du Colombier 
1590*80ee5cbfSDavid du Colombier 			for (j = 1; j < N; j++) {
1591*80ee5cbfSDavid du Colombier 				int aval = A->pts[j-1].chaincode;
1592*80ee5cbfSDavid du Colombier 				int diff = abs(bval - aval);
1593*80ee5cbfSDavid du Colombier 				if (diff > 4) diff = 8 - diff;
1594*80ee5cbfSDavid du Colombier 
1595*80ee5cbfSDavid du Colombier 				G[i][j] = (diff == 0)
1596*80ee5cbfSDavid du Colombier 				  ? 10
1597*80ee5cbfSDavid du Colombier 				  : (diff == 1)
1598*80ee5cbfSDavid du Colombier 				  ? 6
1599*80ee5cbfSDavid du Colombier 				  : 0;
1600*80ee5cbfSDavid du Colombier 			}
1601*80ee5cbfSDavid du Colombier 
1602*80ee5cbfSDavid du Colombier 			/* Sink column. */
1603*80ee5cbfSDavid du Colombier 			G[i][N] = 0;
1604*80ee5cbfSDavid du Colombier 		}
1605*80ee5cbfSDavid du Colombier 
1606*80ee5cbfSDavid du Colombier 	/* Do the DP algorithm. */
1607*80ee5cbfSDavid du Colombier 	/* Proceed in column order, from highest column to the lowest. */
1608*80ee5cbfSDavid du Colombier 	/* Within each column, proceed from the highest row to the lowest. */
1609*80ee5cbfSDavid du Colombier 	/* Skip the highest column. */
1610*80ee5cbfSDavid du Colombier 		for (j = N - 1; j >= 0; j--)
1611*80ee5cbfSDavid du Colombier 			for (i = M - 1; i > 0; i--) {
1612*80ee5cbfSDavid du Colombier 				int max = G[i][j + 1];
1613*80ee5cbfSDavid du Colombier 
1614*80ee5cbfSDavid du Colombier 				if (i < (M - 1)) {
1615*80ee5cbfSDavid du Colombier 					int tmp = G[i + 1][j + 1];
1616*80ee5cbfSDavid du Colombier 					if (tmp > max) max = tmp;
1617*80ee5cbfSDavid du Colombier 				}
1618*80ee5cbfSDavid du Colombier 
1619*80ee5cbfSDavid du Colombier 				G[i][j] += max;
1620*80ee5cbfSDavid du Colombier 		}
1621*80ee5cbfSDavid du Colombier 
1622*80ee5cbfSDavid du Colombier 		sim = (10 * G[1][0] + (N - 1) / 2) / (N - 1);
1623*80ee5cbfSDavid du Colombier 
1624*80ee5cbfSDavid du Colombier 		if (G != nil)
1625*80ee5cbfSDavid du Colombier 				free(G);
1626*80ee5cbfSDavid du Colombier 		if (junk != nil)
1627*80ee5cbfSDavid du Colombier 				free(junk);
1628*80ee5cbfSDavid du Colombier 		return(sim);
1629*80ee5cbfSDavid du Colombier }
1630*80ee5cbfSDavid du Colombier 
1631*80ee5cbfSDavid du Colombier 
lialg_compute_distance(point_list * input_dompts,point_list * curr_dompts)1632*80ee5cbfSDavid du Colombier static int lialg_compute_distance(point_list *input_dompts,
1633*80ee5cbfSDavid du Colombier 								   point_list *curr_dompts) {
1634*80ee5cbfSDavid du Colombier 		int dist;
1635*80ee5cbfSDavid du Colombier 		point_list *A, *B;
1636*80ee5cbfSDavid du Colombier 		int N, M;
1637*80ee5cbfSDavid du Colombier 		int **C;
1638*80ee5cbfSDavid du Colombier 		int *junk;
1639*80ee5cbfSDavid du Colombier 		int *BE;
1640*80ee5cbfSDavid du Colombier 		int *TE;
1641*80ee5cbfSDavid du Colombier 		int i, j;
1642*80ee5cbfSDavid du Colombier 
1643*80ee5cbfSDavid du Colombier 		/* A is the	longer sequence, length	N. */
1644*80ee5cbfSDavid du Colombier 		/* B is the shorter sequence, length M. */
1645*80ee5cbfSDavid du Colombier 		if (input_dompts->npts >= curr_dompts->npts) {
1646*80ee5cbfSDavid du Colombier 				A = input_dompts;
1647*80ee5cbfSDavid du Colombier 				N = input_dompts->npts;
1648*80ee5cbfSDavid du Colombier 				B = curr_dompts;
1649*80ee5cbfSDavid du Colombier 				M = curr_dompts->npts;
1650*80ee5cbfSDavid du Colombier 		}
1651*80ee5cbfSDavid du Colombier 		else {
1652*80ee5cbfSDavid du Colombier 				A = curr_dompts;
1653*80ee5cbfSDavid du Colombier 				N = curr_dompts->npts;
1654*80ee5cbfSDavid du Colombier 				B = input_dompts;
1655*80ee5cbfSDavid du Colombier 				M = input_dompts->npts;
1656*80ee5cbfSDavid du Colombier 		}
1657*80ee5cbfSDavid du Colombier 
1658*80ee5cbfSDavid du Colombier 		/* Construct the helper vectors, BE and TE, which say for each column */
1659*80ee5cbfSDavid du Colombier 		/* what are the ``bottom'' and ``top'' rows of interest. */
1660*80ee5cbfSDavid du Colombier 		BE = malloc((N + 1)*sizeof(int));
1661*80ee5cbfSDavid du Colombier 		TE = malloc((N + 1)*sizeof(int));
1662*80ee5cbfSDavid du Colombier 
1663*80ee5cbfSDavid du Colombier 		for (j = 1; j <= N; j++) {
1664*80ee5cbfSDavid du Colombier 				int bot, top;
1665*80ee5cbfSDavid du Colombier 
1666*80ee5cbfSDavid du Colombier 				bot = j + (M - DP_BAND);
1667*80ee5cbfSDavid du Colombier 				if (bot > M) bot = M;
1668*80ee5cbfSDavid du Colombier 				BE[j] = bot;
1669*80ee5cbfSDavid du Colombier 
1670*80ee5cbfSDavid du Colombier 				top = j - (N - DP_BAND);
1671*80ee5cbfSDavid du Colombier 				if (top < 1) top = 1;
1672*80ee5cbfSDavid du Colombier 				TE[j] = top;
1673*80ee5cbfSDavid du Colombier 		}
1674*80ee5cbfSDavid du Colombier 
1675*80ee5cbfSDavid du Colombier 		/* Allocate and initialize the Cost matrix, C. */
1676*80ee5cbfSDavid du Colombier 		/* The size of C is (M + 1) x (N + 1). */
1677*80ee5cbfSDavid du Colombier 		/* Note that row and column 0 are unused. */
1678*80ee5cbfSDavid du Colombier 		/* Costs are x 100. */
1679*80ee5cbfSDavid du Colombier 		/*	C = (int **)safe_malloc((M + 1) * sizeof(int *)); */
1680*80ee5cbfSDavid du Colombier 		C = malloc((M + 1)*sizeof( int *));
1681*80ee5cbfSDavid du Colombier 		junk = malloc((M + 1) * (N + 1)*sizeof(int));
1682*80ee5cbfSDavid du Colombier 		for (i = 0; i <= M; i++)
1683*80ee5cbfSDavid du Colombier 				C[i] = junk + (i * (N + 1));
1684*80ee5cbfSDavid du Colombier 
1685*80ee5cbfSDavid du Colombier 		for (i = 1; i <= M; i++) {
1686*80ee5cbfSDavid du Colombier 				int bx = B->pts[i-1].x;
1687*80ee5cbfSDavid du Colombier 				int by = B->pts[i-1].y;
1688*80ee5cbfSDavid du Colombier 
1689*80ee5cbfSDavid du Colombier 				for (j = 1; j <= N; j++) {
1690*80ee5cbfSDavid du Colombier 						int ax = A->pts[j-1].x;
1691*80ee5cbfSDavid du Colombier 						int ay = A->pts[j-1].y;
1692*80ee5cbfSDavid du Colombier 						int dx = bx - ax;
1693*80ee5cbfSDavid du Colombier 						int dy = by - ay;
1694*80ee5cbfSDavid du Colombier 						int dist = isqrt(10000 * (dx * dx + dy * dy));
1695*80ee5cbfSDavid du Colombier 
1696*80ee5cbfSDavid du Colombier 						C[i][j] = dist;
1697*80ee5cbfSDavid du Colombier 				}
1698*80ee5cbfSDavid du Colombier 		}
1699*80ee5cbfSDavid du Colombier 
1700*80ee5cbfSDavid du Colombier 		/* Do the DP algorithm. */
1701*80ee5cbfSDavid du Colombier 		/* Proceed in column order, from highest column to the lowest. */
1702*80ee5cbfSDavid du Colombier 		/* Within each column, proceed from the highest row to the lowest. */
1703*80ee5cbfSDavid du Colombier 		for (j = N; j > 0; j--)
1704*80ee5cbfSDavid du Colombier 				for (i = M; i > 0; i--) {
1705*80ee5cbfSDavid du Colombier 						int min = MAX_DIST;
1706*80ee5cbfSDavid du Colombier 
1707*80ee5cbfSDavid du Colombier 						if (i > BE[j] || i < TE[j] || (j == N && i == M))
1708*80ee5cbfSDavid du Colombier 								continue;
1709*80ee5cbfSDavid du Colombier 
1710*80ee5cbfSDavid du Colombier 						if (j < N) {
1711*80ee5cbfSDavid du Colombier 								if (i >= TE[j+1]) {
1712*80ee5cbfSDavid du Colombier 										int tmp = C[i][j+1];
1713*80ee5cbfSDavid du Colombier 										if (tmp < min)
1714*80ee5cbfSDavid du Colombier 												min = tmp;
1715*80ee5cbfSDavid du Colombier 								}
1716*80ee5cbfSDavid du Colombier 
1717*80ee5cbfSDavid du Colombier 								if (i < M) {
1718*80ee5cbfSDavid du Colombier 										int tmp = C[i+1][j+1];
1719*80ee5cbfSDavid du Colombier 										if (tmp < min)
1720*80ee5cbfSDavid du Colombier 												min = tmp;
1721*80ee5cbfSDavid du Colombier 								}
1722*80ee5cbfSDavid du Colombier 						}
1723*80ee5cbfSDavid du Colombier 
1724*80ee5cbfSDavid du Colombier 						if (i < BE[j]) {
1725*80ee5cbfSDavid du Colombier 								int tmp = C[i+1][j];
1726*80ee5cbfSDavid du Colombier 								if (tmp < min) min = tmp;
1727*80ee5cbfSDavid du Colombier 						}
1728*80ee5cbfSDavid du Colombier 
1729*80ee5cbfSDavid du Colombier 						C[i][j] += min;
1730*80ee5cbfSDavid du Colombier 				}
1731*80ee5cbfSDavid du Colombier 
1732*80ee5cbfSDavid du Colombier 		dist = (C[1][1] + N / 2) / N;
1733*80ee5cbfSDavid du Colombier 
1734*80ee5cbfSDavid du Colombier 		if (C != nil) free(C);
1735*80ee5cbfSDavid du Colombier 		if (junk != nil) free(junk);
1736*80ee5cbfSDavid du Colombier 		if (BE != nil) free(BE);
1737*80ee5cbfSDavid du Colombier 		if (TE != nil) free(TE);
1738*80ee5cbfSDavid du Colombier 		return(dist);
1739*80ee5cbfSDavid du Colombier }
1740*80ee5cbfSDavid du Colombier 
1741*80ee5cbfSDavid du Colombier 
1742*80ee5cbfSDavid du Colombier /*************************************************************
1743*80ee5cbfSDavid du Colombier 
1744*80ee5cbfSDavid du Colombier   Digest-processing routines
1745*80ee5cbfSDavid du Colombier 
1746*80ee5cbfSDavid du Colombier  *************************************************************/
1747*80ee5cbfSDavid du Colombier 
lialg_read_classifier_digest(rClassifier * rec)1748*80ee5cbfSDavid du Colombier static int lialg_read_classifier_digest(rClassifier *rec) {
1749*80ee5cbfSDavid du Colombier 	int nclasses;
1750*80ee5cbfSDavid du Colombier 	FILE *fp;
1751*80ee5cbfSDavid du Colombier 
1752*80ee5cbfSDavid du Colombier 	/* Try to open the corresponding digest file. */
1753*80ee5cbfSDavid du Colombier 	{
1754*80ee5cbfSDavid du Colombier 				char *clx_path;
1755*80ee5cbfSDavid du Colombier 				char *dot;
1756*80ee5cbfSDavid du Colombier 
1757*80ee5cbfSDavid du Colombier 				/* Get a copy of the filename, with some room on the end. */
1758*80ee5cbfSDavid du Colombier 				/*	clx_path = safe_malloc(strlen(rec->file_name) + 5); */
1759*80ee5cbfSDavid du Colombier 				clx_path = malloc((strlen(rec->file_name) + 5) *sizeof(char));
1760*80ee5cbfSDavid du Colombier 				strcpy(clx_path, rec->file_name);
1761*80ee5cbfSDavid du Colombier 
1762*80ee5cbfSDavid du Colombier 				/* Truncate the path after the last dot. */
1763*80ee5cbfSDavid du Colombier 				dot = strrchr(clx_path, '.');
1764*80ee5cbfSDavid du Colombier 				if (dot == nil) { free(clx_path); return(-1); }
1765*80ee5cbfSDavid du Colombier 				*(dot + 1) = 0;
1766*80ee5cbfSDavid du Colombier 
1767*80ee5cbfSDavid du Colombier 				/* Append the classifier-digest extension. */
1768*80ee5cbfSDavid du Colombier 				strcat(clx_path, "clx");
1769*80ee5cbfSDavid du Colombier 
1770*80ee5cbfSDavid du Colombier 				fp = fopen(clx_path, "r");
1771*80ee5cbfSDavid du Colombier 				if (fp == nil) {
1772*80ee5cbfSDavid du Colombier 						free(clx_path);
1773*80ee5cbfSDavid du Colombier 						return(-1);
1774*80ee5cbfSDavid du Colombier 				}
1775*80ee5cbfSDavid du Colombier 
1776*80ee5cbfSDavid du Colombier 				free(clx_path);
1777*80ee5cbfSDavid du Colombier 	}
1778*80ee5cbfSDavid du Colombier 
1779*80ee5cbfSDavid du Colombier 	/* Read-in the name and dominant points for each class. */
1780*80ee5cbfSDavid du Colombier 	for (nclasses = 0; !feof(fp); nclasses++) {
1781*80ee5cbfSDavid du Colombier 		point_list *dpts = nil;
1782*80ee5cbfSDavid du Colombier 		char class[BUFSIZ];
1783*80ee5cbfSDavid du Colombier 		int npts;
1784*80ee5cbfSDavid du Colombier 		int j;
1785*80ee5cbfSDavid du Colombier 
1786*80ee5cbfSDavid du Colombier 		if (fscanf(fp, "%s %d", class, &npts) != 2) {
1787*80ee5cbfSDavid du Colombier 			if (feof(fp)) break;
1788*80ee5cbfSDavid du Colombier 
1789*80ee5cbfSDavid du Colombier 			goto failed;
1790*80ee5cbfSDavid du Colombier 		}
1791*80ee5cbfSDavid du Colombier 		rec->cnames[nclasses] = strdup(class);
1792*80ee5cbfSDavid du Colombier 
1793*80ee5cbfSDavid du Colombier 		/* Allocate a dominant-points list. */
1794*80ee5cbfSDavid du Colombier 		/* dpts = (point_list *)safe_malloc(sizeof(point_list)); */
1795*80ee5cbfSDavid du Colombier 		dpts = malloc(sizeof(point_list));
1796*80ee5cbfSDavid du Colombier 		dpts->pts = mallocz(npts*sizeof(pen_point), 1);
1797*80ee5cbfSDavid du Colombier 		if (dpts->pts == nil) goto failed;
1798*80ee5cbfSDavid du Colombier 		dpts->npts = npts;
1799*80ee5cbfSDavid du Colombier 		dpts->next = nil;
1800*80ee5cbfSDavid du Colombier 
1801*80ee5cbfSDavid du Colombier 		/* Read in each point. */
1802*80ee5cbfSDavid du Colombier 		for (j = 0; j < npts; j++) {
1803*80ee5cbfSDavid du Colombier 			int x, y;
1804*80ee5cbfSDavid du Colombier 
1805*80ee5cbfSDavid du Colombier 			if (fscanf(fp, "%d %d", &x, &y) != 2) goto failed;
1806*80ee5cbfSDavid du Colombier 			dpts->pts[j].x = x;
1807*80ee5cbfSDavid du Colombier 			dpts->pts[j].y = y;
1808*80ee5cbfSDavid du Colombier 		}
1809*80ee5cbfSDavid du Colombier 
1810*80ee5cbfSDavid du Colombier 		/* Compute the chain-code. */
1811*80ee5cbfSDavid du Colombier 		lialg_compute_chain_code(dpts);
1812*80ee5cbfSDavid du Colombier 
1813*80ee5cbfSDavid du Colombier 		/* Store the list in the rec data structure. */
1814*80ee5cbfSDavid du Colombier 		rec->dompts[nclasses] = dpts;
1815*80ee5cbfSDavid du Colombier 
1816*80ee5cbfSDavid du Colombier 		continue;
1817*80ee5cbfSDavid du Colombier 
1818*80ee5cbfSDavid du Colombier failed:
1819*80ee5cbfSDavid du Colombier 		fprint(2, "read_classifier_digest failed...\n");
1820*80ee5cbfSDavid du Colombier 		for (; nclasses >= 0; nclasses--) {
1821*80ee5cbfSDavid du Colombier 			if (rec->cnames[nclasses] != nil) {
1822*80ee5cbfSDavid du Colombier 				free(rec->cnames[nclasses]);
1823*80ee5cbfSDavid du Colombier 				rec->cnames[nclasses] = nil;
1824*80ee5cbfSDavid du Colombier 			}
1825*80ee5cbfSDavid du Colombier 			if (rec->dompts[nclasses] != nil) {
1826*80ee5cbfSDavid du Colombier 				delete_examples(rec->dompts[nclasses]);
1827*80ee5cbfSDavid du Colombier 				rec->dompts[nclasses] = nil;
1828*80ee5cbfSDavid du Colombier 			}
1829*80ee5cbfSDavid du Colombier 		}
1830*80ee5cbfSDavid du Colombier 		if (dpts != nil)
1831*80ee5cbfSDavid du Colombier 			delete_examples(dpts);
1832*80ee5cbfSDavid du Colombier 		fclose(fp);
1833*80ee5cbfSDavid du Colombier 		return(-1);
1834*80ee5cbfSDavid du Colombier 	}
1835*80ee5cbfSDavid du Colombier 
1836*80ee5cbfSDavid du Colombier 	fclose(fp);
1837*80ee5cbfSDavid du Colombier 	return(0);
1838*80ee5cbfSDavid du Colombier }
1839*80ee5cbfSDavid du Colombier 
1840*80ee5cbfSDavid du Colombier 
1841*80ee5cbfSDavid du Colombier /*************************************************************
1842*80ee5cbfSDavid du Colombier 
1843*80ee5cbfSDavid du Colombier   Canonicalization routines
1844*80ee5cbfSDavid du Colombier 
1845*80ee5cbfSDavid du Colombier  *************************************************************/
1846*80ee5cbfSDavid du Colombier 
lialg_canonicalize_examples(rClassifier * rec)1847*80ee5cbfSDavid du Colombier static int lialg_canonicalize_examples(rClassifier *rec) {
1848*80ee5cbfSDavid du Colombier 	int i;
1849*80ee5cbfSDavid du Colombier 	int nclasses;
1850*80ee5cbfSDavid du Colombier 
1851*80ee5cbfSDavid du Colombier 	if (lidebug) {
1852*80ee5cbfSDavid du Colombier 		fprint(2, "lialg_canonicalize_examples working on %s\n",
1853*80ee5cbfSDavid du Colombier 				rec->file_name);
1854*80ee5cbfSDavid du Colombier 	}
1855*80ee5cbfSDavid du Colombier 	/* Initialize canonical-example arrays. */
1856*80ee5cbfSDavid du Colombier 	for (i = 0; i < MAXSCLASSES; i++) {
1857*80ee5cbfSDavid du Colombier 		rec->canonex[i] = nil;
1858*80ee5cbfSDavid du Colombier 	}
1859*80ee5cbfSDavid du Colombier 
1860*80ee5cbfSDavid du Colombier 	/* Figure out number of classes. */
1861*80ee5cbfSDavid du Colombier 	for (nclasses = 0;
1862*80ee5cbfSDavid du Colombier 		  nclasses < MAXSCLASSES && rec->cnames[nclasses] != nil;
1863*80ee5cbfSDavid du Colombier 		  nclasses++)
1864*80ee5cbfSDavid du Colombier 		;
1865*80ee5cbfSDavid du Colombier 
1866*80ee5cbfSDavid du Colombier 	/* Canonicalize the examples for each class. */
1867*80ee5cbfSDavid du Colombier 	for (i = 0; i < nclasses; i++) {
1868*80ee5cbfSDavid du Colombier 		int j, k;
1869*80ee5cbfSDavid du Colombier 		int nex;
1870*80ee5cbfSDavid du Colombier 		point_list *pts, *tmp, *avg;
1871*80ee5cbfSDavid du Colombier 		int maxxrange, maxyrange;
1872*80ee5cbfSDavid du Colombier 		int minx, miny, maxx, maxy;
1873*80ee5cbfSDavid du Colombier 		int avgxrange, avgyrange, avgxoff, avgyoff, avgscale;
1874*80ee5cbfSDavid du Colombier 
1875*80ee5cbfSDavid du Colombier 
1876*80ee5cbfSDavid du Colombier 		if (lidebug) {
1877*80ee5cbfSDavid du Colombier 			fprint(2, "lialg_canonicalize_examples working on class %s\n",
1878*80ee5cbfSDavid du Colombier 					rec->cnames[i]);
1879*80ee5cbfSDavid du Colombier 		}
1880*80ee5cbfSDavid du Colombier 		/* Make a copy of the examples. */
1881*80ee5cbfSDavid du Colombier 		pts = nil;
1882*80ee5cbfSDavid du Colombier 		tmp = rec->ex[i];
1883*80ee5cbfSDavid du Colombier 		for (nex = 0; tmp != nil; nex++, tmp = tmp->next) {
1884*80ee5cbfSDavid du Colombier 			if ((pts = add_example(pts, tmp->npts, tmp->pts)) == nil) {
1885*80ee5cbfSDavid du Colombier 				delete_examples(pts);
1886*80ee5cbfSDavid du Colombier 				return(-1);
1887*80ee5cbfSDavid du Colombier 			}
1888*80ee5cbfSDavid du Colombier 		}
1889*80ee5cbfSDavid du Colombier 
1890*80ee5cbfSDavid du Colombier 		/* Canonicalize each example, and derive the max x and y ranges. */
1891*80ee5cbfSDavid du Colombier 		maxxrange = 0;
1892*80ee5cbfSDavid du Colombier 		maxyrange = 0;
1893*80ee5cbfSDavid du Colombier 		for (j = 0, tmp = pts; j < nex; j++, tmp = tmp->next) {
1894*80ee5cbfSDavid du Colombier 			if (lialg_canonicalize_example_stroke(tmp) != 0) {
1895*80ee5cbfSDavid du Colombier   	        if (lidebug) {
1896*80ee5cbfSDavid du Colombier 					fprint(2, "lialg_canonicalize_example_stroke returned error\n");
1897*80ee5cbfSDavid du Colombier 				}
1898*80ee5cbfSDavid du Colombier 				return(-1);
1899*80ee5cbfSDavid du Colombier 			}
1900*80ee5cbfSDavid du Colombier 
1901*80ee5cbfSDavid du Colombier 			if (tmp->xrange > maxxrange) maxxrange = tmp->xrange;
1902*80ee5cbfSDavid du Colombier 			if (tmp->yrange > maxyrange) maxyrange = tmp->yrange;
1903*80ee5cbfSDavid du Colombier 		}
1904*80ee5cbfSDavid du Colombier 
1905*80ee5cbfSDavid du Colombier 		/* Normalize max ranges. */
1906*80ee5cbfSDavid du Colombier 		if (((100 * maxxrange + CANONICAL_X / 2) / CANONICAL_X) >
1907*80ee5cbfSDavid du Colombier 			((100 * maxyrange + CANONICAL_Y / 2) / CANONICAL_Y)) {
1908*80ee5cbfSDavid du Colombier 			maxyrange = (maxyrange * CANONICAL_X + maxxrange / 2) / maxxrange;
1909*80ee5cbfSDavid du Colombier 			maxxrange = CANONICAL_X;
1910*80ee5cbfSDavid du Colombier 		}
1911*80ee5cbfSDavid du Colombier 		else {
1912*80ee5cbfSDavid du Colombier 			maxxrange = (maxxrange * CANONICAL_Y + maxyrange / 2) / maxyrange;
1913*80ee5cbfSDavid du Colombier 			maxyrange = CANONICAL_Y;
1914*80ee5cbfSDavid du Colombier 		}
1915*80ee5cbfSDavid du Colombier 
1916*80ee5cbfSDavid du Colombier 		/* Re-scale each example to max ranges. */
1917*80ee5cbfSDavid du Colombier 		for (j = 0, tmp = pts; j < nex; j++, tmp = tmp->next) {
1918*80ee5cbfSDavid du Colombier 			int scalex = (tmp->xrange == 0) ? 100 : (100 * maxxrange + tmp->xrange / 2) / tmp->xrange;
1919*80ee5cbfSDavid du Colombier 			int scaley = (tmp->yrange == 0) ? 100 : (100 * maxyrange + tmp->yrange / 2) / tmp->yrange;
1920*80ee5cbfSDavid du Colombier 			if (lialg_translate_points(tmp, 0, 0, scalex, scaley) != 0) {
1921*80ee5cbfSDavid du Colombier 				delete_examples(pts);
1922*80ee5cbfSDavid du Colombier 				return(-1);
1923*80ee5cbfSDavid du Colombier 			}
1924*80ee5cbfSDavid du Colombier 		}
1925*80ee5cbfSDavid du Colombier 
1926*80ee5cbfSDavid du Colombier 		/* Average the examples; leave average in first example. */
1927*80ee5cbfSDavid du Colombier 		avg = pts;				/* careful aliasing!! */
1928*80ee5cbfSDavid du Colombier 		for (k = 0; k < NCANONICAL; k++) {
1929*80ee5cbfSDavid du Colombier 			int xsum = 0;
1930*80ee5cbfSDavid du Colombier 			int ysum = 0;
1931*80ee5cbfSDavid du Colombier 
1932*80ee5cbfSDavid du Colombier 			for (j = 0, tmp = pts; j < nex; j++, tmp = tmp->next) {
1933*80ee5cbfSDavid du Colombier 						xsum += tmp->pts[k].x;
1934*80ee5cbfSDavid du Colombier 						ysum += tmp->pts[k].y;
1935*80ee5cbfSDavid du Colombier 			}
1936*80ee5cbfSDavid du Colombier 
1937*80ee5cbfSDavid du Colombier 			avg->pts[k].x = (xsum + j / 2) / j;
1938*80ee5cbfSDavid du Colombier 			avg->pts[k].y = (ysum + j / 2) / j;
1939*80ee5cbfSDavid du Colombier 		}
1940*80ee5cbfSDavid du Colombier 
1941*80ee5cbfSDavid du Colombier 		/* Compute BB of averaged stroke and re-scale. */
1942*80ee5cbfSDavid du Colombier 		lialg_get_bounding_box(avg, &minx, &miny, &maxx, &maxy);
1943*80ee5cbfSDavid du Colombier 		avgxrange = maxx - minx;
1944*80ee5cbfSDavid du Colombier 		avgyrange = maxy - miny;
1945*80ee5cbfSDavid du Colombier 		avgscale = (((100 * avgxrange + CANONICAL_X / 2) / CANONICAL_X) >
1946*80ee5cbfSDavid du Colombier 					((100 * avgyrange + CANONICAL_Y / 2) / CANONICAL_Y))
1947*80ee5cbfSDavid du Colombier 		  ? (100 * CANONICAL_X + avgxrange / 2) / avgxrange
1948*80ee5cbfSDavid du Colombier 		  : (100 * CANONICAL_Y + avgyrange / 2) / avgyrange;
1949*80ee5cbfSDavid du Colombier 		if (lialg_translate_points(avg, minx, miny, avgscale, avgscale) != 0) {
1950*80ee5cbfSDavid du Colombier 			delete_examples(pts);
1951*80ee5cbfSDavid du Colombier 			return(-1);
1952*80ee5cbfSDavid du Colombier 		}
1953*80ee5cbfSDavid du Colombier 
1954*80ee5cbfSDavid du Colombier 		/* Re-compute the x and y ranges and center the stroke. */
1955*80ee5cbfSDavid du Colombier 		lialg_get_bounding_box(avg, &minx, &miny, &maxx, &maxy);
1956*80ee5cbfSDavid du Colombier 		avgxrange = maxx - minx;
1957*80ee5cbfSDavid du Colombier 		avgyrange = maxy - miny;
1958*80ee5cbfSDavid du Colombier 		avgxoff = -((CANONICAL_X - avgxrange + 1) / 2);
1959*80ee5cbfSDavid du Colombier 		avgyoff = -((CANONICAL_Y - avgyrange + 1) / 2);
1960*80ee5cbfSDavid du Colombier 		if (lialg_translate_points(avg, avgxoff, avgyoff, 100, 100) != 0) {
1961*80ee5cbfSDavid du Colombier 			delete_examples(pts);
1962*80ee5cbfSDavid du Colombier 			return(-1);
1963*80ee5cbfSDavid du Colombier 		}
1964*80ee5cbfSDavid du Colombier 
1965*80ee5cbfSDavid du Colombier 		/* Create a point list to serve as the ``canonical representation. */
1966*80ee5cbfSDavid du Colombier 		if ((rec->canonex[i] = add_example(nil, avg->npts, avg->pts)) == nil) {
1967*80ee5cbfSDavid du Colombier 			delete_examples(pts);
1968*80ee5cbfSDavid du Colombier 			return(-1);
1969*80ee5cbfSDavid du Colombier 		}
1970*80ee5cbfSDavid du Colombier 		(rec->canonex[i])->xrange = maxx - minx;
1971*80ee5cbfSDavid du Colombier 		(rec->canonex[i])->yrange = maxy - miny;
1972*80ee5cbfSDavid du Colombier 
1973*80ee5cbfSDavid du Colombier 		if (lidebug) {
1974*80ee5cbfSDavid du Colombier 			fprint(2, "%s, avgpts = %d\n", rec->cnames[i], avg->npts);
1975*80ee5cbfSDavid du Colombier 			for (j = 0; j < avg->npts; j++) {
1976*80ee5cbfSDavid du Colombier 						fprint(2, "  (%P)\n", avg->pts[j].Point);
1977*80ee5cbfSDavid du Colombier 			}
1978*80ee5cbfSDavid du Colombier 		}
1979*80ee5cbfSDavid du Colombier 
1980*80ee5cbfSDavid du Colombier 		/* Compute dominant points of canonical representation. */
1981*80ee5cbfSDavid du Colombier 		rec->dompts[i] = lialg_compute_dominant_points(avg);
1982*80ee5cbfSDavid du Colombier 
1983*80ee5cbfSDavid du Colombier 		/* Clean up. */
1984*80ee5cbfSDavid du Colombier 		delete_examples(pts);
1985*80ee5cbfSDavid du Colombier 	}
1986*80ee5cbfSDavid du Colombier 
1987*80ee5cbfSDavid du Colombier 	/* Sanity check. */
1988*80ee5cbfSDavid du Colombier 	for (i = 0; i < nclasses; i++) {
1989*80ee5cbfSDavid du Colombier 		char *best_name = lialg_recognize_stroke(rec, rec->canonex[i]);
1990*80ee5cbfSDavid du Colombier 
1991*80ee5cbfSDavid du Colombier 		if (best_name != rec->cnames[i])
1992*80ee5cbfSDavid du Colombier 			fprint(2, "%s, best = %s\n", rec->cnames[i], best_name);
1993*80ee5cbfSDavid du Colombier 	}
1994*80ee5cbfSDavid du Colombier 
1995*80ee5cbfSDavid du Colombier 	return(0);
1996*80ee5cbfSDavid du Colombier }
1997*80ee5cbfSDavid du Colombier 
1998*80ee5cbfSDavid du Colombier 
lialg_canonicalize_example_stroke(point_list * points)1999*80ee5cbfSDavid du Colombier static int lialg_canonicalize_example_stroke(point_list *points) {
2000*80ee5cbfSDavid du Colombier 	int minx, miny, maxx, maxy, xrange, yrange, scale;
2001*80ee5cbfSDavid du Colombier 
2002*80ee5cbfSDavid du Colombier 	/* Filter out points that are too close. */
2003*80ee5cbfSDavid du Colombier 	if (lialg_filter_points(points) != 0) return(-1);
2004*80ee5cbfSDavid du Colombier 
2005*80ee5cbfSDavid du Colombier 	/* Must be at least two points! */
2006*80ee5cbfSDavid du Colombier 	if (points->npts < 2) {
2007*80ee5cbfSDavid du Colombier 		if (lidebug) {
2008*80ee5cbfSDavid du Colombier 			fprint(2, "lialg_canonicalize_example_stroke: npts=%d\n",
2009*80ee5cbfSDavid du Colombier 					points->npts);
2010*80ee5cbfSDavid du Colombier 		}
2011*80ee5cbfSDavid du Colombier 		return(-1);
2012*80ee5cbfSDavid du Colombier 	}
2013*80ee5cbfSDavid du Colombier 
2014*80ee5cbfSDavid du Colombier 	/* Scale up to avoid conversion errors. */
2015*80ee5cbfSDavid du Colombier 	lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
2016*80ee5cbfSDavid du Colombier 	xrange = maxx - minx;
2017*80ee5cbfSDavid du Colombier 	yrange = maxy - miny;
2018*80ee5cbfSDavid du Colombier 	scale = (((100 * xrange + CANONICAL_X / 2) / CANONICAL_X) >
2019*80ee5cbfSDavid du Colombier 			 ((100 * yrange + CANONICAL_Y / 2) / CANONICAL_Y))
2020*80ee5cbfSDavid du Colombier 	  ? (100 * CANONICAL_X + xrange / 2) / xrange
2021*80ee5cbfSDavid du Colombier 	  : (100 * CANONICAL_Y + yrange / 2) / yrange;
2022*80ee5cbfSDavid du Colombier 	if (lialg_translate_points(points, minx, miny, scale, scale) != 0) {
2023*80ee5cbfSDavid du Colombier 		if (lidebug) {
2024*80ee5cbfSDavid du Colombier 			fprint(2, "lialg_translate_points (minx=%d,miny=%d,scale=%d) returned error\n", minx, miny, scale);
2025*80ee5cbfSDavid du Colombier 		}
2026*80ee5cbfSDavid du Colombier 		return(-1);
2027*80ee5cbfSDavid du Colombier 	}
2028*80ee5cbfSDavid du Colombier 
2029*80ee5cbfSDavid du Colombier 	/* Compute an equivalent stroke with equi-distant points. */
2030*80ee5cbfSDavid du Colombier 	if (lialg_compute_equipoints(points) != 0) return(-1);
2031*80ee5cbfSDavid du Colombier 
2032*80ee5cbfSDavid du Colombier 	/* Re-translate the points to the origin. */
2033*80ee5cbfSDavid du Colombier 	lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
2034*80ee5cbfSDavid du Colombier 	if (lialg_translate_points(points, minx, miny, 100, 100) != 0) {
2035*80ee5cbfSDavid du Colombier 		if (lidebug) {
2036*80ee5cbfSDavid du Colombier 			fprint(2, "lialg_translate_points (minx=%d,miny=%d) returned error\n", minx, miny);
2037*80ee5cbfSDavid du Colombier 		}
2038*80ee5cbfSDavid du Colombier 		return(-1);
2039*80ee5cbfSDavid du Colombier 	}
2040*80ee5cbfSDavid du Colombier 
2041*80ee5cbfSDavid du Colombier 	/* Store the x and y ranges in the point list. */
2042*80ee5cbfSDavid du Colombier 	xrange = maxx - minx;
2043*80ee5cbfSDavid du Colombier 	yrange = maxy - miny;
2044*80ee5cbfSDavid du Colombier 	points->xrange = xrange;
2045*80ee5cbfSDavid du Colombier 	points->yrange = yrange;
2046*80ee5cbfSDavid du Colombier 
2047*80ee5cbfSDavid du Colombier 	if (lidebug) {
2048*80ee5cbfSDavid du Colombier 		int i;
2049*80ee5cbfSDavid du Colombier 		fprint(2, "Canonicalized:   %d, %d, %d, %d\n", minx, miny, maxx, maxy);
2050*80ee5cbfSDavid du Colombier 		for (i = 0; i < points->npts; i++)
2051*80ee5cbfSDavid du Colombier 			fprint(2, "      (%P)\n", points->pts[i].Point);
2052*80ee5cbfSDavid du Colombier 		fflush(stderr);
2053*80ee5cbfSDavid du Colombier 	}
2054*80ee5cbfSDavid du Colombier 
2055*80ee5cbfSDavid du Colombier 	return(0);
2056*80ee5cbfSDavid du Colombier }
2057*80ee5cbfSDavid du Colombier 
2058*80ee5cbfSDavid du Colombier 
lialg_compute_equipoints(point_list * points)2059*80ee5cbfSDavid du Colombier static int lialg_compute_equipoints(point_list *points) {
2060*80ee5cbfSDavid du Colombier 	pen_point *equipoints = mallocz(NCANONICAL*sizeof(pen_point), 1);
2061*80ee5cbfSDavid du Colombier 	int nequipoints = 0;
2062*80ee5cbfSDavid du Colombier 	int pathlen = lialg_compute_pathlen(points);
2063*80ee5cbfSDavid du Colombier 	int equidist = (pathlen + (NCANONICAL - 1) / 2) / (NCANONICAL - 1);
2064*80ee5cbfSDavid du Colombier 	int i;
2065*80ee5cbfSDavid du Colombier 	int dist_since_last_eqpt;
2066*80ee5cbfSDavid du Colombier 	int remaining_seglen;
2067*80ee5cbfSDavid du Colombier 	int dist_to_next_eqpt;
2068*80ee5cbfSDavid du Colombier 
2069*80ee5cbfSDavid du Colombier 	if (equipoints == nil) {
2070*80ee5cbfSDavid du Colombier 		fprint(2, "can't allocate memory in lialg_compute_equipoints");
2071*80ee5cbfSDavid du Colombier 		return(-1);
2072*80ee5cbfSDavid du Colombier 	}
2073*80ee5cbfSDavid du Colombier 
2074*80ee5cbfSDavid du Colombier 	if (lidebug) {
2075*80ee5cbfSDavid du Colombier 		fprint(2, "compute_equipoints:  npts = %d, pathlen = %d, equidist = %d\n",
2076*80ee5cbfSDavid du Colombier 				points->npts, pathlen, equidist);
2077*80ee5cbfSDavid du Colombier 		fflush(stderr);
2078*80ee5cbfSDavid du Colombier 	}
2079*80ee5cbfSDavid du Colombier 
2080*80ee5cbfSDavid du Colombier 	/* First original point is an equipoint. */
2081*80ee5cbfSDavid du Colombier 	equipoints[0] = points->pts[0];
2082*80ee5cbfSDavid du Colombier 	nequipoints++;
2083*80ee5cbfSDavid du Colombier 	dist_since_last_eqpt = 0;
2084*80ee5cbfSDavid du Colombier 
2085*80ee5cbfSDavid du Colombier 	for (i = 1; i < points->npts; i++) {
2086*80ee5cbfSDavid du Colombier 		int dx1 = points->pts[i].x - points->pts[i-1].x;
2087*80ee5cbfSDavid du Colombier 		int dy1 = points->pts[i].y - points->pts[i-1].y;
2088*80ee5cbfSDavid du Colombier 		int endx = 100 * points->pts[i-1].x;
2089*80ee5cbfSDavid du Colombier 		int endy = 100 * points->pts[i-1].y;
2090*80ee5cbfSDavid du Colombier 		remaining_seglen = isqrt(10000 * (dx1 * dx1 + dy1 * dy1));
2091*80ee5cbfSDavid du Colombier 		dist_to_next_eqpt = equidist - dist_since_last_eqpt;
2092*80ee5cbfSDavid du Colombier 
2093*80ee5cbfSDavid du Colombier 		while (remaining_seglen >= dist_to_next_eqpt) {
2094*80ee5cbfSDavid du Colombier 			if (dx1 == 0) {
2095*80ee5cbfSDavid du Colombier 				/* x-coordinate stays the same */
2096*80ee5cbfSDavid du Colombier 				if (dy1 >= 0)
2097*80ee5cbfSDavid du Colombier 					endy += dist_to_next_eqpt;
2098*80ee5cbfSDavid du Colombier 				else
2099*80ee5cbfSDavid du Colombier 					endy -= dist_to_next_eqpt;
2100*80ee5cbfSDavid du Colombier 			}
2101*80ee5cbfSDavid du Colombier 			else {
2102*80ee5cbfSDavid du Colombier 				int slope = (100 * dy1 + dx1 / 2) / dx1;
2103*80ee5cbfSDavid du Colombier 				int tmp = isqrt(10000 + slope * slope);
2104*80ee5cbfSDavid du Colombier 				int dx = (100 * dist_to_next_eqpt + tmp / 2) / tmp;
2105*80ee5cbfSDavid du Colombier 				int dy = (slope * dx + 50) / 100;
2106*80ee5cbfSDavid du Colombier 
2107*80ee5cbfSDavid du Colombier 				if (dy < 0) dy = -dy;
2108*80ee5cbfSDavid du Colombier 				if (dx1 >= 0)
2109*80ee5cbfSDavid du Colombier 					endx += dx;
2110*80ee5cbfSDavid du Colombier 				else
2111*80ee5cbfSDavid du Colombier 					endx -= dx;
2112*80ee5cbfSDavid du Colombier 				if (dy1 >= 0)
2113*80ee5cbfSDavid du Colombier 					endy += dy;
2114*80ee5cbfSDavid du Colombier 				else
2115*80ee5cbfSDavid du Colombier 					endy -= dy;
2116*80ee5cbfSDavid du Colombier 			}
2117*80ee5cbfSDavid du Colombier 
2118*80ee5cbfSDavid du Colombier 			equipoints[nequipoints].x = (endx + 50) / 100;
2119*80ee5cbfSDavid du Colombier 			equipoints[nequipoints].y = (endy + 50) / 100;
2120*80ee5cbfSDavid du Colombier 			nequipoints++;
2121*80ee5cbfSDavid du Colombier /*	    assert(nequipoints <= NCANONICAL);*/
2122*80ee5cbfSDavid du Colombier 			dist_since_last_eqpt = 0;
2123*80ee5cbfSDavid du Colombier 			remaining_seglen -= dist_to_next_eqpt;
2124*80ee5cbfSDavid du Colombier 			dist_to_next_eqpt = equidist;
2125*80ee5cbfSDavid du Colombier 		}
2126*80ee5cbfSDavid du Colombier 
2127*80ee5cbfSDavid du Colombier 		dist_since_last_eqpt += remaining_seglen;
2128*80ee5cbfSDavid du Colombier 	}
2129*80ee5cbfSDavid du Colombier 
2130*80ee5cbfSDavid du Colombier 	/* Take care of last equipoint. */
2131*80ee5cbfSDavid du Colombier 	if (nequipoints == NCANONICAL) {
2132*80ee5cbfSDavid du Colombier 		/* Good. */
2133*80ee5cbfSDavid du Colombier 	} else if (nequipoints == (NCANONICAL - 1)) {
2134*80ee5cbfSDavid du Colombier 		/* Make last original point the last equipoint. */
2135*80ee5cbfSDavid du Colombier 		equipoints[nequipoints] = points->pts[points->npts - 1];
2136*80ee5cbfSDavid du Colombier 	} else {
2137*80ee5cbfSDavid du Colombier 	  if (lidebug) {
2138*80ee5cbfSDavid du Colombier 		fprint(2,"lialg_compute_equipoints: nequipoints = %d\n",
2139*80ee5cbfSDavid du Colombier 				nequipoints);
2140*80ee5cbfSDavid du Colombier 	  }
2141*80ee5cbfSDavid du Colombier /*	assert(false);*/
2142*80ee5cbfSDavid du Colombier 		return(-1);
2143*80ee5cbfSDavid du Colombier 	}
2144*80ee5cbfSDavid du Colombier 
2145*80ee5cbfSDavid du Colombier 	points->npts = NCANONICAL;
2146*80ee5cbfSDavid du Colombier 	delete_pen_point_array(points->pts);
2147*80ee5cbfSDavid du Colombier 	points->pts = equipoints;
2148*80ee5cbfSDavid du Colombier 	return(0);
2149*80ee5cbfSDavid du Colombier }
2150*80ee5cbfSDavid du Colombier 
2151*80ee5cbfSDavid du Colombier 
2152*80ee5cbfSDavid du Colombier /*************************************************************
2153*80ee5cbfSDavid du Colombier 
2154*80ee5cbfSDavid du Colombier   Utility routines
2155*80ee5cbfSDavid du Colombier 
2156*80ee5cbfSDavid du Colombier  *************************************************************/
2157*80ee5cbfSDavid du Colombier 
2158*80ee5cbfSDavid du Colombier /* Result is x 100. */
lialg_compute_pathlen(point_list * points)2159*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen(point_list *points) {
2160*80ee5cbfSDavid du Colombier 	return(lialg_compute_pathlen_subset(points, 0, points->npts - 1));
2161*80ee5cbfSDavid du Colombier }
2162*80ee5cbfSDavid du Colombier 
2163*80ee5cbfSDavid du Colombier 
2164*80ee5cbfSDavid du Colombier /* Result is x 100. */
lialg_compute_pathlen_subset(point_list * points,int start,int end)2165*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen_subset(point_list *points,
2166*80ee5cbfSDavid du Colombier 										   int start, int end) {
2167*80ee5cbfSDavid du Colombier 	int pathlen;
2168*80ee5cbfSDavid du Colombier 	int i;
2169*80ee5cbfSDavid du Colombier 
2170*80ee5cbfSDavid du Colombier 	pathlen = 0;
2171*80ee5cbfSDavid du Colombier 	for (i = start + 1; i <= end; i++) {
2172*80ee5cbfSDavid du Colombier 		int dx = points->pts[i].x - points->pts[i-1].x;
2173*80ee5cbfSDavid du Colombier 		int dy = points->pts[i].y - points->pts[i-1].y;
2174*80ee5cbfSDavid du Colombier 		int dist = isqrt(10000 * (dx * dx + dy * dy));
2175*80ee5cbfSDavid du Colombier 		pathlen += dist;
2176*80ee5cbfSDavid du Colombier 	}
2177*80ee5cbfSDavid du Colombier 
2178*80ee5cbfSDavid du Colombier 	return(pathlen);
2179*80ee5cbfSDavid du Colombier }
2180*80ee5cbfSDavid du Colombier 
2181*80ee5cbfSDavid du Colombier 
2182*80ee5cbfSDavid du Colombier /* Note that this does NOT update points->xrange and points->yrange! */
lialg_filter_points(point_list * points)2183*80ee5cbfSDavid du Colombier static int lialg_filter_points(point_list *points) {
2184*80ee5cbfSDavid du Colombier 	int filtered_npts;
2185*80ee5cbfSDavid du Colombier 	pen_point *filtered_pts = mallocz(points->npts*sizeof(pen_point), 1);
2186*80ee5cbfSDavid du Colombier 	int i;
2187*80ee5cbfSDavid du Colombier 
2188*80ee5cbfSDavid du Colombier 	if (filtered_pts == nil) {
2189*80ee5cbfSDavid du Colombier 		fprint(2, "can't allocate memory in lialg_filter_points");
2190*80ee5cbfSDavid du Colombier 		return(-1);
2191*80ee5cbfSDavid du Colombier 	}
2192*80ee5cbfSDavid du Colombier 
2193*80ee5cbfSDavid du Colombier 	filtered_pts[0] = points->pts[0];
2194*80ee5cbfSDavid du Colombier 	filtered_npts = 1;
2195*80ee5cbfSDavid du Colombier 	for (i = 1; i < points->npts; i++) {
2196*80ee5cbfSDavid du Colombier 		int j = filtered_npts - 1;
2197*80ee5cbfSDavid du Colombier 		int dx = points->pts[i].x - filtered_pts[j].x;
2198*80ee5cbfSDavid du Colombier 		int dy = points->pts[i].y - filtered_pts[j].y;
2199*80ee5cbfSDavid du Colombier 		int magsq = dx * dx + dy * dy;
2200*80ee5cbfSDavid du Colombier 
2201*80ee5cbfSDavid du Colombier 		if (magsq >= DIST_SQ_THRESHOLD) {
2202*80ee5cbfSDavid du Colombier 			filtered_pts[filtered_npts] = points->pts[i];
2203*80ee5cbfSDavid du Colombier 			filtered_npts++;
2204*80ee5cbfSDavid du Colombier 		}
2205*80ee5cbfSDavid du Colombier 	}
2206*80ee5cbfSDavid du Colombier 
2207*80ee5cbfSDavid du Colombier 	points->npts = filtered_npts;
2208*80ee5cbfSDavid du Colombier 	delete_pen_point_array(points->pts);
2209*80ee5cbfSDavid du Colombier 	points->pts = filtered_pts;
2210*80ee5cbfSDavid du Colombier 	return(0);
2211*80ee5cbfSDavid du Colombier }
2212*80ee5cbfSDavid du Colombier 
2213*80ee5cbfSDavid du Colombier 
2214*80ee5cbfSDavid du Colombier /* scalex and scaley are x 100. */
2215*80ee5cbfSDavid du Colombier /* Note that this does NOT update points->xrange and points->yrange! */
lialg_translate_points(point_list * points,int minx,int miny,int scalex,int scaley)2216*80ee5cbfSDavid du Colombier static int lialg_translate_points(point_list *points,
2217*80ee5cbfSDavid du Colombier 								   int minx, int miny,
2218*80ee5cbfSDavid du Colombier 								   int scalex, int scaley) {
2219*80ee5cbfSDavid du Colombier 	int i;
2220*80ee5cbfSDavid du Colombier 
2221*80ee5cbfSDavid du Colombier 	for (i = 0; i < points->npts; i++) {
2222*80ee5cbfSDavid du Colombier 				points->pts[i].x = ((points->pts[i].x - minx) * scalex + 50) / 100;
2223*80ee5cbfSDavid du Colombier 				points->pts[i].y = ((points->pts[i].y - miny) * scaley + 50) / 100;
2224*80ee5cbfSDavid du Colombier 	}
2225*80ee5cbfSDavid du Colombier 
2226*80ee5cbfSDavid du Colombier 	return(0);
2227*80ee5cbfSDavid du Colombier }
2228*80ee5cbfSDavid du Colombier 
2229*80ee5cbfSDavid du Colombier 
lialg_get_bounding_box(point_list * points,int * pminx,int * pminy,int * pmaxx,int * pmaxy)2230*80ee5cbfSDavid du Colombier static void lialg_get_bounding_box(point_list *points,
2231*80ee5cbfSDavid du Colombier 									int *pminx, int *pminy,
2232*80ee5cbfSDavid du Colombier 									int *pmaxx, int *pmaxy) {
2233*80ee5cbfSDavid du Colombier 	int minx, miny, maxx, maxy;
2234*80ee5cbfSDavid du Colombier 	int i;
2235*80ee5cbfSDavid du Colombier 
2236*80ee5cbfSDavid du Colombier 	minx = maxx = points->pts[0].x;
2237*80ee5cbfSDavid du Colombier 	miny = maxy = points->pts[0].y;
2238*80ee5cbfSDavid du Colombier 	for (i = 1; i < points->npts; i++) {
2239*80ee5cbfSDavid du Colombier 				pen_point *pt = &(points->pts[i]);
2240*80ee5cbfSDavid du Colombier 				if (pt->x < minx) minx = pt->x;
2241*80ee5cbfSDavid du Colombier 				else if (pt->x > maxx) maxx = pt->x;
2242*80ee5cbfSDavid du Colombier 				if (pt->y < miny) miny = pt->y;
2243*80ee5cbfSDavid du Colombier 				else if (pt->y > maxy) maxy = pt->y;
2244*80ee5cbfSDavid du Colombier 	}
2245*80ee5cbfSDavid du Colombier 
2246*80ee5cbfSDavid du Colombier 	*pminx = minx;
2247*80ee5cbfSDavid du Colombier 	*pminy = miny;
2248*80ee5cbfSDavid du Colombier 	*pmaxx = maxx;
2249*80ee5cbfSDavid du Colombier 	*pmaxy = maxy;
2250*80ee5cbfSDavid du Colombier }
2251*80ee5cbfSDavid du Colombier 
2252*80ee5cbfSDavid du Colombier 
2253*80ee5cbfSDavid du Colombier int wtvals[] = {100, 104, 117, 143, 189, 271, 422};
2254*80ee5cbfSDavid du Colombier 
lialg_compute_lpf_parameters(void)2255*80ee5cbfSDavid du Colombier static void lialg_compute_lpf_parameters(void) {
2256*80ee5cbfSDavid du Colombier 	int i;
2257*80ee5cbfSDavid du Colombier 
2258*80ee5cbfSDavid du Colombier 		for (i = LP_FILTER_WIDTH; i >= 0; i--) {
2259*80ee5cbfSDavid du Colombier //		double x = 0.04 * (i * i);
2260*80ee5cbfSDavid du Colombier //		double tmp = 100.0 * exp(x);
2261*80ee5cbfSDavid du Colombier //		int wt = floor((double)tmp);
2262*80ee5cbfSDavid du Colombier 				int wt = wtvals[i];
2263*80ee5cbfSDavid du Colombier 				lialg_lpfwts[LP_FILTER_WIDTH - i] = wt;
2264*80ee5cbfSDavid du Colombier 				lialg_lpfwts[LP_FILTER_WIDTH + i] = wt;
2265*80ee5cbfSDavid du Colombier 		}
2266*80ee5cbfSDavid du Colombier 		lialg_lpfconst = 0;
2267*80ee5cbfSDavid du Colombier 		for (i = 0; i < (2 * LP_FILTER_WIDTH + 1); i++) {
2268*80ee5cbfSDavid du Colombier 				lialg_lpfconst += lialg_lpfwts[i];
2269*80ee5cbfSDavid du Colombier 		}
2270*80ee5cbfSDavid du Colombier }
2271*80ee5cbfSDavid du Colombier 
2272*80ee5cbfSDavid du Colombier 
2273*80ee5cbfSDavid du Colombier /* Code from Joseph Hall (jnhall@sat.mot.com). */
isqrt(int n)2274*80ee5cbfSDavid du Colombier static int isqrt(int n) {
2275*80ee5cbfSDavid du Colombier 		register int i;
2276*80ee5cbfSDavid du Colombier 		register long k0, k1, nn;
2277*80ee5cbfSDavid du Colombier 
2278*80ee5cbfSDavid du Colombier 		for (nn = i = n, k0 = 2; i > 0; i >>= 2, k0 <<= 1)
2279*80ee5cbfSDavid du Colombier 				;
2280*80ee5cbfSDavid du Colombier 		nn <<= 2;
2281*80ee5cbfSDavid du Colombier 	for (;;) {
2282*80ee5cbfSDavid du Colombier 				k1 = (nn / k0 + k0) >> 1;
2283*80ee5cbfSDavid du Colombier 				if (((k0 ^ k1) & ~1) == 0)
2284*80ee5cbfSDavid du Colombier 				break;
2285*80ee5cbfSDavid du Colombier 				k0 = k1;
2286*80ee5cbfSDavid du Colombier 		}
2287*80ee5cbfSDavid du Colombier 		return (int) ((k1 + 1) >> 1);
2288*80ee5cbfSDavid du Colombier }
2289*80ee5cbfSDavid du Colombier 
2290*80ee5cbfSDavid du Colombier 
2291*80ee5cbfSDavid du Colombier /* Helper routines from Mark Hayter. */
likeatan(int tantop,int tanbot)2292*80ee5cbfSDavid du Colombier static int likeatan(int tantop, int tanbot) {
2293*80ee5cbfSDavid du Colombier 		int t;
2294*80ee5cbfSDavid du Colombier 		/* Use tan(theta)=top/bot --> order for t */
2295*80ee5cbfSDavid du Colombier 		/* t in range 0..0x40000 */
2296*80ee5cbfSDavid du Colombier 
2297*80ee5cbfSDavid du Colombier 		if ((tantop == 0) && (tanbot == 0))
2298*80ee5cbfSDavid du Colombier 				t = 0;
2299*80ee5cbfSDavid du Colombier 	else
2300*80ee5cbfSDavid du Colombier 		{
2301*80ee5cbfSDavid du Colombier 				t = (tantop << 16) / (abs(tantop) + abs(tanbot));
2302*80ee5cbfSDavid du Colombier 				if (tanbot < 0)
2303*80ee5cbfSDavid du Colombier 						t = 0x20000 - t;
2304*80ee5cbfSDavid du Colombier 				else
2305*80ee5cbfSDavid du Colombier 						if (tantop < 0) t = 0x40000 + t;
2306*80ee5cbfSDavid du Colombier 		}
2307*80ee5cbfSDavid du Colombier 		return t;
2308*80ee5cbfSDavid du Colombier }
2309*80ee5cbfSDavid du Colombier 
2310*80ee5cbfSDavid du Colombier 
quadr(int t)2311*80ee5cbfSDavid du Colombier static int quadr(int t) {
2312*80ee5cbfSDavid du Colombier 	return (8 - (((t + 0x4000) >> 15) & 7)) & 7;
2313*80ee5cbfSDavid du Colombier }
2314