Raw File
view.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 view module handles the drawing of the scene on texture. The scene can be drawn on the next
//! frame to be displayed, or on a "fake texture" that is used to map pixels to objects.

use super::{camera, ActionMode};
use crate::consts::*;
use crate::design::Axis;
use crate::utils::{bindgroup_manager, texture};
use crate::{DrawArea, PhySize};
use camera::{Camera, CameraPtr, Projection, ProjectionPtr};
use iced_wgpu::wgpu;
use std::cell::RefCell;
use std::rc::Rc;
use texture::Texture;
use ultraviolet::{Mat4, Rotor3, Vec3};
use wgpu::{Device, Queue};

/// A `Uniform` is a structure that manages view and projection matrices.
mod uniforms;
pub use uniforms::FogParameters;
use uniforms::Uniforms;
mod direction_cube;
mod dna_obj;
/// This modules defines a trait for drawing widget made of several meshes.
mod drawable;
mod grid;
mod grid_disc;
/// A HandleDrawer draws the widget for translating objects
mod handle_drawer;
mod instances_drawer;
mod letter;
/// A RotationWidget draws the widget for rotating objects
mod rotation_widget;

use super::maths_3d;
use crate::text::Letter;
use bindgroup_manager::{DynamicBindGroup, UniformBindGroup};
use direction_cube::*;
pub use dna_obj::{ConeInstance, DnaObject, RawDnaInstance, SphereInstance, TubeInstance};
use drawable::{Drawable, Drawer, Vertex};
pub use grid::{GridInstance, GridIntersection, GridTypeDescr};
use grid::{GridManager, GridTextures};
pub use grid_disc::GridDisc;
use handle_drawer::HandlesDrawer;
pub use handle_drawer::{HandleDir, HandleOrientation, HandlesDescriptor};
pub use instances_drawer::Instanciable;
use instances_drawer::{InstanceDrawer, RawDrawer};
pub use letter::LetterInstance;
use maths_3d::unproject_point_on_line;
use rotation_widget::RotationWidget;
pub use rotation_widget::{RotationMode, RotationWidgetDescriptor, RotationWidgetOrientation};
//use plane_drawer::PlaneDrawer;
//pub use plane_drawer::Plane;

static MODEL_BG_ENTRY: &'static [wgpu::BindGroupLayoutEntry] = &[wgpu::BindGroupLayoutEntry {
    binding: 0,
    visibility: wgpu::ShaderStage::from_bits_truncate(wgpu::ShaderStage::VERTEX.bits()),
    ty: wgpu::BindingType::Buffer {
        has_dynamic_offset: false,
        min_binding_size: None,
        ty: wgpu::BufferBindingType::Storage { read_only: true },
    },
    count: None,
}];

use crate::mediator::{Background3D, RenderingMode};

/// An object that handles the communication with the GPU to draw the scene.
pub struct View {
    /// The camera, that is in charge of producing the view and projection matrices.
    camera: CameraPtr,
    projection: ProjectionPtr,
    /// The depth texture is updated every time the size of the drawing area is modified
    depth_texture: Texture,
    /// The fake depth texture is updated every time the size of the drawing area is modified and
    /// has a sample count of 1
    fake_depth_texture: Texture,
    /// The handle drawers draw handles to translate the elements
    handle_drawers: HandlesDrawer,
    /// The rotation widget draw the widget to rotate the elements
    rotation_widget: RotationWidget,
    /// A possible update of the size of the drawing area, must be taken into account before
    /// drawing the next frame
    new_size: Option<PhySize>,
    /// The pipilines that draw the basis symbols
    letter_drawer: Vec<InstanceDrawer<LetterInstance>>,
    helix_letter_drawer: Vec<InstanceDrawer<LetterInstance>>,
    device: Rc<Device>,
    /// A bind group associated to the uniform buffer containing the view and projection matrices.
    //TODO this is currently only passed to the widgets, it could be passed to the mesh pipeline as
    //well.
    viewer: UniformBindGroup,
    models: DynamicBindGroup,
    redraw_twice: bool,
    need_redraw: bool,
    need_redraw_fake: bool,
    draw_letter: bool,
    msaa_texture: Option<wgpu::TextureView>,
    grid_manager: GridManager,
    disc_drawer: InstanceDrawer<GridDisc>,
    dna_drawers: DnaDrawers,
    direction_cube: InstanceDrawer<DirectionCube>,
    skybox_cube: InstanceDrawer<SkyBox>,
    fog_parameters: FogParameters,
    rendering_mode: RenderingMode,
    background3d: Background3D,
}

