Raw File
flattypes.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 introduces types that are used in the flatscene's data structures.
//!
//! The motivation behind these types is that flatscene's representation of helices are stored in a
//! Vec as opposed to a HashMap in the design. This means that their identifier needs to be
//! converted. For both the flatscene and the design, usize could be used but having distinct types
//! reduces the confusion, since erros will be detected by the typechecker.

use super::{HashMap, Nucl, Selection};
use crate::utils::PhantomElement;

/// An helix identifier in the flatscene data structures.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct FlatIdx(pub usize);

#[derive(Debug, Clone, Copy, Hash)]
pub struct FlatHelix {
    /// The identifier of the helix in the flatscene data strucutres.
    pub flat: FlatIdx,
    /// The identifier of the helix in the designs data strucutres.
    pub real: usize,
}

impl std::cmp::PartialEq for FlatHelix {
    fn eq(&self, other: &Self) -> bool {
        self.flat == other.flat
    }
}

impl Eq for FlatHelix {}

impl std::cmp::PartialOrd for FlatHelix {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.flat.partial_cmp(&other.flat)
    }
}

impl std::cmp::Ord for FlatHelix {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.flat.cmp(&other.flat)
    }
}

impl FlatHelix {
    pub fn from_real(real: usize, helix_map: &HashMap<usize, FlatIdx>) -> Self {
        Self {
            flat: helix_map[&real],
            real,
        }
    }
}

/// This trait is a marker, incating that if T:Flat, then [T] can be indexed by a FlatHelix.
pub trait Flat {}

impl<T: Flat> std::ops::Index<FlatHelix> for [T] {
    type Output = T;
    fn index(&self, idx: FlatHelix) -> &Self::Output {
        &self[idx.flat.0]
    }
}

impl<T: Flat> std::ops::Index<FlatHelix> for Vec<T> {
    type Output = T;
    fn index(&self, idx: FlatHelix) -> &Self::Output {
        &self[idx.flat.0]
    }
}

impl<T: Flat> std::ops::Index<FlatIdx> for [T] {
    type Output = T;
    fn index(&self, idx: FlatIdx) -> &Self::Output {
        &self[idx.0]
    }
}

impl<T: Flat> std::ops::Index<FlatIdx> for Vec<T> {
    type Output = T;
    fn index(&self, idx: FlatIdx) -> &Self::Output {
        &self[idx.0]
    }
}

/// The nucleotide type manipulated by the flatscene
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, Ord, Hash)]
pub struct FlatNucl {
    pub helix: FlatHelix,
    pub position: isize,
    pub forward: bool,
}

impl FlatNucl {
    pub fn to_real(&self) -> Nucl {
        Nucl {
            helix: self.helix.real,
            position: self.position,
            forward: self.forward,
        }
    }

    pub fn from_real(real: &Nucl, id_map: &HashMap<usize, FlatIdx>) -> Self {
        Self {
            helix: FlatHelix::from_real(real.helix, id_map),
            position: real.position,
            forward: real.forward,
        }
    }

    pub fn prime3(&self) -> Self {
        Self {
            position: if self.forward {
                self.position + 1
            } else {
                self.position - 1
            },
            ..*self
        }
    }

    pub fn prime5(&self) -> Self {
        Self {
            position: if self.forward {
                self.position - 1
            } else {
                self.position + 1
            },
            ..*self
        }
    }

    #[allow(dead_code)]
    pub fn left(&self) -> Self {
        Self {
            position: self.position - 1,
            ..*self
        }
    }

    #[allow(dead_code)]
    pub fn right(&self) -> Self {
        Self {
            position: self.position + 1,
            ..*self
        }
    }
}

pub enum FlatSelection {
    Nucleotide(usize, FlatNucl),
    Bound(usize, FlatNucl, FlatNucl),
    Xover(usize, usize),
    Design(usize),
    Strand(usize, usize),
    Helix(usize, FlatHelix),
    Grid(usize, usize),
    Phantom(PhantomElement),
    Nothing,
}

impl FlatSelection {
    pub fn from_real(
        selection: Option<&Selection>,
        id_map: &HashMap<usize, FlatIdx>,
    ) -> FlatSelection {
        if let Some(selection) = selection {
            match selection {
                Selection::Nucleotide(d, nucl) => {
                    Self::Nucleotide(*d as usize, FlatNucl::from_real(nucl, id_map))
                }
                Selection::Bound(d, n1, n2) => {
                    let n1 = FlatNucl::from_real(n1, id_map);
                    let n2 = FlatNucl::from_real(n2, id_map);
                    Self::Bound(*d as usize, n1, n2)
                }
                Selection::Xover(d, xover_id) => Self::Xover(*d as usize, *xover_id),
                Selection::Design(d) => Self::Design(*d as usize),
                Selection::Strand(d, s_id) => Self::Strand(*d as usize, *s_id as usize),
                Selection::Helix(d, h_id) => {
                    Self::Helix(*d as usize, FlatHelix::from_real(*h_id as usize, id_map))
                }
                Selection::Grid(d, g_id) => Self::Grid(*d as usize, *g_id),
                Selection::Phantom(pe) => Self::Phantom(pe.clone()),
                Selection::Nothing => Self::Nothing,
            }
        } else {
            Self::Nothing
        }
    }
}

pub struct HelixVec<T: Flat>(Vec<T>);

impl<T: Flat> std::ops::Index<FlatIdx> for HelixVec<T> {
    type Output = T;

    fn index(&self, idx: FlatIdx) -> &T {
        &self.0[idx.0]
    }
}

impl<T: Flat> std::ops::IndexMut<FlatIdx> for HelixVec<T> {
    fn index_mut(&mut self, idx: FlatIdx) -> &mut Self::Output {
        &mut self.0[idx.0]
    }
}

impl<T: Flat> std::ops::Deref for HelixVec<T> {
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T: Flat> std::ops::DerefMut for HelixVec<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl<T: Flat> HelixVec<T> {
    pub fn new() -> Self {
        Self(Vec::new())
    }

    pub fn remove(&mut self, idx: FlatIdx) -> T {
        self.0.remove(idx.0)
    }

    pub fn push(&mut self, value: T) {
        self.0.push(value)
    }

    pub fn get(&self, idx: FlatIdx) -> Option<&T> {
        self.0.get(idx.0)
    }
}
back to top