mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 07:48:12 +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,
|
||||
preferences_message_handler: PreferencesMessageHandler,
|
||||
tool_message_handler: ToolMessageHandler,
|
||||
viewport_message_handler: ViewportMessageHandler,
|
||||
}
|
||||
|
||||
impl DispatcherMessageHandlers {
|
||||
@@ -146,7 +147,6 @@ impl Dispatcher {
|
||||
let context = DialogMessageContext {
|
||||
portfolio: &self.message_handlers.portfolio_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);
|
||||
}
|
||||
@@ -169,9 +169,14 @@ impl Dispatcher {
|
||||
Message::InputPreprocessor(message) => {
|
||||
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
|
||||
|
||||
self.message_handlers
|
||||
.input_preprocessor_message_handler
|
||||
.process_message(message, &mut queue, InputPreprocessorMessageContext { keyboard_platform });
|
||||
self.message_handlers.input_preprocessor_message_handler.process_message(
|
||||
message,
|
||||
&mut queue,
|
||||
InputPreprocessorMessageContext {
|
||||
keyboard_platform,
|
||||
viewport: &self.message_handlers.viewport_message_handler,
|
||||
},
|
||||
);
|
||||
}
|
||||
Message::KeyMapping(message) => {
|
||||
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 timing_information = self.message_handlers.animation_message_handler.timing_information();
|
||||
let animation = &self.message_handlers.animation_message_handler;
|
||||
let viewport = &self.message_handlers.viewport_message_handler;
|
||||
|
||||
self.message_handlers.portfolio_message_handler.process_message(
|
||||
message,
|
||||
@@ -207,6 +213,7 @@ impl Dispatcher {
|
||||
reset_node_definitions_on_open,
|
||||
timing_information,
|
||||
animation,
|
||||
viewport,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -230,10 +237,14 @@ impl Dispatcher {
|
||||
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
|
||||
node_graph: &self.message_handlers.portfolio_message_handler.executor,
|
||||
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);
|
||||
}
|
||||
Message::Viewport(message) => {
|
||||
self.message_handlers.viewport_message_handler.process_message(message, &mut queue, ());
|
||||
}
|
||||
Message::NoOp => {}
|
||||
Message::Batched { messages } => {
|
||||
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 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::prelude::*;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct DialogMessageContext<'a> {
|
||||
pub portfolio: &'a PortfolioMessageHandler,
|
||||
pub viewport_bounds: &'a ViewportBounds,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
}
|
||||
|
||||
@@ -23,15 +20,11 @@ pub struct DialogMessageHandler {
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHandler {
|
||||
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, context: DialogMessageContext) {
|
||||
let DialogMessageContext {
|
||||
portfolio,
|
||||
preferences,
|
||||
viewport_bounds,
|
||||
} = context;
|
||||
let DialogMessageContext { portfolio, preferences } = context;
|
||||
|
||||
match message {
|
||||
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::CloseAllDocumentsWithConfirmation => {
|
||||
|
||||
@@ -4,4 +4,4 @@ mod new_document_dialog_message_handler;
|
||||
#[doc(inline)]
|
||||
pub use new_document_dialog_message::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant};
|
||||
#[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::{input_mapper::utility_types::input_mouse::ViewportBounds, layout::utility_types::widget_prelude::*};
|
||||
use glam::{IVec2, UVec2};
|
||||
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.
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct NewDocumentDialogMessageHandler {
|
||||
@@ -17,8 +12,8 @@ pub struct NewDocumentDialogMessageHandler {
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<NewDocumentDialogMessage, NewDocumentDialogMessageContext<'a>> for NewDocumentDialogMessageHandler {
|
||||
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, context: NewDocumentDialogMessageContext<'a>) {
|
||||
impl<'a> MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler {
|
||||
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||
match message {
|
||||
NewDocumentDialogMessage::Name { name } => self.name = name,
|
||||
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(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(InputPreprocessorMessage::BoundsOfViewports {
|
||||
bounds_of_viewports: vec![context.viewport_bounds.clone()],
|
||||
});
|
||||
}
|
||||
|
||||
responses.add(ViewportMessage::RepropagateUpdate);
|
||||
|
||||
responses.add(DeferMessage::AfterNavigationReady {
|
||||
messages: vec![DocumentMessage::ZoomCanvasToFitAll.into(), DocumentMessage::DeselectAllLayers.into()],
|
||||
});
|
||||
|
||||
@@ -350,6 +350,12 @@ pub enum FrontendMessage {
|
||||
UpdateViewportHolePunch {
|
||||
active: bool,
|
||||
},
|
||||
UpdateViewportPhysicalBounds {
|
||||
x: f64,
|
||||
y: f64,
|
||||
width: f64,
|
||||
height: f64,
|
||||
},
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
RenderOverlays {
|
||||
#[serde(skip, default = "OverlayContext::default")]
|
||||
|
||||
@@ -3,41 +3,13 @@ use crate::messages::prelude::*;
|
||||
use bitflags::bitflags;
|
||||
use glam::DVec2;
|
||||
use std::collections::VecDeque;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
// Origin is top left
|
||||
pub type DocumentPosition = DVec2;
|
||||
pub type ViewportPosition = 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)]
|
||||
pub struct ScrollDelta {
|
||||
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 {
|
||||
position: self.editor_position - active_viewport_bounds.top_left,
|
||||
position: (viewport.logical(self.editor_position) - viewport.offset()).into(),
|
||||
mouse_keys: self.mouse_keys,
|
||||
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_mouse::{EditorMouseState, ViewportBounds};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::EditorMouseState;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[impl_message(Message, InputPreprocessor)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum InputPreprocessorMessage {
|
||||
BoundsOfViewports { bounds_of_viewports: Vec<ViewportBounds> },
|
||||
DoubleClick { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||
KeyDown { 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_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::portfolio::utility_types::KeyboardPlatformLayout;
|
||||
use crate::messages::prelude::*;
|
||||
use glam::DVec2;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct InputPreprocessorMessageContext {
|
||||
pub struct InputPreprocessorMessageContext<'a> {
|
||||
pub keyboard_platform: KeyboardPlatformLayout,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
@@ -17,38 +17,18 @@ pub struct InputPreprocessorMessageHandler {
|
||||
pub time: u64,
|
||||
pub keyboard: KeyStates,
|
||||
pub mouse: MouseState,
|
||||
pub viewport_bounds: ViewportBounds,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> for InputPreprocessorMessageHandler {
|
||||
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, context: InputPreprocessorMessageContext) {
|
||||
let InputPreprocessorMessageContext { keyboard_platform } = context;
|
||||
impl<'a> MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext<'a>> for InputPreprocessorMessageHandler {
|
||||
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, context: InputPreprocessorMessageContext<'a>) {
|
||||
let InputPreprocessorMessageContext { keyboard_platform, viewport } = context;
|
||||
|
||||
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 } => {
|
||||
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;
|
||||
|
||||
for key in mouse_state.mouse_keys {
|
||||
@@ -81,7 +61,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
||||
InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys } => {
|
||||
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.translate_mouse_event(mouse_state, true, responses);
|
||||
@@ -89,7 +69,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
||||
InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys } => {
|
||||
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;
|
||||
|
||||
responses.add(InputMapperMessage::PointerMove);
|
||||
@@ -100,7 +80,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
||||
InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys } => {
|
||||
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.translate_mouse_event(mouse_state, false, responses);
|
||||
@@ -108,7 +88,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
||||
InputPreprocessorMessage::PointerShake { editor_mouse_state, modifier_keys } => {
|
||||
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;
|
||||
|
||||
responses.add(InputMapperMessage::PointerShake);
|
||||
@@ -121,7 +101,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
|
||||
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {
|
||||
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.scroll_delta = mouse_state.scroll_delta;
|
||||
|
||||
@@ -202,11 +182,6 @@ impl InputPreprocessorMessageHandler {
|
||||
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)]
|
||||
@@ -232,6 +207,7 @@ mod test {
|
||||
|
||||
let context = InputPreprocessorMessageContext {
|
||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||
viewport: &ViewportMessageHandler::default(),
|
||||
};
|
||||
input_preprocessor.process_message(message, &mut responses, context);
|
||||
|
||||
@@ -251,6 +227,7 @@ mod test {
|
||||
|
||||
let context = InputPreprocessorMessageContext {
|
||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||
viewport: &ViewportMessageHandler::default(),
|
||||
};
|
||||
input_preprocessor.process_message(message, &mut responses, context);
|
||||
|
||||
@@ -270,6 +247,7 @@ mod test {
|
||||
|
||||
let context = InputPreprocessorMessageContext {
|
||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||
viewport: &ViewportMessageHandler::default(),
|
||||
};
|
||||
input_preprocessor.process_message(message, &mut responses, context);
|
||||
|
||||
@@ -291,6 +269,7 @@ mod test {
|
||||
|
||||
let context = InputPreprocessorMessageContext {
|
||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||
viewport: &ViewportMessageHandler::default(),
|
||||
};
|
||||
input_preprocessor.process_message(message, &mut responses, context);
|
||||
|
||||
@@ -311,6 +290,7 @@ mod test {
|
||||
|
||||
let context = InputPreprocessorMessageContext {
|
||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||
viewport: &ViewportMessageHandler::default(),
|
||||
};
|
||||
input_preprocessor.process_message(message, &mut responses, context);
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ pub enum Message {
|
||||
Preferences(PreferencesMessage),
|
||||
#[child]
|
||||
Tool(ToolMessage),
|
||||
#[child]
|
||||
Viewport(ViewportMessage),
|
||||
|
||||
// Messages
|
||||
Batched {
|
||||
|
||||
@@ -16,3 +16,4 @@ pub mod portfolio;
|
||||
pub mod preferences;
|
||||
pub mod prelude;
|
||||
pub mod tool;
|
||||
pub mod viewport;
|
||||
|
||||
@@ -52,10 +52,10 @@ pub struct DocumentMessageContext<'a> {
|
||||
pub executor: &'a mut NodeGraphExecutor,
|
||||
pub current_tool: &'a ToolType,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
pub device_pixel_ratio: f64,
|
||||
pub data_panel_open: bool,
|
||||
pub layers_panel_open: bool,
|
||||
pub properties_panel_open: bool,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ExtractField)]
|
||||
@@ -195,9 +195,9 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
ipp,
|
||||
persistent_data,
|
||||
executor,
|
||||
viewport,
|
||||
current_tool,
|
||||
preferences,
|
||||
device_pixel_ratio,
|
||||
data_panel_open,
|
||||
layers_panel_open,
|
||||
properties_panel_open,
|
||||
@@ -213,6 +213,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
document_ptz: &mut self.document_ptz,
|
||||
graph_view_overlay_open: self.graph_view_overlay_open,
|
||||
preferences,
|
||||
viewport,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
// Send the overlays message to the overlays message handler
|
||||
self.overlays_message_handler.process_message(
|
||||
message,
|
||||
responses,
|
||||
OverlaysMessageContext {
|
||||
visibility_settings,
|
||||
ipp,
|
||||
device_pixel_ratio,
|
||||
},
|
||||
);
|
||||
self.overlays_message_handler
|
||||
.process_message(message, responses, OverlaysMessageContext { visibility_settings, viewport });
|
||||
}
|
||||
DocumentMessage::PropertiesPanel(message) => {
|
||||
let context = PropertiesPanelMessageContext {
|
||||
@@ -268,6 +262,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
navigation_handler: &self.navigation_handler,
|
||||
preferences,
|
||||
layers_panel_open,
|
||||
viewport,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -380,8 +375,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
|
||||
self.layer_range_selection_reference = None;
|
||||
}
|
||||
DocumentMessage::DocumentHistoryBackward => self.undo_with_history(ipp, responses),
|
||||
DocumentMessage::DocumentHistoryForward => self.redo_with_history(ipp, responses),
|
||||
DocumentMessage::DocumentHistoryBackward => self.undo_with_history(viewport, responses),
|
||||
DocumentMessage::DocumentHistoryForward => self.redo_with_history(viewport, responses),
|
||||
DocumentMessage::DocumentStructureChanged => {
|
||||
if layers_panel_open {
|
||||
self.network_interface.load_structure();
|
||||
@@ -836,7 +831,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
let scale = DAffine2::from_scale(enlargement_factor);
|
||||
let pivot = DAffine2::from_translation(pivot);
|
||||
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)) {
|
||||
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);
|
||||
|
||||
// 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 center_in_viewport = DAffine2::from_translation(document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left));
|
||||
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 - viewport.offset().into_dvec2()));
|
||||
let center_in_viewport_layerspace = center_in_viewport;
|
||||
|
||||
// Make layer the size of the image
|
||||
@@ -913,9 +908,9 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
mouse,
|
||||
parent_and_insert_index,
|
||||
} => {
|
||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
||||
let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into());
|
||||
let center_in_viewport = DAffine2::from_translation(document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left));
|
||||
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(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 - viewport.offset().into_dvec2()));
|
||||
|
||||
let layer_node_id = NodeId::new();
|
||||
let layer_id = LayerNodeIdentifier::new_unchecked(layer_node_id);
|
||||
@@ -970,7 +965,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
} else {
|
||||
&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 {
|
||||
self.navigation_handler.snapped_zoom(current_ptz.zoom())
|
||||
@@ -1006,12 +1001,12 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
});
|
||||
}
|
||||
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 viewport_size = ipp.viewport_bounds.size();
|
||||
let viewport_mid = ipp.viewport_bounds.center();
|
||||
let viewport_size = viewport.size().into_dvec2();
|
||||
let viewport_mid = viewport.center_in_viewport_space().into_dvec2();
|
||||
let [bounds1, bounds2] = if !self.graph_view_overlay_open {
|
||||
self.metadata().document_bounds_viewport_space().unwrap_or([viewport_mid; 2])
|
||||
} else {
|
||||
@@ -1333,7 +1328,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
}
|
||||
|
||||
for _ in 0..undo_count {
|
||||
self.undo(ipp, responses);
|
||||
self.undo(viewport, responses);
|
||||
}
|
||||
|
||||
self.network_interface.finish_transaction();
|
||||
@@ -1483,7 +1478,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
}
|
||||
DocumentMessage::PTZUpdate => {
|
||||
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);
|
||||
// Ensure selection box is kept in sync with the pointer when the PTZ changes
|
||||
responses.add(SelectToolMessage::PointerMove {
|
||||
@@ -1502,7 +1497,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
|
||||
let transform = self
|
||||
.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);
|
||||
|
||||
responses.add(DocumentMessage::RenderRulers);
|
||||
@@ -1656,29 +1651,30 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
|
||||
impl DocumentMessageHandler {
|
||||
/// 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> {
|
||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
||||
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(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||
let document_quad = document_to_viewport.inverse() * viewport_quad;
|
||||
|
||||
ClickXRayIter::new(&self.network_interface, XRayTarget::Quad(document_quad))
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
self.intersect_quad(viewport_quad, ipp).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
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, viewport).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
||||
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(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||
viewport_polygon.apply_transform(document_to_viewport.inverse());
|
||||
|
||||
ClickXRayIter::new(&self.network_interface, XRayTarget::Polygon(viewport_polygon))
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
self.intersect_polygon(viewport_polygon, ipp).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
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, 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 {
|
||||
@@ -1704,8 +1700,8 @@ impl DocumentMessageHandler {
|
||||
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 {
|
||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
||||
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(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||
viewport_polygon.apply_transform(document_to_viewport.inverse());
|
||||
|
||||
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
|
||||
pub fn click_xray(&self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'_> {
|
||||
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
|
||||
pub fn click_xray(&self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'_> {
|
||||
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);
|
||||
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.
|
||||
pub fn click_list<'a>(&'a self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||
self.click_xray(ipp)
|
||||
pub fn click_list<'a>(&'a self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||
self.click_xray(ipp, viewport)
|
||||
.filter(move |&layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
.skip_while(|&layer| layer == LayerNodeIdentifier::ROOT_PARENT)
|
||||
.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.
|
||||
pub fn click_list_with_artboards<'a>(&'a self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||
self.click_xray(ipp)
|
||||
pub fn click_list_with_artboards<'a>(&'a self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||
self.click_xray(ipp, viewport)
|
||||
.skip_while(|&layer| layer == LayerNodeIdentifier::ROOT_PARENT)
|
||||
.scan(true, |last_had_children, layer| {
|
||||
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> {
|
||||
self.click_xray(ipp)
|
||||
pub fn click_list_no_parents<'a>(&'a self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||
self.click_xray(ipp, viewport)
|
||||
.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.
|
||||
pub fn click(&self, ipp: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||
self.click_list(ipp).last()
|
||||
pub fn click(&self, ipp: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||
self.click_list(ipp, viewport).last()
|
||||
}
|
||||
|
||||
pub fn click_based_on_position(&self, mouse_snapped_positon: DVec2) -> Option<LayerNodeIdentifier> {
|
||||
@@ -1943,8 +1939,8 @@ impl DocumentMessageHandler {
|
||||
structure_section.as_slice().into()
|
||||
}
|
||||
|
||||
pub fn undo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
let Some(previous_network) = self.undo(ipp, responses) else { return };
|
||||
pub fn undo_with_history(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
let Some(previous_network) = self.undo(viewport, responses) else { return };
|
||||
|
||||
self.document_redo_history.push_back(previous_network);
|
||||
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
|
||||
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);
|
||||
|
||||
//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);
|
||||
|
||||
// Ensure document structure is loaded so that updating the selected nodes has the correct metadata
|
||||
@@ -1980,9 +1976,9 @@ impl DocumentMessageHandler {
|
||||
|
||||
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
|
||||
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);
|
||||
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
|
||||
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);
|
||||
|
||||
//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);
|
||||
|
||||
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.
|
||||
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_clicked_artboard = self
|
||||
.click_xray(ipp)
|
||||
.click_xray(ipp, viewport)
|
||||
.find(|layer| self.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
.unwrap_or(LayerNodeIdentifier::ROOT_PARENT);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ pub struct NavigationMessageContext<'a> {
|
||||
pub document_ptz: &'a mut PTZ,
|
||||
pub graph_view_overlay_open: bool,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default, ExtractField)]
|
||||
@@ -41,6 +42,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
document_ptz,
|
||||
graph_view_overlay_open,
|
||||
preferences,
|
||||
viewport,
|
||||
} = 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> {
|
||||
@@ -135,7 +137,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
log::error!("Could not get PTZ in CanvasPan");
|
||||
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);
|
||||
|
||||
ptz.pan += transformed_delta;
|
||||
@@ -169,8 +171,8 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
||||
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
||||
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 * viewport.size().into_dvec2());
|
||||
|
||||
ptz.pan += transformed_delta;
|
||||
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());
|
||||
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 });
|
||||
}
|
||||
@@ -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());
|
||||
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 });
|
||||
}
|
||||
@@ -245,9 +247,9 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
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 {
|
||||
zoom_factor: ptz.zoom() * zoom_factor,
|
||||
});
|
||||
@@ -264,7 +266,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
return;
|
||||
};
|
||||
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);
|
||||
if graph_view_overlay_open {
|
||||
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||
@@ -343,7 +345,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
let (pos1, pos2) = (pos1.min(pos2), pos1.max(pos2));
|
||||
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");
|
||||
return;
|
||||
}
|
||||
@@ -352,10 +354,10 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
||||
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 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 size = (v2 - v1) / diagonal;
|
||||
@@ -397,7 +399,8 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
log::error!("Could not get node graph PTZ in FitViewportToSelection");
|
||||
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 {
|
||||
bounds: [document_to_viewport.inverse().transform_point2(bounds[0]), document_to_viewport.inverse().transform_point2(bounds[1])],
|
||||
prevent_zoom_past_100: false,
|
||||
@@ -419,7 +422,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
||||
let tilt_raw_not_snapped = {
|
||||
// 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 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 end_offset = viewport_to_document.transform_vector2(ipp.mouse.position - half_viewport);
|
||||
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)
|
||||
};
|
||||
|
||||
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 {
|
||||
log::error!("Could not get mutable PTZ in Zoom");
|
||||
@@ -563,9 +566,9 @@ impl NavigationMessageHandler {
|
||||
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 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 {
|
||||
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::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
use crate::messages::viewport::{Position, Rect};
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
||||
use graph_craft::proto::GraphErrors;
|
||||
@@ -43,6 +44,7 @@ pub struct NodeGraphMessageContext<'a> {
|
||||
pub navigation_handler: &'a NavigationMessageHandler,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
pub layers_panel_open: bool,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, ExtractField)]
|
||||
@@ -113,6 +115,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
navigation_handler,
|
||||
preferences,
|
||||
layers_panel_open,
|
||||
viewport,
|
||||
} = context;
|
||||
|
||||
match message {
|
||||
@@ -783,12 +786,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
|
||||
// TODO: Create function
|
||||
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_above_mouse = if click.y > ipp.viewport_bounds.size().y - 200. { -200. } else { 0. };
|
||||
let appear_right_of_mouse = if click.x > viewport.size().x() - 180. { -180. } 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
|
||||
} else {
|
||||
let appear_right_of_mouse = if click.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. };
|
||||
let appear_above_mouse = if click.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. };
|
||||
let appear_right_of_mouse = if click.x > viewport.size().x() - 173. { -173. } 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
|
||||
};
|
||||
|
||||
@@ -983,7 +986,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
|
||||
// Auto-panning
|
||||
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 point = network_metadata
|
||||
@@ -1218,8 +1221,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
_ => 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_above_mouse = if ipp.mouse.position.y > ipp.viewport_bounds.size().y - 34. { -34. } 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 > 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;
|
||||
|
||||
self.context_menu = Some(ContextMenuInformation {
|
||||
@@ -1403,7 +1406,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
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()) {
|
||||
let _ = self.auto_panning.shift_viewport(ipp, responses);
|
||||
let _ = self.auto_panning.shift_viewport(ipp, viewport, responses);
|
||||
} else {
|
||||
// Auto-panning
|
||||
let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()];
|
||||
@@ -1611,7 +1614,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
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 mut nodes = Vec::new();
|
||||
@@ -1657,7 +1661,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
NodeGraphMessage::SetGridAlignedEdges => {
|
||||
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
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 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 cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||
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_b = angle_b.to_radians().tan();
|
||||
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 cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||
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_b = angle_b.to_radians().tan();
|
||||
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
||||
|
||||
@@ -4,8 +4,7 @@ use crate::messages::prelude::*;
|
||||
#[derive(ExtractField)]
|
||||
pub struct OverlaysMessageContext<'a> {
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub device_pixel_ratio: f64,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
@@ -20,19 +19,14 @@ pub struct OverlaysMessageHandler {
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMessageHandler {
|
||||
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, context: OverlaysMessageContext) {
|
||||
let OverlaysMessageContext {
|
||||
visibility_settings,
|
||||
ipp,
|
||||
device_pixel_ratio,
|
||||
..
|
||||
} = context;
|
||||
let OverlaysMessageContext { visibility_settings, viewport, .. } = context;
|
||||
|
||||
match message {
|
||||
#[cfg(target_family = "wasm")]
|
||||
OverlaysMessage::Draw => {
|
||||
use super::utility_functions::overlay_canvas_element;
|
||||
use super::utility_types::OverlayContext;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use crate::messages::viewport::{Position, ToPhysical};
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
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")
|
||||
});
|
||||
|
||||
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();
|
||||
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();
|
||||
canvas_context.clear_rect(0., 0., width, height);
|
||||
|
||||
if visibility_settings.all() {
|
||||
responses.add(DocumentMessage::GridOverlays {
|
||||
context: OverlayContext {
|
||||
render_context: canvas_context.clone(),
|
||||
size: size.as_dvec2(),
|
||||
device_pixel_ratio,
|
||||
visibility_settings: visibility_settings.clone(),
|
||||
viewport: *viewport,
|
||||
},
|
||||
});
|
||||
for provider in &self.overlay_providers {
|
||||
responses.add(provider(OverlayContext {
|
||||
render_context: canvas_context.clone(),
|
||||
size: size.as_dvec2(),
|
||||
device_pixel_ratio,
|
||||
visibility_settings: visibility_settings.clone(),
|
||||
viewport: *viewport,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -78,9 +70,7 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
|
||||
OverlaysMessage::Draw => {
|
||||
use super::utility_types::OverlayContext;
|
||||
|
||||
let size = ipp.viewport_bounds.size();
|
||||
|
||||
let overlay_context = OverlayContext::new(size, device_pixel_ratio, visibility_settings);
|
||||
let overlay_context = OverlayContext::new(*viewport, visibility_settings);
|
||||
|
||||
if visibility_settings.all() {
|
||||
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))]
|
||||
OverlaysMessage::Draw => {
|
||||
let _ = (responses, visibility_settings, ipp, device_pixel_ratio);
|
||||
let _ = (responses, visibility_settings, viewport);
|
||||
}
|
||||
OverlaysMessage::AddProvider { provider: 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 {
|
||||
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 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));
|
||||
|
||||
@@ -2,10 +2,11 @@ use super::utility_functions::overlay_canvas_context;
|
||||
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,
|
||||
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::prelude::Message;
|
||||
use crate::messages::viewport::ViewportMessageHandler;
|
||||
use core::borrow::Borrow;
|
||||
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -141,10 +142,7 @@ pub struct OverlayContext {
|
||||
#[serde(skip, default = "overlay_canvas_context")]
|
||||
#[specta(skip)]
|
||||
pub render_context: web_sys::CanvasRenderingContext2d,
|
||||
pub size: DVec2,
|
||||
// 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 viewport: ViewportMessageHandler,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
|
||||
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
|
||||
///
|
||||
/// Overwrites all existing tranforms. This operation can be reversed with [`Self::reset_transform`].
|
||||
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
|
||||
.set_transform(a, b, c, d, e, f)
|
||||
.expect("transform should be able to be set to be able to account for DPI");
|
||||
@@ -967,7 +979,7 @@ impl OverlayContext {
|
||||
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");
|
||||
|
||||
if let Some(background) = background_color {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
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,
|
||||
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::prelude::Message;
|
||||
use crate::messages::prelude::ViewportMessageHandler;
|
||||
use core::borrow::Borrow;
|
||||
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -157,24 +158,18 @@ pub struct OverlayContext {
|
||||
#[serde(skip)]
|
||||
#[specta(skip)]
|
||||
internal: Arc<Mutex<OverlayContextInternal>>,
|
||||
pub size: DVec2,
|
||||
// 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 viewport: ViewportMessageHandler,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
}
|
||||
|
||||
impl Clone for OverlayContext {
|
||||
fn clone(&self) -> Self {
|
||||
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;
|
||||
drop(internal); // Explicitly release the lock before cloning the Arc<Mutex<_>>
|
||||
Self {
|
||||
internal: self.internal.clone(),
|
||||
size,
|
||||
device_pixel_ratio,
|
||||
viewport: self.viewport,
|
||||
visibility_settings,
|
||||
}
|
||||
}
|
||||
@@ -183,7 +178,7 @@ impl Clone for OverlayContext {
|
||||
// Manual implementations since Scene doesn't implement PartialEq or Debug
|
||||
impl PartialEq for OverlayContext {
|
||||
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 {
|
||||
f.debug_struct("OverlayContext")
|
||||
.field("scene", &"Scene { ... }")
|
||||
.field("size", &self.size)
|
||||
.field("device_pixel_ratio", &self.device_pixel_ratio)
|
||||
.field("viewport", &self.viewport)
|
||||
.field("visibility_settings", &self.visibility_settings)
|
||||
.finish()
|
||||
}
|
||||
@@ -203,8 +197,7 @@ impl Default for OverlayContext {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
internal: Mutex::new(OverlayContextInternal::default()).into(),
|
||||
size: DVec2::ZERO,
|
||||
device_pixel_ratio: 1.0,
|
||||
viewport: ViewportMessageHandler::default(),
|
||||
visibility_settings: OverlaysVisibilitySettings::default(),
|
||||
}
|
||||
}
|
||||
@@ -217,11 +210,10 @@ impl core::hash::Hash for OverlayContext {
|
||||
|
||||
impl OverlayContext {
|
||||
#[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 {
|
||||
internal: Arc::new(Mutex::new(OverlayContextInternal::new(size, device_pixel_ratio, visibility_settings))),
|
||||
size,
|
||||
device_pixel_ratio,
|
||||
internal: Arc::new(Mutex::new(OverlayContextInternal::new(viewport, visibility_settings))),
|
||||
viewport,
|
||||
visibility_settings,
|
||||
}
|
||||
}
|
||||
@@ -280,6 +272,14 @@ impl OverlayContext {
|
||||
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>) {
|
||||
self.internal().square(position, size, color_fill, color_stroke);
|
||||
}
|
||||
@@ -292,6 +292,7 @@ impl OverlayContext {
|
||||
self.internal().circle(position, radius, color_fill, color_stroke);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn dashed_ellipse(
|
||||
&mut self,
|
||||
center: DVec2,
|
||||
@@ -421,23 +422,21 @@ pub enum DrawHandles {
|
||||
|
||||
pub(super) struct OverlayContextInternal {
|
||||
scene: Scene,
|
||||
size: DVec2,
|
||||
device_pixel_ratio: f64,
|
||||
viewport: ViewportMessageHandler,
|
||||
visibility_settings: OverlaysVisibilitySettings,
|
||||
}
|
||||
|
||||
impl Default for OverlayContextInternal {
|
||||
fn default() -> Self {
|
||||
Self::new(DVec2::new(100., 100.), 1., OverlaysVisibilitySettings::default())
|
||||
Self::new(ViewportMessageHandler::default(), OverlaysVisibilitySettings::default())
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
scene: Scene::new(),
|
||||
size,
|
||||
device_pixel_ratio,
|
||||
viewport,
|
||||
visibility_settings,
|
||||
}
|
||||
}
|
||||
@@ -473,7 +472,7 @@ impl OverlayContextInternal {
|
||||
|
||||
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>) {
|
||||
@@ -506,7 +505,7 @@ impl OverlayContextInternal {
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
.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) {
|
||||
@@ -563,13 +562,13 @@ impl OverlayContextInternal {
|
||||
|
||||
let fill = COLOR_OVERLAY_BLUE_50;
|
||||
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 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.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>) {
|
||||
@@ -584,8 +583,22 @@ impl OverlayContextInternal {
|
||||
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 {
|
||||
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>) {
|
||||
@@ -601,7 +614,7 @@ impl OverlayContextInternal {
|
||||
|
||||
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>) {
|
||||
@@ -627,9 +640,10 @@ impl OverlayContextInternal {
|
||||
|
||||
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(
|
||||
&mut self,
|
||||
_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) {
|
||||
@@ -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();
|
||||
fill_color.insert(0, '#');
|
||||
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 * scale.abs(), fill_color, None);
|
||||
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>) {
|
||||
const HOVER_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_HOVER_RING_DIAMETER / 2.;
|
||||
const MAIN_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_MAIN_RING_DIAMETER / 2.;
|
||||
const MAIN_RING_INNER_RADIUS: f64 = COMPASS_ROSE_RING_INNER_DIAMETER / 2.;
|
||||
const ARROW_RADIUS: f64 = COMPASS_ROSE_ARROW_SIZE / 2.;
|
||||
const 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.;
|
||||
const 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 hover_ring_outer_radius: f64 = COMPASS_ROSE_HOVER_RING_DIAMETER / 2.;
|
||||
let main_ring_outer_radius: f64 = COMPASS_ROSE_MAIN_RING_DIAMETER / 2.;
|
||||
let main_ring_inner_radius: f64 = COMPASS_ROSE_RING_INNER_DIAMETER / 2.;
|
||||
let arrow_radius: f64 = COMPASS_ROSE_ARROW_SIZE / 2.;
|
||||
let hover_ring_stroke_width: f64 = hover_ring_outer_radius - main_ring_inner_radius;
|
||||
let hover_ring_centerline_radius: f64 = (hover_ring_outer_radius + main_ring_inner_radius) / 2.;
|
||||
let main_ring_stroke_width: f64 = main_ring_outer_radius - main_ring_inner_radius;
|
||||
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 };
|
||||
|
||||
@@ -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();
|
||||
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
|
||||
.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
|
||||
@@ -743,11 +757,11 @@ impl OverlayContextInternal {
|
||||
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 tip = center + direction * HOVER_RING_OUTER_RADIUS;
|
||||
let base = center + direction * (MAIN_RING_INNER_RADIUS + MAIN_RING_OUTER_RADIUS) / 2.;
|
||||
let tip = center + direction * hover_ring_outer_radius;
|
||||
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 (cos, sin) = (MAIN_RING_INNER_RADIUS / r, ARROW_RADIUS / r);
|
||||
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 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);
|
||||
|
||||
@@ -764,9 +778,9 @@ impl OverlayContextInternal {
|
||||
}
|
||||
|
||||
// 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
|
||||
.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) {
|
||||
@@ -780,22 +794,22 @@ impl OverlayContextInternal {
|
||||
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &circle);
|
||||
|
||||
// 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);
|
||||
stroke = stroke.with_caps(kurbo::Cap::Round);
|
||||
|
||||
// Horizontal line
|
||||
let mut path = BezPath::new();
|
||||
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.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));
|
||||
|
||||
self.scene.stroke(&stroke, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &path);
|
||||
|
||||
// Vertical line
|
||||
let mut path = BezPath::new();
|
||||
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.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));
|
||||
|
||||
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
|
||||
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.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
|
||||
let mut path = BezPath::new();
|
||||
|
||||
// Top-left sector
|
||||
path.move_to(kurbo::Point::new(x, y));
|
||||
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_x = x + DOWEL_PIN_RADIUS * (FRAC_PI_2 + angle.cos());
|
||||
let end_y = y + DOWEL_PIN_RADIUS * (FRAC_PI_2 + angle.sin());
|
||||
path.line_to(kurbo::Point::new(end_x, end_y));
|
||||
// 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);
|
||||
@@ -828,8 +842,8 @@ impl OverlayContextInternal {
|
||||
|
||||
// Bottom-right sector
|
||||
path.move_to(kurbo::Point::new(x, y));
|
||||
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_x = x + DOWEL_PIN_RADIUS * (PI + FRAC_PI_2 + angle.cos());
|
||||
let end_y = y + DOWEL_PIN_RADIUS * (PI + FRAC_PI_2 + angle.sin());
|
||||
path.line_to(kurbo::Point::new(end_x, end_y));
|
||||
// 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);
|
||||
@@ -861,7 +875,7 @@ impl OverlayContextInternal {
|
||||
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.
|
||||
@@ -870,7 +884,7 @@ impl OverlayContextInternal {
|
||||
let mut path = BezPath::new();
|
||||
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.
|
||||
@@ -963,7 +977,7 @@ impl OverlayContextInternal {
|
||||
let path = self.push_path(subpaths.iter(), transform);
|
||||
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,
|
||||
y_extend: peniko::Extend::Repeat,
|
||||
quality: peniko::ImageQuality::default(),
|
||||
alpha: 1.0,
|
||||
alpha: 1.,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -112,9 +112,6 @@ pub enum PortfolioMessage {
|
||||
SetActivePanel {
|
||||
panel: PanelType,
|
||||
},
|
||||
SetDevicePixelRatio {
|
||||
ratio: f64,
|
||||
},
|
||||
SelectDocument {
|
||||
document_id: DocumentId,
|
||||
},
|
||||
|
||||
@@ -42,6 +42,7 @@ pub struct PortfolioMessageContext<'a> {
|
||||
pub message_logging_verbosity: MessageLoggingVerbosity,
|
||||
pub reset_node_definitions_on_open: bool,
|
||||
pub timing_information: TimingInformation,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Derivative, ExtractField)]
|
||||
@@ -56,7 +57,6 @@ pub struct PortfolioMessageHandler {
|
||||
pub persistent_data: PersistentData,
|
||||
pub executor: NodeGraphExecutor,
|
||||
pub selection_mode: SelectionMode,
|
||||
device_pixel_ratio: Option<f64>,
|
||||
pub reset_node_definitions_on_open: bool,
|
||||
pub data_panel_open: bool,
|
||||
#[derivative(Default(value = "true"))]
|
||||
@@ -76,6 +76,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
message_logging_verbosity,
|
||||
reset_node_definitions_on_open,
|
||||
timing_information,
|
||||
viewport,
|
||||
} = context;
|
||||
|
||||
match message {
|
||||
@@ -124,7 +125,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
executor: &mut self.executor,
|
||||
current_tool,
|
||||
preferences,
|
||||
device_pixel_ratio: self.device_pixel_ratio.unwrap_or(1.),
|
||||
viewport,
|
||||
data_panel_open: self.data_panel_open,
|
||||
layers_panel_open: self.layers_panel_open,
|
||||
properties_panel_open: self.properties_panel_open,
|
||||
@@ -165,7 +166,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
executor: &mut self.executor,
|
||||
current_tool,
|
||||
preferences,
|
||||
device_pixel_ratio: self.device_pixel_ratio.unwrap_or(1.),
|
||||
viewport,
|
||||
data_panel_open: self.data_panel_open,
|
||||
layers_panel_open: self.layers_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());
|
||||
for document_id in self.document_ids.iter() {
|
||||
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(
|
||||
self.documents.get_mut(document_id).expect("Tried to render non-existent document"),
|
||||
*document_id,
|
||||
ipp.viewport_bounds.size().as_uvec2(),
|
||||
resolution,
|
||||
scale,
|
||||
timing_information,
|
||||
node_to_inspect,
|
||||
true,
|
||||
@@ -699,7 +705,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
}
|
||||
PortfolioMessage::CenterPastedLayers { layers } => {
|
||||
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 doc_to_viewport_transform = document.metadata().document_to_viewport;
|
||||
@@ -878,10 +884,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
self.active_panel = 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 } => {
|
||||
// Auto-save the document we are leaving
|
||||
let mut node_graph_open = false;
|
||||
@@ -961,15 +963,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
}
|
||||
PortfolioMessage::SubmitGraphRender { document_id, ignore_hash } => {
|
||||
let node_to_inspect = self.node_to_inspect();
|
||||
|
||||
let Some(document) = self.documents.get_mut(&document_id) else {
|
||||
log::error!("Tried to render non-existent document");
|
||||
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
|
||||
.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 {
|
||||
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::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler};
|
||||
pub use crate::messages::tool::{ToolMessage, ToolMessageContext, ToolMessageDiscriminant, ToolMessageHandler};
|
||||
pub use crate::messages::viewport::{ViewportMessage, ViewportMessageDiscriminant, ViewportMessageHandler};
|
||||
|
||||
// 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 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;
|
||||
|
||||
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.
|
||||
/// 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.
|
||||
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 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let viewport_size = input.viewport_bounds.size();
|
||||
let viewport_size = viewport.size().into_dvec2();
|
||||
let mouse_position = input.mouse.position.clamp(
|
||||
DVec2::ZERO - 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 {
|
||||
PointRadiusHandleState::Inactive => {
|
||||
let Some(layer) = selected_star_layer else { return };
|
||||
@@ -187,7 +187,7 @@ impl PointRadiusHandle {
|
||||
|
||||
let viewport = document.metadata().transform_to_viewport(layer);
|
||||
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
|
||||
if let Some((sides, radius1, radius2)) = extract_star_parameters(Some(layer), document) {
|
||||
|
||||
@@ -15,10 +15,10 @@ pub struct Resize {
|
||||
|
||||
impl Resize {
|
||||
/// 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 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;
|
||||
}
|
||||
|
||||
@@ -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`].
|
||||
/// 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?;
|
||||
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
@@ -42,21 +49,37 @@ impl Resize {
|
||||
self.layer.take();
|
||||
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.
|
||||
/// 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 center = input.keyboard.get(center as usize);
|
||||
|
||||
// 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> {
|
||||
let points_viewport = self.calculate_points(document, input, center, lock_ratio)?;
|
||||
pub fn calculate_transform(
|
||||
&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(
|
||||
GraphOperationMessage::TransformSet {
|
||||
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);
|
||||
|
||||
// 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.
|
||||
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 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 mut points_viewport = [start, mouse];
|
||||
|
||||
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 {
|
||||
let viewport_size = points_viewport[1] - points_viewport[0];
|
||||
|
||||
@@ -503,8 +503,16 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
// Snap, returning a viewport delta
|
||||
pub fn snap(&self, snap_manager: &mut SnapManager, snap_cache: &SnapCache, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, previous_mouse: DVec2) -> DVec2 {
|
||||
let snap_data = SnapData::new_snap_cache(document, input, snap_cache);
|
||||
pub fn snap(
|
||||
&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
|
||||
.network_interface
|
||||
|
||||
@@ -146,13 +146,14 @@ impl Arc {
|
||||
pub fn update_shape(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) {
|
||||
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 {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -89,13 +89,14 @@ impl Circle {
|
||||
pub fn update_shape(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) {
|
||||
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 {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ impl Ellipse {
|
||||
pub fn update_shape(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
@@ -30,7 +31,7 @@ impl Ellipse {
|
||||
) {
|
||||
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 {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -48,6 +48,7 @@ impl Line {
|
||||
pub fn update_shape(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
@@ -59,7 +60,7 @@ impl Line {
|
||||
|
||||
let keyboard = &ipp.keyboard;
|
||||
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));
|
||||
|
||||
if shape_tool_data.line_data.dragging_endpoint == Some(LineEnd::Start) {
|
||||
|
||||
@@ -58,13 +58,13 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
||||
&self,
|
||||
document: &DocumentMessageHandler,
|
||||
selected_polygon_layer: Option<LayerNodeIdentifier>,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
_input: &InputPreprocessorMessageHandler,
|
||||
shape_editor: &mut &mut ShapeState,
|
||||
mouse_position: DVec2,
|
||||
overlay_context: &mut OverlayContext,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
||||
fn dragging_overlays(
|
||||
&self,
|
||||
document: &DocumentMessageHandler,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
_input: &InputPreprocessorMessageHandler,
|
||||
shape_editor: &mut &mut ShapeState,
|
||||
mouse_position: DVec2,
|
||||
overlay_context: &mut OverlayContext,
|
||||
@@ -82,7 +82,7 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
||||
}
|
||||
|
||||
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(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
@@ -123,7 +124,7 @@ impl Polygon {
|
||||
) {
|
||||
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
|
||||
update_radius_sign(end, start, layer, document, responses);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ impl Rectangle {
|
||||
pub fn update_shape(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
@@ -30,7 +31,7 @@ impl Rectangle {
|
||||
) {
|
||||
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 {
|
||||
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::*;
|
||||
|
||||
let viewport_drag_start = shape_tool_data.data.viewport_drag_start(document);
|
||||
|
||||
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 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);
|
||||
|
||||
@@ -58,13 +58,13 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
||||
&self,
|
||||
document: &DocumentMessageHandler,
|
||||
selected_star_layer: Option<LayerNodeIdentifier>,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
_input: &InputPreprocessorMessageHandler,
|
||||
shape_editor: &mut &mut ShapeState,
|
||||
mouse_position: DVec2,
|
||||
overlay_context: &mut OverlayContext,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
||||
fn dragging_overlays(
|
||||
&self,
|
||||
document: &DocumentMessageHandler,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
_input: &InputPreprocessorMessageHandler,
|
||||
shape_editor: &mut &mut ShapeState,
|
||||
mouse_position: DVec2,
|
||||
overlay_context: &mut OverlayContext,
|
||||
@@ -82,7 +82,7 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
||||
}
|
||||
|
||||
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(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
@@ -128,7 +129,7 @@ impl Star {
|
||||
) {
|
||||
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
|
||||
update_radius_sign(end, start, layer, document, responses);
|
||||
|
||||
|
||||
@@ -206,29 +206,31 @@ pub struct SnapCache {
|
||||
pub struct SnapData<'a> {
|
||||
pub document: &'a DocumentMessageHandler,
|
||||
pub input: &'a InputPreprocessorMessageHandler,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
pub ignore: &'a [LayerNodeIdentifier],
|
||||
pub node_snap_cache: Option<&'a SnapCache>,
|
||||
pub candidates: Option<&'a Vec<LayerNodeIdentifier>>,
|
||||
pub alignment_candidates: Option<&'a Vec<LayerNodeIdentifier>>,
|
||||
}
|
||||
impl<'a> SnapData<'a> {
|
||||
pub fn new(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler) -> Self {
|
||||
Self::ignore(document, input, &[])
|
||||
pub fn new(document: &'a DocumentMessageHandler, input: &'a InputPreprocessorMessageHandler, viewport: &'a ViewportMessageHandler) -> Self {
|
||||
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 {
|
||||
document,
|
||||
input,
|
||||
viewport,
|
||||
ignore,
|
||||
candidates: None,
|
||||
alignment_candidates: 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 {
|
||||
node_snap_cache: Some(snap_cache),
|
||||
..Self::new(document, input)
|
||||
..Self::new(document, input, viewport)
|
||||
}
|
||||
}
|
||||
fn get_candidates(&self) -> &[LayerNodeIdentifier] {
|
||||
@@ -301,7 +303,7 @@ impl SnapManager {
|
||||
|
||||
for point in snapped_points {
|
||||
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 {
|
||||
continue;
|
||||
}
|
||||
@@ -337,7 +339,7 @@ impl SnapManager {
|
||||
return;
|
||||
};
|
||||
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 self.alignment_candidates.as_ref().is_none_or(|candidates| candidates.len() <= 100) {
|
||||
self.alignment_candidates.get_or_insert_with(Vec::new).push(layer);
|
||||
|
||||
@@ -76,7 +76,7 @@ impl DistributionSnapper {
|
||||
self.down.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();
|
||||
|
||||
// Collect artboard bounds
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::snapping::{self, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnappedPoint};
|
||||
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,
|
||||
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,
|
||||
SELECTION_DRAG_ANGLE, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
|
||||
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_MIDPOINT_VISIBILITY, MIN_LENGTH_FOR_RESIZE_TO_INCLUDE_INTERIOR, MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY, RESIZE_HANDLE_SIZE, SELECTION_DRAG_ANGLE, SKEW_TRIANGLE_OFFSET,
|
||||
SKEW_TRIANGLE_SIZE,
|
||||
};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
overlay_context.skew_handles(start, end);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 vertical_angle = (quad.top_left() - quad.top_right()).to_angle();
|
||||
|
||||
@@ -579,7 +568,7 @@ impl BoundingBoxManager {
|
||||
TransformCageSizeCategory::Full | TransformCageSizeCategory::Narrow | TransformCageSizeCategory::ReducedLandscape
|
||||
) {
|
||||
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
|
||||
) {
|
||||
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
|
||||
) {
|
||||
for point in quad.0 {
|
||||
draw_handle(point, angle);
|
||||
overlay_context.resize_handle(point, angle);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the flat line endpoint drag handles
|
||||
if category == TransformCageSizeCategory::Flat {
|
||||
draw_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[0]), 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_candidates: &mut Vec<SnapCandidatePoint>,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
center: bool,
|
||||
constrain: bool,
|
||||
tool: ToolType,
|
||||
@@ -271,7 +272,7 @@ pub fn resize_bounds(
|
||||
let snap = Some(SizeSnapData {
|
||||
manager: snap_manager,
|
||||
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 (delta, mut pivot) = movement.bounds_to_scale_transform(position, size);
|
||||
|
||||
@@ -21,6 +21,7 @@ pub struct ToolMessageContext<'a> {
|
||||
pub persistent_data: &'a PersistentData,
|
||||
pub node_graph: &'a NodeGraphExecutor,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
@@ -41,6 +42,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
||||
persistent_data,
|
||||
node_graph,
|
||||
preferences,
|
||||
viewport,
|
||||
} = context;
|
||||
let font_cache = &persistent_data.font_cache;
|
||||
|
||||
@@ -54,6 +56,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
||||
input,
|
||||
tool_data: &self.tool_state.tool_data,
|
||||
shape_editor: &mut self.shape_editor,
|
||||
viewport,
|
||||
},
|
||||
),
|
||||
|
||||
@@ -123,6 +126,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
||||
shape_editor: &mut self.shape_editor,
|
||||
node_graph,
|
||||
preferences,
|
||||
viewport,
|
||||
};
|
||||
|
||||
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,
|
||||
node_graph,
|
||||
preferences,
|
||||
viewport,
|
||||
};
|
||||
|
||||
// Set initial hints and cursor
|
||||
@@ -336,6 +341,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
||||
shape_editor: &mut self.shape_editor,
|
||||
node_graph,
|
||||
preferences,
|
||||
viewport,
|
||||
};
|
||||
if matches!(tool_message, ToolMessage::UpdateHints) {
|
||||
if graph_view_overlay_open {
|
||||
|
||||
@@ -145,12 +145,12 @@ impl ArtboardToolData {
|
||||
}
|
||||
}
|
||||
|
||||
fn hovered_artboard(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||
document.click_xray(input).find(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
fn hovered_artboard(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||
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 {
|
||||
if let Some(intersection) = Self::hovered_artboard(document, input) {
|
||||
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, viewport) {
|
||||
self.selected_artboard = Some(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 {
|
||||
return;
|
||||
};
|
||||
@@ -193,7 +201,7 @@ impl ArtboardToolData {
|
||||
let snap = Some(SizeSnapData {
|
||||
manager: &mut self.draw.snap_manager,
|
||||
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 max = min + size;
|
||||
@@ -225,9 +233,9 @@ impl Fsm for ArtboardToolFsmState {
|
||||
type ToolOptions = ();
|
||||
|
||||
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 };
|
||||
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);
|
||||
|
||||
// Find hovered artboard or regular layer
|
||||
let hovered_artboard = ArtboardToolData::hovered_artboard(document, input);
|
||||
let hovered_layer = document.click_xray(input).find(|&layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||
let hovered_artboard = ArtboardToolData::hovered_artboard(document, input, viewport);
|
||||
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)
|
||||
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
|
||||
}
|
||||
@@ -295,11 +303,11 @@ impl Fsm for ArtboardToolFsmState {
|
||||
tool_data.start_resizing(selected_edges, document, input);
|
||||
tool_data.get_snap_candidates(document, input);
|
||||
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);
|
||||
ArtboardToolFsmState::Dragging
|
||||
} else {
|
||||
tool_data.draw.start(document, input);
|
||||
tool_data.draw.start(document, input, viewport);
|
||||
|
||||
ArtboardToolFsmState::Drawing
|
||||
};
|
||||
@@ -309,14 +317,14 @@ impl Fsm for ArtboardToolFsmState {
|
||||
(ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
|
||||
let from_center = input.keyboard.get(center 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
|
||||
let messages = [
|
||||
ArtboardToolMessage::PointerOutsideViewport { 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
|
||||
}
|
||||
@@ -325,7 +333,7 @@ impl Fsm for ArtboardToolFsmState {
|
||||
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 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 [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);
|
||||
@@ -355,14 +363,14 @@ impl Fsm for ArtboardToolFsmState {
|
||||
ArtboardToolMessage::PointerOutsideViewport { 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::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
|
||||
// The draw.calculate_points_ignore_layer uses this value to avoid snapping to itself.
|
||||
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 [start, end] = [start, end].map(|point| viewport_to_document.transform_point2(point));
|
||||
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::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
|
||||
}
|
||||
@@ -408,7 +416,7 @@ impl Fsm for ArtboardToolFsmState {
|
||||
.map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, false, false, None));
|
||||
|
||||
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);
|
||||
cursor = MouseCursorIcon::Crosshair;
|
||||
} else {
|
||||
@@ -424,19 +432,19 @@ impl Fsm for ArtboardToolFsmState {
|
||||
}
|
||||
(ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
|
||||
ArtboardToolFsmState::ResizingBounds
|
||||
}
|
||||
(ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// Auto-panning
|
||||
tool_data.auto_panning.shift_viewport(input, responses);
|
||||
tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
|
||||
ArtboardToolFsmState::Dragging
|
||||
}
|
||||
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// Auto-panning
|
||||
tool_data.auto_panning.shift_viewport(input, responses);
|
||||
tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
|
||||
ArtboardToolFsmState::Drawing
|
||||
}
|
||||
@@ -545,7 +553,9 @@ impl Fsm for ArtboardToolFsmState {
|
||||
let scale = DAffine2::from_scale(enlargement_factor);
|
||||
let pivot = DAffine2::from_translation(pivot);
|
||||
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 original_transform = document.metadata().upstream_transform(selected_artboard.to_node());
|
||||
let new = to.inverse() * transformation * to * original_transform;
|
||||
|
||||
@@ -81,7 +81,9 @@ impl Fsm for EyedropperToolFsmState {
|
||||
type ToolOptions = ();
|
||||
|
||||
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 };
|
||||
match (self, event) {
|
||||
@@ -97,7 +99,8 @@ impl Fsm for EyedropperToolFsmState {
|
||||
}
|
||||
// Sampling -> Sampling
|
||||
(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);
|
||||
} else {
|
||||
disable_cursor_preview(responses);
|
||||
|
||||
@@ -94,7 +94,11 @@ impl Fsm for FillToolFsmState {
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
let ToolActionMessageContext {
|
||||
document, global_tool_data, input, ..
|
||||
document,
|
||||
global_tool_data,
|
||||
input,
|
||||
viewport,
|
||||
..
|
||||
} = handler_data;
|
||||
|
||||
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 };
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -117,7 +121,7 @@ impl Fsm for FillToolFsmState {
|
||||
self
|
||||
}
|
||||
(FillToolFsmState::Ready, color_event) => {
|
||||
let Some(layer_identifier) = document.click(input) else {
|
||||
let Some(layer_identifier) = document.click(input, viewport) else {
|
||||
return self;
|
||||
};
|
||||
// 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,
|
||||
shape_editor,
|
||||
preferences,
|
||||
viewport,
|
||||
..
|
||||
} = tool_action_data;
|
||||
|
||||
@@ -283,7 +284,7 @@ impl Fsm for FreehandToolFsmState {
|
||||
|
||||
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 = node_type.default_node_template();
|
||||
|
||||
@@ -255,7 +255,11 @@ impl Fsm for GradientToolFsmState {
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
let ToolActionMessageContext {
|
||||
document, global_tool_data, input, ..
|
||||
document,
|
||||
global_tool_data,
|
||||
input,
|
||||
viewport,
|
||||
..
|
||||
} = tool_action_data;
|
||||
|
||||
let ToolMessage::Gradient(event) = event else { return self };
|
||||
@@ -420,7 +424,7 @@ impl Fsm for GradientToolFsmState {
|
||||
let gradient_state = if dragging {
|
||||
GradientToolFsmState::Drawing
|
||||
} else {
|
||||
let selected_layer = document.click(input);
|
||||
let selected_layer = document.click(input, viewport);
|
||||
|
||||
// Apply the gradient to the selected layer
|
||||
if let Some(layer) = selected_layer {
|
||||
@@ -464,13 +468,13 @@ impl Fsm for GradientToolFsmState {
|
||||
GradientToolMessage::PointerOutsideViewport { 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, GradientToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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 {
|
||||
selected_gradient.transform.translation += shift;
|
||||
}
|
||||
@@ -494,7 +498,7 @@ impl Fsm for GradientToolFsmState {
|
||||
let was_dragging = tool_data.selected_gradient.is_some();
|
||||
|
||||
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) {
|
||||
tool_data.selected_gradient = Some(SelectedGradient::new(gradient, selected_layer, document));
|
||||
}
|
||||
|
||||
@@ -701,6 +701,7 @@ impl PathToolData {
|
||||
shape_editor: &mut ShapeState,
|
||||
document: &DocumentMessageHandler,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
extend_selection: 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)
|
||||
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() {
|
||||
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
|
||||
@@ -1109,8 +1110,9 @@ impl PathToolData {
|
||||
handle_position: DVec2,
|
||||
document: &DocumentMessageHandler,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
) -> 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_result = match using_angle_constraints {
|
||||
@@ -1380,6 +1382,7 @@ impl PathToolData {
|
||||
shape_editor: &mut ShapeState,
|
||||
document: &DocumentMessageHandler,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) {
|
||||
// First check if selection is not just a single handle point
|
||||
@@ -1430,9 +1433,10 @@ impl PathToolData {
|
||||
handle_position,
|
||||
document,
|
||||
input,
|
||||
viewport,
|
||||
)
|
||||
} 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() };
|
||||
@@ -1538,7 +1542,13 @@ impl Fsm for PathToolFsmState {
|
||||
tool_options: &Self::ToolOptions,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> 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);
|
||||
|
||||
@@ -1939,13 +1949,13 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
}
|
||||
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
|
||||
if tool_data.snapping_axis.is_some() {
|
||||
let Some(axis) = tool_data.snapping_axis else { return self };
|
||||
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 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,
|
||||
document,
|
||||
input,
|
||||
viewport,
|
||||
responses,
|
||||
extend_selection,
|
||||
lasso_select,
|
||||
@@ -2056,7 +2067,7 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
.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 }
|
||||
}
|
||||
@@ -2144,6 +2155,7 @@ impl Fsm for PathToolFsmState {
|
||||
tool_action_data.shape_editor,
|
||||
tool_action_data.document,
|
||||
input,
|
||||
viewport,
|
||||
responses,
|
||||
);
|
||||
}
|
||||
@@ -2173,7 +2185,7 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
.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)
|
||||
}
|
||||
@@ -2203,7 +2215,7 @@ impl Fsm for PathToolFsmState {
|
||||
|
||||
// When moving the cursor around we want to update the hovered layers
|
||||
let new_hovered_layers: Vec<LayerNodeIdentifier> = document
|
||||
.click_list_no_parents(input)
|
||||
.click_list_no_parents(input, viewport)
|
||||
.filter(|&layer| {
|
||||
// Filter out artboards and parent holders, and already selected layers
|
||||
!document.network_interface.is_artboard(&layer.to_node(), &[])
|
||||
@@ -2220,7 +2232,7 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
(PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -2228,7 +2240,7 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
(PathToolFsmState::Dragging(dragging_state), PathToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -2388,7 +2400,7 @@ impl Fsm for PathToolFsmState {
|
||||
|
||||
if tool_data.drag_start_pos.distance(previous_mouse) < 1e-8 {
|
||||
// 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());
|
||||
}
|
||||
|
||||
@@ -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 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() {
|
||||
tool_data.reset_drill_through_cycle();
|
||||
None
|
||||
|
||||
@@ -612,7 +612,7 @@ impl PenToolData {
|
||||
self.handle_end = None;
|
||||
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 {
|
||||
self.set_lock_angle(&vector, id, self.prior_segment);
|
||||
@@ -668,7 +668,7 @@ impl PenToolData {
|
||||
self.handle_end_offset = None;
|
||||
self.path_closed = true;
|
||||
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;
|
||||
if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) {
|
||||
self.set_lock_angle(vector, prior_endpoint, self.prior_segment);
|
||||
@@ -802,13 +802,14 @@ impl PenToolData {
|
||||
mouse: &DVec2,
|
||||
vector: &Vector,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
) -> Option<DVec2> {
|
||||
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_pos = self.target_handle_position(end_handle, vector);
|
||||
let ref_pos = self.target_handle_position(reference_handle, vector)?;
|
||||
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 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(
|
||||
&mut self,
|
||||
snap_data: SnapData,
|
||||
@@ -972,6 +974,7 @@ impl PenToolData {
|
||||
responses: &mut VecDeque<Message>,
|
||||
layer: Option<LayerNodeIdentifier>,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
) -> Option<PenToolFsmState> {
|
||||
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;
|
||||
@@ -982,7 +985,7 @@ impl PenToolData {
|
||||
|
||||
// Handles pressing Space to drag anchor and its 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));
|
||||
};
|
||||
|
||||
@@ -1221,6 +1224,7 @@ impl PenToolData {
|
||||
&mut self,
|
||||
document: &DocumentMessageHandler,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
tool_options: &PenOptions,
|
||||
append: bool,
|
||||
@@ -1228,21 +1232,21 @@ impl PenToolData {
|
||||
shape_editor: &mut ShapeState,
|
||||
) {
|
||||
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 viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
self.handle_type = TargetHandle::FuturePreviewOutHandle;
|
||||
|
||||
let selected_nodes = document.network_interface.selected_nodes();
|
||||
self.handle_end = None;
|
||||
|
||||
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 {
|
||||
self.current_layer = Some(layer);
|
||||
self.extend_existing_path(document, layer, point, position);
|
||||
return;
|
||||
} 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 layer = closest_segment.layer();
|
||||
let position = closest_segment.closest_point_document();
|
||||
@@ -1258,7 +1262,7 @@ impl PenToolData {
|
||||
}
|
||||
|
||||
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 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());
|
||||
if let Some(layer) = existing_layer {
|
||||
// Add point to existing layer
|
||||
responses.add(PenToolMessage::AddPointLayerPosition { layer, viewport });
|
||||
responses.add(PenToolMessage::AddPointLayerPosition { layer, viewport: viewport_vec });
|
||||
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 segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
|
||||
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 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);
|
||||
self.current_layer = Some(layer);
|
||||
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)
|
||||
responses.add(DeferMessage::AfterGraphRun {
|
||||
messages: vec![PenToolMessage::AddPointLayerPosition { layer, viewport }.into()],
|
||||
messages: vec![PenToolMessage::AddPointLayerPosition { layer, viewport: viewport_vec }.into()],
|
||||
});
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
@@ -1360,14 +1364,21 @@ impl PenToolData {
|
||||
}
|
||||
|
||||
// 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 unselected = Vec::new();
|
||||
let mut layer_manipulators = HashSet::with_hasher(NoHashBuilder);
|
||||
|
||||
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 tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
||||
@@ -1456,6 +1467,7 @@ impl Fsm for PenToolFsmState {
|
||||
input,
|
||||
shape_editor,
|
||||
preferences,
|
||||
viewport,
|
||||
..
|
||||
} = tool_action_data;
|
||||
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
|
||||
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
||||
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 viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||
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 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 perp = closest_segment.calculate_perp(document);
|
||||
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);
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
(_, PenToolMessage::Overlays { context: mut overlay_context }) => {
|
||||
@@ -1743,7 +1755,7 @@ impl Fsm for PenToolFsmState {
|
||||
if self == PenToolFsmState::PlacingAnchor && preferences.vector_meshes {
|
||||
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
|
||||
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 close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
|
||||
if !close_to_point {
|
||||
@@ -1805,7 +1817,7 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -1822,8 +1834,8 @@ impl Fsm for PenToolFsmState {
|
||||
// Get the closest point and the segment it is on
|
||||
let append = input.keyboard.key(append_to_selected);
|
||||
|
||||
tool_data.store_clicked_endpoint(document, &transform, input, preferences);
|
||||
tool_data.create_initial_point(document, input, responses, tool_options, append, preferences, shape_editor);
|
||||
tool_data.store_clicked_endpoint(document, &transform, input, viewport, preferences);
|
||||
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
|
||||
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
|
||||
@@ -1839,16 +1851,16 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
(PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected }) => {
|
||||
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 viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||
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)
|
||||
if tool_data.buffering_merged_vector {
|
||||
if let Some(layer) = layer {
|
||||
tool_data.buffering_merged_vector = false;
|
||||
tool_data.handle_mode = HandleMode::ColinearLocked;
|
||||
tool_data.bend_from_previous_point(SnapData::new(document, input), transform, layer, preferences, shape_editor, responses);
|
||||
tool_data.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, 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, viewport), transform, input.mouse.position, preferences, responses);
|
||||
}
|
||||
tool_data.buffering_merged_vector = false;
|
||||
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
|
||||
@@ -1863,7 +1875,7 @@ impl Fsm for PenToolFsmState {
|
||||
let layers = LayerNodeIdentifier::ROOT_PARENT
|
||||
.descendants(document.metadata())
|
||||
.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 mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||
if let Some(current_layer) = selected_layers
|
||||
@@ -1893,7 +1905,7 @@ impl Fsm for PenToolFsmState {
|
||||
(PenToolFsmState::DraggingHandle(_), PenToolMessage::DragStop) => {
|
||||
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
|
||||
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)
|
||||
}
|
||||
(
|
||||
@@ -1914,7 +1926,7 @@ impl Fsm for PenToolFsmState {
|
||||
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 {
|
||||
tool_data.handle_mode = match tool_data.handle_mode {
|
||||
HandleMode::Free => {
|
||||
@@ -1962,7 +1974,7 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if tool_data.handle_swapped {
|
||||
@@ -1988,7 +2000,7 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
.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
|
||||
}
|
||||
@@ -2012,7 +2024,7 @@ impl Fsm for PenToolFsmState {
|
||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||
};
|
||||
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);
|
||||
|
||||
// Auto-panning
|
||||
@@ -2034,7 +2046,7 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
.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
|
||||
}
|
||||
@@ -2063,13 +2075,13 @@ impl Fsm for PenToolFsmState {
|
||||
colinear: input.keyboard.key(colinear),
|
||||
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);
|
||||
self
|
||||
}
|
||||
(PenToolFsmState::DraggingHandle(mode), PenToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
|
||||
PenToolFsmState::DraggingHandle(mode)
|
||||
}
|
||||
@@ -2078,7 +2090,7 @@ impl Fsm for PenToolFsmState {
|
||||
return self;
|
||||
}
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
|
||||
PenToolFsmState::PlacingAnchor
|
||||
}
|
||||
@@ -2119,7 +2131,7 @@ impl Fsm for PenToolFsmState {
|
||||
// Confirm to end path
|
||||
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;
|
||||
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;
|
||||
|
||||
if latest_points && single_point_in_layer {
|
||||
@@ -2166,7 +2178,7 @@ impl Fsm for PenToolFsmState {
|
||||
PenToolFsmState::Ready
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -2197,7 +2209,7 @@ impl Fsm for PenToolFsmState {
|
||||
if tool_data.point_index > 0 {
|
||||
tool_data.point_index -= 1;
|
||||
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)
|
||||
} else {
|
||||
responses.add(PenToolMessage::Abort);
|
||||
@@ -2206,7 +2218,7 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
(_, PenToolMessage::Redo) => {
|
||||
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 {
|
||||
0 => PenToolFsmState::Ready,
|
||||
_ => PenToolFsmState::PlacingAnchor,
|
||||
|
||||
@@ -419,11 +419,11 @@ struct 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();
|
||||
for &layer in &self.layers_dragging {
|
||||
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) {
|
||||
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 {
|
||||
return Vec::new();
|
||||
}
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
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.
|
||||
@@ -598,12 +598,18 @@ impl Fsm for SelectToolFsmState {
|
||||
type ToolOptions = ();
|
||||
|
||||
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 };
|
||||
match (self, event) {
|
||||
(_, 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();
|
||||
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) {
|
||||
// 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
|
||||
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()));
|
||||
if let Some(layer) = not_selected_click {
|
||||
if overlay_context.visibility_settings.hover_outline() && !document.network_interface.is_artboard(&layer.to_node(), &[]) {
|
||||
@@ -896,7 +902,7 @@ impl Fsm for SelectToolFsmState {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
||||
let viewport_diagonal = viewport.size().into_dvec2().length();
|
||||
|
||||
let color = if !hover {
|
||||
color
|
||||
@@ -918,7 +924,7 @@ impl Fsm for SelectToolFsmState {
|
||||
|
||||
let extension = tool_data.drag_current - tool_data.drag_start;
|
||||
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 perp = edge.perp();
|
||||
@@ -949,13 +955,13 @@ impl Fsm for SelectToolFsmState {
|
||||
|
||||
// Draw outline visualizations on the layers to be selected
|
||||
let intersected_layers = match selection_shape {
|
||||
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, input).collect(),
|
||||
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, input),
|
||||
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, viewport).collect(),
|
||||
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, viewport),
|
||||
};
|
||||
let layers_to_outline = intersected_layers.into_iter().filter(|layer| match current_selection_mode {
|
||||
SelectionMode::Enclosed => match selection_shape {
|
||||
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 {
|
||||
NestedSelectionBehavior::Deepest => !layer.has_children(document.metadata()),
|
||||
@@ -1008,7 +1014,7 @@ impl Fsm for SelectToolFsmState {
|
||||
self
|
||||
}
|
||||
(_, SelectToolMessage::EditLayerExec) => {
|
||||
if let Some(intersect) = document.click(input) {
|
||||
if let Some(intersect) = document.click(input, viewport) {
|
||||
match tool_data.nested_selection_behavior {
|
||||
NestedSelectionBehavior::Shallowest => edit_layer_shallowest_manipulation(document, intersect, 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;
|
||||
|
||||
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 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
|
||||
else if resize {
|
||||
tool_data.get_snap_candidates(document, input);
|
||||
tool_data.get_snap_candidates(document, input, viewport);
|
||||
SelectToolFsmState::ResizingBounds
|
||||
} else if skew {
|
||||
tool_data.get_snap_candidates(document, input);
|
||||
tool_data.get_snap_candidates(document, input, viewport);
|
||||
SelectToolFsmState::SkewingBounds { skew: Key::Control }
|
||||
}
|
||||
// Dragging the selected layers around to transform them
|
||||
@@ -1081,7 +1087,7 @@ impl Fsm for SelectToolFsmState {
|
||||
}
|
||||
|
||||
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_state = compass_rose_state.axis_type().filter(|_| can_grab_compass_rose);
|
||||
(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),
|
||||
_ => 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);
|
||||
|
||||
@@ -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 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 e0 = tool_data
|
||||
.bounding_box_manager
|
||||
@@ -1208,7 +1214,7 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.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 {
|
||||
axis,
|
||||
@@ -1228,6 +1234,7 @@ impl Fsm for SelectToolFsmState {
|
||||
&mut tool_data.snap_manager,
|
||||
&mut tool_data.snap_candidates,
|
||||
input,
|
||||
viewport,
|
||||
input.keyboard.key(modifier_keys.center),
|
||||
input.keyboard.key(modifier_keys.axis_align),
|
||||
ToolType::Select,
|
||||
@@ -1236,7 +1243,7 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.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
|
||||
}
|
||||
@@ -1283,7 +1290,7 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.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
|
||||
}
|
||||
@@ -1304,7 +1311,7 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolMessage::PointerOutsideViewport { modifier_keys: modifier_keys.clone() }.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 }
|
||||
}
|
||||
@@ -1347,7 +1354,7 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolMessage::PointerOutsideViewport { .. },
|
||||
) => {
|
||||
// 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_start += shift;
|
||||
}
|
||||
@@ -1362,7 +1369,7 @@ impl Fsm for SelectToolFsmState {
|
||||
}
|
||||
(SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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 {
|
||||
bounds.center_of_transformation += shift;
|
||||
bounds.original_bound_transform.translation += shift;
|
||||
@@ -1373,13 +1380,13 @@ impl Fsm for SelectToolFsmState {
|
||||
}
|
||||
(SelectToolFsmState::DraggingPivot, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
|
||||
self
|
||||
}
|
||||
(SelectToolFsmState::Drawing { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
// 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 intersection = document.intersect_quad_no_artboards(quad, input);
|
||||
let intersection = document.intersect_quad_no_artboards(quad, viewport);
|
||||
|
||||
if let Some(path) = intersection.last() {
|
||||
let replacement_selected_layers: Vec<_> = document
|
||||
@@ -1433,7 +1440,7 @@ impl Fsm for SelectToolFsmState {
|
||||
} 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.
|
||||
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);
|
||||
if let Some(intersection) = 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 {
|
||||
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, input).collect(),
|
||||
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, input),
|
||||
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, viewport).collect(),
|
||||
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, viewport),
|
||||
};
|
||||
let new_selected: HashSet<_> = if selection_mode == SelectionMode::Enclosed {
|
||||
let is_inside = |layer: &LayerNodeIdentifier| match selection_shape {
|
||||
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()
|
||||
} else {
|
||||
|
||||
@@ -501,11 +501,11 @@ pub struct 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();
|
||||
for &layer in &self.layers_dragging {
|
||||
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) {
|
||||
let quad = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
||||
@@ -558,6 +558,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
input,
|
||||
preferences,
|
||||
shape_editor,
|
||||
viewport,
|
||||
..
|
||||
}: &mut ToolActionMessageContext,
|
||||
tool_options: &Self::ToolOptions,
|
||||
@@ -596,7 +597,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
let hovering_over_gizmo = tool_data.gizmo_manager.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) {
|
||||
@@ -783,7 +784,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
|
||||
match (resize, rotate, skew) {
|
||||
(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);
|
||||
|
||||
return ShapeToolFsmState::ResizingBounds;
|
||||
@@ -795,7 +796,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
return ShapeToolFsmState::RotatingBounds;
|
||||
}
|
||||
(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);
|
||||
|
||||
return ShapeToolFsmState::SkewingBounds { skew: Key::Control };
|
||||
@@ -806,11 +807,14 @@ impl Fsm for ShapeToolFsmState {
|
||||
|
||||
match tool_data.current_shape {
|
||||
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 => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -830,7 +834,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
@@ -868,20 +872,20 @@ impl Fsm for ShapeToolFsmState {
|
||||
};
|
||||
|
||||
match tool_data.current_shape {
|
||||
ShapeType::Polygon => Polygon::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Star => Star::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Circle => Circle::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Arc => Arc::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Spiral => Spiral::update_shape(document, input, layer, tool_data, responses),
|
||||
ShapeType::Polygon => Polygon::update_shape(document, input, viewport, 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, viewport, 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, viewport, layer, tool_data, 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::Ellipse => Ellipse::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Line => Line::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, viewport, layer, tool_data, modifier, responses),
|
||||
ShapeType::Line => Line::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||
}
|
||||
|
||||
// Auto-panning
|
||||
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
|
||||
}
|
||||
@@ -890,10 +894,10 @@ impl Fsm for ShapeToolFsmState {
|
||||
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
|
||||
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
|
||||
}
|
||||
@@ -915,11 +919,12 @@ impl Fsm for ShapeToolFsmState {
|
||||
&mut tool_data.data.snap_manager,
|
||||
&mut tool_data.snap_candidates,
|
||||
input,
|
||||
viewport,
|
||||
input.keyboard.key(modifier[0]),
|
||||
input.keyboard.key(modifier[1]),
|
||||
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);
|
||||
@@ -974,14 +979,14 @@ impl Fsm for ShapeToolFsmState {
|
||||
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);
|
||||
self
|
||||
}
|
||||
(ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::SkewingBounds { .. }, ShapeToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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
|
||||
{
|
||||
bounds.center_of_transformation += shift;
|
||||
@@ -993,7 +998,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
(ShapeToolFsmState::Ready(_), ShapeToolMessage::PointerOutsideViewport { .. }) => self,
|
||||
(_, ShapeToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
self
|
||||
}
|
||||
(
|
||||
|
||||
@@ -267,12 +267,12 @@ impl SplineToolData {
|
||||
}
|
||||
|
||||
/// 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 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 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())
|
||||
}
|
||||
}
|
||||
@@ -295,6 +295,7 @@ impl Fsm for SplineToolFsmState {
|
||||
input,
|
||||
shape_editor,
|
||||
preferences,
|
||||
viewport,
|
||||
..
|
||||
} = tool_action_data;
|
||||
|
||||
@@ -303,7 +304,7 @@ impl Fsm for SplineToolFsmState {
|
||||
(_, SplineToolMessage::CanvasTransformed) => self,
|
||||
(_, SplineToolMessage::Overlays { context: mut overlay_context }) => {
|
||||
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
|
||||
}
|
||||
(SplineToolFsmState::MergingEndpoints, SplineToolMessage::MergeEndpoints) => {
|
||||
@@ -342,15 +343,15 @@ impl Fsm for SplineToolFsmState {
|
||||
tool_data.weight = tool_options.line_weight;
|
||||
|
||||
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 viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
|
||||
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
|
||||
let layers = LayerNodeIdentifier::ROOT_PARENT
|
||||
.descendants(document.metadata())
|
||||
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||
|
||||
// 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 the point is the part of Spline then we extend it.
|
||||
tool_data.current_layer = Some(layer);
|
||||
@@ -386,7 +387,7 @@ impl Fsm for SplineToolFsmState {
|
||||
|
||||
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 = path_node_type.default_node_template();
|
||||
@@ -410,7 +411,7 @@ impl Fsm for SplineToolFsmState {
|
||||
if tool_data.current_layer.is_none() {
|
||||
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) {
|
||||
let preview_point = tool_data.preview_point;
|
||||
extend_spline(tool_data, false, responses);
|
||||
@@ -433,7 +434,7 @@ impl Fsm for SplineToolFsmState {
|
||||
tool_data.next_point = point;
|
||||
tool_data.snap_manager.clear_indicator();
|
||||
} 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.snap_manager.update_indicator(snapped_point);
|
||||
}
|
||||
@@ -442,12 +443,12 @@ impl Fsm for SplineToolFsmState {
|
||||
|
||||
// Auto-panning
|
||||
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
|
||||
}
|
||||
(_, 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);
|
||||
self
|
||||
}
|
||||
@@ -456,7 +457,7 @@ impl Fsm for SplineToolFsmState {
|
||||
return self;
|
||||
}
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses);
|
||||
|
||||
SplineToolFsmState::Drawing
|
||||
}
|
||||
|
||||
@@ -499,6 +499,7 @@ impl Fsm for TextToolFsmState {
|
||||
global_tool_data,
|
||||
input,
|
||||
font_cache,
|
||||
viewport,
|
||||
..
|
||||
} = transition_data;
|
||||
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 };
|
||||
match (self, event) {
|
||||
(TextToolFsmState::Editing, TextToolMessage::Overlays { context: mut overlay_context }) => {
|
||||
responses.add(FrontendMessage::DisplayEditableTextboxTransform {
|
||||
transform: document.metadata().transform_to_viewport(tool_data.layer).to_cols_array(),
|
||||
});
|
||||
let 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() {
|
||||
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. {
|
||||
@@ -529,7 +529,7 @@ impl Fsm for TextToolFsmState {
|
||||
let quad = Quad::from_box(tool_data.cached_resize_bounds);
|
||||
|
||||
// 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(
|
||||
Quad::from_box(document.metadata().bounding_box_viewport(layer).unwrap_or([DVec2::ZERO; 2])),
|
||||
None,
|
||||
@@ -572,7 +572,7 @@ impl Fsm for TextToolFsmState {
|
||||
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
|
||||
}
|
||||
@@ -585,7 +585,7 @@ impl Fsm for TextToolFsmState {
|
||||
state
|
||||
}
|
||||
(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.drag_start = input.mouse.position;
|
||||
tool_data.drag_current = input.mouse.position;
|
||||
@@ -659,7 +659,7 @@ impl Fsm for TextToolFsmState {
|
||||
TextToolFsmState::Ready
|
||||
}
|
||||
(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);
|
||||
|
||||
@@ -668,7 +668,7 @@ impl Fsm for TextToolFsmState {
|
||||
TextToolMessage::PointerOutsideViewport { 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
|
||||
}
|
||||
@@ -691,7 +691,7 @@ impl Fsm for TextToolFsmState {
|
||||
TextToolMessage::PointerOutsideViewport { 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
|
||||
@@ -713,7 +713,7 @@ impl Fsm for TextToolFsmState {
|
||||
let snap = Some(SizeSnapData {
|
||||
manager: &mut tool_data.resize.snap_manager,
|
||||
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);
|
||||
@@ -752,26 +752,26 @@ impl Fsm for TextToolFsmState {
|
||||
TextToolMessage::PointerOutsideViewport { 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
|
||||
}
|
||||
(_, 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);
|
||||
|
||||
self
|
||||
}
|
||||
(TextToolFsmState::Placing, TextToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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::ResizingBounds | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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 {
|
||||
bounds.center_of_transformation += shift;
|
||||
bounds.original_bound_transform.translation += shift;
|
||||
|
||||
@@ -28,6 +28,7 @@ pub struct TransformLayerMessageContext<'a> {
|
||||
pub input: &'a InputPreprocessorMessageHandler,
|
||||
pub tool_data: &'a ToolData,
|
||||
pub shape_editor: &'a mut ShapeState,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
@@ -77,6 +78,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
|
||||
input,
|
||||
tool_data,
|
||||
shape_editor,
|
||||
viewport,
|
||||
} = context;
|
||||
|
||||
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 format_rounded = |value: f64, precision: usize| {
|
||||
|
||||
@@ -28,6 +28,7 @@ pub struct ToolActionMessageContext<'a> {
|
||||
pub shape_editor: &'a mut ShapeState,
|
||||
pub node_graph: &'a NodeGraphExecutor,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
pub viewport: &'a ViewportMessageHandler,
|
||||
}
|
||||
|
||||
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::prelude::*;
|
||||
use glam::{DAffine2, DVec2, UVec2};
|
||||
use glam::{DAffine2, UVec2};
|
||||
use graph_craft::document::value::{RenderOutput, TaggedValue};
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
|
||||
use graph_craft::proto::GraphErrors;
|
||||
@@ -136,14 +136,17 @@ impl NodeGraphExecutor {
|
||||
document: &mut DocumentMessageHandler,
|
||||
document_id: DocumentId,
|
||||
viewport_resolution: UVec2,
|
||||
viewport_scale: f64,
|
||||
time: TimingInformation,
|
||||
) -> Result<Message, String> {
|
||||
let viewport = Footprint {
|
||||
transform: document.metadata().document_to_viewport,
|
||||
resolution: viewport_resolution,
|
||||
..Default::default()
|
||||
};
|
||||
let render_config = RenderConfig {
|
||||
viewport: Footprint {
|
||||
transform: document.metadata().document_to_viewport,
|
||||
resolution: viewport_resolution,
|
||||
..Default::default()
|
||||
},
|
||||
viewport,
|
||||
scale: viewport_scale,
|
||||
time,
|
||||
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||
export_format: graphene_std::application_io::ExportFormat::Raster,
|
||||
@@ -163,23 +166,31 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
|
||||
/// Evaluates a node graph, computing the entire graph
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn submit_node_graph_evaluation(
|
||||
&mut self,
|
||||
document: &mut DocumentMessageHandler,
|
||||
document_id: DocumentId,
|
||||
viewport_resolution: UVec2,
|
||||
viewport_scale: f64,
|
||||
time: TimingInformation,
|
||||
node_to_inspect: Option<NodeId>,
|
||||
ignore_hash: bool,
|
||||
) -> Result<Message, String> {
|
||||
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
|
||||
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 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
|
||||
let bounds = match export_config.bounds {
|
||||
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),
|
||||
}
|
||||
.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 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 {
|
||||
viewport: Footprint {
|
||||
transform: DAffine2::from_scale(DVec2::splat(export_config.scale_factor)) * transform,
|
||||
resolution: (size * export_config.scale_factor).as_uvec2(),
|
||||
resolution,
|
||||
transform,
|
||||
..Default::default()
|
||||
},
|
||||
scale: export_config.scale_factor,
|
||||
time: Default::default(),
|
||||
export_format,
|
||||
render_mode: document.render_mode,
|
||||
hide_artboards: export_config.transparent_background,
|
||||
for_export: true,
|
||||
};
|
||||
export_config.size = size;
|
||||
export_config.size = resolution.as_dvec2();
|
||||
|
||||
// Execute the node graph
|
||||
self.runtime_io
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::application::Editor;
|
||||
use crate::application::set_uuid_seed;
|
||||
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::portfolio::utility_types::Platform;
|
||||
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::NodeRuntime;
|
||||
use crate::test_utils::test_prelude::LayerNodeIdentifier;
|
||||
use glam::DVec2;
|
||||
use glam::{DVec2, UVec2};
|
||||
use graph_craft::document::DocumentNode;
|
||||
use graphene_std::InputAccessor;
|
||||
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}")),
|
||||
};
|
||||
|
||||
let viewport_resolution = glam::UVec2::ONE;
|
||||
if let Err(e) = exector.submit_current_node_graph_evaluation(document, DocumentId(0), viewport_resolution, Default::default()) {
|
||||
if let Err(e) = exector.submit_current_node_graph_evaluation(document, DocumentId(0), UVec2::ONE, 1.0, Default::default()) {
|
||||
return Err(format!("submit_current_node_graph_evaluation failed\n\n{e}"));
|
||||
}
|
||||
runtime.run().await;
|
||||
@@ -296,8 +294,12 @@ impl EditorTestUtils {
|
||||
|
||||
/// 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) {
|
||||
self.handle_message(InputPreprocessorMessage::BoundsOfViewports {
|
||||
bounds_of_viewports: vec![ViewportBounds { top_left, bottom_right }],
|
||||
self.handle_message(ViewportMessage::Update {
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user