impl View {
    pub fn new(
        window_size: PhySize,
        area_size: PhySize,
        device: Rc<Device>,
        queue: Rc<Queue>,
        encoder: &mut wgpu::CommandEncoder,
    ) -> Self {
        let camera = Rc::new(RefCell::new(Camera::new(
            (0.0, 5.0, 10.0),
            Rotor3::identity(),
        )));
        let projection = Rc::new(RefCell::new(Projection::new(
            area_size.width,
            area_size.height,
            70f32.to_radians(),
            0.1,
            1000.0,
        )));
        let viewer = UniformBindGroup::new(
            device.clone(),
            queue.clone(),
            &Uniforms::from_view_proj(camera.clone(), projection.clone()),
        );
        let model_bg_desc = wgpu::BindGroupLayoutDescriptor {
            entries: MODEL_BG_ENTRY,
            label: None,
        };
        println!("Create letter drawer");
        let letter_drawer = BASIS_SYMBOLS
            .iter()
            .map(|c| {
                let letter = Letter::new(*c, device.clone(), queue.clone());
                InstanceDrawer::new(
                    device.clone(),
                    queue.clone(),
                    &viewer.get_layout_desc(),
                    &model_bg_desc,
                    letter,
                    false,
                )
            })
            .collect();
        println!("Create helix letter drawer");
        let helix_letter_drawer = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
            .iter()
            .map(|c| {
                let letter = Letter::new(*c, device.clone(), queue.clone());
                InstanceDrawer::new(
                    device.clone(),
                    queue.clone(),
                    &viewer.get_layout_desc(),
                    &model_bg_desc,
                    letter,
                    false,
                )
            })
            .collect();

        let depth_texture =
            texture::Texture::create_depth_texture(device.as_ref(), &area_size, SAMPLE_COUNT);
        let fake_depth_texture =
            texture::Texture::create_depth_texture(device.as_ref(), &window_size, 1);
        let msaa_texture = if SAMPLE_COUNT > 1 {
            Some(crate::utils::texture::Texture::create_msaa_texture(
                device.clone().as_ref(),
                &area_size,
                SAMPLE_COUNT,
                wgpu::TextureFormat::Bgra8UnormSrgb,
            ))
        } else {
            None
        };
        let models = DynamicBindGroup::new(device.clone(), queue.clone());

        let grid_textures = GridTextures::new(device.as_ref(), encoder);
        println!("Create grid drawer");

        let grid_drawer = InstanceDrawer::new(
            device.clone(),
            queue.clone(),
            &viewer.get_layout_desc(),
            &model_bg_desc,
            grid_textures,
            false,
        );
        let grid_textures = GridTextures::new(device.as_ref(), encoder);
        let fake_grid_drawer = InstanceDrawer::new(
            device.clone(),
            queue.clone(),
            &viewer.get_layout_desc(),
            &model_bg_desc,
            grid_textures,
            true,
        );
        let grid_manager = GridManager::new(grid_drawer, fake_grid_drawer);

        println!("Create disc  drawer");
        let disc_drawer = InstanceDrawer::new(
            device.clone(),
            queue.clone(),
            &viewer.get_layout_desc(),
            &model_bg_desc,
            (),
            false,
        );

        println!("Create dna drawer");
        let dna_drawers = DnaDrawers::new(
            device.clone(),
            queue.clone(),
            &viewer.get_layout_desc(),
            &model_bg_desc,
        );

        let direction_texture = DirectionTexture::new(device.clone(), queue.clone());
        let mut direction_cube = InstanceDrawer::new(
            device.clone(),
            queue.clone(),
            &viewer.get_layout_desc(),
            &model_bg_desc,
            direction_texture,
            false,
        );
        direction_cube.new_instances(vec![Default::default()]);

        let mut skybox_cube = InstanceDrawer::new(
            device.clone(),
            queue.clone(),
            &viewer.get_layout_desc(),
            &model_bg_desc,
            (),
            false,
        );
        skybox_cube.new_instances(vec![SkyBox::new(500.)]);

        Self {
            camera,
            projection,
            depth_texture,
            fake_depth_texture,
            new_size: None,
            device: device.clone(),
            viewer,
            models,
            handle_drawers: HandlesDrawer::new(device.clone()),
            rotation_widget: RotationWidget::new(device),
            letter_drawer,
            helix_letter_drawer,
            redraw_twice: false,
            need_redraw: true,
            need_redraw_fake: true,
            draw_letter: false,
            msaa_texture,
            grid_manager,
            disc_drawer,
            dna_drawers,
            direction_cube,
            skybox_cube,
            fog_parameters: FogParameters::new(),
            rendering_mode: Default::default(),
            background3d: Default::default(),
        }
    }

    /// Notify the view of an update. According to the nature of this update, the view decides if
    /// it needs to be redrawn or not.
    pub fn update(&mut self, view_update: ViewUpdate) {
        self.need_redraw = true;
        match view_update {
            ViewUpdate::Size(size) => {
                self.new_size = Some(size);
                self.need_redraw_fake = true;
            }
            ViewUpdate::Camera => {
                self.viewer.update(&Uniforms::from_view_proj_fog(
                    self.camera.clone(),
                    self.projection.clone(),
                    &self.fog_parameters,
                ));
                self.handle_drawers
                    .update_camera(self.camera.clone(), self.projection.clone());
                self.need_redraw_fake = true;
                let dist = self.projection.borrow().cube_dist();
                self.direction_cube
                    .new_instances(vec![DirectionCube::new(dist)]);
            }
            ViewUpdate::Fog(fog) => {
                let fog_center = self.fog_parameters.alt_fog_center.clone();
                self.fog_parameters = fog;
                self.fog_parameters.alt_fog_center = fog_center;
                self.viewer.update(&Uniforms::from_view_proj_fog(
                    self.camera.clone(),
                    self.projection.clone(),
                    &self.fog_parameters,
                ));
            }
            ViewUpdate::Handles(descr) => {
                self.handle_drawers.update_decriptor(
                    descr,
                    self.camera.clone(),
                    self.projection.clone(),
                );
                self.need_redraw_fake = true;
            }

            ViewUpdate::RotationWidget(descr) => {
                self.rotation_widget.update_decriptor(
                    descr,
                    self.camera.clone(),
                    self.projection.clone(),
                );
                self.need_redraw_fake = true;
            }
            ViewUpdate::ModelMatrices(ref matrices) => {
                self.models.update(matrices.clone().as_slice());
                self.need_redraw_fake = true;
            }
            ViewUpdate::Letter(letter) => {
                for (i, instance) in letter.into_iter().enumerate() {
                    self.letter_drawer[i].new_instances(instance);
                }
            }
            ViewUpdate::GridLetter(letter) => {
                for (i, instance) in letter.into_iter().enumerate() {
                    self.helix_letter_drawer[i].new_instances(instance);
                }
            }
            ViewUpdate::Grids(grid) => self.grid_manager.new_instances(grid),
            ViewUpdate::GridDiscs(instances) => self.disc_drawer.new_instances(instances),
            ViewUpdate::RawDna(mesh, instances) => {
                self.dna_drawers
                    .get_mut(mesh)
                    .new_instances_raw(instances.as_ref());
                if let Some(mesh) = mesh.to_fake() {
                    let mut instances = instances.as_ref().clone();
                    for i in instances.iter_mut() {
                        if i.scale.z < 0.99 {
                            i.scale *= 2.5;
                        }
                    }
                    self.need_redraw_fake = true;
                    self.dna_drawers
                        .get_mut(mesh)
                        .new_instances_raw(instances.as_ref());
                }
                if let Some(mesh) = mesh.to_outline() {
                    self.dna_drawers
                        .get_mut(mesh)
                        .new_instances_raw(instances.as_ref());
                }
            }
            ViewUpdate::FogCenter(center) => {
                self.fog_parameters.alt_fog_center = center;
                self.viewer.update(&Uniforms::from_view_proj_fog(
                    self.camera.clone(),
                    self.projection.clone(),
                    &self.fog_parameters,
                ));
            }
        }
    }

