Fix transforms, Brush tool, and G/R/S (#1473)

* Transform fixes

* Fix the desert artwork

* Change artboard icon

* Better handling when transforming brush strokes

* Code review pass

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2023-11-26 20:54:06 -08:00
committed by GitHub
co-authored by Keavon Chambers
parent 5ee79031ab
commit b2ca643e6e
26 changed files with 308 additions and 238 deletions
@@ -158,7 +158,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}
#[remain::unsorted]
Navigation(message) => {
let document_bounds = self.metadata().document_bounds();
let document_bounds = self.metadata().document_bounds_viewport_space();
self.navigation_handler.process_message(
message,
responses,
@@ -306,7 +306,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(BroadcastEvent::DocumentIsDirty);
}
DeselectAllLayers => {
responses.add_front(SetSelectedLayers { replacement_selected_layers: vec![] });
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
self.layer_range_selection_reference = None;
}
DirtyRenderDocument => {
@@ -623,7 +623,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
let viewport_size = ipp.viewport_bounds.size();
let viewport_mid = ipp.viewport_bounds.center();
let [bounds1, bounds2] = self.metadata().document_bounds().unwrap_or([viewport_mid; 2]);
let [bounds1, bounds2] = self.metadata().document_bounds_viewport_space().unwrap_or([viewport_mid; 2]);
let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale;
let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale;
let bounds_length = (bounds2 - bounds1) * (1. + SCROLLBAR_SPACING);
@@ -657,20 +657,20 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
})
}
SelectAllLayers => {
let all = self.all_layers().map(|path| path.to_vec()).collect();
responses.add_front(SetSelectedLayers { replacement_selected_layers: all });
let all = self.metadata().all_layers().map(|layer| layer.to_node()).collect();
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: all });
}
SelectedLayersLower => {
responses.add_front(DocumentMessage::SelectedLayersReorder { relative_index_offset: -1 });
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: 1 });
}
SelectedLayersLowerToBack => {
responses.add_front(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MIN });
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MAX });
}
SelectedLayersRaise => {
responses.add_front(DocumentMessage::SelectedLayersReorder { relative_index_offset: 1 });
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: -1 });
}
SelectedLayersRaiseToFront => {
responses.add_front(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MAX });
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MIN });
}
SelectedLayersReorder { relative_index_offset } => {
self.selected_layers_reorder(relative_index_offset, responses);
@@ -813,7 +813,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}
SetViewMode { view_mode } => {
self.view_mode = view_mode;
responses.add_front(DocumentMessage::DirtyRenderDocument);
responses.add_front(NodeGraphMessage::RunDocumentGraph);
}
StartTransaction => self.backup(responses),
ToggleLayerExpansion { layer } => {
@@ -876,7 +876,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 2. });
}
ZoomCanvasToFitAll => {
if let Some(bounds) = self.metadata().document_bounds() {
if let Some(bounds) = self.metadata().document_bounds_document_space() {
responses.add(NavigationMessage::FitViewportToBounds {
bounds,
padding_scale_factor: Some(VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR),
@@ -68,12 +68,10 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
responses.add(SetCanvasZoom { zoom_factor: new_scale });
}
FitViewportToBounds {
bounds: [bounds_corner_a, bounds_corner_b],
bounds: [pos1, pos2],
padding_scale_factor,
prevent_zoom_past_100,
} => {
let pos1 = document.metadata.document_to_viewport.inverse().transform_point2(bounds_corner_a);
let pos2 = document.metadata.document_to_viewport.inverse().transform_point2(bounds_corner_b);
let v1 = document.metadata.document_to_viewport.inverse().transform_point2(DVec2::ZERO);
let v2 = document.metadata.document_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size());
@@ -98,8 +96,9 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
}
FitViewportToSelection => {
if let Some(bounds) = selection_bounds {
let transform = document.metadata.document_to_viewport.inverse();
responses.add(FitViewportToBounds {
bounds,
bounds: [transform.transform_point2(bounds[0]), transform.transform_point2(bounds[1])],
padding_scale_factor: Some(VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR),
prevent_zoom_past_100: false,
})
@@ -382,16 +381,18 @@ impl NavigationMessageHandler {
}
pub fn calculate_offset_transform(&self, viewport_center: DVec2) -> DAffine2 {
let scaled_centre = viewport_center / self.snapped_scale();
// Try to avoid fractional coordinates to reduce anti aliasing.
let scale = self.snapped_scale();
let rounded_pan = ((self.pan + viewport_center) * scale).round() / scale - viewport_center;
let rounded_pan = ((self.pan + scaled_centre) * scale).round() / scale - scaled_centre;
// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
let offset_transform = DAffine2::from_translation(viewport_center);
let offset_transform = DAffine2::from_translation(scaled_centre);
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
let angle_transform = DAffine2::from_angle(self.snapped_angle());
let translation_transform = DAffine2::from_translation(rounded_pan);
scale_transform * offset_transform * angle_transform * offset_transform.inverse() * translation_transform
scale_transform * offset_transform * angle_transform * translation_transform
}
fn create_document_transform(&self, viewport_center: DVec2, responses: &mut VecDeque<Message>) {
@@ -403,7 +404,7 @@ impl NavigationMessageHandler {
let new_viewport_bounds = viewport_bounds / zoom_factor;
let delta_size = viewport_bounds - new_viewport_bounds;
let mouse_fraction = mouse / viewport_bounds;
let delta = delta_size * (-mouse_fraction);
let delta = delta_size * (DVec2::splat(0.5) - mouse_fraction);
NavigationMessage::TranslateCanvas { delta }.into()
}
@@ -282,7 +282,7 @@ impl<'a> ModifyInputsContext<'a> {
};
let metadata = output_node.metadata.clone();
let new_input = output_node.inputs[0].clone();
let new_input = output_node.inputs.first().cloned().filter(|input| input.as_node().is_some());
let node_id = generate_uuid();
output_node.metadata.position.x += 8;
@@ -292,7 +292,7 @@ impl<'a> ModifyInputsContext<'a> {
warn!("Node type \"{name}\" doesn't exist");
return;
};
let mut new_document_node = node_type.to_document_node_default_inputs([Some(new_input)], metadata);
let mut new_document_node = node_type.to_document_node_default_inputs([new_input], metadata);
update_input(&mut new_document_node.inputs, node_id, self.document_metadata);
self.network.nodes.insert(node_id, new_document_node);
}
@@ -575,7 +575,10 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
transform_in,
skip_rerender,
} => {
let parent_transform = document.metadata.document_to_viewport * document.multiply_transforms(&layer[..layer.len() - 1]).unwrap_or_default();
let layer_identifier = LayerNodeIdentifier::new(*layer.last().unwrap(), &document.document_network);
let parent_transform = document
.metadata
.transform_to_viewport(layer_identifier.parent(&document.metadata).unwrap_or(LayerNodeIdentifier::ROOT));
let bounds = LayerBounds::new(document, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&layer, document, node_graph, responses) {
modify_inputs.transform_change(transform, transform_in, parent_transform, bounds, skip_rerender);
@@ -597,8 +600,12 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
transform_in,
skip_rerender,
} => {
let parent_transform = document.metadata.document_to_viewport * document.multiply_transforms(&layer[..layer.len() - 1]).unwrap_or_default();
let current_transform = Some(document.metadata.transform_to_viewport(LayerNodeIdentifier::new(*layer.last().unwrap(), &document.document_network)));
let layer_identifier = LayerNodeIdentifier::new(*layer.last().unwrap(), &document.document_network);
let parent_transform = document
.metadata
.transform_to_viewport(layer_identifier.parent(&document.metadata).unwrap_or(LayerNodeIdentifier::ROOT));
let current_transform = Some(document.metadata.transform_to_viewport(layer_identifier));
let bounds = LayerBounds::new(document, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&layer, document, node_graph, responses) {
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, bounds, skip_rerender);
@@ -34,7 +34,7 @@ impl OriginalTransforms {
match self {
OriginalTransforms::Layer(layer_map) => {
for &layer in selected {
layer_map.entry(layer).or_insert_with(|| document.metadata.transform_to_document(layer));
layer_map.entry(layer).or_insert_with(|| document.metadata.local_transform(layer));
}
}
OriginalTransforms::Path(path_map) => {
@@ -349,7 +349,7 @@ impl<'a> Selected<'a> {
let xy_summation = self
.selected
.iter()
.filter_map(|&layer| graph_modification_utils::get_viewport_pivot(layer, self.document))
.map(|&layer| graph_modification_utils::get_viewport_pivot(layer, self.document))
.reduce(|a, b| a + b)
.unwrap_or_default();
@@ -366,6 +366,40 @@ impl<'a> Selected<'a> {
(min + max) / 2.
}
fn transform_layer(document: &Document, layer: LayerNodeIdentifier, original_transform: Option<&DAffine2>, transformation: DAffine2, responses: &mut VecDeque<Message>) {
let Some(&original_transform) = original_transform else { return };
let parent = layer.parent(&document.metadata);
let to = parent.map(|parent| document.metadata.transform_to_viewport(parent)).unwrap_or(document.metadata.document_to_viewport);
let new = to.inverse() * transformation * to * original_transform;
responses.add(GraphOperationMessage::TransformSet {
layer: layer.to_path(),
transform: new,
transform_in: TransformIn::Local,
skip_rerender: false,
});
}
fn transform_path(document: &Document, layer: LayerNodeIdentifier, initial_points: Option<&Vec<(ManipulatorPointId, DVec2)>>, transformation: DAffine2, responses: &mut VecDeque<Message>) {
let viewspace = document.metadata.transform_to_viewport(layer);
let layerspace_rotation = viewspace.inverse() * transformation;
let Some(initial_points) = initial_points else {
return;
};
for (point_id, position) in initial_points {
let viewport_point = viewspace.transform_point2(*position);
let new_pos_viewport = layerspace_rotation.transform_point2(viewport_point);
let point = *point_id;
let position = new_pos_viewport;
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}
}
pub fn update_transforms(&mut self, delta: DAffine2) {
if !self.selected.is_empty() {
let pivot = DAffine2::from_translation(*self.pivot);
@@ -374,56 +408,13 @@ impl<'a> Selected<'a> {
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
for layer_ancestors in self.document.metadata.shallowest_unique_layers(self.selected.iter().copied()) {
let layer = *layer_ancestors.last().unwrap();
let parent = layer.parent(&self.document.metadata);
if *self.tool_type == ToolType::Select {
let original_layer_transforms = match self.original_transforms {
OriginalTransforms::Layer(layer_map) => *layer_map.get(&layer).unwrap(),
OriginalTransforms::Path(_path_map) => {
warn!("Found Path variant in original_transforms, returning identity transform for layer {layer:?}");
DAffine2::IDENTITY
}
};
let to = parent
.map(|parent| self.document.metadata.transform_to_viewport(parent))
.unwrap_or(self.document.metadata.document_to_viewport);
let new = to.inverse() * transformation * to * original_layer_transforms;
self.responses.add(GraphOperationMessage::TransformSet {
layer: layer.to_path(),
transform: new,
transform_in: TransformIn::Local,
skip_rerender: false,
});
match &self.original_transforms {
OriginalTransforms::Layer(layer_transforms) => Self::transform_layer(self.document, layer, layer_transforms.get(&layer), transformation, self.responses),
OriginalTransforms::Path(path_transforms) => Self::transform_path(self.document, layer, path_transforms.get(&layer), transformation, self.responses),
}
if *self.tool_type == ToolType::Path {
let viewspace = self.document.metadata.transform_to_viewport(layer);
let layerspace_rotation = viewspace.inverse() * transformation;
let initial_points = match self.original_transforms {
OriginalTransforms::Layer(_layer_map) => {
warn!("Found Layer variant in original_transforms when Path wanted, returning identity transform for layer");
None
}
OriginalTransforms::Path(path_map) => path_map.get(&layer),
};
let Some(original) = initial_points else {
warn!("Initial Points empty, it should not be possible to reach here without points");
continue;
};
for (point_id, position) in original {
let viewport_point = viewspace.transform_point2(*position);
let new_pos_viewport = layerspace_rotation.transform_point2(viewport_point);
let point = *point_id;
let position = new_pos_viewport;
self.responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}
}
self.responses.add(BroadcastEvent::DocumentIsDirty);
}
self.responses.add(BroadcastEvent::DocumentIsDirty);
}
}