We are hiring ! See our job offers.
Raw File
operation.rs
/*
ENSnano, a 3d graphical application for DNA nanostructures.
    Copyright (C) 2021  Nicolas Levy <nicolaspierrelevy@gmail.com> and Nicolas Schabanel <nicolas.schabanel@ens-lyon.fr>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
//! This modules defines the `Operation` trait and several struct that implement it.
//!
//! An structure that implements `Operation` can produce an `UndoableOpperation` that will have an
//! effect on the design.
//!
//! Moreover, these operations are meant to be modifiable via GUI component or user interaction.
use super::{DesignRotation, DesignTranslation, GridDescriptor, GridHelixDescriptor, UndoableOp};
use crate::design::{
    GridTypeDescr, Helix, Hyperboloid, IsometryTarget, Nucl, Strand, StrandBuilder, StrandState,
};
use std::sync::Arc;
use ultraviolet::{Bivec3, Rotor3, Vec3};

pub enum ParameterField {
    Choice(Vec<String>),
    Value,
}

pub struct Parameter {
    pub field: ParameterField,
    pub name: String,
}

pub trait Operation: std::fmt::Debug + Sync + Send {
    /// The set of parameters that can be modified via a GUI component
    fn parameters(&self) -> Vec<Parameter>;
    /// The values associated to the parameters.
    fn values(&self) -> Vec<String>;
    /// Return an opperation whose effect cancels the effect of `self`.
    fn reverse(&self) -> Arc<dyn Operation>;
    /// The effect of self that must be sent as a notifications to the targeted designs
    fn effect(&self) -> UndoableOp;
    /// A description of self of display in the GUI
    fn description(&self) -> String;
    /// The targeted designs of self.
    fn target(&self) -> usize;
    /// Produce an new opperation by setting the value of the `n`-th parameter to `val`.
    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>>;
    fn descr(&self) -> OperationDescriptor;
    /// If `other` is compatible with `self` return the operation whose effect is equivalent to
    /// applying the effects of `other` and then `self`.
    fn compose(&self, other: &dyn Operation) -> Option<Arc<dyn Operation>>;

    fn must_reverse(&self) -> bool {
        true
    }

    fn drop_undo(&self) -> bool {
        false
    }

    fn redoable(&self) -> bool {
        true
    }
}

#[derive(Clone, Debug)]
pub struct GridRotation {
    pub origin: Vec3,
    pub design_id: usize,
    pub grid_id: usize,
    pub angle: f32,
    pub plane: Bivec3,
}

impl Operation for GridRotation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::GridRotation(self.design_id, self.grid_id, self.plane)
    }

    fn compose(&self, other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        if self.descr() == other.descr() {
            let angle = other.values()[0].parse::<f32>().unwrap().to_radians();
            Some(Arc::new(Self {
                angle: self.angle + angle,
                ..*self
            }))
        } else {
            None
        }
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![Parameter {
            field: ParameterField::Value,
            name: String::from("angle"),
        }]
    }

    fn values(&self) -> Vec<String> {
        vec![self.angle.to_degrees().to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Self {
            angle: -self.angle,
            ..*self
        })
    }

    fn effect(&self) -> UndoableOp {
        let rotor = Rotor3::from_angle_plane(self.angle, self.plane);
        UndoableOp::Rotation(DesignRotation {
            rotation: rotor,
            origin: self.origin,
            target: IsometryTarget::Grid(self.grid_id as u32),
        })
    }

    fn description(&self) -> String {
        format!("Rotate grid {} of design {}", self.grid_id, self.design_id)
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        if n == 0 {
            let degrees: f32 = val.parse().ok()?;
            Some(Arc::new(Self {
                angle: degrees.to_radians(),
                ..*self
            }))
        } else {
            None
        }
    }
}

#[derive(Clone, Debug)]
pub struct HelixRotation {
    pub origin: Vec3,
    pub design_id: usize,
    pub helix_id: usize,
    pub angle: f32,
    pub plane: Bivec3,
}

impl Operation for HelixRotation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::HelixRotation(self.design_id, self.helix_id, self.plane)
    }

    fn compose(&self, other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        if self.descr() == other.descr() {
            let angle = other.values()[0].parse::<f32>().unwrap().to_radians();
            Some(Arc::new(Self {
                angle: self.angle + angle,
                ..*self
            }))
        } else {
            None
        }
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![Parameter {
            field: ParameterField::Value,
            name: String::from("angle"),
        }]
    }

    fn values(&self) -> Vec<String> {
        vec![self.angle.to_degrees().to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Self {
            angle: -self.angle,
            ..*self
        })
    }

    fn effect(&self) -> UndoableOp {
        let rotor = Rotor3::from_angle_plane(self.angle, self.plane);
        UndoableOp::Rotation(DesignRotation {
            rotation: rotor,
            origin: self.origin,
            target: IsometryTarget::Helix(self.helix_id as u32, false),
        })
    }

    fn description(&self) -> String {
        format!(
            "Rotate helix {} of design {}",
            self.helix_id, self.design_id
        )
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        if n == 0 {
            let degrees: f32 = val.parse().ok()?;
            Some(Arc::new(Self {
                angle: degrees.to_radians(),
                ..*self
            }))
        } else {
            None
        }
    }
}

#[derive(Debug, Clone)]
pub struct DesignViewRotation {
    pub origin: Vec3,
    pub design_id: usize,
    pub angle: f32,
    pub plane: Bivec3,
}

impl Operation for DesignViewRotation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::DesignRotation(self.design_id, self.plane)
    }

    fn compose(&self, other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        if self.descr() == other.descr() {
            let angle = other.values()[0].parse::<f32>().unwrap().to_radians();
            Some(Arc::new(Self {
                angle: self.angle + angle,
                ..*self
            }))
        } else {
            None
        }
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![Parameter {
            field: ParameterField::Value,
            name: String::from("angle"),
        }]
    }

    fn values(&self) -> Vec<String> {
        vec![self.angle.to_degrees().to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Self {
            angle: -self.angle,
            ..*self
        })
    }

    fn effect(&self) -> UndoableOp {
        let rotor = Rotor3::from_angle_plane(self.angle, self.plane);
        UndoableOp::Rotation(DesignRotation {
            rotation: rotor,
            origin: self.origin,
            target: IsometryTarget::Design,
        })
    }

    fn description(&self) -> String {
        format!("Rotate view of design {}", self.design_id)
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        if n == 0 {
            let degrees: f32 = val.parse().ok()?;
            Some(Arc::new(Self {
                angle: degrees.to_radians(),
                ..*self
            }))
        } else {
            None
        }
    }
}

#[derive(Debug, Clone)]
pub struct DesignViewTranslation {
    pub design_id: usize,
    pub right: Vec3,
    pub top: Vec3,
    pub dir: Vec3,
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl Operation for DesignViewTranslation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::DesignTranslation(self.design_id)
    }

    fn compose(&self, other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        if self.descr() == other.descr() {
            let x = other.values()[0].parse::<f32>().unwrap();
            let y = other.values()[1].parse::<f32>().unwrap();
            let z = other.values()[2].parse::<f32>().unwrap();
            Some(Arc::new(Self {
                x: self.x + x,
                y: self.y + y,
                z: self.z + z,
                ..*self
            }))
        } else {
            None
        }
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![
            Parameter {
                field: ParameterField::Value,
                name: String::from("x"),
            },
            Parameter {
                field: ParameterField::Value,
                name: String::from("y"),
            },
            Parameter {
                field: ParameterField::Value,
                name: String::from("z"),
            },
        ]
    }

    fn values(&self) -> Vec<String> {
        vec![self.x.to_string(), self.y.to_string(), self.z.to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Self {
            x: -self.x,
            y: -self.y,
            z: -self.z,
            ..*self
        })
    }

    fn effect(&self) -> UndoableOp {
        let translation = self.x * self.right + self.y * self.top + self.z * self.dir;
        UndoableOp::Translation(DesignTranslation {
            translation,
            target: IsometryTarget::Design,
        })
    }

    fn description(&self) -> String {
        format!("Translate design {}", self.design_id)
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        match n {
            0 => {
                let new_x: f32 = val.parse().ok()?;
                Some(Arc::new(Self { x: new_x, ..*self }))
            }
            1 => {
                let new_y: f32 = val.parse().ok()?;
                Some(Arc::new(Self { y: new_y, ..*self }))
            }
            2 => {
                let new_z: f32 = val.parse().ok()?;
                Some(Arc::new(Self { z: new_z, ..*self }))
            }
            _ => None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct HelixTranslation {
    pub design_id: usize,
    pub helix_id: usize,
    pub right: Vec3,
    pub top: Vec3,
    pub dir: Vec3,
    pub x: f32,
    pub y: f32,
    pub z: f32,
    pub snap: bool,
}

impl Operation for HelixTranslation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::HelixTranslation(self.design_id, self.helix_id)
    }

    fn compose(&self, other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        if self.descr() == other.descr() {
            let x = other.values()[0].parse::<f32>().unwrap();
            let y = other.values()[1].parse::<f32>().unwrap();
            let z = other.values()[2].parse::<f32>().unwrap();
            Some(Arc::new(Self {
                x: self.x + x,
                y: self.y + y,
                z: self.z + z,
                ..*self
            }))
        } else {
            None
        }
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![
            Parameter {
                field: ParameterField::Value,
                name: String::from("x"),
            },
            Parameter {
                field: ParameterField::Value,
                name: String::from("y"),
            },
            Parameter {
                field: ParameterField::Value,
                name: String::from("z"),
            },
        ]
    }

    fn values(&self) -> Vec<String> {
        vec![self.x.to_string(), self.y.to_string(), self.z.to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Self {
            x: -self.x,
            y: -self.y,
            z: -self.z,
            ..*self
        })
    }

    fn effect(&self) -> UndoableOp {
        let translation = self.x * self.right + self.y * self.top + self.z * self.dir;
        UndoableOp::Translation(DesignTranslation {
            translation,
            target: IsometryTarget::Helix(self.helix_id as u32, self.snap),
        })
    }

    fn description(&self) -> String {
        format!(
            "Translate helix {} of design {}",
            self.helix_id, self.design_id
        )
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        match n {
            0 => {
                let new_x: f32 = val.parse().ok()?;
                Some(Arc::new(Self { x: new_x, ..*self }))
            }
            1 => {
                let new_y: f32 = val.parse().ok()?;
                Some(Arc::new(Self { y: new_y, ..*self }))
            }
            2 => {
                let new_z: f32 = val.parse().ok()?;
                Some(Arc::new(Self { z: new_z, ..*self }))
            }
            _ => None,
        }
    }

    fn must_reverse(&self) -> bool {
        false
    }
}

#[derive(Debug, Clone)]
pub struct GridTranslation {
    pub design_id: usize,
    pub grid_id: usize,
    pub right: Vec3,
    pub top: Vec3,
    pub dir: Vec3,
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl Operation for GridTranslation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::GridTranslation(self.design_id, self.grid_id)
    }

    fn compose(&self, other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        if self.descr() == other.descr() {
            let x = other.values()[0].parse::<f32>().unwrap();
            let y = other.values()[1].parse::<f32>().unwrap();
            let z = other.values()[2].parse::<f32>().unwrap();
            Some(Arc::new(Self {
                x: self.x + x,
                y: self.y + y,
                z: self.z + z,
                ..*self
            }))
        } else {
            None
        }
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![
            Parameter {
                field: ParameterField::Value,
                name: String::from("x"),
            },
            Parameter {
                field: ParameterField::Value,
                name: String::from("y"),
            },
            Parameter {
                field: ParameterField::Value,
                name: String::from("z"),
            },
        ]
    }

    fn values(&self) -> Vec<String> {
        vec![self.x.to_string(), self.y.to_string(), self.z.to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Self {
            x: -self.x,
            y: -self.y,
            z: -self.z,
            ..*self
        })
    }

    fn effect(&self) -> UndoableOp {
        let translation = self.x * self.right + self.y * self.top + self.z * self.dir;
        UndoableOp::Translation(DesignTranslation {
            translation,
            target: IsometryTarget::Grid(self.grid_id as u32),
        })
    }

    fn description(&self) -> String {
        format!(
            "Translate grid {} of design {}",
            self.grid_id, self.design_id
        )
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        match n {
            0 => {
                let new_x: f32 = val.parse().ok()?;
                Some(Arc::new(Self { x: new_x, ..*self }))
            }
            1 => {
                let new_y: f32 = val.parse().ok()?;
                Some(Arc::new(Self { y: new_y, ..*self }))
            }
            2 => {
                let new_z: f32 = val.parse().ok()?;
                Some(Arc::new(Self { z: new_z, ..*self }))
            }
            _ => None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct GridHelixCreation {
    pub design_id: usize,
    pub grid_id: usize,
    pub x: isize,
    pub y: isize,
    pub position: isize,
    pub length: usize,
}

impl Operation for GridHelixCreation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::GridHelixCreation(self.design_id, self.grid_id)
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![self.x.to_string(), self.y.to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(GridHelixDeletion {
            x: self.x,
            y: self.y,
            design_id: self.design_id,
            grid_id: self.grid_id,
            position: self.position,
            length: self.length,
        })
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::AddGridHelix(
            GridHelixDescriptor {
                grid_id: self.grid_id,
                x: self.x,
                y: self.y,
            },
            self.position,
            self.length,
        )
    }

    fn description(&self) -> String {
        format!(
            "Create helix on grid {} of design {}",
            self.grid_id, self.design_id
        )
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        match n {
            0 => {
                let new_x: f32 = val.parse().ok()?;
                Some(Arc::new(Self {
                    x: new_x as isize,
                    ..*self
                }))
            }
            1 => {
                let new_y: f32 = val.parse().ok()?;
                Some(Arc::new(Self {
                    y: new_y as isize,
                    ..*self
                }))
            }
            _ => None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct GridHelixDeletion {
    design_id: usize,
    grid_id: usize,
    x: isize,
    y: isize,
    position: isize,
    length: usize,
}

impl Operation for GridHelixDeletion {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::GridHelixCreation(self.design_id, self.grid_id)
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![self.x.to_string(), self.y.to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(GridHelixCreation {
            x: self.x,
            y: self.y,
            design_id: self.design_id,
            grid_id: self.grid_id,
            position: self.position,
            length: self.length,
        })
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::RmGridHelix(
            GridHelixDescriptor {
                grid_id: self.grid_id,
                x: self.x,
                y: self.y,
            },
            self.position,
            self.length,
        )
    }

    fn description(&self) -> String {
        format!(
            "Create helix on grid {} of design {}",
            self.grid_id, self.design_id
        )
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        match n {
            0 => {
                let new_x: f32 = val.parse().ok()?;
                Some(Arc::new(Self {
                    x: new_x as isize,
                    ..*self
                }))
            }
            1 => {
                let new_y: f32 = val.parse().ok()?;
                Some(Arc::new(Self {
                    y: new_y as isize,
                    ..*self
                }))
            }
            _ => None,
        }
    }
}

#[derive(Clone, Debug)]
pub struct RawHelixCreation {
    pub helix: Helix,
    pub helix_id: usize,
    pub delete: bool,
    pub design_id: usize,
}

impl Operation for RawHelixCreation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::RawHelixCreation
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(RawHelixCreation {
            delete: !self.delete,
            ..self.clone()
        })
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::RawHelixCreation {
            helix: self.helix.clone(),
            h_id: self.helix_id,
            delete: self.delete,
        }
    }

    fn description(&self) -> String {
        if self.delete {
            format!("Delete grid")
        } else {
            format!("Create grid")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }
}

#[derive(Clone, Debug)]
/// Cut a strand at a given nucleotide.
///
/// If the nucleotide is the 3' end of a cross-over, it will be the 5' end of the 3' half of the
/// split.
/// In all other cases, it will be the 3' end of the 5' end of the split.
pub struct Cut {
    pub strand: Strand,
    pub nucl: Nucl,
    pub strand_id: usize,
    pub undo: bool,
    pub design_id: usize,
}

impl Operation for Cut {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::Cut
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Cut {
            undo: !self.undo,
            ..self.clone()
        })
    }

    fn effect(&self) -> UndoableOp {
        if self.strand.length() < 2 {
            UndoableOp::RmStrand {
                strand: self.strand.clone(),
                strand_id: self.strand_id,
                undo: self.undo,
            }
        } else {
            UndoableOp::Cut {
                nucl: self.nucl,
                strand: self.strand.clone(),
                s_id: self.strand_id,
                undo: self.undo,
            }
        }
    }

    fn description(&self) -> String {
        if self.undo {
            format!("Undo Cut")
        } else {
            format!("Do Cut")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }
}

#[derive(Clone, Debug)]
pub struct Xover {
    pub strand_5prime: Strand,
    pub strand_3prime: Strand,
    pub prime5_id: usize,
    pub prime3_id: usize,
    pub undo: bool,
    pub design_id: usize,
}

impl Operation for Xover {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::Xover
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(Xover {
            undo: !self.undo,
            ..self.clone()
        })
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::Xover {
            strand_5prime: self.strand_5prime.clone(),
            strand_3prime: self.strand_3prime.clone(),
            prime5_id: self.prime5_id,
            prime3_id: self.prime3_id,
            undo: self.undo,
        }
    }

    fn description(&self) -> String {
        if self.undo {
            format!("Undo Cut")
        } else {
            format!("Do Cut")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }
}

/// Delete a strand
#[derive(Clone, Debug)]
pub struct RmStrand {
    pub strand: Strand,
    pub strand_id: usize,
    pub undo: bool,
    pub design_id: usize,
}

impl Operation for RmStrand {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::CrossCut
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(RmStrand {
            undo: !self.undo,
            ..self.clone()
        })
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::RmStrand {
            strand: self.strand.clone(),
            strand_id: self.strand_id,
            undo: self.undo,
        }
    }

    fn description(&self) -> String {
        if self.undo {
            format!("Undo Cut")
        } else {
            format!("Do Cut")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }
}

/// Cut the target strand at nucl, and make a cross over from the source strand.
#[derive(Clone, Debug)]
pub struct CrossCut {
    pub source_strand: Strand,
    pub target_strand: Strand,
    pub source_id: usize,
    pub target_id: usize,
    pub nucl: Nucl,
    /// True if the target strand will be the 3 prime part of the merged strand
    pub target_3prime: bool,
    pub undo: bool,
    pub design_id: usize,
}

impl Operation for CrossCut {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::CrossCut
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(CrossCut {
            undo: !self.undo,
            ..self.clone()
        })
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::CrossCut {
            source_strand: self.source_strand.clone(),
            target_strand: self.target_strand.clone(),
            source_id: self.source_id,
            target_id: self.target_id,
            target_3prime: self.target_3prime,
            nucl: self.nucl,
            undo: self.undo,
        }
    }

    fn description(&self) -> String {
        if self.undo {
            format!("Undo Cut")
        } else {
            format!("Do Cut")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }
}

#[derive(Clone, Debug)]
pub struct CreateGrid {
    pub position: Vec3,
    pub orientation: Rotor3,
    pub grid_type: GridTypeDescr,
    pub delete: bool,
    pub design_id: usize,
}

impl Operation for CreateGrid {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::CreateGrid
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![Parameter {
            field: ParameterField::Choice(vec![String::from("Square"), String::from("Honeycomb")]),
            name: String::from("Grid type"),
        }]
    }

    fn values(&self) -> Vec<String> {
        vec![self.grid_type.to_string()]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(CreateGrid {
            delete: !self.delete,
            ..*self
        })
    }

    fn effect(&self) -> UndoableOp {
        if self.delete {
            UndoableOp::RmGrid
        } else {
            UndoableOp::AddGrid(GridDescriptor {
                position: self.position,
                orientation: self.orientation,
                grid_type: self.grid_type,
            })
        }
    }

    fn description(&self) -> String {
        if self.delete {
            format!("Delete grid")
        } else {
            format!("Create grid")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, n: usize, val: String) -> Option<Arc<dyn Operation>> {
        match n {
            0 => match val.as_str() {
                "Square" => Some(Arc::new(Self {
                    grid_type: GridTypeDescr::Square,
                    ..*self
                })),
                "Honeycomb" => Some(Arc::new(Self {
                    grid_type: GridTypeDescr::Honeycomb,
                    ..*self
                })),
                _ => None,
            },
            _ => None,
        }
    }
}

#[derive(Clone)]
pub struct BigStrandModification {
    pub initial_state: StrandState,
    pub final_state: StrandState,
    pub reverse: bool,
    pub design_id: usize,
}

impl std::fmt::Debug for BigStrandModification {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("BigStrandModification")
            .field("reverse", &self.reverse)
            .finish()
    }
}

impl Operation for BigStrandModification {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::BigStrandModification
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(BigStrandModification {
            reverse: !self.reverse,
            ..self.clone()
        })
    }

    fn effect(&self) -> UndoableOp {
        if self.reverse {
            UndoableOp::NewStrandState(self.initial_state.clone())
        } else {
            UndoableOp::NewStrandState(self.final_state.clone())
        }
    }

    fn description(&self) -> String {
        if self.reverse {
            format!("Reverse Big Change")
        } else {
            format!("Redo Big Change")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }
}

#[derive(Clone, Debug)]
pub struct NewHyperboloid {
    pub position: Vec3,
    pub orientation: Rotor3,
    pub hyperboloid: Hyperboloid,
    pub delete: bool,
    pub design_id: usize,
}

impl Operation for NewHyperboloid {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::CreateGrid
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(NewHyperboloid {
            delete: !self.delete,
            ..self.clone()
        })
    }

    fn effect(&self) -> UndoableOp {
        if self.delete {
            UndoableOp::ClearHyperboloid
        } else {
            UndoableOp::NewHyperboloid {
                position: self.position,
                orientation: self.orientation,
                hyperboloid: self.hyperboloid.clone(),
            }
        }
    }

    fn description(&self) -> String {
        if self.delete {
            format!("Delete nanotube")
        } else {
            format!("Create nanotube")
        }
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }

    fn drop_undo(&self) -> bool {
        true
    }
}

#[derive(Debug)]
pub struct StrandConstruction {
    pub builder: Box<StrandBuilder>,
    pub redo: Option<u32>,
    pub color: u32,
}

impl Operation for StrandConstruction {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::BuildStrand(self.builder.get_timestamp())
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        let redo = self.redo.xor(Some(self.color));
        Arc::new(StrandConstruction {
            builder: self.builder.clone(),
            redo,
            color: self.color,
        })
    }

    fn effect(&self) -> UndoableOp {
        if let Some(color) = self.redo {
            let remake = if self.builder.created_de_novo() {
                Some((self.builder.get_strand_id(), color))
            } else {
                None
            };
            UndoableOp::MoveBuilder(self.builder.clone(), remake)
        } else {
            UndoableOp::ResetBuilder(self.builder.clone())
        }
    }

    fn description(&self) -> String {
        "Building strand".to_string()
    }

    fn target(&self) -> usize {
        self.builder.get_design_id() as usize
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }

    fn must_reverse(&self) -> bool {
        false
    }
}

#[derive(Clone)]
pub struct RigidGridSimulation {
    pub initial_state: crate::design::GridSystemState,
    pub design_id: usize,
}

impl std::fmt::Debug for RigidGridSimulation {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RigidGridSimulation").finish()
    }
}

impl Operation for RigidGridSimulation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::BigStrandModification
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(self.clone())
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::UndoGridSimulation(self.initial_state.clone())
    }

    fn description(&self) -> String {
        format!("Undo grid simulation")
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }

    fn redoable(&self) -> bool {
        false
    }
}

#[derive(Clone)]
pub struct RigidHelixSimulation {
    pub initial_state: crate::design::RigidHelixState,
    pub design_id: usize,
}

impl std::fmt::Debug for RigidHelixSimulation {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RigidHelixSimulation").finish()
    }
}

impl Operation for RigidHelixSimulation {
    fn descr(&self) -> OperationDescriptor {
        OperationDescriptor::BigStrandModification
    }

    fn compose(&self, _other: &dyn Operation) -> Option<Arc<dyn Operation>> {
        None
    }

    fn parameters(&self) -> Vec<Parameter> {
        vec![]
    }

    fn values(&self) -> Vec<String> {
        vec![]
    }

    fn reverse(&self) -> Arc<dyn Operation> {
        Arc::new(self.clone())
    }

    fn effect(&self) -> UndoableOp {
        UndoableOp::UndoHelixSimulation(self.initial_state.clone())
    }

    fn description(&self) -> String {
        format!("Undo helix simulation")
    }

    fn target(&self) -> usize {
        self.design_id
    }

    fn with_new_value(&self, _n: usize, _val: String) -> Option<Arc<dyn Operation>> {
        None
    }

    fn redoable(&self) -> bool {
        false
    }
}

#[derive(Debug)]
/// A description of an operation. Two opperations whose `descr` is equal are considered to be the
/// same operation with different parameters.
pub enum OperationDescriptor {
    DesignTranslation(usize),
    DesignRotation(usize, Bivec3),
    HelixRotation(usize, usize, Bivec3),
    HelixTranslation(usize, usize),
    GridRotation(usize, usize, Bivec3),
    GridTranslation(usize, usize),
    GridHelixCreation(usize, usize),
    GridHelixDeletion(usize, usize),
    RawHelixCreation,
    Cut,
    CrossCut,
    Xover,
    RmStrand,
    BuildStrand(std::time::SystemTime),
    CreateGrid,
    BigStrandModification,
}

impl PartialEq<Self> for OperationDescriptor {
    fn eq(&self, rhs: &Self) -> bool {
        use OperationDescriptor::*;
        match (self, rhs) {
            (DesignTranslation(d1), DesignTranslation(d2)) => d1 == d2,
            (DesignRotation(d1, bv1), DesignRotation(d2, bv2)) => {
                d1 == d2 && (*bv1 - *bv2).mag() < 1e-3
            }
            (HelixRotation(d1, h1, bv1), HelixRotation(d2, h2, bv2)) => {
                d1 == d2 && h1 == h2 && (*bv1 - *bv2).mag() < 1e-3
            }
            (HelixTranslation(d1, h1), HelixTranslation(d2, h2)) => d1 == d2 && h1 == h2,
            (GridTranslation(d1, g1), GridTranslation(d2, g2)) => d1 == d2 && g1 == g2,
            (GridRotation(d1, g1, bv1), GridRotation(d2, g2, bv2)) => {
                d1 == d2 && g1 == g2 && (*bv1 - *bv2).mag() < 1e-3
            }
            (GridHelixCreation(d1, g1), GridHelixCreation(d2, g2)) => d1 == d2 && g1 == g2,
            (GridHelixDeletion(d1, g1), GridHelixDeletion(d2, g2)) => d1 == d2 && g1 == g2,
            (CreateGrid, CreateGrid) => true,
            (BuildStrand(ts1), BuildStrand(ts2)) => ts1 == ts2,
            _ => false,
        }
    }

    fn ne(&self, rhs: &Self) -> bool {
        !self.eq(rhs)
    }
}
back to top