We are hiring ! See our job offers.
Raw File
mediator.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/>.
*/
//! The Mediator coordinates the interaction between the designs and the applications.
//! When a design is modified, it notifies the mediator of its changes and the mediator forwards
//! that information to the applications.
//!
//! When an application wants to modify a design, it makes the modification request to the
//! mediator.
//!
//! The mediator also holds data that is common to all applications.
use crate::gui::RigidBodyParametersRequest;
use crate::gui::{HyperboloidRequest, KeepProceed, Requests, SimulationRequest};
use crate::utils::{message, yes_no_dialog, PhantomElement};
use crate::{DrawArea, Duration, ElementType, IcedMessages, Multiplexer, WindowEvent};
use iced_wgpu::wgpu;
use iced_winit::winit::{
    dpi::{PhysicalPosition, PhysicalSize},
    event::ModifiersState,
};
use simple_excel_writer::{row, Row, Workbook};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock};
use ultraviolet::Vec3;

use crate::{design, ApplicationState};

use design::{
    Design, DesignNotification, DesignRotation, DesignTranslation, DnaAttribute, DnaElementKey,
    GridDescriptor, GridHelixDescriptor, Helix, Hyperboloid, Nucl, OperationResult,
    Parameters as DNAParameters, RigidBodyConstants, Stapple, Strand, StrandBuilder, StrandState,
};
use ensnano_organizer::OrganizerTree;

mod graphic_options;
mod operation;
mod selection;
pub use graphic_options::*;
pub use operation::*;
pub use selection::*;

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum AppId {
    FlatScene,
    Scene,
    Organizer,
    Mediator,
}

pub type MediatorPtr = Arc<Mutex<Mediator>>;

pub struct Mediator {
    applications: HashMap<ElementType, Arc<Mutex<dyn Application>>>,
    designs: Vec<Arc<RwLock<Design>>>,
    selection: Vec<Selection>,
    candidate: Option<(Vec<Selection>, AppId)>,
    last_selection: Option<(Vec<Selection>, AppId)>,
    messages: Arc<Mutex<IcedMessages>>,
    /// The operation that is beign modified by the current drag and drop
    current_operation: Option<Arc<dyn Operation>>,
    /// The operation that can currently be eddited via the status bar or in the scene
    last_op: Option<Arc<dyn Operation>>,
    undo_stack: Vec<Arc<dyn Operation>>,
    redo_stack: Vec<Arc<dyn Operation>>,
    computing: Arc<Mutex<bool>>,
    centring: Option<(Nucl, usize)>,
    center_selection: Option<(Selection, AppId)>,
    pasting: PastingMode,
    last_selected_design: usize,
    pasting_attempt: Option<Nucl>,
    duplication_attempt: bool,
    canceling_pasting: bool,
    parameters_ptr: ParameterPtr,
    main_state: MainState,
}

/// The scheduler is responsible for running the different applications
pub struct Scheduler {
    applications: HashMap<ElementType, Arc<Mutex<dyn Application>>>,
    needs_redraw: Vec<ElementType>,
}

impl Scheduler {
    pub fn new() -> Self {
        Self {
            applications: HashMap::new(),
            needs_redraw: Vec::new(),
        }
    }

    pub fn add_application(
        &mut self,
        application: Arc<Mutex<dyn Application>>,
        element_type: ElementType,
    ) {
        self.applications.insert(element_type, application);
    }

    /// Forwards an event to the appropriate application
    pub fn forward_event(
        &mut self,
        event: &WindowEvent,
        area: ElementType,
        cursor_position: PhysicalPosition<f64>,
    ) {
        if let Some(app) = self.applications.get_mut(&area) {
            app.lock().unwrap().on_event(event, cursor_position)
        }
    }

    pub fn check_redraw(&mut self, multiplexer: &Multiplexer, dt: Duration) -> bool {
        self.needs_redraw.clear();
        for (area, app) in self.applications.iter_mut() {
            if multiplexer.is_showing(area) && app.lock().unwrap().needs_redraw(dt) {
                self.needs_redraw.push(*area)
            }
        }
        self.needs_redraw.len() > 0
    }

    /// Request an application to draw on a texture
    pub fn draw_apps(
        &mut self,
        encoder: &mut wgpu::CommandEncoder,
        multiplexer: &Multiplexer,
        dt: Duration,
    ) {
        for area in self.needs_redraw.iter() {
            let app = self.applications.get_mut(area).unwrap();
            if let Some(target) = multiplexer.get_texture_view(*area) {
                app.lock().unwrap().on_redraw_request(encoder, target, dt);
            }
        }
    }

    /// Notify all applications that the size of the window has been modified
    pub fn forward_new_size(&mut self, window_size: PhysicalSize<u32>, multiplexer: &Multiplexer) {
        if window_size.height > 0 && window_size.width > 0 {
            for (area, app) in self.applications.iter_mut() {
                if let Some(draw_area) = multiplexer.get_draw_area(*area) {
                    app.lock().unwrap().on_resize(window_size, draw_area);
                    self.needs_redraw.push(*area);
                }
            }
        }
    }
}

#[derive(Clone)]
/// A notification that must be send to the application
pub enum Notification {
    /// A design has been modified
    DesignNotification(DesignNotification),
    #[allow(dead_code)]
    AppNotification(AppNotification),
    /// A new design has been added
    NewDesign(Arc<RwLock<Design>>),
    /// The application must show/hide the sequences
    ToggleText(bool),
    /// The scroll sensitivity has been modified
    NewSensitivity(f32),
    /// The action mode has been modified
    NewActionMode(ActionMode),
    /// The selection mode has been modified
    NewSelectionMode(SelectionMode),
    FitRequest,
    /// The designs have been deleted
    ClearDesigns,
    /// A new element of the design must be highlighted
    NewCandidate(Vec<Selection>, AppId),
    /// An element has been selected in the 3d view
    Selection3D(Vec<Selection>, AppId),
    /// A save request has been filled
    Save(usize),
    /// The 3d camera must face a given target
    CameraTarget((Vec3, Vec3)),
    CameraRotation(f32, f32, f32),
    Centering(Nucl, usize),
    CenterSelection(Selection, AppId),
    Pasting(bool),
    ShowTorsion(bool),
    ModifersChanged(ModifiersState),
    Split2d,
    Redim2dHelices(bool),
    ToggleWidget(bool),
    Background3D(Background3D),
    RenderingMode(RenderingMode),
}

