Table of Contents
- src/n_trajectory.c
- Functions
- Detailed Description
- Functions Documentation
- function trajectory_load_segment
- function trajectory_compute_tangents
- function hermite_h11
- function hermite_h10
- function hermite_h01
- function hermite_h00
- function hermite_dh11
- function hermite_dh10
- function hermite_dh01
- function hermite_dh00
- function hermite_d2h11
- function hermite_d2h10
- function hermite_d2h01
- function hermite_d2h00
- Source code
src/n_trajectory.c
Trajectory interpolation and dead reckoning for 2D/3D. More...
Functions
| Name | |
|---|---|
| void | trajectory_load_segment(TRAJECTORY * traj, double time_val) Load the correct spline segment for a given time when using multi-point paths. |
| void | trajectory_compute_tangents(TRAJECTORY * traj) Recompute the Hermite tangent vectors from current start/end states. |
| double | hermite_h11(double s) Hermite basis function h11: weights the end tangent. |
| double | hermite_h10(double s) Hermite basis function h10: weights the start tangent. |
| double | hermite_h01(double s) Hermite basis function h01: weights the end position. |
| double | hermite_h00(double s) Hermite basis function h00: weights the start position. |
| double | hermite_dh11(double s) First derivative of h11: dh11/ds = 3s^2 - 2s. |
| double | hermite_dh10(double s) First derivative of h10: dh10/ds = 3s^2 - 4s + 1. |
| double | hermite_dh01(double s) First derivative of h01: dh01/ds = -6s^2 + 6s. |
| double | hermite_dh00(double s) First derivative of h00: dh00/ds = 6s^2 - 6s. |
| double | hermite_d2h11(double s) Second derivative of h11: d2h11/ds2 = 6s - 2. |
| double | hermite_d2h10(double s) Second derivative of h10: d2h10/ds2 = 6s - 4. |
| double | hermite_d2h01(double s) Second derivative of h01: d2h01/ds2 = -12s + 6. |
| double | hermite_d2h00(double s) Second derivative of h00: d2h00/ds2 = 12s - 6. |
Detailed Description
Trajectory interpolation and dead reckoning for 2D/3D.
Author: Castagnier Mickael
Version: 1.0
Date: 28/02/2026
Implements the "Defeating Lag With Cubic Splines" approach from GameDev.net Article 914 by Nicholas Van Caldwell, using cubic Hermite spline interpolation with quadratic extrapolation.
Cubic Hermite spline formula: P(s) = h00(s)*P0 + h10(s)*M0 + h01(s)*P1 + h11(s)*M1
Where s is normalized time in [0,1], P0/P1 are endpoint positions, M0/M1 are tangent vectors (velocity * duration), and: h00(s) = 2s^3 - 3s^2 + 1 (start position weight) h10(s) = s^3 - 2s^2 + s (start tangent weight) h01(s) = -2s^3 + 3s^2 (end position weight) h11(s) = s^3 - s^2 (end tangent weight)
Functions Documentation
function trajectory_load_segment
static void trajectory_load_segment(
TRAJECTORY * traj,
double time_val
)
Load the correct spline segment for a given time when using multi-point paths.
Parameters:
- traj The trajectory (must have nb_points >= 2)
- time_val The time to find the segment for
Searches forward/backward from current_segment for efficiency with sequential access. Updates traj->start, end, start_time, end_time, duration, and tangents for the found segment.
function trajectory_compute_tangents
static void trajectory_compute_tangents(
TRAJECTORY * traj
)
Recompute the Hermite tangent vectors from current start/end states.
Parameters:
- traj The trajectory to update tangents for
Tangents are: m = velocity * duration (for position) and angular_speed * duration (for orientation)
function hermite_h11
static double hermite_h11(
double s
)
Hermite basis function h11: weights the end tangent.
Parameters:
- s normalized time in [0,1]
Return: h11(s) = s^3 - s^2
function hermite_h10
static double hermite_h10(
double s
)
Hermite basis function h10: weights the start tangent.
Parameters:
- s normalized time in [0,1]
Return: h10(s) = s^3 - 2s^2 + s
function hermite_h01
static double hermite_h01(
double s
)
Hermite basis function h01: weights the end position.
Parameters:
- s normalized time in [0,1]
Return: h01(s) = -2s^3 + 3s^2
function hermite_h00
static double hermite_h00(
double s
)
Hermite basis function h00: weights the start position.
Parameters:
- s normalized time in [0,1]
Return: h00(s) = 2s^3 - 3s^2 + 1
function hermite_dh11
static double hermite_dh11(
double s
)
First derivative of h11: dh11/ds = 3s^2 - 2s.
Parameters:
- s normalized time in [0,1]
Return: dh11(s)
function hermite_dh10
static double hermite_dh10(
double s
)
First derivative of h10: dh10/ds = 3s^2 - 4s + 1.
Parameters:
- s normalized time in [0,1]
Return: dh10(s)
function hermite_dh01
static double hermite_dh01(
double s
)
First derivative of h01: dh01/ds = -6s^2 + 6s.
Parameters:
- s normalized time in [0,1]
Return: dh01(s)
function hermite_dh00
static double hermite_dh00(
double s
)
First derivative of h00: dh00/ds = 6s^2 - 6s.
Parameters:
- s normalized time in [0,1]
Return: dh00(s)
function hermite_d2h11
static double hermite_d2h11(
double s
)
Second derivative of h11: d2h11/ds2 = 6s - 2.
Parameters:
- s normalized time in [0,1]
Return: d2h11(s)
function hermite_d2h10
static double hermite_d2h10(
double s
)
Second derivative of h10: d2h10/ds2 = 6s - 4.
Parameters:
- s normalized time in [0,1]
Return: d2h10(s)
function hermite_d2h01
static double hermite_d2h01(
double s
)
Second derivative of h01: d2h01/ds2 = -12s + 6.
Parameters:
- s normalized time in [0,1]
Return: d2h01(s)
function hermite_d2h00
static double hermite_d2h00(
double s
)
Second derivative of h00: d2h00/ds2 = 12s - 6.
Parameters:
- s normalized time in [0,1]
Return: d2h00(s)
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nilorea/n_trajectory.h"
#include <math.h>
static double hermite_h00(double s) {
return 2.0 * s * s * s - 3.0 * s * s + 1.0;
}
static double hermite_h10(double s) {
return s * s * s - 2.0 * s * s + s;
}
static double hermite_h01(double s) {
return -2.0 * s * s * s + 3.0 * s * s;
}
static double hermite_h11(double s) {
return s * s * s - s * s;
}
static double hermite_dh00(double s) {
return 6.0 * s * s - 6.0 * s;
}
static double hermite_dh10(double s) {
return 3.0 * s * s - 4.0 * s + 1.0;
}
static double hermite_dh01(double s) {
return -6.0 * s * s + 6.0 * s;
}
static double hermite_dh11(double s) {
return 3.0 * s * s - 2.0 * s;
}
static double hermite_d2h00(double s) {
return 12.0 * s - 6.0;
}
static double hermite_d2h10(double s) {
return 6.0 * s - 4.0;
}
static double hermite_d2h01(double s) {
return -12.0 * s + 6.0;
}
static double hermite_d2h11(double s) {
return 6.0 * s - 2.0;
}
static void trajectory_compute_tangents(TRAJECTORY* traj) {
for (int i = 0; i < traj->nb_components; i++) {
traj->m0[i] = traj->start.speed[i] * traj->duration;
traj->m1[i] = traj->end.speed[i] * traj->duration;
traj->m0_orient[i] = traj->start.angular_speed[i] * traj->duration;
traj->m1_orient[i] = traj->end.angular_speed[i] * traj->duration;
}
}
static void trajectory_load_segment(TRAJECTORY* traj, double time_val) {
if (traj->nb_points < 2) return;
int seg = traj->current_segment;
if (seg < 0) seg = 0;
/* search forward from current segment */
while (seg < traj->nb_points - 2 &&
time_val >= traj->points[seg + 1].time_val) {
seg++;
}
/* search backward if needed */
while (seg > 0 && time_val < traj->points[seg].time_val) {
seg--;
}
if (seg != traj->current_segment) {
traj->current_segment = seg;
memcpy(&traj->start, &traj->points[seg].state, sizeof(PHYSICS));
memcpy(&traj->end, &traj->points[seg + 1].state, sizeof(PHYSICS));
traj->start_time = traj->points[seg].time_val;
traj->end_time = traj->points[seg + 1].time_val;
traj->duration = traj->end_time - traj->start_time;
trajectory_compute_tangents(traj);
}
}
TRAJECTORY* trajectory_new(int nb_components) {
if (nb_components != TRAJECTORY_2D && nb_components != TRAJECTORY_3D) {
n_log(LOG_ERR, "nb_components must be TRAJECTORY_2D (2) or TRAJECTORY_3D (3), got %d", nb_components);
return NULL;
}
TRAJECTORY* traj = NULL;
Malloc(traj, TRAJECTORY, 1);
__n_assert(traj, return NULL);
traj->nb_components = nb_components;
traj->mode = TRAJECTORY_BEFORE;
traj->start_time = 0.0;
traj->end_time = 0.0;
traj->duration = 0.0;
traj->current_time = 0.0;
traj->points = NULL;
traj->nb_points = 0;
traj->nb_points_allocated = 0;
traj->current_segment = -1;
return traj;
} /* trajectory_new() */
void trajectory_delete(TRAJECTORY** traj) {
__n_assert(traj, return);
Free((*traj)->points);
Free((*traj));
} /* trajectory_delete() */
int trajectory_set_start(TRAJECTORY* traj, const PHYSICS* state, double time_val) {
__n_assert(traj, return FALSE);
__n_assert(state, return FALSE);
memcpy(&traj->start, state, sizeof(PHYSICS));
traj->start_time = time_val;
traj->duration = traj->end_time - traj->start_time;
if (traj->duration > 0.0) {
trajectory_compute_tangents(traj);
}
return TRUE;
} /* trajectory_set_start() */
int trajectory_set_end(TRAJECTORY* traj, const PHYSICS* state, double time_val) {
__n_assert(traj, return FALSE);
__n_assert(state, return FALSE);
memcpy(&traj->end, state, sizeof(PHYSICS));
traj->end_time = time_val;
traj->duration = traj->end_time - traj->start_time;
if (traj->duration <= 0.0) {
n_log(LOG_ERR, "end_time (%g) must be greater than start_time (%g)", time_val, traj->start_time);
return FALSE;
}
trajectory_compute_tangents(traj);
return TRUE;
} /* trajectory_set_end() */
int trajectory_update(TRAJECTORY* traj, const PHYSICS* new_end, double time_val) {
__n_assert(traj, return FALSE);
__n_assert(new_end, return FALSE);
if (time_val <= traj->end_time) {
n_log(LOG_ERR, "new time (%g) must be greater than current end_time (%g)", time_val, traj->end_time);
return FALSE;
}
/* shift: old end becomes new start */
memcpy(&traj->start, &traj->end, sizeof(PHYSICS));
traj->start_time = traj->end_time;
/* set new end */
memcpy(&traj->end, new_end, sizeof(PHYSICS));
traj->end_time = time_val;
traj->duration = traj->end_time - traj->start_time;
trajectory_compute_tangents(traj);
return TRUE;
} /* trajectory_update() */
int trajectory_compute(TRAJECTORY* traj, double time_val) {
__n_assert(traj, return FALSE);
trajectory_load_segment(traj, time_val);
traj->current_time = time_val;
int nc = traj->nb_components;
if (traj->duration <= 0.0) {
/* no valid interval: just copy start state */
memcpy(&traj->current, &traj->start, sizeof(PHYSICS));
traj->mode = TRAJECTORY_BEFORE;
return TRUE;
}
if (time_val < traj->start_time) {
/* before start: quadratic extrapolation backward from start state */
traj->mode = TRAJECTORY_BEFORE;
double dt = time_val - traj->start_time; /* negative */
for (int i = 0; i < nc; i++) {
traj->current.position[i] = traj->start.position[i] + traj->start.speed[i] * dt + 0.5 * traj->start.acceleration[i] * dt * dt;
traj->current.speed[i] = traj->start.speed[i] + traj->start.acceleration[i] * dt;
traj->current.acceleration[i] = traj->start.acceleration[i];
traj->current.orientation[i] = traj->start.orientation[i] + traj->start.angular_speed[i] * dt + 0.5 * traj->start.angular_acceleration[i] * dt * dt;
traj->current.angular_speed[i] = traj->start.angular_speed[i] + traj->start.angular_acceleration[i] * dt;
traj->current.angular_acceleration[i] = traj->start.angular_acceleration[i];
}
} else if (time_val > traj->end_time) {
/* after end: quadratic extrapolation forward from end state */
traj->mode = TRAJECTORY_EXTRAP;
double dt = time_val - traj->end_time;
for (int i = 0; i < nc; i++) {
traj->current.position[i] = traj->end.position[i] + traj->end.speed[i] * dt + 0.5 * traj->end.acceleration[i] * dt * dt;
traj->current.speed[i] = traj->end.speed[i] + traj->end.acceleration[i] * dt;
traj->current.acceleration[i] = traj->end.acceleration[i];
traj->current.orientation[i] = traj->end.orientation[i] + traj->end.angular_speed[i] * dt + 0.5 * traj->end.angular_acceleration[i] * dt * dt;
traj->current.angular_speed[i] = traj->end.angular_speed[i] + traj->end.angular_acceleration[i] * dt;
traj->current.angular_acceleration[i] = traj->end.angular_acceleration[i];
}
} else {
/* within interval: cubic Hermite spline interpolation */
traj->mode = TRAJECTORY_INTERP;
double s = (time_val - traj->start_time) / traj->duration;
/* Hermite basis function values */
double h00 = hermite_h00(s);
double h10 = hermite_h10(s);
double h01 = hermite_h01(s);
double h11 = hermite_h11(s);
/* first derivatives for velocity */
double dh00 = hermite_dh00(s);
double dh10 = hermite_dh10(s);
double dh01 = hermite_dh01(s);
double dh11 = hermite_dh11(s);
/* second derivatives for acceleration */
double d2h00 = hermite_d2h00(s);
double d2h10 = hermite_d2h10(s);
double d2h01 = hermite_d2h01(s);
double d2h11 = hermite_d2h11(s);
double inv_dur = 1.0 / traj->duration;
double inv_dur2 = inv_dur * inv_dur;
for (int i = 0; i < nc; i++) {
/* position: P(s) = h00*P0 + h10*M0 + h01*P1 + h11*M1 */
traj->current.position[i] = h00 * traj->start.position[i] + h10 * traj->m0[i] + h01 * traj->end.position[i] + h11 * traj->m1[i];
/* velocity: V(t) = P'(s) / duration */
traj->current.speed[i] = (dh00 * traj->start.position[i] + dh10 * traj->m0[i] + dh01 * traj->end.position[i] + dh11 * traj->m1[i]) * inv_dur;
/* acceleration: A(t) = P''(s) / duration^2 */
traj->current.acceleration[i] = (d2h00 * traj->start.position[i] + d2h10 * traj->m0[i] + d2h01 * traj->end.position[i] + d2h11 * traj->m1[i]) * inv_dur2;
/* orientation: same Hermite approach */
traj->current.orientation[i] = h00 * traj->start.orientation[i] + h10 * traj->m0_orient[i] + h01 * traj->end.orientation[i] + h11 * traj->m1_orient[i];
/* angular speed: derivative of orientation spline / duration */
traj->current.angular_speed[i] = (dh00 * traj->start.orientation[i] + dh10 * traj->m0_orient[i] + dh01 * traj->end.orientation[i] + dh11 * traj->m1_orient[i]) * inv_dur;
/* angular acceleration: second derivative / duration^2 */
traj->current.angular_acceleration[i] = (d2h00 * traj->start.orientation[i] + d2h10 * traj->m0_orient[i] + d2h01 * traj->end.orientation[i] + d2h11 * traj->m1_orient[i]) * inv_dur2;
}
}
/* zero out unused components for safety */
for (int i = nc; i < 3; i++) {
traj->current.position[i] = 0.0;
traj->current.speed[i] = 0.0;
traj->current.acceleration[i] = 0.0;
traj->current.orientation[i] = 0.0;
traj->current.angular_speed[i] = 0.0;
traj->current.angular_acceleration[i] = 0.0;
}
return TRUE;
} /* trajectory_compute() */
int trajectory_get_position(TRAJECTORY* traj, double time_val, VECTOR3D out) {
__n_assert(traj, return FALSE);
__n_assert(out, return FALSE);
trajectory_load_segment(traj, time_val);
int nc = traj->nb_components;
if (traj->duration <= 0.0) {
for (int i = 0; i < nc; i++) out[i] = traj->start.position[i];
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
}
if (time_val < traj->start_time) {
double dt = time_val - traj->start_time;
for (int i = 0; i < nc; i++) {
out[i] = traj->start.position[i] + traj->start.speed[i] * dt + 0.5 * traj->start.acceleration[i] * dt * dt;
}
} else if (time_val > traj->end_time) {
double dt = time_val - traj->end_time;
for (int i = 0; i < nc; i++) {
out[i] = traj->end.position[i] + traj->end.speed[i] * dt + 0.5 * traj->end.acceleration[i] * dt * dt;
}
} else {
double s = (time_val - traj->start_time) / traj->duration;
double h00 = hermite_h00(s);
double h10 = hermite_h10(s);
double h01 = hermite_h01(s);
double h11 = hermite_h11(s);
for (int i = 0; i < nc; i++) {
out[i] = h00 * traj->start.position[i] + h10 * traj->m0[i] + h01 * traj->end.position[i] + h11 * traj->m1[i];
}
}
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
} /* trajectory_get_position() */
int trajectory_get_speed(TRAJECTORY* traj, double time_val, VECTOR3D out) {
__n_assert(traj, return FALSE);
__n_assert(out, return FALSE);
trajectory_load_segment(traj, time_val);
int nc = traj->nb_components;
if (traj->duration <= 0.0) {
for (int i = 0; i < nc; i++) out[i] = traj->start.speed[i];
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
}
if (time_val < traj->start_time) {
double dt = time_val - traj->start_time;
for (int i = 0; i < nc; i++) {
out[i] = traj->start.speed[i] + traj->start.acceleration[i] * dt;
}
} else if (time_val > traj->end_time) {
double dt = time_val - traj->end_time;
for (int i = 0; i < nc; i++) {
out[i] = traj->end.speed[i] + traj->end.acceleration[i] * dt;
}
} else {
double s = (time_val - traj->start_time) / traj->duration;
double inv_dur = 1.0 / traj->duration;
double dh00 = hermite_dh00(s);
double dh10 = hermite_dh10(s);
double dh01 = hermite_dh01(s);
double dh11 = hermite_dh11(s);
for (int i = 0; i < nc; i++) {
out[i] = (dh00 * traj->start.position[i] + dh10 * traj->m0[i] + dh01 * traj->end.position[i] + dh11 * traj->m1[i]) * inv_dur;
}
}
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
} /* trajectory_get_speed() */
int trajectory_get_acceleration(TRAJECTORY* traj, double time_val, VECTOR3D out) {
__n_assert(traj, return FALSE);
__n_assert(out, return FALSE);
trajectory_load_segment(traj, time_val);
int nc = traj->nb_components;
if (traj->duration <= 0.0) {
for (int i = 0; i < nc; i++) out[i] = traj->start.acceleration[i];
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
}
if (time_val < traj->start_time || time_val > traj->end_time) {
/* extrapolation: constant acceleration from nearest endpoint */
PHYSICS* ref = (time_val < traj->start_time) ? &traj->start : &traj->end;
for (int i = 0; i < nc; i++) {
out[i] = ref->acceleration[i];
}
} else {
double s = (time_val - traj->start_time) / traj->duration;
double inv_dur2 = 1.0 / (traj->duration * traj->duration);
double d2h00 = hermite_d2h00(s);
double d2h10 = hermite_d2h10(s);
double d2h01 = hermite_d2h01(s);
double d2h11 = hermite_d2h11(s);
for (int i = 0; i < nc; i++) {
out[i] = (d2h00 * traj->start.position[i] + d2h10 * traj->m0[i] + d2h01 * traj->end.position[i] + d2h11 * traj->m1[i]) * inv_dur2;
}
}
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
} /* trajectory_get_acceleration() */
int trajectory_get_orientation(TRAJECTORY* traj, double time_val, VECTOR3D out) {
__n_assert(traj, return FALSE);
__n_assert(out, return FALSE);
trajectory_load_segment(traj, time_val);
int nc = traj->nb_components;
if (traj->duration <= 0.0) {
for (int i = 0; i < nc; i++) out[i] = traj->start.orientation[i];
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
}
if (time_val < traj->start_time) {
double dt = time_val - traj->start_time;
for (int i = 0; i < nc; i++) {
out[i] = traj->start.orientation[i] + traj->start.angular_speed[i] * dt + 0.5 * traj->start.angular_acceleration[i] * dt * dt;
}
} else if (time_val > traj->end_time) {
double dt = time_val - traj->end_time;
for (int i = 0; i < nc; i++) {
out[i] = traj->end.orientation[i] + traj->end.angular_speed[i] * dt + 0.5 * traj->end.angular_acceleration[i] * dt * dt;
}
} else {
double s = (time_val - traj->start_time) / traj->duration;
double h00 = hermite_h00(s);
double h10 = hermite_h10(s);
double h01 = hermite_h01(s);
double h11 = hermite_h11(s);
for (int i = 0; i < nc; i++) {
out[i] = h00 * traj->start.orientation[i] + h10 * traj->m0_orient[i] + h01 * traj->end.orientation[i] + h11 * traj->m1_orient[i];
}
}
for (int i = nc; i < 3; i++) out[i] = 0.0;
return TRUE;
} /* trajectory_get_orientation() */
int trajectory_add_point(TRAJECTORY* traj, const PHYSICS* state, double time_val) {
__n_assert(traj, return FALSE);
__n_assert(state, return FALSE);
/* enforce chronological order */
if (traj->nb_points > 0 && time_val <= traj->points[traj->nb_points - 1].time_val) {
n_log(LOG_ERR, "time_val (%g) must be greater than last point time (%g)",
time_val, traj->points[traj->nb_points - 1].time_val);
return FALSE;
}
/* grow array if needed */
if (traj->nb_points >= traj->nb_points_allocated) {
int new_size = (traj->nb_points_allocated == 0) ? 8 : traj->nb_points_allocated * 2;
__n_assert(new_size > 0, return FALSE);
if (!Realloc(traj->points, TRAJECTORY_POINT, (size_t)new_size)) {
return FALSE;
}
traj->nb_points_allocated = new_size;
}
memcpy(&traj->points[traj->nb_points].state, state, sizeof(PHYSICS));
traj->points[traj->nb_points].time_val = time_val;
traj->nb_points++;
/* when we reach 2 points, load the first segment */
if (traj->nb_points == 2) {
traj->current_segment = -1; /* force reload */
trajectory_load_segment(traj, traj->points[0].time_val);
}
return TRUE;
} /* trajectory_add_point() */
int trajectory_clear_points(TRAJECTORY* traj) {
__n_assert(traj, return FALSE);
traj->nb_points = 0;
traj->current_segment = -1;
memset(&traj->start, 0, sizeof(PHYSICS));
memset(&traj->end, 0, sizeof(PHYSICS));
traj->start_time = 0.0;
traj->end_time = 0.0;
traj->duration = 0.0;
return TRUE;
} /* trajectory_clear_points() */
double trajectory_distance(TRAJECTORY* traj, double time_a, double time_b, int steps) {
__n_assert(traj, return -1.0);
if (steps < 1) steps = 1;
double dist = 0.0;
double dt = (time_b - time_a) / (double)steps;
VECTOR3D prev, curr;
/* nb_components is constrained to TRAJECTORY_2D (2) or TRAJECTORY_3D (3)
* at construction time. Clamp to the VECTOR3D extent so the static
* analyzer can see the bound and the per-component loop below stays
* safely inside the [0,3) array range. */
int nc = traj->nb_components;
if (nc < 2) nc = 2;
if (nc > 3) nc = 3;
trajectory_get_position(traj, time_a, prev);
for (int step = 1; step <= steps; step++) {
double t = time_a + dt * (double)step;
trajectory_get_position(traj, t, curr);
double seg = 0.0;
for (int i = 0; i < nc; i++) {
double d = curr[i] - prev[i];
seg += d * d;
}
dist += sqrt(seg);
for (int i = 0; i < nc; i++) prev[i] = curr[i];
}
return dist;
} /* trajectory_distance() */
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB