Skip to content

Commit 5bca931

Browse files
committed
Rename handle mirroring to colinear
1 parent ea4f3d8 commit 5bca931

20 files changed

Lines changed: 328 additions & 307 deletions

File tree

editor/src/messages/input_mapper/default_mapping.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,9 @@ pub fn default_mapping() -> Mapping {
200200
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors),
201201
entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints),
202202
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
203-
entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop { shift_mirror_distance: Shift }),
204-
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter {
205-
add_to_selection: Shift
206-
}),
207-
entry!(DoubleClick(MouseButton::Left); action_dispatch=PathToolMessage::FlipSharp),
203+
entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop { equidistant: Shift }),
204+
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { add_to_selection: Shift }),
205+
entry!(DoubleClick(MouseButton::Left); action_dispatch=PathToolMessage::FlipSmoothSharp),
208206
entry!(KeyDown(ArrowRight); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: 0. }),
209207
entry!(KeyDown(ArrowRight); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: 0. }),
210208
entry!(KeyDown(ArrowRight); modifiers=[ArrowUp], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT }),

editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ pub enum VectorDataModification {
132132
RemoveManipulatorGroup { id: ManipulatorGroupId },
133133
RemoveManipulatorPoint { point: ManipulatorPointId },
134134
SetClosed { index: usize, closed: bool },
135-
SetManipulatorHandleMirroring { id: ManipulatorGroupId, mirror_angle: bool },
135+
SetManipulatorColinearHandlesState { id: ManipulatorGroupId, colinear: bool },
136136
SetManipulatorPosition { point: ManipulatorPointId, position: DVec2 },
137-
ToggleManipulatorHandleMirroring { id: ManipulatorGroupId },
137+
ToggleManipulatorColinearHandlesState { id: ManipulatorGroupId },
138138
UpdateSubpaths { subpaths: Vec<Subpath<ManipulatorGroupId>> },
139139
}

editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,8 @@ impl<'a> ModifyInputsContext<'a> {
462462
let mut empty = false;
463463

464464
self.modify_inputs("Shape", false, |inputs, _node_id, _metadata| {
465-
let [subpaths, mirror_angle_groups] = inputs.as_mut_slice() else {
466-
panic!("Shape does not have subpath and mirror angle inputs");
465+
let [subpaths, colinear_manipulators] = inputs.as_mut_slice() else {
466+
panic!("Shape does not have both `subpath` and `colinear_manipulators` inputs");
467467
};
468468

469469
let NodeInput::Value {
@@ -474,16 +474,16 @@ impl<'a> ModifyInputsContext<'a> {
474474
return;
475475
};
476476
let NodeInput::Value {
477-
tagged_value: TaggedValue::ManipulatorGroupIds(mirror_angle_groups),
477+
tagged_value: TaggedValue::ManipulatorGroupIds(colinear_manipulators),
478478
..
479-
} = mirror_angle_groups
479+
} = colinear_manipulators
480480
else {
481481
return;
482482
};
483483

484484
[old_bounds_min, old_bounds_max] = transform_utils::nonzero_subpath_bounds(subpaths);
485485

486-
transform_utils::VectorModificationState { subpaths, mirror_angle_groups }.modify(modification);
486+
transform_utils::VectorModificationState { subpaths, colinear_manipulators }.modify(modification);
487487
empty = !subpaths.iter().any(|subpath| !subpath.is_empty());
488488

489489
[new_bounds_min, new_bounds_max] = transform_utils::nonzero_subpath_bounds(subpaths);

editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ pub fn nonzero_subpath_bounds(subpaths: &[Subpath<ManipulatorGroupId>]) -> [DVec
199199

200200
pub struct VectorModificationState<'a> {
201201
pub subpaths: &'a mut Vec<Subpath<ManipulatorGroupId>>,
202-
pub mirror_angle_groups: &'a mut Vec<ManipulatorGroupId>,
202+
pub colinear_manipulators: &'a mut Vec<ManipulatorGroupId>,
203203
}
204204
impl<'a> VectorModificationState<'a> {
205205
fn insert_start(&mut self, subpath_index: usize, manipulator_group: ManipulatorGroup<ManipulatorGroupId>) {
@@ -246,19 +246,19 @@ impl<'a> VectorModificationState<'a> {
246246
}
247247
}
248248

249-
fn set_mirror(&mut self, id: ManipulatorGroupId, mirror_angle: bool) {
250-
if !mirror_angle {
251-
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
252-
} else if !self.mirror_angle_groups.contains(&id) {
253-
self.mirror_angle_groups.push(id);
249+
fn set_manipulator_colinear_handles_state(&mut self, id: ManipulatorGroupId, colinear: bool) {
250+
if !colinear {
251+
self.colinear_manipulators.retain(|&manipulator_group_id| manipulator_group_id != id);
252+
} else if !self.colinear_manipulators.contains(&id) {
253+
self.colinear_manipulators.push(id);
254254
}
255255
}
256256

257-
fn toggle_mirror(&mut self, id: ManipulatorGroupId) {
258-
if self.mirror_angle_groups.contains(&id) {
259-
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
257+
fn toggle_manipulator_colinear_handles_state(&mut self, id: ManipulatorGroupId) {
258+
if self.colinear_manipulators.contains(&id) {
259+
self.colinear_manipulators.retain(|&manipulator_group_id| manipulator_group_id != id);
260260
} else {
261-
self.mirror_angle_groups.push(id);
261+
self.colinear_manipulators.push(id);
262262
}
263263
}
264264

@@ -271,7 +271,7 @@ impl<'a> VectorModificationState<'a> {
271271
SelectedType::InHandle => manipulator.in_handle = Some(position),
272272
SelectedType::OutHandle => manipulator.out_handle = Some(position),
273273
}
274-
if point.manipulator_type != SelectedType::Anchor && self.mirror_angle_groups.contains(&point.group) {
274+
if point.manipulator_type != SelectedType::Anchor && self.colinear_manipulators.contains(&point.group) {
275275
let reflect = |opposite: DVec2| {
276276
(manipulator.anchor - position)
277277
.try_normalize()
@@ -298,9 +298,9 @@ impl<'a> VectorModificationState<'a> {
298298
VectorDataModification::RemoveManipulatorGroup { id } => self.remove_group(id),
299299
VectorDataModification::RemoveManipulatorPoint { point } => self.remove_point(point),
300300
VectorDataModification::SetClosed { index, closed } => self.subpaths[index].set_closed(closed),
301-
VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle } => self.set_mirror(id, mirror_angle),
301+
VectorDataModification::SetManipulatorColinearHandlesState { id, colinear } => self.set_manipulator_colinear_handles_state(id, colinear),
302302
VectorDataModification::SetManipulatorPosition { point, position } => self.set_position(point, position),
303-
VectorDataModification::ToggleManipulatorHandleMirroring { id } => self.toggle_mirror(id),
303+
VectorDataModification::ToggleManipulatorColinearHandlesState { id } => self.toggle_manipulator_colinear_handles_state(id),
304304
VectorDataModification::UpdateSubpaths { subpaths } => *self.subpaths = subpaths,
305305
}
306306
}

editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2442,7 +2442,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
24422442
}),
24432443
inputs: vec![
24442444
DocumentInputType::value("Path Data", TaggedValue::Subpaths(vec![]), false),
2445-
DocumentInputType::value("Mirror", TaggedValue::ManipulatorGroupIds(vec![]), false),
2445+
DocumentInputType::value("Colinear Manipulators", TaggedValue::ManipulatorGroupIds(vec![]), false),
24462446
],
24472447
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
24482448
..Default::default()

editor/src/messages/portfolio/document/utility_types/misc.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub struct SnappingState {
6161
pub geometry_snapping: bool,
6262
pub grid_snapping: bool,
6363
pub bounds: BoundsSnapping,
64-
pub nodes: NodeSnapping,
64+
pub nodes: PointSnapping,
6565
pub grid: GridSnapping,
6666
pub tolerance: f64,
6767
pub artboards: bool,
@@ -79,11 +79,11 @@ impl Default for SnappingState {
7979
edge_midpoints: false,
8080
centers: true,
8181
},
82-
nodes: NodeSnapping {
82+
nodes: PointSnapping {
8383
paths: true,
8484
path_intersections: true,
85-
sharp_nodes: true,
86-
smooth_nodes: true,
85+
point_handles_free: true,
86+
point_handles_colinear: true,
8787
line_midpoints: true,
8888
normals: true,
8989
tangents: true,
@@ -110,8 +110,8 @@ impl SnappingState {
110110
BoundingBoxSnapTarget::Center => self.bounds.centers,
111111
},
112112
SnapTarget::Geometry(nodes) if self.geometry_snapping => match nodes {
113-
GeometrySnapTarget::Smooth => self.nodes.smooth_nodes,
114-
GeometrySnapTarget::Sharp => self.nodes.sharp_nodes,
113+
GeometrySnapTarget::HandlesColinear => self.nodes.point_handles_colinear,
114+
GeometrySnapTarget::HandlesFree => self.nodes.point_handles_free,
115115
GeometrySnapTarget::LineMidpoint => self.nodes.line_midpoints,
116116
GeometrySnapTarget::Path => self.nodes.paths,
117117
GeometrySnapTarget::Normal => self.nodes.normals,
@@ -132,11 +132,11 @@ pub struct BoundsSnapping {
132132
pub centers: bool,
133133
}
134134
#[derive(Clone, Debug, Serialize, Deserialize)]
135-
pub struct NodeSnapping {
135+
pub struct PointSnapping {
136136
pub paths: bool,
137137
pub path_intersections: bool,
138-
pub sharp_nodes: bool,
139-
pub smooth_nodes: bool,
138+
pub point_handles_free: bool,
139+
pub point_handles_colinear: bool,
140140
pub line_midpoints: bool,
141141
pub normals: bool,
142142
pub tangents: bool,
@@ -227,8 +227,8 @@ pub enum BoardSnapSource {
227227
}
228228
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229229
pub enum GeometrySnapSource {
230-
Smooth,
231-
Sharp,
230+
HandlesColinear,
231+
HandlesFree,
232232
LineMidpoint,
233233
PathIntersection,
234234
Handle,
@@ -258,8 +258,8 @@ pub enum BoundingBoxSnapTarget {
258258
}
259259
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260260
pub enum GeometrySnapTarget {
261-
Smooth,
262-
Sharp,
261+
HandlesColinear,
262+
HandlesFree,
263263
LineMidpoint,
264264
Path,
265265
Normal,

editor/src/messages/tool/common_functionality/graph_modification_utils.rs

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,20 @@ pub fn new_svg_layer(svg: String, transform: glam::DAffine2, id: NodeId, parent:
4747
LayerNodeIdentifier::new_unchecked(id)
4848
}
4949

50-
/// Batch set all of the manipulator groups to mirror on a specific layer
51-
pub fn set_manipulator_mirror_angle(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>], layer: LayerNodeIdentifier, mirror_angle: bool, responses: &mut VecDeque<Message>) {
50+
/// Batch set all of the manipulator groups to set their colinear handle state on a specific layer
51+
pub fn set_manipulator_colinear_handles_state(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>], layer: LayerNodeIdentifier, colinear: bool, responses: &mut VecDeque<Message>) {
5252
for manipulator_group in manipulator_groups {
5353
responses.add(GraphOperationMessage::Vector {
5454
layer,
55-
modification: VectorDataModification::SetManipulatorHandleMirroring {
56-
id: manipulator_group.id,
57-
mirror_angle,
58-
},
55+
modification: VectorDataModification::SetManipulatorColinearHandlesState { id: manipulator_group.id, colinear },
5956
});
6057
}
6158
}
6259

6360
/// Locate the subpaths from the shape nodes of a particular layer
6461
pub fn get_subpaths(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<&Vec<Subpath<ManipulatorGroupId>>> {
65-
if let TaggedValue::Subpaths(subpaths) = NodeGraphLayer::new(layer, document_network)?.find_input("Shape", 0)? {
62+
let path_data_node_input_index = 0;
63+
if let TaggedValue::Subpaths(subpaths) = NodeGraphLayer::new(layer, document_network).find_input("Shape", path_data_node_input_index)? {
6664
Some(subpaths)
6765
} else {
6866
None
@@ -71,7 +69,8 @@ pub fn get_subpaths(layer: LayerNodeIdentifier, document_network: &NodeNetwork)
7169

7270
/// Locate the final pivot from the transform (TODO: decide how the pivot should actually work)
7371
pub fn get_pivot(layer: LayerNodeIdentifier, network: &NodeNetwork) -> Option<DVec2> {
74-
if let TaggedValue::DVec2(pivot) = NodeGraphLayer::new(layer, network)?.find_input("Transform", 5)? {
72+
let pivot_node_input_index = 5;
73+
if let TaggedValue::DVec2(pivot) = NodeGraphLayer::new(layer, network).find_input("Transform", pivot_node_input_index)? {
7574
Some(*pivot)
7675
} else {
7776
None
@@ -84,18 +83,19 @@ pub fn get_viewport_pivot(layer: LayerNodeIdentifier, document_network: &NodeNet
8483
document_metadata.transform_to_viewport(layer).transform_point2(min + (max - min) * pivot)
8584
}
8685

87-
/// Get the currently mirrored handles for a particular layer from the shape node
88-
pub fn get_mirror_handles(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<&Vec<ManipulatorGroupId>> {
89-
if let TaggedValue::ManipulatorGroupIds(mirror_handles) = NodeGraphLayer::new(layer, document_network)?.find_input("Shape", 1)? {
90-
Some(mirror_handles)
86+
/// Get the manipulator groups that currently have colinear handles for a particular layer from the shape node
87+
pub fn get_colinear_manipulators(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<&Vec<ManipulatorGroupId>> {
88+
let colinear_manipulators_node_input_index = 1;
89+
if let TaggedValue::ManipulatorGroupIds(manipulator_groups) = NodeGraphLayer::new(layer, document_network).find_input("Shape", colinear_manipulators_node_input_index)? {
90+
Some(manipulator_groups)
9191
} else {
9292
None
9393
}
9494
}
9595

9696
/// Get the current gradient of a layer from the closest Fill node
9797
pub fn get_gradient(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<Gradient> {
98-
let inputs = NodeGraphLayer::new(layer, document_network)?.find_node_inputs("Fill")?;
98+
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Fill")?;
9999
let TaggedValue::FillType(FillType::Gradient) = inputs.get(1)?.as_value()? else {
100100
return None;
101101
};
@@ -125,7 +125,7 @@ pub fn get_gradient(layer: LayerNodeIdentifier, document_network: &NodeNetwork)
125125

126126
/// Get the current fill of a layer from the closest Fill node
127127
pub fn get_fill_color(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<Color> {
128-
let inputs = NodeGraphLayer::new(layer, document_network)?.find_node_inputs("Fill")?;
128+
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Fill")?;
129129
let TaggedValue::Color(color) = inputs.get(2)?.as_value()? else {
130130
return None;
131131
};
@@ -134,7 +134,7 @@ pub fn get_fill_color(layer: LayerNodeIdentifier, document_network: &NodeNetwork
134134

135135
/// Get the current blend mode of a layer from the closest Blend Mode node
136136
pub fn get_blend_mode(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<BlendMode> {
137-
let inputs = NodeGraphLayer::new(layer, document_network)?.find_node_inputs("Blend Mode")?;
137+
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Blend Mode")?;
138138
let TaggedValue::BlendMode(blend_mode) = inputs.get(1)?.as_value()? else {
139139
return None;
140140
};
@@ -149,24 +149,24 @@ pub fn get_blend_mode(layer: LayerNodeIdentifier, document_network: &NodeNetwork
149149
/// - The default value of 100% if no Opacity node is present, but this function returns None in that case
150150
/// With those limitations in mind, the intention of this function is to show just the value already present in an upstream Opacity node so that value can be directly edited.
151151
pub fn get_opacity(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<f64> {
152-
let inputs = NodeGraphLayer::new(layer, document_network)?.find_node_inputs("Opacity")?;
152+
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Opacity")?;
153153
let TaggedValue::F64(opacity) = inputs.get(1)?.as_value()? else {
154154
return None;
155155
};
156156
Some(*opacity)
157157
}
158158

159159
pub fn get_fill_id(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<NodeId> {
160-
NodeGraphLayer::new(layer, document_network)?.node_id("Fill")
160+
NodeGraphLayer::new(layer, document_network).node_id("Fill")
161161
}
162162

163163
pub fn get_text_id(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<NodeId> {
164-
NodeGraphLayer::new(layer, document_network)?.node_id("Text")
164+
NodeGraphLayer::new(layer, document_network).node_id("Text")
165165
}
166166

167167
/// Gets properties from the Text node
168168
pub fn get_text(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<(&String, &Font, f64)> {
169-
let inputs = NodeGraphLayer::new(layer, document_network)?.find_node_inputs("Text")?;
169+
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Text")?;
170170
let NodeInput::Value {
171171
tagged_value: TaggedValue::String(text),
172172
..
@@ -195,7 +195,8 @@ pub fn get_text(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> O
195195
}
196196

197197
pub fn get_stroke_width(layer: LayerNodeIdentifier, network: &NodeNetwork) -> Option<f64> {
198-
if let TaggedValue::F64(width) = NodeGraphLayer::new(layer, network)?.find_input("Stroke", 2)? {
198+
let weight_node_input_index = 2;
199+
if let TaggedValue::F64(width) = NodeGraphLayer::new(layer, network).find_input("Stroke", weight_node_input_index)? {
199200
Some(*width)
200201
} else {
201202
None
@@ -204,7 +205,7 @@ pub fn get_stroke_width(layer: LayerNodeIdentifier, network: &NodeNetwork) -> Op
204205

205206
/// Checks if a specified layer uses an upstream node matching the given name.
206207
pub fn is_layer_fed_by_node_of_name(layer: LayerNodeIdentifier, document_network: &NodeNetwork, node_name: &str) -> bool {
207-
NodeGraphLayer::new(layer, document_network).is_some_and(|layer| layer.find_node_inputs(node_name).is_some())
208+
NodeGraphLayer::new(layer, document_network).find_node_inputs(node_name).is_some()
208209
}
209210

210211
/// Convert subpaths to an iterator of manipulator groups
@@ -220,20 +221,16 @@ pub fn get_manipulator_from_id(subpaths: &[Subpath<ManipulatorGroupId>], id: Man
220221
/// An immutable reference to a layer within the document node graph for easy access.
221222
pub struct NodeGraphLayer<'a> {
222223
node_graph: &'a NodeNetwork,
223-
_outwards_links: HashMap<NodeId, Vec<NodeId>>,
224224
layer_node: NodeId,
225225
}
226226

227227
impl<'a> NodeGraphLayer<'a> {
228228
/// Get the layer node from the document
229-
pub fn new(layer: LayerNodeIdentifier, network: &'a NodeNetwork) -> Option<Self> {
230-
let outwards_links = network.collect_outwards_links();
231-
232-
Some(Self {
229+
pub fn new(layer: LayerNodeIdentifier, network: &'a NodeNetwork) -> Self {
230+
Self {
233231
node_graph: network,
234-
_outwards_links: outwards_links,
235232
layer_node: layer.to_node(),
236-
})
233+
}
237234
}
238235

239236
/// Return an iterator up the primary flow of the layer
@@ -257,6 +254,7 @@ impl<'a> NodeGraphLayer<'a> {
257254

258255
/// Find a specific input of a node within the layer's primary flow
259256
pub fn find_input(&self, node_name: &str, index: usize) -> Option<&'a TaggedValue> {
257+
// TODO: Find a better way to accept a node input rather than using its index (which is quite unclear and fragile)
260258
self.find_node_inputs(node_name)?.get(index)?.as_value()
261259
}
262260
}

0 commit comments

Comments
 (0)