pub trait Application {
    /// For notification about the data
    fn on_notify(&mut self, notification: Notification);
    /// The method must be called when the window is resized or when the drawing area is modified
    fn on_resize(&mut self, window_size: PhysicalSize<u32>, area: DrawArea);
    /// The methods is used to forwards the window events to applications
    fn on_event(&mut self, event: &WindowEvent, position: PhysicalPosition<f64>);
    /// The method is used to forwards redraw_requests to applications
    fn on_redraw_request(
        &mut self,
        encoder: &mut wgpu::CommandEncoder,
        target: &wgpu::TextureView,
        dt: Duration,
    );
    fn needs_redraw(&mut self, dt: Duration) -> bool;
}

impl Mediator {
    pub fn new(messages: Arc<Mutex<IcedMessages>>, computing: Arc<Mutex<bool>>) -> Self {
        Self {
            applications: HashMap::new(),
            designs: Vec::new(),
            selection: vec![],
            messages,
            current_operation: None,
            last_op: None,
            undo_stack: Vec::new(),
            redo_stack: Vec::new(),
            candidate: None,
            last_selection: None,
            computing,
            centring: None,
            center_selection: None,
            pasting: PastingMode::Nothing,
            last_selected_design: 0,
            pasting_attempt: None,
            duplication_attempt: false,
            canceling_pasting: false,
            parameters_ptr: Default::default(),
            main_state: Default::default(),
        }
    }

    pub fn update_modifiers(&mut self, modifers: ModifiersState) {
        self.notify_apps(Notification::ModifersChanged(modifers))
    }

    pub fn add_application(
        &mut self,
        application: Arc<Mutex<dyn Application>>,
        element_type: ElementType,
    ) {
        self.applications.insert(element_type, application);
    }

    pub fn change_sensitivity(&mut self, sensitivity: f32) {
        self.notify_apps(Notification::NewSensitivity(sensitivity));
    }

    pub fn change_action_mode(&mut self, action_mode: ActionMode) {
        self.main_state.action_mode = action_mode;
        self.notify_apps(Notification::NewActionMode(action_mode))
    }

    pub fn change_selection_mode(&mut self, selection_mode: SelectionMode) {
        self.main_state.selection_mode = selection_mode;
        self.notify_apps(Notification::NewSelectionMode(selection_mode))
    }

    pub fn request_fits(&mut self) {
        self.notify_apps(Notification::FitRequest)
    }

    pub fn add_design(&mut self, design: Arc<RwLock<Design>>) {
        self.drop_undo_stack();
        if design
            .read()
            .unwrap()
            .has_at_least_on_strand_with_insertions()
        {
            message(
                "Your design contains insertions and/or deletions. These are not very well \
            handled by ENSnano at the moment and the current solution is to replace them by single \
            strands on helices specially created for that puropse."
                    .into(),
                rfd::MessageLevel::Warning,
            );
            design.write().unwrap().replace_insertions_by_helices();
        }
        self.parameters_ptr = ParameterPtr(Arc::new(design.read().unwrap().get_dna_parameters()));
        self.designs.push(design.clone());
        self.notify_apps(Notification::NewDesign(design));
        self.request_fits();
    }

    pub fn change_strand_color(&mut self, color: u32) {
        for s in self.selection.iter() {
            if let Selection::Strand(design_id, strand_id) = s {
                self.designs[*design_id as usize]
                    .write()
                    .unwrap()
                    .change_strand_color(*strand_id as usize, color)
            }
        }
    }

    pub fn change_sequence(&mut self, sequence: String) {
        for s in self.selection.iter() {
            if let Selection::Strand(design_id, strand_id) = s {
                self.designs[*design_id as usize]
                    .write()
                    .unwrap()
                    .change_strand_sequence(*strand_id as usize, sequence.clone())
            }
        }
    }

    pub fn set_scaffold(&mut self, scaffold_id: Option<usize>) {
        let d_id = if let Some(d_id) = self.selected_design() {
            d_id as usize
        } else {
            if self.designs.len() > 1 {
                message(
                    "No design selected, setting scaffold for design 0".into(),
                    rfd::MessageLevel::Warning,
                );
            }
            0
        };
        self.designs[d_id]
            .write()
            .unwrap()
            .set_scaffold_id(scaffold_id)
    }

    pub fn set_scaffold_shift(&mut self, shift: usize) {
        self.designs[0].write().unwrap().set_scaffold_shift(shift);
    }

    pub fn set_scaffold_sequence(
        &mut self,
        sequence: String,
        requests: Arc<Mutex<Requests>>,
        shift: usize,
    ) {
        let d_id = if let Some(d_id) = self.selected_design() {
            d_id as usize
        } else {
            if self.designs.len() > 1 {
                message(
                    "No design selected, setting sequence for design 0".into(),
                    rfd::MessageLevel::Warning,
                );
            }
            0
        };
        self.designs[d_id]
            .write()
            .unwrap()
            .set_scaffold_sequence(sequence, shift);
        if self.designs[d_id].read().unwrap().scaffold_is_set() {
            let message = format!("Optimize the scaffold position ?\n
            If you chose \"Yes\", icednano will position the scaffold in a way that minimizes the number of anti-patern (G^4, C^4 (A|T)^7) in the stapples sequence. If you chose \"No\", the scaffold sequence will begin at position {}", shift);
            yes_no_dialog(
                message.into(),
                requests.clone(),
                KeepProceed::OptimizeShift(d_id as usize),
                None,
            )
        }
    }

    pub fn optimize_shift(&mut self, d_id: usize) {
        let computing = self.computing.clone();
        let design = self.designs[d_id].clone();
        let messages = self.messages.clone();
        std::thread::spawn(move || {
            let (send, rcv) = std::sync::mpsc::channel::<f32>();
            std::thread::spawn(move || {
                *computing.lock().unwrap() = true;
                let (position, score) = design.read().unwrap().optimize_shift(send);
                let msg = format!("Scaffold position set to {}\n {}", position, score);
                message(msg.into(), rfd::MessageLevel::Info);
                *computing.lock().unwrap() = false;
            });
            while let Ok(progress) = rcv.recv() {
                messages
                    .lock()
                    .unwrap()
                    .push_progress("Optimizing position".to_string(), progress)
            }
            messages.lock().unwrap().finish_progess();
        });
    }

