From b0fdf1faca3115d31f7460f34c417ca1022086dd Mon Sep 17 00:00:00 2001 From: Yingjie Wang Date: Thu, 27 Aug 2026 04:25:45 -0400 Subject: [PATCH] Output: default to PNG with PPM fallback --- Makefile | 7 +++++-- README.md | 22 ++++++++++++---------- assets/2mass/processed/all_sky/README.md | 7 ++++--- src/main.c | 16 +++++++++++++--- src/optics.c | 15 ++++++++++----- src/optics.h | 2 +- video_rendering_plan.md | 4 ++-- 7 files changed, 47 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index fc7dc5e..fd79a1d 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,15 @@ CC ?= cc CFLAGS ?= -std=c11 -O2 -Wall -Wextra -Wpedantic OPENMP_FLAGS ?= -fopenmp LDLIBS ?= -lm -ENABLE_PNG ?= 0 +ENABLE_PNG ?= 1 SPACETIME ?= minkowski ifeq ($(ENABLE_PNG),1) CPPFLAGS += -DENABLE_PNG LDLIBS += -lpng +IMAGE_EXT := png +else +IMAGE_EXT := ppm endif COMMON_SOURCES := $(filter-out src/main.c src/spacetime_minkowski.c src/spacetime_schwarzschild.c,$(wildcard src/*.c)) @@ -40,7 +43,7 @@ build: run: $(TARGET) mkdir -p output/imgs - ./$(TARGET) --catalog assets/sky_grid_5deg.csv --output output/imgs/$(SPACETIME)_sky.ppm + ./$(TARGET) --catalog assets/sky_grid_5deg.csv --output output/imgs/$(SPACETIME)_sky.$(IMAGE_EXT) minkowski: $(MAKE) SPACETIME=minkowski all diff --git a/README.md b/README.md index f1e2959..760ad84 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,7 @@ which equalizes their CIE/linear-sRGB luminance under the renderer's blackbody integration. Longitude boundaries belong to the sector to their east and the equator to the northern hemisphere, so boundary stars have a deterministic color. -The output is a binary PPM at `output/imgs/minkowski_sky.ppm`; it can be inspected by -most image viewers or converted to PNG with ImageMagick. +The default output is PNG at `output/imgs/minkowski_sky.png`. ## Movie PNG sequence (Phase A) @@ -33,7 +32,7 @@ Minkowski benchmark—two coordinate seconds at 30 fps, accelerating from rest to about `0.95c`—then render its numbered PNG frames: ```sh -make clean && make ENABLE_PNG=1 +make clean && make mkdir -p output/imgs ./build/minkowski_sky --write-minkowski-accel-track output/minkowski_accel_2s.csv \ --duration 2 --fps 30 --proper-acceleration 1.52 @@ -55,15 +54,18 @@ synthetic test catalog uses global default exposure `1e-3`; the accelerated benchmark explicitly uses `1e-5` because its physical Doppler blue shift otherwise clips the later frames. -PNG output is optional so the default build has no `libpng` dependency. Build -with `make ENABLE_PNG=1`, then select it with a `.png` output path: +PNG is the default output and the default build links `libpng`: ```sh -make clean && make ENABLE_PNG=1 +make clean && make mkdir -p output/imgs ./build/minkowski_sky --output output/imgs/minkowski_sky.png ``` +If `libpng` is unavailable, rebuild with `make clean && make ENABLE_PNG=0`. +That intentionally selects the binary-PPM fallback, whose default path is +`output/imgs/minkowski_sky.ppm`; pass a `.ppm` path for explicit output. + Run the flat-spacetime geodesic regression with: ```sh @@ -79,7 +81,7 @@ Kerr--Schild coordinates (regular at the horizon), then render the test catalog to PNG: ```sh -make clean && make SPACETIME=schwarzschild ENABLE_PNG=1 +make clean && make SPACETIME=schwarzschild mkdir -p output/imgs ./build/schwarzschild_sky --catalog assets/sky_grid_5deg.csv \ --width 640 --height 360 --coarse-cell-pixels 8 --fov-deg 60 \ @@ -113,13 +115,13 @@ Useful options: ```sh ./build/minkowski_sky --width 1920 --height 1080 --fov-deg 30 \ - --catalog assets/sky_grid_5deg.csv --output output/imgs/frame.ppm + --catalog assets/sky_grid_5deg.csv --output output/imgs/frame.png ./build/minkowski_sky --catalog assets/2mass/processed/2mass_psc_m31_0p5deg_stars.csv \ --look-ra-deg 10.6847083 --look-dec-deg 41.26875 --fov-deg 1.8 \ - --exposure 1e15 --output output/imgs/2mass_m31.ppm + --exposure 1e15 --output output/imgs/2mass_m31.png ./build/minkowski_sky --catalog assets/2mass/processed/2mass_psc_m44_1p0deg_stars.csv \ --look-ra-deg 129.99165 --look-dec-deg 19.54139 --fov-deg 2.0 \ - --exposure 1e15 --width 1920 --height 1920 --output output/imgs/2mass_m44.ppm + --exposure 1e15 --width 1920 --height 1920 --output output/imgs/2mass_m44.png ./build/minkowski_sky --write-catalog assets/sky_grid_5deg.csv ``` diff --git a/assets/2mass/processed/all_sky/README.md b/assets/2mass/processed/all_sky/README.md index 85c6ee5..f6cbc62 100644 --- a/assets/2mass/processed/all_sky/README.md +++ b/assets/2mass/processed/all_sky/README.md @@ -38,7 +38,7 @@ tile tables after successful cleaning unless `--keep-raw` is passed. Use the directory mode rather than `--catalog` for this partitioned dataset: ```sh -make ENABLE_PNG=1 SPACETIME=minkowski all +make SPACETIME=minkowski all ./build/minkowski_sky --all-sky-catalog assets/2mass/processed/all_sky \ --fov-deg 2 --look-ra-deg 180 --look-dec-deg -75 \ --exposure 1e15 \ @@ -87,8 +87,9 @@ with the rotating observer fixture: Missing tiles are skipped until the downloader has produced them. Fully contained tiles skip per-star source-triangle containment; partial tiles retain that exact check. For 2MASS apparent-solid-angle amplitudes, start with -`--exposure 1e15`; use PNG (`make ENABLE_PNG=1`) for substantially smaller -single-frame output than binary PPM. +`--exposure 1e15`; PNG is the default output format. If libpng is unavailable, +use the PPM fallback with `make clean && make ENABLE_PNG=0` and a `.ppm` +output path. The two existing fields imply roughly 15--17 GiB of cleaned CSV for the full PSC. Their raw response rows imply 70--75 GiB if every source appeared once. diff --git a/src/main.c b/src/main.c index 77aa8d0..d35b261 100644 --- a/src/main.c +++ b/src/main.c @@ -96,6 +96,11 @@ static int parse_moffat_beta(const char *text, double *value) { static int parse_args(int argc, char **argv, Settings *s, const char **write_path) { +#ifdef ENABLE_PNG + const char *default_output_path = "output/imgs/minkowski_sky.png"; +#else + const char *default_output_path = "output/imgs/minkowski_sky.ppm"; +#endif *s = (Settings){.width = 1280, .height = 720, .coarse_cell_pixels = 16, @@ -106,7 +111,7 @@ static int parse_args(int argc, char **argv, Settings *s, .observer_radius = 30.0, .psf = {2.7, 4.5}, .catalog_path = "assets/sky_grid_5deg.csv", - .output_path = "output/imgs/minkowski_sky.ppm", + .output_path = default_output_path, .frames_prefix = "frame", .movie_duration = 2.0, .movie_fps = 30.0, @@ -246,13 +251,18 @@ static int render_frame(const Settings *s, StarCatalog *catalog, static int frame_output_path(char path[PATH_MAX], const Settings *s, size_t frame_id) { +#ifdef ENABLE_PNG + const char *extension = "png"; +#else + const char *extension = "ppm"; +#endif struct stat st; if (s->frames_dir == NULL || s->frames_prefix == NULL || strchr(s->frames_prefix, '/') != NULL || stat(s->frames_dir, &st) || !S_ISDIR(st.st_mode)) return -1; - const int written = snprintf(path, PATH_MAX, "%s/%s_%06zu.png", s->frames_dir, - s->frames_prefix, frame_id); + const int written = snprintf(path, PATH_MAX, "%s/%s_%06zu.%s", s->frames_dir, + s->frames_prefix, frame_id, extension); return written < 0 || written >= PATH_MAX ? -1 : 0; } diff --git a/src/optics.c b/src/optics.c index 8e7befb..ad59ebc 100644 --- a/src/optics.c +++ b/src/optics.c @@ -120,6 +120,7 @@ static unsigned char tonemap_channel(double hdr_value) return (unsigned char)lround(255.0 * clamp(display, 0.0, 1.0)); } +#ifndef ENABLE_PNG static int write_tonemapped_ppm(const char *path, const double *hdr, int width, int height) { @@ -132,6 +133,7 @@ static int write_tonemapped_ppm(const char *path, const double *hdr, int width, } return fclose(file) == 0 ? 0 : -1; } +#endif #ifdef ENABLE_PNG static int write_tonemapped_png(const char *path, const double *hdr, int width, @@ -171,13 +173,16 @@ done: int write_tonemapped_image(const char *path, const double *hdr, int width, int height) { const size_t path_length = strlen(path); - if (path_length >= 4 && strcmp(path + path_length - 4, ".png") == 0) { #ifdef ENABLE_PNG + if (path_length >= 4 && strcmp(path + path_length - 4, ".png") == 0) return write_tonemapped_png(path, hdr, width, height); + fputs("PNG output is enabled; use a .png output path.\n", stderr); + return -1; #else - fputs("PNG output is disabled; rebuild with make ENABLE_PNG=1.\n", stderr); - return -1; + if (path_length >= 4 && strcmp(path + path_length - 4, ".ppm") == 0) + return write_tonemapped_ppm(path, hdr, width, height); + fputs("PNG output is unavailable; use a .ppm output path or rebuild with libpng.\n", + stderr); + return -1; #endif - } - return write_tonemapped_ppm(path, hdr, width, height); } diff --git a/src/optics.h b/src/optics.h index f618806..40ea112 100644 --- a/src/optics.h +++ b/src/optics.h @@ -13,7 +13,7 @@ LinearRgb blackbody_to_linear_rgb(double temperature_K); void splat_moffat(double *hdr, int width, int height, double x, double y, LinearRgb color, double flux, const PointSpreadFunction *psf); -/* Writes PPM by default; a .png path requires a build with ENABLE_PNG=1. */ +/* Writes PNG when built with libpng; non-libpng builds use PPM fallback. */ int write_tonemapped_image(const char *path, const double *hdr, int width, int height); diff --git a/video_rendering_plan.md b/video_rendering_plan.md index 38314ca..286793a 100644 --- a/video_rendering_plan.md +++ b/video_rendering_plan.md @@ -41,8 +41,8 @@ SampleRequest -> RayPool (SoA, inactive / active / terminated) 1. 新增 `observer_track.h/.c`,将目前的 `ObserverState` 提升为可插值的 `ObserverTrack`:每个 sample 保存 `t_coordinate`、`tau`、`x[3]` 和完整 tetrad。提供加载、严格单调时间校验、区间查找、插值和销毁函数。原有固定 observer 构造函数保留,作为只含一个 sample 的兼容快捷方式。 2. 第一版轨迹输入采用人可审阅的 21 列 CSV,字段固定为 `t,tau,x,y,z,e0t,e0x,...,e3z`;不在 renderer 内从“速度曲线”临时猜 tetrad。loader 做完整性、有限数与严格单调时间检查;tetrad 的 metric 正交归一由轨迹生成器和 backend-aware regression 验证。另提供生成器工具/模式为测试写出 CSV。这样 NR 生产轨迹、解析测试轨迹和以后离线 trajectory generator 使用完全相同的 renderer 输入。 3. 加入 `movie.h/.c`,定义 `MovieSettings`、`MovieFrame`(`frame_id`、`t_camera`、插值得到的 observer state、mesh、HDR image)与 `Movie`。用 `start-time`、`duration`、`fps` 生成 frame 时间表;先拒绝轨迹覆盖范围外的时间。 -4. 将 `render_frame()` 中与单帧无关的工作拆成“初始化 mesh / 安装 endpoint / splat HDR / 写出”四个明确步骤。保持现有单帧命令和 PPM/PNG 行为可用;movie 模式只是调用同一套 frame rendering 与 optics。 -5. 新增 `output.h/.c`,从 `optics` 移出文件命名和写图逻辑。PNG 仍复用当前 libpng 路径,movie PNG 命名固定为 `/_%06u.png`,例如 `output/imgs/minkowski_accel_000042.png`。在创建前验证目录、prefix 与帧号范围,写入失败应指出 frame id 和路径。 +4. 将 `render_frame()` 中与单帧无关的工作拆成“初始化 mesh / 安装 endpoint / splat HDR / 写出”四个明确步骤。默认构建写 PNG;未启用 libpng 时单帧和 movie 序列统一退回 PPM。movie 模式只是调用同一套 frame rendering 与 optics。 +5. 新增 `output.h/.c`,从 `optics` 移出文件命名和写图逻辑。默认 movie PNG 仍复用当前 libpng 路径,命名固定为 `/_%06u.png`,例如 `output/imgs/minkowski_accel_000042.png`;无 libpng 的 PPM fallback 只替换扩展名。创建前验证目录、prefix 与帧号范围,写入失败应指出 frame id 和路径。 验收:原有 `make test` 和两种单帧 backend 的输出保持不变;新测试验证 CSV 拒绝缺列、非有限数与非单调时间,frame 时间表精确包含首帧且不越界,PNG 序号无冲突。此阶段不要求 FFmpeg,也不改变 ray tracing 的调度方式。