We are hiring ! See our job offers.
Raw File
scene.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/>.
*/
use iced_wgpu::wgpu;
use iced_winit::winit;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use ultraviolet::{Mat4, Rotor3, Vec3};

use crate::{design, mediator, utils};
use crate::{DrawArea, PhySize, WindowEvent};
use design::{Hyperboloid, Nucl};
use instance::Instance;
use mediator::{
    ActionMode, AppId, Application, CreateGrid, GridHelixCreation, GridRotation, GridTranslation,
    HelixRotation, HelixTranslation, MediatorPtr, NewHyperboloid, Notification, Operation,
    Selection, SelectionMode, StrandConstruction,
};
use utils::instance;
use wgpu::{Device, Queue};
use winit::dpi::PhysicalPosition;

/// Computation of the view and projection matrix.
mod camera;
/// Display of the scene
mod view;
use view::{
    DrawType, HandleDir, HandleOrientation, HandlesDescriptor, LetterInstance,
    RotationMode as WidgetRotationMode, RotationWidgetDescriptor, RotationWidgetOrientation, View,
    ViewUpdate,
};
pub use view::{FogParameters, GridInstance, GridTypeDescr};
/// Handling of inputs and notifications
mod controller;
use controller::{Consequence, Controller};
/// Handling of designs and internal data
mod data;
pub use controller::ClickMode;
use data::Data;
use design::{Design, DesignNotification, DesignNotificationContent};
mod element_selector;
use element_selector::{ElementSelector, SceneElement};
mod maths_3d;

type ViewPtr = Rc<RefCell<View>>;
type DataPtr = Rc<RefCell<Data>>;

/// A structure responsible of the 3D display of the designs
pub struct Scene {
    /// The update to be performed before next frame
    update: SceneUpdate,
    /// The Object that handles the drawing to the 3d texture
    view: ViewPtr,
    /// The Object thant handles the designs data
    data: DataPtr,
    /// The Object that handles input and notifications
    controller: Controller,
    /// The limits of the area on which the scene is displayed
    area: DrawArea,
    mediator: MediatorPtr,
    element_selector: ElementSelector,
}

impl Scene {
    /// Create a new scene.
    /// # Argument
    ///
    /// * `device` a reference to a `Device` object. This can be seen as a socket to the GPU
    ///
    /// * `queue` the command queue of `device`.
    ///
    /// * `window_size` the *Physical* size of the window in which the application is displayed
    ///
    /// * `area` the limits, in *physical* size of the area on which the scene is displayed
    pub fn new(
        device: Rc<Device>,
        queue: Rc<Queue>,
        window_size: PhySize,
        area: DrawArea,
        mediator: MediatorPtr,
        encoder: &mut wgpu::CommandEncoder,
    ) -> Self {
        let update = SceneUpdate::new();
        let view: ViewPtr = Rc::new(RefCell::new(View::new(
            window_size,
            area.size,
            device.clone(),
            queue.clone(),
            encoder,
        )));
        let data: DataPtr = Rc::new(RefCell::new(Data::new(view.clone())));
        let controller: Controller =
            Controller::new(view.clone(), data.clone(), window_size, area.size);
        let element_selector = ElementSelector::new(
            device,
            queue,
            controller.get_window_size(),
            view.clone(),
            data.clone(),
            area,
        );
        Self {
            view,
            data,
            update,
            controller,
            area,
            mediator,
            element_selector,
        }
    }

    /// Add a design to be rendered.
    fn add_design(&mut self, design: Arc<RwLock<Design>>) {
        self.data.borrow_mut().add_design(design)
    }

    /// Remove all designs
    fn clear_design(&mut self) {
        self.data.borrow_mut().clear_designs()
    }

    /// Input an event to the scene. The controller parse the event and return the consequence that
    /// the event must have.
    fn input(&mut self, event: &WindowEvent, cursor_position: PhysicalPosition<f64>) {
        let consequence = self
            .controller
            .input(event, cursor_position, &mut self.element_selector);
        self.read_consequence(consequence);
    }

    fn check_timers(&mut self) {
        let consequence = self.controller.check_timers();
        self.read_consequence(consequence);
    }