    pub fn need_redraw_fake(&self) -> bool {
        self.need_redraw_fake
    }

    pub fn need_redraw(&self) -> bool {
        self.need_redraw | self.redraw_twice
    }

    /// Draw the scene
    pub fn draw(
        &mut self,
        encoder: &mut wgpu::CommandEncoder,
        target: &wgpu::TextureView,
        draw_type: DrawType,
        area: DrawArea,
        action_mode: ActionMode,
    ) {
        let fake_color = draw_type.is_fake();
        if let Some(size) = self.new_size.take() {
            self.depth_texture =
                Texture::create_depth_texture(self.device.as_ref(), &area.size, SAMPLE_COUNT);
            self.fake_depth_texture = Texture::create_depth_texture(self.device.as_ref(), &size, 1);
            self.msaa_texture = if SAMPLE_COUNT > 1 {
                Some(crate::utils::texture::Texture::create_msaa_texture(
                    self.device.clone().as_ref(),
                    &area.size,
                    SAMPLE_COUNT,
                    wgpu::TextureFormat::Bgra8UnormSrgb,
                ))
            } else {
                None
            };
        }
        let clear_color = if fake_color || self.background3d == Background3D::White {
            wgpu::Color {
                r: 1.,
                g: 1.,
                b: 1.,
                a: 1.,
            }
        } else {
            wgpu::Color {
                r: 0.,
                g: 0.,
                b: 0.,
                a: 1.,
            }
        };
        let viewer = &self.viewer;
        let viewer_bind_group = viewer.get_bindgroup();
        let viewer_bind_group_layout = viewer.get_layout();

        let attachment = if !fake_color {
            if let Some(ref msaa) = self.msaa_texture {
                msaa
            } else {
                target
            }
        } else {
            target
        };

        let resolve_target = if !fake_color && self.msaa_texture.is_some() {
            Some(target)
        } else {
            None
        };

        let depth_attachement = if !fake_color {
            &self.depth_texture
        } else {
            &self.fake_depth_texture
        };

        {
            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: None,
                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
                    attachment,
                    resolve_target,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(clear_color),
                        store: true,
                    },
                }],
                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
                    attachment: &depth_attachement.view,
                    depth_ops: Some(wgpu::Operations {
                        load: wgpu::LoadOp::Clear(1.),
                        store: true,
                    }),
                    stencil_ops: Some(wgpu::Operations {
                        load: wgpu::LoadOp::Clear(0),
                        store: true,
                    }),
                }),
            });
            if fake_color {
                render_pass.set_viewport(
                    area.position.x as f32,
                    area.position.y as f32,
                    area.size.width as f32,
                    area.size.height as f32,
                    0.0,
                    1.0,
                );
                render_pass.set_scissor_rect(
                    area.position.x,
                    area.position.y,
                    area.size.width,
                    area.size.height,
                );
            }

            if draw_type == DrawType::Design {
                for drawer in self.dna_drawers.fakes() {
                    drawer.draw(
                        &mut render_pass,
                        self.viewer.get_bindgroup(),
                        self.models.get_bindgroup(),
                    )
                }
            } else if draw_type == DrawType::Scene {
                if self.background3d == Background3D::Sky {
                    self.skybox_cube.draw(
                        &mut render_pass,
                        self.viewer.get_bindgroup(),
                        self.models.get_bindgroup(),
                    );
                }
                for drawer in self.dna_drawers.reals(self.rendering_mode) {
                    drawer.draw(
                        &mut render_pass,
                        self.viewer.get_bindgroup(),
                        self.models.get_bindgroup(),
                    )
                }
            } else if draw_type == DrawType::Phantom {
                for drawer in self.dna_drawers.phantoms() {
                    drawer.draw(
                        &mut render_pass,
                        self.viewer.get_bindgroup(),
                        self.models.get_bindgroup(),
                    )
                }
            } else if draw_type == DrawType::Grid {
                // Draw design elements and phantoms, to fill the depth buffer
                for drawer in self.dna_drawers.fakes_and_phantoms() {
                    drawer.draw(
                        &mut render_pass,
                        self.viewer.get_bindgroup(),
                        self.models.get_bindgroup(),
                    )
                }
            }

            if draw_type.wants_widget() {
                if action_mode.wants_handle() {
                    self.handle_drawers.draw(
                        &mut render_pass,
                        viewer_bind_group,
                        viewer_bind_group_layout,
                        fake_color,
                    );
                }

                if action_mode.wants_rotation() {
                    self.rotation_widget.draw(
                        &mut render_pass,
                        viewer_bind_group,
                        viewer_bind_group_layout,
                        fake_color,
                    );
                }
            }

            if !fake_color && self.draw_letter {
                for drawer in self.letter_drawer.iter_mut() {
                    drawer.draw(
                        &mut render_pass,
                        viewer_bind_group,
                        self.models.get_bindgroup(),
                    )
                }
            }

            if !fake_color {
                self.grid_manager.draw(
                    &mut render_pass,
                    viewer_bind_group,
                    self.models.get_bindgroup(),
                    false,
                );
                self.disc_drawer.draw(
                    &mut render_pass,
                    viewer_bind_group,
                    self.models.get_bindgroup(),
                );
                for drawer in self.helix_letter_drawer.iter_mut() {
                    drawer.draw(
                        &mut render_pass,
                        viewer_bind_group,
                        self.models.get_bindgroup(),
                    )
                }
            }

            if fake_color {
                self.need_redraw_fake = false;
            } else if self.redraw_twice {
                self.redraw_twice = false;
                self.need_redraw = true;
            } else {
                self.need_redraw = false;
            }
        }
        if !fake_color {
            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: None,
                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
                    attachment,
                    resolve_target,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Load,
                        store: true,
                    },
                }],
                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
                    attachment: &depth_attachement.view,
                    depth_ops: Some(wgpu::Operations {
                        load: wgpu::LoadOp::Clear(1.),
                        store: true,
                    }),
                    stencil_ops: Some(wgpu::Operations {
                        load: wgpu::LoadOp::Clear(0),
                        store: true,
                    }),
                }),
            });
            render_pass.set_viewport(
                area.size.width as f32 / 20.,
                0.,
                (area.size.width as f32 / 10. * 1.5)
                    .max(100.)
                    .min(area.size.width as f32),
                (area.size.height as f32 / 10. * 1.5)
                    .max((100. * area.size.height as f32 / area.size.width as f32) as f32)
                    .min(area.size.height as f32),
                0.0,
                1.0,
            );
            self.direction_cube.draw(
                &mut render_pass,
                viewer_bind_group,
                self.models.get_bindgroup(),
            )
        } else if draw_type == DrawType::Grid {
            // render pass to draw the grids
            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: None,
                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
                    attachment,
                    resolve_target,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(clear_color),
                        store: true,
                    },
                }],
                // Reuse previous depth_stencil_attachment
                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
                    attachment: &depth_attachement.view,
                    depth_ops: Some(wgpu::Operations {
                        load: wgpu::LoadOp::Load,
                        store: true,
                    }),
                    stencil_ops: Some(wgpu::Operations {
                        load: wgpu::LoadOp::Load,
                        store: true,
                    }),
                }),
            });
            render_pass.set_viewport(
                area.position.x as f32,
                area.position.y as f32,
                area.size.width as f32,
                area.size.height as f32,
                0.0,
                1.0,
            );
            render_pass.set_scissor_rect(
                area.position.x,
                area.position.y,
                area.size.width,
                area.size.height,
            );
            self.grid_manager.draw(
                &mut render_pass,
                viewer_bind_group,
                self.models.get_bindgroup(),
                true,
            );
        }
    }

    /// Get a pointer to the camera
    pub fn get_camera(&self) -> CameraPtr {
        self.camera.clone()
    }

    /// A pointer to the projection camera
    pub fn get_projection(&self) -> ProjectionPtr {
        self.projection.clone()
    }

    pub fn set_draw_letter(&mut self, value: bool) {
        self.draw_letter = value;
    }

    /// Compute the translation that needs to be applied to the objects affected by the handle
    /// widget.
    pub fn compute_translation_handle(
        &self,
        x_coord: f32,
        y_coord: f32,
        direction: HandleDir,
    ) -> Option<Vec3> {
        let (origin, dir) = self.handle_drawers.get_handle(direction)?;
        let (x0, y0) = self.handle_drawers.get_origin_translation()?;
        let p1 = unproject_point_on_line(
            origin,
            dir,
            self.camera.clone(),
            self.projection.clone(),
            x0,
            y0,
        )?;
        let p2 = unproject_point_on_line(
            origin,
            dir,
            self.camera.clone(),
            self.projection.clone(),
            x_coord,
            y_coord,
        )?;
        Some(p2 - p1)
    }

    /// Translate the widgets when the associated objects are translated.
    pub fn translate_widgets(&mut self, translation: Vec3) {
        self.need_redraw = true;
        self.handle_drawers.translate(translation);
        self.rotation_widget.translate(translation);
    }

    /// Initialise the rotation that will be applied on objects affected by the rotation widget.
    pub fn init_rotation(&mut self, x_coord: f32, y_coord: f32) {
        self.need_redraw = true;
        self.rotation_widget.init_rotation(x_coord, y_coord)
    }

    /// Initialise the translation that will be applied on objects affected by the handle widget.
    pub fn init_translation(&mut self, x: f32, y: f32) {
        self.need_redraw = true;
        self.handle_drawers.init_translation(x, y)
    }

    /// Compute the rotation that needs to be applied to the objects affected by the rotation
    /// widget.
    pub fn compute_rotation(
        &self,
        x: f32,
        y: f32,
        mode: RotationMode,
    ) -> Option<(Rotor3, Vec3, bool)> {
        self.rotation_widget.compute_rotation(
            x,
            y,
            self.camera.clone(),
            self.projection.clone(),
            mode,
        )
    }

    pub fn set_widget_candidate(&mut self, selected_id: Option<u32>) {
        self.redraw_twice |= self.rotation_widget.set_selected(selected_id);
        self.redraw_twice |= self.handle_drawers.set_selected(selected_id);
    }

    pub fn compute_projection_axis(
        &self,
        axis: &Axis,
        mouse_x: f64,
        mouse_y: f64,
    ) -> Option<isize> {
        let p1 = unproject_point_on_line(
            axis.origin,
            axis.direction,
            self.camera.clone(),
            self.projection.clone(),
            mouse_x as f32,
            mouse_y as f32,
        )?;

        let sign = (p1 - axis.origin).dot(axis.direction).signum();
        Some(((p1 - axis.origin).mag() * sign / axis.direction.mag()).round() as isize)
    }

    pub fn grid_intersection(&self, x_ndc: f32, y_ndc: f32) -> Option<GridIntersection> {
        let ray = maths_3d::cast_ray(x_ndc, y_ndc, self.camera.clone(), self.projection.clone());
        self.grid_manager.intersect(ray.0, ray.1)
    }

    pub fn set_candidate_grid(&mut self, grids: Vec<(usize, usize)>) {
        self.grid_manager.set_candidate_grid(grids)
    }

    pub fn set_selected_grid(&mut self, grids: Vec<(usize, usize)>) {
        self.grid_manager.set_selected_grid(grids)
    }

    pub fn rendering_mode(&mut self, mode: RenderingMode) {
        self.rendering_mode = mode;
        self.need_redraw = true;
    }

    pub fn background3d(&mut self, bg: Background3D) {
        self.background3d = bg;
        self.need_redraw = true;
    }
}

