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