]> git.itanic.dy.fi Git - sdl-planets/blobdiff - planet.c
planets: Add support for spatial search from quadtree
[sdl-planets] / planet.c
index 47d82f3adedae0d2ac388d12a6963fcd351931e9..da83d3a1d491762fb1ae8cbe0196bbae10fd9e06 100644 (file)
--- a/planet.c
+++ b/planet.c
@@ -18,38 +18,127 @@ static void putpixel(struct SDL_Surface *screen, const int x, const int y,
 
 static void reshape_planet(struct planet *p)
 {
-       p->size = sqrt(p->mass / 100);
+       p->radius = pow(p->mass / 100, 1 / 3.0);
 }
 
-void init_planet(const SDL_Surface *screen, struct planet *p)
+void init_planet(struct planet *p)
 {
        p->speed.x = 0;
        p->speed.y = 0;
-       p->pos.x = get_random() % screen->w;
-       p->pos.y = get_random() % screen->h;
-       p->mass = get_random() % 1000 + 100;
+       p->pos.x = 0;
+       p->pos.y = 0;
+       p->mass = 0;
        reshape_planet(p);
        p->r = get_random() % 256;
        p->g = get_random() % 256;
        p->b = get_random() % 256;
 
        INIT_LIST_HEAD(&p->list);
+       init_quadtree(&p->tree);
 }
 
-void draw_planet(SDL_Surface *screen, struct planet *p)
+/**
+ * setup_planet - set the planet on a "solarsystem"
+ * @p:         pointer to struct planet to set up
+ * @mass:      mass of the planet to set up
+ * @total_mass: total mass of the system
+ * @radius:    maximum radius of the system
+ */
+
+static void setup_planet(struct planet *p, double mass, double total_mass,
+                        double radius)
+{
+       double angle = M_PI * 2 * get_random_double();
+       double velocity;
+       double distance;
+
+       distance = radius * pow(get_random_double(), 2);
+       velocity = sqrt(total_mass / radius);
+
+       velocity *= pow(distance / radius, 0.2);
+
+       p->pos.x = cos(angle) * distance;
+       p->pos.y = sin(angle) * distance;
+
+       p->speed.x = -sin(angle) * velocity;
+       p->speed.y = cos(angle) * velocity;
+
+       p->mass = mass;
+
+       reshape_planet(p);
+}
+
+void create_planets(struct planet *p, int num, double total_mass, double radius)
+{
+       int i;
+       double sum = 0;
+       struct planet *new_planet;
+
+       setup_planet(p,
+                    total_mass / num * 2 * get_random_double(),
+                    total_mass,
+                    radius);
+
+       for (i = 0; i < num; i++) {
+               new_planet = malloc(sizeof(*new_planet));
+               init_planet(new_planet);
+
+               list_add(&new_planet->list, &p->list);
+
+               setup_planet(new_planet,
+                            total_mass / num * 2 * get_random_double(),
+                            total_mass,
+                            radius);
+
+               quadtree_add(&p->tree, &new_planet->tree,
+                            planet_spatial_compare);
+
+               sum += new_planet->mass;
+       }
+}
+
+void draw_planet(SDL_Surface *screen, struct planet *p,
+                const struct camera *cam)
 {
-       int size = p->size / 2;
-       int x, x_start, y, x_end, y_end;
+       struct vector pos;
+       float radius = p->radius * cam->zoom;
+       float r2 = radius * radius;
+       int x, x_start, y, x_end;
 
-       x_start = MAX(p->pos.x - size, 0);
-       y = MAX(p->pos.y - size, 0);
+       vector_sub(&p->pos, &cam->pos, &pos);
+       vector_scale(&pos, cam->zoom, &pos);
+       pos.x += screen->w / 2;
+       pos.y += screen->h / 2;
 
-       x_end = MIN(p->pos.x + size, screen->w);
-       y_end = MIN(p->pos.y + size, screen->h);
+       y = MAX(pos.y - radius, 0);
 
-       for (; y < y_end; y++)
-               for (x = x_start; x < x_end; x++)
-                       putpixel(screen, x, y, p->r, p->g, p->b);
+       if (radius * 2 <= 1) {
+               if (pos.x >= 0 && pos.x < screen->w &&
+                   pos.y >= 0 && pos.y < screen->h)
+                       putpixel(screen, (int)pos.x, (int)pos.y,
+                                p->r, p->g, p->b);
+               return;
+       }
+
+       for (; y < MIN(pos.y + radius, screen->h); y++) {
+               int offset;
+               unsigned char *buf = screen->pixels;
+               float y2 = (y - pos.y);
+
+               y2 = sqrt(r2 - y2 * y2);
+               x_start = pos.x - y2;
+               x_end = pos.x + y2;
+               x_start = MAX(0, x_start);
+               x_end = MIN(x_end, screen->w);
+
+               offset = y * screen->pitch + x_start * 4;
+               for (x = x_start; x < x_end; x++) {
+                       buf[offset++] = p->b;
+                       buf[offset++] = p->g;
+                       buf[offset++] = p->r;
+                       offset++;
+               }
+       }
 }
 
 int gravitize_planets(struct planet *a, struct planet *b, const double time)
@@ -62,12 +151,12 @@ int gravitize_planets(struct planet *a, struct planet *b, const double time)
        dist = vector_abs(&distance);
 
        /* Return true in case of a collision */
-       if (dist < (a->size + b->size))
+       if (dist < (a->radius + b->radius))
                return 1;
 
        vector_div(&distance, dist, &distance);
 
-       f = a->mass * b->mass / (dist * dist + 5) * time;
+       f = a->mass * b->mass / (dist * dist) * time;
 
        acc = f / b->mass;
        vector_scale(&distance, acc, &sum);
@@ -80,15 +169,120 @@ int gravitize_planets(struct planet *a, struct planet *b, const double time)
        return 0;
 }
 
