Skip to content

Commit

Permalink
Rename handle mirroring to colinear
Browse files Browse the repository at this point in the history
  • Loading branch information
Keavon committed Mar 14, 2024
1 parent ea4f3d8 commit 5bca931
Show file tree
Hide file tree
Showing 20 changed files with 328 additions and 307 deletions.
8 changes: 3 additions & 5 deletions editor/src/messages/input_mapper/default_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,9 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors),
entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints),
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop { shift_mirror_distance: Shift }),
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter {
add_to_selection: Shift
}),
entry!(DoubleClick(MouseButton::Left); action_dispatch=PathToolMessage::FlipSharp),
entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop { equidistant: Shift }),
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { add_to_selection: Shift }),
entry!(DoubleClick(MouseButton::Left); action_dispatch=PathToolMessage::FlipSmoothSharp),
entry!(KeyDown(ArrowRight); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: 0. }),
entry!(KeyDown(ArrowRight); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: 0. }),
entry!(KeyDown(ArrowRight); modifiers=[ArrowUp], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ pub enum VectorDataModification {
RemoveManipulatorGroup { id: ManipulatorGroupId },
RemoveManipulatorPoint { point: ManipulatorPointId },
SetClosed { index: usize, closed: bool },
SetManipulatorHandleMirroring { id: ManipulatorGroupId, mirror_angle: bool },
SetManipulatorColinearHandlesState { id: ManipulatorGroupId, colinear: bool },
SetManipulatorPosition { point: ManipulatorPointId, position: DVec2 },
ToggleManipulatorHandleMirroring { id: ManipulatorGroupId },
ToggleManipulatorColinearHandlesState { id: ManipulatorGroupId },
UpdateSubpaths { subpaths: Vec<Subpath<ManipulatorGroupId>> },
}
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,8 @@ impl<'a> ModifyInputsContext<'a> {
let mut empty = false;

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

let NodeInput::Value {
Expand All @@ -474,16 +474,16 @@ impl<'a> ModifyInputsContext<'a> {
return;
};
let NodeInput::Value {
tagged_value: TaggedValue::ManipulatorGroupIds(mirror_angle_groups),
tagged_value: TaggedValue::ManipulatorGroupIds(colinear_manipulators),
..
} = mirror_angle_groups
} = colinear_manipulators
else {
return;
};

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

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

[new_bounds_min, new_bounds_max] = transform_utils::nonzero_subpath_bounds(subpaths);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ pub fn nonzero_subpath_bounds(subpaths: &[Subpath<ManipulatorGroupId>]) -> [DVec

pub struct VectorModificationState<'a> {
pub subpaths: &'a mut Vec<Subpath<ManipulatorGroupId>>,
pub mirror_angle_groups: &'a mut Vec<ManipulatorGroupId>,
pub colinear_manipulators: &'a mut Vec<ManipulatorGroupId>,
}
impl<'a> VectorModificationState<'a> {
fn insert_start(&mut self, subpath_index: usize, manipulator_group: ManipulatorGroup<ManipulatorGroupId>) {
Expand Down Expand Up @@ -246,19 +246,19 @@ impl<'a> VectorModificationState<'a> {
}
}

fn set_mirror(&mut self, id: ManipulatorGroupId, mirror_angle: bool) {
if !mirror_angle {
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
} else if !self.mirror_angle_groups.contains(&id) {
self.mirror_angle_groups.push(id);
fn set_manipulator_colinear_handles_state(&mut self, id: ManipulatorGroupId, colinear: bool) {
if !colinear {
self.colinear_manipulators.retain(|&manipulator_group_id| manipulator_group_id != id);
} else if !self.colinear_manipulators.contains(&id) {
self.colinear_manipulators.push(id);
}
}

fn toggle_mirror(&mut self, id: ManipulatorGroupId) {
if self.mirror_angle_groups.contains(&id) {
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
fn toggle_manipulator_colinear_handles_state(&mut self, id: ManipulatorGroupId) {
if self.colinear_manipulators.contains(&id) {
self.colinear_manipulators.retain(|&manipulator_group_id| manipulator_group_id != id);
} else {
self.mirror_angle_groups.push(id);
self.colinear_manipulators.push(id);
}
}

Expand All @@ -271,7 +271,7 @@ impl<'a> VectorModificationState<'a> {
SelectedType::InHandle => manipulator.in_handle = Some(position),
SelectedType::OutHandle => manipulator.out_handle = Some(position),
}
if point.manipulator_type != SelectedType::Anchor && self.mirror_angle_groups.contains(&point.group) {
if point.manipulator_type != SelectedType::Anchor && self.colinear_manipulators.contains(&point.group) {
let reflect = |opposite: DVec2| {
(manipulator.anchor - position)
.try_normalize()
Expand All @@ -298,9 +298,9 @@ impl<'a> VectorModificationState<'a> {
VectorDataModification::RemoveManipulatorGroup { id } => self.remove_group(id),
VectorDataModification::RemoveManipulatorPoint { point } => self.remove_point(point),
VectorDataModification::SetClosed { index, closed } => self.subpaths[index].set_closed(closed),
VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle } => self.set_mirror(id, mirror_angle),
VectorDataModification::SetManipulatorColinearHandlesState { id, colinear } => self.set_manipulator_colinear_handles_state(id, colinear),
VectorDataModification::SetManipulatorPosition { point, position } => self.set_position(point, position),
VectorDataModification::ToggleManipulatorHandleMirroring { id } => self.toggle_mirror(id),
VectorDataModification::ToggleManipulatorColinearHandlesState { id } => self.toggle_manipulator_colinear_handles_state(id),
VectorDataModification::UpdateSubpaths { subpaths } => *self.subpaths = subpaths,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2442,7 +2442,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}),
inputs: vec![
DocumentInputType::value("Path Data", TaggedValue::Subpaths(vec![]), false),
DocumentInputType::value("Mirror", TaggedValue::ManipulatorGroupIds(vec![]), false),
DocumentInputType::value("Colinear Manipulators", TaggedValue::ManipulatorGroupIds(vec![]), false),
],
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
..Default::default()
Expand Down
26 changes: 13 additions & 13 deletions editor/src/messages/portfolio/document/utility_types/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub struct SnappingState {
pub geometry_snapping: bool,
pub grid_snapping: bool,
pub bounds: BoundsSnapping,
pub nodes: NodeSnapping,
pub nodes: PointSnapping,
pub grid: GridSnapping,
pub tolerance: f64,
pub artboards: bool,
Expand All @@ -79,11 +79,11 @@ impl Default for SnappingState {
edge_midpoints: false,
centers: true,
},
nodes: NodeSnapping {
nodes: PointSnapping {
paths: true,
path_intersections: true,
sharp_nodes: true,
smooth_nodes: true,
point_handles_free: true,
point_handles_colinear: true,
line_midpoints: true,
normals: true,
tangents: true,
Expand All @@ -110,8 +110,8 @@ impl SnappingState {
BoundingBoxSnapTarget::Center => self.bounds.centers,
},
SnapTarget::Geometry(nodes) if self.geometry_snapping => match nodes {
GeometrySnapTarget::Smooth => self.nodes.smooth_nodes,
GeometrySnapTarget::Sharp => self.nodes.sharp_nodes,
GeometrySnapTarget::HandlesColinear => self.nodes.point_handles_colinear,
GeometrySnapTarget::HandlesFree => self.nodes.point_handles_free,
GeometrySnapTarget::LineMidpoint => self.nodes.line_midpoints,
GeometrySnapTarget::Path => self.nodes.paths,
GeometrySnapTarget::Normal => self.nodes.normals,
Expand All @@ -132,11 +132,11 @@ pub struct BoundsSnapping {
pub centers: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NodeSnapping {
pub struct PointSnapping {
pub paths: bool,
pub path_intersections: bool,
pub sharp_nodes: bool,
pub smooth_nodes: bool,
pub point_handles_free: bool,
pub point_handles_colinear: bool,
pub line_midpoints: bool,
pub normals: bool,
pub tangents: bool,
Expand Down Expand Up @@ -227,8 +227,8 @@ pub enum BoardSnapSource {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GeometrySnapSource {
Smooth,
Sharp,
HandlesColinear,
HandlesFree,
LineMidpoint,
PathIntersection,
Handle,
Expand Down Expand Up @@ -258,8 +258,8 @@ pub enum BoundingBoxSnapTarget {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GeometrySnapTarget {
Smooth,
Sharp,
HandlesColinear,
HandlesFree,
LineMidpoint,
Path,
Normal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,20 @@ pub fn new_svg_layer(svg: String, transform: glam::DAffine2, id: NodeId, parent:
LayerNodeIdentifier::new_unchecked(id)
}

/// Batch set all of the manipulator groups to mirror on a specific layer
pub fn set_manipulator_mirror_angle(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>], layer: LayerNodeIdentifier, mirror_angle: bool, responses: &mut VecDeque<Message>) {
/// Batch set all of the manipulator groups to set their colinear handle state on a specific layer
pub fn set_manipulator_colinear_handles_state(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>], layer: LayerNodeIdentifier, colinear: bool, responses: &mut VecDeque<Message>) {
for manipulator_group in manipulator_groups {
responses.add(GraphOperationMessage::Vector {
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring {
id: manipulator_group.id,
mirror_angle,
},
modification: VectorDataModification::SetManipulatorColinearHandlesState { id: manipulator_group.id, colinear },
});
}
}

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

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

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

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

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

/// Get the current blend mode of a layer from the closest Blend Mode node
pub fn get_blend_mode(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<BlendMode> {
let inputs = NodeGraphLayer::new(layer, document_network)?.find_node_inputs("Blend Mode")?;
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Blend Mode")?;
let TaggedValue::BlendMode(blend_mode) = inputs.get(1)?.as_value()? else {
return None;
};
Expand All @@ -149,24 +149,24 @@ pub fn get_blend_mode(layer: LayerNodeIdentifier, document_network: &NodeNetwork
/// - The default value of 100% if no Opacity node is present, but this function returns None in that case
/// 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.
pub fn get_opacity(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<f64> {
let inputs = NodeGraphLayer::new(layer, document_network)?.find_node_inputs("Opacity")?;
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Opacity")?;
let TaggedValue::F64(opacity) = inputs.get(1)?.as_value()? else {
return None;
};
Some(*opacity)
}

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

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

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

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

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

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

impl<'a> NodeGraphLayer<'a> {
/// Get the layer node from the document
pub fn new(layer: LayerNodeIdentifier, network: &'a NodeNetwork) -> Option<Self> {
let outwards_links = network.collect_outwards_links();

Some(Self {
pub fn new(layer: LayerNodeIdentifier, network: &'a NodeNetwork) -> Self {
Self {
node_graph: network,
_outwards_links: outwards_links,
layer_node: layer.to_node(),
})
}
}

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

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

0 comments on commit 5bca931

Please sign in to comment.