mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
multi-layer drag and repeat
This commit is contained in:
@@ -22,8 +22,11 @@ pub enum GraphOperationMessage {
|
||||
layer: LayerNodeIdentifier,
|
||||
fill: Fill,
|
||||
},
|
||||
RepeatSet {
|
||||
CircularRepeatSet {
|
||||
layer: LayerNodeIdentifier,
|
||||
angle: f64,
|
||||
radius: f64,
|
||||
count: u32,
|
||||
},
|
||||
BlendingFillSet {
|
||||
layer: LayerNodeIdentifier,
|
||||
|
||||
@@ -37,9 +37,9 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
modify_inputs.fill_set(fill);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::RepeatSet { layer } => {
|
||||
GraphOperationMessage::CircularRepeatSet { layer, angle, radius, count } => {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.create_node("Repeat");
|
||||
modify_inputs.circular_repeat_set(angle, radius, count);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::BlendingFillSet { layer, fill } => {
|
||||
|
||||
@@ -394,6 +394,17 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.dash_offset), false), true);
|
||||
}
|
||||
|
||||
pub fn circular_repeat_set(&mut self, angle: f64, radius: f64, count: u32) {
|
||||
let Some(circular_repeat_node_id) = self.existing_node_id("Circular Repeat", true) else { return };
|
||||
|
||||
let input_connector = InputConnector::node(circular_repeat_node_id, graphene_std::vector::circular_repeat::AngleOffsetInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(angle), false), true);
|
||||
let input_connector = InputConnector::node(circular_repeat_node_id, graphene_std::vector::circular_repeat::RadiusInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(radius), false), true);
|
||||
let input_connector = InputConnector::node(circular_repeat_node_id, graphene_std::vector::circular_repeat::CountInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::U32(count), false), false);
|
||||
}
|
||||
|
||||
/// Update the transform value of the upstream Transform node based a change to its existing value and the given parent transform.
|
||||
/// A new Transform node is created if one does not exist, unless it would be given the identity transform.
|
||||
pub fn transform_change_with_parent(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, skip_rerender: bool) {
|
||||
|
||||
@@ -230,6 +230,18 @@ pub fn extract_star_parameters(layer: Option<LayerNodeIdentifier>, document: &Do
|
||||
Some((sides, radius_1, radius_2))
|
||||
}
|
||||
|
||||
pub fn extract_circular_repeat_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(f64, f64, u32)> {
|
||||
let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs("Circular Repeat")?;
|
||||
|
||||
let (Some(&TaggedValue::F64(angle)), Some(&TaggedValue::F64(radius)), Some(&TaggedValue::U32(count))) =
|
||||
(node_inputs.get(1)?.as_value(), node_inputs.get(2)?.as_value(), node_inputs.get(3)?.as_value())
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some((angle, radius, count))
|
||||
}
|
||||
|
||||
/// Extract the node input values of Polygon.
|
||||
/// Returns an option of (sides, radius).
|
||||
pub fn extract_polygon_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(u32, f64)> {
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::{self, find_spline, merge_layers, merge_points};
|
||||
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_circular_repeat_parameters, extract_star_parameters};
|
||||
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint};
|
||||
use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend};
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
@@ -49,7 +50,6 @@ pub enum OperationToolMessage {
|
||||
Confirm,
|
||||
DragStart,
|
||||
DragStop,
|
||||
MergeEndpoints,
|
||||
PointerMove,
|
||||
PointerOutsideViewport,
|
||||
Undo,
|
||||
@@ -222,10 +222,19 @@ impl ToolTransition for OperationTool {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct OperationToolData {}
|
||||
struct OperationToolData {
|
||||
drag_start: DVec2,
|
||||
clicked_layer: LayerNodeIdentifier,
|
||||
layers_dragging: Vec<(LayerNodeIdentifier, f64)>,
|
||||
initial_center: DVec2,
|
||||
}
|
||||
|
||||
impl OperationToolData {
|
||||
fn cleanup(&mut self) {}
|
||||
fn cleanup(&mut self) {
|
||||
self.layers_dragging.clear();
|
||||
}
|
||||
|
||||
fn modify_circular_repeat(&mut self) {}
|
||||
}
|
||||
|
||||
impl Fsm for OperationToolFsmState {
|
||||
@@ -251,19 +260,66 @@ impl Fsm for OperationToolFsmState {
|
||||
|
||||
let ToolMessage::Operation(event) = event else { return self };
|
||||
match (self, event) {
|
||||
(_, OperationToolMessage::Overlays { context: mut overlay_context }) => self,
|
||||
(_, OperationToolMessage::Overlays { context: mut overlay_context }) => {}
|
||||
(OperationToolFsmState::Ready, OperationToolMessage::DragStart) => {
|
||||
let selected_layers = document
|
||||
.network_interface
|
||||
.selected_nodes()
|
||||
.selected_layers(document.metadata())
|
||||
.collect::<HashSet<LayerNodeIdentifier>>();
|
||||
let Some(layer) = document.click(&input) else { return self };
|
||||
responses.add(GraphOperationMessage::RepeatSet { layer });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
let viewport = document.metadata().transform_to_viewport(layer);
|
||||
|
||||
if selected_layers.contains(&layer) {
|
||||
// store all
|
||||
tool_data.layers_dragging = selected_layers
|
||||
.iter()
|
||||
.map(|layer| {
|
||||
let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(*layer), document).unwrap_or((0.0, 0.0, 6));
|
||||
(*layer, radius)
|
||||
})
|
||||
.collect::<Vec<(LayerNodeIdentifier, f64)>>();
|
||||
} else {
|
||||
// deselect all the layer and store the clicked layer for repeat and dragging
|
||||
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
||||
let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(layer), document).unwrap_or((0.0, 0.0, 6));
|
||||
tool_data.layers_dragging = vec![(layer, radius)];
|
||||
}
|
||||
tool_data.drag_start = input.mouse.position;
|
||||
tool_data.clicked_layer = layer;
|
||||
tool_data.initial_center = viewport.transform_point2(DVec2::ZERO);
|
||||
|
||||
OperationToolFsmState::Drawing
|
||||
}
|
||||
(OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => OperationToolFsmState::Drawing,
|
||||
(OperationToolFsmState::Drawing, OperationToolMessage::PointerMove) => OperationToolFsmState::Drawing,
|
||||
(_, OperationToolMessage::PointerMove) => {
|
||||
log::info!("hello");
|
||||
self
|
||||
(OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => {
|
||||
tool_data.cleanup();
|
||||
OperationToolFsmState::Ready
|
||||
}
|
||||
(OperationToolFsmState::Drawing, OperationToolMessage::PointerMove) => {
|
||||
// Don't add the repeat node unless dragging more that 5 px
|
||||
if tool_data.drag_start.distance(input.mouse.position) < 5. {
|
||||
return self;
|
||||
};
|
||||
|
||||
let viewport = document.metadata().transform_to_viewport(tool_data.clicked_layer);
|
||||
let center = viewport.transform_point2(DVec2::ZERO);
|
||||
let sign = (input.mouse.position - tool_data.initial_center).dot(tool_data.drag_start - tool_data.initial_center).signum();
|
||||
let delta = viewport.inverse().transform_vector2(input.mouse.position - tool_data.drag_start).length() * sign;
|
||||
log::info!("{:?}", delta);
|
||||
for (layer, initial_radius) in &tool_data.layers_dragging {
|
||||
responses.add(GraphOperationMessage::CircularRepeatSet {
|
||||
layer: *layer,
|
||||
angle: 0.,
|
||||
radius: initial_radius + delta,
|
||||
count: 6,
|
||||
});
|
||||
}
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
|
||||
OperationToolFsmState::Drawing
|
||||
}
|
||||
(_, OperationToolMessage::PointerMove) => self,
|
||||
|
||||
(OperationToolFsmState::Drawing, OperationToolMessage::PointerOutsideViewport) => OperationToolFsmState::Drawing,
|
||||
(state, OperationToolMessage::PointerOutsideViewport) => state,
|
||||
|
||||
Reference in New Issue
Block a user