-void move_planet(struct planet *p, const double time)
+/*
+ * Merge planets a and b into planet a
+ *
+ * It is left for the caller to deal with the scrap planet b
+ */
+static void _merge_planets(struct planet *a, struct planet *b)
+{
+       struct vector pa, pb, p;
+
+       vector_scale(&a->speed, a->mass, &pa);
+       vector_scale(&b->speed, b->mass, &pb);
+       vector_add(&pa, &pb, &p);
+
+       if (a->mass < b->mass)
+               a->pos = b->pos;
+
+       a->mass += b->mass;
+       reshape_planet(a);
+       vector_div(&p, a->mass, &a->speed);
+}
+
+/*
+ * Merge planets a and b into a the new planet a, which pointer is
+ * returned to the caller. Planet b is removed from the linked list
+ * and it's memory is freed. The merged planet will retain in the
+ * list.
+ */
+struct planet *merge_planets(struct planet *a, struct planet *b)
+{
+       _merge_planets(a, b);
+
+       list_del(&b->list);
+       quadtree_del(&b->tree, planet_spatial_compare);
+
+       free(b);
+       return a;
+}
+
+struct planet *move_planet(struct planet *p, const double time)
 {
        struct vector tmp;
+       struct quadtree *tree_parent;
+
        vector_scale(&p->speed, time, &tmp);
        vector_add(&p->pos, &tmp, &p->pos);
+
+       tree_parent = quadtree_del(&p->tree, planet_spatial_compare);
+       quadtree_add(tree_parent, &p->tree, planet_spatial_compare);
+       return tree_to_planet(tree_parent);
 }
 
 void print_planet(const struct planet *p)
 {
-       printf("pos: (%f,%f), speed: (%f,%f), mass: %f, size %f\n",
-              p->pos.x, p->pos.y, p->speed.x, p->speed.y, p->mass, p->size);
+       printf("pos: (%f,%f), speed: (%f,%f), mass: %f, radius %f\n",
+              p->pos.x, p->pos.y, p->speed.x, p->speed.y, p->mass, p->radius);
+}
+
+int planet_spatial_compare(struct quadtree *ta, struct quadtree *tb)
+{
+       struct planet *a, *b;
+       int up, left;
+       a = tree_to_planet(ta);
+       b = tree_to_planet(tb);
+
+       up = b->pos.y < a->pos.y;
+       left = b->pos.x < a->pos.x;
+
+       if (up && left)
+               return 0;
+       if (up && !left)
+               return 1;
+       if (left)
+               return 2;
+       return 3;
+}
+
+int planet_search_rectangular(struct quadtree *node,
+                             struct quadtree_iterator *itr)
+{
+       struct planet_search_iterator *it = qt_itr_to_planet_itr(itr);
+       struct planet *p = tree_to_planet(node);
+       int direction = 0, i;
+       int up = 0, left = 0, right = 0, down = 0;
+
+       for (i = 0; i < 2; i++) {
+               if (it->limit[i].x < p->pos.x)
+                       left = 1;
+               else
+                       right = 1;
+               if (it->limit[i].y < p->pos.y)
+                       up = 1;
+               else
+                       down = 1;
+       }
+
+       if (left && up)
+               direction |= QUADTREE_UPLEFT;
+       if (right && up)
+               direction |= QUADTREE_UPRIGHT;
+       if (left && down)
+               direction |= QUADTREE_DOWNLEFT;
+       if (right && down)
+               direction |= QUADTREE_DOWNRIGHT;
+       if (direction == (QUADTREE_UPLEFT | QUADTREE_UPRIGHT |
+                         QUADTREE_DOWNLEFT | QUADTREE_DOWNRIGHT))
+               direction |= QUADTREE_SELF;
+
+       return direction;
+}
+
+void planet_draw_iterator(struct quadtree *node, struct quadtree_iterator *it)
+{
+       struct planet *p = tree_to_planet(node);
+       struct planet_search_iterator *i = qt_itr_to_planet_itr(it);
+
+       draw_planet(i->screen, p, i->cam);
 }