Merge branch 'master' of git://github.com/BTAxis/naev into testmission
[naev.git] / src / physics.c
bloba8bee28623625301bb51b3185730320db1c023cf
1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
7 #include "physics.h"
9 #include "naev.h"
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
15 #include "log.h"
19 * M I S C
21 static double angle_cleanup( double a )
23 double na;
24 if (FABS(a) >= 2.*M_PI) {
25 na = fmod(a, 2.*M_PI);
26 if (a < 0.)
27 na += 2.*M_PI;
28 return na;
30 return a;
32 /**
33 * @brief Gets the difference between two angles.
35 * @param ref Reference angle.
36 * @param a Angle to get distance from ref.
38 double angle_diff( const double ref, double a )
40 double d;
41 double a1, a2;
43 /* Get angles. */
44 a1 = angle_cleanup(ref);
45 a2 = angle_cleanup(a);
46 d = a2 - a1;
48 /* Filter offsets. */
49 d = (d < M_PI) ? d : d - 2.*M_PI;
50 d = (d > -M_PI) ? d : d + 2.*M_PI;
51 return d;
53 /**
54 * @brief Limits the speed of an object.
56 * @param vel Velocity vector to limit.
57 * @param speed Maximum speed.
58 * @param dt Current delta tick.
60 void limit_speed( Vector2d* vel, const double speed, const double dt )
62 double vmod;
64 vmod = VMOD(*vel);
65 if (vmod > speed) /* shouldn't go faster */
66 vect_pset( vel, (vmod-speed)*(1. - dt*3.) + speed, VANGLE(*vel) );
72 * V E C T O R 2 D
75 /**
76 * @brief Set the vector value using cartesian coordinates
78 * @param v Vector to set.
79 * @param x X value for vector.
80 * @param y Y value for vector.
82 void vect_cset( Vector2d* v, const double x, const double y )
84 v->x = x;
85 v->y = y;
86 v->mod = MOD(x,y);
87 v->angle = ANGLE(x,y);
91 /**
92 * @brief Creates a minimal vector only valid for blitting and not other operations.
94 * @param v Vector to set.
95 * @param x X value for vector.
96 * @param y Y value for vector.
98 void vect_csetmin( Vector2d* v, const double x, const double y )
100 v->x = x;
101 v->y = y;
106 * @brief Set the vector value using polar coordinates.
108 * @param v Vector to set.
109 * @param mod Modulus of the vector.
110 * @param angle Angle of the vector.
112 void vect_pset( Vector2d* v, const double mod, const double angle )
114 v->mod = mod;
115 v->angle = angle;
116 v->x = v->mod*cos(v->angle);
117 v->y = v->mod*sin(v->angle);
122 * @brief Copies vector src to dest.
124 * @param dest Destination vector.
125 * @param src Vector to copy.
127 void vectcpy( Vector2d* dest, const Vector2d* src )
129 dest->x = src->x;
130 dest->y = src->y;
131 dest->mod = src->mod;
132 dest->angle = src->angle;
137 * @brief Sets a vector to NULL.
139 * @param v Vector to set to NULL.
141 void vectnull( Vector2d* v )
143 v->x = 0.;
144 v->y = 0.;
145 v->mod = 0.;
146 v->angle = 0.;
151 * @brief Get the direction pointed to by two vectors (from ref to v).
153 * @param ref Reference vector.
154 * @param v Vector to get angle from reference vector.
155 * @return Angle between ref and v.
157 double vect_angle( const Vector2d* ref, const Vector2d* v )
159 double x,y;
161 x = v->x - ref->x;
162 y = v->y - ref->y;
164 return ANGLE( x, y );
169 * @brief Adds x and y to the current vector
171 * @param v Vector to add x and y to.
172 * @param x X value to add to vector.
173 * @param y Y value to add to vector.
175 void vect_cadd( Vector2d* v, const double x, const double y )
177 v->x += x;
178 v->y += y;
179 v->mod = MOD(v->x,v->y);
180 v->angle = ANGLE(v->x,v->y);
185 * @brief Mirrors a vector off another, stores results in vector.
187 * @param r Resulting vector of the reflection.
188 * @param v Vector to reflect.
189 * @param n Normal to reflect off of.
191 void vect_reflect( Vector2d* r, Vector2d* v, Vector2d* n )
193 double dot;
195 dot = vect_dot( v, n );
196 r->x = v->x - ((2. * dot) * n->x);
197 r->y = v->y - ((2. * dot) * n->y);
198 r->mod = MOD(r->x,r->y);
199 r->angle = MOD(r->x,r->y);
204 * @brief Vector dot product.
206 * @param a Vector 1 for dot product.
207 * @param b Vector 2 for dot product.
208 * @return Dot product of vectors.
210 double vect_dot( Vector2d* a, Vector2d* b )
212 return a->x * b->x + a->y * b->y;
216 * S O L I D
218 #if HAS_FREEBSD
220 * @brief Updates the solid's position using an euler integration.
222 * Simple method
224 * d^2 x(t) / d t^2 = a, a = constant (acceleration)
225 * x'(0) = v, x(0) = p
227 * d x(t) / d t = a*t + v, v = constant (initial velocity)
228 * x(t) = a/2*t + v*t + p, p = constant (initial position)
230 * since d t isn't actually diferential this gives us ERROR!
231 * so watch out with big values for dt
234 static void simple_update (Solid *obj, const double dt)
236 double px,py, vx,vy, ax;
238 /* make sure angle doesn't flip */
239 obj->dir += M_PI/180.*obj->dir_vel*dt;
240 if (obj->dir >= 2*M_PI)
241 obj->dir -= 2*M_PI;
242 if (obj->dir < 0.)
243 obj->dir += 2*M_PI;
245 /* Initial positions. */
246 px = obj->pos.x;
247 py = obj->pos.y;
248 vx = obj->vel.x;
249 vy = obj->vel.y;
251 if (obj->force_x != 0.) { /* force applied on object */
252 ax = obj->force_x / obj->mass;
253 /*ay = obj->force_x / obj->mass;*/
255 vx += ax*cos(obj->dir) * dt;
256 vy += ax*sin(obj->dir) * dt;
258 px += vx*dt + 0.5*ax * dt*dt;
259 py += vy*dt; /* + 0.5*ay * dt*dt; */
261 obj->vel.mod = MOD(vx,vy);
262 obj->vel.angle = ANGLE(vx,vy);
264 else {
265 px += vx*dt;
266 py += vy*dt;
269 /* Update position and velocity. */
270 vect_cset( &obj->vel, vx, vy );
271 vect_cset( &obj->pos, px, py );
273 #endif /* HAS_FREEBSD */
277 * @fn static void rk4_update (Solid *obj, const double dt)
279 * @brief Runge-Kutta method of updating a solid based on it's acceleration.
281 * Runge-Kutta 4 method
283 * d^2 x(t) / d t^2 = a, a = constant (acceleration)
284 * x'(0) = v, x(0) = p
285 * x'' = f( t, x, x' ) = ( x' , a )
287 * x_{n+1} = x_n + h/6 (k1 + 2*k2 + 3*k3 + k4)
288 * h = (b-a)/2
289 * k1 = f(t_n, X_n ), X_n = (x_n, x'_n)
290 * k2 = f(t_n + h/2, X_n + h/2*k1)
291 * k3 = f(t_n + h/2, X_n + h/2*k2)
292 * k4 = f(t_n + h, X_n + h*k3)
294 * x_{n+1} = x_n + h/6*(6x'_n + 3*h*a, 4*a)
297 * Main advantage comes thanks to the fact that NAEV is on a 2d plane.
298 * Therefore RK chops it up in chunks and actually creates a tiny curve
299 * instead of aproximating the curve for a tiny straight line.
301 #if !HAS_FREEBSD
302 #define RK4_MIN_H 0.01 /**< Minimal pass we want. */
303 static void rk4_update (Solid *obj, const double dt)
305 int i, N; /* for iteration, and pass calcualtion */
306 double h, px,py, vx,vy; /* pass, and position/velocity values */
307 double ix,iy, tx,ty, ax; /* initial and temporary cartesian vector values */
309 /* Initial RK parameters. */
310 N = (dt>RK4_MIN_H) ? (int)(dt/RK4_MIN_H) : 1 ;
311 h = dt / (double)N; /* step */
313 /* Initial positions and velocity. */
314 px = obj->pos.x;
315 py = obj->pos.y;
316 vx = obj->vel.x;
317 vy = obj->vel.y;
319 if (obj->force_x > 0.) { /* force applied on object */
321 /* Movement Quantity Theorem: m*a = \sum f */
322 ax = obj->force_x / obj->mass;
323 /*ay = obj->force.x / obj->mass;*/
325 for (i=0; i < N; i++) { /* iterations */
327 /* x component */
328 tx = ix = vx;
329 tx += 2.*ix + h*tx;
330 tx += 2.*ix + h*tx;
331 tx += ix + h*tx;
332 tx *= h/6.;
334 px += tx;
335 vx += ax*cos(obj->dir) * h;
337 /* y component */
338 ty = iy = vy;
339 ty += 2.*(iy + h/2.*ty);
340 ty += 2.*(iy + h/2.*ty);
341 ty += iy + h*ty;
342 ty *= h/6.;
344 py += ty;
345 vy += ax*sin(obj->dir) * h;
347 /* rotation. */
348 obj->dir += M_PI/180.*obj->dir_vel*h;
350 vect_cset( &obj->vel, vx, vy );
352 else { /* euler method -> p = v*t + 0.5*a*t^2 (no accel, so no error) */
353 px += dt*vx;
354 py += dt*vy;
355 obj->dir += M_PI/180.*obj->dir_vel*dt;
357 vect_cset( &obj->pos, px, py );
359 /* Sanity check. */
360 if (obj->dir >= 2.*M_PI)
361 obj->dir -= 2.*M_PI;
362 else if (obj->dir < 0.)
363 obj->dir += 2.*M_PI;
365 #endif /* !HAS_FREEBSD */
369 * @brief Initializes a new Solid.
371 * @param dest Solid to initialize.
372 * @param mass Mass to set solid to.
373 * @param dir Solid initial direction.
374 * @param pos Initial solid position.
375 * @param vel Initial solid velocity.
377 void solid_init( Solid* dest, const double mass, const double dir,
378 const Vector2d* pos, const Vector2d* vel )
380 memset(dest, 0, sizeof(Solid));
382 dest->mass = mass;
384 /* Set direction velocity. */
385 dest->dir_vel = 0.;
387 /* Set force. */
388 dest->force_x = 0.;
389 /*dest->force_y = 0.;*/
391 /* Set direction. */
392 dest->dir = dir;
393 if ((dest->dir > 2.*M_PI) || (dest->dir < 0.))
394 dest->dir = fmod(dest->dir, 2.*M_PI);
396 /* Set velocity. */
397 if (vel == NULL)
398 vectnull( &dest->vel );
399 else
400 vectcpy( &dest->vel, vel );
402 /* Set position. */
403 if (pos == NULL)
404 vectnull( &dest->pos );
405 else
406 vectcpy( &dest->pos, pos);
409 * FreeBSD seems to have a bug with optimizations in rk4_update causing it to
410 * eventually become NaN.
412 #if HAS_FREEBSD
413 dest->update = simple_update;
414 #else /* HAS_FREEBSD */
415 dest->update = rk4_update;
416 #endif /* HAS_FREEBSD */
421 * @brief Creates a new Solid.
423 * @param mass Mass to set solid to.
424 * @param dir Solid initial direction.
425 * @param pos Initial solid position.
426 * @param vel Initial solid velocity.
427 * @return A newly created solid.
429 Solid* solid_create( const double mass, const double dir,
430 const Vector2d* pos, const Vector2d* vel )
432 Solid* dyn = malloc(sizeof(Solid));
433 if (dyn==NULL) ERR("Out of Memory");
434 solid_init( dyn, mass, dir, pos, vel );
435 return dyn;
440 * @brief Frees an existing solid.
442 * @param src Solid to free.
444 void solid_free( Solid* src )
446 free(src);