    pub fn download_stapples(&self, requests: Arc<Mutex<Requests>>) {
        let d_id = if let Some(d_id) = self.selected_design() {
            d_id as usize
        } else {
            if self.designs.len() > 1 {
                message(
                    "No design selected, Downloading stapples design 0".into(),
                    rfd::MessageLevel::Warning,
                );
            }
            0
        };
        if !self.designs[d_id].read().unwrap().scaffold_is_set() {
            message(
                "No scaffold set. \n
                    Chose a strand and set it as the scaffold by checking the scaffold checkbox\
                    in the status bar"
                    .into(),
                rfd::MessageLevel::Error,
            );
            return;
        }
        if !self.designs[d_id].read().unwrap().scaffold_sequence_set() {
            message(
                "No sequence uploaded for scaffold. \n
                Upload a sequence for the scaffold by pressing the \"Load scaffold\" button"
                    .into(),
                rfd::MessageLevel::Error,
            );
            return;
        }
        if let Some(nucl) = self.designs[d_id].read().unwrap().get_stapple_mismatch() {
            let _msg = format!(
                "All stapples are not paired \n
                first unpaired nucleotide {:?}",
                nucl
            );
            //message(msg.into(), rfd::MessageLevel::Warning);
        }

        let scaf_len = self.designs[d_id]
            .read()
            .unwrap()
            .get_scaffold_len()
            .unwrap();
        let scaf_seq_len = self.designs[d_id]
            .read()
            .unwrap()
            .get_scaffold_sequence_len()
            .unwrap();
        if scaf_len != scaf_seq_len {
            let msg = format!(
                "The scaffod length does not match its sequence\n
                Length of the scaffold {}\n
                Length of the sequence {}\n
                Proceed anyway ?",
                scaf_len, scaf_seq_len
            );

            yes_no_dialog(
                msg.into(),
                requests.clone(),
                KeepProceed::Stapples(d_id),
                None,
            );
        } else {
            requests.lock().unwrap().keep_proceed = Some(KeepProceed::Stapples(d_id))
        }
    }

    pub fn proceed_stapples(&mut self, design_id: usize, path: PathBuf) {
        let stapples = self.designs[design_id].read().unwrap().get_stapples();
        /*
        let path = if cfg!(target_os = "windows") {
            let (snd, rcv) = std::sync::mpsc::channel();
            std::thread::spawn(move || {
                let xls_file = FileDialog::new()
                    .add_filter("Excel file", &["xlsx"])
                    .show_save_single_file();
                snd.send(xls_file.ok().and_then(|x| x)).unwrap()
            });
            rcv.recv().unwrap()
        } else {
            use nfd2::Response;
            let result = match nfd2::open_save_dialog(Some("xlsx"), None).expect("oh no") {
                Response::Okay(file_path) => Some(file_path),
                Response::OkayMultiple(_) => {
                    println!("Please open only one file");
                    None
                }
                Response::Cancel => None,
            };
            result
        };*/
        write_stapples(stapples, path);
    }

    pub fn set_persistent_phantom(&mut self, persistent: bool) {
        match self.selection.get(0) {
            Some(Selection::Grid(d_id, g_id)) => self.designs[*d_id as usize]
                .read()
                .unwrap()
                .set_persistent_phantom(&g_id, persistent),
            _ => println!("Selection is not a grid"),
        }
    }

    pub fn set_small_spheres(&mut self, small: bool) {
        match self.selection.get(0) {
            Some(Selection::Grid(d_id, g_id)) => self.designs[*d_id as usize]
                .read()
                .unwrap()
                .set_small_spheres(&g_id, small),
            _ => println!("Selection is not a grid"),
        }
    }

    pub fn save_design(&mut self, path: &PathBuf) {
        if let Some(d_id) = self.selected_design() {
            self.notify_apps(Notification::Save(d_id as usize));
            self.designs[d_id as usize].read().unwrap().save_to(path)
        } else {
            self.notify_apps(Notification::Save(0));
            self.designs[0].read().unwrap().save_to(path);
            if self.designs.len() > 1 {
                message(
                    "No design selected, saved design 0".into(),
                    rfd::MessageLevel::Warning,
                );
            }
        }
    }

    pub fn clear_designs(&mut self) {
        for d in self.designs.iter() {
            d.read().unwrap().notify_death()
        }
        self.designs = vec![];
        self.notify_apps(Notification::ClearDesigns)
    }

    pub fn notify_multiple_selection(&mut self, selection: Vec<Selection>, app_id: AppId) {
        self.selection = selection.clone();
        self.last_selection = Some((selection.clone(), app_id));
        self.cancel_pasting();
        self.finish_op();

        if selection.len() == 1 {
            let selection = selection[0];
            if let Some(d_id) = selection.get_design() {
                let values = selection.fetch_values(self.designs[d_id as usize].clone());
                self.last_selected_design = d_id as usize;
                self.messages
                    .lock()
                    .unwrap()
                    .push_selection(selection, values);
            } else {
                self.messages
                    .lock()
                    .unwrap()
                    .push_selection(Selection::Nothing, vec![])
            }
        } else if selection.len() == 0 {
            self.messages
                .lock()
                .unwrap()
                .push_selection(Selection::Nothing, vec![])
        }
    }

    fn cancel_pasting(&mut self) {
        self.pasting = PastingMode::Nothing;
        self.notify_all_designs(AppNotification::ResetCopyPaste);
        self.canceling_pasting = true;
    }

    pub fn notify_unique_selection(&mut self, selection: Selection, app_id: AppId) {
        self.cancel_pasting();
        self.finish_op();
        self.selection = vec![selection];
        self.last_selection = Some((vec![selection], app_id));
        if selection.is_strand() {
            let mut messages = self.messages.lock().unwrap();
            if let Selection::Strand(d_id, s_id) = selection {
                let design = self.designs[d_id as usize].read().unwrap();
                if let Some(color) = design.get_strand_color(s_id as usize) {
                    messages.push_color(color);
                }
                if let Some(sequence) = design.get_strand_sequence(s_id as usize) {
                    messages.push_sequence(sequence);
                }
            }
        }
        if let Selection::Helix(d_id, h_id) = selection {
            let roll = self.designs[d_id as usize]
                .read()
                .unwrap()
                .get_roll_helix(h_id as usize);
            if let Some(roll) = roll {
                self.messages.lock().unwrap().push_roll(roll)
            }
        } else if let Selection::Nucleotide(d_id, nucl) = selection {
            self.designs[d_id as usize]
                .write()
                .unwrap()
                .shake_nucl(nucl)
        }
        if let Some(d_id) = selection.get_design() {
            let values = selection.fetch_values(self.designs[d_id as usize].clone());
            self.last_selected_design = d_id as usize;
            self.messages
                .lock()
                .unwrap()
                .push_selection(selection, values);
        } else {
            self.messages
                .lock()
                .unwrap()
                .push_selection(Selection::Nothing, vec![])
        }
    }

    /// Show/Hide the DNA sequences
    pub fn toggle_text(&mut self, value: bool) {
        self.notify_apps(Notification::ToggleText(value));
    }

    pub fn notify_apps(&mut self, notification: Notification) {
        for app_wrapper in self.applications.values().cloned() {
            let mut app = app_wrapper.lock().unwrap();
            app.on_notify(notification.clone());
        }
    }

    fn notify_all_designs(&mut self, notification: AppNotification) {
        for design_wrapper in self.designs.clone() {
            design_wrapper
                .write()
                .unwrap()
                .on_notify(notification.clone())
        }
    }

    fn notify_designs(&mut self, designs: &HashSet<u32>, notification: AppNotification) {
        for design_id in designs.iter() {
            self.designs.clone()[*design_id as usize]
                .write()
                .unwrap()
                .on_notify(notification.clone());
            //design.on_notify(notification.clone(), self);
        }
    }

    pub fn make_grids(&mut self) {
        if let Some((d_id, h)) = list_of_helices(&self.selection) {
            let designs: HashSet<u32> = [d_id as u32].iter().cloned().collect();
            self.notify_designs(&designs, AppNotification::MakeGrids(h));
        }
        //self.notify_all_designs(AppNotification::MakeAllGrids)
    }

    /// Querry designs for modifcations that must be notified to the applications
    pub fn observe_designs(&mut self) -> bool {
        let mut ret = false;
        let mut notifications = Vec::new();
        for design_wrapper in self.designs.clone() {
            if let Some(notification) = design_wrapper.read().unwrap().view_was_updated() {
                ret = true;
                notifications.push(Notification::DesignNotification(notification))
            }
            if let Some(notification) = design_wrapper.read().unwrap().data_was_updated() {
                let scaffold_info = design_wrapper.read().unwrap().get_scaffold_info();
                self.messages
                    .lock()
                    .unwrap()
                    .push_scaffold_info(scaffold_info);
                ret = true;
                notifications.push(Notification::DesignNotification(notification))
            }
        }
        if let Some(elements) = self
            .designs
            .get(0)
            .and_then(|d| d.read().unwrap().get_new_elements())
        {
            self.messages.lock().unwrap().push_dna_elements(elements);
        }
        for notification in notifications {
            self.notify_apps(notification)
        }
        if let Some((candidate, app_id)) = self.candidate.take() {
            ret = true;
            if candidate.len() == 1 && self.last_op.is_none() {
                if let Some(d_id) = candidate[0].get_design() {
                    let values = candidate[0].fetch_values(self.designs[d_id as usize].clone());
                    self.messages
                        .lock()
                        .unwrap()
                        .push_candidate(candidate[0], values);
                }
            }
            self.notify_apps(Notification::NewCandidate(candidate, app_id))
        }
        if let Some(nucl) = self.pasting_attempt.take() {
            match self.pasting {
                PastingMode::Pasting | PastingMode::FirstDulplication => {
                    let result = self.designs[self.last_selected_design]
                        .write()
                        .unwrap()
                        .paste(nucl);

                    if let Some((initial_state, final_state)) = result {
                        self.finish_op();
                        self.undo_stack.push(Arc::new(BigStrandModification {
                            initial_state,
                            final_state,
                            reverse: false,
                            design_id: self.last_selected_design,
                        }));
                        self.redo_stack.clear();
                        self.pasting.place_paste();
                        self.notify_apps(Notification::Pasting(self.pasting.is_placing_paste()));
                    }
                }
                _ => {
                    let result = self.designs[self.last_selected_design]
                        .write()
                        .unwrap()
                        .paste_xover(nucl);
                    if let Some((initial_state, final_state)) = result {
                        self.finish_op();
                        self.undo_stack.push(Arc::new(BigStrandModification {
                            initial_state,
                            final_state,
                            reverse: false,
                            design_id: self.last_selected_design,
                        }));
                        self.redo_stack.clear();
                        self.pasting.place_paste();
                        self.notify_apps(Notification::Pasting(self.pasting.is_placing_paste()));
                    }
                }
            }
        }
        if self.duplication_attempt {
            if self.pasting.strand() {
                let paste_result = self.designs[self.last_selected_design]
                    .write()
                    .unwrap()
                    .apply_duplication();
                if let Some((initial_state, final_state)) = paste_result {
                    self.finish_op();
                    self.undo_stack.push(Arc::new(BigStrandModification {
                        initial_state,
                        final_state,
                        reverse: false,
                        design_id: self.last_selected_design,
                    }));
                    self.redo_stack.clear();
                } else {
                    self.pasting = PastingMode::FirstDulplication;
                }
            } else if self.pasting.xover() {
                let result = self.designs[self.last_selected_design]
                    .write()
                    .unwrap()
                    .apply_duplication_xover();
                if let Some((initial_state, final_state)) = result {
                    self.finish_op();
                    self.undo_stack.push(Arc::new(BigStrandModification {
                        initial_state,
                        final_state,
                        reverse: false,
                        design_id: self.last_selected_design,
                    }));
                    self.redo_stack.clear();
                } else {
                    self.pasting = PastingMode::FirstDulplicationXover;
                }
            }
            self.notify_apps(Notification::Pasting(self.pasting.is_placing_paste()));
            self.duplication_attempt = false;
        }
        if let Some((selection, app_id)) = self.last_selection.take() {
            ret = true;
            let can_make_grid = all_helices_no_grid(&selection, self.designs.as_slice());
            self.messages
                .lock()
                .unwrap()
                .push_can_make_grid(can_make_grid);
            let organizer_selection: Vec<DnaElementKey> = selection
                .iter()
                .filter_map(|s| DnaElementKey::from_selection(s, 0))
                .collect();
            self.messages
                .lock()
                .unwrap()
                .push_organizer_selection(organizer_selection);
            self.notify_apps(Notification::Selection3D(selection, app_id));
        }

        if let Some(centring) = self.centring.take() {
            ret = true;
            self.notify_apps(Notification::NewSelectionMode(SelectionMode::Nucleotide));
            self.notify_apps(Notification::Centering(centring.0, centring.1))
        }

        if let Some(center_selection) = self.center_selection.take() {
            ret = true;
            self.notify_apps(Notification::CenterSelection(
                center_selection.0,
                center_selection.1,
            ));
        }

        if self.canceling_pasting {
            self.canceling_pasting = false;
            self.notify_apps(Notification::Pasting(false))
        }
        self.update_application_state();
        ret
    }

