7 struct quadtree_ops planet_ops;
8 int gravitations, optimizations;
10 static int draw_lines = 0;
11 static int draw_tree_area = 0;
13 static void putpixel(struct SDL_Surface *screen, const int x, const int y,
14 const unsigned char r, const unsigned char g,
15 const unsigned char b)
17 int offset = y * screen->pitch + x * 4;
18 unsigned char *buf = screen->pixels;
25 static void draw_line(SDL_Surface *screen, const struct vector *p1,
26 const struct vector *p2,
27 const unsigned char r, const unsigned char g,
28 const unsigned char b, const struct camera *cam)
34 vector_sub(p2, p1, &dir);
36 len = vector_abs(&dir);
37 vector_scale(&dir, 1 / len, &dir);
40 vector_sub(p1, &cam->pos, &v);
41 vector_scale(&v, cam->zoom, &v);
46 for (i = 0; i < len ; i++) {
47 if (v.x >= 0 && v.x < screen->w &&
48 v.y >= 0 && v.y < screen->h)
49 putpixel(screen, v.x, v.y, r, g, b);
50 vector_add(&v, &dir, &v);
54 static void reshape_planet(struct planet *p)
56 p->radius = pow(p->mass / 100, 1 / 3.0);
59 void init_planet(struct planet *p)
61 memset(p, 0, sizeof(*p));
63 p->r = get_random() % 256;
64 p->g = get_random() % 256;
65 p->b = get_random() % 256;
67 INIT_LIST_HEAD(&p->list);
68 init_quadtree(&p->tree);
72 * setup_planet - set the planet on a "solarsystem"
73 * @p: pointer to struct planet to set up
74 * @mass: mass of the planet to set up
75 * @total_mass: total mass of the system
76 * @radius: maximum radius of the system
79 static void setup_planet(struct planet *p, double mass, double total_mass,
82 double angle = M_PI * 2 * get_random_double();
86 distance = radius * pow(get_random_double(), 2);
87 velocity = sqrt(total_mass / radius);
89 velocity *= pow(distance / radius, 0.2);
91 p->tree.pos.x = cos(angle) * distance;
92 p->tree.pos.y = sin(angle) * distance;
94 p->speed.x = -sin(angle) * velocity;
95 p->speed.y = cos(angle) * velocity;
102 void create_planets(struct planet *p, int num, double total_mass, double radius)
106 struct planet *new_planet;
109 total_mass / num * 2 * get_random_double(),
113 for (i = 0; i < num; i++) {
114 new_planet = malloc(sizeof(*new_planet));
115 init_planet(new_planet);
117 list_add(&new_planet->list, &p->list);
119 setup_planet(new_planet,
120 total_mass / num * 2 * get_random_double(),
124 quadtree_add(&p->tree, &new_planet->tree, &planet_ops);
126 sum += new_planet->mass;
130 void draw_circle(SDL_Surface *screen, const struct vector *pos,
132 char r, char g, char b, int transparent)
134 int x, x_start, y, x_end;
135 float r2 = radius * radius;
137 y = MAX(pos->y - radius, 0);
139 if (radius * 2 <= 1) {
140 if (pos->x >= 0 && pos->x < screen->w &&
141 pos->y >= 0 && pos->y < screen->h)
142 putpixel(screen, (int)pos->x, (int)pos->y,
147 for (; y < MIN(pos->y + radius, screen->h); y++) {
149 unsigned char *buf = screen->pixels;
150 float y2 = (y - pos->y);
152 y2 = sqrt(r2 - y2 * y2);
153 x_start = pos->x - y2;
155 x_start = MAX(0, x_start);
156 x_end = MIN(x_end, screen->w);
158 offset = y * screen->pitch + x_start * 4;
160 for (x = x_start; x < x_end; x++) {
167 for (x = x_start; x < x_end; x++) {
177 void draw_planet(SDL_Surface *screen, const struct planet *p,
178 const struct camera *cam)
181 float radius = p->radius * cam->zoom;
184 vector_sub(&p->tree.pos, &cam->pos, &pos);
185 vector_scale(&pos, cam->zoom, &pos);
186 pos.x += screen->w / 2;
187 pos.y += screen->h / 2;
190 draw_circle(screen, &pos, p->tree_area * cam->zoom,
191 p->r / 8, p->g / 8, p->b / 8, 1);
194 for (i = 0; i < 4; i++) {
195 if (!p->tree.child[i])
198 struct planet *q = tree_to_planet(p->tree.child[i]);
200 draw_line(screen, &p->tree.pos, &q->tree.pos,
201 p->r, p->g, p->b, cam);
205 draw_circle(screen, &pos, radius, p->r, p->g, p->b, 0);
208 int gravitize_planet_with_planet(struct planet *a, struct planet *b,
211 struct vector distance, sum;
214 if (a->to_be_deleted || b->to_be_deleted)
219 vector_sub(&a->tree.pos, &b->tree.pos, &distance);
221 dist = vector_abs(&distance);
223 if (dist < (a->radius + b->radius)) {
228 vector_div(&distance, dist, &distance);
230 f = a->mass * b->mass / (dist * dist) * time;
233 vector_scale(&distance, acc, &sum);
234 vector_add(&b->speed, &sum, &b->speed);
237 vector_scale(&distance, acc, &sum);
238 vector_sub(&a->speed, &sum, &a->speed);
243 int gravitize_planet_with_tree(struct planet *a, struct planet *b,
246 struct vector distance, sum;
249 if (a->to_be_deleted || b->to_be_deleted)
254 vector_sub(&a->tree.pos, &b->tree.pos, &distance);
256 dist = vector_abs(&distance);
258 vector_div(&distance, dist, &distance);
260 f = a->mass * b->tree_mass / (dist * dist) * time;
263 vector_scale(&distance, acc, &sum);
264 vector_sub(&a->speed, &sum, &a->speed);
266 acc = f / b->tree_mass;
267 vector_scale(&distance, acc, &sum);
268 vector_add(&b->tree_speed, &sum, &b->tree_speed);
274 * Merge planets a and b together
276 * The smaller planet will be deleted, matter will be transferred to
279 static struct planet *_merge_planets(struct planet *a, struct planet *b)
281 struct quadtree *parent = &a->tree;
282 struct vector pa, pb, p;
285 vector_scale(&a->speed, a->mass, &pa);
286 vector_scale(&b->speed, b->mass, &pb);
287 vector_add(&pa, &pb, &p);
288 mass = a->mass + b->mass;
290 if (a->mass < b->mass)
291 parent = quadtree_move(&a->tree, b->tree.pos, &planet_ops);
295 vector_div(&p, nr->mass, &nr->speed);
299 vector_div(&p, a->mass, &a->speed);
301 return tree_to_planet(quadtree_find_parent(parent));
305 * Merge planets a and b into a the new planet, which pointer is
306 * returned to the caller. The other planet is removed from the linked
307 * list and it's memory is freed. The merged planet will be kept in
310 struct planet *merge_planets(struct planet *a, struct planet *b)
313 _merge_planets(a, b);
316 p = quadtree_del(&b->tree, &planet_ops);
319 return tree_to_planet(p);
322 static void gravitize_planet(struct planet *p, struct planet *pt, double time)
328 vector_sub(&p->tree.pos, &pt->tree.pos, &vect);
329 dist = vector_abs(&vect);
332 gravitize_planet_with_planet(p, pt, time);
334 if (dist > pt->tree_area * 8) {
336 * OK, the node is far enough. We can approximate the
337 * entire tree as a single entity.
339 optimizations += pt->tree.children;
340 gravitize_planet_with_tree(p, pt, time);
344 /* Otherwise, gravitize with each of the child */
345 for (i = 0; i < 4; i++) {
346 if (!pt->tree.child[i])
349 gravitize_planet(p, tree_to_planet(pt->tree.child[i]),
354 void gravitize_planet_tree(struct planet *p, double time)
358 for (i = 0; i < 4; i++) {
359 if (!p->tree.child[i])
362 gravitize_planet_tree(tree_to_planet(p->tree.child[i]),
367 tree_to_planet(quadtree_find_parent(&p->tree)),
371 struct planet *move_planet(struct planet *p, const double time)
373 struct vector tmp, new_pos;
375 vector_scale(&p->speed, time, &tmp);
376 vector_add(&p->tree.pos, &tmp, &new_pos);
378 return tree_to_planet(quadtree_move(&p->tree, new_pos, &planet_ops));
381 int propagate_tree_movement(struct planet *p)
385 for (i = 0; i < 4; i++) {
386 if (!p->tree.child[i])
389 vector_add(&tree_to_planet(p->tree.child[i])->speed,
391 &tree_to_planet(p->tree.child[i])->speed);
393 count += propagate_tree_movement(
394 tree_to_planet(p->tree.child[i]));
397 vector_add(&p->tree_speed, &p->speed, &p->speed);
405 struct planet *prune_planet_tree(struct planet *p)
407 struct quadtree *tree_parent = &p->tree;
410 while (head != start) {
411 p = list_to_planet(head);
412 if (p->to_be_deleted) {
416 tree_parent = quadtree_del(&p->tree, &planet_ops);
425 return tree_to_planet(tree_parent);
427 for (i = 0; i < 4; i++) {
428 if (!p->tree.child[i])
431 prune_planet_tree(tree_to_planet(p->tree.child[i]));
434 if (p->to_be_deleted) {
436 tree_parent = quadtree_del(&p->tree, &planet_ops);
438 return tree_to_planet(tree_parent);
441 planet_ops.recalculate_stats(&p->tree);
443 return tree_to_planet(quadtree_find_parent(&p->tree));
446 void print_planet(const struct planet *p)
448 printf("pos: (%f,%f), speed: (%f,%f), mass: %f, radius %f\n",
449 p->tree.pos.x, p->tree.pos.y, p->speed.x, p->speed.y, p->mass, p->radius);
452 int planet_search_rectangular(struct quadtree *node,
453 struct quadtree_iterator *itr)
455 struct planet_search_iterator *it = qt_itr_to_planet_itr(itr);
456 struct planet *p = tree_to_planet(node);
457 int direction = 0, i;
458 int up = 0, left = 0, right = 0, down = 0;
460 for (i = 0; i < 2; i++) {
461 if (it->limit[i].x < p->tree.pos.x)
465 if (it->limit[i].y < p->tree.pos.y)
472 direction |= QUADTREE_UPLEFT;
474 direction |= QUADTREE_UPRIGHT;
476 direction |= QUADTREE_DOWNLEFT;
478 direction |= QUADTREE_DOWNRIGHT;
479 if (direction == (QUADTREE_UPLEFT | QUADTREE_UPRIGHT |
480 QUADTREE_DOWNLEFT | QUADTREE_DOWNRIGHT))
481 direction |= QUADTREE_SELF;
486 void planet_draw_iterator(struct quadtree *node, struct quadtree_iterator *it)
488 struct planet *p = tree_to_planet(node);
489 struct planet_search_iterator *i = qt_itr_to_planet_itr(it);
491 draw_planet(i->screen, p, i->cam);
494 static void planet_recalculate_stats(struct quadtree *node)
496 struct planet *p = tree_to_planet(node), *c;
502 p->tree_mass = p->mass;
504 for (i = 0; i < 4; i++) {
508 c = tree_to_planet(node->child[i]);
509 p->tree_mass += c->tree_mass;
511 vector_sub(&p->tree.pos, &c->tree.pos, &vect);
512 dist = vector_abs(&vect);
513 dist += c->tree_area;
514 p->tree_area = MAX(p->tree_area, dist);
518 struct quadtree_ops planet_ops = {
519 .recalculate_stats = planet_recalculate_stats,