    fn read_consequence(&mut self, consequence: Consequence) {
        match consequence {
            Consequence::Nothing => (),
            Consequence::CameraMoved => self.notify(SceneNotification::CameraMoved),
            Consequence::CameraTranslated(dx, dy) => {
                self.controller.translate_camera(dx, dy);
                self.notify(SceneNotification::CameraMoved);
            }
            Consequence::XoverAtempt(source, target, d_id) => {
                self.attempt_xover(source, target, d_id);
                self.data.borrow_mut().end_free_xover();
            }
            Consequence::Translation(dir, x_coord, y_coord) => {
                let translation = self.view.borrow().compute_translation_handle(
                    x_coord as f32,
                    y_coord as f32,
                    dir,
                );
                if let Some(t) = translation {
                    self.translate_selected_design(t);
                }
            }
            Consequence::MovementEnded => {
                self.mediator.lock().unwrap().suspend_op();
                self.data.borrow_mut().end_movement();
                self.update_handle();
            }
            Consequence::InitRotation(x, y) => {
                self.view.borrow_mut().init_rotation(x as f32, y as f32)
            }
            Consequence::InitTranslation(x, y) => {
                self.view.borrow_mut().init_translation(x as f32, y as f32)
            }
            Consequence::Rotation(mode, x, y) => {
                let rotation = self
                    .view
                    .borrow()
                    .compute_rotation(x as f32, y as f32, mode);
                if let Some((rotation, origin, positive)) = rotation {
                    if rotation.bv.mag() > 1e-3 {
                        self.rotate_selected_desgin(rotation, origin, positive)
                    }
                } else {
                    println!("Warning rotiation was None")
                }
            }
            Consequence::Swing(x, y) => {
                let mut pivot = self.data.borrow().get_pivot_position();
                if pivot.is_none() {
                    self.data.borrow_mut().try_update_pivot_position();
                    pivot = self.data.borrow().get_pivot_position();
                }
                self.controller.set_pivot_point(pivot);
                self.controller.swing(-x, -y);
                self.notify(SceneNotification::CameraMoved);
            }
            Consequence::ToggleWidget => self.data.borrow_mut().toggle_widget_basis(false),
            Consequence::BuildEnded(d_id, id) => {
                self.select(Some(SceneElement::DesignElement(d_id, id)))
            }
            Consequence::Undo => self.mediator.lock().unwrap().undo(),
            Consequence::Redo => self.mediator.lock().unwrap().redo(),
            Consequence::Building(builder, _) => {
                let color = builder.get_strand_color();
                self.mediator
                    .lock()
                    .unwrap()
                    .update_opperation(Arc::new(StrandConstruction {
                        redo: Some(color),
                        color,
                        builder,
                    }));
            }
            Consequence::Candidate(element) => self.set_candidate(element),
            Consequence::PivotElement(element) => {
                self.data.borrow_mut().set_pivot_element(element);
                let pivot = self.data.borrow().get_pivot_position();
                self.view.borrow_mut().update(ViewUpdate::FogCenter(pivot));
            }
            Consequence::ElementSelected(element, adding) => {
                if adding {
                    self.add_selection(element)
                } else {
                    self.select(element)
                }
            }
            Consequence::InitFreeXover(nucl, d_id, position) => {
                self.data.borrow_mut().init_free_xover(nucl, position, d_id)
            }
            Consequence::MoveFreeXover(element, position) => self
                .data
                .borrow_mut()
                .update_free_xover_target(element, position),
            Consequence::EndFreeXover => self.data.borrow_mut().end_free_xover(),
            Consequence::BuildHelix {
                grid_id,
                design_id,
                length,
                position,
                x,
                y,
            } => {
                self.mediator
                    .lock()
                    .unwrap()
                    .update_opperation(Arc::new(GridHelixCreation {
                        grid_id,
                        design_id: design_id as usize,
                        x,
                        y,
                        length,
                        position,
                    }));
                self.select(Some(SceneElement::Grid(design_id, grid_id)));
                self.view.borrow_mut().update(ViewUpdate::Camera);
                self.mediator.lock().unwrap().suspend_op();
            }
            Consequence::PasteCandidate(element) => self.pasting_candidate(element),
            Consequence::Paste(element) => self.attempt_paste(element),
            Consequence::DoubleClick(element) => {
                let selection = self.data.borrow().to_selection(element);
                if let Some(selection) = selection {
                    self.mediator
                        .lock()
                        .unwrap()
                        .request_center_selection(selection, AppId::Scene);
                }
            }
        };
    }

