Update copyright statements and change license to LGPL
[alexxy/gromacs.git] / src / ngmx / g_highway.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team,
6  * check out http://www.gromacs.org for more information.
7  * Copyright (c) 2012, by the GROMACS development team, led by
8  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
9  * others, as listed in the AUTHORS file in the top-level source
10  * directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #include <stdio.h>
43 #include <string.h>
44 #include <math.h>
45 #include "futil.h"
46 #include "macros.h"
47 #include "smalloc.h"
48 #include "xutil.h"
49 #include "copyrite.h"
50 #include "statutil.h"
51 #include "gmx_fatal.h"
52
53 /* Units are meter and second   */
54
55 typedef struct {
56   int   id;             /* Identification               */
57   float x,xold;
58   float v;              /* Position and velocity        */
59   float vwanted;        /* Wants to drive at this speed */
60   float acc;            /* Acceleration                 */
61   float brake;          /* Break                        */
62   int   lane,oldlane;   /* Currently in lane            */
63   gmx_bool  bBrake;             /* Currently on the brakes      */
64   unsigned long col;            /* Colour                       */
65   unsigned long roof;           /* Roof Colour                  */
66 } t_car;
67
68 typedef struct {
69   int   nlane;          /* Number of lanes on highway   */
70   int   metres;         /* Road length                  */
71   float dt;             /* Time step                    */
72   float min_dist;       /* Min distance cars can come   */
73   int   sleep;          /* How long to sleep in between updates */
74 } t_input;
75
76 static const char *Driving[]      = { "Start","Stop"   };
77 static const char *Fogs[]         = { "Fog",  "No Fog" };
78 enum buttons                        {  Quit,   StartStop,   Fog, NBUT };
79 static const char *but_name[NBUT] = { "Quit", "Start", "Fog"  };
80
81 typedef struct {
82   int       ncars;
83   t_car     *cars;
84   t_input   ir;
85   int       step;
86   gmx_bool      bDriving;       /* Are we driving ?             */
87   gmx_bool      bFog;           /* Is it foggy ?                */
88   t_windata main;
89   t_windata win;
90   t_windata but[NBUT];
91 } t_xhighway;
92
93 int read_input(t_x11 *x11,const char *fn,t_car **cars,t_input *ir)
94 {
95   FILE   *in;
96   int    i,n;
97   char   buf[100],b2[100];
98   t_car  *c;
99   
100   in=ffopen(fn,"r");
101   if (fscanf(in,"%d %d %f %f  %d",
102              &ir->nlane,&ir->metres,&ir->dt,&ir->min_dist,&ir->sleep) != 5)
103     gmx_fatal(FARGS,"Not enough parameters in %s line 1",fn);
104   if (fscanf(in,"%d",&n) != 1)
105     gmx_fatal(FARGS,"Not enough parameters in %s line 2",fn);
106   snew(*cars,n);
107
108   for(i=0; (i<n); i++) {
109     c=&((*cars)[i]);
110     c->id=i;
111     c->lane=0;
112     if (fscanf(in,"%f %f %f %f %f %s %s",&(c->x),&(c->v),&(c->vwanted),
113                &(c->acc),&(c->brake),buf,b2) != 7)
114       gmx_fatal(FARGS,"Not enough parameters in %s line %d",fn,3+i);
115     x11->GetNamedColor(x11,buf,&(c->col));
116     x11->GetNamedColor(x11,b2,&(c->roof));
117   }
118   ffclose(in);
119
120   return n;
121 }
122
123 static float get_dist(int ncars,t_car cars[],int which,gmx_bool bFog,
124                       int dir,int lane,int metres,int *nearest)
125 {
126   int   i,near;
127   float dist,nd;
128
129   if (dir < 0)
130     dist=-metres;
131   else
132     dist=metres;
133   near=-1;
134
135   for(i=0; (i<ncars); i++) 
136     if ((i != which) && (cars[i].oldlane == lane)){
137       nd=cars[i].xold-cars[which].xold;
138       if ((nd < 0) && (dir > 0))
139         nd+=metres;
140       else if ((nd > 0) && (dir < 0))
141         nd-=metres;
142
143       if (!bFog || (fabs(nd) < 50)) {
144         if (dir < 0) {
145           if (nd > dist) {
146             dist=nd;
147             near=i;
148           }
149         }
150         else if (dir > 0) {
151           if (nd < dist) {
152             dist=nd;
153             near=i;
154           }
155         }
156       }
157     }
158   *nearest=near;
159   return fabs(dist);
160 }
161
162 void simulate(t_x11 *x11,t_xhighway *xhw,
163               int ncars,t_car cars[],t_input *ir)
164 {
165   int   i,n_bef,n_bef1,n_beh;
166   float dist,distf,distb;
167
168   for(i=0; (i<ncars); i++) {
169     cars[i].xold=cars[i].x;
170     cars[i].oldlane=cars[i].lane;
171   }
172   for(i=0; (i<ncars); i++) {
173     cars[i].bBrake=FALSE;
174     dist=get_dist(ncars,cars,i,xhw->bFog,
175                   1,cars[i].lane,ir->metres,&n_bef);
176     if (dist < ir->min_dist) {
177       distf=get_dist(ncars,cars,i,xhw->bFog,
178                      1,cars[i].lane+1,ir->metres,&n_bef1);      
179       distb=get_dist(ncars,cars,i,xhw->bFog,
180                      -1,cars[i].lane+1,ir->metres,&n_beh);      
181       if ((cars[i].lane < ir->nlane-1) && (distb >= ir->min_dist) &&
182           (distf >= ir->min_dist))
183         cars[i].lane += 1;
184       else {
185         /* Use brakes */
186         cars[i].v -= cars[i].brake*ir->dt;
187         if (cars[i].v < 0)
188           cars[i].v = 0;
189         if (n_bef != -1) 
190           if ((cars[i].v < cars[n_bef].v) && (dist > ir->min_dist/2))
191             cars[i].v=cars[n_bef].v;
192         cars[i].bBrake=TRUE;
193       }
194     }
195     else if ((cars[i].lane > 0) && (cars[i].v == cars[i].vwanted)) {
196       /* Check if I can go right again */
197       dist=get_dist(ncars,cars,i,xhw->bFog,
198                     1,cars[i].lane-1,ir->metres,&n_bef);      
199       distb=get_dist(ncars,cars,i,xhw->bFog,
200                      -1,cars[i].lane-1,ir->metres,&n_beh);      
201       if ((dist >= ir->min_dist) && (distb >= ir->min_dist))
202         cars[i].lane -= 1;
203     }
204
205     cars[i].x += cars[i].v*ir->dt;
206     if (cars[i].x > ir->metres)
207       cars[i].x -= ir->metres;
208     if (!cars[i].bBrake && (cars[i].v < cars[i].vwanted)) {
209       cars[i].v += cars[i].acc*ir->dt;
210       if (cars[i].v > cars[i].vwanted)
211         cars[i].v = cars[i].vwanted;
212     }
213   }
214   /* Detect Crashes */
215   /* Plot */
216   usleep(xhw->ir.sleep);
217   ExposeWin(x11->disp,xhw->win.self);
218 }
219
220 static void Configure(t_xhighway *xhw)
221 {
222   Window self;
223   int   i,h,w,dh;
224   float dw;
225
226   dh=20;
227   h=xhw->main.height;
228   w=xhw->main.width;
229   dw=((float)(w-2))/NBUT-4;
230   for(i=0; (i<NBUT); i++) {
231     t_windata *wd=&(xhw->but[i]);
232
233     self=wd->self;
234     InitWin(wd,2+i*(dw+4),2,dw,dh,1,but_name[i]);
235     wd->self=self;
236   }
237   self=xhw->win.self;
238   InitWin(&xhw->win,2,dh+6,w-6,h-dh-10,1,xhw->main.text);
239   xhw->win.self=self;
240 }
241
242 static void draw_car(Display *disp,Window wd,GC gc,
243                      t_car *car,int w0,int h0)
244 {
245   const int w=30;
246   const int h=14;
247   const int wr=10;
248   const int hr=8;
249   int   j,w1,h1;
250   int   jmax,hmax;
251
252   w1=w0-w / 2;
253   h1=h0-h / 2;
254
255   /* Carosserie */
256   XSetForeground(disp,gc,car->col);
257   XFillRectangle(disp,wd,gc,w1,h1,w,h);
258
259   /* Dak */
260   XSetForeground(disp,gc,car->roof);
261   XFillRectangle(disp,wd,gc,w0-wr/2,h0-hr/2,wr,hr);
262
263   /* Achterlicht */
264   if (car->bBrake) {
265     XSetForeground(disp,gc,YELLOW);
266     jmax=5;
267     hmax=5;
268   }
269   else {
270     XSetForeground(disp,gc,LIGHTRED);
271     jmax=3;
272     hmax=3;
273   }
274   for(j=1; (j<jmax); j++) {
275     int w11=w1-1-j;
276     int h11=h1-1;
277     XDrawLine(disp,wd,gc,w11,h11,       w11,h11+hmax);
278     XDrawLine(disp,wd,gc,w11,h11+h-hmax,w11,h11+h);
279   }
280
281   /* Voorlicht */
282   XSetForeground(disp,gc,WHITE);
283   for(j=1; (j<3); j++) {
284     int w11=w1+w+j;
285     int h11=h1-1;
286     XDrawLine(disp,wd,gc,w11,h11,    w11,h11+3);
287     XDrawLine(disp,wd,gc,w11,h11+h-3,w11,h11+h);
288   }
289 }
290
291 static gmx_bool xhwCallBack(struct t_x11 *x11,XEvent *event, Window wd, void *data)
292 {
293   t_xhighway *xhw;
294   t_windata  *win;
295   float      sx;
296   int        i;
297   static     int nyy=0;
298   static     int *yy;
299
300   xhw = (t_xhighway *)data;
301   win = &(xhw->win); 
302
303   if (nyy == 0) {
304     nyy=2*xhw->ir.nlane+1;
305     snew(yy,nyy);
306   }
307   for(i=0; (i<nyy); i++) 
308     yy[i]=((float) i*win->height)/(nyy-1);
309
310   switch (event->type) {
311   case Expose: {
312     if (wd == win->self) {
313       sx=(float)win->width  / xhw->ir.metres;
314       
315       XClearWindow(x11->disp,win->self);
316       XSetForeground(x11->disp,x11->gc,WHITE);
317
318       for(i=2; (i<nyy-1); i+=2)
319         XDrawLine(x11->disp,win->self,x11->gc,0,yy[i],win->width-1,yy[i]);
320       
321       for(i=0; (i<xhw->ncars); i++) {
322         t_car *car=&(xhw->cars[i]);
323         int   w1=car->x*sx;
324         int   h1=yy[1+2*(xhw->ir.nlane-1-car->lane)];
325
326         draw_car(x11->disp,win->self,x11->gc,car,w1,h1);
327       }
328       if (xhw->bDriving)
329         simulate(x11,xhw,xhw->ncars,xhw->cars,&xhw->ir);
330     }
331     break;
332   }
333   case ConfigureNotify:
334     if (wd == xhw->main.self) {
335       xhw->main.width=event->xconfigure.width;
336       xhw->main.height=event->xconfigure.height;
337       Configure(xhw);
338       for(i=0; (i<NBUT); i++)
339         XMoveResizeWindow(x11->disp,xhw->but[i].self,
340                           xhw->but[i].x,xhw->but[i].y,
341                           xhw->but[i].width,xhw->but[i].height);
342       XMoveResizeWindow(x11->disp,win->self,
343                         win->x,win->y,win->width,win->height);
344     }
345     else if (wd == win->self) {
346       win->width=event->xconfigure.width;
347       win->height=event->xconfigure.height;
348     }
349     break;
350   case ButtonPress:
351     return TRUE;
352   default:
353     break;
354   }
355   return FALSE;
356 }
357
358 static gmx_bool butCallBack(struct t_x11 *x11,XEvent *event, Window wd, void *data)
359 {
360   XSetWindowAttributes attr;
361   t_xhighway *xhw;
362   t_windata  *win;
363   const char *label;
364   int        i;
365
366   xhw = (t_xhighway *)data;
367   for(i=0; (i<NBUT); i++) {
368     if (xhw->but[i].self == wd)
369       break;
370   }
371   if (i == NBUT) {
372     fprintf(stderr,"Incorrect window: %x in butcallback\n",(unsigned)wd);
373     return FALSE;
374   }
375   win=&(xhw->but[i]);
376
377   switch (event->type) {
378   case Expose: 
379     XClearWindow(x11->disp,win->self);
380     switch (i) {
381     case StartStop:
382       label=Driving[xhw->bDriving];
383       break;
384     case Fog:
385       label=Fogs[xhw->bFog];
386       break;
387     default:
388       label=win->text;
389     }
390     XSetForeground(x11->disp,x11->gc,WHITE);
391     TextInWin(x11,win,label,eXCenter,eYCenter);
392     break;
393   
394   case ConfigureNotify:
395     win->width=event->xconfigure.width;
396     win->height=event->xconfigure.height;
397     break;
398   case ButtonPress:
399     switch (i) {
400     case Quit:
401       return TRUE;
402     case StartStop:
403       xhw->bDriving=1-xhw->bDriving;
404       ExposeWin(x11->disp,win->self);
405       if (xhw->bDriving) 
406         ExposeWin(x11->disp,xhw->win.self);
407       break;
408     case Fog:
409       xhw->bFog=1-xhw->bFog;
410       if (xhw->bFog) 
411         attr.background_pixel=DARKGREY;
412       else
413         attr.background_pixel=BLACK;
414       XChangeWindowAttributes(x11->disp,xhw->win.self,CWBackPixel,&attr);
415       /*ExposeWin(x11->disp,win->self);*/
416       break;
417     default:
418       break;
419     }
420     break;
421   default:
422     break;
423   }
424   return FALSE;
425 }
426
427 t_xhighway *GetXHW(t_x11 *x11,const char *infile)
428 {
429   t_xhighway *xhw;
430   int        i,h,dh,w;
431   char       progname[STRLEN];
432
433   snew(xhw,1);
434   xhw->ncars=read_input(x11,infile,&(xhw->cars),&xhw->ir);
435   
436   h=xhw->ir.nlane*40;
437   dh=20;
438   w=752;
439   strncpy(progname,Program(),STRLEN-1);
440   InitWin(&xhw->main,0,0,w,h+dh+7,1,progname);
441   xhw->main.self=XCreateSimpleWindow(x11->disp,x11->root,
442                                      xhw->main.x,xhw->main.y,
443                                      xhw->main.width,xhw->main.height,
444                                      xhw->main.bwidth,WHITE,BLACK);
445   x11->RegisterCallback(x11,xhw->main.self,0,xhwCallBack,xhw);
446   x11->SetInputMask(x11,xhw->main.self,ButtonPressMask | ExposureMask |
447                     StructureNotifyMask);
448
449   Configure(xhw);
450
451   for(i=0; (i<NBUT); i++) {
452     t_windata *wd=&(xhw->but[i]);
453
454     wd->self=XCreateSimpleWindow(x11->disp,xhw->main.self,
455                                  wd->x,wd->y,
456                                  wd->width,wd->height,
457                                  wd->bwidth,WHITE,BLACK);
458     x11->RegisterCallback(x11,wd->self,xhw->main.self,
459                           butCallBack,xhw);
460     x11->SetInputMask(x11,wd->self,ButtonPressMask | ExposureMask |
461                       StructureNotifyMask);
462     
463   }
464   xhw->win.self=XCreateSimpleWindow(x11->disp,xhw->main.self,
465                                     xhw->win.x,xhw->win.y,
466                                     xhw->win.width,xhw->win.height,
467                                     xhw->win.bwidth,WHITE,BLACK);
468   x11->RegisterCallback(x11,xhw->win.self,0,xhwCallBack,xhw);
469   x11->SetInputMask(x11,xhw->win.self,ButtonPressMask | ExposureMask |
470                     StructureNotifyMask);
471
472   return xhw;
473 }
474
475 int main(int argc,char *argv[])
476 {
477   const char *desc[] = {
478     "[TT]highway[tt] is the GROMCAS highway simulator. It is an X-windows",
479     "gadget that shows a (periodic) Autobahn with a user defined",
480     "number of cars. Fog can be turned on or off to increase the",
481     "number of crashes. Nice for a background CPU-eater. A sample",
482     "input file is in [TT]$GMXDATA/top/highway.dat[tt]"
483   };
484   output_env_t oenv;
485   t_x11      *x11;
486   t_xhighway *xhw;
487   t_filenm fnm[] = {
488     { efDAT, "-f", "highway", ffREAD }
489   };
490 #define NFILE asize(fnm)
491
492   CopyRight(stderr,argv[0]);
493   parse_common_args(&argc,argv,0,NFILE,fnm,
494                     0,NULL,asize(desc),desc,0,NULL,&oenv);
495   
496   if ((x11=GetX11(&argc,argv))==NULL) {
497     fprintf(stderr,"Can't connect to X Server.\n"
498             "Check your DISPLAY environment variable\n");
499     exit(1);
500   }
501   xhw=GetXHW(x11,opt2fn("-f",NFILE,fnm));
502   
503   XMapWindow(x11->disp,xhw->main.self);
504   XMapSubwindows(x11->disp,xhw->main.self);
505   x11->MainLoop(x11);
506   x11->CleanUp(x11);
507
508   thanx(stderr);
509   
510   return 0;
511 }