Feat: generate freely falling Schwarzschild camera tracks

Integrate timelike geodesics and Fermi-Walker tetrads in ingoing Kerr-Schild coordinates, sampled at a configurable proper-time cadence.

Add movie-track-samples to preserve CSV events as frames. Document usage and singularity guards, and cover analytic orbits, transport convergence, sampling, and CSV rendering.
This commit is contained in:
wyj committed 2026-09-06 05:56:51 -04:00
1 parent 8d011b7ec5
commit b69c9cfd45
9 files changed
+446 -3

No files matched your search

+13 -3
View File
@@ -44,6 +44,7 @@ typedef struct {
char hdr_output_path[PATH_MAX];
#endif
const char *observer_track_path;
int movie_track_samples;
const char *frames_dir;
const char *frames_prefix;
const char *write_minkowski_accel_track_path;
@@ -299,6 +300,8 @@ static int parse_args(int argc, char **argv, Settings *s,
*write_path = argv[++i];
else if (!strcmp(argv[i], "--observer-track") && i + 1 < argc)
s->observer_track_path = argv[++i];
else if (!strcmp(argv[i], "--movie-track-samples"))
s->movie_track_samples = 1;
else if (!strcmp(argv[i], "--frames-dir") && i + 1 < argc)
s->frames_dir = argv[++i];
else if (!strcmp(argv[i], "--frames-prefix") && i + 1 < argc)
@@ -385,6 +388,7 @@ static void print_help(const char *program) {
" --draw-mesh Draw the final lens mesh overlay (default: disabled)\n"
"\nMovie and observer track:\n"
" --observer-track PATH Observer worldline/tetrad CSV for movie rendering (default: disabled)\n"
" --movie-track-samples One frame per CSV row; ignores start-time/duration/fps (default: disabled)\n"
" --frames-dir DIR Write a movie image sequence to this directory (default: disabled)\n"
" --frames-prefix NAME Movie frame filename prefix (default: frame)\n"
" --start-time T Movie start coordinate time (default: 0)\n"
@@ -508,6 +512,11 @@ static GeodesicTraceConfig trace_config(void) {
}
static int resolve_camera(Settings *s) {
if (s->movie_track_samples && (!s->observer_track_path || !s->frames_dir ||
s->lens_map_input_path)) {
fputs("--movie-track-samples requires --observer-track and --frames-dir, without --lens-map-input.\n", stderr);
return -1;
}
const int camera_specified = s->position_specified || s->look_specified ||
s->radius_specified || s->velocity_specified || s->roll_specified;
if (s->position_specified && s->radius_specified) {
@@ -798,8 +807,9 @@ static int render_movie(const Settings *s, StarCatalog *catalog,
int result = -1;
if (s->observer_track_path == NULL ||
observer_track_load_csv(&track, s->observer_track_path) ||
movie_init(&movie, &track, s->movie_start_time, s->movie_duration,
s->movie_fps) ||
(s->movie_track_samples ? movie_init_track_samples(&movie, &track) :
movie_init(&movie, &track, s->movie_start_time, s->movie_duration,
s->movie_fps)) ||
movie_build_coarse_meshes(&movie, s->width, s->height,
s->coarse_cell_pixels, s->horizontal_fov_deg))
goto done;
@@ -990,7 +1000,7 @@ int main(int argc, char **argv) {
"[--draw-mesh] [--write-catalog PATH] "
"[--catalog-load-workers N] "
"[--observer-track PATH --frames-dir DIR --frames-prefix NAME "
"--start-time T --duration T --fps N] "
"--start-time T --duration T --fps N | --movie-track-samples] "
"[--proper-acceleration A --write-minkowski-accel-track PATH]\n",
argv[0]);
return 2;
+22
View File
@@ -3,6 +3,28 @@
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
int movie_init_track_samples(Movie *movie, const ObserverTrack *track) {
if (!movie || !track || !track->samples || !track->count ||
track->count > SIZE_MAX / sizeof *movie->frames)
return -1;
*movie = (Movie){0};
movie->frames = calloc(track->count, sizeof *movie->frames);
if (!movie->frames) return -1;
movie->frame_count = track->count;
for (size_t i = 0; i < track->count; ++i) {
const ObserverSample *s = &track->samples[i];
MovieFrame *f = &movie->frames[i];
f->frame_id = i;
f->coordinate_time = f->observer.coordinate_time = s->coordinate_time;
f->proper_time = s->proper_time;
memcpy(f->observer.coordinate_position, s->coordinate_position,
sizeof s->coordinate_position);
memcpy(f->observer.tetrad, s->tetrad, sizeof s->tetrad);
}
return 0;
}
int movie_init(Movie *movie, const ObserverTrack *track, double start_time,
double duration, double frames_per_second) {
+2
View File
@@ -21,6 +21,8 @@ typedef struct {
int movie_init(Movie *movie, const ObserverTrack *track, double start_time,
double duration, double frames_per_second);
/* Preserve the supplied event and tetrad exactly: one CSV sample per frame. */
int movie_init_track_samples(Movie *movie, const ObserverTrack *track);
int movie_build_coarse_meshes(Movie *movie, int width, int height,
int cell_pixels, double horizontal_fov_deg);
void movie_destroy(Movie *movie);