rvimage 0.8.5

A remote image viewer with a labeling tool
Documentation
use image::DynamicImage;
use rvimage_domain::{RvResult, ShapeI};

use crate::{
    InstanceAnnotate, annotations_accessor_mut,
    events::{Events, KeyCode},
    history::{History, Record},
    make_tool_transform,
    result::trace_ok_err,
    tools_data::{annotations::InstanceAnnotations, rot90_data::NRotations},
    util::Visibility,
    world::World,
    world_annotations_accessor,
};
use std::mem;

use super::{BBOX_NAME, BRUSH_NAME, Manipulate, bbox, brush};

pub const ACTOR_NAME: &str = "Rot90";
annotations_accessor_mut!(ACTOR_NAME, rot90_mut, "Rotation 90 didn't work", NRotations);
world_annotations_accessor!(ACTOR_NAME, rot90, "Rotation 90 didn't work", NRotations);

fn rot90_instannos_of_file<T>(
    annos: Option<&mut InstanceAnnotations<T>>,
    shape: ShapeI,
) -> RvResult<()>
where
    T: InstanceAnnotate,
{
    if let Some(annos) = annos {
        for elt in annos.elts_iter_mut() {
            *elt = mem::take(elt).rot90_with_image_ntimes(shape, 1)?;
        }
    }
    Ok(())
}

fn rot90_instannos_once(world: &mut World, shape: ShapeI) -> RvResult<()> {
    macro_rules! rot {
        ($name:expr, $module:ident) => {
            let annos = $module::get_annos_mut(world);
            rot90_instannos_of_file(annos, shape)?;
            if let Some(d) = $module::get_options_mut(world) {
                d.core.is_redraw_annos_triggered = true;
            }
            world.request_redraw_annotations($name, Visibility::None);
        };
    }
    rot!(BRUSH_NAME, brush);
    rot!(BBOX_NAME, bbox);
    Ok(())
}

pub fn rotate90(world: &World, im: DynamicImage, file: &str) -> RvResult<DynamicImage> {
    #[allow(clippy::indexing_slicing)]
    let data = world.data.tools_data_map[ACTOR_NAME].specifics.rot90()?;
    let data_of_file = data.annotations_map().get(file);
    Ok(if let Some((nrot, _)) = &data_of_file {
        match nrot {
            NRotations::Zero => im,
            NRotations::One => im.rotate270(),
            NRotations::Two => im.rotate180(),
            NRotations::Three => im.rotate90(),
        }
    } else {
        im
    })
}

/// rotate 90 degrees counter clockwise
fn rot90(mut world: World, n_rotations: NRotations, skip_annos: bool) -> World {
    let shape = world.data.shape();
    match n_rotations {
        NRotations::Zero => (),
        NRotations::One => {
            if !skip_annos {
                trace_ok_err(rot90_instannos_once(&mut world, shape));
            }
            world.data.apply(|im| im.rotate270());
        }
        NRotations::Two => {
            world.data.apply(|im| im.rotate180());
        }
        NRotations::Three => {
            world.data.apply(|im| im.rotate90());
        }
    }
    if !matches!(n_rotations, NRotations::Zero) {
        world.set_zoom_box(None);
        world.request_redraw_image();
    }
    world
}
#[derive(Clone, Copy, Debug)]
pub struct Rot90;

impl Rot90 {
    fn key_pressed(
        &mut self,
        _events: &Events,
        mut world: World,
        mut history: History,
    ) -> (World, History) {
        let skip_annos = false;
        world = rot90(world, NRotations::One, skip_annos);
        if let Some(anno) = get_annos_mut(&mut world) {
            *anno = anno.increase();
            tracing::info!(
                "Image rotated by 90°. Image has been rotated {} {}.",
                anno.to_num(),
                if anno.to_num() == 1 { "time" } else { "times" }
            );
        }
        history.push(Record::new(world.clone(), ACTOR_NAME));
        (world, history)
    }
}

impl Manipulate for Rot90 {
    fn new() -> Self {
        Self {}
    }

    fn on_filechange(&mut self, mut world: World, history: History) -> (World, History) {
        use_currentimageshape_for_annos(&mut world);
        if let Some(nrot) = get_annos_if_some(&world).copied() {
            let skip_annos = true;
            world = rot90(world, nrot, skip_annos);
        }
        (world, history)
    }

    fn events_tf(&mut self, world: World, history: History, event: &Events) -> (World, History) {
        let (world, history) = make_tool_transform!(
            self,
            world,
            history,
            event,
            [(pressed, KeyCode::R, key_pressed)]
        );
        (world, history)
    }
}