|
|
|
|
|
use super::graph_modification_utils; |
|
use crate::consts::PIVOT_DIAMETER; |
|
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; |
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; |
|
use crate::messages::prelude::*; |
|
use glam::{DAffine2, DVec2}; |
|
use graphene_std::transform::ReferencePoint; |
|
use std::collections::VecDeque; |
|
|
|
#[derive(Clone, Debug)] |
|
pub struct Pivot { |
|
|
|
normalized_pivot: DVec2, |
|
|
|
transform_from_normalized: DAffine2, |
|
|
|
pivot: Option<DVec2>, |
|
|
|
old_pivot_position: ReferencePoint, |
|
|
|
active: bool, |
|
} |
|
|
|
impl Default for Pivot { |
|
fn default() -> Self { |
|
Self { |
|
normalized_pivot: DVec2::splat(0.5), |
|
transform_from_normalized: Default::default(), |
|
pivot: Default::default(), |
|
old_pivot_position: ReferencePoint::Center, |
|
active: true, |
|
} |
|
} |
|
} |
|
|
|
impl Pivot { |
|
|
|
fn get_layer_pivot_transform(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> DAffine2 { |
|
let [min, max] = document.metadata().nonzero_bounding_box(layer); |
|
|
|
let bounds_transform = DAffine2::from_translation(min) * DAffine2::from_scale(max - min); |
|
let layer_transform = document.metadata().transform_to_viewport(layer); |
|
layer_transform * bounds_transform |
|
} |
|
|
|
|
|
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) { |
|
if !self.active { |
|
return; |
|
} |
|
|
|
let selected_nodes = document.network_interface.selected_nodes(); |
|
let mut layers = selected_nodes.selected_visible_and_unlocked_layers(&document.network_interface); |
|
let Some(first) = layers.next() else { |
|
|
|
self.normalized_pivot = DVec2::splat(0.5); |
|
self.pivot = None; |
|
return; |
|
}; |
|
|
|
|
|
let selected_layers_count = layers.count() + 1; |
|
|
|
|
|
if selected_layers_count == 1 { |
|
let normalized_pivot = graph_modification_utils::get_pivot(first, &document.network_interface).unwrap_or(DVec2::splat(0.5)); |
|
self.normalized_pivot = normalized_pivot; |
|
self.transform_from_normalized = Self::get_layer_pivot_transform(first, document); |
|
self.pivot = Some(self.transform_from_normalized.transform_point2(normalized_pivot)); |
|
} else { |
|
|
|
let xy_summation = document |
|
.network_interface |
|
.selected_nodes() |
|
.selected_visible_and_unlocked_layers(&document.network_interface) |
|
.map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network_interface)) |
|
.reduce(|a, b| a + b) |
|
.unwrap_or_default(); |
|
|
|
let pivot = xy_summation / selected_layers_count as f64; |
|
self.pivot = Some(pivot); |
|
let [min, max] = document.selected_visible_and_unlock_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]); |
|
self.normalized_pivot = (pivot - min) / (max - min); |
|
|
|
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min); |
|
} |
|
} |
|
|
|
pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, draw_data: Option<(f64,)>) { |
|
if !overlay_context.visibility_settings.pivot() { |
|
self.active = false; |
|
return; |
|
} else { |
|
self.active = true; |
|
} |
|
|
|
self.recalculate_pivot(document); |
|
if let (Some(pivot), Some(data)) = (self.pivot, draw_data) { |
|
overlay_context.pivot(pivot, data.0); |
|
} |
|
} |
|
|
|
|
|
pub fn should_refresh_pivot_position(&mut self) -> bool { |
|
if !self.active { |
|
return false; |
|
} |
|
|
|
let new = self.to_pivot_position(); |
|
let should_refresh = new != self.old_pivot_position; |
|
self.old_pivot_position = new; |
|
should_refresh |
|
} |
|
|
|
pub fn to_pivot_position(&self) -> ReferencePoint { |
|
self.normalized_pivot.into() |
|
} |
|
|
|
|
|
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { |
|
if !self.active { |
|
return; |
|
} |
|
|
|
for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) { |
|
let transform = Self::get_layer_pivot_transform(layer, document); |
|
|
|
if transform.matrix2.determinant().abs() <= f64::EPSILON { |
|
return; |
|
}; |
|
let pivot = transform.inverse().transform_point2(position); |
|
responses.add(GraphOperationMessage::TransformSetPivot { layer, pivot }); |
|
} |
|
} |
|
|
|
|
|
pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { |
|
if !self.active { |
|
return; |
|
} |
|
|
|
self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, responses); |
|
} |
|
|
|
|
|
pub fn is_over(&self, mouse: DVec2) -> bool { |
|
if !self.active { |
|
return false; |
|
} |
|
self.pivot.filter(|&pivot| mouse.distance_squared(pivot) < (PIVOT_DIAMETER / 2.).powi(2)).is_some() |
|
} |
|
} |
|
|