Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add PTZ support for flipping the canvas #2394

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,13 +342,26 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
continue;
}
let Some(bounds) = self.metadata().bounding_box_document(layer) else { continue };
let [min, max] = [bounds[0].min(bounds[1]), bounds[0].max(bounds[1])];

let name = self.network_interface.frontend_display_name(&layer.to_node(), &[]);

// Calculate position of the text
let corner_pos = if !self.document_ptz.canvas_flipped {
min // Use the top left corner
} else {
DVec2::new(max.x, min.y) // Use the top right corner (appears to be the top left due to flipping)
};

// When canvas is flipped, reverse the flip so the text reads in the same direction
let scale = if !self.document_ptz.canvas_flipped { DVec2::ONE } else { DVec2::new(-1., 1.) };

// Create a transform that puts the text at the true top-left regardless of flip
let transform = self.metadata().document_to_viewport
* DAffine2::from_translation(bounds[0].min(bounds[1]))
* DAffine2::from_translation(corner_pos)
* DAffine2::from_scale(DVec2::splat(self.document_ptz.zoom().recip()))
* DAffine2::from_translation(-DVec2::Y * 4.);
* DAffine2::from_translation(-DVec2::Y * 4.)
* DAffine2::from_scale(scale); // Counter the flip for the text itself

overlay_context.text(&name, COLOR_OVERLAY_GRAY, None, transform, 0., [Pivot::Start, Pivot::End]);
}
Expand Down Expand Up @@ -2529,7 +2542,7 @@ impl<'a> ClickXRayIter<'a> {
}
}

pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHandler, tooltip_name: &str) -> [WidgetHolder; 5] {
pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHandler, tooltip_name: &str) -> [WidgetHolder; 7] {
[
IconButton::new("ZoomIn", 24)
.tooltip("Zoom In")
Expand All @@ -2547,6 +2560,11 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
.disabled(ptz.tilt().abs() < 1e-4 && (ptz.zoom() - 1.).abs() < 1e-4)
.widget_holder(),
CheckboxInput::new(ptz.canvas_flipped)
.on_update(|_| NavigationMessage::FlipCanvas.into())
.tooltip("Flip Canvas Horizontally")
.widget_holder(),
TextLabel::new("Flip Canvas").widget_holder(),
// PopoverButton::new()
// .popover_layout(vec![
// LayoutGroup::Row {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ pub enum NavigationMessage {
FitViewportToBounds { bounds: [DVec2; 2], prevent_zoom_past_100: bool },
FitViewportToSelection,
PointerMove { snap: Key },
FlipCanvas,
}
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
..
} => {
let tilt_raw_not_snapped = {
// Compute the angle in document space to counter for any flipping
let viewport_to_document = network_interface.document_metadata().document_to_viewport.inverse();
let half_viewport = ipp.viewport_bounds.size() / 2.;
let start_offset = self.mouse_position - half_viewport;
let end_offset = ipp.mouse.position - half_viewport;
let start_offset = viewport_to_document.transform_vector2(self.mouse_position - half_viewport);
let end_offset = viewport_to_document.transform_vector2(ipp.mouse.position - half_viewport);
let angle = start_offset.angle_to(end_offset);

tilt_raw_not_snapped + angle
Expand Down Expand Up @@ -459,6 +461,17 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation

self.mouse_position = ipp.mouse.position;
}
NavigationMessage::FlipCanvas => {
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 FlipCanvas");
return;
};

ptz.canvas_flipped = !ptz.canvas_flipped;

responses.add(DocumentMessage::PTZUpdate);
responses.add(BroadcastEvent::CanvasTransformed);
}
}
}

Expand Down Expand Up @@ -516,15 +529,16 @@ impl NavigationMessageHandler {
let tilt = ptz.tilt();
let zoom = ptz.zoom();

let scaled_center = viewport_center / self.snapped_zoom(zoom);
let scale = self.snapped_zoom(zoom);
let scale_vec = if ptz.canvas_flipped { DVec2::new(-scale, scale) } else { DVec2::splat(scale) };
let scaled_center = viewport_center / scale_vec;

// Try to avoid fractional coordinates to reduce anti aliasing.
let scale = self.snapped_zoom(zoom);
let rounded_pan = ((pan + scaled_center) * scale).round() / scale - scaled_center;

// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
let offset_transform = DAffine2::from_translation(scaled_center);
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
let scale_transform = DAffine2::from_scale(scale_vec);
let angle_transform = DAffine2::from_angle(self.snapped_tilt(tilt));
let translation_transform = DAffine2::from_translation(rounded_pan);
scale_transform * offset_transform * angle_transform * translation_transform
Expand Down
9 changes: 8 additions & 1 deletion editor/src/messages/portfolio/document/utility_types/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,11 +639,18 @@ pub struct PTZ {
tilt: f64,
/// Scale factor.
zoom: f64,
/// Whether the canvas is horizontally flipped.
pub canvas_flipped: bool,
}

impl Default for PTZ {
fn default() -> Self {
Self { pan: DVec2::ZERO, tilt: 0., zoom: 1. }
Self {
pan: DVec2::ZERO,
tilt: 0.,
zoom: 1.,
canvas_flipped: false,
}
}
}

Expand Down
Loading