    fn update_application_state(&self) {
        let state = self.get_application_state();
        self.messages.lock().unwrap().push_application_state(state);
    }

    fn selected_design(&self) -> Option<u32> {
        self.selection.get(0).and_then(Selection::get_design)
    }

    /// Update the current operation.
    ///
    /// This method is called when an operation is performed in the scene. If the operation is
    /// compatible with the last operation it is treated as an eddition of the last operation.
    /// Otherwise the last operation is considered finished.
    pub fn update_opperation(&mut self, operation: Arc<dyn Operation>) {
        // If the operation is compatible with the last operation, the last operation is eddited.
        if *self.computing.lock().unwrap() {
            return;
        }
        /*
        let operation = if let Some(op) = self
            .last_op
            .as_ref()
            .and_then(|op| operation.compose(op.as_ref()))
        {
            op
        } else {
            // Otherwise, the last operation is saved on the undo stack.
            self.finish_pending();
            operation
        };
        */
        self.finish_pending();
        let target = operation.target();
        let effect = operation.effect();
        if let Some(current_op) = self.current_operation.as_ref() {
            // If there already is a current operation. We test if the current operation is being
            // eddited.
            if current_op.descr() == operation.descr() && current_op.must_reverse() {
                let rev_op = current_op.reverse();
                let target = current_op.target();
                self.apply_operation(target, rev_op.effect());
            } else if current_op.descr() != operation.descr() {
                self.finish_op();
            }
        }
        self.messages.lock().unwrap().push_op(operation.clone());

        let result = self.apply_operation(target, effect);

        match result {
            OperationResult::UndoableChange => {
                if operation.drop_undo() {
                    self.drop_undo_stack();
                    self.current_operation = None;
                } else {
                    self.current_operation = Some(operation);
                }
            }
            OperationResult::NoChange => (),
            OperationResult::BigChange(init, after) => {
                self.current_operation = None;
                self.undo_stack.push(Arc::new(BigStrandModification {
                    initial_state: init,
                    final_state: after,
                    reverse: false,
                    design_id: self.last_selected_design,
                }))
            }
        }
    }

    /// Update the pending operation.
    ///
    /// This method is called when a parameter of the pending operation is modified in the status
    /// bar.
    pub fn update_pending(&mut self, operation: Arc<dyn Operation>) {
        if *self.computing.lock().unwrap() {
            return;
        }
        let target = operation.target();
        let effect = operation.effect();
        if let Some(current_op) = self.last_op.as_ref() {
            if current_op.descr() == operation.descr() {
                let rev_op = current_op.reverse();
                let target = current_op.target();
                self.apply_operation(target, rev_op.effect());
            } else {
                self.finish_op();
            }
        }
        self.last_op = Some(operation.clone());
        self.apply_operation(target, effect);
    }

    /// Save the last operation and the pending operation on the undo stack.
    pub fn finish_op(&mut self) {
        if *self.computing.lock().unwrap() {
            return;
        }
        if let Some(op) = self.last_op.take() {
            self.messages.lock().unwrap().clear_op();
            self.notify_all_designs(AppNotification::MovementEnded);
            self.undo_stack.push(op);
            self.redo_stack.clear();
        }
        if let Some(op) = self.current_operation.take() {
            self.messages.lock().unwrap().clear_op();
            self.notify_all_designs(AppNotification::MovementEnded);
            self.undo_stack.push(op);
            self.redo_stack.clear();
        }
    }

    /// Save the pending operation on the undo stack.
    fn finish_pending(&mut self) {
        if let Some(op) = self.last_op.take() {
            self.notify_all_designs(AppNotification::MovementEnded);
            self.undo_stack.push(op);
            self.redo_stack.clear();
        }
    }

    /// Suspend the current operation.
    ///
    /// This means that the current drag and drop movement is finished, but the current operation
    /// can still be modified in the satus bar or by initiating a combatible new operation.
    pub fn suspend_op(&mut self) {
        if let Some(op) = self.current_operation.take() {
            self.last_op = Some(op)
        }
    }

    pub fn undo(&mut self) {
        /*
        if let Some(op) = self.last_op.take() {
            let rev_op = op.reverse();
            let target = {
                let mut set = HashSet::new();
                set.insert(rev_op.target() as u32);
                set
            };
            self.notify_designs(&target, rev_op.effect());
            self.notify_all_designs(AppNotification::MovementEnded);
            self.redo_stack.push(rev_op);
        } else if let Some(op) = self.current_operation.take() {
            let rev_op = op.reverse();
            let target = {
                let mut set = HashSet::new();
                set.insert(rev_op.target() as u32);
                set
            };
            self.notify_designs(&target, rev_op.effect());
            self.notify_all_designs(AppNotification::MovementEnded);
            self.redo_stack.push(rev_op);
        } else         */
        self.suspend_op();
        self.finish_pending();
        if let Some(op) = self.undo_stack.pop() {
            //println!("effect {:?}", op.effect());
            let rev_op = op.reverse();
            //println!("reversed effect {:?}", rev_op.effect());
            self.apply_operation(rev_op.target(), rev_op.effect());
            self.notify_all_designs(AppNotification::MovementEnded);
            if rev_op.redoable() {
                self.redo_stack.push(rev_op);
            } else {
                self.redo_stack.clear();
            }
            self.notify_multiple_selection(vec![], AppId::Mediator);
        }
    }

    fn apply_operation(&mut self, target: usize, effect: UndoableOp) -> OperationResult {
        self.designs[target]
            .write()
            .unwrap()
            .apply_operation(effect)
    }

    pub fn redo(&mut self) {
        if let Some(op) = self.redo_stack.pop() {
            let rev_op = op.reverse();
            //println!("{:?}", rev_op);
            let target = rev_op.target();
            self.apply_operation(target, rev_op.effect());
            self.notify_all_designs(AppNotification::MovementEnded);
            self.undo_stack.push(rev_op);
        }
    }

