]> git.itanic.dy.fi Git - sdl-planets/blob - planet.c
quadtree: Obey naming conventions
[sdl-planets] / planet.c
1 #include <math.h>
2
3 #include "random.h"
4 #include "planet.h"
5 #include "utils.h"
6
7 static void putpixel(struct SDL_Surface *screen, const int x, const int y,
8                      const unsigned char r, const unsigned char g,
9                      const unsigned char b)
10 {
11         int offset = y * screen->pitch + x * 4;
12         unsigned char *buf = screen->pixels;
13
14         buf[offset++] = b;
15         buf[offset++] = g;
16         buf[offset]   = r;
17 }
18
19 static void reshape_planet(struct planet *p)
20 {
21         p->radius = pow(p->mass / 100, 1 / 3.0);
22 }
23
24 void init_planet(struct planet *p)
25 {
26         p->speed.x = 0;
27         p->speed.y = 0;
28         p->pos.x = 0;
29         p->pos.y = 0;
30         p->mass = 0;
31         reshape_planet(p);
32         p->r = get_random() % 256;
33         p->g = get_random() % 256;
34         p->b = get_random() % 256;
35
36         INIT_LIST_HEAD(&p->list);
37 }
38
39 /**
40  * setup_planet - set the planet on a "solarsystem"
41  * @p:          pointer to struct planet to set up
42  * @mass:       mass of the planet to set up
43  * @total_mass: total mass of the system
44  * @radius:     maximum radius of the system
45  */
46
47 static void setup_planet(struct planet *p, double mass, double total_mass,
48                          double radius)
49 {
50         double angle = M_PI * 2 * get_random_double();
51         double velocity;
52         double distance;
53
54         distance = radius * pow(get_random_double(), 2);
55         velocity = sqrt(total_mass / radius);
56
57         velocity *= pow(distance / radius, 0.2);
58
59         p->pos.x = cos(angle) * distance;
60         p->pos.y = sin(angle) * distance;
61
62         p->speed.x = -sin(angle) * velocity;
63         p->speed.y = cos(angle) * velocity;
64
65         p->mass = mass;
66
67         reshape_planet(p);
68 }
69
70 void create_planets(struct planet *p, int num, double total_mass, double radius)
71 {
72         int i;
73         double sum = 0;
74         struct planet *new_planet;
75
76         setup_planet(p,
77                      total_mass / num * 2 * get_random_double(),
78                      total_mass,
79                      radius);
80
81         for (i = 0; i < num; i++) {
82                 new_planet = malloc(sizeof(*new_planet));
83                 init_planet(new_planet);
84                 list_add(&new_planet->list, &p->list);
85                 setup_planet(new_planet,
86                              total_mass / num * 2 * get_random_double(),
87                              total_mass,
88                              radius);
89
90                 sum += new_planet->mass;
91         }
92 }
93
94 void draw_planet(SDL_Surface *screen, struct planet *p,
95                  const struct camera *cam)
96 {
97         struct vector pos;
98         float radius = p->radius * cam->zoom;
99         float r2 = radius * radius;
100         int x, x_start, y, x_end;
101
102         vector_sub(&p->pos, &cam->pos, &pos);
103         vector_scale(&pos, cam->zoom, &pos);
104         pos.x += screen->w / 2;
105         pos.y += screen->h / 2;
106
107         y = MAX(pos.y - radius, 0);
108
109         if (radius * 2 <= 1) {
110                 if (pos.x >= 0 && pos.x < screen->w &&
111                     pos.y >= 0 && pos.y < screen->h)
112                         putpixel(screen, (int)pos.x, (int)pos.y,
113                                  p->r, p->g, p->b);
114                 return;
115         }
116
117         for (; y < MIN(pos.y + radius, screen->h); y++) {
118                 int offset;
119                 unsigned char *buf = screen->pixels;
120                 float y2 = (y - pos.y);
121
122                 y2 = sqrt(r2 - y2 * y2);
123                 x_start = pos.x - y2;
124                 x_end = pos.x + y2;
125                 x_start = MAX(0, x_start);
126                 x_end = MIN(x_end, screen->w);
127
128                 offset = y * screen->pitch + x_start * 4;
129                 for (x = x_start; x < x_end; x++) {
130                         buf[offset++] = p->b;
131                         buf[offset++] = p->g;
132                         buf[offset++] = p->r;
133                         offset++;
134                 }
135         }
136 }
137
138 int gravitize_planets(struct planet *a, struct planet *b, const double time)
139 {
140         struct vector distance, sum;
141         double dist, f, acc;
142
143         vector_sub(&a->pos, &b->pos, &distance);
144
145         dist = vector_abs(&distance);
146
147         /* Return true in case of a collision */
148         if (dist < (a->radius + b->radius))
149                 return 1;
150
151         vector_div(&distance, dist, &distance);
152
153         f = a->mass * b->mass / (dist * dist) * time;
154
155         acc = f / b->mass;
156         vector_scale(&distance, acc, &sum);
157         vector_add(&b->speed, &sum, &b->speed);
158
159         acc = f / a->mass;
160         vector_scale(&distance, acc, &sum);
161         vector_sub(&a->speed, &sum, &a->speed);
162
163         return 0;
164 }
165
166 /*
167  * Merge planets a and b into planet a
168  *
169  * It is left for the caller to deal with the scrap planet b
170  */
171 static void _merge_planets(struct planet *a, struct planet *b)
172 {
173         struct vector pa, pb, p;
174
175         vector_scale(&a->speed, a->mass, &pa);
176         vector_scale(&b->speed, b->mass, &pb);
177         vector_add(&pa, &pb, &p);
178
179         if (a->mass < b->mass)
180                 a->pos = b->pos;
181
182         a->mass += b->mass;
183         reshape_planet(a);
184         vector_div(&p, a->mass, &a->speed);
185 }
186
187 /*
188  * Merge planets a and b into a the new planet a, which pointer is
189  * returned to the caller. Planet b is removed from the linked list
190  * and it's memory is freed. The merged planet will retain in the
191  * list.
192  */
193 struct planet *merge_planets(struct planet *a, struct planet *b)
194 {
195         _merge_planets(a, b);
196
197         list_del(&b->list);
198         free(b);
199         return a;
200 }
201
202 void move_planet(struct planet *p, const double time)
203 {
204         struct vector tmp;
205         vector_scale(&p->speed, time, &tmp);
206         vector_add(&p->pos, &tmp, &p->pos);
207 }
208
209 void print_planet(const struct planet *p)
210 {
211         printf("pos: (%f,%f), speed: (%f,%f), mass: %f, radius %f\n",
212                p->pos.x, p->pos.y, p->speed.x, p->speed.y, p->mass, p->radius);
213 }