Refactor ViewportPosition from u32 (UVec2) to f64 (DVec2) (#345)

* Refactor ViewportPosition from u32 (UVec2) to f64 (DVec2)

* Fix pseudo_hash call

* Replace hash function with proper function for uuid generation

* Cargo fmt

Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
Keavon Chambers
2021-08-14 02:45:24 -07:00
parent 42c3b1f6e9
commit 3f230c02b4
20 changed files with 128 additions and 75 deletions

View File

@@ -15,6 +15,8 @@ thiserror = "1.0.24"
serde = { version = "1.0", features = ["derive"] }
graphite-proc-macros = { path = "../proc-macros" }
glam = { version="0.17", features = ["serde"] }
rand_chacha = "0.3.1"
spin = "0.9.2"
[dependencies.graphene]
path = "../graphene"

View File

@@ -100,11 +100,11 @@ mod test {
}));
editor.select_primary_color(Color::RED);
editor.draw_rect(100, 200, 300, 400);
editor.draw_rect(100., 200., 300., 400.);
editor.select_primary_color(Color::BLUE);
editor.draw_shape(10, 1200, 1300, 400);
editor.draw_shape(10., 1200., 1300., 400.);
editor.select_primary_color(Color::GREEN);
editor.draw_ellipse(104, 1200, 1300, 400);
editor.draw_ellipse(104., 1200., 1300., 400.);
editor
}
@@ -278,7 +278,7 @@ mod test {
editor.handle_message(DocumentMessage::SelectLayers(vec![vec![rect_id], vec![ellipse_id]])).unwrap();
editor.handle_message(DocumentsMessage::CopySelectedLayers).unwrap();
editor.handle_message(DocumentMessage::DeleteSelectedLayers).unwrap();
editor.draw_rect(0, 800, 12, 200);
editor.draw_rect(0., 800., 12., 200.);
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
@@ -303,7 +303,7 @@ mod test {
/// - create rect, shape and ellipse
/// - select ellipse and rect
/// - move them down and back up again
fn move_seletion() {
fn move_selection() {
init_logger();
let mut editor = create_editor_with_three_layers();

View File

@@ -1,16 +1,20 @@
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
pub mod dispatcher;
pub mod message;
use crate::message_prelude::*;
pub use dispatcher::*;
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha20Rng,
};
use spin::Mutex;
pub use crate::input::InputPreprocessor;
use std::collections::VecDeque;
pub type ActionList = Vec<Vec<MessageDiscriminant>>;
static RNG: Mutex<Option<ChaCha20Rng>> = Mutex::new(None);
// TODO: Add Send + Sync requirement
// Use something like rw locks for synchronization
pub trait MessageHandlerData {}
@@ -25,12 +29,10 @@ where
fn actions(&self) -> ActionList;
}
pub fn generate_hash<'a>(messages: impl IntoIterator<Item = &'a Message>, ipp: &InputPreprocessor, document_hash: u64) -> u64 {
let mut s = DefaultHasher::new();
document_hash.hash(&mut s);
ipp.hash(&mut s);
for message in messages {
message.pseudo_hash().hash(&mut s);
pub fn generate_uuid() -> u64 {
let mut lock = RNG.lock();
if lock.is_none() {
*lock = Some(ChaCha20Rng::seed_from_u64(0));
}
s.finish()
lock.as_mut().map(ChaCha20Rng::next_u64).unwrap()
}

View File

@@ -242,7 +242,7 @@ impl DocumentMessageHandler {
}
/// Returns a list of `LayerPanelEntry`s intended for display purposes. These don't contain
/// any actual data, but ratfolderch as visibility and names of the layers.
/// any actual data, but rather attributes such as visibility and names of the layers.
pub fn layer_panel(&mut self, path: &[LayerId]) -> Result<Vec<LayerPanelEntry>, EditorError> {
let folder = self.document.folder(path)?;
let paths: Vec<Vec<LayerId>> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect();
@@ -284,7 +284,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}
CommitTransaction => self.document_backup = None,
ExportDocument => {
let bbox = self.document.visible_layers_bounding_box().unwrap_or([DVec2::ZERO, ipp.viewport_size.as_f64()]);
let bbox = self.document.visible_layers_bounding_box().unwrap_or([DVec2::ZERO, ipp.viewport_size]);
let size = bbox[1] - bbox[0];
let name = match self.name.ends_with(FILE_SAVE_SUFFIX) {
true => self.name.clone().replace(FILE_SAVE_SUFFIX, FILE_EXPORT_SUFFIX),

View File

@@ -32,7 +32,7 @@ pub enum MovementMessage {
ZoomCanvasToFitAll,
}
#[derive(Debug, Clone, Hash, Default, PartialEq)]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct MovementMessageHandler {
translating: bool,
rotating: bool,
@@ -43,7 +43,7 @@ pub struct MovementMessageHandler {
impl MovementMessageHandler {
fn create_document_transform_from_layerdata(&self, layerdata: &LayerData, viewport_size: &ViewportPosition, responses: &mut VecDeque<Message>) {
let half_viewport = viewport_size.as_f64() / 2.;
let half_viewport = *viewport_size / 2.;
let scaled_half_viewport = half_viewport / layerdata.scale;
responses.push_back(
DocumentOperation::SetLayerTransform {
@@ -64,7 +64,6 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
self.translating = true;
self.mouse_pos = ipp.mouse.position;
}
RotateCanvasBegin { snap } => {
self.rotating = true;
self.snapping = snap;
@@ -86,17 +85,17 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
}
MouseMove => {
if self.translating {
let delta = ipp.mouse.position.as_f64() - self.mouse_pos.as_f64();
let delta = ipp.mouse.position - self.mouse_pos;
let transformed_delta = document.root.transform.inverse().transform_vector2(delta);
layerdata.translation += transformed_delta;
self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_size, responses);
}
if self.rotating {
let half_viewport = ipp.viewport_size.as_f64() / 2.;
let half_viewport = ipp.viewport_size / 2.;
let rotation = {
let start_vec = self.mouse_pos.as_f64() - half_viewport;
let end_vec = ipp.mouse.position.as_f64() - half_viewport;
let start_vec = self.mouse_pos - half_viewport;
let end_vec = ipp.mouse.position - half_viewport;
start_vec.angle_between(end_vec)
};
@@ -140,8 +139,8 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
}
WheelCanvasZoom => {
let scroll = ipp.mouse.scroll_delta.scroll_delta();
let mouse = ipp.mouse.position.as_f64();
let viewport_size = ipp.viewport_size.as_f64();
let mouse = ipp.mouse.position;
let viewport_size = ipp.viewport_size;
let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE;
if ipp.mouse.scroll_delta.y > 0 {
zoom_factor = 1. / zoom_factor
@@ -177,7 +176,7 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
let pos1 = document.root.transform.inverse().transform_point2(pos1);
let pos2 = document.root.transform.inverse().transform_point2(pos2);
let v1 = document.root.transform.inverse().transform_point2(DVec2::ZERO);
let v2 = document.root.transform.inverse().transform_point2(ipp.viewport_size.as_f64());
let v2 = document.root.transform.inverse().transform_point2(ipp.viewport_size);
let center = v1.lerp(v2, 0.5) - pos1.lerp(pos2, 0.5);
let size = (pos2 - pos1) / (v2 - v1);

View File

@@ -30,7 +30,7 @@ bitflags! {
}
}
#[derive(Debug, Default, Hash)]
#[derive(Debug, Default)]
pub struct InputPreprocessor {
pub keyboard: KeyStates,
pub mouse: MouseState,
@@ -81,7 +81,7 @@ impl MessageHandler<InputPreprocessorMessage, ()> for InputPreprocessor {
responses.push_back(
graphene::Operation::TransformLayer {
path: vec![],
transform: glam::DAffine2::from_translation((size.as_f64() - self.viewport_size.as_f64()) / 2.).to_cols_array(),
transform: glam::DAffine2::from_translation((size - self.viewport_size) / 2.).to_cols_array(),
}
.into(),
);
@@ -141,7 +141,7 @@ mod test {
#[test]
fn process_action_mouse_move_handle_modifier_keys() {
let mut input_preprocessor = InputPreprocessor::default();
let message = InputPreprocessorMessage::MouseMove((4, 809).into(), ModifierKeys::ALT);
let message = InputPreprocessorMessage::MouseMove((4., 809.).into(), ModifierKeys::ALT);
let mut responses = VecDeque::new();
input_preprocessor.process_action(message, (), &mut responses);

View File

@@ -1,8 +1,8 @@
use bitflags::bitflags;
use glam::DVec2;
// origin is top left
pub type ViewportPosition = glam::UVec2;
// Origin is top left
pub type ViewportPosition = DVec2;
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub struct ScrollDelta {
@@ -23,7 +23,7 @@ impl ScrollDelta {
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Default, PartialEq)]
pub struct MouseState {
pub position: ViewportPosition,
pub mouse_keys: MouseKeys,
@@ -31,17 +31,18 @@ pub struct MouseState {
}
impl MouseState {
pub fn new() -> MouseState {
pub fn new() -> Self {
Self::default()
}
pub fn from_pos(x: u32, y: u32) -> MouseState {
MouseState {
pub fn from_pos(x: f64, y: f64) -> Self {
Self {
position: (x, y).into(),
mouse_keys: MouseKeys::default(),
scroll_delta: ScrollDelta::default(),
}
}
pub fn from_u8_pos(keys: u8, position: ViewportPosition) -> Self {
let mouse_keys = MouseKeys::from_bits(keys).expect("invalid modifier keys");
Self {

View File

@@ -50,7 +50,7 @@ impl Editor {
}
pub mod message_prelude {
pub use crate::communication::generate_hash;
pub use crate::communication::generate_uuid;
pub use crate::communication::message::{AsMessage, Message, MessageDiscriminant};
pub use crate::communication::{ActionList, MessageHandler};
pub use crate::document::{DocumentMessage, DocumentMessageDiscriminant};

View File

@@ -11,35 +11,35 @@ use graphene::color::Color;
/// A set of utility functions to make the writing of editor test more declarative
pub trait EditorTestUtils {
fn draw_rect(&mut self, x1: u32, y1: u32, x2: u32, y2: u32);
fn draw_shape(&mut self, x1: u32, y1: u32, x2: u32, y2: u32);
fn draw_ellipse(&mut self, x1: u32, y1: u32, x2: u32, y2: u32);
fn draw_rect(&mut self, x1: f64, y1: f64, x2: f64, y2: f64);
fn draw_shape(&mut self, x1: f64, y1: f64, x2: f64, y2: f64);
fn draw_ellipse(&mut self, x1: f64, y1: f64, x2: f64, y2: f64);
/// Select given tool and drag it from (x1, y1) to (x2, y2)
fn drag_tool(&mut self, typ: ToolType, x1: u32, y1: u32, x2: u32, y2: u32);
fn move_mouse(&mut self, x: u32, y: u32);
fn drag_tool(&mut self, typ: ToolType, x1: f64, y1: f64, x2: f64, y2: f64);
fn move_mouse(&mut self, x: f64, y: f64);
fn mousedown(&mut self, state: MouseState);
fn mouseup(&mut self, state: MouseState);
fn lmb_mousedown(&mut self, x: u32, y: u32);
fn lmb_mousedown(&mut self, x: f64, y: f64);
fn input(&mut self, message: InputPreprocessorMessage);
fn select_tool(&mut self, typ: ToolType);
fn select_primary_color(&mut self, color: Color);
}
impl EditorTestUtils for Editor {
fn draw_rect(&mut self, x1: u32, y1: u32, x2: u32, y2: u32) {
fn draw_rect(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
self.drag_tool(ToolType::Rectangle, x1, y1, x2, y2);
}
fn draw_shape(&mut self, x1: u32, y1: u32, x2: u32, y2: u32) {
fn draw_shape(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
self.drag_tool(ToolType::Shape, x1, y1, x2, y2);
}
fn draw_ellipse(&mut self, x1: u32, y1: u32, x2: u32, y2: u32) {
fn draw_ellipse(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
self.drag_tool(ToolType::Ellipse, x1, y1, x2, y2);
}
fn drag_tool(&mut self, typ: ToolType, x1: u32, y1: u32, x2: u32, y2: u32) {
fn drag_tool(&mut self, typ: ToolType, x1: f64, y1: f64, x2: f64, y2: f64) {
self.select_tool(typ);
self.move_mouse(x1, y1);
self.lmb_mousedown(x1, y1);
@@ -51,7 +51,7 @@ impl EditorTestUtils for Editor {
});
}
fn move_mouse(&mut self, x: u32, y: u32) {
fn move_mouse(&mut self, x: f64, y: f64) {
self.input(InputPreprocessorMessage::MouseMove((x, y).into(), ModifierKeys::default()));
}
@@ -63,7 +63,7 @@ impl EditorTestUtils for Editor {
self.handle_message(InputPreprocessorMessage::MouseUp(state, ModifierKeys::default())).unwrap()
}
fn lmb_mousedown(&mut self, x: u32, y: u32) {
fn lmb_mousedown(&mut self, x: f64, y: f64) {
self.mousedown(MouseState {
position: (x, y).into(),
mouse_keys: MouseKeys::LEFT,

View File

@@ -58,7 +58,7 @@ impl Fsm for EllipseToolFsmState {
fn transition(
self,
event: ToolMessage,
document: &DocumentMessageHandler,
_document: &DocumentMessageHandler,
tool_data: &DocumentToolData,
data: &mut Self::ToolData,
input: &InputPreprocessor,
@@ -72,7 +72,7 @@ impl Fsm for EllipseToolFsmState {
(Ready, DragStart) => {
shape_data.drag_start = input.mouse.position;
responses.push_back(DocumentMessage::StartTransaction.into());
shape_data.path = Some(vec![generate_hash(&*responses, input, document.document.hash())]);
shape_data.path = Some(vec![generate_uuid()]);
responses.push_back(DocumentMessage::DeselectAllLayers.into());
responses.push_back(

View File

@@ -19,7 +19,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Eyedropper {
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
let mouse_pos = data.2.mouse.position;
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([mouse_pos.as_f64() - tolerance, mouse_pos.as_f64() + tolerance]);
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
if let Some(path) = data.0.document.intersects_quad_root(quad).last() {
if let Ok(layer) = data.0.document.layer(path) {

View File

@@ -17,7 +17,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Fill {
fn process_action(&mut self, _action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
let mouse_pos = data.2.mouse.position;
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([mouse_pos.as_f64() - tolerance, mouse_pos.as_f64() + tolerance]);
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
if let Some(path) = data.0.document.intersects_quad_root(quad).last() {
responses.push_back(

View File

@@ -59,7 +59,7 @@ impl Fsm for LineToolFsmState {
fn transition(
self,
event: ToolMessage,
document: &DocumentMessageHandler,
_document: &DocumentMessageHandler,
tool_data: &DocumentToolData,
data: &mut Self::ToolData,
input: &InputPreprocessor,
@@ -72,7 +72,7 @@ impl Fsm for LineToolFsmState {
(Ready, DragStart) => {
data.drag_start = input.mouse.position;
responses.push_back(DocumentMessage::StartTransaction.into());
data.path = Some(vec![generate_hash(&*responses, input, document.document.hash())]);
data.path = Some(vec![generate_uuid()]);
responses.push_back(DocumentMessage::DeselectAllLayers.into());
responses.push_back(
@@ -122,8 +122,8 @@ impl Fsm for LineToolFsmState {
}
fn generate_transform(data: &mut LineToolData, lock: bool, snap: bool, center: bool) -> Message {
let mut start = data.drag_start.as_f64();
let stop = data.drag_current.as_f64();
let mut start = data.drag_start;
let stop = data.drag_current;
let dir = stop - start;

View File

@@ -65,7 +65,7 @@ impl Fsm for PenToolFsmState {
responses: &mut VecDeque<Message>,
) -> Self {
let transform = document.document.root.transform;
let pos = transform.inverse() * DAffine2::from_translation(input.mouse.position.as_f64());
let pos = transform.inverse() * DAffine2::from_translation(input.mouse.position);
use PenMessage::*;
use PenToolFsmState::*;
@@ -74,7 +74,7 @@ impl Fsm for PenToolFsmState {
(Ready, DragStart) => {
responses.push_back(DocumentMessage::StartTransaction.into());
responses.push_back(DocumentMessage::DeselectAllLayers.into());
data.path = Some(vec![generate_hash(&*responses, input, document.document.hash())]);
data.path = Some(vec![generate_uuid()]);
data.points.push(pos);
data.next_point = pos;

View File

@@ -58,7 +58,7 @@ impl Fsm for RectangleToolFsmState {
fn transition(
self,
event: ToolMessage,
document: &DocumentMessageHandler,
_document: &DocumentMessageHandler,
tool_data: &DocumentToolData,
data: &mut Self::ToolData,
input: &InputPreprocessor,
@@ -72,7 +72,7 @@ impl Fsm for RectangleToolFsmState {
(Ready, DragStart) => {
shape_data.drag_start = input.mouse.position;
responses.push_back(DocumentMessage::StartTransaction.into());
shape_data.path = Some(vec![generate_hash(&*responses, input, document.document.hash())]);
shape_data.path = Some(vec![generate_uuid()]);
responses.push_back(DocumentMessage::DeselectAllLayers.into());
responses.push_back(

View File

@@ -11,8 +11,8 @@ pub struct Resize {
}
impl Resize {
pub fn calculate_transform(&self, center: Key, lock_ratio: Key, ipp: &InputPreprocessor) -> Option<Message> {
let mut start = self.drag_start.as_f64();
let stop = ipp.mouse.position.as_f64();
let mut start = self.drag_start;
let stop = ipp.mouse.position;
let mut size = stop - start;
if ipp.keyboard.get(lock_ratio as usize) {

View File

@@ -79,9 +79,9 @@ impl SelectToolData {
fn selection_box(&self) -> [DVec2; 2] {
if self.drag_current == self.drag_start {
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
[self.drag_start.as_f64() - tolerance, self.drag_start.as_f64() + tolerance]
[self.drag_start - tolerance, self.drag_start + tolerance]
} else {
[self.drag_start.as_f64(), self.drag_current.as_f64()]
[self.drag_start, self.drag_current]
}
}
}
@@ -122,7 +122,7 @@ impl Fsm for SelectToolFsmState {
Dragging
} else {
responses.push_back(DocumentMessage::DeselectAllLayers.into());
data.box_id = Some(vec![generate_hash(&*responses, input, document.document.hash())]);
data.box_id = Some(vec![generate_uuid()]);
responses.push_back(
Operation::AddBoundingBox {
path: data.box_id.clone().unwrap(),
@@ -139,7 +139,7 @@ impl Fsm for SelectToolFsmState {
responses.push_back(
Operation::TransformLayerInViewport {
path: path.clone(),
transform: DAffine2::from_translation(input.mouse.position.as_f64() - data.drag_current.as_f64()).to_cols_array(),
transform: DAffine2::from_translation(input.mouse.position - data.drag_current).to_cols_array(),
}
.into(),
);
@@ -149,8 +149,8 @@ impl Fsm for SelectToolFsmState {
}
(DrawingBox, MouseMove) => {
data.drag_current = input.mouse.position;
let start = data.drag_start.as_f64();
let size = data.drag_current.as_f64() - start;
let start = data.drag_start;
let size = data.drag_current - start;
responses.push_back(
Operation::SetLayerTransformInViewport {

View File

@@ -58,7 +58,7 @@ impl Fsm for ShapeToolFsmState {
fn transition(
self,
event: ToolMessage,
document: &DocumentMessageHandler,
_document: &DocumentMessageHandler,
tool_data: &DocumentToolData,
data: &mut Self::ToolData,
input: &InputPreprocessor,
@@ -72,7 +72,7 @@ impl Fsm for ShapeToolFsmState {
(Ready, DragStart) => {
shape_data.drag_start = input.mouse.position;
responses.push_back(DocumentMessage::StartTransaction.into());
shape_data.path = Some(vec![generate_hash(&*responses, input, document.document.hash())]);
shape_data.path = Some(vec![generate_uuid()]);
responses.push_back(DocumentMessage::DeselectAllLayers.into());
data.sides = match tool_data.tool_options.get(&ToolType::Shape) {
Some(&ToolOptions::Shape {