    pub fn make_new_grid(&self, grid_type: GridTypeDescr) {
        let camera = self.view.borrow().get_camera();
        let position = camera.borrow().position + 10_f32 * camera.borrow().direction();
        let orientation = camera.borrow().rotor.reversed()
            * Rotor3::from_rotation_xz(std::f32::consts::FRAC_PI_2);
        self.mediator
            .lock()
            .unwrap()
            .update_opperation(Arc::new(CreateGrid {
                design_id: 0,
                position,
                orientation,
                grid_type,
                delete: false,
            }));
        self.data.borrow_mut().notify_instance_update();
        self.mediator.lock().unwrap().suspend_op();
    }

    pub fn make_hyperboloid(&self, hyperboloid: Hyperboloid) {
        let camera = self.view.borrow().get_camera();
        let position = camera.borrow().position + 40_f32 * camera.borrow().direction();
        let orientation = camera.borrow().rotor.reversed()
            * Rotor3::from_rotation_xz(std::f32::consts::FRAC_PI_2);
        self.mediator
            .lock()
            .unwrap()
            .update_opperation(Arc::new(NewHyperboloid {
                design_id: 0,
                position,
                orientation,
                hyperboloid,
                delete: false,
            }));
        self.data.borrow_mut().set_pivot_position(position);
        self.data.borrow_mut().notify_instance_update();
        self.mediator.lock().unwrap().suspend_op();
    }

    /// If a nucleotide is selected, and the clicked_pixel corresponds to an other nucleotide,
    /// request a cross-over between the two nucleotides.
    fn attempt_xover(&mut self, source: Nucl, target: Nucl, design_id: usize) {
        self.mediator
            .lock()
            .unwrap()
            .xover_request(source, target, design_id)
    }

    fn element_center(&mut self) -> Option<SceneElement> {
        let clicked_pixel = PhysicalPosition::new(
            self.area.size.width as f64 / 2.,
            self.area.size.height as f64 / 2.,
        );
        let grid = self
            .view
            .borrow()
            .grid_intersection(0.5, 0.5)
            .map(|g| SceneElement::Grid(g.design_id as u32, g.grid_id));

        grid.or_else(move || self.element_selector.set_selected_id(clicked_pixel))
    }

    fn select(&mut self, element: Option<SceneElement>) {
        let selection = self.data.borrow_mut().set_selection(element);
        if let Some(selection) = selection {
            self.mediator
                .lock()
                .unwrap()
                .notify_unique_selection(selection, AppId::Scene);
        }
        self.update_handle();
    }

    fn add_selection(&mut self, element: Option<SceneElement>) {
        let selection = self.data.borrow_mut().add_to_selection(element);
        if let Some(selection) = selection {
            self.mediator
                .lock()
                .unwrap()
                .notify_multiple_selection(selection, AppId::Scene);
        }
    }

    fn attempt_paste(&mut self, element: Option<SceneElement>) {
        let nucl = self.data.borrow().element_to_nucl(&element, false);
        self.mediator
            .lock()
            .unwrap()
            .attempt_paste(nucl.map(|n| n.0));
    }

    fn pasting_candidate(&self, element: Option<SceneElement>) {
        let nucl = self.data.borrow().element_to_nucl(&element, false);
        self.mediator
            .lock()
            .unwrap()
            .set_paste_candidate(nucl.map(|n| n.0));
    }

    fn set_candidate(&mut self, element: Option<SceneElement>) {
        self.data.borrow_mut().set_candidate(element);
        let widget = if let Some(SceneElement::WidgetElement(widget_id)) = element {
            Some(widget_id)
        } else {
            None
        };
        self.view.borrow_mut().set_widget_candidate(widget);
        let selection = self.data.borrow().get_candidate();
        self.mediator
            .lock()
            .unwrap()
            .set_candidate(None, selection, AppId::Scene);
    }