/// An notification to be given to the view
#[derive(Debug)]
pub enum ViewUpdate {
    /// The camera has moved and the view and projection matrix must be updated.
    Camera,
    /// The size of the drawing area has been modified
    Size(PhySize),
    /// The set of model matrices has been modified
    ModelMatrices(Vec<Mat4>),
    /// The set of phantom instances has been modified
    Handles(Option<HandlesDescriptor>),
    RotationWidget(Option<RotationWidgetDescriptor>),
    Letter(Vec<Vec<LetterInstance>>),
    GridLetter(Vec<Vec<LetterInstance>>),
    Grids(Rc<Vec<GridInstance>>),
    GridDiscs(Vec<GridDisc>),
    RawDna(Mesh, Rc<Vec<RawDnaInstance>>),
    Fog(FogParameters),
    FogCenter(Option<Vec3>),
}

#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)]
pub enum Mesh {
    Sphere,
    Tube,
    OutlineSphere,
    OutlineTube,
    FakeSphere,
    FakeTube,
    CandidateSphere,
    CandidateTube,
    SelectedSphere,
    SelectedTube,
    PhantomSphere,
    PhantomTube,
    FakePhantomTube,
    FakePhantomSphere,
    SuggestionSphere,
    SuggestionTube,
    PastedSphere,
    PastedTube,
    PivotSphere,
    XoverSphere,
    XoverTube,
    Prime3Cone,
    Prime3ConeOutline,
}

