~jpl8/text-to-piet

9fe77902fedab8f924b176ac738d5f5d23795f43 — jpl 1 year, 6 months ago 416e2fd
Add error propagation and minimum image size finder.

Finds the minimum image size by starting at the
minimum possible image size constrained by
the number of instructions and then checks if
trying to convert the instructions results in an
error.
This can happen when the terminator doesn't have
enough room or when the blocks outnumber the
available space on the spiral.

Need to separate search_for_minimum_satisfier into
a util.rs.
3 files changed, 51 insertions(+), 16 deletions(-)

M src/instructions.rs
M src/main.rs
M test.png
M src/instructions.rs => src/instructions.rs +3 -3
@@ 120,7 120,7 @@ impl PietEncoder for EncoderBFS {
    }
}

pub fn expected_size(instructions: &Vec<Instructions>) -> Result<usize> {
pub fn expected_size(instructions: &Vec<Instructions>) -> Result<(u32, u32)> {
    // Num of extra blocks for corner_turn = 2 * num_corners
    // num_corners(side) = side - 3, for side > 2, 0 otherwise
    // num_blocks(instructions, side) = instructions.len() + corner_turn.len() * num_corners(side)


@@ 159,12 159,12 @@ pub fn expected_size(instructions: &Vec<Instructions>) -> Result<usize> {
    let min_img_size_range = match search_for_minimum_satisfier(eq_to_minimize, 1..usize::MAX) {
        Some(lower_bound) => {
            let upper_bound = lower_bound + std::cmp::min(num_pushes, num_corners(&lower_bound));
            Ok((lower_bound, upper_bound))
            Ok((lower_bound as u32, upper_bound as u32))
        }
        None => Err(anyhow!("Unable to find a lower bound on the image size!")),
    };

    Ok(min_img_size_range?.0)
    min_img_size_range

    // ((2 * num_blocks + corner_turn_len * corner_turn_len - 6 * corner_turn_len + 2) as f64).sqrt()
    //     as u32

M src/main.rs => src/main.rs +48 -13
@@ 35,23 35,58 @@ struct Args {
fn main() -> Result<()> {
    let args = Args::parse();

    let input = fs::read_to_string(args.input_file).expect("Failed to read input file");
    let input = fs::read_to_string(args.input_file).context("Failed to read input file")?;

    let encoder = instructions::EncoderBFS::new("instruction_cache/bfs_instructions.ron");
    let instrs = encoder.encode_text(&input);

    let img_size = args.img_size.unwrap_or(expected_size(&instrs)? as u32);
    eprintln!("img_size: {:?}", img_size);
    eprintln!("spiral_len: {:?}", Spiral2D::length(img_size));
    if let Some(img_size) = args.img_size {
        try_image_with_size(img_size, &instrs)
            .unwrap()
            .save(args.output.unwrap_or("test.png".to_string()))
            .context("Failed to write ouput image")
    } else {
        let (min_img_size, max_img_size) = expected_size(&instrs)?;
        eprintln!(
            "(min_img_size, max_img_size: {:?}",
            (min_img_size, max_img_size)
        );

        let sat_img_size: u32 = search_for_minimum_satisfier(
            |size| try_image_with_size(*size, &instrs).is_ok(),
            min_img_size..=max_img_size,
        )
        .unwrap();

        try_image_with_size(sat_img_size, &instrs)
            .unwrap()
            .save(args.output.unwrap_or("test.png".to_string()))
            .context("Failed to write ouput image")
    }
}

fn search_for_minimum_satisfier<T, F, I>(predicate: F, search_space: I) -> Option<T>
where
    F: Fn(&T) -> bool,
    T: Sized + PartialEq + Clone,
    I: IntoIterator<Item = T>,
{
    for (satisfies, val) in search_space.into_iter().map(|v| (predicate(&v), v)) {
        if satisfies {
            return Some(val.clone());
        }
    }

    let colors = instructions_to_blocks(&instrs, img_size);
    None
}

fn try_image_with_size(img_size: u32, instrs: &Vec<Instructions>) -> Result<RgbImage> {
    eprintln!("spiral_len: {:?}", Spiral2D::length(img_size));
    eprintln!("img_size: {:?}", img_size);
    let img = colors_to_img(colors, img_size)
        .context("Failed to convert colors to an image during main execution")?;
    img.save(args.output.unwrap_or("test.png".to_string()))
        .expect("Failed to write output image");
    let colors = instructions_to_blocks(instrs, img_size)?;

    Ok(())
    colors_to_img(colors, img_size)
        .context("Failed to convert colors to an image during main execution")
}

fn update_x_y_spiral(x: u32, y: u32, step: u32, dir: &Direction) -> (u32, u32) {


@@ 62,7 97,7 @@ fn update_x_y_spiral(x: u32, y: u32, step: u32, dir: &Direction) -> (u32, u32) {
        Direction::Left => (x - step, y),
    }
}
fn instructions_to_blocks(instructions: &Vec<Instructions>, img_size: u32) -> Vec<Color> {
fn instructions_to_blocks(instructions: &Vec<Instructions>, img_size: u32) -> Result<Vec<Color>> {
    let mut colors = vec![];
    let mut prev = Color::Red;



@@ 129,7 164,7 @@ fn instructions_to_blocks(instructions: &Vec<Instructions>, img_size: u32) -> Ve
                colors.push(prev);
                remaining_space = remaining_space
                    .checked_sub(1)
                    .expect("Ran out of space to place more blocks in the image!");
                    .context("Ran out of space to place more blocks in the image!")?;
            }
        }
        prev = colors::instruction_to_color(*instr, prev);


@@ 154,7 189,7 @@ fn instructions_to_blocks(instructions: &Vec<Instructions>, img_size: u32) -> Ve

    colors.push(prev);

    colors
    Ok(colors)
}

fn colors_to_img(colors: Vec<Color>, img_size: u32) -> Result<RgbImage> {

M test.png => test.png +0 -0