xref: /inferno-os/appl/lib/strokes/readstrokes.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Readstrokes;
2
3#
4# read structures from stroke classifier files
5#
6
7include "sys.m";
8	sys: Sys;
9
10include "bufio.m";
11	bufio: Bufio;
12	Iobuf: import bufio;
13
14include "strokes.m";
15	strokes: Strokes;
16	Classifier, Penpoint, Stroke, Region: import strokes;
17	buildstrokes: Buildstrokes;
18
19init(s: Strokes)
20{
21	sys = load Sys Sys->PATH;
22	bufio = load Bufio Bufio->PATH;
23	strokes = s;
24}
25
26getint(fp: ref Iobuf): (int, int)
27{
28	while((c := fp.getc()) == ' ' || c == '\t' || c == '\n')
29		;
30	if(c < 0)
31		return (c, 0);
32	sign := 1;
33	if(c == '-')
34		sign = -1;
35	else if(c == '+')
36		;
37	else
38		fp.ungetc();
39	rc := 0;
40	n := 0;
41	while((c = fp.getc()) >= '0' && c <= '9'){
42		n = n*10 + (c-'0');
43		rc = 1;
44	}
45	return (rc, n*sign);
46}
47
48getstr(fp: ref Iobuf): (int, string)
49{
50	while((c := fp.getc()) == ' ' || c == '\t' || c == '\n')
51		;
52	if(c < 0)
53		return (c, nil);
54	fp.ungetc();
55	s := "";
56	while((c = fp.getc()) != ' ' && c != '\t' && c != '\n')
57		s[len s] = c;
58	return (0, s);
59}
60
61getpoint(fp: ref Iobuf): (int, Penpoint)
62{
63	(okx, x) := getint(fp);
64	(oky, y) := getint(fp);
65	if(okx <= 0 || oky <= 0)
66		return (-1, (0,0,0));
67	return (0, (x,y,0));
68}
69
70getpoints(fp: ref Iobuf): ref Stroke
71{
72	(ok, npts) := getint(fp);
73	if(ok <= 0 || npts < 0 || npts > 4000)
74		return nil;
75	pts := array[npts] of Penpoint;
76	for(i := 0; i < npts; i++){
77		(ok, pts[i]) = getpoint(fp);
78		if(ok < 0)
79			return nil;
80	}
81	return ref Stroke(npts, pts, 0, 0);
82}
83
84read_classifier_points(fp: ref Iobuf, nclass: int): (int, array of string, array of list of ref Stroke)
85{
86	names := array[nclass] of string;
87	examples := array[nclass] of list of ref Stroke;
88	for(k := 0; k < nclass; k++){
89		# read class name and number of examples
90		(ok, nex) := getint(fp);
91		if(ok <= 0)
92			return (-1, nil, nil);
93		(ok, names[k]) = getstr(fp);
94		if(ok < 0)
95			return (ok, nil, nil);
96
97		# read examples
98		for(i := 0; i < nex; i++){
99			pts := getpoints(fp);
100			if(pts == nil)
101				return (-1, nil, nil);
102			examples[k] = pts :: examples[k];
103		}
104	}
105	return (0, names, examples);
106}
107
108#
109# read a classifier, using its digest if that exists
110#
111read_classifier(file: string, build: int, needex: int): (string, ref Classifier)
112{
113	rc := ref Classifier;
114	l := len file;
115	digestfile: string;
116	if(l >= 4 && file[l-4:]==".clx")
117		digestfile = file;
118	else if(!needex && l >= 3 && file[l-3:]==".cl")
119		digestfile = file[0:l-3]+".clx";	# try the digest file first
120	err: string;
121	if(digestfile != nil){
122		fd := sys->open(digestfile, Sys->OREAD);
123		if(fd != nil){
124			(err, rc.cnames, rc.dompts) = read_digest(fd);
125			rc.nclasses = len rc.cnames;
126			if(rc.cnames == nil)
127				err = "empty digest file";
128			if(err == nil)
129				return (nil, rc);
130		}else
131			err = sys->sprint("%r");
132		if(!build)
133			return (sys->sprint("digest file: %s", err), nil);
134	}
135
136	if(buildstrokes == nil){
137		buildstrokes = load Buildstrokes Buildstrokes->PATH;
138		if(buildstrokes == nil)
139			return (sys->sprint("module %s: %r", Buildstrokes->PATH), nil);
140		buildstrokes->init(strokes);
141	}
142
143	fd := sys->open(file, Sys->OREAD);
144	if(fd == nil)
145		return (sys->sprint("%r"), nil);
146	(emsg, cnames, examples) := read_examples(fd);
147	if(emsg != nil)
148		return (emsg, nil);
149	rc.nclasses = len cnames;
150	(err, rc.canonex, rc.dompts) = buildstrokes->canonical_example(rc.nclasses, cnames, examples);
151	if(err != nil)
152		return ("failed to calculate canonical examples", nil);
153	rc.cnames = cnames;
154	if(needex)
155		rc.examples = examples;
156
157	return (nil, rc);
158}
159
160read_examples(fd: ref Sys->FD): (string, array of string, array of list of ref Strokes->Stroke)
161{
162	fp := bufio->fopen(fd, Bufio->OREAD);
163	(ok, nclasses) := getint(fp);
164	if(ok <= 0)
165		return ("missing number of classes", nil, nil);
166	(okc, cnames, examples) := read_classifier_points(fp, nclasses);
167	if(okc < 0)
168		return ("couldn't read examples", nil, nil);
169	return (nil, cnames, examples);
170}
171
172#
173# attempt to read the digest of a classifier,
174# and return its contents if successful;
175# return a diagnostic if not
176#
177read_digest(fd: ref Sys->FD): (string, array of string, array of ref Stroke)
178{
179	#  Read-in the name and dominant points for each class.
180	fp := bufio->fopen(fd, Bufio->OREAD);
181	cnames := array[32] of string;
182	dompts := array[32] of ref Stroke;
183	for(nclasses := 0;; nclasses++){
184		if(nclasses >= len cnames){
185			a := array[nclasses+32] of string;
186			a[0:] = cnames;
187			cnames = a;
188			b := array[nclasses+32] of ref Stroke;
189			b[0:] = dompts;
190			dompts = b;
191		}
192		(okn, class) := getstr(fp);
193		if(okn == Bufio->EOF)
194			break;
195		if(class == nil)
196			return ("expected class name", nil, nil);
197		cnames[nclasses] = class;
198		dpts := getpoints(fp);
199		if(dpts == nil)
200			return ("bad points list", nil, nil);
201		strokes->compute_chain_code(dpts);
202		dompts[nclasses] = dpts;
203	}
204	return (nil, cnames[0:nclasses], dompts[0:nclasses]);
205}
206