    fn translate_selected_design(&mut self, translation: Vec3) {
        let rotor = self.data.borrow().get_widget_basis();
        self.view.borrow_mut().translate_widgets(translation);
        if rotor.is_none() {
            return;
        }
        let rotor = rotor.unwrap();
        let right = Vec3::unit_x().rotated_by(rotor);
        let top = Vec3::unit_y().rotated_by(rotor);
        let dir = Vec3::unit_z().rotated_by(rotor);

        let translation_op: Arc<dyn Operation> = match self.data.borrow().get_selected_element() {
            Selection::Grid(d_id, g_id) => Arc::new(GridTranslation {
                design_id: d_id as usize,
                grid_id: g_id as usize,
                right: Vec3::unit_x().rotated_by(rotor),
                top: Vec3::unit_y().rotated_by(rotor),
                dir: Vec3::unit_z().rotated_by(rotor),
                x: translation.dot(right),
                y: translation.dot(top),
                z: translation.dot(dir),
            }),
            Selection::Helix(d_id, h_id) => Arc::new(HelixTranslation {
                design_id: d_id as usize,
                helix_id: h_id as usize,
                right: Vec3::unit_x().rotated_by(rotor),
                top: Vec3::unit_y().rotated_by(rotor),
                dir: Vec3::unit_z().rotated_by(rotor),
                x: translation.dot(right),
                y: translation.dot(top),
                z: translation.dot(dir),
                snap: true,
            }),
            _ => return,
        };

        self.mediator
            .lock()
            .unwrap()
            .update_opperation(translation_op);
    }

    fn rotate_selected_desgin(&mut self, rotation: Rotor3, origin: Vec3, positive: bool) {
        let (mut angle, mut plane) = rotation.into_angle_plane();
        if !positive {
            angle *= -1.;
            plane *= -1.;
        }
        let rotation: Arc<dyn Operation> = match self.data.borrow().get_selected_element() {
            Selection::Helix(d_id, h_id) => {
                let helix_id = h_id as usize;
                Arc::new(HelixRotation {
                    helix_id,
                    angle,
                    plane,
                    origin,
                    design_id: d_id as usize,
                })
            }
            Selection::Grid(d_id, g_id) => {
                let grid_id = g_id as usize;
                Arc::new(GridRotation {
                    grid_id,
                    angle,
                    plane,
                    origin,
                    design_id: d_id as usize,
                })
            }
            _ => return,
        };

        self.mediator.lock().unwrap().update_opperation(rotation);
    }

    /// Adapt the camera, position, orientation and pivot point to a design so that the design fits
    /// the scene, and the pivot point of the camera is the center of the design.
    fn fit_design(&mut self) {
        let camera_position = self.data.borrow().get_fitting_camera_position();
        if let Some(position) = camera_position {
            let pivot_point = self.data.borrow().get_middle_point(0);
            self.notify(SceneNotification::NewCameraPosition(position));
            self.controller.set_pivot_point(Some(pivot_point));
            self.controller.set_pivot_point(None);
        }
    }

    fn need_redraw(&mut self, dt: Duration) -> bool {
        self.check_timers();
        if self.controller.camera_is_moving() {
            self.notify(SceneNotification::CameraMoved);
        }
        if self.update.need_update {
            self.perform_update(dt);
        }
        self.data.borrow_mut().update_view();
        self.view.borrow().need_redraw()
    }

    /// Draw the scene
    fn draw_view(&mut self, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView) {
        self.view.borrow_mut().draw(
            encoder,
            target,
            DrawType::Scene,
            self.area,
            self.data.borrow().get_action_mode(),
        );
    }

    fn update_handle(&mut self) {
        let origin = self.data.borrow().get_selected_position();
        let orientation = self.data.borrow().get_widget_basis();
        let descr = origin
            .clone()
            .zip(orientation.clone())
            .map(|(origin, orientation)| HandlesDescriptor {
                origin,
                orientation: HandleOrientation::Rotor(orientation),
                size: 0.25,
            });
        self.view.borrow_mut().update(ViewUpdate::Handles(descr));
        let only_right = !self.data.borrow().selection_can_rotate_freely();
        let descr = origin
            .clone()
            .zip(orientation.clone())
            .map(|(origin, orientation)| RotationWidgetDescriptor {
                origin,
                orientation: RotationWidgetOrientation::Rotor(orientation),
                size: 0.2,
                only_right,
            });
        self.view
            .borrow_mut()
            .update(ViewUpdate::RotationWidget(descr));
    }

