Fix viewport bounds getting out of sync at times, like when toggling rulers

This commit is contained in:
Keavon Chambers
2024-07-27 23:41:21 -07:00
parent a4a513911d
commit 06177597ae
19 changed files with 124 additions and 116 deletions

View File

@@ -119,7 +119,7 @@ impl Dispatcher {
}
Message::Frontend(message) => {
// Handle these messages immediately by returning early
if let FrontendMessage::TriggerFontLoad { .. } | FrontendMessage::TriggerRefreshBoundsOfViewports = message {
if let FrontendMessage::TriggerFontLoad { .. } = message {
self.responses.push(message);
self.cleanup_queues(false);

View File

@@ -4,7 +4,7 @@ use crate::messages::prelude::*;
use graph_craft::document::NodeId;
use graphene_core::uuid::generate_uuid;
use glam::{DVec2, IVec2, UVec2};
use glam::{IVec2, UVec2};
/// A dialog to allow users to set some initial options about a new document.
#[derive(Debug, Clone, Default)]
@@ -26,16 +26,13 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
let create_artboard = !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0;
if create_artboard {
let id = NodeId(generate_uuid());
responses.add(GraphOperationMessage::NewArtboard {
id,
id: NodeId(generate_uuid()),
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
});
responses.add(NavigationMessage::FitViewportToBounds {
bounds: [DVec2::ZERO, self.dimensions.as_dvec2()],
prevent_zoom_past_100: true,
});
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
}
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
}

View File

@@ -46,6 +46,7 @@ pub enum FrontendMessage {
#[serde(rename = "blobUrl")]
blob_url: String,
},
TriggerDelayedZoomCanvasToFitAll,
TriggerDownloadBlobUrl {
#[serde(rename = "layerName")]
layer_name: String,
@@ -87,7 +88,6 @@ pub enum FrontendMessage {
TriggerLoadPreferences,
TriggerOpenDocument,
TriggerPaste,
TriggerRefreshBoundsOfViewports,
TriggerRevokeBlobUrl {
url: String,
},
@@ -112,7 +112,6 @@ pub enum FrontendMessage {
#[serde(rename = "documentSerializedContent")]
document_serialized_content: String,
},
TriggerViewportResize,
TriggerVisitLink {
url: String,
},

View File

@@ -31,7 +31,6 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for
self.viewport_bounds = bounds;
responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO });
responses.add(FrontendMessage::TriggerViewportResize);
}
}
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {

View File

@@ -429,7 +429,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
}
responses.add(FrontendMessage::TriggerGraphViewOverlay { open });
responses.add(FrontendMessage::TriggerRefreshBoundsOfViewports);
// Update the tilt menu bar buttons to be disabled when the graph is open
responses.add(MenuBarMessage::SendLayout);
if open {
@@ -780,7 +779,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
}
DocumentMessage::RenderRulers => {
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());
let ruler_origin = if !self.graph_view_overlay_open {
self.metadata().document_to_viewport.transform_point2(DVec2::ZERO)
@@ -802,7 +801,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
});
}
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());
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
@@ -1871,7 +1870,7 @@ impl DocumentMessageHandler {
.tooltip("Reset Tilt and Zoom to 100%")
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
.disabled(self.document_ptz.tilt.abs() < 1e-4 && (self.document_ptz.zoom - 1.).abs() < 1e-4)
.disabled(self.document_ptz.tilt.abs() < 1e-4 && (self.document_ptz.zoom() - 1.).abs() < 1e-4)
.widget_holder(),
PopoverButton::new()
.popover_layout(vec![
@@ -1902,7 +1901,7 @@ impl DocumentMessageHandler {
])
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.document_ptz.zoom) * 100.))
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.document_ptz.zoom()) * 100.))
.unit("%")
.min(0.000001)
.max(1000000.)

View File