impl Mesh {
    fn to_fake(&self) -> Option<Self> {
        match self {
            Self::Sphere => Some(Self::FakeSphere),
            Self::Tube => Some(Self::FakeTube),
            Self::PhantomSphere => Some(Self::FakePhantomSphere),
            Self::PhantomTube => Some(Self::FakePhantomTube),
            _ => None,
        }
    }

    fn to_outline(&self) -> Option<Self> {
        match self {
            Self::Sphere => Some(Self::OutlineSphere),
            Self::Tube => Some(Self::OutlineTube),
            Self::Prime3Cone => Some(Self::Prime3ConeOutline),
            _ => None,
        }
    }
}

struct DnaDrawers {
    sphere: InstanceDrawer<SphereInstance>,
    tube: InstanceDrawer<TubeInstance>,
    outline_sphere: InstanceDrawer<SphereInstance>,
    outline_tube: InstanceDrawer<TubeInstance>,
    candidate_sphere: InstanceDrawer<SphereInstance>,
    candidate_tube: InstanceDrawer<TubeInstance>,
    selected_sphere: InstanceDrawer<SphereInstance>,
    selected_tube: InstanceDrawer<TubeInstance>,
    fake_sphere: InstanceDrawer<SphereInstance>,
    fake_tube: InstanceDrawer<TubeInstance>,
    phantom_sphere: InstanceDrawer<SphereInstance>,
    phantom_tube: InstanceDrawer<TubeInstance>,
    fake_phantom_sphere: InstanceDrawer<SphereInstance>,
    fake_phantom_tube: InstanceDrawer<TubeInstance>,
    suggestion_sphere: InstanceDrawer<SphereInstance>,
    suggestion_tube: InstanceDrawer<TubeInstance>,
    pasted_sphere: InstanceDrawer<SphereInstance>,
    pasted_tube: InstanceDrawer<TubeInstance>,
    pivot_sphere: InstanceDrawer<SphereInstance>,
    xover_sphere: InstanceDrawer<SphereInstance>,
    xover_tube: InstanceDrawer<TubeInstance>,
    prime3_cones: InstanceDrawer<dna_obj::ConeInstance>,
    outline_prime3_cones: InstanceDrawer<dna_obj::ConeInstance>,
}