    fn perform_update(&mut self, dt: Duration) {
        if self.update.camera_update {
            self.controller.update_camera(dt);
            self.view.borrow_mut().update(ViewUpdate::Camera);
            self.update.camera_update = false;
            self.update_handle()
        }
        self.update.need_update = false;
    }

    fn change_selection_mode(&mut self, selection_mode: SelectionMode) {
        self.data.borrow_mut().change_selection_mode(selection_mode);
        self.update_handle();
    }

    fn change_action_mode(&mut self, action_mode: ActionMode) {
        self.data.borrow_mut().change_action_mode(action_mode);
        self.update_handle();
    }

    fn change_sensitivity(&mut self, sensitivity: f32) {
        self.controller.change_sensitivity(sensitivity)
    }

    fn set_camera_target(&mut self, target: Vec3, up: Vec3) {
        let pivot = self.data.borrow().get_selected_position();
        let pivot = pivot.or_else(|| {
            let element_center = self.element_center();
            self.data.borrow_mut().set_selection(element_center);
            self.data.borrow().get_selected_position()
        });
        self.controller.set_camera_target(target, up, pivot);
        self.fit_design();
    }

    fn request_camera_rotation(&mut self, xz: f32, yz: f32, xy: f32) {
        let pivot = self.data.borrow().get_selected_position();
        let pivot = pivot.or_else(|| {
            let element_center = self.element_center();
            self.data.borrow_mut().set_selection(element_center);
            self.data.borrow().get_selected_position()
        });
        self.controller.rotate_camera(xz, yz, xy, pivot);
    }
}

/// A structure that stores the element that needs to be updated in a scene
pub struct SceneUpdate {
    pub tube_instances: Option<Vec<Instance>>,
    pub sphere_instances: Option<Vec<Instance>>,
    pub fake_tube_instances: Option<Vec<Instance>>,
    pub fake_sphere_instances: Option<Vec<Instance>>,
    pub selected_tube: Option<Vec<Instance>>,
    pub selected_sphere: Option<Vec<Instance>>,
    pub candidate_spheres: Option<Vec<Instance>>,
    pub candidate_tubes: Option<Vec<Instance>>,
    pub model_matrices: Option<Vec<Mat4>>,
    pub need_update: bool,
    pub camera_update: bool,
}

impl SceneUpdate {
    pub fn new() -> Self {
        Self {
            tube_instances: None,
            sphere_instances: None,
            fake_tube_instances: None,
            fake_sphere_instances: None,
            selected_tube: None,
            selected_sphere: None,
            candidate_spheres: None,
            candidate_tubes: None,
            need_update: false,
            camera_update: false,
            model_matrices: None,
        }
    }
}

/// A notification to be given to the scene
pub enum SceneNotification {
    /// The camera has moved. As a consequence, the projection and view matrix must be
    /// updated.
    CameraMoved,
    /// The camera is replaced by a new one.
    #[allow(dead_code)]
    NewCamera(Vec3, Rotor3),
    /// The drawing area has been modified
    NewSize(PhySize, DrawArea),
    NewCameraPosition(Vec3),
}

impl Scene {
    /// Send a notificatoin to the scene
    pub fn notify(&mut self, notification: SceneNotification) {
        match notification {
            SceneNotification::NewCamera(position, projection) => {
                self.controller.teleport_camera(position, projection);
                self.update.camera_update = true;
            }
            SceneNotification::NewCameraPosition(position) => {
                self.controller.set_camera_position(position);
                self.update.camera_update = true;
            }
            SceneNotification::CameraMoved => self.update.camera_update = true,
            SceneNotification::NewSize(window_size, area) => {
                self.area = area;
                self.resize(window_size);
            }
        };
        self.update.need_update = true;
    }

    fn resize(&mut self, window_size: PhySize) {
        self.view.borrow_mut().update(ViewUpdate::Size(window_size));
        self.controller.resize(window_size, self.area.size);
        self.update.camera_update = true;
        self.element_selector
            .resize(self.controller.get_window_size(), self.area);
    }