@@ -50,7 +50,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
} else {
node_graph_ptz.entry(node_graph_handler.network.clone()).or_insert(PTZ::default())
};
let old_zoom = ptz.zoom;
let old_zoom = ptz.zoom();
match message {
NavigationMessage::BeginCanvasPan => {
@@ -110,8 +110,8 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
});
self.navigation_operation = NavigationOperation::Zoom {
zoom_raw_not_snapped: ptz.zoom,
zoom_original_for_abort: ptz.zoom,
zoom_raw_not_snapped: ptz.zoom(),
zoom_original_for_abort: ptz.zoom(),
snap: false,
};
self.mouse_position = ipp.mouse.position;
@@ -145,7 +145,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
}
NavigationMessage::CanvasTiltResetAndZoomTo100Percent => {
ptz.tilt = 0.;
ptz.zoom = 1.;
ptz.set_zoom(1.);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
}
@@ -154,16 +154,16 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
}
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom).unwrap_or(&ptz.zoom);
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom()).unwrap_or(&ptz.zoom());
if center_on_mouse {
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
}
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
}
NavigationMessage::CanvasZoomIncrease { center_on_mouse } => {
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom).unwrap_or(&ptz.zoom);
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom()).unwrap_or(&ptz.zoom());
if center_on_mouse {
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
}
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
}
@@ -179,10 +179,12 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
} else {
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
};
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, ipp);
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom * zoom_factor });
responses.add(NavigationMessage::CanvasZoomSet {
zoom_factor: ptz.zoom() * zoom_factor,
});
}
NavigationMessage::CanvasZoomSet { zoom_factor } => {
let document_bounds = if !graph_view_overlay_open {
@@ -191,8 +193,9 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
} else {
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
};
ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
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);
ptz.set_zoom(zoom);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
}
@@ -208,7 +211,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
ptz.pan = pan_original_for_abort;
}
NavigationOperation::Zoom { zoom_original_for_abort, .. } => {
ptz.zoom = zoom_original_for_abort;
ptz.set_zoom(zoom_original_for_abort);
}
}
@@ -217,7 +220,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
// Final chance to apply snapping if the key was pressed during this final frame
ptz.tilt = self.snapped_tilt(ptz.tilt);
ptz.zoom = self.snapped_zoom(ptz.zoom);
ptz.set_zoom(self.snapped_zoom(ptz.zoom()));
// Reset the navigation operation now that it's done
self.navigation_operation = NavigationOperation::None;
@@ -238,19 +241,18 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
bounds: [pos1, pos2],
prevent_zoom_past_100,
} => {
let v1 = if !graph_view_overlay_open {
metadata.document_to_viewport.inverse().transform_point2(DVec2::ZERO)
} else {
node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO)
};
let v2 = if !graph_view_overlay_open {
metadata.document_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size())
} else {
node_graph_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size())
};
let (pos1, pos2) = (pos1.min(pos2), pos1.max(pos2));
let diagonal = pos2 - pos1;
let center = ((v1 + v2) - (pos1 + pos2)) / 2.;
let size = 1. / ((pos2 - pos1) / (v2 - v1));
if diagonal.length() < f64::EPSILON * 1000. || ipp.viewport_bounds.size() == DVec2::ZERO {
return;
}
let transform = (if graph_view_overlay_open { *node_graph_to_viewport } else { metadata.document_to_viewport }).inverse();
let (v1, v2) = (transform.transform_point2(DVec2::ZERO), transform.transform_point2(ipp.viewport_bounds.size()));
let center = ((v2 + v1) - (pos2 + pos1)) / 2.;
let size = (v2 - v1) / diagonal;
let new_scale = size.min_element();
let viewport_change = if !graph_view_overlay_open {
@@ -264,12 +266,12 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
ptz.pan += center;
}
ptz.zoom *= new_scale * VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR;
ptz.set_zoom(ptz.zoom() * new_scale * VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR);
// Keep the canvas filling less than the full available viewport bounds if requested.
// And if the zoom is close to the full viewport bounds, we ignore the padding because 100% is preferrable if it still fits.
if prevent_zoom_past_100 && ptz.zoom > VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR {
ptz.zoom = 1.;
if prevent_zoom_past_100 && ptz.zoom() > VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR {
ptz.set_zoom(1.);
}
responses.add(PortfolioMessage::UpdateDocumentWidgets);
@@ -339,7 +341,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp)
};
ptz.zoom = self.snapped_zoom(zoom_raw_not_snapped);
ptz.set_zoom(self.snapped_zoom(zoom_raw_not_snapped));
let snap = ipp.keyboard.get(snap as usize);
@@ -349,7 +351,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
snap,
};
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom });
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom() });
}
}
@@ -427,7 +429,7 @@ impl NavigationMessageHandler {
}
fn create_document_transform(&self, viewport_center: DVec2, ptz: &PTZ, responses: &mut VecDeque<Message>) {
let transform = self.calculate_offset_transform(viewport_center, ptz.pan, ptz.tilt, ptz.zoom);
let transform = self.calculate_offset_transform(viewport_center, ptz.pan, ptz.tilt, ptz.zoom());
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
}

