2 * See Licensing and Copyright notice in naev.h
21 static double angle_cleanup( double a
)
24 if (FABS(a
) >= 2.*M_PI
) {
25 na
= fmod(a
, 2.*M_PI
);
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
)
44 a1
= angle_cleanup(ref
);
45 a2
= angle_cleanup(a
);
49 d
= (d
< M_PI
) ? d
: d
- 2.*M_PI
;
50 d
= (d
> -M_PI
) ? d
: d
+ 2.*M_PI
;
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
)
65 if (vmod
> speed
) /* shouldn't go faster */
66 vect_pset( vel
, (vmod
-speed
)*(1. - dt
*3.) + speed
, VANGLE(*vel
) );
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
)
87 v
->angle
= ANGLE(x
,y
);
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
)
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
)
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
)
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
)
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
)
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
)
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
)
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
;
220 * @brief Updates the solid's position using an euler integration.
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
)
245 /* Initial positions. */
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
);
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)
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.
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. */
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 */
335 vx
+= ax
*cos(obj
->dir
) * h
;
339 ty
+= 2.*(iy
+ h
/2.*ty
);
340 ty
+= 2.*(iy
+ h
/2.*ty
);
345 vy
+= ax
*sin(obj
->dir
) * h
;
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) */
355 obj
->dir
+= M_PI
/180.*obj
->dir_vel
*dt
;
357 vect_cset( &obj
->pos
, px
, py
);
360 if (obj
->dir
>= 2.*M_PI
)
362 else if (obj
->dir
< 0.)
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
));
384 /* Set direction velocity. */
389 /*dest->force_y = 0.;*/
393 if ((dest
->dir
> 2.*M_PI
) || (dest
->dir
< 0.))
394 dest
->dir
= fmod(dest
->dir
, 2.*M_PI
);
398 vectnull( &dest
->vel
);
400 vectcpy( &dest
->vel
, vel
);
404 vectnull( &dest
->pos
);
406 vectcpy( &dest
->pos
, pos
);
409 * FreeBSD seems to have a bug with optimizations in rk4_update causing it to
410 * eventually become NaN.
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
);
440 * @brief Frees an existing solid.
442 * @param src Solid to free.
444 void solid_free( Solid
* src
)