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

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 graphene::{
layers::style::{self, Stroke},
@@ -11,41 +12,40 @@ use crate::{
message_prelude::*,
};
use super::DocumentMessageHandler;
#[derive(Debug, Clone, Default)]
pub struct SnapHandler {
enabled: bool,
document: DocumentMessageHandler,
snap_targets: Option<(Vec<f64>, Vec<f64>)>,
snappables: HashMap<Vec<LayerId>, Vec<Vec<LayerId>>>,
overlay_messages: Vec<Message>,
overlay_paths: Vec<Vec<LayerId>>,
}
impl SnapHandler {
fn add_overlays(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2) {
fn add_overlay_line(responses: &mut VecDeque<Message>, transform: DAffine2) -> Vec<LayerId> {
let layer_path = vec![generate_uuid()];
fn add_overlay_line(&mut self, transform: DAffine2) -> Vec<LayerId> {
let layer_path = vec![generate_uuid()];
let operation = Operation::AddOverlayLine {
path: layer_path.clone(),
transform: transform.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), None),
};
responses.push_back(DocumentMessage::Overlay(operation.into()).into());
let operation = Operation::AddOverlayLine {
path: layer_path.clone(),
transform: transform.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), None),
};
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 {
for x_target in x_targets {
self.overlay_paths.push(add_overlay_line(
responses,
DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.y, 1.), PI / 2., DVec2::new(x_target.round(), 0.)),
));
self.overlay_paths
.push(self.add_overlay_line(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 {
self.overlay_paths.push(add_overlay_line(
responses,
DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.x, 1.), 0., DVec2::new(0., y_target.round())),
));
self.overlay_paths
.push(self.add_overlay_line(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.)
/// 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>>) {
if document_message_handler.snapping_enabled {
pub fn start_snap(&mut self, viewport_bounds: DVec2, target_layers: Vec<Vec<LayerId>>) {
if self.document.snapping_enabled {
// Could be made into sorted Vec or a HashSet for more performant lookups.
self.snap_targets = Some(
target_layers
.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.)])
.map(|vec| vec.into())
.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.
/// 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 {
if document_message_handler.snapping_enabled {
pub fn snap_layers(&self, mouse_delta: DVec2) -> DVec2 {
if self.enabled {
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()
.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.])
.map(|vec| vec.into())
.unzip();
@@ -112,8 +113,8 @@ impl SnapHandler {
}
/// Handles snapping of a viewport position, returning another viewport position.
pub fn snap_position(&self, document_message_handler: &DocumentMessageHandler, position_viewport: DVec2) -> DVec2 {
if document_message_handler.snapping_enabled {
pub fn snap_position(&self, position_viewport: DVec2) -> DVec2 {
if self.document.snapping_enabled {
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.
let closest_move = DVec2::new(
@@ -148,4 +149,18 @@ impl SnapHandler {
self.remove_overlays(responses);
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();
}
}

View File

@@ -1,4 +1,5 @@
use crate::message_prelude::*;
use glam::DVec2;
use graphene::color::Color;
use crate::input::InputPreprocessor;
@@ -21,6 +22,10 @@ pub enum ToolMessage {
DocumentIsDirty,
UpdateHints,
SetToolOptions(ToolType, ToolOptions),
SetSnapping(bool),
AddSnappable(Vec<LayerId>),
RemoveSnappable(Vec<LayerId>),
SnapLayer(Vec<LayerId>),
#[child]
Fill(FillMessage),
#[child]