View File

@@ -24,7 +24,13 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessag
use super::utility_types::OverlayContext;
use wasm_bindgen::JsCast;
let canvas = self.canvas.get_or_insert_with(|| overlay_canvas_element().expect("Failed to get canvas element"));
let canvas = match &self.canvas {
Some(canvas) => canvas,
None => {
let Some(new_canvas) = overlay_canvas_element() else { return };
self.canvas.get_or_insert(new_canvas)
}
};
let context = self.context.get_or_insert_with(|| {
let context = canvas.get_context("2d").ok().flatten().expect("Failed to get canvas context");

View File

@@ -246,7 +246,7 @@ impl GridSnapping {
pub fn compute_rectangle_spacing(mut size: DVec2, navigation: &PTZ) -> Option<DVec2> {
let mut iterations = 0;
size = size.abs();
while (size * navigation.zoom).cmplt(DVec2::splat(10.)).any() {
while (size * navigation.zoom()).cmplt(DVec2::splat(10.)).any() {
if iterations > 100 {
return None;
}
@@ -261,7 +261,7 @@ impl GridSnapping {
let length = length.abs();
let mut iterations = 0;
let mut multiplier = 1.;
while (length / divisor.abs().max(1.)) * multiplier * navigation.zoom < 10. {
while (length / divisor.abs().max(1.)) * multiplier * navigation.zoom() < 10. {
if iterations > 100 {
return None;
}
@@ -409,8 +409,7 @@ impl fmt::Display for SnappingOptions {
pub struct PTZ {
pub pan: DVec2,
pub tilt: f64,
// TODO: Make this private and add getter/setter methods which ensure zoom is always positive and greater than the smallest zoom level in `VIEWPORT_ZOOM_LEVELS`.
pub zoom: f64,
zoom: f64,
}
impl Default for PTZ {
@@ -418,3 +417,13 @@ impl Default for PTZ {
Self { pan: DVec2::ZERO, tilt: 0., zoom: 1. }
}
}
impl PTZ {
pub fn zoom(&self) -> f64 {
self.zoom
}
pub fn set_zoom(&mut self, zoom: f64) {
self.zoom = zoom.clamp(crate::consts::VIEWPORT_ZOOM_SCALE_MIN, crate::consts::VIEWPORT_ZOOM_SCALE_MAX)
}
}

View File

@@ -618,7 +618,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
responses.add(DocumentMessage::RenderRulers);
responses.add(MenuBarMessage::SendLayout);
responses.add(FrontendMessage::TriggerRefreshBoundsOfViewports);
}
}
PortfolioMessage::UpdateDocumentWidgets => {

View File

@@ -64,7 +64,7 @@ impl SnapConstraint {
}
}
pub fn snap_tolerance(document: &DocumentMessageHandler) -> f64 {
document.snapping_state.tolerance / document.document_ptz.zoom
document.snapping_state.tolerance / document.document_ptz.zoom()
}
fn compare_points(a: &&SnappedPoint, b: &&SnappedPoint) -> Ordering {

View File

@@ -151,7 +151,6 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
// Notify the frontend about the initial working colors
document_data.update_working_colors(responses);
responses.add(FrontendMessage::TriggerRefreshBoundsOfViewports);
let mut data = ToolActionHandlerData {
document,