    pub fn fog_request(&mut self, fog: FogParameters) {
        self.view.borrow_mut().update(ViewUpdate::Fog(fog))
    }
}

impl Application for Scene {
    fn on_notify(&mut self, notification: Notification) {
        match notification {
            Notification::DesignNotification(notification) => {
                self.handle_design_notification(notification)
            }
            Notification::AppNotification(_) => (),
            Notification::NewDesign(design) => self.add_design(design),
            Notification::ClearDesigns => self.clear_design(),
            Notification::ToggleText(value) => self.view.borrow_mut().set_draw_letter(value),
            Notification::FitRequest => self.fit_design(),
            Notification::NewActionMode(am) => self.change_action_mode(am),
            Notification::NewSelectionMode(sm) => self.change_selection_mode(sm),
            Notification::NewSensitivity(x) => self.change_sensitivity(x),
            Notification::NewCandidate(candidate, app_id) => {
                if let AppId::Scene = app_id {
                    ()
                } else {
                    self.data.borrow_mut().notify_candidate(candidate)
                }
            }
            Notification::Selection3D(selection, app_id) => {
                if let AppId::Scene = app_id {
                    ()
                } else {
                    self.data.borrow_mut().notify_selection(selection)
                }
            }
            Notification::Save(_) => (),
            Notification::CameraTarget((target, up)) => {
                self.set_camera_target(target, up);
                self.notify(SceneNotification::CameraMoved);
            }
            Notification::CameraRotation(xz, yz, xy) => {
                self.request_camera_rotation(xz, yz, xy);
                self.notify(SceneNotification::CameraMoved);
            }
            Notification::Centering(nucl, design_id) => {
                let mut selected = false;
                if let Some(position) = self.data.borrow().get_nucl_position(nucl, design_id) {
                    self.controller.center_camera(position);
                    selected = true;
                }
                if selected {
                    self.data.borrow_mut().select_nucl(nucl, design_id);
                }
                self.notify(SceneNotification::CameraMoved);
            }
            Notification::CenterSelection(selection, app_id) => {
                if app_id != AppId::Scene {
                    self.data.borrow_mut().notify_selection(vec![selection]);
                    if let Some(position) = self.data.borrow().get_selected_position() {
                        self.controller.center_camera(position);
                    }
                    self.notify(SceneNotification::CameraMoved);
                }
            }
            Notification::ShowTorsion(_) => (),
            Notification::Pasting(b) => self.controller.pasting = b,
            Notification::ModifersChanged(modifiers) => self.controller.update_modifiers(modifiers),
            Notification::Split2d => (),
            Notification::Redim2dHelices(_) => (),
            Notification::ToggleWidget(b) => {
                self.data.borrow_mut().toggle_widget_basis(b);
                self.update_handle();
            }
            Notification::RenderingMode(mode) => self.view.borrow_mut().rendering_mode(mode),
            Notification::Background3D(bg) => self.view.borrow_mut().background3d(bg),
        }
    }

    fn on_event(&mut self, event: &WindowEvent, cursor_position: PhysicalPosition<f64>) {
        self.input(event, cursor_position)
    }

    fn on_resize(&mut self, window_size: PhySize, area: DrawArea) {
        self.notify(SceneNotification::NewSize(window_size, area))
    }

    fn on_redraw_request(
        &mut self,
        encoder: &mut wgpu::CommandEncoder,
        target: &wgpu::TextureView,
        _dt: Duration,
    ) {
        self.draw_view(encoder, target)
    }

    fn needs_redraw(&mut self, dt: Duration) -> bool {
        self.need_redraw(dt)
    }
}

impl Scene {
    fn handle_design_notification(&mut self, notification: DesignNotification) {
        let _design_id = notification.design_id;
        match notification.content {
            DesignNotificationContent::ModelChanged(_) => {
                self.update.need_update = true;
                self.data.borrow_mut().notify_matrices_update();
            }
            DesignNotificationContent::InstanceChanged => {
                self.data.borrow_mut().notify_instance_update()
            }
            DesignNotificationContent::ViewNeedReset => {
                self.data.borrow_mut().notify_instance_update();
                self.data.borrow_mut().set_selection(None);
            }
        }
    }
}
back to top