git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commite0fb168336b283292275b30507a2297651b1b6b2
parentb4eb9daba5
authorLucas Galante <[email protected]>
date2026-07-14 19:07
feat: --samples flag for --thumbnail

Overrides the 96-spp default — the knob that made the denoiser's
low-sample A/B possible and lets callers trade quality for speed.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01G5djCURa3LVnRU8WacanLC

 src/main.rs      | 6 +++++-
 src/thumbnail.rs | 6 +++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 33dfc4d..5645248 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -71,7 +71,11 @@ fn main() {
             .and_then(|w| w[1].parse::<u32>().ok())
             .unwrap_or(256)
             .clamp(16, 2048);
-        match thumbnail::run(std::path::Path::new(project), std::path::Path::new(out), size) {
+        let samples = args
+            .windows(2)
+            .find(|w| w[0] == "--samples")
+            .and_then(|w| w[1].parse::<u32>().ok());
+        match thumbnail::run(std::path::Path::new(project), std::path::Path::new(out), size, samples) {
             Ok(()) => std::process::exit(0),
             Err(e) => {
                 eprintln!("cce-designer --thumbnail: {e}");
diff --git a/src/thumbnail.rs b/src/thumbnail.rs
index 2dc2a6d..4abcf9a 100644
--- a/src/thumbnail.rs
+++ b/src/thumbnail.rs
@@ -18,8 +18,8 @@ use crate::geometry::{network_sphere_vertices_with_errors, rt_scene_from_verts};
 const SAMPLES: u32 = 96;
 
 /// Render `project` (a project directory or a `state.json` path) to a square
-/// `size`×`size` PNG at `out`.
-pub fn run(project: &Path, out: &Path, size: u32) -> Result<(), String> {
+/// `size`×`size` PNG at `out`, with `samples` paths per pixel (None = 96).
+pub fn run(project: &Path, out: &Path, size: u32, samples: Option<u32>) -> Result<(), String> {
     let state_file = if project.is_dir() { project.join("state.json") } else { project.to_path_buf() };
     let content = std::fs::read_to_string(&state_file)
         .map_err(|e| format!("read {}: {e}", state_file.display()))?;
@@ -50,7 +50,7 @@ pub fn run(project: &Path, out: &Path, size: u32) -> Result<(), String> {
 
     let mut off = cce_ui::vk::RtOffscreen::new();
     off.set_scene(&tris, &mats);
-    let pixels = off.render(camera, size, size, SAMPLES);
+    let pixels = off.render(camera, size, size, samples.unwrap_or(SAMPLES));
 
     let file = std::fs::File::create(out).map_err(|e| format!("create {}: {e}", out.display()))?;
     let mut encoder = png::Encoder::new(std::io::BufWriter::new(file), size, size);