From ab67f4eab901c73871b15ecd2c1d1cc41c6d98a4 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Tue, 16 Jan 2018 04:48:54 -0800 Subject: [PATCH] Add support for cropping map renders --- src/cli/main.rs | 76 ++++++++++++++++++++++++++++++++++++++++++-- src/tools/dmm.rs | 6 ++++ src/tools/minimap.rs | 30 ++++++++++------- 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/src/cli/main.rs b/src/cli/main.rs index 4054dd3b..559a72c2 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -6,6 +6,8 @@ extern crate structopt; extern crate dreammaker as dm; #[macro_use] extern crate dmm_tools; +use std::fmt; + use structopt::StructOpt; use dmm_tools::*; @@ -35,6 +37,14 @@ struct Opt { #[structopt(long="reformat")] reformat: bool, + /// Set the minimum x,y or x,y,z coordinate to act upon (1-indexed, inclusive). + #[structopt(long="min")] + min: Option, + + /// Set the maximum x,y or x,y,z coordinate to act upon (1-indexed, inclusive). + #[structopt(long="max")] + max: Option, + /// The list of files to process. files: Vec, } @@ -67,9 +77,27 @@ fn main() { } if opt.minimap { - for z in 0..map.dim_z() { + let (dim_x, dim_y, dim_z) = map.dim_xyz(); + let mut min = opt.min.unwrap_or(CoordArg { x: 0, y: 0, z: 0 }); + let mut max = opt.max.unwrap_or(CoordArg { x: dim_x + 1, y: dim_y + 1, z: dim_z + 1 }); + min.x = clamp(min.x, 1, dim_x); + min.y = clamp(min.y, 1, dim_y); + min.z = clamp(min.z, 1, dim_z); + max.x = clamp(max.x, min.x, dim_x); + max.y = clamp(max.y, min.y, dim_y); + max.z = clamp(max.z, min.z, dim_z); + println!(" rendering from {} to {}", min, max); + + for z in (min.z - 1)..(max.z) { println!(" generating z={}", 1 + z); - let image = minimap::generate(&objtree, &map, z, &mut icon_cache).unwrap(); + let context = minimap::Context { + objtree: &objtree, + map: &map, + grid: map.z_level(z), + min: (min.x - 1, min.y - 1), + max: (max.x - 1, max.y - 1), + }; + let image = minimap::generate(context, &mut icon_cache).unwrap(); let output = format!("{}/{}-{}.png", opt.output, path.file_stem().unwrap().to_string_lossy(), 1 + z); if !opt.dry_run { println!(" saving {}", output); @@ -84,3 +112,47 @@ fn main() { flame::dump_html(&mut std::io::BufWriter::new(std::fs::File::create(format!("{}/flame-graph.html", opt.output)).unwrap())).unwrap(); } } + +#[derive(Debug, Copy, Clone)] +struct CoordArg { + x: usize, + y: usize, + z: usize, +} + +impl fmt::Display for CoordArg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.z != 0 { + write!(f, "{},{},{}", self.x, self.y, self.z) + } else { + write!(f, "{},{}", self.x, self.y) + } + } +} + +impl std::str::FromStr for CoordArg { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.split(",").map(|x| x.parse()).collect::, std::num::ParseIntError>>() { + Ok(ref vec) if vec.len() == 2 => { + Ok(CoordArg { x: vec[0], y: vec[1], z: 0 }) + } + Ok(ref vec) if vec.len() == 3 => { + Ok(CoordArg { x: vec[0], y: vec[1], z: vec[2] }) + } + Ok(_) => Err("must specify 2 or 3 coordinates".into()), + Err(e) => Err(e.to_string()), + } + } +} + +fn clamp(val: usize, min: usize, max: usize) -> usize { + if val < min { + min + } else if val > max { + max + } else { + val + } +} diff --git a/src/tools/dmm.rs b/src/tools/dmm.rs index c8c09abc..f3fea93c 100644 --- a/src/tools/dmm.rs +++ b/src/tools/dmm.rs @@ -45,6 +45,12 @@ impl Map { save_tgm(self, File::create(path)?) } + #[inline] + pub fn dim_xyz(&self) -> (usize, usize, usize) { + let dim = self.grid.dim(); + (dim.2, dim.1, dim.0) + } + #[inline] pub fn dim_z(&self) -> usize { self.grid.dim().0 diff --git a/src/tools/minimap.rs b/src/tools/minimap.rs index 2d992f36..ccf247a2 100644 --- a/src/tools/minimap.rs +++ b/src/tools/minimap.rs @@ -17,31 +17,37 @@ const LEGIT_POSTERS: u32 = 35; // Main minimap code #[derive(Clone, Copy)] -struct Context<'a> { - objtree: &'a ObjectTree, - map: &'a Map, - grid: Grid<'a>, +pub struct Context<'a> { + pub objtree: &'a ObjectTree, + pub map: &'a Map, + pub grid: Grid<'a>, + pub min: (usize, usize), + pub max: (usize, usize), } pub fn generate( - objtree: &ObjectTree, - map: &Map, - z: usize, + ctx: Context, icon_cache: &mut HashMap, ) -> Result { use rand::Rng; flame!("minimap"); - let grid = map.z_level(z); - let (len_x, len_y) = grid.dim(); - let ctx = Context { objtree, map, grid }; + let Context { objtree, map, grid, .. } = ctx; + + // transform min/max from bottom-left-based to top-left-based + // probably doesn't belong here + let (_, len_y) = ctx.grid.dim(); + let (min_y, max_y) = (len_y - ctx.max.1 - 1, len_y - ctx.min.1 - 1); + let (len_x, len_y) = (ctx.max.0 - ctx.min.0 + 1, ctx.max.1 - ctx.min.1 + 1); // loads atoms from the prefabs on the map and adds overlays and smoothing let mut atoms = Vec::new(); let mut overlays = Vec::new(); //flame!("collect"); for (y, row) in grid.axis_iter(Axis(0)).enumerate() { + if y < min_y || y > max_y { continue } for (x, e) in row.iter().enumerate() { + if x < ctx.min.0 || x > ctx.max.0 { continue } for mut atom in get_atom_list(objtree, &map.dictionary[e], (x as u32, y as u32)) { // icons which differ from their map states let p = &atom.type_.path; @@ -189,8 +195,8 @@ pub fn generate( let pixel_y = atom.get_var("pixel_y", ctx.objtree).to_int().unwrap_or(0) + icon_file.metadata.height as i32; let mut loc = ( - (atom.loc.0 * TILE_SIZE) as i32 + pixel_x, - (atom.loc.1 * TILE_SIZE + TILE_SIZE) as i32 - pixel_y + ((atom.loc.0 - ctx.min.0 as u32) * TILE_SIZE) as i32 + pixel_x, + ((atom.loc.1 + 1 - min_y as u32) * TILE_SIZE) as i32 - pixel_y ); // OOB handling