]> git.itanic.dy.fi Git - sdl-planets/blob - planet.c
planet: Add tree statistics
[sdl-planets] / planet.c
1 #include <math.h>
2
3 #include "random.h"
4 #include "planet.h"
5 #include "utils.h"
6
7 struct quadtree_ops planet_ops;
8
9 static void putpixel(struct SDL_Surface *screen, const int x, const int y,
10                      const unsigned char r, const unsigned char g,
11                      const unsigned char b)
12 {
13         int offset = y * screen->pitch + x * 4;
14         unsigned char *buf = screen->pixels;
15
16         buf[offset++] = b;
17         buf[offset++] = g;
18         buf[offset]   = r;
19 }
20
21 static void reshape_planet(struct planet *p)
22 {
23         p->radius = pow(p->mass / 100, 1 / 3.0);
24 }
25
26 void init_planet(struct planet *p)
27 {
28         memset(p, 0, sizeof(*p));
29         reshape_planet(p);
30         p->r = get_random() % 256;
31         p->g = get_random() % 256;
32         p->b = get_random() % 256;
33
34         INIT_LIST_HEAD(&p->list);
35         init_quadtree(&p->tree);
36 }
37
38 /**
39  * setup_planet - set the planet on a "solarsystem"
40  * @p:          pointer to struct planet to set up
41  * @mass:       mass of the planet to set up
42  * @total_mass: total mass of the system
43  * @radius:     maximum radius of the system
44  */
45
46 static void setup_planet(struct planet *p, double mass, double total_mass,
47                          double radius)
48 {
49         double angle = M_PI * 2 * get_random_double();
50         double velocity;
51         double distance;
52
53         distance = radius * pow(get_random_double(), 2);
54         velocity = sqrt(total_mass / radius);
55
56         velocity *= pow(distance / radius, 0.2);
57
58         p->pos.x = cos(angle) * distance;
59         p->pos.y = sin(angle) * distance;
60
61         p->speed.x = -sin(angle) * velocity;
62         p->speed.y = cos(angle) * velocity;
63
64         p->mass = mass;
65
66         reshape_planet(p);
67 }
68
69 void create_planets(struct planet *p, int num, double total_mass, double radius)
70 {
71         int i;
72         double sum = 0;
73         struct planet *new_planet;
74
75         setup_planet(p,
76                      total_mass / num * 2 * get_random_double(),
77                      total_mass,
78                      radius);
79
80         for (i = 0; i < num; i++) {
81                 new_planet = malloc(sizeof(*new_planet));
82                 init_planet(new_planet);
83
84                 list_add(&new_planet->list, &p->list);
85
86                 setup_planet(new_planet,
87                              total_mass / num * 2 * get_random_double(),
88                              total_mass,
89                              radius);
90
91                 quadtree_add(&p->tree, &new_planet->tree, &planet_ops);
92
93                 sum += new_planet->mass;
94         }
95 }
96
97 void draw_planet(SDL_Surface *screen, struct planet *p,
98                  const struct camera *cam)
99 {
100         struct vector pos;
101         float radius = p->radius * cam->zoom;
102         float r2 = radius * radius;
103         int x, x_start, y, x_end;
104
105         vector_sub(&p->pos, &cam->pos, &pos);
106         vector_scale(&pos, cam->zoom, &pos);
107         pos.x += screen->w / 2;
108         pos.y += screen->h / 2;
109
110         y = MAX(pos.y - radius, 0);
111
112         if (radius * 2 <= 1) {
113                 if (pos.x >= 0 && pos.x < screen->w &&
114                     pos.y >= 0 && pos.y < screen->h)
115                         putpixel(screen, (int)pos.x, (int)pos.y,
116                                  p->r, p->g, p->b);
117                 return;
118         }
119
120         for (; y < MIN(pos.y + radius, screen->h); y++) {
121                 int offset;
122                 unsigned char *buf = screen->pixels;
123                 float y2 = (y - pos.y);
124
125                 y2 = sqrt(r2 - y2 * y2);
126                 x_start = pos.x - y2;
127                 x_end = pos.x + y2;
128                 x_start = MAX(0, x_start);
129                 x_end = MIN(x_end, screen->w);
130
131                 offset = y * screen->pitch + x_start * 4;
132                 for (x = x_start; x < x_end; x++) {
133                         buf[offset++] = p->b;
134                         buf[offset++] = p->g;
135                         buf[offset++] = p->r;
136                         offset++;
137                 }
138         }
139 }
140
141 int gravitize_planets(struct planet *a, struct planet *b, const double time)
142 {
143         struct vector distance, sum;
144         double dist, f, acc;
145
146         vector_sub(&a->pos, &b->pos, &distance);
147
148         dist = vector_abs(&distance);
149
150         /* Return true in case of a collision */
151         if (dist < (a->radius + b->radius))
152                 return 1;
153
154         vector_div(&distance, dist, &distance);
155
156         f = a->mass * b->mass / (dist * dist) * time;
157
158         acc = f / b->mass;
159         vector_scale(&distance, acc, &sum);
160         vector_add(&b->speed, &sum, &b->speed);
161
162         acc = f / a->mass;
163         vector_scale(&distance, acc, &sum);
164         vector_sub(&a->speed, &sum, &a->speed);
165
166         return 0;
167 }
168
169 /*
170  * Merge planets a and b into planet a
171  *
172  * It is left for the caller to deal with the scrap planet b
173  */
174 static void _merge_planets(struct planet *a, struct planet *b)
175 {
176         struct vector pa, pb, p;
177
178         vector_scale(&a->speed, a->mass, &pa);
179         vector_scale(&b->speed, b->mass, &pb);
180         vector_add(&pa, &pb, &p);
181
182         if (a->mass < b->mass)
183                 a->pos = b->pos;
184
185         a->mass += b->mass;
186         reshape_planet(a);
187         vector_div(&p, a->mass, &a->speed);
188 }
189
190 /*
191  * Merge planets a and b into a the new planet a, which pointer is
192  * returned to the caller. Planet b is removed from the linked list
193  * and it's memory is freed. The merged planet will retain in the
194  * list.
195  */
196 struct planet *merge_planets(struct planet *a, struct planet *b)
197 {
198         _merge_planets(a, b);
199
200         list_del(&b->list);
201         quadtree_del(&b->tree, &planet_ops);
202
203         free(b);
204         return a;
205 }
206
207 static int planet_search_when_moving(struct quadtree *node,
208                                      struct quadtree_iterator *itr)
209 {
210         struct planet *p = tree_to_planet(node);
211         struct planet_search_iterator *it = qt_itr_to_planet_itr(itr);
212         int direction = 0, i;
213         int up = 0, left = 0, right = 0, down = 0;
214
215         for (i = 0; i < 2; i++) {
216                 if (it->limit[i].x < p->pos.x)
217                         left = 1;
218                 else
219                         right = 1;
220                 if (it->limit[i].y < p->pos.y)
221                         up = 1;
222                 else
223                         down = 1;
224         }
225
226         if (left || up)
227                 direction |= QUADTREE_UPLEFT;
228         if (right || up)
229                 direction |= QUADTREE_UPRIGHT;
230         if (left || down)
231                 direction |= QUADTREE_DOWNLEFT;
232         if (right || down)
233                 direction |= QUADTREE_DOWNRIGHT;
234         if ((left && right) || (up && down))
235                 direction |= QUADTREE_SELF;
236
237         return direction;
238 }
239
240 void planet_move_iterator(struct quadtree *node, struct quadtree_iterator *it)
241 {
242         struct quadtree *parent;
243
244         parent = quadtree_del(node, &planet_ops);
245         quadtree_add(parent, node, &planet_ops);
246 }
247
248 struct planet *move_planet(struct planet *p, const double time)
249 {
250         struct vector tmp, new_pos;
251         struct quadtree *parent, *tree_parent;
252         struct planet *pa;
253         struct planet_search_iterator it;
254
255         int modify = 0;
256
257         vector_scale(&p->speed, time, &tmp);
258         vector_add(&p->pos, &tmp, &new_pos);
259
260         /* Check if we have crossed any of the parents */
261         parent = p->tree.parent;
262         while (parent) {
263                 pa = tree_to_planet(parent);
264                 if (p->pos.x < pa->pos.x && new_pos.x > pa->pos.x)
265                         modify = 1;
266                 if (p->pos.x > pa->pos.x && new_pos.x < pa->pos.x)
267                         modify = 1;
268                 if (p->pos.y < pa->pos.y && new_pos.y > pa->pos.y)
269                         modify = 1;
270                 if (p->pos.y > pa->pos.y && new_pos.y < pa->pos.y)
271                         modify = 1;
272
273                 if (!modify) {
274                         parent = parent->parent;
275                         continue;
276                 }
277
278                 tree_parent = quadtree_del(&p->tree, &planet_ops);
279                 p->pos = new_pos;
280                 quadtree_add(tree_parent, &p->tree, &planet_ops);
281                 return tree_to_planet(tree_parent);
282         }
283
284         /*
285          * Now, search the subtree for any crossed children and move
286          * them into correct place within the tree.
287          */
288         it.qt_iterator.head = &p->tree;
289         it.limit[0] = p->pos;
290         it.limit[1] = new_pos;
291         it.qt_iterator.direction = planet_search_when_moving;
292         it.qt_iterator.callback = planet_move_iterator;
293         walk_quadtree(&it.qt_iterator);
294
295         p->pos = new_pos;
296
297         return tree_to_planet(quadtree_find_parent(&p->tree));
298 }
299
300 void print_planet(const struct planet *p)
301 {
302         printf("pos: (%f,%f), speed: (%f,%f), mass: %f, radius %f\n",
303                p->pos.x, p->pos.y, p->speed.x, p->speed.y, p->mass, p->radius);
304 }
305
306 int planet_spatial_compare(struct quadtree *ta, struct quadtree *tb)
307 {
308         struct planet *a, *b;
309         int up, left;
310         a = tree_to_planet(ta);
311         b = tree_to_planet(tb);
312
313         up = b->pos.y < a->pos.y;
314         left = b->pos.x < a->pos.x;
315
316         if (up && left)
317                 return 0;
318         if (up && !left)
319                 return 1;
320         if (left)
321                 return 2;
322         return 3;
323 }
324
325 int planet_search_rectangular(struct quadtree *node,
326                               struct quadtree_iterator *itr)
327 {
328         struct planet_search_iterator *it = qt_itr_to_planet_itr(itr);
329         struct planet *p = tree_to_planet(node);
330         int direction = 0, i;
331         int up = 0, left = 0, right = 0, down = 0;
332
333         for (i = 0; i < 2; i++) {
334                 if (it->limit[i].x < p->pos.x)
335                         left = 1;
336                 else
337                         right = 1;
338                 if (it->limit[i].y < p->pos.y)
339                         up = 1;
340                 else
341                         down = 1;
342         }
343
344         if (left && up)
345                 direction |= QUADTREE_UPLEFT;
346         if (right && up)
347                 direction |= QUADTREE_UPRIGHT;
348         if (left && down)
349                 direction |= QUADTREE_DOWNLEFT;
350         if (right && down)
351                 direction |= QUADTREE_DOWNRIGHT;
352         if (direction == (QUADTREE_UPLEFT | QUADTREE_UPRIGHT |
353                           QUADTREE_DOWNLEFT | QUADTREE_DOWNRIGHT))
354                 direction |= QUADTREE_SELF;
355
356         return direction;
357 }
358
359 void planet_draw_iterator(struct quadtree *node, struct quadtree_iterator *it)
360 {
361         struct planet *p = tree_to_planet(node);
362         struct planet_search_iterator *i = qt_itr_to_planet_itr(it);
363
364         draw_planet(i->screen, p, i->cam);
365 }
366
367 static void planet_recalculate_stats(struct quadtree *node)
368 {
369         struct planet *p = tree_to_planet(node), *c;
370         struct vector vect;
371         double dist;
372         int i;
373
374         p->tree_area = 0;
375         p->tree_mass = p->mass;
376
377         for (i = 0; i < 4; i++) {
378                 if (!node->child[i])
379                         continue;
380
381                 c = tree_to_planet(node->child[i]);
382                 p->tree_mass += c->tree_mass;
383
384                 vector_sub(&p->pos, &c->pos, &vect);
385                 dist = vector_abs(&vect);
386                 dist += c->tree_area;
387                 p->tree_area = MAX(p->tree_area, dist);
388         }
389 }
390
391 struct quadtree_ops planet_ops = {
392         .compare = planet_spatial_compare,
393         .recalculate_stats = planet_recalculate_stats,
394 };
395