impl DnaDrawers {
    pub fn get_mut(&mut self, key: Mesh) -> &mut dyn RawDrawer<RawInstance = RawDnaInstance> {
        match key {
            Mesh::Sphere => &mut self.sphere,
            Mesh::Tube => &mut self.tube,
            Mesh::OutlineSphere => &mut self.outline_sphere,
            Mesh::OutlineTube => &mut self.outline_tube,
            Mesh::CandidateSphere => &mut self.candidate_sphere,
            Mesh::CandidateTube => &mut self.candidate_tube,
            Mesh::SelectedSphere => &mut self.selected_sphere,
            Mesh::SelectedTube => &mut self.selected_tube,
            Mesh::PhantomSphere => &mut self.phantom_sphere,
            Mesh::PhantomTube => &mut self.phantom_tube,
            Mesh::FakeSphere => &mut self.fake_sphere,
            Mesh::FakeTube => &mut self.fake_tube,
            Mesh::FakePhantomSphere => &mut self.fake_phantom_sphere,
            Mesh::FakePhantomTube => &mut self.fake_phantom_tube,
            Mesh::SuggestionTube => &mut self.suggestion_tube,
            Mesh::SuggestionSphere => &mut self.suggestion_sphere,
            Mesh::PastedSphere => &mut self.pasted_sphere,
            Mesh::PastedTube => &mut self.pasted_tube,
            Mesh::PivotSphere => &mut self.pivot_sphere,
            Mesh::XoverSphere => &mut self.xover_sphere,
            Mesh::XoverTube => &mut self.xover_tube,
            Mesh::Prime3Cone => &mut self.prime3_cones,
            Mesh::Prime3ConeOutline => &mut self.outline_prime3_cones,
        }
    }