    pub fn set_candidate(
        &mut self,
        candidate: Option<PhantomElement>,
        selection: Vec<Selection>,
        app_id: AppId,
    ) {
        let nucl = candidate.map(|c| c.to_nucl());
        if self.pasting.is_placing_paste() {
            if self.pasting.strand() {
                self.designs[self.last_selected_design]
                    .write()
                    .unwrap()
                    .request_paste_candidate(nucl)
            } else if self.pasting.xover() {
                self.designs[self.last_selected_design]
                    .write()
                    .unwrap()
                    .request_paste_candidate_xover(nucl);
            }
        }
        self.candidate = Some((selection, app_id))
    }

    pub fn set_paste_candidate(&mut self, candidate: Option<Nucl>) {
        if self.pasting.is_placing_paste() {
            if self.pasting.strand() {
                self.designs[self.last_selected_design]
                    .write()
                    .unwrap()
                    .request_paste_candidate(candidate)
            } else if self.pasting.xover() {
                self.designs[self.last_selected_design]
                    .write()
                    .unwrap()
                    .request_paste_candidate_xover(candidate);
            }
        }
    }

    pub fn request_centering(&mut self, nucl: Nucl, design_id: usize) {
        self.centring = Some((nucl, design_id))
    }

    pub fn request_center_selection(&mut self, selection: Selection, app_id: AppId) {
        self.center_selection = Some((selection, app_id));
    }

    pub fn request_camera_rotation(&mut self, rotation: (f32, f32, f32)) {
        self.notify_apps(Notification::CameraRotation(
            rotation.0, rotation.1, rotation.2,
        ))
    }

    pub fn set_camera_target(&mut self, target: (Vec3, Vec3)) {
        self.notify_apps(Notification::CameraTarget(target))
    }

    pub fn recolor_stapples(&mut self) {
        for d in self.designs.iter() {
            d.write().unwrap().recolor_stapples();
        }
    }

    pub fn clean_designs(&mut self) {
        if !*self.computing.lock().unwrap() {
            for d in self.designs.iter() {
                d.write().unwrap().clean_up_domains()
            }
        }
    }

    pub fn roll_request(&mut self, request: SimulationRequest) {
        for d in self.designs.iter() {
            d.write()
                .unwrap()
                .roll_request(request.clone(), self.computing.clone());
        }
    }

    pub fn stop_roll(&mut self) {
        let request = SimulationRequest {
            roll: false,
            springs: false,
            target_helices: None,
        };
        for d in self.designs.iter() {
            d.write()
                .unwrap()
                .roll_request(request.clone(), self.computing.clone());
        }
    }

    pub fn rigid_grid_request(&mut self, request: RigidBodyParametersRequest) {
        let parameters = rigid_parameters(request);
        let d = &self.designs[self.last_selected_design];
        let state_opt = d.write().unwrap().grid_simulation(
            (0., 1.),
            self.computing.clone(),
            parameters.clone(),
        );
        if let Some(initial_state) = state_opt {
            self.finish_op();
            self.undo_stack.push(Arc::new(RigidGridSimulation {
                initial_state,
                design_id: self.last_selected_design,
            }));
            self.redo_stack.clear();
        }
    }

    pub fn rigid_helices_request(&mut self, request: RigidBodyParametersRequest) {
        let parameters = rigid_parameters(request);
        let d = &self.designs[self.last_selected_design];
        let state_opt = d.write().unwrap().rigid_helices_simulation(
            (0., 0.1),
            self.computing.clone(),
            parameters.clone(),
        );
        if let Some(initial_state) = state_opt {
            self.finish_op();
            self.undo_stack.push(Arc::new(RigidHelixSimulation {
                initial_state,
                design_id: self.last_selected_design,
            }));
            self.redo_stack.clear();
        }
        println!("self.computing {:?}", self.computing);
    }

    pub fn rigid_parameters_request(&mut self, request: RigidBodyParametersRequest) {
        let parameters = rigid_parameters(request);
        for d in self.designs.iter() {
            d.write()
                .unwrap()
                .rigid_body_parameters_update(parameters.clone());
        }
    }

    pub fn hyperboloid_update(&mut self, request: HyperboloidRequest) {
        if let Some(design) = self.designs.get(0) {
            design.write().unwrap().update_hyperboloid(
                request.radius,
                request.shift,
                request.length,
                request.radius_shift,
            );
        }
    }

    pub fn finalize_hyperboloid(&mut self) {
        if let Some(design) = self.designs.get(0) {
            design.write().unwrap().finalize_hyperboloid()
        }
        self.drop_undo_stack();
    }

    pub fn cancel_hyperboloid(&mut self) {
        if let Some(design) = self.designs.get(0) {
            design.write().unwrap().cancel_hyperboloid()
        }
    }

    pub fn roll_helix(&mut self, roll: f32) {
        for h in self.selection.iter() {
            if let Selection::Helix(d_id, h_id) = h {
                self.designs[*d_id as usize]
                    .write()
                    .unwrap()
                    .roll_helix(*h_id as usize, roll);
            }
        }
    }

    /// Request a cross-over between source and nucl.
    /// The design chose to accept the request depending on the rules defined in
    /// `design::operation::general_cross_over`
    pub fn xover_request(&mut self, source: Nucl, target: Nucl, design_id: usize) {
        let states = self.designs[design_id]
            .read()
            .unwrap()
            .general_cross_over(source, target);

        if let Some((initial_state, final_state)) = states {
            self.finish_op();
            self.undo_stack.push(Arc::new(BigStrandModification {
                initial_state,
                final_state,
                reverse: false,
                design_id: self.last_selected_design,
            }));
            self.redo_stack.clear();
        }
    }

    pub fn show_torsion_request(&mut self, show: bool) {
        self.notify_apps(Notification::ShowTorsion(show))
    }

    pub fn request_copy(&mut self) {
        self.pasting = PastingMode::Nothing;
        self.notify_all_designs(AppNotification::ResetCopyPaste);
        println!("selection : {:?}", self.selection);
        let strand_opt = list_of_strands(&self.selection, &self.designs);
        let xover_opt = list_of_xovers(&self.selection, &self.designs);
        if let Some((d_id, s_ids)) = strand_opt {
            self.designs[d_id as usize]
                .write()
                .unwrap()
                .request_copy_strands(s_ids);
        } else if let Some((d_id, xover_ids)) = xover_opt {
            let copy = self.designs[d_id as usize]
                .write()
                .unwrap()
                .request_copy_xovers(xover_ids);
            println!("copy success: {}", copy);
        }
    }

