mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-04 02:18:11 +08:00
Split out viewport handling into its own message handler (#3331)
* extract viewport handeling * fix web overlays * some cleanup * remove some physical conversions * fix resize snapping * fixup * apply some review feedback * make viewport api more ergonomic * fix * fix web overlay canvas clear size * clear workaround for canvas * rename trigger message --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
@@ -26,6 +26,7 @@ pub struct DispatcherMessageHandlers {
|
|||||||
pub portfolio_message_handler: PortfolioMessageHandler,
|
pub portfolio_message_handler: PortfolioMessageHandler,
|
||||||
preferences_message_handler: PreferencesMessageHandler,
|
preferences_message_handler: PreferencesMessageHandler,
|
||||||
tool_message_handler: ToolMessageHandler,
|
tool_message_handler: ToolMessageHandler,
|
||||||
|
viewport_message_handler: ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DispatcherMessageHandlers {
|
impl DispatcherMessageHandlers {
|
||||||
@@ -146,7 +147,6 @@ impl Dispatcher {
|
|||||||
let context = DialogMessageContext {
|
let context = DialogMessageContext {
|
||||||
portfolio: &self.message_handlers.portfolio_message_handler,
|
portfolio: &self.message_handlers.portfolio_message_handler,
|
||||||
preferences: &self.message_handlers.preferences_message_handler,
|
preferences: &self.message_handlers.preferences_message_handler,
|
||||||
viewport_bounds: &self.message_handlers.input_preprocessor_message_handler.viewport_bounds,
|
|
||||||
};
|
};
|
||||||
self.message_handlers.dialog_message_handler.process_message(message, &mut queue, context);
|
self.message_handlers.dialog_message_handler.process_message(message, &mut queue, context);
|
||||||
}
|
}
|
||||||
@@ -169,9 +169,14 @@ impl Dispatcher {
|
|||||||
Message::InputPreprocessor(message) => {
|
Message::InputPreprocessor(message) => {
|
||||||
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
|
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
|
||||||
|
|
||||||
self.message_handlers
|
self.message_handlers.input_preprocessor_message_handler.process_message(
|
||||||
.input_preprocessor_message_handler
|
message,
|
||||||
.process_message(message, &mut queue, InputPreprocessorMessageContext { keyboard_platform });
|
&mut queue,
|
||||||
|
InputPreprocessorMessageContext {
|
||||||
|
keyboard_platform,
|
||||||
|
viewport: &self.message_handlers.viewport_message_handler,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::KeyMapping(message) => {
|
Message::KeyMapping(message) => {
|
||||||
let input = &self.message_handlers.input_preprocessor_message_handler;
|
let input = &self.message_handlers.input_preprocessor_message_handler;
|
||||||
@@ -195,6 +200,7 @@ impl Dispatcher {
|
|||||||
let reset_node_definitions_on_open = self.message_handlers.portfolio_message_handler.reset_node_definitions_on_open;
|
let reset_node_definitions_on_open = self.message_handlers.portfolio_message_handler.reset_node_definitions_on_open;
|
||||||
let timing_information = self.message_handlers.animation_message_handler.timing_information();
|
let timing_information = self.message_handlers.animation_message_handler.timing_information();
|
||||||
let animation = &self.message_handlers.animation_message_handler;
|
let animation = &self.message_handlers.animation_message_handler;
|
||||||
|
let viewport = &self.message_handlers.viewport_message_handler;
|
||||||
|
|
||||||
self.message_handlers.portfolio_message_handler.process_message(
|
self.message_handlers.portfolio_message_handler.process_message(
|
||||||
message,
|
message,
|
||||||
@@ -207,6 +213,7 @@ impl Dispatcher {
|
|||||||
reset_node_definitions_on_open,
|
reset_node_definitions_on_open,
|
||||||
timing_information,
|
timing_information,
|
||||||
animation,
|
animation,
|
||||||
|
viewport,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -230,10 +237,14 @@ impl Dispatcher {
|
|||||||
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
|
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
|
||||||
node_graph: &self.message_handlers.portfolio_message_handler.executor,
|
node_graph: &self.message_handlers.portfolio_message_handler.executor,
|
||||||
preferences: &self.message_handlers.preferences_message_handler,
|
preferences: &self.message_handlers.preferences_message_handler,
|
||||||
|
viewport: &self.message_handlers.viewport_message_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.message_handlers.tool_message_handler.process_message(message, &mut queue, context);
|
self.message_handlers.tool_message_handler.process_message(message, &mut queue, context);
|
||||||
}
|
}
|
||||||
|
Message::Viewport(message) => {
|
||||||
|
self.message_handlers.viewport_message_handler.process_message(message, &mut queue, ());
|
||||||
|
}
|
||||||
Message::NoOp => {}
|
Message::NoOp => {}
|
||||||
Message::Batched { messages } => {
|
Message::Batched { messages } => {
|
||||||
messages.into_iter().for_each(|message| self.handle_message(message, false));
|
messages.into_iter().for_each(|message| self.handle_message(message, false));
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
use super::new_document_dialog::NewDocumentDialogMessageContext;
|
|
||||||
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog};
|
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog};
|
||||||
use crate::messages::dialog::simple_dialogs::LicensesThirdPartyDialog;
|
use crate::messages::dialog::simple_dialogs::LicensesThirdPartyDialog;
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportBounds;
|
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct DialogMessageContext<'a> {
|
pub struct DialogMessageContext<'a> {
|
||||||
pub portfolio: &'a PortfolioMessageHandler,
|
pub portfolio: &'a PortfolioMessageHandler,
|
||||||
pub viewport_bounds: &'a ViewportBounds,
|
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,15 +20,11 @@ pub struct DialogMessageHandler {
|
|||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHandler {
|
impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHandler {
|
||||||
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, context: DialogMessageContext) {
|
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, context: DialogMessageContext) {
|
||||||
let DialogMessageContext {
|
let DialogMessageContext { portfolio, preferences } = context;
|
||||||
portfolio,
|
|
||||||
preferences,
|
|
||||||
viewport_bounds,
|
|
||||||
} = context;
|
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageContext { portfolio }),
|
DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageContext { portfolio }),
|
||||||
DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, NewDocumentDialogMessageContext { viewport_bounds }),
|
DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()),
|
||||||
DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageContext { preferences }),
|
DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageContext { preferences }),
|
||||||
|
|
||||||
DialogMessage::CloseAllDocumentsWithConfirmation => {
|
DialogMessage::CloseAllDocumentsWithConfirmation => {
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ mod new_document_dialog_message_handler;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use new_document_dialog_message::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant};
|
pub use new_document_dialog_message::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use new_document_dialog_message_handler::{NewDocumentDialogMessageContext, NewDocumentDialogMessageHandler};
|
pub use new_document_dialog_message_handler::NewDocumentDialogMessageHandler;
|
||||||
|
|||||||
+6
-14
@@ -1,13 +1,8 @@
|
|||||||
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::{input_mapper::utility_types::input_mouse::ViewportBounds, layout::utility_types::widget_prelude::*};
|
|
||||||
use glam::{IVec2, UVec2};
|
use glam::{IVec2, UVec2};
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
|
||||||
pub struct NewDocumentDialogMessageContext<'a> {
|
|
||||||
pub viewport_bounds: &'a ViewportBounds,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A dialog to allow users to set some initial options about a new document.
|
/// A dialog to allow users to set some initial options about a new document.
|
||||||
#[derive(Debug, Clone, Default, ExtractField)]
|
#[derive(Debug, Clone, Default, ExtractField)]
|
||||||
pub struct NewDocumentDialogMessageHandler {
|
pub struct NewDocumentDialogMessageHandler {
|
||||||
@@ -17,8 +12,8 @@ pub struct NewDocumentDialogMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<NewDocumentDialogMessage, NewDocumentDialogMessageContext<'a>> for NewDocumentDialogMessageHandler {
|
impl<'a> MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler {
|
||||||
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, context: NewDocumentDialogMessageContext<'a>) {
|
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
NewDocumentDialogMessage::Name { name } => self.name = name,
|
NewDocumentDialogMessage::Name { name } => self.name = name,
|
||||||
NewDocumentDialogMessage::Infinite { infinite } => self.infinite = infinite,
|
NewDocumentDialogMessage::Infinite { infinite } => self.infinite = infinite,
|
||||||
@@ -35,12 +30,9 @@ impl<'a> MessageHandler<NewDocumentDialogMessage, NewDocumentDialogMessageContex
|
|||||||
});
|
});
|
||||||
responses.add(NavigationMessage::CanvasPan { delta: self.dimensions.as_dvec2() });
|
responses.add(NavigationMessage::CanvasPan { delta: self.dimensions.as_dvec2() });
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
// If we already have bounds, we won't receive a viewport bounds update so we just fabricate one ourselves
|
|
||||||
if *context.viewport_bounds != ViewportBounds::default() {
|
responses.add(ViewportMessage::RepropagateUpdate);
|
||||||
responses.add(InputPreprocessorMessage::BoundsOfViewports {
|
|
||||||
bounds_of_viewports: vec![context.viewport_bounds.clone()],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
responses.add(DeferMessage::AfterNavigationReady {
|
responses.add(DeferMessage::AfterNavigationReady {
|
||||||
messages: vec![DocumentMessage::ZoomCanvasToFitAll.into(), DocumentMessage::DeselectAllLayers.into()],
|
messages: vec![DocumentMessage::ZoomCanvasToFitAll.into(), DocumentMessage::DeselectAllLayers.into()],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -350,6 +350,12 @@ pub enum FrontendMessage {
|
|||||||
UpdateViewportHolePunch {
|
UpdateViewportHolePunch {
|
||||||
active: bool,
|
active: bool,
|
||||||
},
|
},
|
||||||
|
UpdateViewportPhysicalBounds {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
},
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
RenderOverlays {
|
RenderOverlays {
|
||||||
#[serde(skip, default = "OverlayContext::default")]
|
#[serde(skip, default = "OverlayContext::default")]
|
||||||
|
|||||||
@@ -3,41 +3,13 @@ use crate::messages::prelude::*;
|
|||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
// Origin is top left
|
// Origin is top left
|
||||||
pub type DocumentPosition = DVec2;
|
pub type DocumentPosition = DVec2;
|
||||||
pub type ViewportPosition = DVec2;
|
pub type ViewportPosition = DVec2;
|
||||||
pub type EditorPosition = DVec2;
|
pub type EditorPosition = DVec2;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct ViewportBounds {
|
|
||||||
pub top_left: DVec2,
|
|
||||||
pub bottom_right: DVec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ViewportBounds {
|
|
||||||
pub fn from_slice(slice: &[f64]) -> Self {
|
|
||||||
Self {
|
|
||||||
top_left: DVec2::from_slice(&slice[0..2]),
|
|
||||||
bottom_right: DVec2::from_slice(&slice[2..4]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> DVec2 {
|
|
||||||
(self.bottom_right - self.top_left).ceil()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn center(&self) -> DVec2 {
|
|
||||||
(self.bottom_right - self.top_left).ceil() / 2.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn in_bounds(&self, position: ViewportPosition) -> bool {
|
|
||||||
position.x >= 0. && position.y >= 0. && position.x <= self.bottom_right.x && position.y <= self.bottom_right.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct ScrollDelta {
|
pub struct ScrollDelta {
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
@@ -113,9 +85,9 @@ impl EditorMouseState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_mouse_state(&self, active_viewport_bounds: &ViewportBounds) -> MouseState {
|
pub fn to_mouse_state(&self, viewport: &ViewportMessageHandler) -> MouseState {
|
||||||
MouseState {
|
MouseState {
|
||||||
position: self.editor_position - active_viewport_bounds.top_left,
|
position: (viewport.logical(self.editor_position) - viewport.offset()).into(),
|
||||||
mouse_keys: self.mouse_keys,
|
mouse_keys: self.mouse_keys,
|
||||||
scroll_delta: self.scroll_delta,
|
scroll_delta: self.scroll_delta,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
|
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ViewportBounds};
|
use crate::messages::input_mapper::utility_types::input_mouse::EditorMouseState;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[impl_message(Message, InputPreprocessor)]
|
#[impl_message(Message, InputPreprocessor)]
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum InputPreprocessorMessage {
|
pub enum InputPreprocessorMessage {
|
||||||
BoundsOfViewports { bounds_of_viewports: Vec<ViewportBounds> },
|
|
||||||
DoubleClick { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
DoubleClick { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||||
KeyDown { key: Key, key_repeat: bool, modifier_keys: ModifierKeys },
|
KeyDown { key: Key, key_repeat: bool, modifier_keys: ModifierKeys },
|
||||||
KeyUp { key: Key, key_repeat: bool, modifier_keys: ModifierKeys },
|
KeyUp { key: Key, key_repeat: bool, modifier_keys: ModifierKeys },
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeyStates, ModifierKeys};
|
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeyStates, ModifierKeys};
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::{MouseButton, MouseKeys, MouseState, ViewportBounds};
|
use crate::messages::input_mapper::utility_types::input_mouse::{MouseButton, MouseKeys, MouseState};
|
||||||
use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo;
|
use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo;
|
||||||
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use glam::DVec2;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct InputPreprocessorMessageContext {
|
pub struct InputPreprocessorMessageContext<'a> {
|
||||||
pub keyboard_platform: KeyboardPlatformLayout,
|
pub keyboard_platform: KeyboardPlatformLayout,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, ExtractField)]
|
#[derive(Debug, Default, ExtractField)]
|
||||||
@@ -17,38 +17,18 @@ pub struct InputPreprocessorMessageHandler {
|
|||||||
pub time: u64,
|
pub time: u64,
|
||||||
pub keyboard: KeyStates,
|
pub keyboard: KeyStates,
|
||||||
pub mouse: MouseState,
|
pub mouse: MouseState,
|
||||||
pub viewport_bounds: ViewportBounds,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> for InputPreprocessorMessageHandler {
|
impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext<'a>> for InputPreprocessorMessageHandler {
|
||||||
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, context: InputPreprocessorMessageContext) {
|
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, context: InputPreprocessorMessageContext<'a>) {
|
||||||
let InputPreprocessorMessageContext { keyboard_platform } = context;
|
let InputPreprocessorMessageContext { keyboard_platform, viewport } = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
InputPreprocessorMessage::BoundsOfViewports { bounds_of_viewports } => {
|
|
||||||
assert_eq!(bounds_of_viewports.len(), 1, "Only one viewport is currently supported");
|
|
||||||
|
|
||||||
for bounds in bounds_of_viewports {
|
|
||||||
// TODO: Extend this to multiple viewports instead of setting it to the value of this last loop iteration
|
|
||||||
self.viewport_bounds = bounds;
|
|
||||||
|
|
||||||
responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO });
|
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
|
||||||
}
|
|
||||||
responses.add(DeferMessage::AfterGraphRun {
|
|
||||||
messages: vec![
|
|
||||||
DeferMessage::AfterGraphRun {
|
|
||||||
messages: vec![DeferMessage::TriggerNavigationReady.into()],
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
|
||||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
|
|
||||||
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
|
let mouse_state = editor_mouse_state.to_mouse_state(viewport);
|
||||||
self.mouse.position = mouse_state.position;
|
self.mouse.position = mouse_state.position;
|
||||||
|
|
||||||
for key in mouse_state.mouse_keys {
|
for key in mouse_state.mouse_keys {
|
||||||
@@ -81,7 +61,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
|||||||
InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys } => {
|
||||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
|
|
||||||
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
|
let mouse_state = editor_mouse_state.to_mouse_state(viewport);
|
||||||
self.mouse.position = mouse_state.position;
|
self.mouse.position = mouse_state.position;
|
||||||
|
|
||||||
self.translate_mouse_event(mouse_state, true, responses);
|
self.translate_mouse_event(mouse_state, true, responses);
|
||||||
@@ -89,7 +69,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
|||||||
InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys } => {
|
||||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
|
|
||||||
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
|
let mouse_state = editor_mouse_state.to_mouse_state(viewport);
|
||||||
self.mouse.position = mouse_state.position;
|
self.mouse.position = mouse_state.position;
|
||||||
|
|
||||||
responses.add(InputMapperMessage::PointerMove);
|
responses.add(InputMapperMessage::PointerMove);
|
||||||
@@ -100,7 +80,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
|||||||
InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys } => {
|
||||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
|
|
||||||
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
|
let mouse_state = editor_mouse_state.to_mouse_state(viewport);
|
||||||
self.mouse.position = mouse_state.position;
|
self.mouse.position = mouse_state.position;
|
||||||
|
|
||||||
self.translate_mouse_event(mouse_state, false, responses);
|
self.translate_mouse_event(mouse_state, false, responses);
|
||||||
@@ -108,7 +88,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
|||||||
InputPreprocessorMessage::PointerShake { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::PointerShake { editor_mouse_state, modifier_keys } => {
|
||||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
|
|
||||||
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
|
let mouse_state = editor_mouse_state.to_mouse_state(viewport);
|
||||||
self.mouse.position = mouse_state.position;
|
self.mouse.position = mouse_state.position;
|
||||||
|
|
||||||
responses.add(InputMapperMessage::PointerShake);
|
responses.add(InputMapperMessage::PointerShake);
|
||||||
@@ -121,7 +101,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
|||||||
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {
|
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {
|
||||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
|
|
||||||
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
|
let mouse_state = editor_mouse_state.to_mouse_state(viewport);
|
||||||
self.mouse.position = mouse_state.position;
|
self.mouse.position = mouse_state.position;
|
||||||
self.mouse.scroll_delta = mouse_state.scroll_delta;
|
self.mouse.scroll_delta = mouse_state.scroll_delta;
|
||||||
|
|
||||||
@@ -202,11 +182,6 @@ impl InputPreprocessorMessageHandler {
|
|||||||
responses.add(InputMapperMessage::KeyDown(key));
|
responses.add(InputMapperMessage::KeyDown(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn document_bounds(&self) -> [DVec2; 2] {
|
|
||||||
// IPP bounds are relative to the entire application
|
|
||||||
[(0., 0.).into(), self.viewport_bounds.bottom_right - self.viewport_bounds.top_left]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -232,6 +207,7 @@ mod test {
|
|||||||
|
|
||||||
let context = InputPreprocessorMessageContext {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
|
viewport: &ViewportMessageHandler::default(),
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, context);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
@@ -251,6 +227,7 @@ mod test {
|
|||||||
|
|
||||||
let context = InputPreprocessorMessageContext {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
|
viewport: &ViewportMessageHandler::default(),
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, context);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
@@ -270,6 +247,7 @@ mod test {
|
|||||||
|
|
||||||
let context = InputPreprocessorMessageContext {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
|
viewport: &ViewportMessageHandler::default(),
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, context);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
@@ -291,6 +269,7 @@ mod test {
|
|||||||
|
|
||||||
let context = InputPreprocessorMessageContext {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
|
viewport: &ViewportMessageHandler::default(),
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, context);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
@@ -311,6 +290,7 @@ mod test {
|
|||||||
|
|
||||||
let context = InputPreprocessorMessageContext {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
|
viewport: &ViewportMessageHandler::default(),
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, context);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ pub enum Message {
|
|||||||
Preferences(PreferencesMessage),
|
Preferences(PreferencesMessage),
|
||||||
#[child]
|
#[child]
|
||||||
Tool(ToolMessage),
|
Tool(ToolMessage),
|
||||||
|
#[child]
|
||||||
|
Viewport(ViewportMessage),
|
||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
Batched {
|
Batched {
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ pub mod portfolio;
|
|||||||
pub mod preferences;
|
pub mod preferences;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod tool;
|
pub mod tool;
|
||||||
|
pub mod viewport;
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ pub struct DocumentMessageContext<'a> {
|
|||||||
pub executor: &'a mut NodeGraphExecutor,
|
pub executor: &'a mut NodeGraphExecutor,
|
||||||
pub current_tool: &'a ToolType,
|
pub current_tool: &'a ToolType,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
pub device_pixel_ratio: f64,
|
|
||||||
pub data_panel_open: bool,
|
pub data_panel_open: bool,
|
||||||
pub layers_panel_open: bool,
|
pub layers_panel_open: bool,
|
||||||
pub properties_panel_open: bool,
|
pub properties_panel_open: bool,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ExtractField)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ExtractField)]
|
||||||
@@ -195,9 +195,9 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
ipp,
|
ipp,
|
||||||
persistent_data,
|
persistent_data,
|
||||||
executor,
|
executor,
|
||||||
|
viewport,
|
||||||
current_tool,
|
current_tool,
|
||||||
preferences,
|
preferences,
|
||||||
device_pixel_ratio,
|
|
||||||
data_panel_open,
|
data_panel_open,
|
||||||
layers_panel_open,
|
layers_panel_open,
|
||||||
properties_panel_open,
|
properties_panel_open,
|
||||||
@@ -213,6 +213,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
document_ptz: &mut self.document_ptz,
|
document_ptz: &mut self.document_ptz,
|
||||||
graph_view_overlay_open: self.graph_view_overlay_open,
|
graph_view_overlay_open: self.graph_view_overlay_open,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.navigation_handler.process_message(message, responses, context);
|
self.navigation_handler.process_message(message, responses, context);
|
||||||
@@ -221,15 +222,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
let visibility_settings = self.overlays_visibility_settings;
|
let visibility_settings = self.overlays_visibility_settings;
|
||||||
|
|
||||||
// Send the overlays message to the overlays message handler
|
// Send the overlays message to the overlays message handler
|
||||||
self.overlays_message_handler.process_message(
|
self.overlays_message_handler
|
||||||
message,
|
.process_message(message, responses, OverlaysMessageContext { visibility_settings, viewport });
|
||||||
responses,
|
|
||||||
OverlaysMessageContext {
|
|
||||||
visibility_settings,
|
|
||||||
ipp,
|
|
||||||
device_pixel_ratio,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
DocumentMessage::PropertiesPanel(message) => {
|
DocumentMessage::PropertiesPanel(message) => {
|
||||||
let context = PropertiesPanelMessageContext {
|
let context = PropertiesPanelMessageContext {
|
||||||
@@ -268,6 +262,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
navigation_handler: &self.navigation_handler,
|
navigation_handler: &self.navigation_handler,
|
||||||
preferences,
|
preferences,
|
||||||
layers_panel_open,
|
layers_panel_open,
|
||||||
|
viewport,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -380,8 +375,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
|
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
|
||||||
self.layer_range_selection_reference = None;
|
self.layer_range_selection_reference = None;
|
||||||
}
|
}
|
||||||
DocumentMessage::DocumentHistoryBackward => self.undo_with_history(ipp, responses),
|
DocumentMessage::DocumentHistoryBackward => self.undo_with_history(viewport, responses),
|
||||||
DocumentMessage::DocumentHistoryForward => self.redo_with_history(ipp, responses),
|
DocumentMessage::DocumentHistoryForward => self.redo_with_history(viewport, responses),
|
||||||
DocumentMessage::DocumentStructureChanged => {
|
DocumentMessage::DocumentStructureChanged => {
|
||||||
if layers_panel_open {
|
if layers_panel_open {
|
||||||
self.network_interface.load_structure();
|
self.network_interface.load_structure();
|
||||||
@@ -836,7 +831,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
let scale = DAffine2::from_scale(enlargement_factor);
|
let scale = DAffine2::from_scale(enlargement_factor);
|
||||||
let pivot = DAffine2::from_translation(pivot);
|
let pivot = DAffine2::from_translation(pivot);
|
||||||
let transformation = pivot * scale * pivot.inverse();
|
let transformation = pivot * scale * pivot.inverse();
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
|
|
||||||
for layer in self.network_interface.shallowest_unique_layers(&[]).filter(|layer| can_move(*layer)) {
|
for layer in self.network_interface.shallowest_unique_layers(&[]).filter(|layer| can_move(*layer)) {
|
||||||
let to = document_to_viewport.inverse() * self.metadata().downstream_transform_to_viewport(layer);
|
let to = document_to_viewport.inverse() * self.metadata().downstream_transform_to_viewport(layer);
|
||||||
@@ -861,10 +856,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
let image_size = DVec2::new(image.width as f64, image.height as f64);
|
let image_size = DVec2::new(image.width as f64, image.height as f64);
|
||||||
|
|
||||||
// Align the layer with the mouse or center of viewport
|
// Align the layer with the mouse or center of viewport
|
||||||
let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into());
|
let viewport_location = mouse.map_or(viewport.center_in_viewport_space().into_dvec2() + viewport.offset().into_dvec2(), |pos| pos.into());
|
||||||
|
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
let center_in_viewport = DAffine2::from_translation(document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left));
|
let center_in_viewport = DAffine2::from_translation(document_to_viewport.inverse().transform_point2(viewport_location - viewport.offset().into_dvec2()));
|
||||||
let center_in_viewport_layerspace = center_in_viewport;
|
let center_in_viewport_layerspace = center_in_viewport;
|
||||||
|
|
||||||
// Make layer the size of the image
|
// Make layer the size of the image
|
||||||
@@ -913,9 +908,9 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
mouse,
|
mouse,
|
||||||
parent_and_insert_index,
|
parent_and_insert_index,
|
||||||
} => {
|
} => {
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into());
|
let viewport_location = mouse.map_or(viewport.center_in_viewport_space().into_dvec2() + viewport.offset().into_dvec2(), |pos| pos.into());
|
||||||
let center_in_viewport = DAffine2::from_translation(document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left));
|
let center_in_viewport = DAffine2::from_translation(document_to_viewport.inverse().transform_point2(viewport_location - viewport.offset().into_dvec2()));
|
||||||
|
|
||||||
let layer_node_id = NodeId::new();
|
let layer_node_id = NodeId::new();
|
||||||
let layer_id = LayerNodeIdentifier::new_unchecked(layer_node_id);
|
let layer_id = LayerNodeIdentifier::new_unchecked(layer_node_id);
|
||||||
@@ -970,7 +965,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
} else {
|
} else {
|
||||||
&self.document_ptz
|
&self.document_ptz
|
||||||
};
|
};
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), current_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), current_ptz);
|
||||||
|
|
||||||
let ruler_scale = if !self.graph_view_overlay_open {
|
let ruler_scale = if !self.graph_view_overlay_open {
|
||||||
self.navigation_handler.snapped_zoom(current_ptz.zoom())
|
self.navigation_handler.snapped_zoom(current_ptz.zoom())
|
||||||
@@ -1006,12 +1001,12 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
DocumentMessage::RenderScrollbars => {
|
DocumentMessage::RenderScrollbars => {
|
||||||
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom());
|
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom()) / viewport.scale();
|
||||||
|
|
||||||
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
||||||
|
|
||||||
let viewport_size = ipp.viewport_bounds.size();
|
let viewport_size = viewport.size().into_dvec2();
|
||||||
let viewport_mid = ipp.viewport_bounds.center();
|
let viewport_mid = viewport.center_in_viewport_space().into_dvec2();
|
||||||
let [bounds1, bounds2] = if !self.graph_view_overlay_open {
|
let [bounds1, bounds2] = if !self.graph_view_overlay_open {
|
||||||
self.metadata().document_bounds_viewport_space().unwrap_or([viewport_mid; 2])
|
self.metadata().document_bounds_viewport_space().unwrap_or([viewport_mid; 2])
|
||||||
} else {
|
} else {
|
||||||
@@ -1333,7 +1328,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..undo_count {
|
for _ in 0..undo_count {
|
||||||
self.undo(ipp, responses);
|
self.undo(viewport, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.network_interface.finish_transaction();
|
self.network_interface.finish_transaction();
|
||||||
@@ -1483,7 +1478,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
}
|
}
|
||||||
DocumentMessage::PTZUpdate => {
|
DocumentMessage::PTZUpdate => {
|
||||||
if !self.graph_view_overlay_open {
|
if !self.graph_view_overlay_open {
|
||||||
let transform = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let transform = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
self.network_interface.set_document_to_viewport_transform(transform);
|
self.network_interface.set_document_to_viewport_transform(transform);
|
||||||
// Ensure selection box is kept in sync with the pointer when the PTZ changes
|
// Ensure selection box is kept in sync with the pointer when the PTZ changes
|
||||||
responses.add(SelectToolMessage::PointerMove {
|
responses.add(SelectToolMessage::PointerMove {
|
||||||
@@ -1502,7 +1497,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
|
|
||||||
let transform = self
|
let transform = self
|
||||||
.navigation_handler
|
.navigation_handler
|
||||||
.calculate_offset_transform(ipp.viewport_bounds.center(), &network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz);
|
.calculate_offset_transform(viewport.center_in_viewport_space().into(), &network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz);
|
||||||
self.network_interface.set_transform(transform, &self.breadcrumb_network_path);
|
self.network_interface.set_transform(transform, &self.breadcrumb_network_path);
|
||||||
|
|
||||||
responses.add(DocumentMessage::RenderRulers);
|
responses.add(DocumentMessage::RenderRulers);
|
||||||
@@ -1656,29 +1651,30 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
|
|
||||||
impl DocumentMessageHandler {
|
impl DocumentMessageHandler {
|
||||||
/// Runs an intersection test with all layers and a viewport space quad
|
/// Runs an intersection test with all layers and a viewport space quad
|
||||||
pub fn intersect_quad<'a>(&'a self, viewport_quad: graphene_std::renderer::Quad, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
pub fn intersect_quad<'a>(&'a self, viewport_quad: graphene_std::renderer::Quad, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
let document_quad = document_to_viewport.inverse() * viewport_quad;
|
let document_quad = document_to_viewport.inverse() * viewport_quad;
|
||||||
|
|
||||||
ClickXRayIter::new(&self.network_interface, XRayTarget::Quad(document_quad))
|
ClickXRayIter::new(&self.network_interface, XRayTarget::Quad(document_quad))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an intersection test with all layers and a viewport space quad; ignoring artboards
|
/// Runs an intersection test with all layers and a viewport space quad; ignoring artboards
|
||||||
pub fn intersect_quad_no_artboards<'a>(&'a self, viewport_quad: graphene_std::renderer::Quad, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
pub fn intersect_quad_no_artboards<'a>(&'a self, viewport_quad: graphene_std::renderer::Quad, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
self.intersect_quad(viewport_quad, ipp).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
self.intersect_quad(viewport_quad, viewport).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an intersection test with all layers and a viewport space subpath
|
/// Runs an intersection test with all layers and a viewport space subpath
|
||||||
pub fn intersect_polygon<'a>(&'a self, mut viewport_polygon: Subpath<PointId>, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
pub fn intersect_polygon<'a>(&'a self, mut viewport_polygon: Subpath<PointId>, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
viewport_polygon.apply_transform(document_to_viewport.inverse());
|
viewport_polygon.apply_transform(document_to_viewport.inverse());
|
||||||
|
|
||||||
ClickXRayIter::new(&self.network_interface, XRayTarget::Polygon(viewport_polygon))
|
ClickXRayIter::new(&self.network_interface, XRayTarget::Polygon(viewport_polygon))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an intersection test with all layers and a viewport space subpath; ignoring artboards
|
/// Runs an intersection test with all layers and a viewport space subpath; ignoring artboards
|
||||||
pub fn intersect_polygon_no_artboards<'a>(&'a self, viewport_polygon: Subpath<PointId>, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
pub fn intersect_polygon_no_artboards<'a>(&'a self, viewport_polygon: Subpath<PointId>, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
self.intersect_polygon(viewport_polygon, ipp).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
self.intersect_polygon(viewport_polygon, viewport)
|
||||||
|
.filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_layer_fully_inside(&self, layer: &LayerNodeIdentifier, quad: graphene_std::renderer::Quad) -> bool {
|
pub fn is_layer_fully_inside(&self, layer: &LayerNodeIdentifier, quad: graphene_std::renderer::Quad) -> bool {
|
||||||
@@ -1704,8 +1700,8 @@ impl DocumentMessageHandler {
|
|||||||
layer_left >= quad_left && layer_right <= quad_right && layer_top <= quad_top && layer_bottom >= quad_bottom
|
layer_left >= quad_left && layer_right <= quad_right && layer_top <= quad_top && layer_bottom >= quad_bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_layer_fully_inside_polygon(&self, layer: &LayerNodeIdentifier, ipp: &InputPreprocessorMessageHandler, mut viewport_polygon: Subpath<PointId>) -> bool {
|
pub fn is_layer_fully_inside_polygon(&self, layer: &LayerNodeIdentifier, viewport: &ViewportMessageHandler, mut viewport_polygon: Subpath<PointId>) -> bool {
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
viewport_polygon.apply_transform(document_to_viewport.inverse());
|
viewport_polygon.apply_transform(document_to_viewport.inverse());
|
||||||
|
|
||||||
let layer_click_targets = self.network_interface.document_metadata().click_targets(*layer);
|
let layer_click_targets = self.network_interface.document_metadata().click_targets(*layer);
|
||||||
@@ -1728,8 +1724,8 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find all of the layers that were clicked on from a viewport space location
|
/// Find all of the layers that were clicked on from a viewport space location
|
||||||
pub fn click_xray(&self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'_> {
|
pub fn click_xray(&self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'_> {
|
||||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
let point = document_to_viewport.inverse().transform_point2(ipp.mouse.position);
|
let point = document_to_viewport.inverse().transform_point2(ipp.mouse.position);
|
||||||
ClickXRayIter::new(&self.network_interface, XRayTarget::Point(point))
|
ClickXRayIter::new(&self.network_interface, XRayTarget::Point(point))
|
||||||
}
|
}
|
||||||
@@ -1750,8 +1746,8 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find layers under the location in viewport space that was clicked, listed by their depth in the layer tree hierarchy.
|
/// Find layers under the location in viewport space that was clicked, listed by their depth in the layer tree hierarchy.
|
||||||
pub fn click_list<'a>(&'a self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
pub fn click_list<'a>(&'a self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
self.click_xray(ipp)
|
self.click_xray(ipp, viewport)
|
||||||
.filter(move |&layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
.filter(move |&layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||||
.skip_while(|&layer| layer == LayerNodeIdentifier::ROOT_PARENT)
|
.skip_while(|&layer| layer == LayerNodeIdentifier::ROOT_PARENT)
|
||||||
.scan(true, |last_had_children, layer| {
|
.scan(true, |last_had_children, layer| {
|
||||||
@@ -1765,8 +1761,8 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find layers (including artboards) under the location in viewport space that was clicked, listed by their depth in the layer tree hierarchy.
|
/// Find layers (including artboards) under the location in viewport space that was clicked, listed by their depth in the layer tree hierarchy.
|
||||||
pub fn click_list_with_artboards<'a>(&'a self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
pub fn click_list_with_artboards<'a>(&'a self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
self.click_xray(ipp)
|
self.click_xray(ipp, viewport)
|
||||||
.skip_while(|&layer| layer == LayerNodeIdentifier::ROOT_PARENT)
|
.skip_while(|&layer| layer == LayerNodeIdentifier::ROOT_PARENT)
|
||||||
.scan(true, |last_had_children, layer| {
|
.scan(true, |last_had_children, layer| {
|
||||||
if *last_had_children {
|
if *last_had_children {
|
||||||
@@ -1778,14 +1774,14 @@ impl DocumentMessageHandler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn click_list_no_parents<'a>(&'a self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
pub fn click_list_no_parents<'a>(&'a self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
self.click_xray(ipp)
|
self.click_xray(ipp, viewport)
|
||||||
.filter(move |&layer| !self.network_interface.is_artboard(&layer.to_node(), &[]) && !layer.has_children(self.network_interface.document_metadata()))
|
.filter(move |&layer| !self.network_interface.is_artboard(&layer.to_node(), &[]) && !layer.has_children(self.network_interface.document_metadata()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the deepest layer that has been clicked on from a location in viewport space.
|
/// Find the deepest layer that has been clicked on from a location in viewport space.
|
||||||
pub fn click(&self, ipp: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
|
pub fn click(&self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||||
self.click_list(ipp).last()
|
self.click_list(ipp, viewport).last()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn click_based_on_position(&self, mouse_snapped_positon: DVec2) -> Option<LayerNodeIdentifier> {
|
pub fn click_based_on_position(&self, mouse_snapped_positon: DVec2) -> Option<LayerNodeIdentifier> {
|
||||||
@@ -1943,8 +1939,8 @@ impl DocumentMessageHandler {
|
|||||||
structure_section.as_slice().into()
|
structure_section.as_slice().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
pub fn undo_with_history(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
let Some(previous_network) = self.undo(ipp, responses) else { return };
|
let Some(previous_network) = self.undo(viewport, responses) else { return };
|
||||||
|
|
||||||
self.document_redo_history.push_back(previous_network);
|
self.document_redo_history.push_back(previous_network);
|
||||||
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
@@ -1952,7 +1948,7 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
pub fn undo(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
||||||
// If there is no history return and don't broadcast SelectionChanged
|
// If there is no history return and don't broadcast SelectionChanged
|
||||||
let mut network_interface = self.document_undo_history.pop_back()?;
|
let mut network_interface = self.document_undo_history.pop_back()?;
|
||||||
|
|
||||||
@@ -1961,7 +1957,7 @@ impl DocumentMessageHandler {
|
|||||||
std::mem::swap(&mut network_interface.resolved_types, &mut self.network_interface.resolved_types);
|
std::mem::swap(&mut network_interface.resolved_types, &mut self.network_interface.resolved_types);
|
||||||
|
|
||||||
//Update the metadata transform based on document PTZ
|
//Update the metadata transform based on document PTZ
|
||||||
let transform = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let transform = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
network_interface.set_document_to_viewport_transform(transform);
|
network_interface.set_document_to_viewport_transform(transform);
|
||||||
|
|
||||||
// Ensure document structure is loaded so that updating the selected nodes has the correct metadata
|
// Ensure document structure is loaded so that updating the selected nodes has the correct metadata
|
||||||
@@ -1980,9 +1976,9 @@ impl DocumentMessageHandler {
|
|||||||
|
|
||||||
Some(previous_network)
|
Some(previous_network)
|
||||||
}
|
}
|
||||||
pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
pub fn redo_with_history(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
// Push the UpdateOpenDocumentsList message to the queue in order to update the save status of the open documents
|
// Push the UpdateOpenDocumentsList message to the queue in order to update the save status of the open documents
|
||||||
let Some(previous_network) = self.redo(ipp, responses) else { return };
|
let Some(previous_network) = self.redo(viewport, responses) else { return };
|
||||||
|
|
||||||
self.document_undo_history.push_back(previous_network);
|
self.document_undo_history.push_back(previous_network);
|
||||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
@@ -1990,7 +1986,7 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redo(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
pub fn redo(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
||||||
// If there is no history return and don't broadcast SelectionChanged
|
// If there is no history return and don't broadcast SelectionChanged
|
||||||
let mut network_interface = self.document_redo_history.pop_back()?;
|
let mut network_interface = self.document_redo_history.pop_back()?;
|
||||||
|
|
||||||
@@ -1999,7 +1995,7 @@ impl DocumentMessageHandler {
|
|||||||
std::mem::swap(&mut network_interface.resolved_types, &mut self.network_interface.resolved_types);
|
std::mem::swap(&mut network_interface.resolved_types, &mut self.network_interface.resolved_types);
|
||||||
|
|
||||||
//Update the metadata transform based on document PTZ
|
//Update the metadata transform based on document PTZ
|
||||||
let transform = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
let transform = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
network_interface.set_document_to_viewport_transform(transform);
|
network_interface.set_document_to_viewport_transform(transform);
|
||||||
|
|
||||||
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
|
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
|
||||||
@@ -2045,11 +2041,11 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the artboard that bounds the point in viewport space and be the container of any newly added layers.
|
/// Finds the artboard that bounds the point in viewport space and be the container of any newly added layers.
|
||||||
pub fn new_layer_bounding_artboard(&self, ipp: &InputPreprocessorMessageHandler) -> LayerNodeIdentifier {
|
pub fn new_layer_bounding_artboard(&self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> LayerNodeIdentifier {
|
||||||
let container_based_on_selection = self.new_layer_parent(true);
|
let container_based_on_selection = self.new_layer_parent(true);
|
||||||
|
|
||||||
let container_based_on_clicked_artboard = self
|
let container_based_on_clicked_artboard = self
|
||||||
.click_xray(ipp)
|
.click_xray(ipp, viewport)
|
||||||
.find(|layer| self.network_interface.is_artboard(&layer.to_node(), &[]))
|
.find(|layer| self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||||
.unwrap_or(LayerNodeIdentifier::ROOT_PARENT);
|
.unwrap_or(LayerNodeIdentifier::ROOT_PARENT);
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ pub struct NavigationMessageContext<'a> {
|
|||||||
pub document_ptz: &'a mut PTZ,
|
pub document_ptz: &'a mut PTZ,
|
||||||
pub graph_view_overlay_open: bool,
|
pub graph_view_overlay_open: bool,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default, ExtractField)]
|
#[derive(Debug, Clone, PartialEq, Default, ExtractField)]
|
||||||
@@ -41,6 +42,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
document_ptz,
|
document_ptz,
|
||||||
graph_view_overlay_open,
|
graph_view_overlay_open,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
} = context;
|
} = context;
|
||||||
|
|
||||||
fn get_ptz<'a>(document_ptz: &'a PTZ, network_interface: &'a NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a PTZ> {
|
fn get_ptz<'a>(document_ptz: &'a PTZ, network_interface: &'a NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a PTZ> {
|
||||||
@@ -135,7 +137,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
log::error!("Could not get PTZ in CanvasPan");
|
log::error!("Could not get PTZ in CanvasPan");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
let document_to_viewport = self.calculate_offset_transform(viewport.center_in_viewport_space().into_dvec2(), ptz);
|
||||||
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta);
|
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta);
|
||||||
|
|
||||||
ptz.pan += transformed_delta;
|
ptz.pan += transformed_delta;
|
||||||
@@ -169,8 +171,8 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
let document_to_viewport = self.calculate_offset_transform(viewport.center_in_viewport_space().into_dvec2(), ptz);
|
||||||
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta * viewport.size().into_dvec2());
|
||||||
|
|
||||||
ptz.pan += transformed_delta;
|
ptz.pan += transformed_delta;
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
@@ -214,7 +216,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
|
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom()).unwrap_or(&ptz.zoom());
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom()).unwrap_or(&ptz.zoom());
|
||||||
if center_on_mouse {
|
if center_on_mouse {
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
|
responses.add(self.center_zoom(viewport.size().into(), new_scale / ptz.zoom(), ipp.mouse.position));
|
||||||
}
|
}
|
||||||
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
||||||
}
|
}
|
||||||
@@ -225,7 +227,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
|
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom()).unwrap_or(&ptz.zoom());
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom()).unwrap_or(&ptz.zoom());
|
||||||
if center_on_mouse {
|
if center_on_mouse {
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
|
responses.add(self.center_zoom(viewport.size().into(), new_scale / ptz.zoom(), ipp.mouse.position));
|
||||||
}
|
}
|
||||||
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
||||||
}
|
}
|
||||||
@@ -245,9 +247,9 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
zoom_factor *= Self::clamp_zoom(ptz.zoom() * zoom_factor, document_bounds, old_zoom, ipp);
|
zoom_factor *= Self::clamp_zoom(ptz.zoom() * zoom_factor, document_bounds, old_zoom, viewport);
|
||||||
|
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
responses.add(self.center_zoom(viewport.size().into(), zoom_factor, ipp.mouse.position));
|
||||||
responses.add(NavigationMessage::CanvasZoomSet {
|
responses.add(NavigationMessage::CanvasZoomSet {
|
||||||
zoom_factor: ptz.zoom() * zoom_factor,
|
zoom_factor: ptz.zoom() * zoom_factor,
|
||||||
});
|
});
|
||||||
@@ -264,7 +266,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
let zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||||
let zoom = zoom * Self::clamp_zoom(zoom, document_bounds, old_zoom, ipp);
|
let zoom = zoom * Self::clamp_zoom(zoom, document_bounds, old_zoom, viewport);
|
||||||
ptz.set_zoom(zoom);
|
ptz.set_zoom(zoom);
|
||||||
if graph_view_overlay_open {
|
if graph_view_overlay_open {
|
||||||
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
@@ -343,7 +345,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
let (pos1, pos2) = (pos1.min(pos2), pos1.max(pos2));
|
let (pos1, pos2) = (pos1.min(pos2), pos1.max(pos2));
|
||||||
let diagonal = pos2 - pos1;
|
let diagonal = pos2 - pos1;
|
||||||
|
|
||||||
if diagonal.length() < f64::EPSILON * 1000. || ipp.viewport_bounds.size() == DVec2::ZERO {
|
if diagonal.length() < f64::EPSILON * 1000. || viewport.size().into_dvec2() == DVec2::ZERO {
|
||||||
warn!("Cannot center since the viewport size is 0");
|
warn!("Cannot center since the viewport size is 0");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -352,10 +354,10 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
let document_to_viewport = self.calculate_offset_transform(viewport.center_in_viewport_space().into_dvec2(), ptz);
|
||||||
|
|
||||||
let v1 = document_to_viewport.inverse().transform_point2(DVec2::ZERO);
|
let v1 = document_to_viewport.inverse().transform_point2(DVec2::ZERO);
|
||||||
let v2 = document_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size());
|
let v2 = document_to_viewport.inverse().transform_point2(viewport.size().into_dvec2());
|
||||||
|
|
||||||
let center = ((v2 + v1) - (pos2 + pos1)) / 2.;
|
let center = ((v2 + v1) - (pos2 + pos1)) / 2.;
|
||||||
let size = (v2 - v1) / diagonal;
|
let size = (v2 - v1) / diagonal;
|
||||||
@@ -397,7 +399,8 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
log::error!("Could not get node graph PTZ in FitViewportToSelection");
|
log::error!("Could not get node graph PTZ in FitViewportToSelection");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
|
||||||
|
let document_to_viewport = self.calculate_offset_transform(viewport.center_in_viewport_space().into_dvec2(), ptz);
|
||||||
responses.add(NavigationMessage::FitViewportToBounds {
|
responses.add(NavigationMessage::FitViewportToBounds {
|
||||||
bounds: [document_to_viewport.inverse().transform_point2(bounds[0]), document_to_viewport.inverse().transform_point2(bounds[1])],
|
bounds: [document_to_viewport.inverse().transform_point2(bounds[0]), document_to_viewport.inverse().transform_point2(bounds[1])],
|
||||||
prevent_zoom_past_100: false,
|
prevent_zoom_past_100: false,
|
||||||
@@ -419,7 +422,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
let tilt_raw_not_snapped = {
|
let tilt_raw_not_snapped = {
|
||||||
// Compute the angle in document space to counter for the canvas being flipped
|
// Compute the angle in document space to counter for the canvas being flipped
|
||||||
let viewport_to_document = network_interface.document_metadata().document_to_viewport.inverse();
|
let viewport_to_document = network_interface.document_metadata().document_to_viewport.inverse();
|
||||||
let half_viewport = ipp.viewport_bounds.size() / 2.;
|
let half_viewport = viewport.center_in_viewport_space().into_dvec2();
|
||||||
let start_offset = viewport_to_document.transform_vector2(self.mouse_position - half_viewport);
|
let start_offset = viewport_to_document.transform_vector2(self.mouse_position - half_viewport);
|
||||||
let end_offset = viewport_to_document.transform_vector2(ipp.mouse.position - half_viewport);
|
let end_offset = viewport_to_document.transform_vector2(ipp.mouse.position - half_viewport);
|
||||||
let angle = start_offset.angle_to(end_offset);
|
let angle = start_offset.angle_to(end_offset);
|
||||||
@@ -459,7 +462,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
network_interface.graph_bounds_viewport_space(breadcrumb_network_path)
|
network_interface.graph_bounds_viewport_space(breadcrumb_network_path)
|
||||||
};
|
};
|
||||||
|
|
||||||
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp)
|
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, viewport)
|
||||||
};
|
};
|
||||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||||
log::error!("Could not get mutable PTZ in Zoom");
|
log::error!("Could not get mutable PTZ in Zoom");
|
||||||
@@ -563,9 +566,9 @@ impl NavigationMessageHandler {
|
|||||||
NavigationMessage::CanvasPan { delta }.into()
|
NavigationMessage::CanvasPan { delta }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clamp_zoom(zoom: f64, document_bounds: Option<[DVec2; 2]>, old_zoom: f64, ipp: &InputPreprocessorMessageHandler) -> f64 {
|
pub fn clamp_zoom(zoom: f64, document_bounds: Option<[DVec2; 2]>, old_zoom: f64, viewport: &ViewportMessageHandler) -> f64 {
|
||||||
let document_size = (document_bounds.map(|[min, max]| max - min).unwrap_or_default() / old_zoom) * zoom;
|
let document_size = (document_bounds.map(|[min, max]| max - min).unwrap_or_default() / old_zoom) * zoom;
|
||||||
let scale_factor = (document_size / ipp.viewport_bounds.size()).max_element();
|
let scale_factor = (document_size / viewport.size().into_dvec2()).max_element();
|
||||||
|
|
||||||
if scale_factor <= f64::EPSILON * 100. || !scale_factor.is_finite() || scale_factor >= VIEWPORT_ZOOM_MIN_FRACTION_COVER {
|
if scale_factor <= f64::EPSILON * 100. || !scale_factor.is_finite() || scale_factor >= VIEWPORT_ZOOM_MIN_FRACTION_COVER {
|
||||||
return 1.;
|
return 1.;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use crate::messages::tool::common_functionality::graph_modification_utils::{self
|
|||||||
use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed;
|
use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
use crate::messages::viewport::{Position, Rect};
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
@@ -43,6 +44,7 @@ pub struct NodeGraphMessageContext<'a> {
|
|||||||
pub navigation_handler: &'a NavigationMessageHandler,
|
pub navigation_handler: &'a NavigationMessageHandler,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
pub layers_panel_open: bool,
|
pub layers_panel_open: bool,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, ExtractField)]
|
#[derive(Debug, Clone, ExtractField)]
|
||||||
@@ -113,6 +115,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
navigation_handler,
|
navigation_handler,
|
||||||
preferences,
|
preferences,
|
||||||
layers_panel_open,
|
layers_panel_open,
|
||||||
|
viewport,
|
||||||
} = context;
|
} = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
@@ -783,12 +786,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
|
|
||||||
// TODO: Create function
|
// TODO: Create function
|
||||||
let node_graph_shift = if matches!(context_menu_data, ContextMenuData::CreateNode { compatible_type: None }) {
|
let node_graph_shift = if matches!(context_menu_data, ContextMenuData::CreateNode { compatible_type: None }) {
|
||||||
let appear_right_of_mouse = if click.x > ipp.viewport_bounds.size().x - 180. { -180. } else { 0. };
|
let appear_right_of_mouse = if click.x > viewport.size().x() - 180. { -180. } else { 0. };
|
||||||
let appear_above_mouse = if click.y > ipp.viewport_bounds.size().y - 200. { -200. } else { 0. };
|
let appear_above_mouse = if click.y > viewport.size().y() - 200. { -200. } else { 0. };
|
||||||
DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x
|
DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x
|
||||||
} else {
|
} else {
|
||||||
let appear_right_of_mouse = if click.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. };
|
let appear_right_of_mouse = if click.x > viewport.size().x() - 173. { -173. } else { 0. };
|
||||||
let appear_above_mouse = if click.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. };
|
let appear_above_mouse = if click.y > viewport.size().y() - 34. { -34. } else { 0. };
|
||||||
DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x
|
DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -983,7 +986,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
|
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()];
|
let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()];
|
||||||
self.auto_panning.setup_by_mouse_position(ipp, &messages, responses);
|
self.auto_panning.setup_by_mouse_position(ipp, viewport, &messages, responses);
|
||||||
|
|
||||||
let viewport_location = ipp.mouse.position;
|
let viewport_location = ipp.mouse.position;
|
||||||
let point = network_metadata
|
let point = network_metadata
|
||||||
@@ -1218,8 +1221,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
_ => Some(format!("type:{}", output_type.nested_type())),
|
_ => Some(format!("type:{}", output_type.nested_type())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let appear_right_of_mouse = if ipp.mouse.position.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. };
|
let appear_right_of_mouse = if ipp.mouse.position.x > viewport.size().y() - 173. { -173. } else { 0. };
|
||||||
let appear_above_mouse = if ipp.mouse.position.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. };
|
let appear_above_mouse = if ipp.mouse.position.y > viewport.size().y() - 34. { -34. } else { 0. };
|
||||||
let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x;
|
let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x;
|
||||||
|
|
||||||
self.context_menu = Some(ContextMenuInformation {
|
self.context_menu = Some(ContextMenuInformation {
|
||||||
@@ -1403,7 +1406,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
}
|
}
|
||||||
NodeGraphMessage::PointerOutsideViewport { shift } => {
|
NodeGraphMessage::PointerOutsideViewport { shift } => {
|
||||||
if self.drag_start.is_some() || self.box_selection_start.is_some() || (self.wire_in_progress_from_connector.is_some() && self.context_menu.is_none()) {
|
if self.drag_start.is_some() || self.box_selection_start.is_some() || (self.wire_in_progress_from_connector.is_some() && self.context_menu.is_none()) {
|
||||||
let _ = self.auto_panning.shift_viewport(ipp, responses);
|
let _ = self.auto_panning.shift_viewport(ipp, viewport, responses);
|
||||||
} else {
|
} else {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()];
|
let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()];
|
||||||
@@ -1611,7 +1614,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let viewport_bbox = ipp.document_bounds();
|
let viewport_bounds = viewport.bounds();
|
||||||
|
let viewport_bbox: [DVec2; 2] = viewport_bounds.into();
|
||||||
let document_bbox: [DVec2; 2] = viewport_bbox.map(|p| network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(p));
|
let document_bbox: [DVec2; 2] = viewport_bbox.map(|p| network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(p));
|
||||||
|
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
@@ -1657,7 +1661,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
}
|
}
|
||||||
NodeGraphMessage::SetGridAlignedEdges => {
|
NodeGraphMessage::SetGridAlignedEdges => {
|
||||||
if graph_view_overlay_open {
|
if graph_view_overlay_open {
|
||||||
network_interface.set_grid_aligned_edges(DVec2::new(ipp.viewport_bounds.bottom_right.x - ipp.viewport_bounds.top_left.x, 0.), breadcrumb_network_path);
|
let viewport_bounds = viewport.bounds();
|
||||||
|
network_interface.set_grid_aligned_edges(DVec2::new(viewport_bounds.x() - viewport_bounds.width(), 0.), breadcrumb_network_path);
|
||||||
// Send the new edges to the frontend
|
// Send the new edges to the frontend
|
||||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
|
|||||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
let document_to_viewport = document
|
||||||
|
.navigation_handler
|
||||||
|
.calculate_offset_transform(overlay_context.viewport.center_in_viewport_space().into(), &document.document_ptz);
|
||||||
|
|
||||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.viewport.size().into()]);
|
||||||
|
|
||||||
for primary in 0..2 {
|
for primary in 0..2 {
|
||||||
let secondary = 1 - primary;
|
let secondary = 1 - primary;
|
||||||
@@ -52,9 +54,11 @@ fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_conte
|
|||||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
let document_to_viewport = document
|
||||||
|
.navigation_handler
|
||||||
|
.calculate_offset_transform(overlay_context.viewport.center_in_viewport_space().into(), &document.document_ptz);
|
||||||
|
|
||||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.viewport.size().into()]);
|
||||||
|
|
||||||
let min = bounds.0.iter().map(|corner| corner.y).min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or_default();
|
let min = bounds.0.iter().map(|corner| corner.y).min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or_default();
|
||||||
let max = bounds.0.iter().map(|corner| corner.y).max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or_default();
|
let max = bounds.0.iter().map(|corner| corner.y).max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or_default();
|
||||||
@@ -85,9 +89,11 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
|
|||||||
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
||||||
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||||
let origin = document.snapping_state.grid.origin;
|
let origin = document.snapping_state.grid.origin;
|
||||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
let document_to_viewport = document
|
||||||
|
.navigation_handler
|
||||||
|
.calculate_offset_transform(overlay_context.viewport.center_in_viewport_space().into(), &document.document_ptz);
|
||||||
|
|
||||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.viewport.size().into()]);
|
||||||
let tan_a = angle_a.to_radians().tan();
|
let tan_a = angle_a.to_radians().tan();
|
||||||
let tan_b = angle_b.to_radians().tan();
|
let tan_b = angle_b.to_radians().tan();
|
||||||
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
||||||
@@ -128,9 +134,11 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context
|
|||||||
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
||||||
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||||
let origin = document.snapping_state.grid.origin;
|
let origin = document.snapping_state.grid.origin;
|
||||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
let document_to_viewport = document
|
||||||
|
.navigation_handler
|
||||||
|
.calculate_offset_transform(overlay_context.viewport.center_in_viewport_space().into(), &document.document_ptz);
|
||||||
|
|
||||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.viewport.size().into()]);
|
||||||
let tan_a = angle_a.to_radians().tan();
|
let tan_a = angle_a.to_radians().tan();
|
||||||
let tan_b = angle_b.to_radians().tan();
|
let tan_b = angle_b.to_radians().tan();
|
||||||
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ use crate::messages::prelude::*;
|
|||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct OverlaysMessageContext<'a> {
|
pub struct OverlaysMessageContext<'a> {
|
||||||
pub visibility_settings: OverlaysVisibilitySettings,
|
pub visibility_settings: OverlaysVisibilitySettings,
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
pub device_pixel_ratio: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ExtractField)]
|
#[derive(Debug, Clone, Default, ExtractField)]
|
||||||
@@ -20,19 +19,14 @@ pub struct OverlaysMessageHandler {
|
|||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMessageHandler {
|
impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMessageHandler {
|
||||||
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, context: OverlaysMessageContext) {
|
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, context: OverlaysMessageContext) {
|
||||||
let OverlaysMessageContext {
|
let OverlaysMessageContext { visibility_settings, viewport, .. } = context;
|
||||||
visibility_settings,
|
|
||||||
ipp,
|
|
||||||
device_pixel_ratio,
|
|
||||||
..
|
|
||||||
} = context;
|
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
OverlaysMessage::Draw => {
|
OverlaysMessage::Draw => {
|
||||||
use super::utility_functions::overlay_canvas_element;
|
use super::utility_functions::overlay_canvas_element;
|
||||||
use super::utility_types::OverlayContext;
|
use super::utility_types::OverlayContext;
|
||||||
use glam::{DAffine2, DVec2};
|
use crate::messages::viewport::{Position, ToPhysical};
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
let canvas = match &self.canvas {
|
let canvas = match &self.canvas {
|
||||||
@@ -48,28 +42,26 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
|
|||||||
canvas_context.dyn_into().expect("Context should be a canvas 2d context")
|
canvas_context.dyn_into().expect("Context should be a canvas 2d context")
|
||||||
});
|
});
|
||||||
|
|
||||||
let size = ipp.viewport_bounds.size().as_uvec2();
|
let size_logical = viewport.size();
|
||||||
|
let size_physical = size_logical.to_physical();
|
||||||
|
let width = size_logical.x().max(size_physical.x());
|
||||||
|
let height = size_logical.y().max(size_physical.y());
|
||||||
|
|
||||||
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(device_pixel_ratio)).to_cols_array();
|
canvas_context.clear_rect(0., 0., width, height);
|
||||||
let _ = canvas_context.set_transform(a, b, c, d, e, f);
|
|
||||||
canvas_context.clear_rect(0., 0., ipp.viewport_bounds.size().x, ipp.viewport_bounds.size().y);
|
|
||||||
let _ = canvas_context.reset_transform();
|
|
||||||
|
|
||||||
if visibility_settings.all() {
|
if visibility_settings.all() {
|
||||||
responses.add(DocumentMessage::GridOverlays {
|
responses.add(DocumentMessage::GridOverlays {
|
||||||
context: OverlayContext {
|
context: OverlayContext {
|
||||||
render_context: canvas_context.clone(),
|
render_context: canvas_context.clone(),
|
||||||
size: size.as_dvec2(),
|
|
||||||
device_pixel_ratio,
|
|
||||||
visibility_settings: visibility_settings.clone(),
|
visibility_settings: visibility_settings.clone(),
|
||||||
|
viewport: *viewport,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
for provider in &self.overlay_providers {
|
for provider in &self.overlay_providers {
|
||||||
responses.add(provider(OverlayContext {
|
responses.add(provider(OverlayContext {
|
||||||
render_context: canvas_context.clone(),
|
render_context: canvas_context.clone(),
|
||||||
size: size.as_dvec2(),
|
|
||||||
device_pixel_ratio,
|
|
||||||
visibility_settings: visibility_settings.clone(),
|
visibility_settings: visibility_settings.clone(),
|
||||||
|
viewport: *viewport,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,9 +70,7 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
|
|||||||
OverlaysMessage::Draw => {
|
OverlaysMessage::Draw => {
|
||||||
use super::utility_types::OverlayContext;
|
use super::utility_types::OverlayContext;
|
||||||
|
|
||||||
let size = ipp.viewport_bounds.size();
|
let overlay_context = OverlayContext::new(*viewport, visibility_settings);
|
||||||
|
|
||||||
let overlay_context = OverlayContext::new(size, device_pixel_ratio, visibility_settings);
|
|
||||||
|
|
||||||
if visibility_settings.all() {
|
if visibility_settings.all() {
|
||||||
responses.add(DocumentMessage::GridOverlays { context: overlay_context.clone() });
|
responses.add(DocumentMessage::GridOverlays { context: overlay_context.clone() });
|
||||||
@@ -93,7 +83,7 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
|
|||||||
}
|
}
|
||||||
#[cfg(all(not(target_family = "wasm"), test))]
|
#[cfg(all(not(target_family = "wasm"), test))]
|
||||||
OverlaysMessage::Draw => {
|
OverlaysMessage::Draw => {
|
||||||
let _ = (responses, visibility_settings, ipp, device_pixel_ratio);
|
let _ = (responses, visibility_settings, viewport);
|
||||||
}
|
}
|
||||||
OverlaysMessage::AddProvider { provider: message } => {
|
OverlaysMessage::AddProvider { provider: message } => {
|
||||||
self.overlay_providers.insert(message);
|
self.overlay_providers.insert(message);
|
||||||
|
|||||||
@@ -207,7 +207,6 @@ pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &
|
|||||||
let Some(vector) = document.network_interface.compute_modified_vector(layer) else {
|
let Some(vector) = document.network_interface.compute_modified_vector(layer) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
//let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
|
||||||
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
|
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
|
||||||
let selected = shape_editor.selected_shape_state.get(&layer);
|
let selected = shape_editor.selected_shape_state.get(&layer);
|
||||||
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));
|
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ use super::utility_functions::overlay_canvas_context;
|
|||||||
use crate::consts::{
|
use crate::consts::{
|
||||||
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL,
|
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL,
|
||||||
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
|
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
|
||||||
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, SEGMENT_SELECTED_THICKNESS,
|
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SEGMENT_SELECTED_THICKNESS, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
|
||||||
};
|
};
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::prelude::Message;
|
use crate::messages::prelude::Message;
|
||||||
|
use crate::messages::viewport::ViewportMessageHandler;
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
@@ -141,10 +142,7 @@ pub struct OverlayContext {
|
|||||||
#[serde(skip, default = "overlay_canvas_context")]
|
#[serde(skip, default = "overlay_canvas_context")]
|
||||||
#[specta(skip)]
|
#[specta(skip)]
|
||||||
pub render_context: web_sys::CanvasRenderingContext2d,
|
pub render_context: web_sys::CanvasRenderingContext2d,
|
||||||
pub size: DVec2,
|
pub viewport: ViewportMessageHandler,
|
||||||
// The device pixel ratio is a property provided by the browser window and is the CSS pixel size divided by the physical monitor's pixel size.
|
|
||||||
// It allows better pixel density of visualizations on high-DPI displays where the OS display scaling is not 100%, or where the browser is zoomed.
|
|
||||||
pub device_pixel_ratio: f64,
|
|
||||||
pub visibility_settings: OverlaysVisibilitySettings,
|
pub visibility_settings: OverlaysVisibilitySettings,
|
||||||
}
|
}
|
||||||
// Message hashing isn't used but is required by the message system macros
|
// Message hashing isn't used but is required by the message system macros
|
||||||
@@ -499,11 +497,25 @@ impl OverlayContext {
|
|||||||
self.square(position, None, Some(color_fill), Some(COLOR_OVERLAY_BLUE));
|
self.square(position, None, Some(color_fill), Some(COLOR_OVERLAY_BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize_handle(&mut self, position: DVec2, rotation: f64) {
|
||||||
|
let quad = DAffine2::from_angle_translation(rotation, position) * Quad::from_box([DVec2::splat(-RESIZE_HANDLE_SIZE / 2.), DVec2::splat(RESIZE_HANDLE_SIZE / 2.)]);
|
||||||
|
self.quad(quad, None, Some(COLOR_OVERLAY_WHITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skew_handles(&mut self, edge_start: DVec2, edge_end: DVec2) {
|
||||||
|
let edge_dir = (edge_end - edge_start).normalize();
|
||||||
|
let mid = edge_end.midpoint(edge_start);
|
||||||
|
|
||||||
|
for edge in [edge_dir, -edge_dir] {
|
||||||
|
self.draw_triangle(mid + edge * (3. + SKEW_TRIANGLE_OFFSET), edge, SKEW_TRIANGLE_SIZE, None, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Transforms the canvas context to adjust for DPI scaling
|
/// Transforms the canvas context to adjust for DPI scaling
|
||||||
///
|
///
|
||||||
/// Overwrites all existing tranforms. This operation can be reversed with [`Self::reset_transform`].
|
/// Overwrites all existing tranforms. This operation can be reversed with [`Self::reset_transform`].
|
||||||
fn start_dpi_aware_transform(&self) {
|
fn start_dpi_aware_transform(&self) {
|
||||||
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.device_pixel_ratio)).to_cols_array();
|
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.viewport.scale())).to_cols_array();
|
||||||
self.render_context
|
self.render_context
|
||||||
.set_transform(a, b, c, d, e, f)
|
.set_transform(a, b, c, d, e, f)
|
||||||
.expect("transform should be able to be set to be able to account for DPI");
|
.expect("transform should be able to be set to be able to account for DPI");
|
||||||
@@ -967,7 +979,7 @@ impl OverlayContext {
|
|||||||
Pivot::End => -padding,
|
Pivot::End => -padding,
|
||||||
};
|
};
|
||||||
|
|
||||||
let [a, b, c, d, e, f] = (DAffine2::from_scale(DVec2::splat(self.device_pixel_ratio)) * transform * DAffine2::from_translation(DVec2::new(x, y))).to_cols_array();
|
let [a, b, c, d, e, f] = (DAffine2::from_scale(DVec2::splat(self.viewport.scale())) * transform * DAffine2::from_translation(DVec2::new(x, y))).to_cols_array();
|
||||||
self.render_context.set_transform(a, b, c, d, e, f).expect("Failed to rotate the render context to the specified angle");
|
self.render_context.set_transform(a, b, c, d, e, f).expect("Failed to rotate the render context to the specified angle");
|
||||||
|
|
||||||
if let Some(background) = background_color {
|
if let Some(background) = background_color {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use crate::consts::{
|
use crate::consts::{
|
||||||
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL,
|
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL,
|
||||||
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
|
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
|
||||||
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
|
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
|
||||||
};
|
};
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::prelude::Message;
|
use crate::messages::prelude::Message;
|
||||||
|
use crate::messages::prelude::ViewportMessageHandler;
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
@@ -157,24 +158,18 @@ pub struct OverlayContext {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[specta(skip)]
|
#[specta(skip)]
|
||||||
internal: Arc<Mutex<OverlayContextInternal>>,
|
internal: Arc<Mutex<OverlayContextInternal>>,
|
||||||
pub size: DVec2,
|
pub viewport: ViewportMessageHandler,
|
||||||
// The device pixel ratio is a property provided by the browser window and is the CSS pixel size divided by the physical monitor's pixel size.
|
|
||||||
// It allows better pixel density of visualizations on high-DPI displays where the OS display scaling is not 100%, or where the browser is zoomed.
|
|
||||||
pub device_pixel_ratio: f64,
|
|
||||||
pub visibility_settings: OverlaysVisibilitySettings,
|
pub visibility_settings: OverlaysVisibilitySettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for OverlayContext {
|
impl Clone for OverlayContext {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
let internal = self.internal.lock().expect("Failed to lock internal overlay context");
|
let internal = self.internal.lock().expect("Failed to lock internal overlay context");
|
||||||
let size = internal.size;
|
|
||||||
let device_pixel_ratio = internal.device_pixel_ratio;
|
|
||||||
let visibility_settings = internal.visibility_settings;
|
let visibility_settings = internal.visibility_settings;
|
||||||
drop(internal); // Explicitly release the lock before cloning the Arc<Mutex<_>>
|
drop(internal); // Explicitly release the lock before cloning the Arc<Mutex<_>>
|
||||||
Self {
|
Self {
|
||||||
internal: self.internal.clone(),
|
internal: self.internal.clone(),
|
||||||
size,
|
viewport: self.viewport,
|
||||||
device_pixel_ratio,
|
|
||||||
visibility_settings,
|
visibility_settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +178,7 @@ impl Clone for OverlayContext {
|
|||||||
// Manual implementations since Scene doesn't implement PartialEq or Debug
|
// Manual implementations since Scene doesn't implement PartialEq or Debug
|
||||||
impl PartialEq for OverlayContext {
|
impl PartialEq for OverlayContext {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.size == other.size && self.device_pixel_ratio == other.device_pixel_ratio && self.visibility_settings == other.visibility_settings
|
self.viewport == other.viewport && self.visibility_settings == other.visibility_settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,8 +186,7 @@ impl std::fmt::Debug for OverlayContext {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("OverlayContext")
|
f.debug_struct("OverlayContext")
|
||||||
.field("scene", &"Scene { ... }")
|
.field("scene", &"Scene { ... }")
|
||||||
.field("size", &self.size)
|
.field("viewport", &self.viewport)
|
||||||
.field("device_pixel_ratio", &self.device_pixel_ratio)
|
|
||||||
.field("visibility_settings", &self.visibility_settings)
|
.field("visibility_settings", &self.visibility_settings)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
@@ -203,8 +197,7 @@ impl Default for OverlayContext {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
internal: Mutex::new(OverlayContextInternal::default()).into(),
|
internal: Mutex::new(OverlayContextInternal::default()).into(),
|
||||||
size: DVec2::ZERO,
|
viewport: ViewportMessageHandler::default(),
|
||||||
device_pixel_ratio: 1.0,
|
|
||||||
visibility_settings: OverlaysVisibilitySettings::default(),
|
visibility_settings: OverlaysVisibilitySettings::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,11 +210,10 @@ impl core::hash::Hash for OverlayContext {
|
|||||||
|
|
||||||
impl OverlayContext {
|
impl OverlayContext {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(super) fn new(size: DVec2, device_pixel_ratio: f64, visibility_settings: OverlaysVisibilitySettings) -> Self {
|
pub(super) fn new(viewport: ViewportMessageHandler, visibility_settings: OverlaysVisibilitySettings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
internal: Arc::new(Mutex::new(OverlayContextInternal::new(size, device_pixel_ratio, visibility_settings))),
|
internal: Arc::new(Mutex::new(OverlayContextInternal::new(viewport, visibility_settings))),
|
||||||
size,
|
viewport,
|
||||||
device_pixel_ratio,
|
|
||||||
visibility_settings,
|
visibility_settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,6 +272,14 @@ impl OverlayContext {
|
|||||||
self.internal().manipulator_anchor(position, selected, color);
|
self.internal().manipulator_anchor(position, selected, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize_handle(&mut self, position: DVec2, rotation: f64) {
|
||||||
|
self.internal().resize_handle(position, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skew_handles(&mut self, edge_start: DVec2, edge_end: DVec2) {
|
||||||
|
self.internal().skew_handles(edge_start, edge_end);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
|
pub fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
|
||||||
self.internal().square(position, size, color_fill, color_stroke);
|
self.internal().square(position, size, color_fill, color_stroke);
|
||||||
}
|
}
|
||||||
@@ -292,6 +292,7 @@ impl OverlayContext {
|
|||||||
self.internal().circle(position, radius, color_fill, color_stroke);
|
self.internal().circle(position, radius, color_fill, color_stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn dashed_ellipse(
|
pub fn dashed_ellipse(
|
||||||
&mut self,
|
&mut self,
|
||||||
center: DVec2,
|
center: DVec2,
|
||||||
@@ -421,23 +422,21 @@ pub enum DrawHandles {
|
|||||||
|
|
||||||
pub(super) struct OverlayContextInternal {
|
pub(super) struct OverlayContextInternal {
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
size: DVec2,
|
viewport: ViewportMessageHandler,
|
||||||
device_pixel_ratio: f64,
|
|
||||||
visibility_settings: OverlaysVisibilitySettings,
|
visibility_settings: OverlaysVisibilitySettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for OverlayContextInternal {
|
impl Default for OverlayContextInternal {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(DVec2::new(100., 100.), 1., OverlaysVisibilitySettings::default())
|
Self::new(ViewportMessageHandler::default(), OverlaysVisibilitySettings::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OverlayContextInternal {
|
impl OverlayContextInternal {
|
||||||
pub(super) fn new(size: DVec2, device_pixel_ratio: f64, visibility_settings: OverlaysVisibilitySettings) -> Self {
|
pub(super) fn new(viewport: ViewportMessageHandler, visibility_settings: OverlaysVisibilitySettings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
scene: Scene::new(),
|
scene: Scene::new(),
|
||||||
size,
|
viewport,
|
||||||
device_pixel_ratio,
|
|
||||||
visibility_settings,
|
visibility_settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -473,7 +472,7 @@ impl OverlayContextInternal {
|
|||||||
|
|
||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &path);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &path);
|
||||||
|
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color_stroke), None, &path);
|
self.scene.stroke(&kurbo::Stroke::new(1.), transform, Self::parse_color(color_stroke), None, &path);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dashed_quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
|
fn dashed_quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
|
||||||
@@ -506,7 +505,7 @@ impl OverlayContextInternal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let stroke_color = stroke_color.unwrap_or(COLOR_OVERLAY_BLUE);
|
let stroke_color = stroke_color.unwrap_or(COLOR_OVERLAY_BLUE);
|
||||||
let mut stroke = kurbo::Stroke::new(1.0);
|
let mut stroke = kurbo::Stroke::new(1.);
|
||||||
|
|
||||||
if let Some(dash_width) = dash_width {
|
if let Some(dash_width) = dash_width {
|
||||||
let dash_gap = dash_gap_width.unwrap_or(1.);
|
let dash_gap = dash_gap_width.unwrap_or(1.);
|
||||||
@@ -551,7 +550,7 @@ impl OverlayContextInternal {
|
|||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(fill), None, &circle);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(fill), None, &circle);
|
||||||
|
|
||||||
self.scene
|
self.scene
|
||||||
.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color.unwrap_or(COLOR_OVERLAY_BLUE)), None, &circle);
|
.stroke(&kurbo::Stroke::new(1.), transform, Self::parse_color(color.unwrap_or(COLOR_OVERLAY_BLUE)), None, &circle);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hover_manipulator_handle(&mut self, position: DVec2, selected: bool) {
|
fn hover_manipulator_handle(&mut self, position: DVec2, selected: bool) {
|
||||||
@@ -563,13 +562,13 @@ impl OverlayContextInternal {
|
|||||||
|
|
||||||
let fill = COLOR_OVERLAY_BLUE_50;
|
let fill = COLOR_OVERLAY_BLUE_50;
|
||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(fill), None, &circle);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(fill), None, &circle);
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(COLOR_OVERLAY_BLUE_50), None, &circle);
|
self.scene.stroke(&kurbo::Stroke::new(1.), transform, Self::parse_color(COLOR_OVERLAY_BLUE_50), None, &circle);
|
||||||
|
|
||||||
let inner_circle = kurbo::Circle::new((position.x, position.y), MANIPULATOR_GROUP_MARKER_SIZE / 2.);
|
let inner_circle = kurbo::Circle::new((position.x, position.y), MANIPULATOR_GROUP_MARKER_SIZE / 2.);
|
||||||
|
|
||||||
let color_fill = if selected { COLOR_OVERLAY_BLUE } else { COLOR_OVERLAY_WHITE };
|
let color_fill = if selected { COLOR_OVERLAY_BLUE } else { COLOR_OVERLAY_WHITE };
|
||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &circle);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &circle);
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &inner_circle);
|
self.scene.stroke(&kurbo::Stroke::new(1.), transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &inner_circle);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn manipulator_anchor(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
|
fn manipulator_anchor(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
|
||||||
@@ -584,8 +583,22 @@ impl OverlayContextInternal {
|
|||||||
self.square(position, None, Some(color_fill), Some(COLOR_OVERLAY_BLUE));
|
self.square(position, None, Some(color_fill), Some(COLOR_OVERLAY_BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resize_handle(&mut self, position: DVec2, rotation: f64) {
|
||||||
|
let quad = DAffine2::from_angle_translation(rotation, position) * Quad::from_box([DVec2::splat(-RESIZE_HANDLE_SIZE / 2.), DVec2::splat(RESIZE_HANDLE_SIZE / 2.)]);
|
||||||
|
self.quad(quad, None, Some(COLOR_OVERLAY_WHITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skew_handles(&mut self, edge_start: DVec2, edge_end: DVec2) {
|
||||||
|
let edge_dir = (edge_end - edge_start).normalize();
|
||||||
|
let mid = edge_end.midpoint(edge_start);
|
||||||
|
|
||||||
|
for edge in [edge_dir, -edge_dir] {
|
||||||
|
self.draw_triangle(mid + edge * 3. + SKEW_TRIANGLE_OFFSET, edge, SKEW_TRIANGLE_SIZE, None, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_transform(&self) -> kurbo::Affine {
|
fn get_transform(&self) -> kurbo::Affine {
|
||||||
kurbo::Affine::scale(self.device_pixel_ratio)
|
kurbo::Affine::scale(self.viewport.scale())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
|
fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
|
||||||
@@ -601,7 +614,7 @@ impl OverlayContextInternal {
|
|||||||
|
|
||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &rect);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &rect);
|
||||||
|
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color_stroke), None, &rect);
|
self.scene.stroke(&kurbo::Stroke::new(1.), transform, Self::parse_color(color_stroke), None, &rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixel(&mut self, position: DVec2, color: Option<&str>) {
|
fn pixel(&mut self, position: DVec2, color: Option<&str>) {
|
||||||
@@ -627,9 +640,10 @@ impl OverlayContextInternal {
|
|||||||
|
|
||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &circle);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &circle);
|
||||||
|
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color_stroke), None, &circle);
|
self.scene.stroke(&kurbo::Stroke::new(1.), transform, Self::parse_color(color_stroke), None, &circle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn dashed_ellipse(
|
fn dashed_ellipse(
|
||||||
&mut self,
|
&mut self,
|
||||||
_center: DVec2,
|
_center: DVec2,
|
||||||
@@ -678,7 +692,7 @@ impl OverlayContextInternal {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), self.get_transform(), Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
|
self.scene.stroke(&kurbo::Stroke::new(1.), self.get_transform(), Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
|
fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
|
||||||
@@ -700,7 +714,7 @@ impl OverlayContextInternal {
|
|||||||
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
|
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
|
||||||
fill_color.insert(0, '#');
|
fill_color.insert(0, '#');
|
||||||
let fill_color = Some(fill_color.as_str());
|
let fill_color = Some(fill_color.as_str());
|
||||||
self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None, None);
|
self.line(start + DVec2::X * radius * sign, start + DVec2::X * radius * scale.abs(), None, None);
|
||||||
self.circle(start, radius, fill_color, None);
|
self.circle(start, radius, fill_color, None);
|
||||||
self.circle(start, radius * scale.abs(), fill_color, None);
|
self.circle(start, radius * scale.abs(), fill_color, None);
|
||||||
self.text(
|
self.text(
|
||||||
@@ -714,14 +728,14 @@ impl OverlayContextInternal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compass_rose(&mut self, compass_center: DVec2, angle: f64, show_compass_with_hover_ring: Option<bool>) {
|
fn compass_rose(&mut self, compass_center: DVec2, angle: f64, show_compass_with_hover_ring: Option<bool>) {
|
||||||
const HOVER_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_HOVER_RING_DIAMETER / 2.;
|
let hover_ring_outer_radius: f64 = COMPASS_ROSE_HOVER_RING_DIAMETER / 2.;
|
||||||
const MAIN_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_MAIN_RING_DIAMETER / 2.;
|
let main_ring_outer_radius: f64 = COMPASS_ROSE_MAIN_RING_DIAMETER / 2.;
|
||||||
const MAIN_RING_INNER_RADIUS: f64 = COMPASS_ROSE_RING_INNER_DIAMETER / 2.;
|
let main_ring_inner_radius: f64 = COMPASS_ROSE_RING_INNER_DIAMETER / 2.;
|
||||||
const ARROW_RADIUS: f64 = COMPASS_ROSE_ARROW_SIZE / 2.;
|
let arrow_radius: f64 = COMPASS_ROSE_ARROW_SIZE / 2.;
|
||||||
const HOVER_RING_STROKE_WIDTH: f64 = HOVER_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;
|
let hover_ring_stroke_width: f64 = hover_ring_outer_radius - main_ring_inner_radius;
|
||||||
const HOVER_RING_CENTERLINE_RADIUS: f64 = (HOVER_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.;
|
let hover_ring_centerline_radius: f64 = (hover_ring_outer_radius + main_ring_inner_radius) / 2.;
|
||||||
const MAIN_RING_STROKE_WIDTH: f64 = MAIN_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;
|
let main_ring_stroke_width: f64 = main_ring_outer_radius - main_ring_inner_radius;
|
||||||
const MAIN_RING_CENTERLINE_RADIUS: f64 = (MAIN_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.;
|
let main_ring_centerline_radius: f64 = (main_ring_outer_radius + main_ring_inner_radius) / 2.;
|
||||||
|
|
||||||
let Some(show_hover_ring) = show_compass_with_hover_ring else { return };
|
let Some(show_hover_ring) = show_compass_with_hover_ring else { return };
|
||||||
|
|
||||||
@@ -733,9 +747,9 @@ impl OverlayContextInternal {
|
|||||||
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).to_rgba_hex_srgb();
|
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).to_rgba_hex_srgb();
|
||||||
fill_color.insert(0, '#');
|
fill_color.insert(0, '#');
|
||||||
|
|
||||||
let circle = kurbo::Circle::new((center.x, center.y), HOVER_RING_CENTERLINE_RADIUS);
|
let circle = kurbo::Circle::new((center.x, center.y), hover_ring_centerline_radius);
|
||||||
self.scene
|
self.scene
|
||||||
.stroke(&kurbo::Stroke::new(HOVER_RING_STROKE_WIDTH), transform, Self::parse_color(&fill_color), None, &circle);
|
.stroke(&kurbo::Stroke::new(hover_ring_stroke_width), transform, Self::parse_color(&fill_color), None, &circle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrows
|
// Arrows
|
||||||
@@ -743,11 +757,11 @@ impl OverlayContextInternal {
|
|||||||
let direction = DVec2::from_angle(i as f64 * FRAC_PI_2 + angle);
|
let direction = DVec2::from_angle(i as f64 * FRAC_PI_2 + angle);
|
||||||
let color = if i % 2 == 0 { COLOR_OVERLAY_RED } else { COLOR_OVERLAY_GREEN };
|
let color = if i % 2 == 0 { COLOR_OVERLAY_RED } else { COLOR_OVERLAY_GREEN };
|
||||||
|
|
||||||
let tip = center + direction * HOVER_RING_OUTER_RADIUS;
|
let tip = center + direction * hover_ring_outer_radius;
|
||||||
let base = center + direction * (MAIN_RING_INNER_RADIUS + MAIN_RING_OUTER_RADIUS) / 2.;
|
let base = center + direction * (main_ring_inner_radius + main_ring_outer_radius) / 2.;
|
||||||
|
|
||||||
let r = (ARROW_RADIUS.powi(2) + MAIN_RING_INNER_RADIUS.powi(2)).sqrt();
|
let r = (arrow_radius.powi(2) + main_ring_inner_radius.powi(2)).sqrt();
|
||||||
let (cos, sin) = (MAIN_RING_INNER_RADIUS / r, ARROW_RADIUS / r);
|
let (cos, sin) = (main_ring_inner_radius / r, arrow_radius / r);
|
||||||
let side1 = center + r * DVec2::new(cos * direction.x - sin * direction.y, sin * direction.x + direction.y * cos);
|
let side1 = center + r * DVec2::new(cos * direction.x - sin * direction.y, sin * direction.x + direction.y * cos);
|
||||||
let side2 = center + r * DVec2::new(cos * direction.x + sin * direction.y, -sin * direction.x + direction.y * cos);
|
let side2 = center + r * DVec2::new(cos * direction.x + sin * direction.y, -sin * direction.x + direction.y * cos);
|
||||||
|
|
||||||
@@ -764,9 +778,9 @@ impl OverlayContextInternal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Main ring
|
// Main ring
|
||||||
let circle = kurbo::Circle::new((center.x, center.y), MAIN_RING_CENTERLINE_RADIUS);
|
let circle = kurbo::Circle::new((center.x, center.y), main_ring_centerline_radius);
|
||||||
self.scene
|
self.scene
|
||||||
.stroke(&kurbo::Stroke::new(MAIN_RING_STROKE_WIDTH), transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &circle);
|
.stroke(&kurbo::Stroke::new(main_ring_stroke_width), transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &circle);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pivot(&mut self, position: DVec2, angle: f64) {
|
fn pivot(&mut self, position: DVec2, angle: f64) {
|
||||||
@@ -780,22 +794,22 @@ impl OverlayContextInternal {
|
|||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &circle);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &circle);
|
||||||
|
|
||||||
// Crosshair
|
// Crosshair
|
||||||
const CROSSHAIR_RADIUS: f64 = (PIVOT_CROSSHAIR_LENGTH - PIVOT_CROSSHAIR_THICKNESS) / 2.;
|
let crosshair_radius: f64 = (PIVOT_CROSSHAIR_LENGTH - PIVOT_CROSSHAIR_THICKNESS) / 2.;
|
||||||
|
|
||||||
let mut stroke = kurbo::Stroke::new(PIVOT_CROSSHAIR_THICKNESS);
|
let mut stroke = kurbo::Stroke::new(PIVOT_CROSSHAIR_THICKNESS);
|
||||||
stroke = stroke.with_caps(kurbo::Cap::Round);
|
stroke = stroke.with_caps(kurbo::Cap::Round);
|
||||||
|
|
||||||
// Horizontal line
|
// Horizontal line
|
||||||
let mut path = BezPath::new();
|
let mut path = BezPath::new();
|
||||||
path.move_to(kurbo::Point::new(x + CROSSHAIR_RADIUS * uv.x, y + CROSSHAIR_RADIUS * uv.y));
|
path.move_to(kurbo::Point::new(x + crosshair_radius * uv.x, y + crosshair_radius * uv.y));
|
||||||
path.line_to(kurbo::Point::new(x - CROSSHAIR_RADIUS * uv.x, y - CROSSHAIR_RADIUS * uv.y));
|
path.line_to(kurbo::Point::new(x - crosshair_radius * uv.x, y - crosshair_radius * uv.y));
|
||||||
|
|
||||||
self.scene.stroke(&stroke, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &path);
|
self.scene.stroke(&stroke, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &path);
|
||||||
|
|
||||||
// Vertical line
|
// Vertical line
|
||||||
let mut path = BezPath::new();
|
let mut path = BezPath::new();
|
||||||
path.move_to(kurbo::Point::new(x - CROSSHAIR_RADIUS * uv.y, y + CROSSHAIR_RADIUS * uv.x));
|
path.move_to(kurbo::Point::new(x - crosshair_radius * uv.y, y + crosshair_radius * uv.x));
|
||||||
path.line_to(kurbo::Point::new(x + CROSSHAIR_RADIUS * uv.y, y - CROSSHAIR_RADIUS * uv.x));
|
path.line_to(kurbo::Point::new(x + crosshair_radius * uv.y, y - crosshair_radius * uv.x));
|
||||||
|
|
||||||
self.scene.stroke(&stroke, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &path);
|
self.scene.stroke(&stroke, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &path);
|
||||||
}
|
}
|
||||||
@@ -809,15 +823,15 @@ impl OverlayContextInternal {
|
|||||||
// Draw the background circle with a white fill and colored outline
|
// Draw the background circle with a white fill and colored outline
|
||||||
let circle = kurbo::Circle::new((x, y), DOWEL_PIN_RADIUS);
|
let circle = kurbo::Circle::new((x, y), DOWEL_PIN_RADIUS);
|
||||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(COLOR_OVERLAY_WHITE), None, &circle);
|
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(COLOR_OVERLAY_WHITE), None, &circle);
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color), None, &circle);
|
self.scene.stroke(&kurbo::Stroke::new(1.), transform, Self::parse_color(color), None, &circle);
|
||||||
|
|
||||||
// Draw the two filled sectors using paths
|
// Draw the two filled sectors using paths
|
||||||
let mut path = BezPath::new();
|
let mut path = BezPath::new();
|
||||||
|
|
||||||
// Top-left sector
|
// Top-left sector
|
||||||
path.move_to(kurbo::Point::new(x, y));
|
path.move_to(kurbo::Point::new(x, y));
|
||||||
let end_x = x + DOWEL_PIN_RADIUS * (FRAC_PI_2 + angle).cos();
|
let end_x = x + DOWEL_PIN_RADIUS * (FRAC_PI_2 + angle.cos());
|
||||||
let end_y = y + DOWEL_PIN_RADIUS * (FRAC_PI_2 + angle).sin();
|
let end_y = y + DOWEL_PIN_RADIUS * (FRAC_PI_2 + angle.sin());
|
||||||
path.line_to(kurbo::Point::new(end_x, end_y));
|
path.line_to(kurbo::Point::new(end_x, end_y));
|
||||||
// Draw arc manually
|
// Draw arc manually
|
||||||
let arc = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), FRAC_PI_2 + angle, FRAC_PI_2, 0.0);
|
let arc = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), FRAC_PI_2 + angle, FRAC_PI_2, 0.0);
|
||||||
@@ -828,8 +842,8 @@ impl OverlayContextInternal {
|
|||||||
|
|
||||||
// Bottom-right sector
|
// Bottom-right sector
|
||||||
path.move_to(kurbo::Point::new(x, y));
|
path.move_to(kurbo::Point::new(x, y));
|
||||||
let end_x = x + DOWEL_PIN_RADIUS * (PI + FRAC_PI_2 + angle).cos();
|
let end_x = x + DOWEL_PIN_RADIUS * (PI + FRAC_PI_2 + angle.cos());
|
||||||
let end_y = y + DOWEL_PIN_RADIUS * (PI + FRAC_PI_2 + angle).sin();
|
let end_y = y + DOWEL_PIN_RADIUS * (PI + FRAC_PI_2 + angle.sin());
|
||||||
path.line_to(kurbo::Point::new(end_x, end_y));
|
path.line_to(kurbo::Point::new(end_x, end_y));
|
||||||
// Draw arc manually
|
// Draw arc manually
|
||||||
let arc = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), PI + FRAC_PI_2 + angle, FRAC_PI_2, 0.0);
|
let arc = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), PI + FRAC_PI_2 + angle, FRAC_PI_2, 0.0);
|
||||||
@@ -861,7 +875,7 @@ impl OverlayContextInternal {
|
|||||||
self.bezier_to_path(bezier, transform, move_to, &mut path);
|
self.bezier_to_path(bezier, transform, move_to, &mut path);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
|
self.scene.stroke(&kurbo::Stroke::new(1.), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by the Pen tool in order to show how the bezier curve would look like.
|
/// Used by the Pen tool in order to show how the bezier curve would look like.
|
||||||
@@ -870,7 +884,7 @@ impl OverlayContextInternal {
|
|||||||
let mut path = BezPath::new();
|
let mut path = BezPath::new();
|
||||||
self.bezier_to_path(bezier, transform, true, &mut path);
|
self.bezier_to_path(bezier, transform, true, &mut path);
|
||||||
|
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
|
self.scene.stroke(&kurbo::Stroke::new(1.), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by the path tool segment mode in order to show the selected segments.
|
/// Used by the path tool segment mode in order to show the selected segments.
|
||||||
@@ -963,7 +977,7 @@ impl OverlayContextInternal {
|
|||||||
let path = self.push_path(subpaths.iter(), transform);
|
let path = self.push_path(subpaths.iter(), transform);
|
||||||
let color = color.unwrap_or(COLOR_OVERLAY_BLUE);
|
let color = color.unwrap_or(COLOR_OVERLAY_BLUE);
|
||||||
|
|
||||||
self.scene.stroke(&kurbo::Stroke::new(1.0), self.get_transform(), Self::parse_color(color), None, &path);
|
self.scene.stroke(&kurbo::Stroke::new(1.), self.get_transform(), Self::parse_color(color), None, &path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1010,7 +1024,7 @@ impl OverlayContextInternal {
|
|||||||
x_extend: peniko::Extend::Repeat,
|
x_extend: peniko::Extend::Repeat,
|
||||||
y_extend: peniko::Extend::Repeat,
|
y_extend: peniko::Extend::Repeat,
|
||||||
quality: peniko::ImageQuality::default(),
|
quality: peniko::ImageQuality::default(),
|
||||||
alpha: 1.0,
|
alpha: 1.,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -112,9 +112,6 @@ pub enum PortfolioMessage {
|
|||||||
SetActivePanel {
|
SetActivePanel {
|
||||||
panel: PanelType,
|
panel: PanelType,
|
||||||
},
|
},
|
||||||
SetDevicePixelRatio {
|
|
||||||
ratio: f64,
|
|
||||||
},
|
|
||||||
SelectDocument {
|
SelectDocument {
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ pub struct PortfolioMessageContext<'a> {
|
|||||||
pub message_logging_verbosity: MessageLoggingVerbosity,
|
pub message_logging_verbosity: MessageLoggingVerbosity,
|
||||||
pub reset_node_definitions_on_open: bool,
|
pub reset_node_definitions_on_open: bool,
|
||||||
pub timing_information: TimingInformation,
|
pub timing_information: TimingInformation,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Derivative, ExtractField)]
|
#[derive(Debug, Derivative, ExtractField)]
|
||||||
@@ -56,7 +57,6 @@ pub struct PortfolioMessageHandler {
|
|||||||
pub persistent_data: PersistentData,
|
pub persistent_data: PersistentData,
|
||||||
pub executor: NodeGraphExecutor,
|
pub executor: NodeGraphExecutor,
|
||||||
pub selection_mode: SelectionMode,
|
pub selection_mode: SelectionMode,
|
||||||
device_pixel_ratio: Option<f64>,
|
|
||||||
pub reset_node_definitions_on_open: bool,
|
pub reset_node_definitions_on_open: bool,
|
||||||
pub data_panel_open: bool,
|
pub data_panel_open: bool,
|
||||||
#[derivative(Default(value = "true"))]
|
#[derivative(Default(value = "true"))]
|
||||||
@@ -76,6 +76,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
message_logging_verbosity,
|
message_logging_verbosity,
|
||||||
reset_node_definitions_on_open,
|
reset_node_definitions_on_open,
|
||||||
timing_information,
|
timing_information,
|
||||||
|
viewport,
|
||||||
} = context;
|
} = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
@@ -124,7 +125,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
executor: &mut self.executor,
|
executor: &mut self.executor,
|
||||||
current_tool,
|
current_tool,
|
||||||
preferences,
|
preferences,
|
||||||
device_pixel_ratio: self.device_pixel_ratio.unwrap_or(1.),
|
viewport,
|
||||||
data_panel_open: self.data_panel_open,
|
data_panel_open: self.data_panel_open,
|
||||||
layers_panel_open: self.layers_panel_open,
|
layers_panel_open: self.layers_panel_open,
|
||||||
properties_panel_open: self.properties_panel_open,
|
properties_panel_open: self.properties_panel_open,
|
||||||
@@ -165,7 +166,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
executor: &mut self.executor,
|
executor: &mut self.executor,
|
||||||
current_tool,
|
current_tool,
|
||||||
preferences,
|
preferences,
|
||||||
device_pixel_ratio: self.device_pixel_ratio.unwrap_or(1.),
|
viewport,
|
||||||
data_panel_open: self.data_panel_open,
|
data_panel_open: self.data_panel_open,
|
||||||
layers_panel_open: self.layers_panel_open,
|
layers_panel_open: self.layers_panel_open,
|
||||||
properties_panel_open: self.properties_panel_open,
|
properties_panel_open: self.properties_panel_open,
|
||||||
@@ -361,10 +362,15 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
self.executor.update_font_cache(self.persistent_data.font_cache.clone());
|
self.executor.update_font_cache(self.persistent_data.font_cache.clone());
|
||||||
for document_id in self.document_ids.iter() {
|
for document_id in self.document_ids.iter() {
|
||||||
let node_to_inspect = self.node_to_inspect();
|
let node_to_inspect = self.node_to_inspect();
|
||||||
|
|
||||||
|
let scale = viewport.scale();
|
||||||
|
let resolution = viewport.size().into_dvec2().round().as_uvec2();
|
||||||
|
|
||||||
if let Ok(message) = self.executor.submit_node_graph_evaluation(
|
if let Ok(message) = self.executor.submit_node_graph_evaluation(
|
||||||
self.documents.get_mut(document_id).expect("Tried to render non-existent document"),
|
self.documents.get_mut(document_id).expect("Tried to render non-existent document"),
|
||||||
*document_id,
|
*document_id,
|
||||||
ipp.viewport_bounds.size().as_uvec2(),
|
resolution,
|
||||||
|
scale,
|
||||||
timing_information,
|
timing_information,
|
||||||
node_to_inspect,
|
node_to_inspect,
|
||||||
true,
|
true,
|
||||||
@@ -699,7 +705,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
}
|
}
|
||||||
PortfolioMessage::CenterPastedLayers { layers } => {
|
PortfolioMessage::CenterPastedLayers { layers } => {
|
||||||
if let Some(document) = self.active_document_mut() {
|
if let Some(document) = self.active_document_mut() {
|
||||||
let viewport_bounds_quad_pixels = Quad::from_box([DVec2::ZERO, ipp.viewport_bounds.size()]);
|
let viewport_bounds_quad_pixels = Quad::from_box([DVec2::ZERO, viewport.size().into_dvec2()]); // In viewport pixel coordinates
|
||||||
let viewport_center_pixels = viewport_bounds_quad_pixels.center(); // In viewport pixel coordinates
|
let viewport_center_pixels = viewport_bounds_quad_pixels.center(); // In viewport pixel coordinates
|
||||||
|
|
||||||
let doc_to_viewport_transform = document.metadata().document_to_viewport;
|
let doc_to_viewport_transform = document.metadata().document_to_viewport;
|
||||||
@@ -878,10 +884,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
self.active_panel = panel;
|
self.active_panel = panel;
|
||||||
responses.add(DocumentMessage::SetActivePanel { active_panel: self.active_panel });
|
responses.add(DocumentMessage::SetActivePanel { active_panel: self.active_panel });
|
||||||
}
|
}
|
||||||
PortfolioMessage::SetDevicePixelRatio { ratio } => {
|
|
||||||
self.device_pixel_ratio = Some(ratio);
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
|
||||||
}
|
|
||||||
PortfolioMessage::SelectDocument { document_id } => {
|
PortfolioMessage::SelectDocument { document_id } => {
|
||||||
// Auto-save the document we are leaving
|
// Auto-save the document we are leaving
|
||||||
let mut node_graph_open = false;
|
let mut node_graph_open = false;
|
||||||
@@ -961,15 +963,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
}
|
}
|
||||||
PortfolioMessage::SubmitGraphRender { document_id, ignore_hash } => {
|
PortfolioMessage::SubmitGraphRender { document_id, ignore_hash } => {
|
||||||
let node_to_inspect = self.node_to_inspect();
|
let node_to_inspect = self.node_to_inspect();
|
||||||
|
|
||||||
let Some(document) = self.documents.get_mut(&document_id) else {
|
let Some(document) = self.documents.get_mut(&document_id) else {
|
||||||
log::error!("Tried to render non-existent document");
|
log::error!("Tried to render non-existent document");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let viewport_resolution = ipp.viewport_bounds.size().as_uvec2();
|
|
||||||
|
let scale = viewport.scale();
|
||||||
|
let resolution = viewport.size().into_dvec2().round().as_uvec2();
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.executor
|
.executor
|
||||||
.submit_node_graph_evaluation(document, document_id, viewport_resolution, timing_information, node_to_inspect, ignore_hash);
|
.submit_node_graph_evaluation(document, document_id, resolution, scale, timing_information, node_to_inspect, ignore_hash);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Err(description) => {
|
Err(description) => {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ pub use crate::messages::portfolio::{PortfolioMessage, PortfolioMessageContext,
|
|||||||
pub use crate::messages::preferences::{PreferencesMessage, PreferencesMessageDiscriminant, PreferencesMessageHandler};
|
pub use crate::messages::preferences::{PreferencesMessage, PreferencesMessageDiscriminant, PreferencesMessageHandler};
|
||||||
pub use crate::messages::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler};
|
pub use crate::messages::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler};
|
||||||
pub use crate::messages::tool::{ToolMessage, ToolMessageContext, ToolMessageDiscriminant, ToolMessageHandler};
|
pub use crate::messages::tool::{ToolMessage, ToolMessageContext, ToolMessageDiscriminant, ToolMessageHandler};
|
||||||
|
pub use crate::messages::viewport::{ViewportMessage, ViewportMessageDiscriminant, ViewportMessageHandler};
|
||||||
|
|
||||||
// Message, MessageDiscriminant
|
// Message, MessageDiscriminant
|
||||||
pub use crate::messages::message::{Message, MessageDiscriminant};
|
pub use crate::messages::message::{Message, MessageDiscriminant};
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ impl AutoPanning {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_by_mouse_position(&mut self, input: &InputPreprocessorMessageHandler, messages: &[Message], responses: &mut VecDeque<Message>) {
|
pub fn setup_by_mouse_position(&mut self, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler, messages: &[Message], responses: &mut VecDeque<Message>) {
|
||||||
let mouse_position = input.mouse.position;
|
let mouse_position = input.mouse.position;
|
||||||
let viewport_size = input.viewport_bounds.size();
|
let viewport_size = viewport.size().into_dvec2();
|
||||||
let is_pointer_outside_edge = mouse_position.x < 0. || mouse_position.x > viewport_size.x || mouse_position.y < 0. || mouse_position.y > viewport_size.y;
|
let is_pointer_outside_edge = mouse_position.x < 0. || mouse_position.x > viewport_size.x || mouse_position.y < 0. || mouse_position.y > viewport_size.y;
|
||||||
|
|
||||||
match is_pointer_outside_edge {
|
match is_pointer_outside_edge {
|
||||||
@@ -50,12 +50,12 @@ impl AutoPanning {
|
|||||||
/// If the mouse was beyond any edge, it returns the amount shifted. Otherwise it returns None.
|
/// If the mouse was beyond any edge, it returns the amount shifted. Otherwise it returns None.
|
||||||
/// The shift is proportional to the distance between edge and mouse, and to the duration of the frame.
|
/// The shift is proportional to the distance between edge and mouse, and to the duration of the frame.
|
||||||
/// It is also guaranteed to be integral.
|
/// It is also guaranteed to be integral.
|
||||||
pub fn shift_viewport(&self, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> Option<DVec2> {
|
pub fn shift_viewport(&self, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> Option<DVec2> {
|
||||||
if !self.subscribed_to_animation_frame {
|
if !self.subscribed_to_animation_frame {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let viewport_size = input.viewport_bounds.size();
|
let viewport_size = viewport.size().into_dvec2();
|
||||||
let mouse_position = input.mouse.position.clamp(
|
let mouse_position = input.mouse.position.clamp(
|
||||||
DVec2::ZERO - DVec2::splat(DRAG_BEYOND_VIEWPORT_MAX_OVEREXTENSION_PIXELS),
|
DVec2::ZERO - DVec2::splat(DRAG_BEYOND_VIEWPORT_MAX_OVEREXTENSION_PIXELS),
|
||||||
viewport_size + DVec2::splat(DRAG_BEYOND_VIEWPORT_MAX_OVEREXTENSION_PIXELS),
|
viewport_size + DVec2::splat(DRAG_BEYOND_VIEWPORT_MAX_OVEREXTENSION_PIXELS),
|
||||||
|
|||||||
+2
-2
@@ -142,7 +142,7 @@ impl PointRadiusHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overlays(&self, selected_star_layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, overlay_context: &mut OverlayContext) {
|
pub fn overlays(&self, selected_star_layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) {
|
||||||
match &self.handle_state {
|
match &self.handle_state {
|
||||||
PointRadiusHandleState::Inactive => {
|
PointRadiusHandleState::Inactive => {
|
||||||
let Some(layer) = selected_star_layer else { return };
|
let Some(layer) = selected_star_layer else { return };
|
||||||
@@ -187,7 +187,7 @@ impl PointRadiusHandle {
|
|||||||
|
|
||||||
let viewport = document.metadata().transform_to_viewport(layer);
|
let viewport = document.metadata().transform_to_viewport(layer);
|
||||||
let center = viewport.transform_point2(DVec2::ZERO);
|
let center = viewport.transform_point2(DVec2::ZERO);
|
||||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
let viewport_diagonal = overlay_context.viewport.size().into_dvec2().length();
|
||||||
|
|
||||||
// Star
|
// Star
|
||||||
if let Some((sides, radius1, radius2)) = extract_star_parameters(Some(layer), document) {
|
if let Some((sides, radius1, radius2)) = extract_star_parameters(Some(layer), document) {
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ pub struct Resize {
|
|||||||
|
|
||||||
impl Resize {
|
impl Resize {
|
||||||
/// Starts a resize, assigning the snap targets and snapping the starting position.
|
/// Starts a resize, assigning the snap targets and snapping the starting position.
|
||||||
pub fn start(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
|
pub fn start(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) {
|
||||||
let root_transform = document.metadata().document_to_viewport;
|
let root_transform = document.metadata().document_to_viewport;
|
||||||
let point = SnapCandidatePoint::handle(root_transform.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(root_transform.inverse().transform_point2(input.mouse.position));
|
||||||
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
self.drag_start = snapped.snapped_point_document;
|
self.drag_start = snapped.snapped_point_document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,14 @@ impl Resize {
|
|||||||
|
|
||||||
/// Compute the drag start and end based on the current mouse position. If the layer doesn't exist, returns [`None`].
|
/// Compute the drag start and end based on the current mouse position. If the layer doesn't exist, returns [`None`].
|
||||||
/// If you want to draw even without a layer, use [`Resize::calculate_points_ignore_layer`].
|
/// If you want to draw even without a layer, use [`Resize::calculate_points_ignore_layer`].
|
||||||
pub fn calculate_points(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: Key, lock_ratio: Key) -> Option<[DVec2; 2]> {
|
pub fn calculate_points(
|
||||||
|
&mut self,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
center: Key,
|
||||||
|
lock_ratio: Key,
|
||||||
|
) -> Option<[DVec2; 2]> {
|
||||||
let layer = self.layer?;
|
let layer = self.layer?;
|
||||||
|
|
||||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||||
@@ -42,21 +49,37 @@ impl Resize {
|
|||||||
self.layer.take();
|
self.layer.take();
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(self.calculate_points_ignore_layer(document, input, center, lock_ratio, false))
|
Some(self.calculate_points_ignore_layer(document, input, viewport, center, lock_ratio, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the drag start and end based on the current mouse position. Ignores the state of the layer.
|
/// Compute the drag start and end based on the current mouse position. Ignores the state of the layer.
|
||||||
/// If you want to only draw whilst a layer exists, use [`Resize::calculate_points`].
|
/// If you want to only draw whilst a layer exists, use [`Resize::calculate_points`].
|
||||||
pub fn calculate_points_ignore_layer(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: Key, lock_ratio: Key, in_document: bool) -> [DVec2; 2] {
|
pub fn calculate_points_ignore_layer(
|
||||||
|
&mut self,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
center: Key,
|
||||||
|
lock_ratio: Key,
|
||||||
|
in_document: bool,
|
||||||
|
) -> [DVec2; 2] {
|
||||||
let ratio = input.keyboard.get(lock_ratio as usize);
|
let ratio = input.keyboard.get(lock_ratio as usize);
|
||||||
let center = input.keyboard.get(center as usize);
|
let center = input.keyboard.get(center as usize);
|
||||||
|
|
||||||
// Use shared snapping logic with optional center and ratio constraints, considering if coordinates are in document space.
|
// Use shared snapping logic with optional center and ratio constraints, considering if coordinates are in document space.
|
||||||
self.compute_snapped_resize_points(document, input, center, ratio, in_document)
|
self.compute_snapped_resize_points(document, input, viewport, center, ratio, in_document)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_transform(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: Key, lock_ratio: Key, skip_rerender: bool) -> Option<Message> {
|
pub fn calculate_transform(
|
||||||
let points_viewport = self.calculate_points(document, input, center, lock_ratio)?;
|
&mut self,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
center: Key,
|
||||||
|
lock_ratio: Key,
|
||||||
|
skip_rerender: bool,
|
||||||
|
) -> Option<Message> {
|
||||||
|
let points_viewport = self.calculate_points(document, input, viewport, center, lock_ratio)?;
|
||||||
Some(
|
Some(
|
||||||
GraphOperationMessage::TransformSet {
|
GraphOperationMessage::TransformSet {
|
||||||
layer: self.layer?,
|
layer: self.layer?,
|
||||||
@@ -68,23 +91,33 @@ impl Resize {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_circle_points(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: Key) -> [DVec2; 2] {
|
pub fn calculate_circle_points(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler, center: Key) -> [DVec2; 2] {
|
||||||
let center = input.keyboard.get(center as usize);
|
let center = input.keyboard.get(center as usize);
|
||||||
|
|
||||||
// Use shared snapping logic with enforced aspect ratio and optional center snapping.
|
// Use shared snapping logic with enforced aspect ratio and optional center snapping.
|
||||||
self.compute_snapped_resize_points(document, input, center, true, false)
|
self.compute_snapped_resize_points(document, input, viewport, center, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates two points in viewport space from a drag, applying snapping, optional center mode, and aspect ratio locking.
|
/// Calculates two points in viewport space from a drag, applying snapping, optional center mode, and aspect ratio locking.
|
||||||
fn compute_snapped_resize_points(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, center: bool, lock_ratio: bool, in_document: bool) -> [DVec2; 2] {
|
fn compute_snapped_resize_points(
|
||||||
|
&mut self,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
center: bool,
|
||||||
|
lock_ratio: bool,
|
||||||
|
in_document: bool,
|
||||||
|
) -> [DVec2; 2] {
|
||||||
let start = self.viewport_drag_start(document);
|
let start = self.viewport_drag_start(document);
|
||||||
let mouse = input.mouse.position;
|
let mouse = input.mouse.position;
|
||||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(input.viewport_bounds.center(), &document.document_ptz);
|
let document_to_viewport = document
|
||||||
|
.navigation_handler
|
||||||
|
.calculate_offset_transform(viewport.center_in_viewport_space().into(), &document.document_ptz);
|
||||||
let drag_start = self.drag_start;
|
let drag_start = self.drag_start;
|
||||||
let mut points_viewport = [start, mouse];
|
let mut points_viewport = [start, mouse];
|
||||||
|
|
||||||
let ignore = if let Some(layer) = self.layer { vec![layer] } else { vec![] };
|
let ignore = if let Some(layer) = self.layer { vec![layer] } else { vec![] };
|
||||||
let snap_data = &SnapData::ignore(document, input, &ignore);
|
let snap_data = &SnapData::ignore(document, input, viewport, &ignore);
|
||||||
|
|
||||||
if lock_ratio {
|
if lock_ratio {
|
||||||
let viewport_size = points_viewport[1] - points_viewport[0];
|
let viewport_size = points_viewport[1] - points_viewport[0];
|
||||||
|
|||||||
@@ -503,8 +503,16 @@ impl ShapeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Snap, returning a viewport delta
|
// Snap, returning a viewport delta
|
||||||
pub fn snap(&self, snap_manager: &mut SnapManager, snap_cache: &SnapCache, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, previous_mouse: DVec2) -> DVec2 {
|
pub fn snap(
|
||||||
let snap_data = SnapData::new_snap_cache(document, input, snap_cache);
|
&self,
|
||||||
|
snap_manager: &mut SnapManager,
|
||||||
|
snap_cache: &SnapCache,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
previous_mouse: DVec2,
|
||||||
|
) -> DVec2 {
|
||||||
|
let snap_data = SnapData::new_snap_cache(document, input, viewport, snap_cache);
|
||||||
|
|
||||||
let mouse_delta = document
|
let mouse_delta = document
|
||||||
.network_interface
|
.network_interface
|
||||||
|
|||||||
@@ -146,13 +146,14 @@ impl Arc {
|
|||||||
pub fn update_shape(
|
pub fn update_shape(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
ipp: &InputPreprocessorMessageHandler,
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
shape_tool_data: &mut ShapeToolData,
|
shape_tool_data: &mut ShapeToolData,
|
||||||
modifier: ShapeToolModifierKey,
|
modifier: ShapeToolModifierKey,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) {
|
) {
|
||||||
let (center, lock_ratio) = (modifier[0], modifier[1]);
|
let (center, lock_ratio) = (modifier[0], modifier[1]);
|
||||||
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
|
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, viewport, center, lock_ratio) {
|
||||||
let Some(node_id) = graph_modification_utils::get_arc_id(layer, &document.network_interface) else {
|
let Some(node_id) = graph_modification_utils::get_arc_id(layer, &document.network_interface) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,13 +89,14 @@ impl Circle {
|
|||||||
pub fn update_shape(
|
pub fn update_shape(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
ipp: &InputPreprocessorMessageHandler,
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
shape_tool_data: &mut ShapeToolData,
|
shape_tool_data: &mut ShapeToolData,
|
||||||
modifier: ShapeToolModifierKey,
|
modifier: ShapeToolModifierKey,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) {
|
) {
|
||||||
let center = modifier[0];
|
let center = modifier[0];
|
||||||
let [start, end] = shape_tool_data.data.calculate_circle_points(document, ipp, center);
|
let [start, end] = shape_tool_data.data.calculate_circle_points(document, ipp, viewport, center);
|
||||||
let Some(node_id) = graph_modification_utils::get_circle_id(layer, &document.network_interface) else {
|
let Some(node_id) = graph_modification_utils::get_circle_id(layer, &document.network_interface) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ impl Ellipse {
|
|||||||
pub fn update_shape(
|
pub fn update_shape(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
ipp: &InputPreprocessorMessageHandler,
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
shape_tool_data: &mut ShapeToolData,
|
shape_tool_data: &mut ShapeToolData,
|
||||||
modifier: ShapeToolModifierKey,
|
modifier: ShapeToolModifierKey,
|
||||||
@@ -30,7 +31,7 @@ impl Ellipse {
|
|||||||
) {
|
) {
|
||||||
let [center, lock_ratio, _] = modifier;
|
let [center, lock_ratio, _] = modifier;
|
||||||
|
|
||||||
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
|
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, viewport, center, lock_ratio) {
|
||||||
let Some(node_id) = graph_modification_utils::get_ellipse_id(layer, &document.network_interface) else {
|
let Some(node_id) = graph_modification_utils::get_ellipse_id(layer, &document.network_interface) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ impl Line {
|
|||||||
pub fn update_shape(
|
pub fn update_shape(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
ipp: &InputPreprocessorMessageHandler,
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
shape_tool_data: &mut ShapeToolData,
|
shape_tool_data: &mut ShapeToolData,
|
||||||
modifier: ShapeToolModifierKey,
|
modifier: ShapeToolModifierKey,
|
||||||
@@ -59,7 +60,7 @@ impl Line {
|
|||||||
|
|
||||||
let keyboard = &ipp.keyboard;
|
let keyboard = &ipp.keyboard;
|
||||||
let ignore = [layer];
|
let ignore = [layer];
|
||||||
let snap_data = SnapData::ignore(document, ipp, &ignore);
|
let snap_data = SnapData::ignore(document, ipp, viewport, &ignore);
|
||||||
let mut document_points = generate_line(shape_tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center));
|
let mut document_points = generate_line(shape_tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center));
|
||||||
|
|
||||||
if shape_tool_data.line_data.dragging_endpoint == Some(LineEnd::Start) {
|
if shape_tool_data.line_data.dragging_endpoint == Some(LineEnd::Start) {
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
|||||||
&self,
|
&self,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
selected_polygon_layer: Option<LayerNodeIdentifier>,
|
selected_polygon_layer: Option<LayerNodeIdentifier>,
|
||||||
input: &InputPreprocessorMessageHandler,
|
_input: &InputPreprocessorMessageHandler,
|
||||||
shape_editor: &mut &mut ShapeState,
|
shape_editor: &mut &mut ShapeState,
|
||||||
mouse_position: DVec2,
|
mouse_position: DVec2,
|
||||||
overlay_context: &mut OverlayContext,
|
overlay_context: &mut OverlayContext,
|
||||||
) {
|
) {
|
||||||
self.number_of_points_dial.overlays(document, selected_polygon_layer, shape_editor, mouse_position, overlay_context);
|
self.number_of_points_dial.overlays(document, selected_polygon_layer, shape_editor, mouse_position, overlay_context);
|
||||||
self.point_radius_handle.overlays(selected_polygon_layer, document, input, overlay_context);
|
self.point_radius_handle.overlays(selected_polygon_layer, document, overlay_context);
|
||||||
|
|
||||||
polygon_outline(selected_polygon_layer, document, overlay_context);
|
polygon_outline(selected_polygon_layer, document, overlay_context);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
|||||||
fn dragging_overlays(
|
fn dragging_overlays(
|
||||||
&self,
|
&self,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
input: &InputPreprocessorMessageHandler,
|
_input: &InputPreprocessorMessageHandler,
|
||||||
shape_editor: &mut &mut ShapeState,
|
shape_editor: &mut &mut ShapeState,
|
||||||
mouse_position: DVec2,
|
mouse_position: DVec2,
|
||||||
overlay_context: &mut OverlayContext,
|
overlay_context: &mut OverlayContext,
|
||||||
@@ -82,7 +82,7 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.point_radius_handle.is_dragging_or_snapped() {
|
if self.point_radius_handle.is_dragging_or_snapped() {
|
||||||
self.point_radius_handle.overlays(None, document, input, overlay_context);
|
self.point_radius_handle.overlays(None, document, overlay_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +116,7 @@ impl Polygon {
|
|||||||
pub fn update_shape(
|
pub fn update_shape(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
ipp: &InputPreprocessorMessageHandler,
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
shape_tool_data: &mut ShapeToolData,
|
shape_tool_data: &mut ShapeToolData,
|
||||||
modifier: ShapeToolModifierKey,
|
modifier: ShapeToolModifierKey,
|
||||||
@@ -123,7 +124,7 @@ impl Polygon {
|
|||||||
) {
|
) {
|
||||||
let [center, lock_ratio, _] = modifier;
|
let [center, lock_ratio, _] = modifier;
|
||||||
|
|
||||||
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
|
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, viewport, center, lock_ratio) {
|
||||||
// TODO: We need to determine how to allow the polygon node to make irregular shapes
|
// TODO: We need to determine how to allow the polygon node to make irregular shapes
|
||||||
update_radius_sign(end, start, layer, document, responses);
|
update_radius_sign(end, start, layer, document, responses);
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ impl Rectangle {
|
|||||||
pub fn update_shape(
|
pub fn update_shape(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
ipp: &InputPreprocessorMessageHandler,
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
shape_tool_data: &mut ShapeToolData,
|
shape_tool_data: &mut ShapeToolData,
|
||||||
modifier: ShapeToolModifierKey,
|
modifier: ShapeToolModifierKey,
|
||||||
@@ -30,7 +31,7 @@ impl Rectangle {
|
|||||||
) {
|
) {
|
||||||
let [center, lock_ratio, _] = modifier;
|
let [center, lock_ratio, _] = modifier;
|
||||||
|
|
||||||
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
|
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, viewport, center, lock_ratio) {
|
||||||
let Some(node_id) = graph_modification_utils::get_rectangle_id(layer, &document.network_interface) else {
|
let Some(node_id) = graph_modification_utils::get_rectangle_id(layer, &document.network_interface) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,13 +36,20 @@ impl Spiral {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_shape(document: &DocumentMessageHandler, ipp: &InputPreprocessorMessageHandler, layer: LayerNodeIdentifier, shape_tool_data: &mut ShapeToolData, responses: &mut VecDeque<Message>) {
|
pub fn update_shape(
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
layer: LayerNodeIdentifier,
|
||||||
|
shape_tool_data: &mut ShapeToolData,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) {
|
||||||
use graphene_std::vector::generator_nodes::spiral::*;
|
use graphene_std::vector::generator_nodes::spiral::*;
|
||||||
|
|
||||||
let viewport_drag_start = shape_tool_data.data.viewport_drag_start(document);
|
let viewport_drag_start = shape_tool_data.data.viewport_drag_start(document);
|
||||||
|
|
||||||
let ignore = vec![layer];
|
let ignore = vec![layer];
|
||||||
let snap_data = SnapData::ignore(document, ipp, &ignore);
|
let snap_data = SnapData::ignore(document, ipp, viewport, &ignore);
|
||||||
let config = SnapTypeConfiguration::default();
|
let config = SnapTypeConfiguration::default();
|
||||||
let document_mouse = document.metadata().document_to_viewport.inverse().transform_point2(ipp.mouse.position);
|
let document_mouse = document.metadata().document_to_viewport.inverse().transform_point2(ipp.mouse.position);
|
||||||
let snapped = shape_tool_data.data.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
|
let snapped = shape_tool_data.data.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
|||||||
&self,
|
&self,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
selected_star_layer: Option<LayerNodeIdentifier>,
|
selected_star_layer: Option<LayerNodeIdentifier>,
|
||||||
input: &InputPreprocessorMessageHandler,
|
_input: &InputPreprocessorMessageHandler,
|
||||||
shape_editor: &mut &mut ShapeState,
|
shape_editor: &mut &mut ShapeState,
|
||||||
mouse_position: DVec2,
|
mouse_position: DVec2,
|
||||||
overlay_context: &mut OverlayContext,
|
overlay_context: &mut OverlayContext,
|
||||||
) {
|
) {
|
||||||
self.number_of_points_dial.overlays(document, selected_star_layer, shape_editor, mouse_position, overlay_context);
|
self.number_of_points_dial.overlays(document, selected_star_layer, shape_editor, mouse_position, overlay_context);
|
||||||
self.point_radius_handle.overlays(selected_star_layer, document, input, overlay_context);
|
self.point_radius_handle.overlays(selected_star_layer, document, overlay_context);
|
||||||
|
|
||||||
star_outline(selected_star_layer, document, overlay_context);
|
star_outline(selected_star_layer, document, overlay_context);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
|||||||
fn dragging_overlays(
|
fn dragging_overlays(
|
||||||
&self,
|
&self,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
input: &InputPreprocessorMessageHandler,
|
_input: &InputPreprocessorMessageHandler,
|
||||||
shape_editor: &mut &mut ShapeState,
|
shape_editor: &mut &mut ShapeState,
|
||||||
mouse_position: DVec2,
|
mouse_position: DVec2,
|
||||||
overlay_context: &mut OverlayContext,
|
overlay_context: &mut OverlayContext,
|
||||||
@@ -82,7 +82,7 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.point_radius_handle.is_dragging_or_snapped() {
|
if self.point_radius_handle.is_dragging_or_snapped() {
|
||||||
self.point_radius_handle.overlays(None, document, input, overlay_context);
|
self.point_radius_handle.overlays(None, document, overlay_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +121,7 @@ impl Star {
|
|||||||
pub fn update_shape(
|
pub fn update_shape(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
ipp: &InputPreprocessorMessageHandler,
|
ipp: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
shape_tool_data: &mut ShapeToolData,
|
shape_tool_data: &mut ShapeToolData,
|
||||||
modifier: ShapeToolModifierKey,
|
modifier: ShapeToolModifierKey,
|
||||||
@@ -128,7 +129,7 @@ impl Star {
|
|||||||
) {
|
) {
|
||||||
let [center, lock_ratio, _] = modifier;
|
let [center, lock_ratio, _] = modifier;
|
||||||
|
|
||||||
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
|
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, viewport, center, lock_ratio) {
|
||||||
// TODO: We need to determine how to allow the polygon node to make irregular shapes
|
// TODO: We need to determine how to allow the polygon node to make irregular shapes
|
||||||
update_radius_sign(end, start, layer, document, responses);
|
update_radius_sign(end, start, layer, document, responses);
|
||||||
|
|
||||||
|
|||||||
@@ -206,29 +206,31 @@ pub struct SnapCache {
|
|||||||
pub struct SnapData<'a> {
|
pub struct SnapData<'a> {
|
||||||
pub document: &'a DocumentMessageHandler,
|
pub document: &'a DocumentMessageHandler,
|
||||||
pub input: &'a InputPreprocessorMessageHandler,
|
pub input: &'a InputPreprocessorMessageHandler,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
pub ignore: &'a [LayerNodeIdentifier],
|
pub ignore: &'a [LayerNodeIdentifier],
|
||||||
pub node_snap_cache: Option<&'a SnapCache>,
|
pub node_snap_cache: Option<&'a SnapCache>,
|
||||||
pub candidates: Option<&'a Vec<LayerNodeIdentifier>>,
|
pub candidates: Option<&'a Vec<LayerNodeIdentifier>>,
|
||||||
pub alignment_candidates: Option<&'a Vec<LayerNodeIdentifier>>,
|
pub alignment_candidates: Option<&'a Vec<LayerNodeIdentifier>>,
|
||||||
}
|
}
|
||||||
impl<'a> SnapData<'a> {
|
impl<'a> SnapData<'a> {
|
||||||
pub fn new(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler) -> Self {
|
pub fn new(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler, viewport: &'a ViewportMessageHandler) -> Self {
|
||||||
Self::ignore(document, input, &[])
|
Self::ignore(document, input, viewport, &[])
|
||||||
}
|
}
|
||||||
pub fn ignore(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler, ignore: &'a [LayerNodeIdentifier]) -> Self {
|
pub fn ignore(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler, viewport: &'a ViewportMessageHandler, ignore: &'a [LayerNodeIdentifier]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
document,
|
document,
|
||||||
input,
|
input,
|
||||||
|
viewport,
|
||||||
ignore,
|
ignore,
|
||||||
candidates: None,
|
candidates: None,
|
||||||
alignment_candidates: None,
|
alignment_candidates: None,
|
||||||
node_snap_cache: None,
|
node_snap_cache: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_snap_cache(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler, snap_cache: &'a SnapCache) -> Self {
|
pub fn new_snap_cache(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler, viewport: &'a ViewportMessageHandler, snap_cache: &'a SnapCache) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node_snap_cache: Some(snap_cache),
|
node_snap_cache: Some(snap_cache),
|
||||||
..Self::new(document, input)
|
..Self::new(document, input, viewport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_candidates(&self) -> &[LayerNodeIdentifier] {
|
fn get_candidates(&self) -> &[LayerNodeIdentifier] {
|
||||||
@@ -301,7 +303,7 @@ impl SnapManager {
|
|||||||
|
|
||||||
for point in snapped_points {
|
for point in snapped_points {
|
||||||
let viewport_point = document.metadata().document_to_viewport.transform_point2(point.snapped_point_document);
|
let viewport_point = document.metadata().document_to_viewport.transform_point2(point.snapped_point_document);
|
||||||
let on_screen = viewport_point.cmpgt(DVec2::ZERO).all() && viewport_point.cmplt(snap_data.input.viewport_bounds.size()).all();
|
let on_screen = viewport_point.cmpgt(DVec2::ZERO).all() && viewport_point.cmplt(snap_data.viewport.size().into()).all();
|
||||||
if !on_screen && !off_screen {
|
if !on_screen && !off_screen {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -337,7 +339,7 @@ impl SnapManager {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let layer_bounds = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
let layer_bounds = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
||||||
let screen_bounds = document.metadata().document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, snap_data.input.viewport_bounds.size()]);
|
let screen_bounds = document.metadata().document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, snap_data.viewport.size().into()]);
|
||||||
if screen_bounds.intersects(layer_bounds) {
|
if screen_bounds.intersects(layer_bounds) {
|
||||||
if self.alignment_candidates.as_ref().is_none_or(|candidates| candidates.len() <= 100) {
|
if self.alignment_candidates.as_ref().is_none_or(|candidates| candidates.len() <= 100) {
|
||||||
self.alignment_candidates.get_or_insert_with(Vec::new).push(layer);
|
self.alignment_candidates.get_or_insert_with(Vec::new).push(layer);
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ impl DistributionSnapper {
|
|||||||
self.down.clear();
|
self.down.clear();
|
||||||
self.up.clear();
|
self.up.clear();
|
||||||
|
|
||||||
let screen_bounds = (document.metadata().document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, snap_data.input.viewport_bounds.size()])).bounding_box();
|
let screen_bounds = (document.metadata().document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, snap_data.viewport.size().into()])).bounding_box();
|
||||||
let max_extent = (screen_bounds[1] - screen_bounds[0]).abs().max_element();
|
let max_extent = (screen_bounds[1] - screen_bounds[0]).abs().max_element();
|
||||||
|
|
||||||
// Collect artboard bounds
|
// Collect artboard bounds
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use super::snapping::{self, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnappedPoint};
|
use super::snapping::{self, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnappedPoint};
|
||||||
use crate::consts::{
|
use crate::consts::{
|
||||||
BOUNDS_ROTATE_THRESHOLD, BOUNDS_SELECT_THRESHOLD, COLOR_OVERLAY_WHITE, MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT, MAXIMUM_ALT_SCALE_FACTOR, MIN_LENGTH_FOR_CORNERS_VISIBILITY,
|
BOUNDS_ROTATE_THRESHOLD, BOUNDS_SELECT_THRESHOLD, MAX_LENGTH_FOR_NO_WIDTH_OR_HEIGHT, MAXIMUM_ALT_SCALE_FACTOR, MIN_LENGTH_FOR_CORNERS_VISIBILITY, MIN_LENGTH_FOR_EDGE_RESIZE_PRIORITY_OVER_CORNERS,
|
||||||
MIN_LENGTH_FOR_EDGE_RESIZE_PRIORITY_OVER_CORNERS, MIN_LENGTH_FOR_MIDPOINT_VISIBILITY, MIN_LENGTH_FOR_RESIZE_TO_INCLUDE_INTERIOR, MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY, RESIZE_HANDLE_SIZE,
|
MIN_LENGTH_FOR_MIDPOINT_VISIBILITY, MIN_LENGTH_FOR_RESIZE_TO_INCLUDE_INTERIOR, MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY, RESIZE_HANDLE_SIZE, SELECTION_DRAG_ANGLE, SKEW_TRIANGLE_OFFSET,
|
||||||
SELECTION_DRAG_ANGLE, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
|
SKEW_TRIANGLE_SIZE,
|
||||||
};
|
};
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||||
@@ -489,13 +489,7 @@ impl BoundingBoxManager {
|
|||||||
if (end - start).length() < MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY {
|
if (end - start).length() < MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
overlay_context.skew_handles(start, end);
|
||||||
let edge_dir = (end - start).normalize();
|
|
||||||
let mid = end.midpoint(start);
|
|
||||||
|
|
||||||
for edge in [edge_dir, -edge_dir] {
|
|
||||||
overlay_context.draw_triangle(mid + edge * (3. + SKEW_TRIANGLE_OFFSET), edge, SKEW_TRIANGLE_SIZE, None, None);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some([start, end]) = self.edge_endpoints_vector_from_edge_bool(hover_edge) {
|
if let Some([start, end]) = self.edge_endpoints_vector_from_edge_bool(hover_edge) {
|
||||||
@@ -565,11 +559,6 @@ impl BoundingBoxManager {
|
|||||||
self.render_quad(overlay_context);
|
self.render_quad(overlay_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut draw_handle = |point: DVec2, angle: f64| {
|
|
||||||
let quad = DAffine2::from_angle_translation(angle, point) * Quad::from_box([DVec2::splat(-RESIZE_HANDLE_SIZE / 2.), DVec2::splat(RESIZE_HANDLE_SIZE / 2.)]);
|
|
||||||
overlay_context.quad(quad, None, Some(COLOR_OVERLAY_WHITE));
|
|
||||||
};
|
|
||||||
|
|
||||||
let horizontal_angle = (quad.top_left() - quad.bottom_left()).to_angle();
|
let horizontal_angle = (quad.top_left() - quad.bottom_left()).to_angle();
|
||||||
let vertical_angle = (quad.top_left() - quad.top_right()).to_angle();
|
let vertical_angle = (quad.top_left() - quad.top_right()).to_angle();
|
||||||
|
|
||||||
@@ -579,7 +568,7 @@ impl BoundingBoxManager {
|
|||||||
TransformCageSizeCategory::Full | TransformCageSizeCategory::Narrow | TransformCageSizeCategory::ReducedLandscape
|
TransformCageSizeCategory::Full | TransformCageSizeCategory::Narrow | TransformCageSizeCategory::ReducedLandscape
|
||||||
) {
|
) {
|
||||||
for point in horizontal_edges {
|
for point in horizontal_edges {
|
||||||
draw_handle(point, horizontal_angle);
|
overlay_context.resize_handle(point, horizontal_angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +578,7 @@ impl BoundingBoxManager {
|
|||||||
TransformCageSizeCategory::Full | TransformCageSizeCategory::Narrow | TransformCageSizeCategory::ReducedPortrait
|
TransformCageSizeCategory::Full | TransformCageSizeCategory::Narrow | TransformCageSizeCategory::ReducedPortrait
|
||||||
) {
|
) {
|
||||||
for point in vertical_edges {
|
for point in vertical_edges {
|
||||||
draw_handle(point, vertical_angle);
|
overlay_context.resize_handle(point, vertical_angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,14 +595,14 @@ impl BoundingBoxManager {
|
|||||||
TransformCageSizeCategory::Full | TransformCageSizeCategory::ReducedBoth | TransformCageSizeCategory::ReducedLandscape | TransformCageSizeCategory::ReducedPortrait
|
TransformCageSizeCategory::Full | TransformCageSizeCategory::ReducedBoth | TransformCageSizeCategory::ReducedLandscape | TransformCageSizeCategory::ReducedPortrait
|
||||||
) {
|
) {
|
||||||
for point in quad.0 {
|
for point in quad.0 {
|
||||||
draw_handle(point, angle);
|
overlay_context.resize_handle(point, angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the flat line endpoint drag handles
|
// Draw the flat line endpoint drag handles
|
||||||
if category == TransformCageSizeCategory::Flat {
|
if category == TransformCageSizeCategory::Flat {
|
||||||
draw_handle(self.transform.transform_point2(self.bounds[0]), angle);
|
overlay_context.resize_handle(self.transform.transform_point2(self.bounds[0]), angle);
|
||||||
draw_handle(self.transform.transform_point2(self.bounds[1]), angle);
|
overlay_context.resize_handle(self.transform.transform_point2(self.bounds[1]), angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ pub fn resize_bounds(
|
|||||||
snap_manager: &mut SnapManager,
|
snap_manager: &mut SnapManager,
|
||||||
snap_candidates: &mut Vec<SnapCandidatePoint>,
|
snap_candidates: &mut Vec<SnapCandidatePoint>,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
center: bool,
|
center: bool,
|
||||||
constrain: bool,
|
constrain: bool,
|
||||||
tool: ToolType,
|
tool: ToolType,
|
||||||
@@ -271,7 +272,7 @@ pub fn resize_bounds(
|
|||||||
let snap = Some(SizeSnapData {
|
let snap = Some(SizeSnapData {
|
||||||
manager: snap_manager,
|
manager: snap_manager,
|
||||||
points: snap_candidates,
|
points: snap_candidates,
|
||||||
snap_data: SnapData::ignore(document, input, dragging_layers),
|
snap_data: SnapData::ignore(document, input, viewport, dragging_layers),
|
||||||
});
|
});
|
||||||
let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center, constrain, snap);
|
let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center, constrain, snap);
|
||||||
let (delta, mut pivot) = movement.bounds_to_scale_transform(position, size);
|
let (delta, mut pivot) = movement.bounds_to_scale_transform(position, size);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ pub struct ToolMessageContext<'a> {
|
|||||||
pub persistent_data: &'a PersistentData,
|
pub persistent_data: &'a PersistentData,
|
||||||
pub node_graph: &'a NodeGraphExecutor,
|
pub node_graph: &'a NodeGraphExecutor,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, ExtractField)]
|
#[derive(Debug, Default, ExtractField)]
|
||||||
@@ -41,6 +42,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
|||||||
persistent_data,
|
persistent_data,
|
||||||
node_graph,
|
node_graph,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
} = context;
|
} = context;
|
||||||
let font_cache = &persistent_data.font_cache;
|
let font_cache = &persistent_data.font_cache;
|
||||||
|
|
||||||
@@ -54,6 +56,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
|||||||
input,
|
input,
|
||||||
tool_data: &self.tool_state.tool_data,
|
tool_data: &self.tool_state.tool_data,
|
||||||
shape_editor: &mut self.shape_editor,
|
shape_editor: &mut self.shape_editor,
|
||||||
|
viewport,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -123,6 +126,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
|||||||
shape_editor: &mut self.shape_editor,
|
shape_editor: &mut self.shape_editor,
|
||||||
node_graph,
|
node_graph,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
|
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
|
||||||
@@ -230,6 +234,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
|||||||
shape_editor: &mut self.shape_editor,
|
shape_editor: &mut self.shape_editor,
|
||||||
node_graph,
|
node_graph,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set initial hints and cursor
|
// Set initial hints and cursor
|
||||||
@@ -336,6 +341,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
|||||||
shape_editor: &mut self.shape_editor,
|
shape_editor: &mut self.shape_editor,
|
||||||
node_graph,
|
node_graph,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
};
|
};
|
||||||
if matches!(tool_message, ToolMessage::UpdateHints) {
|
if matches!(tool_message, ToolMessage::UpdateHints) {
|
||||||
if graph_view_overlay_open {
|
if graph_view_overlay_open {
|
||||||
|
|||||||
@@ -145,12 +145,12 @@ impl ArtboardToolData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hovered_artboard(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
|
fn hovered_artboard(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||||
document.click_xray(input).find(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[]))
|
document.click_xray(input, viewport).find(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_artboard(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
fn select_artboard(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
||||||
if let Some(intersection) = Self::hovered_artboard(document, input) {
|
if let Some(intersection) = Self::hovered_artboard(document, input, viewport) {
|
||||||
self.selected_artboard = Some(intersection);
|
self.selected_artboard = Some(intersection);
|
||||||
|
|
||||||
if let Some(bounds) = document.metadata().bounding_box_document(intersection) {
|
if let Some(bounds) = document.metadata().bounding_box_document(intersection) {
|
||||||
@@ -171,7 +171,15 @@ impl ArtboardToolData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize_artboard(&mut self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, from_center: bool, constrain_square: bool) {
|
fn resize_artboard(
|
||||||
|
&mut self,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
from_center: bool,
|
||||||
|
constrain_square: bool,
|
||||||
|
) {
|
||||||
let Some(bounds) = &self.bounding_box_manager else {
|
let Some(bounds) = &self.bounding_box_manager else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -193,7 +201,7 @@ impl ArtboardToolData {
|
|||||||
let snap = Some(SizeSnapData {
|
let snap = Some(SizeSnapData {
|
||||||
manager: &mut self.draw.snap_manager,
|
manager: &mut self.draw.snap_manager,
|
||||||
points: &mut self.snap_candidates,
|
points: &mut self.snap_candidates,
|
||||||
snap_data: SnapData::ignore(document, input, &ignore),
|
snap_data: SnapData::ignore(document, input, viewport, &ignore),
|
||||||
});
|
});
|
||||||
let (min, size) = movement.new_size(input.mouse.position, bounds.transform, center, constrain_square, snap);
|
let (min, size) = movement.new_size(input.mouse.position, bounds.transform, center, constrain_square, snap);
|
||||||
let max = min + size;
|
let max = min + size;
|
||||||
@@ -225,9 +233,9 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
type ToolOptions = ();
|
type ToolOptions = ();
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||||
let ToolActionMessageContext { document, input, .. } = tool_action_data;
|
let ToolActionMessageContext { document, input, viewport, .. } = tool_action_data;
|
||||||
|
|
||||||
let hovered = ArtboardToolData::hovered_artboard(document, input).is_some();
|
let hovered = ArtboardToolData::hovered_artboard(document, input, viewport).is_some();
|
||||||
|
|
||||||
let ToolMessage::Artboard(event) = event else { return self };
|
let ToolMessage::Artboard(event) = event else { return self };
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
@@ -259,8 +267,8 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
let selected_artboard_bounds = tool_data.selected_artboard.and_then(|layer| document.metadata().bounding_box_document(layer)).map(Rect::from_box);
|
let selected_artboard_bounds = tool_data.selected_artboard.and_then(|layer| document.metadata().bounding_box_document(layer)).map(Rect::from_box);
|
||||||
|
|
||||||
// Find hovered artboard or regular layer
|
// Find hovered artboard or regular layer
|
||||||
let hovered_artboard = ArtboardToolData::hovered_artboard(document, input);
|
let hovered_artboard = ArtboardToolData::hovered_artboard(document, input, viewport);
|
||||||
let hovered_layer = document.click_xray(input).find(|&layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
let hovered_layer = document.click_xray(input, viewport).find(|&layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||||
|
|
||||||
// Get bounds for the hovered object (prioritize artboards)
|
// Get bounds for the hovered object (prioritize artboards)
|
||||||
let hovered_bounds = if let Some(artboard) = hovered_artboard {
|
let hovered_bounds = if let Some(artboard) = hovered_artboard {
|
||||||
@@ -281,7 +289,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_data.draw.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.draw.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -295,11 +303,11 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
tool_data.start_resizing(selected_edges, document, input);
|
tool_data.start_resizing(selected_edges, document, input);
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input);
|
||||||
ArtboardToolFsmState::ResizingBounds
|
ArtboardToolFsmState::ResizingBounds
|
||||||
} else if tool_data.select_artboard(document, input, responses) {
|
} else if tool_data.select_artboard(document, input, viewport, responses) {
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input);
|
||||||
ArtboardToolFsmState::Dragging
|
ArtboardToolFsmState::Dragging
|
||||||
} else {
|
} else {
|
||||||
tool_data.draw.start(document, input);
|
tool_data.draw.start(document, input, viewport);
|
||||||
|
|
||||||
ArtboardToolFsmState::Drawing
|
ArtboardToolFsmState::Drawing
|
||||||
};
|
};
|
||||||
@@ -309,14 +317,14 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
(ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
|
(ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
|
||||||
let from_center = input.keyboard.get(center as usize);
|
let from_center = input.keyboard.get(center as usize);
|
||||||
let constrain_square = input.keyboard.get(constrain_axis_or_aspect as usize);
|
let constrain_square = input.keyboard.get(constrain_axis_or_aspect as usize);
|
||||||
tool_data.resize_artboard(responses, document, input, from_center, constrain_square);
|
tool_data.resize_artboard(responses, document, input, viewport, from_center, constrain_square);
|
||||||
|
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let messages = [
|
let messages = [
|
||||||
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
||||||
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
ArtboardToolFsmState::ResizingBounds
|
ArtboardToolFsmState::ResizingBounds
|
||||||
}
|
}
|
||||||
@@ -325,7 +333,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
let axis_align = input.keyboard.get(constrain_axis_or_aspect as usize);
|
let axis_align = input.keyboard.get(constrain_axis_or_aspect as usize);
|
||||||
|
|
||||||
let ignore = tool_data.selected_artboard.map_or(Vec::new(), |layer| vec![layer]);
|
let ignore = tool_data.selected_artboard.map_or(Vec::new(), |layer| vec![layer]);
|
||||||
let snap_data = SnapData::ignore(document, input, &ignore);
|
let snap_data = SnapData::ignore(document, input, viewport, &ignore);
|
||||||
let document_to_viewport = document.metadata().document_to_viewport;
|
let document_to_viewport = document.metadata().document_to_viewport;
|
||||||
let [start, current] = [tool_data.drag_start, tool_data.drag_current].map(|point| document_to_viewport.transform_point2(point));
|
let [start, current] = [tool_data.drag_start, tool_data.drag_current].map(|point| document_to_viewport.transform_point2(point));
|
||||||
let mouse_delta = snap_drag(start, current, axis_align, Axis::None, snap_data, &mut tool_data.draw.snap_manager, &tool_data.snap_candidates);
|
let mouse_delta = snap_drag(start, current, axis_align, Axis::None, snap_data, &mut tool_data.draw.snap_manager, &tool_data.snap_candidates);
|
||||||
@@ -355,14 +363,14 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
||||||
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
}
|
}
|
||||||
ArtboardToolFsmState::Dragging
|
ArtboardToolFsmState::Dragging
|
||||||
}
|
}
|
||||||
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
|
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
|
||||||
// The draw.calculate_points_ignore_layer uses this value to avoid snapping to itself.
|
// The draw.calculate_points_ignore_layer uses this value to avoid snapping to itself.
|
||||||
tool_data.draw.layer = tool_data.selected_artboard;
|
tool_data.draw.layer = tool_data.selected_artboard;
|
||||||
let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, center, constrain_axis_or_aspect, true);
|
let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, viewport, center, constrain_axis_or_aspect, true);
|
||||||
let viewport_to_document = document.metadata().document_to_viewport.inverse();
|
let viewport_to_document = document.metadata().document_to_viewport.inverse();
|
||||||
let [start, end] = [start, end].map(|point| viewport_to_document.transform_point2(point));
|
let [start, end] = [start, end].map(|point| viewport_to_document.transform_point2(point));
|
||||||
if let Some(artboard) = tool_data.selected_artboard {
|
if let Some(artboard) = tool_data.selected_artboard {
|
||||||
@@ -396,7 +404,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
||||||
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
ArtboardToolFsmState::Drawing
|
ArtboardToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
@@ -408,7 +416,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
.map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, false, false, None));
|
.map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, false, false, None));
|
||||||
|
|
||||||
if cursor == MouseCursorIcon::Default && !hovered {
|
if cursor == MouseCursorIcon::Default && !hovered {
|
||||||
tool_data.draw.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position);
|
tool_data.draw.snap_manager.preview_draw(&SnapData::new(document, input, viewport), input.mouse.position);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
cursor = MouseCursorIcon::Crosshair;
|
cursor = MouseCursorIcon::Crosshair;
|
||||||
} else {
|
} else {
|
||||||
@@ -424,19 +432,19 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
}
|
}
|
||||||
(ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
(ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
ArtboardToolFsmState::ResizingBounds
|
ArtboardToolFsmState::ResizingBounds
|
||||||
}
|
}
|
||||||
(ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
(ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
tool_data.auto_panning.shift_viewport(input, responses);
|
tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
ArtboardToolFsmState::Dragging
|
ArtboardToolFsmState::Dragging
|
||||||
}
|
}
|
||||||
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
tool_data.auto_panning.shift_viewport(input, responses);
|
tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
ArtboardToolFsmState::Drawing
|
ArtboardToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
@@ -545,7 +553,9 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
let scale = DAffine2::from_scale(enlargement_factor);
|
let scale = DAffine2::from_scale(enlargement_factor);
|
||||||
let pivot = DAffine2::from_translation(pivot);
|
let pivot = DAffine2::from_translation(pivot);
|
||||||
let transformation = pivot * scale * pivot.inverse();
|
let transformation = pivot * scale * pivot.inverse();
|
||||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(input.viewport_bounds.center(), &document.document_ptz);
|
let document_to_viewport = document
|
||||||
|
.navigation_handler
|
||||||
|
.calculate_offset_transform(viewport.center_in_viewport_space().into(), &document.document_ptz);
|
||||||
let to = document_to_viewport.inverse() * document.metadata().downstream_transform_to_viewport(selected_artboard);
|
let to = document_to_viewport.inverse() * document.metadata().downstream_transform_to_viewport(selected_artboard);
|
||||||
let original_transform = document.metadata().upstream_transform(selected_artboard.to_node());
|
let original_transform = document.metadata().upstream_transform(selected_artboard.to_node());
|
||||||
let new = to.inverse() * transformation * to * original_transform;
|
let new = to.inverse() * transformation * to * original_transform;
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ impl Fsm for EyedropperToolFsmState {
|
|||||||
type ToolOptions = ();
|
type ToolOptions = ();
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, _tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
fn transition(self, event: ToolMessage, _tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||||
let ToolActionMessageContext { global_tool_data, input, .. } = tool_action_data;
|
let ToolActionMessageContext {
|
||||||
|
global_tool_data, input, viewport, ..
|
||||||
|
} = tool_action_data;
|
||||||
|
|
||||||
let ToolMessage::Eyedropper(event) = event else { return self };
|
let ToolMessage::Eyedropper(event) = event else { return self };
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
@@ -97,7 +99,8 @@ impl Fsm for EyedropperToolFsmState {
|
|||||||
}
|
}
|
||||||
// Sampling -> Sampling
|
// Sampling -> Sampling
|
||||||
(EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary, EyedropperToolMessage::PointerMove) => {
|
(EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary, EyedropperToolMessage::PointerMove) => {
|
||||||
if input.viewport_bounds.in_bounds(input.mouse.position) {
|
let mouse_position = viewport.logical(input.mouse.position);
|
||||||
|
if viewport.is_in_bounds(mouse_position) {
|
||||||
update_cursor_preview(responses, input, global_tool_data, None);
|
update_cursor_preview(responses, input, global_tool_data, None);
|
||||||
} else {
|
} else {
|
||||||
disable_cursor_preview(responses);
|
disable_cursor_preview(responses);
|
||||||
|
|||||||
@@ -94,7 +94,11 @@ impl Fsm for FillToolFsmState {
|
|||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ToolActionMessageContext {
|
let ToolActionMessageContext {
|
||||||
document, global_tool_data, input, ..
|
document,
|
||||||
|
global_tool_data,
|
||||||
|
input,
|
||||||
|
viewport,
|
||||||
|
..
|
||||||
} = handler_data;
|
} = handler_data;
|
||||||
|
|
||||||
let ToolMessage::Fill(event) = event else { return self };
|
let ToolMessage::Fill(event) = event else { return self };
|
||||||
@@ -105,7 +109,7 @@ impl Fsm for FillToolFsmState {
|
|||||||
let preview_color = if use_secondary { global_tool_data.secondary_color } else { global_tool_data.primary_color };
|
let preview_color = if use_secondary { global_tool_data.secondary_color } else { global_tool_data.primary_color };
|
||||||
|
|
||||||
// Get the layer the user is hovering over
|
// Get the layer the user is hovering over
|
||||||
if let Some(layer) = document.click(input) {
|
if let Some(layer) = document.click(input, viewport) {
|
||||||
overlay_context.fill_path_pattern(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), &preview_color);
|
overlay_context.fill_path_pattern(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), &preview_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +121,7 @@ impl Fsm for FillToolFsmState {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
(FillToolFsmState::Ready, color_event) => {
|
(FillToolFsmState::Ready, color_event) => {
|
||||||
let Some(layer_identifier) = document.click(input) else {
|
let Some(layer_identifier) = document.click(input, viewport) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
// If the layer is a raster layer, don't fill it, wait till the flood fill tool is implemented
|
// If the layer is a raster layer, don't fill it, wait till the flood fill tool is implemented
|
||||||
|
|||||||
@@ -237,6 +237,7 @@ impl Fsm for FreehandToolFsmState {
|
|||||||
input,
|
input,
|
||||||
shape_editor,
|
shape_editor,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
..
|
..
|
||||||
} = tool_action_data;
|
} = tool_action_data;
|
||||||
|
|
||||||
@@ -283,7 +284,7 @@ impl Fsm for FreehandToolFsmState {
|
|||||||
|
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
responses.add(DocumentMessage::DeselectAllLayers);
|
||||||
|
|
||||||
let parent = document.new_layer_bounding_artboard(input);
|
let parent = document.new_layer_bounding_artboard(input, viewport);
|
||||||
|
|
||||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||||
let node = node_type.default_node_template();
|
let node = node_type.default_node_template();
|
||||||
|
|||||||
@@ -255,7 +255,11 @@ impl Fsm for GradientToolFsmState {
|
|||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ToolActionMessageContext {
|
let ToolActionMessageContext {
|
||||||
document, global_tool_data, input, ..
|
document,
|
||||||
|
global_tool_data,
|
||||||
|
input,
|
||||||
|
viewport,
|
||||||
|
..
|
||||||
} = tool_action_data;
|
} = tool_action_data;
|
||||||
|
|
||||||
let ToolMessage::Gradient(event) = event else { return self };
|
let ToolMessage::Gradient(event) = event else { return self };
|
||||||
@@ -420,7 +424,7 @@ impl Fsm for GradientToolFsmState {
|
|||||||
let gradient_state = if dragging {
|
let gradient_state = if dragging {
|
||||||
GradientToolFsmState::Drawing
|
GradientToolFsmState::Drawing
|
||||||
} else {
|
} else {
|
||||||
let selected_layer = document.click(input);
|
let selected_layer = document.click(input, viewport);
|
||||||
|
|
||||||
// Apply the gradient to the selected layer
|
// Apply the gradient to the selected layer
|
||||||
if let Some(layer) = selected_layer {
|
if let Some(layer) = selected_layer {
|
||||||
@@ -464,13 +468,13 @@ impl Fsm for GradientToolFsmState {
|
|||||||
GradientToolMessage::PointerOutsideViewport { constrain_axis }.into(),
|
GradientToolMessage::PointerOutsideViewport { constrain_axis }.into(),
|
||||||
GradientToolMessage::PointerMove { constrain_axis }.into(),
|
GradientToolMessage::PointerMove { constrain_axis }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
GradientToolFsmState::Drawing
|
GradientToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
(GradientToolFsmState::Drawing, GradientToolMessage::PointerOutsideViewport { .. }) => {
|
(GradientToolFsmState::Drawing, GradientToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) {
|
||||||
if let Some(selected_gradient) = &mut tool_data.selected_gradient {
|
if let Some(selected_gradient) = &mut tool_data.selected_gradient {
|
||||||
selected_gradient.transform.translation += shift;
|
selected_gradient.transform.translation += shift;
|
||||||
}
|
}
|
||||||
@@ -494,7 +498,7 @@ impl Fsm for GradientToolFsmState {
|
|||||||
let was_dragging = tool_data.selected_gradient.is_some();
|
let was_dragging = tool_data.selected_gradient.is_some();
|
||||||
|
|
||||||
if !was_dragging {
|
if !was_dragging {
|
||||||
if let Some(selected_layer) = document.click(input) {
|
if let Some(selected_layer) = document.click(input, viewport) {
|
||||||
if let Some(gradient) = get_gradient(selected_layer, &document.network_interface) {
|
if let Some(gradient) = get_gradient(selected_layer, &document.network_interface) {
|
||||||
tool_data.selected_gradient = Some(SelectedGradient::new(gradient, selected_layer, document));
|
tool_data.selected_gradient = Some(SelectedGradient::new(gradient, selected_layer, document));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -701,6 +701,7 @@ impl PathToolData {
|
|||||||
shape_editor: &mut ShapeState,
|
shape_editor: &mut ShapeState,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
extend_selection: bool,
|
extend_selection: bool,
|
||||||
lasso_select: bool,
|
lasso_select: bool,
|
||||||
@@ -871,7 +872,7 @@ impl PathToolData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If no other layers are selected and this is a single-click, then also select the layer (exception)
|
// If no other layers are selected and this is a single-click, then also select the layer (exception)
|
||||||
else if let Some(layer) = document.click(input) {
|
else if let Some(layer) = document.click(input, viewport) {
|
||||||
if shape_editor.selected_shape_state.is_empty() {
|
if shape_editor.selected_shape_state.is_empty() {
|
||||||
self.first_selected_with_single_click = true;
|
self.first_selected_with_single_click = true;
|
||||||
// This ensures we don't need to double click a second time to get the drill through to work
|
// This ensures we don't need to double click a second time to get the drill through to work
|
||||||
@@ -1109,8 +1110,9 @@ impl PathToolData {
|
|||||||
handle_position: DVec2,
|
handle_position: DVec2,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
) -> DVec2 {
|
) -> DVec2 {
|
||||||
let snap_data = SnapData::new(document, input);
|
let snap_data = SnapData::new(document, input, viewport);
|
||||||
let snap_point = SnapCandidatePoint::handle_neighbors(new_handle_position, [anchor_position]);
|
let snap_point = SnapCandidatePoint::handle_neighbors(new_handle_position, [anchor_position]);
|
||||||
|
|
||||||
let snap_result = match using_angle_constraints {
|
let snap_result = match using_angle_constraints {
|
||||||
@@ -1380,6 +1382,7 @@ impl PathToolData {
|
|||||||
shape_editor: &mut ShapeState,
|
shape_editor: &mut ShapeState,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) {
|
) {
|
||||||
// First check if selection is not just a single handle point
|
// First check if selection is not just a single handle point
|
||||||
@@ -1430,9 +1433,10 @@ impl PathToolData {
|
|||||||
handle_position,
|
handle_position,
|
||||||
document,
|
document,
|
||||||
input,
|
input,
|
||||||
|
viewport,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
shape_editor.snap(&mut self.snap_manager, &self.snap_cache, document, input, previous_mouse)
|
shape_editor.snap(&mut self.snap_manager, &self.snap_cache, document, input, viewport, previous_mouse)
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle_lengths = if equidistant { None } else { self.opposing_handle_lengths.take() };
|
let handle_lengths = if equidistant { None } else { self.opposing_handle_lengths.take() };
|
||||||
@@ -1538,7 +1542,13 @@ impl Fsm for PathToolFsmState {
|
|||||||
tool_options: &Self::ToolOptions,
|
tool_options: &Self::ToolOptions,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ToolActionMessageContext { document, input, shape_editor, .. } = tool_action_data;
|
let ToolActionMessageContext {
|
||||||
|
document,
|
||||||
|
input,
|
||||||
|
viewport,
|
||||||
|
shape_editor,
|
||||||
|
..
|
||||||
|
} = tool_action_data;
|
||||||
|
|
||||||
update_dynamic_hints(self, responses, shape_editor, document, tool_data, tool_options, input.mouse.position);
|
update_dynamic_hints(self, responses, shape_editor, document, tool_data, tool_options, input.mouse.position);
|
||||||
|
|
||||||
@@ -1939,13 +1949,13 @@ impl Fsm for PathToolFsmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Dragging(_) => {
|
Self::Dragging(_) => {
|
||||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
|
|
||||||
// Draw the snapping axis lines
|
// Draw the snapping axis lines
|
||||||
if tool_data.snapping_axis.is_some() {
|
if tool_data.snapping_axis.is_some() {
|
||||||
let Some(axis) = tool_data.snapping_axis else { return self };
|
let Some(axis) = tool_data.snapping_axis else { return self };
|
||||||
let origin = tool_data.drag_start_pos;
|
let origin = tool_data.drag_start_pos;
|
||||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
let viewport_diagonal = viewport.size().into_dvec2().length();
|
||||||
|
|
||||||
let faded = |color: &str| {
|
let faded = |color: &str| {
|
||||||
let mut color = graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
|
let mut color = graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
|
||||||
@@ -1996,6 +2006,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
shape_editor,
|
shape_editor,
|
||||||
document,
|
document,
|
||||||
input,
|
input,
|
||||||
|
viewport,
|
||||||
responses,
|
responses,
|
||||||
extend_selection,
|
extend_selection,
|
||||||
lasso_select,
|
lasso_select,
|
||||||
@@ -2056,7 +2067,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
PathToolFsmState::Drawing { selection_shape }
|
PathToolFsmState::Drawing { selection_shape }
|
||||||
}
|
}
|
||||||
@@ -2144,6 +2155,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
tool_action_data.shape_editor,
|
tool_action_data.shape_editor,
|
||||||
tool_action_data.document,
|
tool_action_data.document,
|
||||||
input,
|
input,
|
||||||
|
viewport,
|
||||||
responses,
|
responses,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2173,7 +2185,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
PathToolFsmState::Dragging(tool_data.dragging_state)
|
PathToolFsmState::Dragging(tool_data.dragging_state)
|
||||||
}
|
}
|
||||||
@@ -2203,7 +2215,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
|
|
||||||
// When moving the cursor around we want to update the hovered layers
|
// When moving the cursor around we want to update the hovered layers
|
||||||
let new_hovered_layers: Vec<LayerNodeIdentifier> = document
|
let new_hovered_layers: Vec<LayerNodeIdentifier> = document
|
||||||
.click_list_no_parents(input)
|
.click_list_no_parents(input, viewport)
|
||||||
.filter(|&layer| {
|
.filter(|&layer| {
|
||||||
// Filter out artboards and parent holders, and already selected layers
|
// Filter out artboards and parent holders, and already selected layers
|
||||||
!document.network_interface.is_artboard(&layer.to_node(), &[])
|
!document.network_interface.is_artboard(&layer.to_node(), &[])
|
||||||
@@ -2220,7 +2232,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
}
|
}
|
||||||
(PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => {
|
(PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(offset) = tool_data.auto_panning.shift_viewport(input, responses) {
|
if let Some(offset) = tool_data.auto_panning.shift_viewport(input, viewport, responses) {
|
||||||
tool_data.drag_start_pos += offset;
|
tool_data.drag_start_pos += offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2228,7 +2240,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
}
|
}
|
||||||
(PathToolFsmState::Dragging(dragging_state), PathToolMessage::PointerOutsideViewport { .. }) => {
|
(PathToolFsmState::Dragging(dragging_state), PathToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(offset) = tool_data.auto_panning.shift_viewport(input, responses) {
|
if let Some(offset) = tool_data.auto_panning.shift_viewport(input, viewport, responses) {
|
||||||
tool_data.drag_start_pos += offset;
|
tool_data.drag_start_pos += offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2388,7 +2400,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
|
|
||||||
if tool_data.drag_start_pos.distance(previous_mouse) < 1e-8 {
|
if tool_data.drag_start_pos.distance(previous_mouse) < 1e-8 {
|
||||||
// Clicked inside or outside the shape then deselect all of the points/segments
|
// Clicked inside or outside the shape then deselect all of the points/segments
|
||||||
if document.click(input).is_some() && tool_data.stored_selection.is_none() {
|
if document.click(input, viewport).is_some() && tool_data.stored_selection.is_none() {
|
||||||
tool_data.stored_selection = Some(shape_editor.selected_shape_state.clone());
|
tool_data.stored_selection = Some(shape_editor.selected_shape_state.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2941,7 +2953,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
let nearest_point = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD);
|
let nearest_point = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD);
|
||||||
|
|
||||||
let mut get_drill_through_layer = || -> Option<LayerNodeIdentifier> {
|
let mut get_drill_through_layer = || -> Option<LayerNodeIdentifier> {
|
||||||
let drill_through_layers = document.click_list_no_parents(input).collect::<Vec<LayerNodeIdentifier>>();
|
let drill_through_layers = document.click_list_no_parents(input, viewport).collect::<Vec<LayerNodeIdentifier>>();
|
||||||
if drill_through_layers.is_empty() {
|
if drill_through_layers.is_empty() {
|
||||||
tool_data.reset_drill_through_cycle();
|
tool_data.reset_drill_through_cycle();
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -612,7 +612,7 @@ impl PenToolData {
|
|||||||
self.handle_end = None;
|
self.handle_end = None;
|
||||||
self.handle_mode = HandleMode::Free;
|
self.handle_mode = HandleMode::Free;
|
||||||
|
|
||||||
self.store_clicked_endpoint(document, &transform, snap_data.input, preferences);
|
self.store_clicked_endpoint(document, &transform, snap_data.input, snap_data.viewport, preferences);
|
||||||
|
|
||||||
if self.modifiers.lock_angle {
|
if self.modifiers.lock_angle {
|
||||||
self.set_lock_angle(&vector, id, self.prior_segment);
|
self.set_lock_angle(&vector, id, self.prior_segment);
|
||||||
@@ -668,7 +668,7 @@ impl PenToolData {
|
|||||||
self.handle_end_offset = None;
|
self.handle_end_offset = None;
|
||||||
self.path_closed = true;
|
self.path_closed = true;
|
||||||
self.next_handle_start = self.next_point;
|
self.next_handle_start = self.next_point;
|
||||||
self.store_clicked_endpoint(document, transform, snap_data.input, preferences);
|
self.store_clicked_endpoint(document, transform, snap_data.input, snap_data.viewport, preferences);
|
||||||
self.handle_mode = HandleMode::Free;
|
self.handle_mode = HandleMode::Free;
|
||||||
if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) {
|
if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) {
|
||||||
self.set_lock_angle(vector, prior_endpoint, self.prior_segment);
|
self.set_lock_angle(vector, prior_endpoint, self.prior_segment);
|
||||||
@@ -802,13 +802,14 @@ impl PenToolData {
|
|||||||
mouse: &DVec2,
|
mouse: &DVec2,
|
||||||
vector: &Vector,
|
vector: &Vector,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
) -> Option<DVec2> {
|
) -> Option<DVec2> {
|
||||||
let reference_handle = if self.path_closed { TargetHandle::PreviewInHandle } else { TargetHandle::FuturePreviewOutHandle };
|
let reference_handle = if self.path_closed { TargetHandle::PreviewInHandle } else { TargetHandle::FuturePreviewOutHandle };
|
||||||
let end_handle = self.get_opposite_handle_type(reference_handle, vector);
|
let end_handle = self.get_opposite_handle_type(reference_handle, vector);
|
||||||
let end_handle_pos = self.target_handle_position(end_handle, vector);
|
let end_handle_pos = self.target_handle_position(end_handle, vector);
|
||||||
let ref_pos = self.target_handle_position(reference_handle, vector)?;
|
let ref_pos = self.target_handle_position(reference_handle, vector)?;
|
||||||
let snap = &mut self.snap_manager;
|
let snap = &mut self.snap_manager;
|
||||||
let snap_data = SnapData::new_snap_cache(snap_data.document, input, &self.snap_cache);
|
let snap_data = SnapData::new_snap_cache(snap_data.document, input, viewport, &self.snap_cache);
|
||||||
|
|
||||||
let handle_start_offset = self.handle_start_offset.unwrap_or(DVec2::ZERO);
|
let handle_start_offset = self.handle_start_offset.unwrap_or(DVec2::ZERO);
|
||||||
let document_pos = viewport_to_document.transform_point2(*mouse + handle_start_offset);
|
let document_pos = viewport_to_document.transform_point2(*mouse + handle_start_offset);
|
||||||
@@ -964,6 +965,7 @@ impl PenToolData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn drag_handle(
|
fn drag_handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
snap_data: SnapData,
|
snap_data: SnapData,
|
||||||
@@ -972,6 +974,7 @@ impl PenToolData {
|
|||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
layer: Option<LayerNodeIdentifier>,
|
layer: Option<LayerNodeIdentifier>,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
) -> Option<PenToolFsmState> {
|
) -> Option<PenToolFsmState> {
|
||||||
let colinear = (self.handle_mode == HandleMode::ColinearEquidistant && self.modifiers.break_handle) || (self.handle_mode == HandleMode::ColinearLocked && !self.modifiers.break_handle);
|
let colinear = (self.handle_mode == HandleMode::ColinearEquidistant && self.modifiers.break_handle) || (self.handle_mode == HandleMode::ColinearLocked && !self.modifiers.break_handle);
|
||||||
let document = snap_data.document;
|
let document = snap_data.document;
|
||||||
@@ -982,7 +985,7 @@ impl PenToolData {
|
|||||||
|
|
||||||
// Handles pressing Space to drag anchor and its handles
|
// Handles pressing Space to drag anchor and its handles
|
||||||
if self.modifiers.move_anchor_with_handles {
|
if self.modifiers.move_anchor_with_handles {
|
||||||
let Some(delta) = self.space_anchor_handle_snap(&viewport_to_document, &transform, &snap_data, &mouse, &vector, input) else {
|
let Some(delta) = self.space_anchor_handle_snap(&viewport_to_document, &transform, &snap_data, &mouse, &vector, input, viewport) else {
|
||||||
return Some(PenToolFsmState::DraggingHandle(self.handle_mode));
|
return Some(PenToolFsmState::DraggingHandle(self.handle_mode));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1221,6 +1224,7 @@ impl PenToolData {
|
|||||||
&mut self,
|
&mut self,
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
tool_options: &PenOptions,
|
tool_options: &PenOptions,
|
||||||
append: bool,
|
append: bool,
|
||||||
@@ -1228,21 +1232,21 @@ impl PenToolData {
|
|||||||
shape_editor: &mut ShapeState,
|
shape_editor: &mut ShapeState,
|
||||||
) {
|
) {
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||||
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||||
self.handle_type = TargetHandle::FuturePreviewOutHandle;
|
self.handle_type = TargetHandle::FuturePreviewOutHandle;
|
||||||
|
|
||||||
let selected_nodes = document.network_interface.selected_nodes();
|
let selected_nodes = document.network_interface.selected_nodes();
|
||||||
self.handle_end = None;
|
self.handle_end = None;
|
||||||
|
|
||||||
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
||||||
let extension_choice = should_extend(document, viewport, tolerance, selected_nodes.selected_layers(document.metadata()), preferences);
|
let extension_choice = should_extend(document, viewport_vec, tolerance, selected_nodes.selected_layers(document.metadata()), preferences);
|
||||||
if let Some((layer, point, position)) = extension_choice {
|
if let Some((layer, point, position)) = extension_choice {
|
||||||
self.current_layer = Some(layer);
|
self.current_layer = Some(layer);
|
||||||
self.extend_existing_path(document, layer, point, position);
|
self.extend_existing_path(document, layer, point, position);
|
||||||
return;
|
return;
|
||||||
} else if preferences.vector_meshes {
|
} else if preferences.vector_meshes {
|
||||||
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) {
|
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance) {
|
||||||
let (point, segments) = closest_segment.adjusted_insert(responses);
|
let (point, segments) = closest_segment.adjusted_insert(responses);
|
||||||
let layer = closest_segment.layer();
|
let layer = closest_segment.layer();
|
||||||
let position = closest_segment.closest_point_document();
|
let position = closest_segment.closest_point_document();
|
||||||
@@ -1258,7 +1262,7 @@ impl PenToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if append {
|
if append {
|
||||||
if let Some((layer, point, _)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) {
|
if let Some((layer, point, _)) = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences) {
|
||||||
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
|
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
|
||||||
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
|
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
|
||||||
|
|
||||||
@@ -1271,12 +1275,12 @@ impl PenToolData {
|
|||||||
let existing_layer = selected_layers_except_artboards.next().filter(|_| selected_layers_except_artboards.next().is_none());
|
let existing_layer = selected_layers_except_artboards.next().filter(|_| selected_layers_except_artboards.next().is_none());
|
||||||
if let Some(layer) = existing_layer {
|
if let Some(layer) = existing_layer {
|
||||||
// Add point to existing layer
|
// Add point to existing layer
|
||||||
responses.add(PenToolMessage::AddPointLayerPosition { layer, viewport });
|
responses.add(PenToolMessage::AddPointLayerPosition { layer, viewport: viewport_vec });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((layer, point, _position)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) {
|
if let Some((layer, point, _position)) = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences) {
|
||||||
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
|
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
|
||||||
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
|
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
|
||||||
self.handle_mode = HandleMode::Free;
|
self.handle_mode = HandleMode::Free;
|
||||||
@@ -1290,7 +1294,7 @@ impl PenToolData {
|
|||||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||||
let nodes = vec![(NodeId(0), node_type.default_node_template())];
|
let nodes = vec![(NodeId(0), node_type.default_node_template())];
|
||||||
|
|
||||||
let parent = document.new_layer_bounding_artboard(input);
|
let parent = document.new_layer_bounding_artboard(input, viewport);
|
||||||
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses);
|
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses);
|
||||||
self.current_layer = Some(layer);
|
self.current_layer = Some(layer);
|
||||||
tool_options.fill.apply_fill(layer, responses);
|
tool_options.fill.apply_fill(layer, responses);
|
||||||
@@ -1301,7 +1305,7 @@ impl PenToolData {
|
|||||||
|
|
||||||
// It is necessary to defer this until the transform of the layer can be accurately computed (quite hacky)
|
// It is necessary to defer this until the transform of the layer can be accurately computed (quite hacky)
|
||||||
responses.add(DeferMessage::AfterGraphRun {
|
responses.add(DeferMessage::AfterGraphRun {
|
||||||
messages: vec![PenToolMessage::AddPointLayerPosition { layer, viewport }.into()],
|
messages: vec![PenToolMessage::AddPointLayerPosition { layer, viewport: viewport_vec }.into()],
|
||||||
});
|
});
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
@@ -1360,14 +1364,21 @@ impl PenToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stores the segment and point ID of the clicked endpoint
|
// Stores the segment and point ID of the clicked endpoint
|
||||||
fn store_clicked_endpoint(&mut self, document: &DocumentMessageHandler, transform: &DAffine2, input: &InputPreprocessorMessageHandler, preferences: &PreferencesMessageHandler) {
|
fn store_clicked_endpoint(
|
||||||
|
&mut self,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
transform: &DAffine2,
|
||||||
|
input: &InputPreprocessorMessageHandler,
|
||||||
|
viewport: &ViewportMessageHandler,
|
||||||
|
preferences: &PreferencesMessageHandler,
|
||||||
|
) {
|
||||||
let mut manipulators = HashMap::with_hasher(NoHashBuilder);
|
let mut manipulators = HashMap::with_hasher(NoHashBuilder);
|
||||||
let mut unselected = Vec::new();
|
let mut unselected = Vec::new();
|
||||||
let mut layer_manipulators = HashSet::with_hasher(NoHashBuilder);
|
let mut layer_manipulators = HashSet::with_hasher(NoHashBuilder);
|
||||||
|
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||||
|
|
||||||
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||||
|
|
||||||
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
||||||
@@ -1456,6 +1467,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
input,
|
input,
|
||||||
shape_editor,
|
shape_editor,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
..
|
..
|
||||||
} = tool_action_data;
|
} = tool_action_data;
|
||||||
let selected_nodes = document.network_interface.selected_nodes();
|
let selected_nodes = document.network_interface.selected_nodes();
|
||||||
@@ -1613,19 +1625,19 @@ impl Fsm for PenToolFsmState {
|
|||||||
// If not check if there is a closest segment within threshold, if yes then draw overlay
|
// If not check if there is a closest segment within threshold, if yes then draw overlay
|
||||||
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||||
|
|
||||||
let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
|
let close_to_point = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
|
||||||
if preferences.vector_meshes && !close_to_point {
|
if preferences.vector_meshes && !close_to_point {
|
||||||
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) {
|
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance) {
|
||||||
let pos = closest_segment.closest_point_to_viewport();
|
let pos = closest_segment.closest_point_to_viewport();
|
||||||
let perp = closest_segment.calculate_perp(document);
|
let perp = closest_segment.calculate_perp(document);
|
||||||
overlay_context.manipulator_anchor(pos, true, None);
|
overlay_context.manipulator_anchor(pos, true, None);
|
||||||
overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None);
|
overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(_, PenToolMessage::Overlays { context: mut overlay_context }) => {
|
(_, PenToolMessage::Overlays { context: mut overlay_context }) => {
|
||||||
@@ -1743,7 +1755,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
if self == PenToolFsmState::PlacingAnchor && preferences.vector_meshes {
|
if self == PenToolFsmState::PlacingAnchor && preferences.vector_meshes {
|
||||||
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||||
let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
|
let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
|
||||||
if !close_to_point {
|
if !close_to_point {
|
||||||
@@ -1805,7 +1817,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw the overlays that visualize current snapping
|
// Draw the overlays that visualize current snapping
|
||||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -1822,8 +1834,8 @@ impl Fsm for PenToolFsmState {
|
|||||||
// Get the closest point and the segment it is on
|
// Get the closest point and the segment it is on
|
||||||
let append = input.keyboard.key(append_to_selected);
|
let append = input.keyboard.key(append_to_selected);
|
||||||
|
|
||||||
tool_data.store_clicked_endpoint(document, &transform, input, preferences);
|
tool_data.store_clicked_endpoint(document, &transform, input, viewport, preferences);
|
||||||
tool_data.create_initial_point(document, input, responses, tool_options, append, preferences, shape_editor);
|
tool_data.create_initial_point(document, input, viewport, responses, tool_options, append, preferences, shape_editor);
|
||||||
|
|
||||||
// Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle
|
// Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle
|
||||||
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
|
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
|
||||||
@@ -1839,16 +1851,16 @@ impl Fsm for PenToolFsmState {
|
|||||||
}
|
}
|
||||||
(PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected }) => {
|
(PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected }) => {
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||||
|
|
||||||
// Early return if the buffer was started and this message is being run again after the buffer (so that place_anchor updates the state with the newly merged vector)
|
// Early return if the buffer was started and this message is being run again after the buffer (so that place_anchor updates the state with the newly merged vector)
|
||||||
if tool_data.buffering_merged_vector {
|
if tool_data.buffering_merged_vector {
|
||||||
if let Some(layer) = layer {
|
if let Some(layer) = layer {
|
||||||
tool_data.buffering_merged_vector = false;
|
tool_data.buffering_merged_vector = false;
|
||||||
tool_data.handle_mode = HandleMode::ColinearLocked;
|
tool_data.handle_mode = HandleMode::ColinearLocked;
|
||||||
tool_data.bend_from_previous_point(SnapData::new(document, input), transform, layer, preferences, shape_editor, responses);
|
tool_data.bend_from_previous_point(SnapData::new(document, input, viewport), transform, layer, preferences, shape_editor, responses);
|
||||||
tool_data.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, responses);
|
tool_data.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses);
|
||||||
}
|
}
|
||||||
tool_data.buffering_merged_vector = false;
|
tool_data.buffering_merged_vector = false;
|
||||||
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
|
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
|
||||||
@@ -1863,7 +1875,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
let layers = LayerNodeIdentifier::ROOT_PARENT
|
let layers = LayerNodeIdentifier::ROOT_PARENT
|
||||||
.descendants(document.metadata())
|
.descendants(document.metadata())
|
||||||
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||||
if let Some((other_layer, _, _)) = should_extend(document, viewport, crate::consts::SNAP_POINT_TOLERANCE, layers, preferences) {
|
if let Some((other_layer, _, _)) = should_extend(document, viewport_vec, crate::consts::SNAP_POINT_TOLERANCE, layers, preferences) {
|
||||||
let selected_nodes = document.network_interface.selected_nodes();
|
let selected_nodes = document.network_interface.selected_nodes();
|
||||||
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||||
if let Some(current_layer) = selected_layers
|
if let Some(current_layer) = selected_layers
|
||||||
@@ -1893,7 +1905,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
(PenToolFsmState::DraggingHandle(_), PenToolMessage::DragStop) => {
|
(PenToolFsmState::DraggingHandle(_), PenToolMessage::DragStop) => {
|
||||||
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
|
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
|
||||||
tool_data
|
tool_data
|
||||||
.finish_placing_handle(SnapData::new(document, input), transform, preferences, responses)
|
.finish_placing_handle(SnapData::new(document, input, viewport), transform, preferences, responses)
|
||||||
.unwrap_or(PenToolFsmState::PlacingAnchor)
|
.unwrap_or(PenToolFsmState::PlacingAnchor)
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
@@ -1914,7 +1926,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||||
};
|
};
|
||||||
|
|
||||||
let snap_data = SnapData::new(document, input);
|
let snap_data = SnapData::new(document, input, viewport);
|
||||||
if tool_data.modifiers.colinear && !tool_data.toggle_colinear_debounce {
|
if tool_data.modifiers.colinear && !tool_data.toggle_colinear_debounce {
|
||||||
tool_data.handle_mode = match tool_data.handle_mode {
|
tool_data.handle_mode = match tool_data.handle_mode {
|
||||||
HandleMode::Free => {
|
HandleMode::Free => {
|
||||||
@@ -1962,7 +1974,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let state = tool_data
|
let state = tool_data
|
||||||
.drag_handle(snap_data, transform, input.mouse.position, responses, layer, input)
|
.drag_handle(snap_data, transform, input.mouse.position, responses, layer, input, viewport)
|
||||||
.unwrap_or(PenToolFsmState::Ready);
|
.unwrap_or(PenToolFsmState::Ready);
|
||||||
|
|
||||||
if tool_data.handle_swapped {
|
if tool_data.handle_swapped {
|
||||||
@@ -1988,7 +2000,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
@@ -2012,7 +2024,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||||
};
|
};
|
||||||
let state = tool_data
|
let state = tool_data
|
||||||
.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, responses)
|
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses)
|
||||||
.unwrap_or(PenToolFsmState::Ready);
|
.unwrap_or(PenToolFsmState::Ready);
|
||||||
|
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
@@ -2034,7 +2046,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
@@ -2063,13 +2075,13 @@ impl Fsm for PenToolFsmState {
|
|||||||
colinear: input.keyboard.key(colinear),
|
colinear: input.keyboard.key(colinear),
|
||||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||||
};
|
};
|
||||||
tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position);
|
tool_data.snap_manager.preview_draw(&SnapData::new(document, input, viewport), input.mouse.position);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(PenToolFsmState::DraggingHandle(mode), PenToolMessage::PointerOutsideViewport { .. }) => {
|
(PenToolFsmState::DraggingHandle(mode), PenToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
PenToolFsmState::DraggingHandle(mode)
|
PenToolFsmState::DraggingHandle(mode)
|
||||||
}
|
}
|
||||||
@@ -2078,7 +2090,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
PenToolFsmState::PlacingAnchor
|
PenToolFsmState::PlacingAnchor
|
||||||
}
|
}
|
||||||
@@ -2119,7 +2131,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
// Confirm to end path
|
// Confirm to end path
|
||||||
if let Some((vector, layer)) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)).zip(layer) {
|
if let Some((vector, layer)) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)).zip(layer) {
|
||||||
let single_point_in_layer = vector.point_domain.ids().len() == 1;
|
let single_point_in_layer = vector.point_domain.ids().len() == 1;
|
||||||
tool_data.finish_placing_handle(SnapData::new(document, input), transform, preferences, responses);
|
tool_data.finish_placing_handle(SnapData::new(document, input, viewport), transform, preferences, responses);
|
||||||
let latest_points = tool_data.latest_points.len() == 1;
|
let latest_points = tool_data.latest_points.len() == 1;
|
||||||
|
|
||||||
if latest_points && single_point_in_layer {
|
if latest_points && single_point_in_layer {
|
||||||
@@ -2166,7 +2178,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
PenToolFsmState::Ready
|
PenToolFsmState::Ready
|
||||||
} else {
|
} else {
|
||||||
tool_data
|
tool_data
|
||||||
.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, responses)
|
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses)
|
||||||
.unwrap_or(PenToolFsmState::Ready)
|
.unwrap_or(PenToolFsmState::Ready)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2197,7 +2209,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
if tool_data.point_index > 0 {
|
if tool_data.point_index > 0 {
|
||||||
tool_data.point_index -= 1;
|
tool_data.point_index -= 1;
|
||||||
tool_data
|
tool_data
|
||||||
.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, responses)
|
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses)
|
||||||
.unwrap_or(PenToolFsmState::PlacingAnchor)
|
.unwrap_or(PenToolFsmState::PlacingAnchor)
|
||||||
} else {
|
} else {
|
||||||
responses.add(PenToolMessage::Abort);
|
responses.add(PenToolMessage::Abort);
|
||||||
@@ -2206,7 +2218,7 @@ impl Fsm for PenToolFsmState {
|
|||||||
}
|
}
|
||||||
(_, PenToolMessage::Redo) => {
|
(_, PenToolMessage::Redo) => {
|
||||||
tool_data.point_index = (tool_data.point_index + 1).min(tool_data.latest_points.len().saturating_sub(1));
|
tool_data.point_index = (tool_data.point_index + 1).min(tool_data.latest_points.len().saturating_sub(1));
|
||||||
tool_data.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, responses);
|
tool_data.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses);
|
||||||
match tool_data.point_index {
|
match tool_data.point_index {
|
||||||
0 => PenToolFsmState::Ready,
|
0 => PenToolFsmState::Ready,
|
||||||
_ => PenToolFsmState::PlacingAnchor,
|
_ => PenToolFsmState::PlacingAnchor,
|
||||||
|
|||||||
@@ -419,11 +419,11 @@ struct SelectToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SelectToolData {
|
impl SelectToolData {
|
||||||
fn get_snap_candidates(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
|
fn get_snap_candidates(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) {
|
||||||
self.snap_candidates.clear();
|
self.snap_candidates.clear();
|
||||||
for &layer in &self.layers_dragging {
|
for &layer in &self.layers_dragging {
|
||||||
if (self.snap_candidates.len() as f64) < document.snapping_state.tolerance {
|
if (self.snap_candidates.len() as f64) < document.snapping_state.tolerance {
|
||||||
snapping::get_layer_snap_points(layer, &SnapData::new(document, input), &mut self.snap_candidates);
|
snapping::get_layer_snap_points(layer, &SnapData::new(document, input, viewport), &mut self.snap_candidates);
|
||||||
}
|
}
|
||||||
if let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) {
|
if let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) {
|
||||||
let quad = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
let quad = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
||||||
@@ -463,20 +463,20 @@ impl SelectToolData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn intersect_lasso_no_artboards(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> Vec<LayerNodeIdentifier> {
|
pub fn intersect_lasso_no_artboards(&self, document: &DocumentMessageHandler, viewport: &ViewportMessageHandler) -> Vec<LayerNodeIdentifier> {
|
||||||
if self.lasso_polygon.len() < 2 {
|
if self.lasso_polygon.len() < 2 {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let polygon = Subpath::from_anchors_linear(self.lasso_polygon.clone(), true);
|
let polygon = Subpath::from_anchors_linear(self.lasso_polygon.clone(), true);
|
||||||
document.intersect_polygon_no_artboards(polygon, input).collect()
|
document.intersect_polygon_no_artboards(polygon, viewport).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_layer_inside_lasso_polygon(&self, layer: &LayerNodeIdentifier, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> bool {
|
pub fn is_layer_inside_lasso_polygon(&self, layer: &LayerNodeIdentifier, document: &DocumentMessageHandler, viewport: &ViewportMessageHandler) -> bool {
|
||||||
if self.lasso_polygon.len() < 2 {
|
if self.lasso_polygon.len() < 2 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let polygon = Subpath::from_anchors_linear(self.lasso_polygon.clone(), true);
|
let polygon = Subpath::from_anchors_linear(self.lasso_polygon.clone(), true);
|
||||||
document.is_layer_fully_inside_polygon(layer, input, polygon)
|
document.is_layer_fully_inside_polygon(layer, viewport, polygon)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duplicates the currently dragging layers. Called when Alt is pressed and the layers have not yet been duplicated.
|
/// Duplicates the currently dragging layers. Called when Alt is pressed and the layers have not yet been duplicated.
|
||||||
@@ -598,12 +598,18 @@ impl Fsm for SelectToolFsmState {
|
|||||||
type ToolOptions = ();
|
type ToolOptions = ();
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||||
let ToolActionMessageContext { document, input, font_cache, .. } = tool_action_data;
|
let ToolActionMessageContext {
|
||||||
|
document,
|
||||||
|
input,
|
||||||
|
viewport,
|
||||||
|
font_cache,
|
||||||
|
..
|
||||||
|
} = tool_action_data;
|
||||||
|
|
||||||
let ToolMessage::Select(event) = event else { return self };
|
let ToolMessage::Select(event) = event else { return self };
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(_, SelectToolMessage::Overlays { context: mut overlay_context }) => {
|
(_, SelectToolMessage::Overlays { context: mut overlay_context }) => {
|
||||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
|
|
||||||
let selected_layers_count = document.network_interface.selected_nodes().selected_unlocked_layers(&document.network_interface).count();
|
let selected_layers_count = document.network_interface.selected_nodes().selected_unlocked_layers(&document.network_interface).count();
|
||||||
tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count;
|
tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count;
|
||||||
@@ -661,7 +667,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
if !matches!(self, Self::Drawing { .. }) && !input.keyboard.get(Key::MouseMiddle as usize) {
|
if !matches!(self, Self::Drawing { .. }) && !input.keyboard.get(Key::MouseMiddle as usize) {
|
||||||
// Get the layer the user is hovering over
|
// Get the layer the user is hovering over
|
||||||
// Artboards are included since they're needed for quick measurement, but will be filtered out for selection later on
|
// Artboards are included since they're needed for quick measurement, but will be filtered out for selection later on
|
||||||
let click = document.click_list_with_artboards(input).last();
|
let click = document.click_list_with_artboards(input, viewport).last();
|
||||||
let not_selected_click = click.filter(|&hovered_layer| !document.network_interface.selected_nodes().selected_layers_contains(hovered_layer, document.metadata()));
|
let not_selected_click = click.filter(|&hovered_layer| !document.network_interface.selected_nodes().selected_layers_contains(hovered_layer, document.metadata()));
|
||||||
if let Some(layer) = not_selected_click {
|
if let Some(layer) = not_selected_click {
|
||||||
if overlay_context.visibility_settings.hover_outline() && !document.network_interface.is_artboard(&layer.to_node(), &[]) {
|
if overlay_context.visibility_settings.hover_outline() && !document.network_interface.is_artboard(&layer.to_node(), &[]) {
|
||||||
@@ -896,7 +902,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
let viewport_diagonal = viewport.size().into_dvec2().length();
|
||||||
|
|
||||||
let color = if !hover {
|
let color = if !hover {
|
||||||
color
|
color
|
||||||
@@ -918,7 +924,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
|
|
||||||
let extension = tool_data.drag_current - tool_data.drag_start;
|
let extension = tool_data.drag_current - tool_data.drag_start;
|
||||||
let origin = compass_center - extension;
|
let origin = compass_center - extension;
|
||||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
let viewport_diagonal = viewport.size().into_dvec2().length();
|
||||||
|
|
||||||
let edge = DVec2::from_angle(snapped_angle).normalize_or(DVec2::X) * viewport_diagonal;
|
let edge = DVec2::from_angle(snapped_angle).normalize_or(DVec2::X) * viewport_diagonal;
|
||||||
let perp = edge.perp();
|
let perp = edge.perp();
|
||||||
@@ -949,13 +955,13 @@ impl Fsm for SelectToolFsmState {
|
|||||||
|
|
||||||
// Draw outline visualizations on the layers to be selected
|
// Draw outline visualizations on the layers to be selected
|
||||||
let intersected_layers = match selection_shape {
|
let intersected_layers = match selection_shape {
|
||||||
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, input).collect(),
|
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, viewport).collect(),
|
||||||
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, input),
|
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, viewport),
|
||||||
};
|
};
|
||||||
let layers_to_outline = intersected_layers.into_iter().filter(|layer| match current_selection_mode {
|
let layers_to_outline = intersected_layers.into_iter().filter(|layer| match current_selection_mode {
|
||||||
SelectionMode::Enclosed => match selection_shape {
|
SelectionMode::Enclosed => match selection_shape {
|
||||||
SelectionShapeType::Box => document.is_layer_fully_inside(layer, quad),
|
SelectionShapeType::Box => document.is_layer_fully_inside(layer, quad),
|
||||||
SelectionShapeType::Lasso => tool_data.is_layer_inside_lasso_polygon(layer, document, input),
|
SelectionShapeType::Lasso => tool_data.is_layer_inside_lasso_polygon(layer, document, viewport),
|
||||||
},
|
},
|
||||||
SelectionMode::Touched => match tool_data.nested_selection_behavior {
|
SelectionMode::Touched => match tool_data.nested_selection_behavior {
|
||||||
NestedSelectionBehavior::Deepest => !layer.has_children(document.metadata()),
|
NestedSelectionBehavior::Deepest => !layer.has_children(document.metadata()),
|
||||||
@@ -1008,7 +1014,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
(_, SelectToolMessage::EditLayerExec) => {
|
(_, SelectToolMessage::EditLayerExec) => {
|
||||||
if let Some(intersect) = document.click(input) {
|
if let Some(intersect) = document.click(input, viewport) {
|
||||||
match tool_data.nested_selection_behavior {
|
match tool_data.nested_selection_behavior {
|
||||||
NestedSelectionBehavior::Shallowest => edit_layer_shallowest_manipulation(document, intersect, responses),
|
NestedSelectionBehavior::Shallowest => edit_layer_shallowest_manipulation(document, intersect, responses),
|
||||||
NestedSelectionBehavior::Deepest => edit_layer_deepest_manipulation(intersect, &document.network_interface, responses),
|
NestedSelectionBehavior::Deepest => edit_layer_deepest_manipulation(intersect, &document.network_interface, responses),
|
||||||
@@ -1031,7 +1037,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
tool_data.selection_mode = None;
|
tool_data.selection_mode = None;
|
||||||
|
|
||||||
let mut selected: Vec<_> = document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface).collect();
|
let mut selected: Vec<_> = document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface).collect();
|
||||||
let intersection_list = document.click_list(input).collect::<Vec<_>>();
|
let intersection_list = document.click_list(input, viewport).collect::<Vec<_>>();
|
||||||
let intersection = document.find_deepest(&intersection_list);
|
let intersection = document.find_deepest(&intersection_list);
|
||||||
|
|
||||||
let position = tool_data.pivot_gizmo().position(document);
|
let position = tool_data.pivot_gizmo().position(document);
|
||||||
@@ -1064,10 +1070,10 @@ impl Fsm for SelectToolFsmState {
|
|||||||
}
|
}
|
||||||
// Dragging one (or two, forming a corner) of the transform cage bounding box edges
|
// Dragging one (or two, forming a corner) of the transform cage bounding box edges
|
||||||
else if resize {
|
else if resize {
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input, viewport);
|
||||||
SelectToolFsmState::ResizingBounds
|
SelectToolFsmState::ResizingBounds
|
||||||
} else if skew {
|
} else if skew {
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input, viewport);
|
||||||
SelectToolFsmState::SkewingBounds { skew: Key::Control }
|
SelectToolFsmState::SkewingBounds { skew: Key::Control }
|
||||||
}
|
}
|
||||||
// Dragging the selected layers around to transform them
|
// Dragging the selected layers around to transform them
|
||||||
@@ -1081,7 +1087,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tool_data.layers_dragging = selected;
|
tool_data.layers_dragging = selected;
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input, viewport);
|
||||||
let (axis, using_compass) = {
|
let (axis, using_compass) = {
|
||||||
let axis_state = compass_rose_state.axis_type().filter(|_| can_grab_compass_rose);
|
let axis_state = compass_rose_state.axis_type().filter(|_| can_grab_compass_rose);
|
||||||
(axis_state.unwrap_or_default(), axis_state.is_some())
|
(axis_state.unwrap_or_default(), axis_state.is_some())
|
||||||
@@ -1124,7 +1130,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
NestedSelectionBehavior::Shallowest if !input.keyboard.key(select_deepest) => drag_shallowest_manipulation(responses, selected, tool_data, document, false, extend),
|
NestedSelectionBehavior::Shallowest if !input.keyboard.key(select_deepest) => drag_shallowest_manipulation(responses, selected, tool_data, document, false, extend),
|
||||||
_ => drag_deepest_manipulation(responses, selected, tool_data, document, false),
|
_ => drag_deepest_manipulation(responses, selected, tool_data, document, false),
|
||||||
}
|
}
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input, viewport);
|
||||||
|
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
@@ -1177,7 +1183,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
let layers_exist = tool_data.layers_dragging.iter().all(|&layer| document.metadata().click_targets(layer).is_some());
|
let layers_exist = tool_data.layers_dragging.iter().all(|&layer| document.metadata().click_targets(layer).is_some());
|
||||||
let ignore = tool_data.non_duplicated_layers.as_ref().filter(|_| !layers_exist).unwrap_or(&tool_data.layers_dragging);
|
let ignore = tool_data.non_duplicated_layers.as_ref().filter(|_| !layers_exist).unwrap_or(&tool_data.layers_dragging);
|
||||||
|
|
||||||
let snap_data = SnapData::ignore(document, input, ignore);
|
let snap_data = SnapData::ignore(document, input, viewport, ignore);
|
||||||
let (start, current) = (tool_data.drag_start, tool_data.drag_current);
|
let (start, current) = (tool_data.drag_start, tool_data.drag_current);
|
||||||
let e0 = tool_data
|
let e0 = tool_data
|
||||||
.bounding_box_manager
|
.bounding_box_manager
|
||||||
@@ -1208,7 +1214,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
||||||
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
SelectToolFsmState::Dragging {
|
SelectToolFsmState::Dragging {
|
||||||
axis,
|
axis,
|
||||||
@@ -1228,6 +1234,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
&mut tool_data.snap_manager,
|
&mut tool_data.snap_manager,
|
||||||
&mut tool_data.snap_candidates,
|
&mut tool_data.snap_candidates,
|
||||||
input,
|
input,
|
||||||
|
viewport,
|
||||||
input.keyboard.key(modifier_keys.center),
|
input.keyboard.key(modifier_keys.center),
|
||||||
input.keyboard.key(modifier_keys.axis_align),
|
input.keyboard.key(modifier_keys.axis_align),
|
||||||
ToolType::Select,
|
ToolType::Select,
|
||||||
@@ -1236,7 +1243,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
||||||
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
}
|
}
|
||||||
SelectToolFsmState::ResizingBounds
|
SelectToolFsmState::ResizingBounds
|
||||||
}
|
}
|
||||||
@@ -1283,7 +1290,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
||||||
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
SelectToolFsmState::DraggingPivot
|
SelectToolFsmState::DraggingPivot
|
||||||
}
|
}
|
||||||
@@ -1304,7 +1311,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.into(),
|
||||||
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
SelectToolMessage::PointerMove { modifier_keys }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
SelectToolFsmState::Drawing { selection_shape, has_drawn: true }
|
SelectToolFsmState::Drawing { selection_shape, has_drawn: true }
|
||||||
}
|
}
|
||||||
@@ -1347,7 +1354,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
SelectToolMessage::PointerOutsideViewport { .. },
|
SelectToolMessage::PointerOutsideViewport { .. },
|
||||||
) => {
|
) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) {
|
||||||
tool_data.drag_current += shift;
|
tool_data.drag_current += shift;
|
||||||
tool_data.drag_start += shift;
|
tool_data.drag_start += shift;
|
||||||
}
|
}
|
||||||
@@ -1362,7 +1369,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
}
|
}
|
||||||
(SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
(SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) {
|
||||||
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
||||||
bounds.center_of_transformation += shift;
|
bounds.center_of_transformation += shift;
|
||||||
bounds.original_bound_transform.translation += shift;
|
bounds.original_bound_transform.translation += shift;
|
||||||
@@ -1373,13 +1380,13 @@ impl Fsm for SelectToolFsmState {
|
|||||||
}
|
}
|
||||||
(SelectToolFsmState::DraggingPivot, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
(SelectToolFsmState::DraggingPivot, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(SelectToolFsmState::Drawing { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
(SelectToolFsmState::Drawing { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) {
|
||||||
tool_data.drag_start += shift;
|
tool_data.drag_start += shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1403,7 +1410,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
if !has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() {
|
if !has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() {
|
||||||
// When you click on the layer with remove from selection key (shift) pressed, we deselect all nodes that are children.
|
// When you click on the layer with remove from selection key (shift) pressed, we deselect all nodes that are children.
|
||||||
let quad = tool_data.selection_quad();
|
let quad = tool_data.selection_quad();
|
||||||
let intersection = document.intersect_quad_no_artboards(quad, input);
|
let intersection = document.intersect_quad_no_artboards(quad, viewport);
|
||||||
|
|
||||||
if let Some(path) = intersection.last() {
|
if let Some(path) = intersection.last() {
|
||||||
let replacement_selected_layers: Vec<_> = document
|
let replacement_selected_layers: Vec<_> = document
|
||||||
@@ -1433,7 +1440,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
} else if tool_data.select_single_layer.take().is_some() {
|
} else if tool_data.select_single_layer.take().is_some() {
|
||||||
// Previously, we may have had many layers selected. If the user clicks without dragging, we should just select the one layer that has been clicked.
|
// Previously, we may have had many layers selected. If the user clicks without dragging, we should just select the one layer that has been clicked.
|
||||||
if !has_dragged {
|
if !has_dragged {
|
||||||
let selected = document.click_list(input).collect::<Vec<_>>();
|
let selected = document.click_list(input, viewport).collect::<Vec<_>>();
|
||||||
let intersection = document.find_deepest(&selected);
|
let intersection = document.find_deepest(&selected);
|
||||||
if let Some(intersection) = intersection {
|
if let Some(intersection) = intersection {
|
||||||
tool_data.layer_selected_on_start = Some(intersection);
|
tool_data.layer_selected_on_start = Some(intersection);
|
||||||
@@ -1449,7 +1456,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input, viewport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1510,13 +1517,13 @@ impl Fsm for SelectToolFsmState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let intersection: Vec<LayerNodeIdentifier> = match selection_shape {
|
let intersection: Vec<LayerNodeIdentifier> = match selection_shape {
|
||||||
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, input).collect(),
|
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, viewport).collect(),
|
||||||
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, input),
|
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, viewport),
|
||||||
};
|
};
|
||||||
let new_selected: HashSet<_> = if selection_mode == SelectionMode::Enclosed {
|
let new_selected: HashSet<_> = if selection_mode == SelectionMode::Enclosed {
|
||||||
let is_inside = |layer: &LayerNodeIdentifier| match selection_shape {
|
let is_inside = |layer: &LayerNodeIdentifier| match selection_shape {
|
||||||
SelectionShapeType::Box => document.is_layer_fully_inside(layer, quad),
|
SelectionShapeType::Box => document.is_layer_fully_inside(layer, quad),
|
||||||
SelectionShapeType::Lasso => tool_data.is_layer_inside_lasso_polygon(layer, document, input),
|
SelectionShapeType::Lasso => tool_data.is_layer_inside_lasso_polygon(layer, document, viewport),
|
||||||
};
|
};
|
||||||
intersection.into_iter().filter(is_inside).collect()
|
intersection.into_iter().filter(is_inside).collect()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -501,11 +501,11 @@ pub struct ShapeToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ShapeToolData {
|
impl ShapeToolData {
|
||||||
fn get_snap_candidates(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
|
fn get_snap_candidates(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) {
|
||||||
self.snap_candidates.clear();
|
self.snap_candidates.clear();
|
||||||
for &layer in &self.layers_dragging {
|
for &layer in &self.layers_dragging {
|
||||||
if (self.snap_candidates.len() as f64) < document.snapping_state.tolerance {
|
if (self.snap_candidates.len() as f64) < document.snapping_state.tolerance {
|
||||||
snapping::get_layer_snap_points(layer, &SnapData::new(document, input), &mut self.snap_candidates);
|
snapping::get_layer_snap_points(layer, &SnapData::new(document, input, viewport), &mut self.snap_candidates);
|
||||||
}
|
}
|
||||||
if let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) {
|
if let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) {
|
||||||
let quad = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
let quad = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
||||||
@@ -558,6 +558,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
input,
|
input,
|
||||||
preferences,
|
preferences,
|
||||||
shape_editor,
|
shape_editor,
|
||||||
|
viewport,
|
||||||
..
|
..
|
||||||
}: &mut ToolActionMessageContext,
|
}: &mut ToolActionMessageContext,
|
||||||
tool_options: &Self::ToolOptions,
|
tool_options: &Self::ToolOptions,
|
||||||
@@ -596,7 +597,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
let hovering_over_gizmo = tool_data.gizmo_manager.hovering_over_gizmo();
|
let hovering_over_gizmo = tool_data.gizmo_manager.hovering_over_gizmo();
|
||||||
|
|
||||||
if !matches!(self, ShapeToolFsmState::ModifyingGizmo) && !modifying_transform_cage && !hovering_over_gizmo {
|
if !matches!(self, ShapeToolFsmState::ModifyingGizmo) && !modifying_transform_cage && !hovering_over_gizmo {
|
||||||
tool_data.data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if modifying_transform_cage && !matches!(self, ShapeToolFsmState::ModifyingGizmo) {
|
if modifying_transform_cage && !matches!(self, ShapeToolFsmState::ModifyingGizmo) {
|
||||||
@@ -783,7 +784,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
|
|
||||||
match (resize, rotate, skew) {
|
match (resize, rotate, skew) {
|
||||||
(true, false, false) => {
|
(true, false, false) => {
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input, viewport);
|
||||||
update_cursor_and_pointer(tool_data, responses);
|
update_cursor_and_pointer(tool_data, responses);
|
||||||
|
|
||||||
return ShapeToolFsmState::ResizingBounds;
|
return ShapeToolFsmState::ResizingBounds;
|
||||||
@@ -795,7 +796,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
return ShapeToolFsmState::RotatingBounds;
|
return ShapeToolFsmState::RotatingBounds;
|
||||||
}
|
}
|
||||||
(false, false, true) => {
|
(false, false, true) => {
|
||||||
tool_data.get_snap_candidates(document, input);
|
tool_data.get_snap_candidates(document, input, viewport);
|
||||||
update_cursor_and_pointer(tool_data, responses);
|
update_cursor_and_pointer(tool_data, responses);
|
||||||
|
|
||||||
return ShapeToolFsmState::SkewingBounds { skew: Key::Control };
|
return ShapeToolFsmState::SkewingBounds { skew: Key::Control };
|
||||||
@@ -806,11 +807,14 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
|
|
||||||
match tool_data.current_shape {
|
match tool_data.current_shape {
|
||||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Spiral | ShapeType::Grid | ShapeType::Rectangle | ShapeType::Ellipse => {
|
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Spiral | ShapeType::Grid | ShapeType::Rectangle | ShapeType::Ellipse => {
|
||||||
tool_data.data.start(document, input)
|
tool_data.data.start(document, input, viewport);
|
||||||
}
|
}
|
||||||
ShapeType::Line => {
|
ShapeType::Line => {
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||||
let snapped = tool_data.data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = tool_data
|
||||||
|
.data
|
||||||
|
.snap_manager
|
||||||
|
.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
tool_data.data.drag_start = snapped.snapped_point_document;
|
tool_data.data.drag_start = snapped.snapped_point_document;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -830,7 +834,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let nodes = vec![(NodeId(0), node)];
|
let nodes = vec![(NodeId(0), node)];
|
||||||
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, document.new_layer_bounding_artboard(input), responses);
|
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, document.new_layer_bounding_artboard(input, viewport), responses);
|
||||||
|
|
||||||
let defered_responses = &mut VecDeque::new();
|
let defered_responses = &mut VecDeque::new();
|
||||||
|
|
||||||
@@ -868,20 +872,20 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match tool_data.current_shape {
|
match tool_data.current_shape {
|
||||||
ShapeType::Polygon => Polygon::update_shape(document, input, layer, tool_data, modifier, responses),
|
ShapeType::Polygon => Polygon::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||||
ShapeType::Star => Star::update_shape(document, input, layer, tool_data, modifier, responses),
|
ShapeType::Star => Star::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||||
ShapeType::Circle => Circle::update_shape(document, input, layer, tool_data, modifier, responses),
|
ShapeType::Circle => Circle::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||||
ShapeType::Arc => Arc::update_shape(document, input, layer, tool_data, modifier, responses),
|
ShapeType::Arc => Arc::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||||
ShapeType::Spiral => Spiral::update_shape(document, input, layer, tool_data, responses),
|
ShapeType::Spiral => Spiral::update_shape(document, input, viewport, layer, tool_data, responses),
|
||||||
ShapeType::Grid => Grid::update_shape(document, input, layer, tool_options.grid_type, tool_data, modifier, responses),
|
ShapeType::Grid => Grid::update_shape(document, input, layer, tool_options.grid_type, tool_data, modifier, responses),
|
||||||
ShapeType::Rectangle => Rectangle::update_shape(document, input, layer, tool_data, modifier, responses),
|
ShapeType::Rectangle => Rectangle::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||||
ShapeType::Ellipse => Ellipse::update_shape(document, input, layer, tool_data, modifier, responses),
|
ShapeType::Ellipse => Ellipse::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||||
ShapeType::Line => Line::update_shape(document, input, layer, tool_data, modifier, responses),
|
ShapeType::Line => Line::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let messages = [ShapeToolMessage::PointerOutsideViewport { modifier }.into(), ShapeToolMessage::PointerMove { modifier }.into()];
|
let messages = [ShapeToolMessage::PointerOutsideViewport { modifier }.into(), ShapeToolMessage::PointerMove { modifier }.into()];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -890,10 +894,10 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
return ShapeToolFsmState::Ready(tool_data.current_shape);
|
return ShapeToolFsmState::Ready(tool_data.current_shape);
|
||||||
};
|
};
|
||||||
|
|
||||||
Line::update_shape(document, input, layer, tool_data, modifier, responses);
|
Line::update_shape(document, input, viewport, layer, tool_data, modifier, responses);
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let messages = [ShapeToolMessage::PointerOutsideViewport { modifier }.into(), ShapeToolMessage::PointerMove { modifier }.into()];
|
let messages = [ShapeToolMessage::PointerOutsideViewport { modifier }.into(), ShapeToolMessage::PointerMove { modifier }.into()];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -915,11 +919,12 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
&mut tool_data.data.snap_manager,
|
&mut tool_data.data.snap_manager,
|
||||||
&mut tool_data.snap_candidates,
|
&mut tool_data.snap_candidates,
|
||||||
input,
|
input,
|
||||||
|
viewport,
|
||||||
input.keyboard.key(modifier[0]),
|
input.keyboard.key(modifier[0]),
|
||||||
input.keyboard.key(modifier[1]),
|
input.keyboard.key(modifier[1]),
|
||||||
ToolType::Shape,
|
ToolType::Shape,
|
||||||
);
|
);
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
@@ -974,14 +979,14 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor });
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_data.data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position);
|
tool_data.data.snap_manager.preview_draw(&SnapData::new(document, input, viewport), input.mouse.position);
|
||||||
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::SkewingBounds { .. }, ShapeToolMessage::PointerOutsideViewport { .. }) => {
|
(ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::SkewingBounds { .. }, ShapeToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses)
|
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses)
|
||||||
&& let Some(bounds) = &mut tool_data.bounding_box_manager
|
&& let Some(bounds) = &mut tool_data.bounding_box_manager
|
||||||
{
|
{
|
||||||
bounds.center_of_transformation += shift;
|
bounds.center_of_transformation += shift;
|
||||||
@@ -993,7 +998,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
(ShapeToolFsmState::Ready(_), ShapeToolMessage::PointerOutsideViewport { .. }) => self,
|
(ShapeToolFsmState::Ready(_), ShapeToolMessage::PointerOutsideViewport { .. }) => self,
|
||||||
(_, ShapeToolMessage::PointerOutsideViewport { .. }) => {
|
(_, ShapeToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -267,12 +267,12 @@ impl SplineToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the snapped point while ignoring current layer
|
/// Get the snapped point while ignoring current layer
|
||||||
fn snapped_point(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> SnappedPoint {
|
fn snapped_point(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> SnappedPoint {
|
||||||
let metadata = document.metadata();
|
let metadata = document.metadata();
|
||||||
let transform = self.current_layer.map_or(metadata.document_to_viewport, |layer| metadata.transform_to_viewport(layer));
|
let transform = self.current_layer.map_or(metadata.document_to_viewport, |layer| metadata.transform_to_viewport(layer));
|
||||||
let point = SnapCandidatePoint::handle(transform.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(transform.inverse().transform_point2(input.mouse.position));
|
||||||
let ignore = if let Some(layer) = self.current_layer { vec![layer] } else { vec![] };
|
let ignore = if let Some(layer) = self.current_layer { vec![layer] } else { vec![] };
|
||||||
let snap_data = SnapData::ignore(document, input, &ignore);
|
let snap_data = SnapData::ignore(document, input, viewport, &ignore);
|
||||||
self.snap_manager.free_snap(&snap_data, &point, SnapTypeConfiguration::default())
|
self.snap_manager.free_snap(&snap_data, &point, SnapTypeConfiguration::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,6 +295,7 @@ impl Fsm for SplineToolFsmState {
|
|||||||
input,
|
input,
|
||||||
shape_editor,
|
shape_editor,
|
||||||
preferences,
|
preferences,
|
||||||
|
viewport,
|
||||||
..
|
..
|
||||||
} = tool_action_data;
|
} = tool_action_data;
|
||||||
|
|
||||||
@@ -303,7 +304,7 @@ impl Fsm for SplineToolFsmState {
|
|||||||
(_, SplineToolMessage::CanvasTransformed) => self,
|
(_, SplineToolMessage::CanvasTransformed) => self,
|
||||||
(_, SplineToolMessage::Overlays { context: mut overlay_context }) => {
|
(_, SplineToolMessage::Overlays { context: mut overlay_context }) => {
|
||||||
path_endpoint_overlays(document, shape_editor, &mut overlay_context, preferences);
|
path_endpoint_overlays(document, shape_editor, &mut overlay_context, preferences);
|
||||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(SplineToolFsmState::MergingEndpoints, SplineToolMessage::MergeEndpoints) => {
|
(SplineToolFsmState::MergingEndpoints, SplineToolMessage::MergeEndpoints) => {
|
||||||
@@ -342,15 +343,15 @@ impl Fsm for SplineToolFsmState {
|
|||||||
tool_data.weight = tool_options.line_weight;
|
tool_data.weight = tool_options.line_weight;
|
||||||
|
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||||
|
|
||||||
let layers = LayerNodeIdentifier::ROOT_PARENT
|
let layers = LayerNodeIdentifier::ROOT_PARENT
|
||||||
.descendants(document.metadata())
|
.descendants(document.metadata())
|
||||||
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||||
|
|
||||||
// Extend an endpoint of the selected path
|
// Extend an endpoint of the selected path
|
||||||
if let Some((layer, point, position)) = should_extend(document, viewport, SNAP_POINT_TOLERANCE, layers, preferences) {
|
if let Some((layer, point, position)) = should_extend(document, viewport_vec, SNAP_POINT_TOLERANCE, layers, preferences) {
|
||||||
if find_spline(document, layer).is_some() {
|
if find_spline(document, layer).is_some() {
|
||||||
// If the point is the part of Spline then we extend it.
|
// If the point is the part of Spline then we extend it.
|
||||||
tool_data.current_layer = Some(layer);
|
tool_data.current_layer = Some(layer);
|
||||||
@@ -386,7 +387,7 @@ impl Fsm for SplineToolFsmState {
|
|||||||
|
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
responses.add(DocumentMessage::DeselectAllLayers);
|
||||||
|
|
||||||
let parent = document.new_layer_bounding_artboard(input);
|
let parent = document.new_layer_bounding_artboard(input, viewport);
|
||||||
|
|
||||||
let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||||
let path_node = path_node_type.default_node_template();
|
let path_node = path_node_type.default_node_template();
|
||||||
@@ -410,7 +411,7 @@ impl Fsm for SplineToolFsmState {
|
|||||||
if tool_data.current_layer.is_none() {
|
if tool_data.current_layer.is_none() {
|
||||||
return SplineToolFsmState::Ready;
|
return SplineToolFsmState::Ready;
|
||||||
};
|
};
|
||||||
tool_data.next_point = tool_data.snapped_point(document, input).snapped_point_document;
|
tool_data.next_point = tool_data.snapped_point(document, input, viewport).snapped_point_document;
|
||||||
if tool_data.points.last().is_none_or(|last_pos| last_pos.1.distance(tool_data.next_point) > DRAG_THRESHOLD) {
|
if tool_data.points.last().is_none_or(|last_pos| last_pos.1.distance(tool_data.next_point) > DRAG_THRESHOLD) {
|
||||||
let preview_point = tool_data.preview_point;
|
let preview_point = tool_data.preview_point;
|
||||||
extend_spline(tool_data, false, responses);
|
extend_spline(tool_data, false, responses);
|
||||||
@@ -433,7 +434,7 @@ impl Fsm for SplineToolFsmState {
|
|||||||
tool_data.next_point = point;
|
tool_data.next_point = point;
|
||||||
tool_data.snap_manager.clear_indicator();
|
tool_data.snap_manager.clear_indicator();
|
||||||
} else {
|
} else {
|
||||||
let snapped_point = tool_data.snapped_point(document, input);
|
let snapped_point = tool_data.snapped_point(document, input, viewport);
|
||||||
tool_data.next_point = snapped_point.snapped_point_document;
|
tool_data.next_point = snapped_point.snapped_point_document;
|
||||||
tool_data.snap_manager.update_indicator(snapped_point);
|
tool_data.snap_manager.update_indicator(snapped_point);
|
||||||
}
|
}
|
||||||
@@ -442,12 +443,12 @@ impl Fsm for SplineToolFsmState {
|
|||||||
|
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let messages = [SplineToolMessage::PointerOutsideViewport.into(), SplineToolMessage::PointerMove.into()];
|
let messages = [SplineToolMessage::PointerOutsideViewport.into(), SplineToolMessage::PointerMove.into()];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
SplineToolFsmState::Drawing
|
SplineToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
(_, SplineToolMessage::PointerMove) => {
|
(_, SplineToolMessage::PointerMove) => {
|
||||||
tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position);
|
tool_data.snap_manager.preview_draw(&SnapData::new(document, input, viewport), input.mouse.position);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -456,7 +457,7 @@ impl Fsm for SplineToolFsmState {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
SplineToolFsmState::Drawing
|
SplineToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -499,6 +499,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
global_tool_data,
|
global_tool_data,
|
||||||
input,
|
input,
|
||||||
font_cache,
|
font_cache,
|
||||||
|
viewport,
|
||||||
..
|
..
|
||||||
} = transition_data;
|
} = transition_data;
|
||||||
let fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
let fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||||
@@ -509,9 +510,8 @@ impl Fsm for TextToolFsmState {
|
|||||||
let ToolMessage::Text(event) = event else { return self };
|
let ToolMessage::Text(event) = event else { return self };
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(TextToolFsmState::Editing, TextToolMessage::Overlays { context: mut overlay_context }) => {
|
(TextToolFsmState::Editing, TextToolMessage::Overlays { context: mut overlay_context }) => {
|
||||||
responses.add(FrontendMessage::DisplayEditableTextboxTransform {
|
let transform = document.metadata().transform_to_viewport(tool_data.layer).to_cols_array();
|
||||||
transform: document.metadata().transform_to_viewport(tool_data.layer).to_cols_array(),
|
responses.add(FrontendMessage::DisplayEditableTextboxTransform { transform });
|
||||||
});
|
|
||||||
if let Some(editing_text) = tool_data.editing_text.as_mut() {
|
if let Some(editing_text) = tool_data.editing_text.as_mut() {
|
||||||
let far = graphene_std::text::bounding_box(&tool_data.new_text, &editing_text.font, font_cache, editing_text.typesetting, false);
|
let far = graphene_std::text::bounding_box(&tool_data.new_text, &editing_text.font, font_cache, editing_text.typesetting, false);
|
||||||
if far.x != 0. && far.y != 0. {
|
if far.x != 0. && far.y != 0. {
|
||||||
@@ -529,7 +529,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
let quad = Quad::from_box(tool_data.cached_resize_bounds);
|
let quad = Quad::from_box(tool_data.cached_resize_bounds);
|
||||||
|
|
||||||
// Draw a bounding box on the layers to be selected
|
// Draw a bounding box on the layers to be selected
|
||||||
for layer in document.intersect_quad_no_artboards(quad, input) {
|
for layer in document.intersect_quad_no_artboards(quad, viewport) {
|
||||||
overlay_context.quad(
|
overlay_context.quad(
|
||||||
Quad::from_box(document.metadata().bounding_box_viewport(layer).unwrap_or([DVec2::ZERO; 2])),
|
Quad::from_box(document.metadata().bounding_box_viewport(layer).unwrap_or([DVec2::ZERO; 2])),
|
||||||
None,
|
None,
|
||||||
@@ -572,7 +572,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
tool_data.bounding_box_manager.take();
|
tool_data.bounding_box_manager.take();
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_data.resize.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
tool_data.resize.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -585,7 +585,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
(TextToolFsmState::Ready, TextToolMessage::DragStart) => {
|
(TextToolFsmState::Ready, TextToolMessage::DragStart) => {
|
||||||
tool_data.resize.start(document, input);
|
tool_data.resize.start(document, input, viewport);
|
||||||
tool_data.cached_resize_bounds = [tool_data.resize.viewport_drag_start(document); 2];
|
tool_data.cached_resize_bounds = [tool_data.resize.viewport_drag_start(document); 2];
|
||||||
tool_data.drag_start = input.mouse.position;
|
tool_data.drag_start = input.mouse.position;
|
||||||
tool_data.drag_current = input.mouse.position;
|
tool_data.drag_current = input.mouse.position;
|
||||||
@@ -659,7 +659,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
TextToolFsmState::Ready
|
TextToolFsmState::Ready
|
||||||
}
|
}
|
||||||
(TextToolFsmState::Placing, TextToolMessage::PointerMove { center, lock_ratio }) => {
|
(TextToolFsmState::Placing, TextToolMessage::PointerMove { center, lock_ratio }) => {
|
||||||
tool_data.cached_resize_bounds = tool_data.resize.calculate_points_ignore_layer(document, input, center, lock_ratio, false);
|
tool_data.cached_resize_bounds = tool_data.resize.calculate_points_ignore_layer(document, input, viewport, center, lock_ratio, false);
|
||||||
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|
||||||
@@ -668,7 +668,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
|
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
|
||||||
TextToolMessage::PointerMove { center, lock_ratio }.into(),
|
TextToolMessage::PointerMove { center, lock_ratio }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
|
|
||||||
TextToolFsmState::Placing
|
TextToolFsmState::Placing
|
||||||
}
|
}
|
||||||
@@ -691,7 +691,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
|
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
|
||||||
TextToolMessage::PointerMove { center, lock_ratio }.into(),
|
TextToolMessage::PointerMove { center, lock_ratio }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextToolFsmState::Dragging
|
TextToolFsmState::Dragging
|
||||||
@@ -713,7 +713,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
let snap = Some(SizeSnapData {
|
let snap = Some(SizeSnapData {
|
||||||
manager: &mut tool_data.resize.snap_manager,
|
manager: &mut tool_data.resize.snap_manager,
|
||||||
points: &mut tool_data.snap_candidates,
|
points: &mut tool_data.snap_candidates,
|
||||||
snap_data: SnapData::ignore(document, input, &selected),
|
snap_data: SnapData::ignore(document, input, viewport, &selected),
|
||||||
});
|
});
|
||||||
|
|
||||||
let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center_position, constrain, snap);
|
let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center_position, constrain, snap);
|
||||||
@@ -752,26 +752,26 @@ impl Fsm for TextToolFsmState {
|
|||||||
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
|
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
|
||||||
TextToolMessage::PointerMove { center, lock_ratio }.into(),
|
TextToolMessage::PointerMove { center, lock_ratio }.into(),
|
||||||
];
|
];
|
||||||
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
|
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextToolFsmState::ResizingBounds
|
TextToolFsmState::ResizingBounds
|
||||||
}
|
}
|
||||||
(_, TextToolMessage::PointerMove { .. }) => {
|
(_, TextToolMessage::PointerMove { .. }) => {
|
||||||
tool_data.resize.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position);
|
tool_data.resize.snap_manager.preview_draw(&SnapData::new(document, input, viewport), input.mouse.position);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(TextToolFsmState::Placing, TextToolMessage::PointerOutsideViewport { .. }) => {
|
(TextToolFsmState::Placing, TextToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning setup
|
// Auto-panning setup
|
||||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||||
|
|
||||||
TextToolFsmState::Placing
|
TextToolFsmState::Placing
|
||||||
}
|
}
|
||||||
(TextToolFsmState::ResizingBounds | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => {
|
(TextToolFsmState::ResizingBounds | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => {
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) {
|
||||||
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
||||||
bounds.center_of_transformation += shift;
|
bounds.center_of_transformation += shift;
|
||||||
bounds.original_bound_transform.translation += shift;
|
bounds.original_bound_transform.translation += shift;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ pub struct TransformLayerMessageContext<'a> {
|
|||||||
pub input: &'a InputPreprocessorMessageHandler,
|
pub input: &'a InputPreprocessorMessageHandler,
|
||||||
pub tool_data: &'a ToolData,
|
pub tool_data: &'a ToolData,
|
||||||
pub shape_editor: &'a mut ShapeState,
|
pub shape_editor: &'a mut ShapeState,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ExtractField)]
|
#[derive(Debug, Clone, Default, ExtractField)]
|
||||||
@@ -77,6 +78,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
|
|||||||
input,
|
input,
|
||||||
tool_data,
|
tool_data,
|
||||||
shape_editor,
|
shape_editor,
|
||||||
|
viewport,
|
||||||
} = context;
|
} = context;
|
||||||
|
|
||||||
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
|
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
|
||||||
@@ -184,7 +186,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let viewport_box = input.viewport_bounds.size();
|
let viewport_box = viewport.size().into_dvec2();
|
||||||
let axis_constraint = self.transform_operation.axis_constraint();
|
let axis_constraint = self.transform_operation.axis_constraint();
|
||||||
|
|
||||||
let format_rounded = |value: f64, precision: usize| {
|
let format_rounded = |value: f64, precision: usize| {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ pub struct ToolActionMessageContext<'a> {
|
|||||||
pub shape_editor: &'a mut ShapeState,
|
pub shape_editor: &'a mut ShapeState,
|
||||||
pub node_graph: &'a NodeGraphExecutor,
|
pub node_graph: &'a NodeGraphExecutor,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
|
pub viewport: &'a ViewportMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToolCommon: for<'a, 'b> MessageHandler<ToolMessage, &'b mut ToolActionMessageContext<'a>> + LayoutHolder + ToolTransition + ToolMetadata {}
|
pub trait ToolCommon: for<'a, 'b> MessageHandler<ToolMessage, &'b mut ToolActionMessageContext<'a>> + LayoutHolder + ToolTransition + ToolMetadata {}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
mod viewport_message;
|
||||||
|
mod viewport_message_handler;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use viewport_message::{ViewportMessage, ViewportMessageDiscriminant};
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use viewport_message_handler::*;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
|
#[impl_message(Message, Viewport)]
|
||||||
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum ViewportMessage {
|
||||||
|
Update { x: f64, y: f64, width: f64, height: f64, scale: f64 },
|
||||||
|
RepropagateUpdate,
|
||||||
|
}
|
||||||
@@ -0,0 +1,602 @@
|
|||||||
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
|
||||||
|
use crate::messages::prelude::*;
|
||||||
|
use crate::messages::tool::tool_messages::tool_prelude::DVec2;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type, ExtractField)]
|
||||||
|
pub struct ViewportMessageHandler {
|
||||||
|
bounds: Bounds,
|
||||||
|
// Ratio of logical pixels to physical pixels
|
||||||
|
scale: f64,
|
||||||
|
}
|
||||||
|
impl Default for ViewportMessageHandler {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: Bounds {
|
||||||
|
offset: Point { x: 0.0, y: 0.0 },
|
||||||
|
size: Point { x: 0.0, y: 0.0 },
|
||||||
|
},
|
||||||
|
scale: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[message_handler_data]
|
||||||
|
impl MessageHandler<ViewportMessage, ()> for ViewportMessageHandler {
|
||||||
|
fn process_message(&mut self, message: ViewportMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
|
match message {
|
||||||
|
ViewportMessage::Update { x, y, width, height, scale } => {
|
||||||
|
assert_ne!(scale, 0.0, "Viewport scale cannot be zero");
|
||||||
|
self.scale = scale;
|
||||||
|
|
||||||
|
self.bounds = Bounds {
|
||||||
|
offset: Point { x, y },
|
||||||
|
size: Point { x: width, y: height },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ViewportMessage::RepropagateUpdate => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let physical_bounds = self.bounds().to_physical();
|
||||||
|
responses.add(FrontendMessage::UpdateViewportPhysicalBounds {
|
||||||
|
x: physical_bounds.x(),
|
||||||
|
y: physical_bounds.y(),
|
||||||
|
width: physical_bounds.width(),
|
||||||
|
height: physical_bounds.height(),
|
||||||
|
});
|
||||||
|
|
||||||
|
responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO });
|
||||||
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
|
|
||||||
|
responses.add(DeferMessage::AfterGraphRun {
|
||||||
|
messages: vec![
|
||||||
|
DeferMessage::AfterGraphRun {
|
||||||
|
messages: vec![DeferMessage::TriggerNavigationReady.into()],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
advertise_actions!(ViewportMessageDiscriminant;);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewportMessageHandler {
|
||||||
|
pub fn scale(&self) -> f64 {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bounds(&self) -> LogicalBounds {
|
||||||
|
self.bounds.into_scaled(self.scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> LogicalPoint {
|
||||||
|
self.bounds.offset.into_scaled(self.scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> LogicalPoint {
|
||||||
|
self.bounds.size().into_scaled(self.scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(private_bounds)]
|
||||||
|
pub fn logical<T: Into<Point>>(&self, point: T) -> LogicalPoint {
|
||||||
|
point.into().convert_to_logical(self.scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(private_bounds)]
|
||||||
|
pub fn physical<T: Into<Point>>(&self, point: T) -> PhysicalPoint {
|
||||||
|
point.into().convert_to_physical(self.scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn center_in_viewport_space(&self) -> LogicalPoint {
|
||||||
|
let size = self.size();
|
||||||
|
LogicalPoint {
|
||||||
|
inner: Point { x: size.x() / 2.0, y: size.y() / 2.0 },
|
||||||
|
scale: size.scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn center_in_window_space(&self) -> LogicalPoint {
|
||||||
|
let size = self.size();
|
||||||
|
let offset = self.offset();
|
||||||
|
LogicalPoint {
|
||||||
|
inner: Point {
|
||||||
|
x: (size.x() / 2.0) + offset.x(),
|
||||||
|
y: (size.y() / 2.0) + offset.y(),
|
||||||
|
},
|
||||||
|
scale: size.scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_in_bounds(&self, point: LogicalPoint) -> bool {
|
||||||
|
point.x() >= self.bounds.x() && point.y() >= self.bounds.y() && point.x() <= self.bounds.x() + self.bounds.width() && point.y() <= self.bounds.y() + self.bounds.height()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToLogical<L: ToPhysical<Self> + ?Sized> {
|
||||||
|
fn to_logical(self) -> L;
|
||||||
|
}
|
||||||
|
pub trait ToPhysical<P: ToLogical<Self> + ?Sized> {
|
||||||
|
fn to_physical(self) -> P;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait IntoScaled<T: Scaled>: Sized {
|
||||||
|
fn into_scaled(self, scale: f64) -> T;
|
||||||
|
}
|
||||||
|
trait FromWithScale<T>: Sized {
|
||||||
|
fn from_with_scale(value: T, scale: f64) -> Self;
|
||||||
|
}
|
||||||
|
impl<T, U: Scaled> IntoScaled<U> for T
|
||||||
|
where
|
||||||
|
U: FromWithScale<T>,
|
||||||
|
{
|
||||||
|
fn into_scaled(self, scale: f64) -> U {
|
||||||
|
U::from_with_scale(self, scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AsPoint {
|
||||||
|
fn as_point(&self) -> Point;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Scaled {
|
||||||
|
fn scale(&self) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Position {
|
||||||
|
fn x(&self) -> f64;
|
||||||
|
fn y(&self) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
struct Point {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
}
|
||||||
|
impl Point {
|
||||||
|
fn convert_to_logical(&self, scale: f64) -> LogicalPoint {
|
||||||
|
Point { x: self.x(), y: self.y() }.into_scaled(scale)
|
||||||
|
}
|
||||||
|
fn convert_to_physical(&self, scale: f64) -> PhysicalPoint {
|
||||||
|
Point {
|
||||||
|
x: self.x() / scale,
|
||||||
|
y: self.y() / scale,
|
||||||
|
}
|
||||||
|
.into_scaled(scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Position for Point {
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
self.x
|
||||||
|
}
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct LogicalPoint {
|
||||||
|
inner: Point,
|
||||||
|
scale: f64,
|
||||||
|
}
|
||||||
|
impl AsPoint for LogicalPoint {
|
||||||
|
fn as_point(&self) -> Point {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Scaled for LogicalPoint {
|
||||||
|
fn scale(&self) -> f64 {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Position for LogicalPoint {
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
self.inner.x()
|
||||||
|
}
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
self.inner.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToPhysical<PhysicalPoint> for LogicalPoint {
|
||||||
|
fn to_physical(self) -> PhysicalPoint {
|
||||||
|
PhysicalPoint { inner: self.inner, scale: self.scale }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromWithScale<Point> for LogicalPoint {
|
||||||
|
fn from_with_scale(value: Point, scale: f64) -> Self {
|
||||||
|
Self { inner: value, scale }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct PhysicalPoint {
|
||||||
|
inner: Point,
|
||||||
|
scale: f64,
|
||||||
|
}
|
||||||
|
impl AsPoint for PhysicalPoint {
|
||||||
|
fn as_point(&self) -> Point {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Scaled for PhysicalPoint {
|
||||||
|
fn scale(&self) -> f64 {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Position for PhysicalPoint {
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
self.inner.x() * self.scale
|
||||||
|
}
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
self.inner.y() * self.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToLogical<LogicalPoint> for PhysicalPoint {
|
||||||
|
fn to_logical(self) -> LogicalPoint {
|
||||||
|
LogicalPoint { inner: self.inner, scale: self.scale }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromWithScale<Point> for PhysicalPoint {
|
||||||
|
fn from_with_scale(value: Point, scale: f64) -> Self {
|
||||||
|
Self { inner: value, scale }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Rect<P: Position>: Position {
|
||||||
|
fn offset(&self) -> P;
|
||||||
|
fn size(&self) -> P;
|
||||||
|
fn width(&self) -> f64;
|
||||||
|
fn height(&self) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type, ExtractField)]
|
||||||
|
struct Bounds {
|
||||||
|
offset: Point,
|
||||||
|
size: Point,
|
||||||
|
}
|
||||||
|
impl Position for Bounds {
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
self.offset.x()
|
||||||
|
}
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
self.offset.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Rect<Point> for Bounds {
|
||||||
|
fn offset(&self) -> Point {
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
fn size(&self) -> Point {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
fn width(&self) -> f64 {
|
||||||
|
self.size.x()
|
||||||
|
}
|
||||||
|
fn height(&self) -> f64 {
|
||||||
|
self.size.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct LogicalBounds {
|
||||||
|
offset: Point,
|
||||||
|
size: Point,
|
||||||
|
scale: f64,
|
||||||
|
}
|
||||||
|
impl Scaled for LogicalBounds {
|
||||||
|
fn scale(&self) -> f64 {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Position for LogicalBounds {
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
self.offset.x()
|
||||||
|
}
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
self.offset.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Rect<LogicalPoint> for LogicalBounds {
|
||||||
|
fn offset(&self) -> LogicalPoint {
|
||||||
|
self.offset.into_scaled(self.scale)
|
||||||
|
}
|
||||||
|
fn size(&self) -> LogicalPoint {
|
||||||
|
self.size.into_scaled(self.scale)
|
||||||
|
}
|
||||||
|
fn width(&self) -> f64 {
|
||||||
|
self.size.x()
|
||||||
|
}
|
||||||
|
fn height(&self) -> f64 {
|
||||||
|
self.size.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToPhysical<PhysicalBounds> for LogicalBounds {
|
||||||
|
fn to_physical(self) -> PhysicalBounds {
|
||||||
|
PhysicalBounds {
|
||||||
|
offset: self.offset,
|
||||||
|
size: self.size,
|
||||||
|
scale: self.scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromWithScale<Bounds> for LogicalBounds {
|
||||||
|
fn from_with_scale(value: Bounds, scale: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
offset: value.offset(),
|
||||||
|
size: value.size(),
|
||||||
|
scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct PhysicalBounds {
|
||||||
|
offset: Point,
|
||||||
|
size: Point,
|
||||||
|
scale: f64,
|
||||||
|
}
|
||||||
|
impl Scaled for PhysicalBounds {
|
||||||
|
fn scale(&self) -> f64 {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Position for PhysicalBounds {
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
self.offset.x() * self.scale
|
||||||
|
}
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
self.offset.y() * self.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Rect<PhysicalPoint> for PhysicalBounds {
|
||||||
|
fn offset(&self) -> PhysicalPoint {
|
||||||
|
self.offset.into_scaled(self.scale)
|
||||||
|
}
|
||||||
|
fn size(&self) -> PhysicalPoint {
|
||||||
|
self.size.into_scaled(self.scale)
|
||||||
|
}
|
||||||
|
fn width(&self) -> f64 {
|
||||||
|
self.size.x() * self.scale
|
||||||
|
}
|
||||||
|
fn height(&self) -> f64 {
|
||||||
|
self.size.y() * self.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToLogical<LogicalBounds> for PhysicalBounds {
|
||||||
|
fn to_logical(self) -> LogicalBounds {
|
||||||
|
LogicalBounds {
|
||||||
|
offset: self.offset,
|
||||||
|
size: self.size,
|
||||||
|
scale: self.scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromWithScale<Bounds> for PhysicalBounds {
|
||||||
|
fn from_with_scale(value: Bounds, scale: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
offset: value.offset(),
|
||||||
|
size: value.size(),
|
||||||
|
scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
assert_ne!(rhs, 0.0, "Cannot multiply point by zero");
|
||||||
|
Point { x: self.x * rhs, y: self.y * rhs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Div<f64> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn div(self, rhs: f64) -> Self::Output {
|
||||||
|
assert_ne!(rhs, 0.0, "Cannot divide point by zero");
|
||||||
|
Point { x: self.x / rhs, y: self.y / rhs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Add<f64> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn add(self, rhs: f64) -> Self::Output {
|
||||||
|
Point { x: self.x + rhs, y: self.y + rhs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub<f64> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn sub(self, rhs: f64) -> Self::Output {
|
||||||
|
Point { x: self.x - rhs, y: self.y - rhs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Mul<Point> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn mul(self, rhs: Point) -> Self::Output {
|
||||||
|
assert_ne!(rhs.x, 0.0, "Cannot multiply point by zero");
|
||||||
|
assert_ne!(rhs.y, 0.0, "Cannot multiply point by zero");
|
||||||
|
Point { x: self.x * rhs.x, y: self.y * rhs.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Div<Point> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn div(self, rhs: Point) -> Self::Output {
|
||||||
|
assert_ne!(rhs.x, 0.0, "Cannot multiply point by zero");
|
||||||
|
assert_ne!(rhs.y, 0.0, "Cannot multiply point by zero");
|
||||||
|
Point { x: self.x / rhs.x, y: self.y / rhs.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Add<Point> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn add(self, rhs: Point) -> Self::Output {
|
||||||
|
Point { x: self.x + rhs.x, y: self.y + rhs.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub<Point> for Point {
|
||||||
|
type Output = Point;
|
||||||
|
fn sub(self, rhs: Point) -> Self::Output {
|
||||||
|
Point { x: self.x - rhs.x, y: self.y - rhs.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for Bounds {
|
||||||
|
type Output = Bounds;
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
assert_ne!(rhs, 0.0, "Cannot multiply bounds by zero");
|
||||||
|
Bounds {
|
||||||
|
offset: self.offset * rhs,
|
||||||
|
size: self.size * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Div<f64> for Bounds {
|
||||||
|
type Output = Bounds;
|
||||||
|
fn div(self, rhs: f64) -> Self::Output {
|
||||||
|
assert_ne!(rhs, 0.0, "Cannot divide bounds by zero");
|
||||||
|
Bounds {
|
||||||
|
offset: self.offset / rhs,
|
||||||
|
size: self.size / rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<LogicalPoint> for LogicalPoint {
|
||||||
|
type Output = LogicalPoint;
|
||||||
|
fn mul(self, rhs: LogicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() * rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Div<LogicalPoint> for LogicalPoint {
|
||||||
|
type Output = LogicalPoint;
|
||||||
|
fn div(self, rhs: LogicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() / rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Add<LogicalPoint> for LogicalPoint {
|
||||||
|
type Output = LogicalPoint;
|
||||||
|
fn add(self, rhs: LogicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() + rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub<LogicalPoint> for LogicalPoint {
|
||||||
|
type Output = LogicalPoint;
|
||||||
|
fn sub(self, rhs: LogicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() - rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Mul<PhysicalPoint> for PhysicalPoint {
|
||||||
|
type Output = PhysicalPoint;
|
||||||
|
fn mul(self, rhs: PhysicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() * rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Div<PhysicalPoint> for PhysicalPoint {
|
||||||
|
type Output = PhysicalPoint;
|
||||||
|
fn div(self, rhs: PhysicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() / rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Add<PhysicalPoint> for PhysicalPoint {
|
||||||
|
type Output = PhysicalPoint;
|
||||||
|
fn add(self, rhs: PhysicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() + rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub<PhysicalPoint> for PhysicalPoint {
|
||||||
|
type Output = PhysicalPoint;
|
||||||
|
fn sub(self, rhs: PhysicalPoint) -> Self::Output {
|
||||||
|
assert_scale(&self, &rhs);
|
||||||
|
(self.as_point() - rhs.as_point()).into_scaled(self.scale())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn assert_scale<T: Scaled>(a: &T, b: &T) {
|
||||||
|
assert_eq!(a.scale(), b.scale(), "Cannot multiply with diffent scale");
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(f64, f64)> for Point {
|
||||||
|
fn from((x, y): (f64, f64)) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(f64, f64, f64, f64)> for Bounds {
|
||||||
|
fn from((x, y, width, height): (f64, f64, f64, f64)) -> Self {
|
||||||
|
Self {
|
||||||
|
offset: Point { x, y },
|
||||||
|
size: Point { x: width, y: height },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LogicalPoint> for (f64, f64) {
|
||||||
|
fn from(point: LogicalPoint) -> Self {
|
||||||
|
(point.x(), point.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<PhysicalPoint> for (f64, f64) {
|
||||||
|
fn from(point: PhysicalPoint) -> Self {
|
||||||
|
(point.x(), point.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<LogicalBounds> for (f64, f64, f64, f64) {
|
||||||
|
fn from(bounds: LogicalBounds) -> Self {
|
||||||
|
(bounds.x(), bounds.y(), bounds.width(), bounds.height())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<PhysicalBounds> for (f64, f64, f64, f64) {
|
||||||
|
fn from(bounds: PhysicalBounds) -> Self {
|
||||||
|
(bounds.x(), bounds.y(), bounds.width(), bounds.height())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<glam::DVec2> for Point {
|
||||||
|
fn from(vec: glam::DVec2) -> Self {
|
||||||
|
Self { x: vec.x, y: vec.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<LogicalPoint> for glam::DVec2 {
|
||||||
|
fn from(val: LogicalPoint) -> Self {
|
||||||
|
glam::DVec2::new(val.x(), val.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<PhysicalPoint> for glam::DVec2 {
|
||||||
|
fn from(val: PhysicalPoint) -> Self {
|
||||||
|
glam::DVec2::new(val.x(), val.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[glam::DVec2; 2]> for Bounds {
|
||||||
|
fn from(bounds: [glam::DVec2; 2]) -> Self {
|
||||||
|
Self {
|
||||||
|
offset: bounds[0].into(),
|
||||||
|
size: Point {
|
||||||
|
x: bounds[1].x - bounds[0].x,
|
||||||
|
y: bounds[1].y - bounds[0].y,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<LogicalBounds> for [glam::DVec2; 2] {
|
||||||
|
fn from(bounds: LogicalBounds) -> Self {
|
||||||
|
[glam::DVec2::new(bounds.x(), bounds.y()), glam::DVec2::new(bounds.x() + bounds.width(), bounds.y() + bounds.height())]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<PhysicalBounds> for [glam::DVec2; 2] {
|
||||||
|
fn from(bounds: PhysicalBounds) -> Self {
|
||||||
|
[glam::DVec2::new(bounds.x(), bounds.y()), glam::DVec2::new(bounds.x() + bounds.width(), bounds.y() + bounds.height())]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogicalPoint {
|
||||||
|
pub fn into_dvec2(self) -> DVec2 {
|
||||||
|
DVec2::new(self.x(), self.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PhysicalPoint {
|
||||||
|
pub fn into_dvec2(self) -> DVec2 {
|
||||||
|
DVec2::new(self.x(), self.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use glam::{DAffine2, DVec2, UVec2};
|
use glam::{DAffine2, UVec2};
|
||||||
use graph_craft::document::value::{RenderOutput, TaggedValue};
|
use graph_craft::document::value::{RenderOutput, TaggedValue};
|
||||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
@@ -136,14 +136,17 @@ impl NodeGraphExecutor {
|
|||||||
document: &mut DocumentMessageHandler,
|
document: &mut DocumentMessageHandler,
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
viewport_resolution: UVec2,
|
viewport_resolution: UVec2,
|
||||||
|
viewport_scale: f64,
|
||||||
time: TimingInformation,
|
time: TimingInformation,
|
||||||
) -> Result<Message, String> {
|
) -> Result<Message, String> {
|
||||||
|
let viewport = Footprint {
|
||||||
|
transform: document.metadata().document_to_viewport,
|
||||||
|
resolution: viewport_resolution,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
let render_config = RenderConfig {
|
let render_config = RenderConfig {
|
||||||
viewport: Footprint {
|
viewport,
|
||||||
transform: document.metadata().document_to_viewport,
|
scale: viewport_scale,
|
||||||
resolution: viewport_resolution,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
time,
|
time,
|
||||||
#[cfg(any(feature = "resvg", feature = "vello"))]
|
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||||
export_format: graphene_std::application_io::ExportFormat::Raster,
|
export_format: graphene_std::application_io::ExportFormat::Raster,
|
||||||
@@ -163,23 +166,31 @@ impl NodeGraphExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluates a node graph, computing the entire graph
|
/// Evaluates a node graph, computing the entire graph
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn submit_node_graph_evaluation(
|
pub fn submit_node_graph_evaluation(
|
||||||
&mut self,
|
&mut self,
|
||||||
document: &mut DocumentMessageHandler,
|
document: &mut DocumentMessageHandler,
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
viewport_resolution: UVec2,
|
viewport_resolution: UVec2,
|
||||||
|
viewport_scale: f64,
|
||||||
time: TimingInformation,
|
time: TimingInformation,
|
||||||
node_to_inspect: Option<NodeId>,
|
node_to_inspect: Option<NodeId>,
|
||||||
ignore_hash: bool,
|
ignore_hash: bool,
|
||||||
) -> Result<Message, String> {
|
) -> Result<Message, String> {
|
||||||
self.update_node_graph(document, node_to_inspect, ignore_hash)?;
|
self.update_node_graph(document, node_to_inspect, ignore_hash)?;
|
||||||
self.submit_current_node_graph_evaluation(document, document_id, viewport_resolution, time)
|
self.submit_current_node_graph_evaluation(document, document_id, viewport_resolution, viewport_scale, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluates a node graph for export
|
/// Evaluates a node graph for export
|
||||||
pub fn submit_document_export(&mut self, document: &mut DocumentMessageHandler, document_id: DocumentId, mut export_config: ExportConfig) -> Result<(), String> {
|
pub fn submit_document_export(&mut self, document: &mut DocumentMessageHandler, document_id: DocumentId, mut export_config: ExportConfig) -> Result<(), String> {
|
||||||
let network = document.network_interface.document_network().clone();
|
let network = document.network_interface.document_network().clone();
|
||||||
|
|
||||||
|
let export_format = if export_config.file_type == FileType::Svg {
|
||||||
|
graphene_std::application_io::ExportFormat::Svg
|
||||||
|
} else {
|
||||||
|
graphene_std::application_io::ExportFormat::Raster
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate the bounding box of the region to be exported
|
// Calculate the bounding box of the region to be exported
|
||||||
let bounds = match export_config.bounds {
|
let bounds = match export_config.bounds {
|
||||||
ExportBounds::AllArtwork => document.network_interface.document_bounds_document_space(!export_config.transparent_background),
|
ExportBounds::AllArtwork => document.network_interface.document_bounds_document_space(!export_config.transparent_background),
|
||||||
@@ -187,28 +198,23 @@ impl NodeGraphExecutor {
|
|||||||
ExportBounds::Artboard(id) => document.metadata().bounding_box_document(id),
|
ExportBounds::Artboard(id) => document.metadata().bounding_box_document(id),
|
||||||
}
|
}
|
||||||
.ok_or_else(|| "No bounding box".to_string())?;
|
.ok_or_else(|| "No bounding box".to_string())?;
|
||||||
let size = bounds[1] - bounds[0];
|
let resolution = (bounds[1] - bounds[0]).as_uvec2();
|
||||||
let transform = DAffine2::from_translation(bounds[0]).inverse();
|
let transform = DAffine2::from_translation(bounds[0]).inverse();
|
||||||
|
|
||||||
let export_format = if export_config.file_type == FileType::Svg {
|
|
||||||
graphene_std::application_io::ExportFormat::Svg
|
|
||||||
} else {
|
|
||||||
graphene_std::application_io::ExportFormat::Raster
|
|
||||||
};
|
|
||||||
|
|
||||||
let render_config = RenderConfig {
|
let render_config = RenderConfig {
|
||||||
viewport: Footprint {
|
viewport: Footprint {
|
||||||
transform: DAffine2::from_scale(DVec2::splat(export_config.scale_factor)) * transform,
|
resolution,
|
||||||
resolution: (size * export_config.scale_factor).as_uvec2(),
|
transform,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
scale: export_config.scale_factor,
|
||||||
time: Default::default(),
|
time: Default::default(),
|
||||||
export_format,
|
export_format,
|
||||||
render_mode: document.render_mode,
|
render_mode: document.render_mode,
|
||||||
hide_artboards: export_config.transparent_background,
|
hide_artboards: export_config.transparent_background,
|
||||||
for_export: true,
|
for_export: true,
|
||||||
};
|
};
|
||||||
export_config.size = size;
|
export_config.size = resolution.as_dvec2();
|
||||||
|
|
||||||
// Execute the node graph
|
// Execute the node graph
|
||||||
self.runtime_io
|
self.runtime_io
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::application::Editor;
|
use crate::application::Editor;
|
||||||
use crate::application::set_uuid_seed;
|
use crate::application::set_uuid_seed;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
|
use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportBounds;
|
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition};
|
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition};
|
||||||
use crate::messages::portfolio::utility_types::Platform;
|
use crate::messages::portfolio::utility_types::Platform;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
@@ -10,7 +9,7 @@ use crate::messages::tool::utility_types::ToolType;
|
|||||||
use crate::node_graph_executor::Instrumented;
|
use crate::node_graph_executor::Instrumented;
|
||||||
use crate::node_graph_executor::NodeRuntime;
|
use crate::node_graph_executor::NodeRuntime;
|
||||||
use crate::test_utils::test_prelude::LayerNodeIdentifier;
|
use crate::test_utils::test_prelude::LayerNodeIdentifier;
|
||||||
use glam::DVec2;
|
use glam::{DVec2, UVec2};
|
||||||
use graph_craft::document::DocumentNode;
|
use graph_craft::document::DocumentNode;
|
||||||
use graphene_std::InputAccessor;
|
use graphene_std::InputAccessor;
|
||||||
use graphene_std::raster::color::Color;
|
use graphene_std::raster::color::Color;
|
||||||
@@ -48,8 +47,7 @@ impl EditorTestUtils {
|
|||||||
Err(e) => return Err(format!("update_node_graph_instrumented failed\n\n{e}")),
|
Err(e) => return Err(format!("update_node_graph_instrumented failed\n\n{e}")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let viewport_resolution = glam::UVec2::ONE;
|
if let Err(e) = exector.submit_current_node_graph_evaluation(document, DocumentId(0), UVec2::ONE, 1.0, Default::default()) {
|
||||||
if let Err(e) = exector.submit_current_node_graph_evaluation(document, DocumentId(0), viewport_resolution, Default::default()) {
|
|
||||||
return Err(format!("submit_current_node_graph_evaluation failed\n\n{e}"));
|
return Err(format!("submit_current_node_graph_evaluation failed\n\n{e}"));
|
||||||
}
|
}
|
||||||
runtime.run().await;
|
runtime.run().await;
|
||||||
@@ -296,8 +294,12 @@ impl EditorTestUtils {
|
|||||||
|
|
||||||
/// Necessary for doing snapping since snaps outside of the viewport are discarded
|
/// Necessary for doing snapping since snaps outside of the viewport are discarded
|
||||||
pub async fn set_viewport_size(&mut self, top_left: DVec2, bottom_right: DVec2) {
|
pub async fn set_viewport_size(&mut self, top_left: DVec2, bottom_right: DVec2) {
|
||||||
self.handle_message(InputPreprocessorMessage::BoundsOfViewports {
|
self.handle_message(ViewportMessage::Update {
|
||||||
bounds_of_viewports: vec![ViewportBounds { top_left, bottom_right }],
|
x: top_left.x,
|
||||||
|
y: top_left.y,
|
||||||
|
width: bottom_right.x - top_left.x,
|
||||||
|
height: bottom_right.y - top_left.y,
|
||||||
|
scale: 1.0,
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import type { DocumentState } from "@graphite/state-providers/document";
|
import type { DocumentState } from "@graphite/state-providers/document";
|
||||||
import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry";
|
import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry";
|
||||||
import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization";
|
import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization";
|
||||||
import { updateBoundsOfViewports } from "@graphite/utility-functions/viewports";
|
import { updateBoundsOfViewports as updateViewport } from "@graphite/utility-functions/viewports";
|
||||||
|
|
||||||
import EyedropperPreview, { ZOOM_WINDOW_DIMENSIONS } from "@graphite/components/floating-menus/EyedropperPreview.svelte";
|
import EyedropperPreview, { ZOOM_WINDOW_DIMENSIONS } from "@graphite/components/floating-menus/EyedropperPreview.svelte";
|
||||||
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
||||||
@@ -74,21 +74,21 @@
|
|||||||
let cursorEyedropperPreviewColorSecondary = "";
|
let cursorEyedropperPreviewColorSecondary = "";
|
||||||
|
|
||||||
// Canvas dimensions
|
// Canvas dimensions
|
||||||
let canvasSvgWidth: number | undefined = undefined;
|
let canvasWidth: number | undefined = undefined;
|
||||||
let canvasSvgHeight: number | undefined = undefined;
|
let canvasHeight: number | undefined = undefined;
|
||||||
|
|
||||||
let devicePixelRatio: number | undefined;
|
let devicePixelRatio: number | undefined;
|
||||||
|
|
||||||
// Dimension is rounded up to the nearest even number because resizing is centered, and dividing an odd number by 2 for centering causes antialiasing
|
// Dimension is rounded up to the nearest even number because resizing is centered, and dividing an odd number by 2 for centering causes antialiasing
|
||||||
$: canvasWidthRoundedToEven = canvasSvgWidth && (canvasSvgWidth % 2 === 1 ? canvasSvgWidth + 1 : canvasSvgWidth);
|
$: canvasWidthRoundedToEven = canvasWidth && (canvasWidth % 2 === 1 ? canvasWidth + 1 : canvasWidth);
|
||||||
$: canvasHeightRoundedToEven = canvasSvgHeight && (canvasSvgHeight % 2 === 1 ? canvasSvgHeight + 1 : canvasSvgHeight);
|
$: canvasHeightRoundedToEven = canvasHeight && (canvasHeight % 2 === 1 ? canvasHeight + 1 : canvasHeight);
|
||||||
// Used to set the canvas element size on the page.
|
// Used to set the canvas element size on the page.
|
||||||
// The value above in pixels, or if undefined, we fall back to 100% as a non-pixel-perfect backup that's hopefully short-lived
|
// The value above in pixels, or if undefined, we fall back to 100% as a non-pixel-perfect backup that's hopefully short-lived
|
||||||
$: canvasWidthCSS = canvasWidthRoundedToEven ? `${canvasWidthRoundedToEven}px` : "100%";
|
$: canvasWidthCSS = canvasWidthRoundedToEven ? `${canvasWidthRoundedToEven}px` : "100%";
|
||||||
$: canvasHeightCSS = canvasHeightRoundedToEven ? `${canvasHeightRoundedToEven}px` : "100%";
|
$: canvasHeightCSS = canvasHeightRoundedToEven ? `${canvasHeightRoundedToEven}px` : "100%";
|
||||||
|
|
||||||
$: canvasWidthScaled = canvasSvgWidth && devicePixelRatio && Math.floor(canvasSvgWidth * devicePixelRatio);
|
$: canvasWidthScaled = canvasWidth && devicePixelRatio && Math.floor(canvasWidth * devicePixelRatio);
|
||||||
$: canvasHeightScaled = canvasSvgHeight && devicePixelRatio && Math.floor(canvasSvgHeight * devicePixelRatio);
|
$: canvasHeightScaled = canvasHeight && devicePixelRatio && Math.floor(canvasHeight * devicePixelRatio);
|
||||||
|
|
||||||
// Used to set the canvas rendering dimensions.
|
// Used to set the canvas rendering dimensions.
|
||||||
$: canvasWidthScaledRoundedToEven = canvasWidthScaled && (canvasWidthScaled % 2 === 1 ? canvasWidthScaled + 1 : canvasWidthScaled);
|
$: canvasWidthScaledRoundedToEven = canvasWidthScaled && (canvasWidthScaled % 2 === 1 ? canvasWidthScaled + 1 : canvasWidthScaled);
|
||||||
@@ -226,14 +226,14 @@
|
|||||||
}
|
}
|
||||||
cursorEyedropper = true;
|
cursorEyedropper = true;
|
||||||
|
|
||||||
if (canvasSvgWidth === undefined || canvasSvgHeight === undefined) return undefined;
|
if (canvasWidth === undefined || canvasHeight === undefined) return undefined;
|
||||||
|
|
||||||
cursorLeft = mousePosition.x;
|
cursorLeft = mousePosition.x;
|
||||||
cursorTop = mousePosition.y;
|
cursorTop = mousePosition.y;
|
||||||
|
|
||||||
// This works nearly perfectly, but sometimes at odd DPI scale factors like 1.25, the anti-aliasing color can yield slightly incorrect colors (potential room for future improvement)
|
// This works nearly perfectly, but sometimes at odd DPI scale factors like 1.25, the anti-aliasing color can yield slightly incorrect colors (potential room for future improvement)
|
||||||
const dpiFactor = window.devicePixelRatio;
|
const dpiFactor = window.devicePixelRatio;
|
||||||
const [width, height] = [canvasSvgWidth, canvasSvgHeight];
|
const [width, height] = [canvasWidth, canvasHeight];
|
||||||
|
|
||||||
const outsideArtboardsColor = getComputedStyle(window.document.documentElement).getPropertyValue("--color-2-mildblack");
|
const outsideArtboardsColor = getComputedStyle(window.document.documentElement).getPropertyValue("--color-2-mildblack");
|
||||||
const outsideArtboards = `<rect x="0" y="0" width="100%" height="100%" fill="${outsideArtboardsColor}" />`;
|
const outsideArtboards = `<rect x="0" y="0" width="100%" height="100%" fill="${outsideArtboardsColor}" />`;
|
||||||
@@ -381,6 +381,22 @@
|
|||||||
showTextInput = false;
|
showTextInput = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateViewportInfo() {
|
||||||
|
if (!viewport) return;
|
||||||
|
// Resize the canvas
|
||||||
|
canvasWidth = Math.ceil(parseFloat(getComputedStyle(viewport).width));
|
||||||
|
canvasHeight = Math.ceil(parseFloat(getComputedStyle(viewport).height));
|
||||||
|
|
||||||
|
devicePixelRatio = window.devicePixelRatio || 1.0;
|
||||||
|
|
||||||
|
// Resize the rulers
|
||||||
|
rulerHorizontal?.resize();
|
||||||
|
rulerVertical?.resize();
|
||||||
|
|
||||||
|
// Send the new bounds of the viewports to the backend
|
||||||
|
if (viewport.parentElement) updateViewport(editor);
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Not compatible with Safari:
|
// Not compatible with Safari:
|
||||||
// <https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#browser_compatibility>
|
// <https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#browser_compatibility>
|
||||||
@@ -393,8 +409,7 @@
|
|||||||
mediaQueryList.addEventListener("change", updatePixelRatio);
|
mediaQueryList.addEventListener("change", updatePixelRatio);
|
||||||
removeUpdatePixelRatio = () => mediaQueryList.removeEventListener("change", updatePixelRatio);
|
removeUpdatePixelRatio = () => mediaQueryList.removeEventListener("change", updatePixelRatio);
|
||||||
|
|
||||||
devicePixelRatio = window.devicePixelRatio;
|
updateViewportInfo();
|
||||||
editor.handle.setDevicePixelRatio(devicePixelRatio);
|
|
||||||
};
|
};
|
||||||
updatePixelRatio();
|
updatePixelRatio();
|
||||||
|
|
||||||
@@ -462,18 +477,7 @@
|
|||||||
window.dispatchEvent(new Event("resize"));
|
window.dispatchEvent(new Event("resize"));
|
||||||
|
|
||||||
const viewportResizeObserver = new ResizeObserver(() => {
|
const viewportResizeObserver = new ResizeObserver(() => {
|
||||||
if (!viewport) return;
|
updateViewportInfo();
|
||||||
|
|
||||||
// Resize the canvas
|
|
||||||
canvasSvgWidth = Math.ceil(parseFloat(getComputedStyle(viewport).width));
|
|
||||||
canvasSvgHeight = Math.ceil(parseFloat(getComputedStyle(viewport).height));
|
|
||||||
|
|
||||||
// Resize the rulers
|
|
||||||
rulerHorizontal?.resize();
|
|
||||||
rulerVertical?.resize();
|
|
||||||
|
|
||||||
// Send the new bounds of the viewports to the backend
|
|
||||||
if (viewport.parentElement) updateBoundsOfViewports(editor);
|
|
||||||
});
|
});
|
||||||
if (viewport) viewportResizeObserver.observe(viewport);
|
if (viewport) viewportResizeObserver.observe(viewport);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,11 @@ import { type Editor } from "@graphite/editor";
|
|||||||
|
|
||||||
export function updateBoundsOfViewports(editor: Editor) {
|
export function updateBoundsOfViewports(editor: Editor) {
|
||||||
const viewports = Array.from(window.document.querySelectorAll("[data-viewport-container]"));
|
const viewports = Array.from(window.document.querySelectorAll("[data-viewport-container]"));
|
||||||
const boundsOfViewports = viewports.map((canvas) => {
|
|
||||||
const bounds = canvas.getBoundingClientRect();
|
|
||||||
return [bounds.left, bounds.top, bounds.right, bounds.bottom];
|
|
||||||
});
|
|
||||||
|
|
||||||
const flattened = boundsOfViewports.flat();
|
if (viewports.length <= 0) return;
|
||||||
const data = Float64Array.from(flattened);
|
|
||||||
|
|
||||||
if (boundsOfViewports.length > 0) editor.handle.boundsOfViewports(data);
|
const bounds = viewports[0].getBoundingClientRect();
|
||||||
|
const scale = window.devicePixelRatio || 1;
|
||||||
|
|
||||||
|
editor.handle.updateViewport(bounds.x, bounds.y, bounds.width, bounds.height, scale);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::helpers::translate_key;
|
|||||||
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER};
|
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER};
|
||||||
use editor::consts::FILE_EXTENSION;
|
use editor::consts::FILE_EXTENSION;
|
||||||
use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
|
use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
|
||||||
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta, ViewportBounds};
|
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta};
|
||||||
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use editor::messages::portfolio::document::utility_types::network_interface::ImportOrExport;
|
use editor::messages::portfolio::document::utility_types::network_interface::ImportOrExport;
|
||||||
use editor::messages::portfolio::utility_types::Platform;
|
use editor::messages::portfolio::utility_types::Platform;
|
||||||
@@ -516,13 +516,10 @@ impl EditorHandle {
|
|||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send new bounds when document panel viewports get resized or moved within the editor
|
/// Send new viewport info to the backend
|
||||||
/// [left, top, right, bottom]...
|
#[wasm_bindgen(js_name = updateViewport)]
|
||||||
#[wasm_bindgen(js_name = boundsOfViewports)]
|
pub fn update_viewport(&self, x: f64, y: f64, width: f64, height: f64, scale: f64) {
|
||||||
pub fn bounds_of_viewports(&self, bounds_of_viewports: &[f64]) {
|
let message = ViewportMessage::Update { x, y, width, height, scale };
|
||||||
let chunked: Vec<_> = bounds_of_viewports.chunks(4).map(ViewportBounds::from_slice).collect();
|
|
||||||
|
|
||||||
let message = InputPreprocessorMessage::BoundsOfViewports { bounds_of_viewports: chunked };
|
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,13 +530,6 @@ impl EditorHandle {
|
|||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inform the overlays system of the current device pixel ratio
|
|
||||||
#[wasm_bindgen(js_name = setDevicePixelRatio)]
|
|
||||||
pub fn set_device_pixel_ratio(&self, ratio: f64) {
|
|
||||||
let message = PortfolioMessage::SetDevicePixelRatio { ratio };
|
|
||||||
self.dispatch(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mouse movement within the screenspace bounds of the viewport
|
/// Mouse movement within the screenspace bounds of the viewport
|
||||||
#[wasm_bindgen(js_name = onMouseMove)]
|
#[wasm_bindgen(js_name = onMouseMove)]
|
||||||
pub fn on_mouse_move(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
pub fn on_mouse_move(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ pub struct TimingInformation {
|
|||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct RenderConfig {
|
pub struct RenderConfig {
|
||||||
pub viewport: Footprint,
|
pub viewport: Footprint,
|
||||||
|
pub scale: f64,
|
||||||
pub export_format: ExportFormat,
|
pub export_format: ExportFormat,
|
||||||
pub time: TimingInformation,
|
pub time: TimingInformation,
|
||||||
#[serde(alias = "view_mode")]
|
#[serde(alias = "view_mode")]
|
||||||
|
|||||||
Reference in New Issue
Block a user