    pub fn reals(
        &mut self,
        rendering_mode: RenderingMode,
    ) -> Vec<&mut dyn RawDrawer<RawInstance = RawDnaInstance>> {
        let mut ret: Vec<&mut dyn RawDrawer<RawInstance = RawDnaInstance>> = vec![
            &mut self.sphere,
            &mut self.tube,
            &mut self.prime3_cones,
            &mut self.candidate_sphere,
            &mut self.candidate_tube,
            &mut self.selected_sphere,
            &mut self.selected_tube,
            &mut self.phantom_tube,
            &mut self.phantom_sphere,
            &mut self.suggestion_sphere,
            &mut self.suggestion_tube,
            &mut self.pasted_tube,
            &mut self.pasted_sphere,
            &mut self.pivot_sphere,
            &mut self.xover_sphere,
            &mut self.xover_tube,
        ];
        if rendering_mode == RenderingMode::Cartoon {
            ret.insert(3, &mut self.outline_tube);
            ret.insert(4, &mut self.outline_sphere);
            ret.insert(5, &mut self.outline_prime3_cones);
        }

        ret
    }

    pub fn fakes(&mut self) -> Vec<&mut dyn RawDrawer<RawInstance = RawDnaInstance>> {
        vec![&mut self.fake_sphere, &mut self.fake_tube]
    }

    pub fn phantoms(&mut self) -> Vec<&mut dyn RawDrawer<RawInstance = RawDnaInstance>> {
        vec![&mut self.fake_phantom_sphere, &mut self.fake_phantom_tube]
    }

    pub fn fakes_and_phantoms(&mut self) -> Vec<&mut dyn RawDrawer<RawInstance = RawDnaInstance>> {
        vec![
            &mut self.fake_sphere,
            &mut self.fake_tube,
            &mut self.fake_phantom_sphere,
            &mut self.fake_phantom_tube,
        ]
    }

    pub fn new(
        device: Rc<Device>,
        queue: Rc<Queue>,
        viewer_desc: &wgpu::BindGroupLayoutDescriptor<'static>,
        model_desc: &wgpu::BindGroupLayoutDescriptor<'static>,
    ) -> Self {
        Self {
            sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            prime3_cones: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            outline_sphere: InstanceDrawer::new_outliner(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
            ),
            outline_tube: InstanceDrawer::new_outliner(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
            ),
            outline_prime3_cones: InstanceDrawer::new_outliner(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
            ),
            candidate_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            candidate_tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            suggestion_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            suggestion_tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            xover_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            xover_tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            pasted_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            pasted_tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            selected_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            selected_tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            pivot_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            phantom_sphere: InstanceDrawer::new_wireframe(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            phantom_tube: InstanceDrawer::new_wireframe(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                false,
            ),
            fake_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                true,
            ),
            fake_tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                true,
            ),
            fake_phantom_sphere: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                true,
            ),
            fake_phantom_tube: InstanceDrawer::new(
                device.clone(),
                queue.clone(),
                viewer_desc,
                model_desc,
                (),
                true,
            ),
        }
    }
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DrawType {
    Scene,
    Design,
    Widget,
    Phantom,
    Grid,
}

impl DrawType {
    fn is_fake(&self) -> bool {
        *self != DrawType::Scene
    }

    fn wants_widget(&self) -> bool {
        match self {
            DrawType::Scene => true,
            DrawType::Design => false,
            DrawType::Widget => true,
            DrawType::Phantom => false,
            DrawType::Grid => false,
        }
    }
}
back to top