    pub fn request_pasting_mode(&mut self) {
        if self.designs[self.last_selected_design]
            .read()
            .unwrap()
            .has_template()
        {
            self.pasting = PastingMode::Pasting;
        } else if self.designs[self.last_selected_design]
            .read()
            .unwrap()
            .has_xovers_copy()
        {
            self.pasting = PastingMode::PastingXover
        }
        println!("{:?}", self.pasting);
        self.notify_apps(Notification::Pasting(self.pasting.is_placing_paste()));
    }

    pub fn request_duplication(&mut self) {
        match self.pasting {
            PastingMode::Nothing => {
                if self.designs[self.last_selected_design]
                    .read()
                    .unwrap()
                    .has_template()
                {
                    self.pasting = PastingMode::FirstDulplication;
                } else if self.designs[self.last_selected_design]
                    .read()
                    .unwrap()
                    .has_xovers_copy()
                {
                    self.pasting = PastingMode::FirstDulplicationXover;
                }
            }
            PastingMode::Pasting => {
                self.pasting = PastingMode::FirstDulplication;
            }
            PastingMode::Duplicating => {
                self.duplication_attempt = true;
            }
            PastingMode::PastingXover => {
                self.pasting = PastingMode::FirstDulplicationXover;
            }
            PastingMode::DuplicatingXover => {
                self.duplication_attempt = true;
            }
            PastingMode::FirstDulplicationXover => (),
            PastingMode::FirstDulplication => (),
        }
        if self.pasting.is_placing_paste() {
            self.change_selection_mode(SelectionMode::Nucleotide);
        }
        self.notify_apps(Notification::Pasting(self.pasting.is_placing_paste()));
    }

    pub fn attempt_paste(&mut self, nucl: Option<Nucl>) {
        println!("Attempt paste {:?}", nucl);
        if let Some(nucl) = nucl {
            if self.pasting.is_placing_paste() {
                self.pasting_attempt = Some(nucl);
            }
        } else {
            self.cancel_pasting();
        }
    }

    pub fn request_anchor(&mut self) {
        let selection = self.selection.get(0).cloned();
        if let Some(Selection::Nucleotide(d_id, nucl)) = selection {
            self.designs[d_id as usize]
                .write()
                .unwrap()
                .add_anchor(nucl);
            //self.notify_unique_selection(selection.unwrap());
        }
    }

    pub fn new_shift_hyperboloid(&mut self, shift: f32) {
        if let Some(Selection::Grid(d_id, g_id)) = self.selection.get(0) {
            self.designs[*d_id as usize]
                .write()
                .unwrap()
                .set_new_shift(*g_id, shift)
        }
    }

    pub fn organizer_selection(&mut self, selection: Vec<DnaElementKey>) {
        let selection: Vec<Selection> = selection.iter().map(|k| k.to_selection(0)).collect();
        self.notify_multiple_selection(selection, AppId::Organizer);
    }

    pub fn organizer_candidates(&mut self, candidates: Vec<DnaElementKey>) {
        let candidates: Vec<Selection> = candidates.iter().map(|k| k.to_selection(0)).collect();
        self.candidate = Some((candidates, AppId::Organizer));
    }

    pub fn update_attribute(&mut self, attribute: DnaAttribute, elements: Vec<DnaElementKey>) {
        if let Some(d) = self.designs.get_mut(0) {
            d.write().unwrap().update_attribute(attribute, elements)
        }
    }

    pub fn update_tree(&mut self, tree: OrganizerTree<DnaElementKey>) {
        if let Some(d) = self.designs.get_mut(0) {
            d.write().unwrap().update_organizer_tree(tree)
        }
    }

    pub fn oxdna_export(&self) {
        if let Some(d) = self.designs.get(0) {
            d.read().unwrap().oxdna_export()
        }
    }

    pub fn split_2d(&mut self) {
        self.notify_apps(Notification::Split2d)
    }

    pub fn make_everything_visible(&mut self) {
        if let Some(d) = self.designs.get_mut(0) {
            d.write().unwrap().clear_visibility_sive();
        }
    }

    pub fn toggle_visibility(&mut self, compl: bool) {
        if let Some(d) = self.designs.get_mut(0) {
            d.write()
                .unwrap()
                .set_visibility_sieve(self.selection.clone(), compl)
        }
    }

    pub fn redim_2d_helices(&mut self, all: bool) {
        self.notify_apps(Notification::Redim2dHelices(all))
    }

    fn drop_undo_stack(&mut self) {
        self.current_operation = None;
        self.last_op = None;
        self.undo_stack.clear();
        self.redo_stack.clear();
    }

    pub fn toggle_widget(&mut self) {
        self.main_state.axis_aligned ^= true;
        self.notify_apps(Notification::ToggleWidget(self.main_state.axis_aligned));
    }

    pub fn delete_selection(&mut self) {
        println!("selection {:?}", self.selection);
        if self.selection.len() == 1 {
            if let Selection::Helix(d_id, h_id) = self.selection[0] {
                if self.designs[d_id as usize]
                    .read()
                    .unwrap()
                    .helix_is_empty(h_id as usize)
                {
                    let helix = self.designs[d_id as usize]
                        .read()
                        .unwrap()
                        .get_raw_helix(h_id as usize)
                        .unwrap();
                    self.update_opperation(Arc::new(RawHelixCreation {
                        helix,
                        helix_id: h_id as usize,
                        design_id: d_id as usize,
                        delete: true,
                    }));
                }
                self.notify_multiple_selection(vec![], AppId::Mediator);
                self.notify_apps(Notification::Selection3D(vec![], AppId::Mediator));
                return;
            }
        }
        if let Some((initial_state, final_state)) = self.designs[self.last_selected_design as usize]
            .write()
            .unwrap()
            .delete_selection(self.selection.clone())
        {
            self.undo_stack.push(Arc::new(BigStrandModification {
                initial_state,
                final_state,
                reverse: false,
                design_id: self.last_selected_design,
            }));
            self.redo_stack.clear();
        }
        self.notify_multiple_selection(vec![], AppId::Mediator);
        self.notify_apps(Notification::Selection3D(vec![], AppId::Mediator));
    }

