WIP conversion to messages

This commit is contained in:
Oliver Davies
2022-01-07 03:41:57 -08:00
parent 28fb9aadd5
commit ddcc23865a
2 changed files with 52 additions and 32 deletions
+47 -32
View File
@@ -1,5 +1,6 @@
use std::f64::consts::PI; use std::{collections::HashMap, f64::consts::PI};
use super::DocumentMessageHandler;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use graphene::{ use graphene::{
layers::style::{self, Stroke}, layers::style::{self, Stroke},
@@ -11,41 +12,40 @@ use crate::{
message_prelude::*, message_prelude::*,
}; };
use super::DocumentMessageHandler;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct SnapHandler { pub struct SnapHandler {
enabled: bool,
document: DocumentMessageHandler,
snap_targets: Option<(Vec<f64>, Vec<f64>)>, snap_targets: Option<(Vec<f64>, Vec<f64>)>,
snappables: HashMap<Vec<LayerId>, Vec<Vec<LayerId>>>,
overlay_messages: Vec<Message>,
overlay_paths: Vec<Vec<LayerId>>, overlay_paths: Vec<Vec<LayerId>>,
} }
impl SnapHandler { impl SnapHandler {
fn add_overlays(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2) { fn add_overlay_line(&mut self, transform: DAffine2) -> Vec<LayerId> {
fn add_overlay_line(responses: &mut VecDeque<Message>, transform: DAffine2) -> Vec<LayerId> { let layer_path = vec![generate_uuid()];
let layer_path = vec![generate_uuid()];
let operation = Operation::AddOverlayLine { let operation = Operation::AddOverlayLine {
path: layer_path.clone(), path: layer_path.clone(),
transform: transform.to_cols_array(), transform: transform.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), None), style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), None),
}; };
responses.push_back(DocumentMessage::Overlay(operation.into()).into()); self.overlay_messages.push(DocumentMessage::Overlay(operation.into()).into());
layer_path layer_path
} }
fn add_overlays(&mut self, viewport_bounds: DVec2) {
if let Some((x_targets, y_targets)) = &self.snap_targets { if let Some((x_targets, y_targets)) = &self.snap_targets {
for x_target in x_targets { for x_target in x_targets {
self.overlay_paths.push(add_overlay_line( self.overlay_paths
responses, .push(self.add_overlay_line(DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.y, 1.), PI / 2., DVec2::new(x_target.round(), 0.))));
DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.y, 1.), PI / 2., DVec2::new(x_target.round(), 0.)),
));
} }
for y_target in y_targets { for y_target in y_targets {
self.overlay_paths.push(add_overlay_line( self.overlay_paths
responses, .push(self.add_overlay_line(DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.x, 1.), 0., DVec2::new(0., y_target.round()))));
DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.x, 1.), 0., DVec2::new(0., y_target.round())),
));
} }
} }
} }
@@ -58,29 +58,30 @@ impl SnapHandler {
/// Gets a list of snap targets for the X and Y axes in Viewport coords for the target layers (usually all layers or all non-selected layers.) /// Gets a list of snap targets for the X and Y axes in Viewport coords for the target layers (usually all layers or all non-selected layers.)
/// This should be called at the start of a drag. /// This should be called at the start of a drag.
pub fn start_snap(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2, document_message_handler: &DocumentMessageHandler, target_layers: Vec<Vec<LayerId>>) { pub fn start_snap(&mut self, viewport_bounds: DVec2, target_layers: Vec<Vec<LayerId>>) {
if document_message_handler.snapping_enabled { if self.document.snapping_enabled {
// Could be made into sorted Vec or a HashSet for more performant lookups. // Could be made into sorted Vec or a HashSet for more performant lookups.
self.snap_targets = Some( self.snap_targets = Some(
target_layers target_layers
.iter() .iter()
.filter_map(|path| document_message_handler.graphene_document.viewport_bounding_box(path).ok()?) .filter_map(|path| self.document.graphene_document.viewport_bounding_box(path).ok()?)
.flat_map(|[bound1, bound2]| [bound1, bound2, ((bound1 + bound2) / 2.)]) .flat_map(|[bound1, bound2]| [bound1, bound2, ((bound1 + bound2) / 2.)])
.map(|vec| vec.into()) .map(|vec| vec.into())
.unzip(), .unzip(),
); );
self.add_overlays(responses, viewport_bounds); self.add_overlays(viewport_bounds);
} }
} }
/// Finds the closest snap from an array of layers to the specified snap targets in viewport coords. /// Finds the closest snap from an array of layers to the specified snap targets in viewport coords.
/// Returns 0 for each axis that there is no snap less than the snap tolerance. /// Returns 0 for each axis that there is no snap less than the snap tolerance.
pub fn snap_layers(&self, document_message_handler: &DocumentMessageHandler, selected_layers: &[Vec<LayerId>], mouse_delta: DVec2) -> DVec2 { pub fn snap_layers(&self, mouse_delta: DVec2) -> DVec2 {
if document_message_handler.snapping_enabled { if self.enabled {
if let Some((targets_x, targets_y)) = &self.snap_targets { if let Some((targets_x, targets_y)) = &self.snap_targets {
let (snap_x, snap_y): (Vec<f64>, Vec<f64>) = selected_layers let (snap_x, snap_y): (Vec<f64>, Vec<f64>) = self
.snappables
.iter() .iter()
.filter_map(|path| document_message_handler.graphene_document.viewport_bounding_box(path).ok()?) .filter_map(|(path, _)| self.document.graphene_document.viewport_bounding_box(path).ok()?)
.flat_map(|[bound1, bound2]| [bound1, bound2, (bound1 + bound2) / 2.]) .flat_map(|[bound1, bound2]| [bound1, bound2, (bound1 + bound2) / 2.])
.map(|vec| vec.into()) .map(|vec| vec.into())
.unzip(); .unzip();
@@ -112,8 +113,8 @@ impl SnapHandler {
} }
/// Handles snapping of a viewport position, returning another viewport position. /// Handles snapping of a viewport position, returning another viewport position.
pub fn snap_position(&self, document_message_handler: &DocumentMessageHandler, position_viewport: DVec2) -> DVec2 { pub fn snap_position(&self, position_viewport: DVec2) -> DVec2 {
if document_message_handler.snapping_enabled { if self.document.snapping_enabled {
if let Some((targets_x, targets_y)) = &self.snap_targets { if let Some((targets_x, targets_y)) = &self.snap_targets {
// For each list of snap targets, find the shortest distance to move the point to that target. // For each list of snap targets, find the shortest distance to move the point to that target.
let closest_move = DVec2::new( let closest_move = DVec2::new(
@@ -148,4 +149,18 @@ impl SnapHandler {
self.remove_overlays(responses); self.remove_overlays(responses);
self.snap_targets = None; self.snap_targets = None;
} }
// ---
pub fn add_snappable(&mut self, layer: Vec<LayerId>) {
self.snappables.insert(layer, vec![]);
}
pub fn remove_snappable(&mut self, layer: Vec<LayerId>) {
self.snappables.remove(&layer);
}
pub fn process_overlays(&mut self, responses: &mut VecDeque<Message>) {
responses.extend(self.overlay_messages.into_iter());
self.overlay_messages.clear();
}
} }
+5
View File
@@ -1,4 +1,5 @@
use crate::message_prelude::*; use crate::message_prelude::*;
use glam::DVec2;
use graphene::color::Color; use graphene::color::Color;
use crate::input::InputPreprocessor; use crate::input::InputPreprocessor;
@@ -21,6 +22,10 @@ pub enum ToolMessage {
DocumentIsDirty, DocumentIsDirty,
UpdateHints, UpdateHints,
SetToolOptions(ToolType, ToolOptions), SetToolOptions(ToolType, ToolOptions),
SetSnapping(bool),
AddSnappable(Vec<LayerId>),
RemoveSnappable(Vec<LayerId>),
SnapLayer(Vec<LayerId>),
#[child] #[child]
Fill(FillMessage), Fill(FillMessage),
#[child] #[child]