chiark / gitweb /
undo broken deletion
[trains.git] / layout / subseg2display.c
1 /*
2  * usage:
3  *   subseg2display encoding.ppm [status-info ...]
4  *
5  * status-info args are:
6  *   <idchar> <alpha> <red> <green> <blue>
7  *   .. <alpha> <red> <green> <blue>
8  *   0. <alpha> <red> <green> <blue>
9  *   <idchar><subseg>
10  *
11  * `..' group is for unspecified segments.
12  * `0.' group is for background.
13  *
14  * <subseg> is
15  *   <segnum>               for fixed track
16  *   <segnum>.<movfeatpos>  for part of a moveable feature
17  * where numbers are in strtoul base=0 format, movfeatpos is the
18  * moveable feature and position as in segcmapassign and
19  *
20  * Resulting pixel is
21  *                                                   [ red   ]
22  *    (|alpha| * DIRECTION_COLOUR) + ((1-|alpha|) *  [ green ] )
23  *                                                   [ blue  ]
24  *
25  * where DIRECTION_COLOUR corresponds to the reverse of the stated
26  * direction iff alpha<0.
27  *
28  * All of alpha, red, green, blue are out of 1000.
29  */
30
31
32 #include <publib.h>
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <math.h>
40
41 #include <ppm.h>
42
43 #include "segcmap.h"
44
45 /*---------- lookup data structure ----------*/
46
47 struct result {
48   double alpha, rgb[3];
49 };
50
51 typedef unsigned char id;
52
53 struct movperseg {
54   id *lookup;
55   int lookup_a;
56 };
57
58 #define NCHARS 256
59
60 static struct result idinfo[NCHARS];
61 static struct result background;
62
63 static id *fixed;
64 static int fixed_a;
65
66 static struct movperseg *moveable;
67 static int moveable_a;
68
69 /*---------- arg parsing helper routines ----------*/
70
71 static char **argv;
72
73 static void badusage(void) {
74   fprintf(stderr,"bad usage\n");
75   exit(12);
76 }
77
78 static const char *nextarg(void) {
79   if (!*argv) badusage();
80   return *argv++;
81 }
82
83 static double channelarg(void) {
84   const char *arg= nextarg();
85   char *ep;
86   double v;
87
88   errno= 0;
89   v= strtod(arg,&ep);
90   if (*ep) badusage();
91   if (errno) badusage();
92   if (!finite(v)) badusage();
93
94   return v / 1000.0;
95 }
96
97 /*---------- specification args ----------*/
98
99 static void resultargs(struct result *r) {
100   int i;
101
102   r->alpha= channelarg();
103   for (i=0; i<3; i++)
104     r->rgb[i]= channelarg();
105 }
106   
107 static void idgroupargs(id ix) {
108   resultargs(&idinfo[ix]);
109 }
110
111 static void ensure_somethings(void **ary, int *allocd, int want, size_t item,
112                               void zero(void *data, int count)) {
113   int new_allocd;
114   
115   if (*allocd > want) return;
116   new_allocd= (want+1)*2;
117   *ary= xrealloc(*ary, new_allocd * item);
118   zero((char*)*ary + item * *allocd, new_allocd - *allocd);
119   *allocd= new_allocd;
120 }
121
122 static void zero_ids(void *data, int count) {
123   memset(data,0, count*sizeof(id));
124 }
125
126 static void zero_mps(void *data_v, int count) {
127   struct movperseg *data= data_v;
128   for (count /= sizeof(*data);
129        count > 0;
130        count--, data++) {
131     data->lookup= 0;
132     data->lookup_a= 0;
133   }
134 }
135
136 #define ENSURE_THINGS(sfx,type)                                         \
137   static void ensure_##sfx(type **ary_io, int *allocd, int want) {      \
138     void *ary= *ary_io;                                                 \
139     ensure_somethings(&ary,allocd,want,sizeof(**ary_io),zero_##sfx);    \
140     *ary_io= ary;                                                       \
141   }                                                                     \
142   struct eat_semicolon
143
144 ENSURE_THINGS(ids,id);
145 ENSURE_THINGS(mps,struct movperseg);
146
147 static void subsegarg(id ix, const char *arg) {
148   struct result *r;
149   struct movperseg *mps;
150   unsigned long segnum, movfeatpos;
151   char *ep;
152   
153   r= &idinfo[ix];
154
155   errno= 0;
156   segnum= strtoul(arg,&ep,0);
157   if (errno) badusage();
158   if (segnum > SEGNUM_MAX) badusage();
159
160   if (!*ep) {
161     ensure_ids(&fixed,&fixed_a,segnum);
162     fixed[segnum]= ix;
163     return;
164   }
165
166   if (*ep != '.') badusage();
167
168   errno= 0;
169   movfeatpos= strtoul(ep+1,&ep,0);
170   if (*ep || errno) badusage();
171   if (movfeatpos > MOVFEATPOS_MAX) badusage();
172
173   ensure_mps(&moveable,&moveable_a,segnum);
174   mps= &moveable[segnum];
175   ensure_ids(&mps->lookup,&mps->lookup_a, movfeatpos ^ MOVFEATPOS_MAX);
176   mps->lookup[movfeatpos ^ MOVFEATPOS_MAX]= ix;
177 }  
178
179 /*---------- actual image processing ----------*/
180
181 static int cols, rows, informat, row, col;
182 static pixval maxval;
183 static FILE *encodingfile;
184
185 static void badformat(const char *m) {
186   fprintf(stderr,"bad format (row=%d,col=%d): %s\n", row,col, m);
187   exit(12);
188 }
189
190 static void angle_to_colour(double result[3],
191                             int angle_i /* [0..ANGLE_MAX> */) {
192   int s;
193   double angle, f, u, d, U, D;
194
195   angle= angle_i / (double)NANGLES * 6.0;
196   s= floor(angle);
197   f= angle - s;
198   
199   u= f * 0.5;
200   U= u + 0.5;
201   d= 0.5 - u;
202   D= d + 0.5; 
203
204   switch (s) {
205 #define R(r,g,b) if(1){ result[0]=r; result[1]=g*0.9; result[2]=b; break; }else
206   case 0: R( D, U, 0 );
207   case 1: R( d, 1, u );
208   case 2: R( 0, D, U );
209   case 3: R( u, d, 1 );
210   case 4: R( U, 0, D );
211   case 5: R( 1, u, d );
212 #undef R
213   }
214 }
215
216 static double anglemap[NANGLES][3];
217
218 static void angle_to_colour_init(void) {
219   int i;
220   
221   for (i=0; i<NANGLES; i++)
222     angle_to_colour(anglemap[i],i);
223 }
224
225 static void process(void) {
226   int i;
227   unsigned char rgbob[3];
228   
229   ppm_readppminit(encodingfile, &cols, &rows, &maxval, &informat);
230   if (maxval != 255) badformat("wrong maxval");
231   if (informat != RPPM_FORMAT) badformat("wrong format");
232   ppm_writeppminit(stdout, cols, rows, 255, 0);
233
234   for (i=0; i<3; i++)
235     rgbob[i]= background.rgb[i] * 255.0;
236
237   angle_to_colour_init();
238
239   for (row=0; row<rows; row++)
240     for (col=0; col<cols; col++) {
241       unsigned char rgbi[3], rgbo[3];
242       unsigned long datum;
243
244       if (fread(rgbi,1,3,encodingfile)!=3) {
245         if (ferror(encodingfile)) { perror("reading"); exit(12); }
246         else badformat("truncated file");
247       }
248
249       datum= ARY2DATUM(rgbi);
250       
251       if (datum == BACKGROUND) {
252
253         if (fwrite(rgbob,1,3,stdout)!=3) { perror("filling"); exit(12); }
254         
255       } else {
256         int segnum, movfeatpos, movfeatposix, ix, angle;
257         double *rgbdirn, alpha;
258         struct result *r;
259
260         if (datum & RESERVED_MASK) badformat("reserved bits set");
261         angle= DATUM2(ANGLE,datum);
262
263         segnum= DATUM2(SEGNUM,datum);
264         movfeatpos= DATUM2(MOVFEATPOS,datum);
265         movfeatposix= movfeatpos ^ MOVFEATPOS_MAX;
266
267         ix= !movfeatpos ?
268           (segnum < fixed_a ? fixed[segnum] : 0) :
269           (segnum < moveable_a && movfeatposix < moveable[segnum].lookup_a
270            ? moveable[segnum].lookup[movfeatposix] : 0);
271
272         r= &idinfo[ix];
273
274         rgbdirn= anglemap[ angle ^ (r->alpha < 0 ? ANGLE_TOPBIT : 0) ];
275         alpha= fabs(r->alpha);
276
277         for (i=0; i<3; i++) {
278           double v;
279           v= alpha * rgbdirn[i] + (1.0 - alpha) * r->rgb[i];
280           rgbo[i]= v * 255.0;
281         }
282
283         if (fwrite(rgbo,1,3,stdout)!=3) { perror("writing"); exit(12); }
284       }
285     }
286 }
287
288 /*---------- main program ----------*/
289
290 int main(int argc_spec, char **argv_spec) {
291   const char *arg, *encodingfilename;
292
293   ppm_init(&argc_spec,argv_spec);
294   
295   argv= argv_spec;
296   nextarg();
297   encodingfilename= nextarg();
298   while (*argv) {
299     arg= nextarg();
300     if (!*arg) badusage();
301     if (!arg[1]) idgroupargs(arg[0]);
302     else if (!strcmp(arg,"..")) idgroupargs(0);
303     else if (!strcmp(arg,"0.")) resultargs(&background);
304     else subsegarg(arg[0],arg+1);
305   }
306
307   encodingfile= fopen(encodingfilename, "rb");
308   if (!encodingfile) { perror(encodingfilename); exit(8); }
309   process();
310   return 0;
311 }