    pub fn select_scaffold(&mut self) {
        let scaffold_info = self.designs[0].read().unwrap().get_scaffold_info();
        if let Some(info) = scaffold_info {
            self.notify_unique_selection(Selection::Strand(0, info.id as u32), AppId::Mediator)
        }
    }

    pub fn rendering_mode(&mut self, mode: RenderingMode) {
        self.notify_apps(Notification::RenderingMode(mode));
    }

    pub fn background3d(&mut self, bg: Background3D) {
        self.notify_apps(Notification::Background3D(bg));
    }

    fn get_application_state(&self) -> ApplicationState {
        let can_undo = !self.undo_stack.is_empty()
            || self.current_operation.is_some()
            || self.last_op.is_some();
        let can_redo = !self.redo_stack.is_empty()
            && self.current_operation.is_none()
            && self.last_op.is_none();
        let simulation_state = self.designs[0].read().unwrap().get_simulation_state();
        ApplicationState {
            can_redo,
            can_undo,
            simulation_state,
            parameter_ptr: self.parameters_ptr.clone(),
            axis_aligned: self.main_state.axis_aligned,
            action_mode: self.main_state.action_mode.clone(),
            selection_mode: self.main_state.selection_mode.clone(),
        }
    }
}

#[derive(Debug, Clone)]
pub enum AppNotification {
    MovementEnded,
    ResetCopyPaste,
    MakeGrids(Vec<usize>),
}

pub enum UndoableOp {
    Rotation(DesignRotation),
    Translation(DesignTranslation),
    AddGridHelix(GridHelixDescriptor, isize, usize),
    RmGridHelix(GridHelixDescriptor, isize, usize),
    RawHelixCreation {
        helix: Helix,
        delete: bool,
        h_id: usize,
    },
    Cut {
        strand: Strand,
        nucl: Nucl,
        undo: bool,
        s_id: usize,
    },
    Xover {
        strand_5prime: Strand,
        strand_3prime: Strand,
        undo: bool,
        prime5_id: usize,
        prime3_id: usize,
    },
    CrossCut {
        source_strand: Strand,
        target_strand: Strand,
        target_3prime: bool,
        source_id: usize,
        target_id: usize,
        nucl: Nucl,
        undo: bool,
    },
    RmStrand {
        strand: Strand,
        strand_id: usize,
        undo: bool,
    },
    MakeAllGrids,
    AddGrid(GridDescriptor),
    MoveBuilder(Box<StrandBuilder>, Option<(usize, u32)>),
    ResetBuilder(Box<StrandBuilder>),
    RmGrid,
    NewHyperboloid {
        position: Vec3,
        orientation: ultraviolet::Rotor3,
        hyperboloid: Hyperboloid,
    },
    ClearHyperboloid,
    NewStrandState(StrandState),
    ResetCopyPaste,
    UndoGridSimulation(crate::design::GridSystemState),
    UndoHelixSimulation(crate::design::RigidHelixState),
}

fn write_stapples(stapples: Vec<Stapple>, path: PathBuf) {
    use std::collections::BTreeMap;
    let mut wb = Workbook::create(path.to_str().unwrap());
    let mut sheets = BTreeMap::new();

    for stapple in stapples.iter() {
        let sheet = sheets
            .entry(stapple.plate)
            .or_insert_with(|| vec![vec!["Well Position", "Name", "Sequence"]]);
        sheet.push(vec![&stapple.well, &stapple.name, &stapple.sequence]);
    }

    for (sheet_id, rows) in sheets.iter() {
        let mut sheet = wb.create_sheet(&format!("Plate {}", sheet_id));
        wb.write_sheet(&mut sheet, |sw| {
            for row in rows {
                sw.append_row(row![row[0], row[1], row[2]])?;
            }
            Ok(())
        })
        .expect("write excel error!");
    }
    wb.close().expect("close excel error!");
}

#[derive(Debug)]
enum PastingMode {
    /// No pasting beeing made
    Nothing,
    /// First duplication, being positioned by the mouse
    FirstDulplication,
    /// Repeating last duplication
    Duplicating,
    /// One time duplication
    Pasting,
    PastingXover,
    FirstDulplicationXover,
    DuplicatingXover,
}

impl PastingMode {
    fn is_placing_paste(&self) -> bool {
        match self {
            Self::FirstDulplication
            | Self::Pasting
            | Self::FirstDulplicationXover
            | Self::PastingXover => true,
            Self::Nothing | Self::Duplicating | Self::DuplicatingXover => false,
        }
    }

    fn place_paste(&mut self) {
        match self {
            Self::FirstDulplication => *self = Self::Duplicating,
            Self::Pasting => *self = Self::Nothing,
            Self::FirstDulplicationXover => *self = Self::DuplicatingXover,
            Self::PastingXover => *self = Self::Nothing,
            _ => unreachable!(),
        }
    }

    fn xover(&self) -> bool {
        match self {
            Self::FirstDulplicationXover | Self::DuplicatingXover | Self::PastingXover => true,
            _ => false,
        }
    }

    fn strand(&self) -> bool {
        match self {
            Self::Duplicating | Self::Pasting | Self::FirstDulplication => true,
            _ => false,
        }
    }
}

fn rigid_parameters(parameters: RigidBodyParametersRequest) -> RigidBodyConstants {
    let ret = RigidBodyConstants {
        k_spring: 10f32.powf(parameters.k_springs),
        k_friction: 10f32.powf(parameters.k_friction),
        mass: 10f32.powf(parameters.mass_factor),
        volume_exclusion: parameters.volume_exclusion,
        brownian_motion: parameters.brownian_motion,
        brownian_rate: 10f32.powf(parameters.brownian_rate),
        brownian_amplitude: parameters.brownian_amplitude,
    };
    println!("{:?}", ret);
    ret
}

#[derive(Clone, Debug, Default)]
pub struct ParameterPtr(Arc<DNAParameters>);

impl PartialEq for ParameterPtr {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}

impl Eq for ParameterPtr {}

impl AsRef<DNAParameters> for ParameterPtr {
    fn as_ref(&self) -> &DNAParameters {
        self.0.as_ref()
    }
}

struct MainState {
    axis_aligned: bool,
    action_mode: ActionMode,
    selection_mode: SelectionMode,
}

impl Default for MainState {
    fn default() -> Self {
        Self {
            axis_aligned: true,
            action_mode: ActionMode::Normal,
            selection_mode: SelectionMode::Nucleotide,
        }
    }
}
back to top