1 /* -copyright-
2 #-#
3 #-# xsnow: let it snow on your desktop
4 #-# Copyright (C) 1984,1988,1990,1993-1995,2000-2001 Rick Jansen
5 #-# 	      2019,2020,2021 Willem Vermin
6 #-#
7 #-# This program is free software: you can redistribute it and/or modify
8 #-# it under the terms of the GNU General Public License as published by
9 #-# the Free Software Foundation, either version 3 of the License, or
10 #-# (at your option) any later version.
11 #-#
12 #-# This program is distributed in the hope that it will be useful,
13 #-# but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #-# GNU General Public License for more details.
16 #-#
17 #-# You should have received a copy of the GNU General Public License
18 #-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #-#
20 */
21 //
22 #include <gtk/gtk.h>
23 #include <stdlib.h>
24 #include <math.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <assert.h>
28 #include "Santa.h"
29 #include "birds.h"
30 #include "clocks.h"
31 #include "debug.h"
32 #include "doitb.h"
33 #include "flags.h"
34 #include "birdglobals.h"
35 #include "hashtable.h"
36 #include "ixpm.h"
37 #include "kdtree.h"
38 #include "mainstub.h"
39 #include "pixmaps.h"
40 #include "ui.h"
41 #include "utils.h"
42 #include "windows.h"
43 
44 
45 
46 #define NWINGS 8
47 #define NBIRDPIXBUFS (3*NWINGS)
48 
49 
50 #define LEAVE_IF_INACTIVE\
51    if (!Flags.ShowBirds || blobals.freeze || !WorkspaceActive()) return TRUE
52 
53 /* Surface to store current scribbles */
54 
55 static GdkPixbuf       *bird_pixbufs[NBIRDPIXBUFS];
56 static cairo_surface_t *attrsurface = NULL;
57 
58 static int Nbirds;  // is copied from Flags.Nbirds in init_birds. We cannot have that
59 //                  // Nbirds is changed outside init_birds
60 
61 
62 typedef struct _Birdtype
63 {
64    float x,y,z;        // position, meters
65    // x: horizontal
66    // y: in/out screen
67    // z: vertical
68    float sx,sy,sz;     // velocity, m/sec
69    int ix,iy,iz,iw,ih; // pixels
70    // ix .. in pixels, used to store previous screen parameters
71    int wingstate, orient;
72    int drawable;       // is in drawable range
73    // comes in handy at erasing a bird:
74    int prevx, prevy, prevw, prevh, prevdrawable;
75 } BirdType;
76 
77 struct _blobals blobals;
78 
79 
80 static void     attrbird2surface(void);
81 static void     birds_init_color(void);
82 static void     birds_set_attraction_point_relative(float x, float y, float z);
83 static void     birds_set_scale(void);
84 static void     birds_set_speed(void);
85 static void     clear_flags(void);
86 static int      do_change_attr(void *);
87 static int      do_update_pos_birds(void *);
88 static int      do_wings(void *);
89 static int      do_update_speed_birds(void *);
90 static int      do_main_window(void *);
91 static void     init_bird_pixbufs(const char *color);
92 static void     main_window(void);
93 static void     normalize_speed(BirdType *bird, float speed);
94 static void     prefxyz(BirdType *bird, float d, float e, float x, float y, float z, float *prefx, float *prefy, float *prefz);
95 static void     r2i(BirdType *bird);
96 static void     i2r(BirdType *bird);
97 static int      attrbird_erase(int force);
98 static void     init_birds(int start);
99 
100 
101 static float time_update_pos_birds     = 0.01;
102 static float time_update_speed_birds   = 0.20;
103 static float time_wings                = 0.10;
104 
105 static struct kdtree *kd = NULL;
106 
107 static BirdType *birds = NULL;
108 static BirdType attrbird;
109 
110 
birds_ui()111 void birds_ui()
112 {
113    UIDO(ShowBirds         , birds_erase(1); attrbird_erase(1); );
114    UIDO(BirdsOnly         , ClearScreen();                     );
115    UIDO(Neighbours        ,                                    );
116    UIDO(Anarchy           ,                                    );
117    UIDO(PrefDistance      ,                                    );
118    UIDO(ViewingDistance   , attrbird2surface();                );
119    UIDO(BirdsSpeed        , birds_set_speed();                 );
120    UIDO(AttrFactor        ,                                    );
121    UIDO(DisWeight         ,                                    );
122    UIDO(FollowWeight      ,                                    );
123    UIDO(BirdsScale        , birds_set_scale();                 );
124    UIDO(ShowAttrPoint     , attrbird_erase(1);                 );
125    UIDOS(BirdsColor       ,
126 	 birds_init_color();
127 	 ClearScreen(););
128    UIDO(Nbirds            ,
129 	 int start = OldFlags.Nbirds;
130 	 if (Flags.Nbirds <= 0)
131 	 Flags.Nbirds = 1;
132 	 if (Flags.Nbirds > NBIRDS_MAX)
133 	 Flags.Nbirds = NBIRDS_MAX;
134 	 init_birds(start););
135    UIDO(FollowSanta,
136 	 if (!Flags.FollowSanta)
137 	 birds_set_attraction_point_relative(0.5, 0.5, 0.5););
138 
139    if(Flags.BirdsRestart)
140    {
141       Flags.BirdsRestart = 0;
142       init_birds(0);
143       birds_set_attraction_point_relative(0.5, 0.5, 0.5);
144       P("Changes: %d\n",Flags.Changes);
145    }
146 }
147 
normalize_speed(BirdType * bird,float speed)148 static void normalize_speed(BirdType *bird, float speed)
149 {
150    float v2 = sq3(bird->sx, bird->sy, bird->sz);
151    if (fabsf(v2) < 1.0e-10)
152       v2 = blobals.meanspeed;
153    float a = speed/sqrtf(v2);
154    bird->sx *= a;
155    bird->sy *= a;
156    bird->sz *= a;
157 }
158 
159 
scale(float y)160 static float scale(float y)
161 {
162    float s;
163    if (y != 0)
164    {
165       s = 0.005*(100-Flags.ViewingDistance)*blobals.maxy/y;
166    }
167    else
168       s = 1.0e6;
169    P("scale:%f\n",s);
170    return s;
171 }
172 
173 //#define CO_REAL
174 // given bird, compute screen coordinates ix and iz, and depth iy
r2i(BirdType * bird)175 void r2i(BirdType *bird)
176 {
177    if(bird->y > Flags.ViewingDistance/8)
178    {
179       bird->drawable = 1;
180       float f = scale(bird->y);
181       P("%f %d %f\n",blobals.maxy,Flags.ViewingDistance,f);
182 #ifdef CO_REAL
183       // classical camera obscura, inverted image:
184       float x = f*(blobals.xc-bird->x) + blobals.xc;
185       float z = f*(blobals.zc-bird->z) + blobals.zc;
186 #else
187       // alternative camera obscura, not inverted image:
188       float x = f*(bird->x-blobals.xc) + blobals.xc;
189       float z = f*(bird->z-blobals.zc) + blobals.zc;
190 #endif
191       bird->ix = blobals.ax*x;
192       bird->iy = blobals.ay*bird->y;
193       bird->iz = blobals.az*z;
194    }
195    else
196    {
197       bird->drawable = 0;
198    }
199    P("r2i %d %d\n",counter++,bird->drawable);
200 }
201 
202 // given bird, x,iy, z given ix, iz and y
i2r(BirdType * bird)203 static void i2r(BirdType *bird)
204 {
205    float f  = scale(bird->y);
206 #ifdef CO_REAL
207    bird->x  = (blobals.ax*blobals.xc - bird->ix)/(blobals.ax*f) + blobals.xc;
208    bird->z  = (blobals.az*blobals.zc - bird->iz)/(blobals.az*f) + blobals.zc;
209 #else
210    bird->x  = (bird->ix - blobals.ax*blobals.xc)/(blobals.ax*f) + blobals.xc;
211    bird->z  = (bird->iz - blobals.az*blobals.zc)/(blobals.az*f) + blobals.zc;
212 #endif
213    bird->iy = blobals.ay*bird->y;
214 }
215 
216 // given:
217 // bird
218 // distance d
219 // optimal distance e
220 // coordinates of other bird x,y,z
221 // compute optimal coordinates for bird: prefx, prefy, prefz
prefxyz(BirdType * bird,float d,float e,float x,float y,float z,float * prefx,float * prefy,float * prefz)222 void prefxyz(BirdType *bird, float d, float e, float x, float y, float z, float *prefx, float *prefy, float *prefz)
223 {
224    *prefx = e*(bird->x-x)/d + x;
225    *prefy = e*(bird->y-y)/d + y;
226    *prefz = e*(bird->z-z)/d + z;
227    P("%f %f - %f %f %f, %f %f %f, %f %f %f\n",
228 	 d,e,
229 	 bird->x, bird->y, bird->z,
230 	 x,y,z,
231 	 *prefx, *prefy, *prefz
232     );
233 }
234 
235 // create a attraction point surface in attrsurface
236 // is called when user changes drawing scale
237 // and when attraction point is changed
attrbird2surface()238 void attrbird2surface()
239 {
240    if(attrsurface)
241       cairo_surface_destroy(attrsurface);
242    r2i(&attrbird);
243    float f =
244       scale(attrbird.y)*4.0e-6*blobals.bird_scale*Flags.BirdsScale*blobals.maxix;
245    P("attrbird2surface %d %f\n",counter++,f);
246    attrsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,2*f+2,2*f+2);
247    cairo_t *cr = cairo_create(attrsurface);
248    cairo_set_source_rgba(cr,0.914,0.592,0.04,0.6);
249    cairo_arc(cr,f+1,f+1,f,0,2*M_PI);
250    cairo_fill(cr);
251    cairo_destroy(cr);
252 }
253 
birds_set_scale()254 void birds_set_scale()
255 {
256    attrbird2surface();
257 }
258 
do_update_speed_birds(void * d)259 int do_update_speed_birds(void *d)
260 {
261    if (Flags.Done)
262       return FALSE;
263    LEAVE_IF_INACTIVE;
264    P("do_update_speed_birds %d\n",counter);
265 
266    kd_free(kd);
267    kd = kd_create(3);
268 
269    int i;
270 
271    for (i=0; i<Nbirds; i++)
272    {
273       BirdType *bird = &birds[i];
274       kd_insert3f(kd, bird->x, bird->y, bird->z, bird);
275    }
276 
277    int sumnum         = 0;
278    float summeandist  = 0;
279    for (i=0; i<Nbirds; i++)
280    {
281       if (drand48() < Flags.Anarchy*0.01)
282 	 continue;
283       BirdType *bird = &birds[i];
284 
285       struct kdres *result = kd_nearest_range3f(kd,bird->x,bird->y,bird->z,blobals.range);
286       float sumsx    = 0;
287       float sumsy    = 0;
288       float sumsz    = 0;
289       float sumprefx = 0;
290       float sumprefy = 0;
291       float sumprefz = 0;
292       float sumdist  = 0;
293       int   num      = 0;
294       while (!kd_res_end(result))
295       {
296 	 float x,y,z;
297 	 BirdType *b = (BirdType *)kd_res_item3f(result, &x, &y, &z);
298 	 kd_res_next(result);
299 	 if (bird == b)
300 	    continue;
301 	 num++;
302 
303 	 // sum the speeds of neighbour birds:
304 
305 	 sumsx += b->sx;
306 	 sumsy += b->sy;
307 	 sumsz += b->sz;
308 
309 	 float dist = sqrtf((bird->x-x)*(bird->x-x)+
310 	       (bird->y-y)*(bird->y-y)+(bird->z-z)*(bird->z-z));
311 
312 	 float prefx=0, prefy=0, prefz=0;
313 	 P("prefxyz %f\n",dist);
314 	 if (dist > 1e-6)
315 	    prefxyz(bird, dist, Flags.PrefDistance, x, y, z, &prefx, &prefy, &prefz);
316 	 sumprefx += prefx;
317 	 sumprefy += prefy;
318 	 sumprefz += prefz;
319 	 sumdist  += dist;
320 
321       }
322       kd_res_free(result);
323 
324       // meanprefx,y,z: mean optimal coordinates with respect to other birds
325       float meanprefx, meanprefy, meanprefz, meandist;
326       P("num: %d\n",num);
327       if (num > 0)
328       {
329 	 meanprefx     = sumprefx / num;
330 	 meanprefy     = sumprefy / num;
331 	 meanprefz     = sumprefz / num;
332 	 meandist      = sumdist  / num;
333 	 summeandist  += meandist;
334 	 P("prefx - x ... %f %f %f %f\n",meanprefx - bird->x, meanprefy - bird->y, meanprefz - bird->z, meandist);
335       }
336       sumnum +=num;
337       // adjust speed to other birds, p is weight for own speed
338       if (num > 0)
339       {
340 	 int p = (100-Flags.FollowWeight)*0.1;
341 	 bird->sx = (sumsx + p*bird->sx)/(p+1+num);
342 	 bird->sy = (sumsy + p*bird->sy)/(p+1+num);
343 	 bird->sz = (sumsz + p*bird->sz)/(p+1+num);
344       }
345       // adjust speed to obtain desired distance to other birds
346       if (num > 0)
347       {
348 	 float q = Flags.DisWeight*0.4;
349 	 bird->sx += q*(meanprefx - bird->x);
350 	 bird->sy += q*(meanprefy - bird->y);
351 	 bird->sz += q*(meanprefz - bird->z);
352 	 P("%d %f %f %f, %f %f %f\n",i,meanprefx,meanprefy,meanprefz,bird->x,bird->y,bird->z);
353       }
354 
355 
356       // attraction of center:
357 
358       float dx = attrbird.x - bird->x;
359       float dy = attrbird.y - bird->y;
360       float dz = attrbird.z - bird->z;
361 
362       float f = Flags.AttrFactor*0.01f*0.05f;
363 
364       bird->sx += f*dx;
365       bird->sy += f*dy;
366       bird->sz += f*dz;
367 
368       // limit vertical speed
369 
370       const float phs = 0.8;
371       float hs = sqrtf(sq2(bird->sx, bird->sy));
372       if (fabs(bird->sz) > phs*hs)
373 	 bird->sz = fsignf(bird->sz)*phs*hs;
374 
375       // randomize:
376       {
377 	 const float p  = 0.4;  //  0<=p<=1 the higher the more random
378 	 bird->sx += bird->sx*p*drand48();
379 	 bird->sy += bird->sy*p*drand48();
380 	 bird->sz += bird->sz*p*drand48();
381       }
382 
383       normalize_speed(bird,blobals.meanspeed*(0.9+drand48()*0.2));
384    }
385    float meannum = (float)sumnum/(float)Nbirds;
386    blobals.mean_distance = summeandist/Nbirds;
387    P("meannum %f %f\n",meannum,blobals.range);
388 
389    if (meannum < Flags.Neighbours)
390    {
391       if (blobals.range < 0.1)
392 	 blobals.range = 0.1;
393       if (meannum < Nbirds-1)
394 	 blobals.range *=1.1;
395       if (blobals.range > blobals.maxrange)
396 	 blobals.range /=1.1;
397    }
398    else
399       blobals.range /=1.1;
400 
401    return TRUE;
402    (void)d;
403 }
404 
do_update_pos_birds(void * d)405 int do_update_pos_birds(void *d)
406 {
407    if (Flags.Done)
408       return FALSE;
409    LEAVE_IF_INACTIVE;
410    P("do_update_pos_birds %d %d\n",Nbirds,counter++);
411    double dt;
412    dt = time_update_pos_birds;
413 
414    P("%f\n",dt);
415 
416    int i;
417    for (i=0; i<Nbirds; i++)
418    {
419       BirdType *bird = &birds[i];
420 
421       bird->x += dt*bird->sx;
422       bird->y += dt*bird->sy;
423       bird->z += dt*bird->sz;
424    }
425    return TRUE;
426    (void)d;
427 }
428 
birds_draw(cairo_t * cr)429 int birds_draw(cairo_t *cr)
430 {
431    P("birds_draw %d\n",counter++);
432    LEAVE_IF_INACTIVE;
433    P("drawing birds %d\n",counter++);
434 
435    int before;
436    int i;
437 
438    for (before=0; before<2; before++)
439    {
440       if(before && Flags.FollowSanta && !Flags.BirdsOnly)
441       {
442 	 static int prevSantasize = -1;
443 	 Santa_draw(cr);
444 	 attrbird.ix = global.SantaX+global.SantaWidth/2;
445 	 attrbird.iz = global.SantaY+global.SantaHeight/2;
446 	 switch(Flags.SantaSize)
447 	 {
448 	    case 0:  attrbird.y = blobals.maxy*1.5; break;
449 	    case 1:  attrbird.y = blobals.maxy*1.0; break;
450 	    default: attrbird.y = blobals.maxy*0.5; break;
451 	 }
452 	 i2r(&attrbird);
453 	 P("santasize: %d %d %d %f\n",prevSantasize,Flags.SantaSize,Flags.ViewingDistance,scale(attrbird.y));
454 	 if (prevSantasize != Flags.SantaSize)
455 	 {
456 	    P("iy: %d %d\n",prevSantasize,Flags.SantaSize);
457 	    prevSantasize = Flags.SantaSize;
458 	    attrbird2surface();
459 	 }
460       }
461       if(before && Flags.ShowAttrPoint)
462       {
463 	 r2i(&attrbird);
464 	 P("attrbird %f %f %f %d %d %d\n",attrbird.x,attrbird.y,attrbird.z,attrbird.ix,attrbird.iy,attrbird.iz);
465 	 int mx = cairo_image_surface_get_width(attrsurface);
466 	 int mz = cairo_image_surface_get_height(attrsurface);
467 	 P("mx: %d mz: %d\n",mx,mz);
468 	 cairo_set_source_surface (cr, attrsurface, attrbird.ix-mx/2, attrbird.iz-mz/2);
469 	 my_cairo_paint_with_alpha(cr,ALPHA);
470 	 attrbird.prevx = attrbird.ix-mx/2;
471 	 attrbird.prevy = attrbird.iz-mz/2;
472 	 attrbird.prevw = mx;
473 	 attrbird.prevh = mz;
474 	 //#define TESTBIRDS
475 #ifdef TESTBIRDS
476 	 {
477 	    // show the three types of birds flying in the centre
478 	    // useful at creating bird xpm's
479 	    static BirdType testbird;
480 	    testbird = birds[0];
481 	    testbird.x = attrbird.x;
482 	    testbird.y = attrbird.y;
483 	    testbird.z = attrbird.z;
484 	    int i;
485 	    int centerbird = 0;
486 	    for (i=0; i<3; i++)
487 	    {
488 	       GdkPixbuf *bird_pixbuf = bird_pixbufs[testbird.wingstate+i*NWINGS];
489 	       int iw = 400;
490 	       int ih = (float)iw*gdk_pixbuf_get_height(bird_pixbuf)/
491 		  (float)gdk_pixbuf_get_width(bird_pixbuf);
492 	       GdkPixbuf       *pixbuf = 0;
493 	       const GdkInterpType interpolation = GDK_INTERP_HYPER;
494 	       if(iw < 1)iw = 1;
495 	       if(ih < 1)ih = 1;
496 	       pixbuf = gdk_pixbuf_scale_simple(bird_pixbuf,iw,ih,interpolation);
497 	       cairo_surface_t *surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL);
498 	       r2i(&testbird);
499 	       cairo_set_source_surface (cr, surface, testbird.ix +(i-centerbird)*(iw+20), testbird.iz);
500 	       my_cairo_paint_with_alpha(cr,ALPHA);
501 	       g_clear_object(&pixbuf);
502 	       cairo_surface_destroy(surface);
503 	    }
504 	 }
505 #endif
506       }
507       for (i=0; i<Nbirds; i++)
508       {
509 	 BirdType *bird = &birds[i];
510 
511 	 if (before)   //before attraction point
512 	 {
513 	    if (bird->y > attrbird.y)
514 	       continue;
515 	 }
516 	 else         // behind attraction point
517 	 {
518 	    if (bird->y <= attrbird.y)
519 	       continue;
520 	 }
521 	 // draw:
522 
523 	 r2i(bird);
524 	 P("%d %f %f %f %d %d %d %d\n",i,bird->x,bird->y,bird->z,bird->ix,bird->iy,bird->iz,bird->drawable);
525 	 bird->prevdrawable = bird->drawable;
526 	 if (bird->drawable)
527 	 {
528 	    float p = scale(bird->y);
529 
530 	    cairo_surface_t *surface;
531 	    int iw,ih,nw;
532 	    nw = bird->wingstate;
533 
534 	    int orient = 0*NWINGS;
535 
536 	    float sxz = fabsf(bird->sx);
537 	    float sy = fabs(bird->sy);
538 	    if (sxz > 1.73*sy)
539 	       orient = 2*NWINGS; // 1*NWINGS
540 	    else if (sy > 1.73*sxz)
541 	       orient = 0*NWINGS;
542 	    else
543 	       orient = 0*NWINGS;
544 
545 	    // canonical:
546 	    // if (sxz > 1.73*sy)
547 	    //    orient = 1*NWINGS;
548 	    //                          aside
549 	    //                            ***
550 	    //                         *********
551 	    //                         *********
552 	    //                         *********
553 	    //                            ***
554 	    //
555 	    // else if(sy > 1.73*sxz)
556 	    //    orient = 0*NWINGS
557 	    //                          front
558 	    //                                 **
559 	    //                        ********************
560 	    //                                 **
561 	    // else
562 	    //    orient = 2*NWINGS
563 	    //                          oblique
564 	    //                             ***
565 	    //                        *************
566 	    //                             ***
567 	    //
568 
569 	    P("%f %f %d\n",sxz,bird->sy,orient);
570 	    GdkPixbuf *bird_pixbuf = bird_pixbufs[nw+orient];
571 	    iw = p*blobals.bird_scale*Flags.BirdsScale*6.0e-6*blobals.maxix;
572 	    P("%d %d\n",Flags.BirdsScale,blobals.maxix);
573 	    ih = (float)iw*gdk_pixbuf_get_height(bird_pixbuf)/
574 	       (float)gdk_pixbuf_get_width(bird_pixbuf);
575 	    // do not draw very large birds (would be bad for cache use)
576 	    // and do not draw vanishing small birds
577 	    if (ih > blobals.maxiz*0.2 || ih <=0) // iw is always larger than ih, we don't have to check iw
578 	    {
579 	       P("ih: %d %d\n",ih,blobals.maxiz);
580 	       continue;
581 	    }
582 
583 	    const GdkInterpType interpolation = GDK_INTERP_HYPER;
584 	    // since we are caching the surfaces, we go for the highest quality
585 
586 	    // logarithmic caching
587 	    const double k   = log(1.2); // should be log(1.05) ... log(1.5). The higher, the less cache will be used
588 	    unsigned int key = ((unsigned int)(log(iw)/k)<<8) + nw + orient;
589 
590 	    if (!table_get(key))
591 	    {
592 	       static int table_counter = 0;
593 	       static double cache = 0;
594 	       table_counter++;
595 	       cache += iw*ih;
596 	       P("Entries: %d Cache: %.0f MB width: %d Wing: %d orient: %d\n",table_counter,cache*4.0e-6,iw,nw,orient/8);
597 	       if(iw < 1)iw = 1;
598 	       if(ih < 1)ih = 1;
599 	       GdkPixbuf *pixbuf = gdk_pixbuf_scale_simple(bird_pixbuf,iw,ih,interpolation);
600 	       table_insert(key,gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL));
601 	       g_clear_object(&pixbuf);
602 	    }
603 	    surface = (cairo_surface_t*) table_get(key);
604 
605 	    int mx = cairo_image_surface_get_width(surface);
606 	    int mz = cairo_image_surface_get_height(surface);
607 	    cairo_set_source_surface (cr, surface, bird->ix-mx/2, bird->iz-mz/2);
608 	    my_cairo_paint_with_alpha(cr,ALPHA);
609 	    bird->prevx = bird->ix-mx/2;
610 	    bird->prevy = bird->iz-mz/2;
611 	    bird->prevw = mx;
612 	    bird->prevh = mz;
613 	    P("draw: %d %d\n",bird->ix-mx/2,bird->iz-mz/2);
614 	 }
615 	 else
616 	 {
617 	    static int skipped = 0;
618 	    skipped++;
619 	    P("skipped: %d %d\n",skipped,bird->drawable);
620 	 }
621       }    // i-loop
622    }  // before-loop
623    return TRUE;
624 }
625 
birds_erase(int force)626 int birds_erase(int force)
627 {
628    if(global.IsDouble)
629       return TRUE;
630    if(!force)
631       LEAVE_IF_INACTIVE;
632    P("birds_erase %d\n",counter++);
633    int i;
634    for (i=0; i<Nbirds; i++)
635    {
636       BirdType *bird = &birds[i];
637       if (bird->prevdrawable && bird->prevw != 0 && bird->prevh != 0)
638       {
639 	 P("birds_erase xclear %d\n",counter++);
640 	 myXClearArea(global.display,global.SnowWin,
641 	       bird->prevx, bird->prevy,
642 	       bird->prevw, bird->prevh,
643 	       global.xxposures);
644       }
645    }
646    P("clearattr: %d %d %d %d\n", attrbird.prevx, attrbird.prevy, attrbird.prevw, attrbird.prevh);
647 
648    attrbird_erase(0);
649 
650    return TRUE;
651 }
652 
attrbird_erase(int force)653 int attrbird_erase(int force)
654 {
655    if(global.IsDouble)
656       return TRUE;
657    if (!force)
658       LEAVE_IF_INACTIVE;
659    static int px = -10000;
660    static int py = -10000;
661    static int pw = -10000;
662 
663    if(force || (attrbird.prevw && ( attrbird.prevx != px || attrbird.prevy!= py || attrbird.prevw != pw)))
664    {
665       P("erase attrbird\n");
666       px = attrbird.prevx;
667       py = attrbird.prevy;
668       pw = attrbird.prevw;
669       myXClearArea(global.display,global.SnowWin, px, py, pw, attrbird.prevh, global.xxposures);
670    }
671    return TRUE;
672 }
673 
init_birds(int start)674 void init_birds(int start)
675 {
676    int i;
677    if(!global.IsDouble)
678       birds_erase(1);
679    P("nbirds: %d %d\n",start,Flags.Nbirds);
680    // Bbirds+1 to prevent allocating zero bytes:
681    birds = (BirdType *)realloc(birds,sizeof(BirdType)*(Flags.Nbirds+1));
682    if (kd)
683       kd_free(kd);
684    kd = kd_create(3);
685    Nbirds = Flags.Nbirds;
686    for (i=start; i<Nbirds; i++)
687    {
688       BirdType *bird = &birds[i];
689       bird->x = drand48()*blobals.maxx;
690       bird->y = drand48()*blobals.maxy;
691       bird->z = drand48()*blobals.maxz;
692 
693       double r = drand48();
694       if (r > 0.75)
695 	 bird->x += blobals.maxx;
696       else if (r > 0.50)
697 	 bird->x -= blobals.maxx;
698       else if (r > 0.25)
699 	 bird->y += blobals.maxy;
700       else
701 	 bird->y -= blobals.maxy;
702 
703       bird->iw = 1;
704       bird->ih = 1;
705       r2i(bird);
706 
707       bird->sx = (0.5-drand48());
708       P("init %f\n",bird->sx);
709       bird->sy = (0.5-drand48());
710       bird->sz = (0.5-drand48());
711       normalize_speed(bird,blobals.meanspeed);
712       P("speed1: %d %f\n",i,sqrtf(sq3(bird->sx,bird->sy,bird->sz)));
713       bird->drawable = 1;
714       bird->wingstate = drand48()*NWINGS;
715       bird->prevdrawable = 0;
716       bird->prevw = 0;
717       bird->prevh = 0;
718       bird->prevx = 0;
719       bird->prevy = 0;
720 
721       kd_insert3f(kd, bird->x, bird->y, bird->z, NULL);
722    }
723 }
724 
725 
do_wings(void * d)726 static int do_wings(void *d)
727 {
728    if (Flags.Done)
729       return FALSE;
730    LEAVE_IF_INACTIVE;
731    int i;
732    for (i=0; i<Nbirds; i++)
733    {
734       BirdType *bird = &birds[i];
735       bird->wingstate++;
736       if (bird->wingstate >= NWINGS)
737 	 bird->wingstate = 0;
738    }
739    return TRUE;
740    (void)d;
741 }
742 
birds_get_range()743 float birds_get_range()
744 {
745    return blobals.range;
746 }
747 
birds_get_mean_dist()748 float birds_get_mean_dist()
749 {
750    return blobals.mean_distance;
751 }
752 
birds_set_attraction_point_relative(float x,float y,float z)753 void birds_set_attraction_point_relative(float x, float y, float z)
754 {
755 
756    attrbird.x = blobals.maxx*x;
757    attrbird.y = blobals.maxy*y;
758    attrbird.z = blobals.maxz*z;
759 }
760 
clear_flags()761 void clear_flags()
762 {
763 #define DOITB(what,type) \
764    blobals.what ## _changed = 0;
765    DOITALLB();
766 #include "undefall.inc"
767 #define DOITB(what) \
768    blobals.what ## _requested   = 0;
769    BUTTONALL();
770 #include "undefall.inc"
771 }
772 
birds_set_speed()773 void birds_set_speed()
774 {
775    blobals.meanspeed = Flags.BirdsSpeed*0.01*blobals.maxx*0.05;
776    P("%f\n",blobals.meanspeed);
777 }
778 
do_main_window(void * d)779 int do_main_window(void *d)
780 {
781    (void) d;
782    if (blobals.maxix != global.SnowWinWidth || blobals.maxiz != global.SnowWinHeight)
783    {
784       P("do_main_window\n");
785       main_window();
786       do_change_attr(NULL);
787    }
788    return TRUE;
789 }
790 
main_window()791 static void main_window()
792 {
793    blobals.maxix = global.SnowWinWidth;
794    blobals.maxiz = global.SnowWinHeight;
795    blobals.maxiy = (blobals.maxix+blobals.maxiz)/2;
796 
797    P("%d %d %d\n",blobals.maxix,blobals.maxiy,blobals.maxiz);
798 
799    blobals.maxz = blobals.maxx*(float)blobals.maxiz/(float)blobals.maxix;
800    blobals.maxy = blobals.maxx*(float)blobals.maxiy/(float)blobals.maxix;
801    blobals.xc   = (blobals.maxx-blobals.ox)/2;
802    blobals.zc   = (blobals.maxz-blobals.oz)/2;
803 
804    blobals.ax = blobals.maxix/blobals.maxx;
805    blobals.ay = blobals.maxiy/blobals.maxy;
806    blobals.az = blobals.maxiz/blobals.maxz;
807 
808    P("drawing window: %d %d %d %f %f %f\n",
809 	 blobals.maxix, blobals.maxiy, blobals.maxiz, blobals.maxx, blobals.maxy,blobals.maxz);
810 }
811 
birds_init_color()812 void birds_init_color()
813 {
814    int i;
815    for (i=0; i<NBIRDPIXBUFS; i++)
816    {
817       g_object_unref(bird_pixbufs[i]);
818    }
819    init_bird_pixbufs(Flags.BirdsColor);
820    table_clear((void(*)(void *))cairo_surface_destroy);
821 }
822 
init_bird_pixbufs(const char * color)823 static void init_bird_pixbufs(const char *color)
824 {
825    int i;
826    for (i=0; i<NBIRDPIXBUFS; i++)
827    {
828       char **x;
829       int lines;
830       xpm_set_color((char **)birds_xpm[i], &x, &lines, color);
831       bird_pixbufs[i] = gdk_pixbuf_new_from_xpm_data((const char **)x);
832       xpm_destroy(x);
833    }
834 }
835 
do_change_attr(void * d)836 int do_change_attr(void *d)
837 {
838    (void)d;
839    // move attraction point in the range
840    // x: 0.3 .. 0.7
841    // y: 0.4 .. 0.6
842    // z: 0.3 .. 0.7
843    if (Flags.Done)
844       return FALSE;
845    if (Flags.FollowSanta && !Flags.BirdsOnly)
846       return TRUE;
847    P("change attr\n");
848    attrbird_erase(1);
849    birds_set_attraction_point_relative(
850 	 0.3+drand48()*0.4,
851 	 0.4+drand48()*0.2,
852 	 0.3+drand48()*0.4
853 	 );
854    r2i(&attrbird);
855    attrbird2surface();
856    return TRUE;
857 }
858 
birds_init()859 void birds_init ()
860 {
861    init_bird_pixbufs("black"); // just to have pixbufs we can throw away
862    birds_init_color();
863    static int running = 0;
864 
865    if (running)
866    {
867       main_window();
868       return;
869    }
870    else
871    {
872       running = 1;
873       P("%d\n",counter++);
874       blobals.neighbours_max = 100;
875       blobals.range          = 20;
876       blobals.freeze         = 0;
877       blobals.maxx           = 1000;    // meters
878       blobals.bird_scale     = 64;
879 
880       blobals.prefdweight    = 1;
881 
882       clear_flags();
883       add_to_mainloop(PRIORITY_HIGH,time_update_pos_birds,     do_update_pos_birds   );
884       add_to_mainloop(PRIORITY_HIGH,time_update_speed_birds,   do_update_speed_birds );
885       add_to_mainloop(PRIORITY_HIGH,time_wings,                do_wings              );
886       add_to_mainloop(PRIORITY_DEFAULT,time_change_attr,       do_change_attr        );
887       add_to_mainloop(PRIORITY_DEFAULT,time_main_window,       do_main_window        );
888       main_window();
889    }
890 
891 
892    attrbird.x = blobals.maxx/2;
893    attrbird.y = blobals.maxy/2;
894    attrbird.z = blobals.maxz/2;
895    attrbird.prevx = 0;
896    attrbird.prevy = 0;
897    attrbird.prevw = 10;
898    attrbird.prevh = 10;
899 
900    blobals.meanspeed = 0;
901 
902    blobals.ox = 0;
903    blobals.oy = 0;
904    blobals.oz = 0;
905 
906    blobals.maxrange = blobals.maxx-blobals.ox+ blobals.maxy-blobals.oy+ blobals.maxz-blobals.oz;
907 
908    birds_set_speed();
909    init_birds(0);
910 
911    attrbird2surface();
912 
913 }
914