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