Promote vector meshes from experimental by removing it from preferences

This commit is contained in:
Keavon Chambers 2025-12-21 15:30:04 -08:00
parent e73e524f3d
commit 123108c813
12 changed files with 71 additions and 133 deletions

View File

@ -256,29 +256,6 @@ impl PreferencesDialogMessageHandler {
.widget_instance(),
];
let checkbox_id = CheckboxId::new();
let vector_mesh_description = "
Allow the Pen tool to produce branching geometry, where more than two segments may be connected to one anchor point.\n\
\n\
Currently, vector meshes do not properly render strokes (branching joins) and fills (multiple regions).
"
.trim();
let vector_meshes = vec![
Separator::new(SeparatorType::Unrelated).widget_instance(),
Separator::new(SeparatorType::Unrelated).widget_instance(),
CheckboxInput::new(preferences.vector_meshes)
.tooltip_label("Vector Meshes")
.tooltip_description(vector_mesh_description)
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::VectorMeshes { enabled: checkbox_input.checked }.into())
.for_label(checkbox_id)
.widget_instance(),
TextLabel::new("Vector Meshes")
.tooltip_label("Vector Meshes")
.tooltip_description(vector_mesh_description)
.for_checkbox(checkbox_id)
.widget_instance(),
];
let checkbox_id = CheckboxId::new();
let brush_tool_description = "
Enable the Brush tool to support basic raster-based layer painting.\n\
@ -304,7 +281,7 @@ impl PreferencesDialogMessageHandler {
.widget_instance(),
];
rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, use_vello, vector_meshes, brush_tool]);
rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, use_vello, brush_tool]);
}
Layout(rows.into_iter().map(|r| LayoutGroup::Row { widgets: r }).collect())

View File

@ -3,7 +3,7 @@ use crate::consts::HIDE_HANDLE_DISTANCE;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerState, ShapeState};
use crate::messages::tool::tool_messages::tool_prelude::{DocumentMessageHandler, PreferencesMessageHandler};
use crate::messages::tool::tool_messages::tool_prelude::DocumentMessageHandler;
use glam::{DAffine2, DVec2};
use graphene_std::subpath::{Bezier, BezierHandles};
use graphene_std::text::{Font, FontCache, TextAlign, TextContext, TypesettingConfig};
@ -200,7 +200,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
}
}
pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext, preferences: &PreferencesMessageHandler) {
pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext) {
if !overlay_context.visibility_settings.anchors() {
return;
}
@ -213,7 +213,7 @@ pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &
let selected = shape_editor.selected_shape_state.get(&layer);
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));
for point in vector.extendable_points(preferences.vector_meshes) {
for point in vector.extendable_points() {
let Some(position) = vector.point_domain.position_from_id(point) else { continue };
let position = transform.transform_point2(position);
overlay_context.manipulator_anchor(position, is_selected(selected, ManipulatorPointId::Anchor(point)), None);

View File

@ -12,7 +12,6 @@ pub enum PreferencesMessage {
// Per-preference messages
UseVello { use_vello: bool },
SelectionMode { selection_mode: SelectionMode },
VectorMeshes { enabled: bool },
BrushTool { enabled: bool },
ModifyLayout { zoom_with_scroll: bool },
GraphWireStyle { style: GraphWireStyle },

View File

@ -17,7 +17,6 @@ pub struct PreferencesMessageHandler {
pub selection_mode: SelectionMode,
pub zoom_with_scroll: bool,
pub use_vello: bool,
pub vector_meshes: bool,
pub brush_tool: bool,
pub graph_wire_style: GraphWireStyle,
pub viewport_zoom_wheel_rate: f64,
@ -46,7 +45,6 @@ impl Default for PreferencesMessageHandler {
selection_mode: SelectionMode::Touched,
zoom_with_scroll: matches!(MappingVariant::default(), MappingVariant::ZoomWithScroll),
use_vello: EditorPreferences::default().use_vello,
vector_meshes: false,
brush_tool: false,
graph_wire_style: GraphWireStyle::default(),
viewport_zoom_wheel_rate: VIEWPORT_ZOOM_WHEEL_RATE,
@ -87,9 +85,6 @@ impl MessageHandler<PreferencesMessage, PreferencesMessageContext<'_>> for Prefe
responses.add(PortfolioMessage::UpdateVelloPreference);
responses.add(PortfolioMessage::EditorPreferences);
}
PreferencesMessage::VectorMeshes { enabled } => {
self.vector_meshes = enabled;
}
PreferencesMessage::BrushTool { enabled } => {
self.brush_tool = enabled;

View File

@ -421,7 +421,7 @@ impl ShapeState {
(point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors)
}
pub fn close_selected_path(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, vector_meshes: bool) {
pub fn close_selected_path(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
// First collect all selected anchor points across all layers
let all_selected_points: Vec<(LayerNodeIdentifier, PointId)> = self
.selected_shape_state
@ -446,14 +446,6 @@ impl ShapeState {
let (layer1, start_point) = all_selected_points[0];
let (layer2, end_point) = all_selected_points[1];
let Some(vector1) = document.network_interface.compute_modified_vector(layer1) else { return };
let Some(vector2) = document.network_interface.compute_modified_vector(layer2) else { return };
// If vector meshes is not selected then only for endpoints, otherwise normally applicable
if !vector_meshes && (vector1.all_connected(start_point).count() != 1 || vector2.all_connected(end_point).count() != 1) {
return;
}
if layer1 == layer2 {
if start_point == end_point {
return;

View File

@ -22,14 +22,8 @@ use graphene_std::vector::{HandleExt, PointId, SegmentId, Vector, VectorModifica
use kurbo::{CubicBez, DEFAULT_ACCURACY, Line, ParamCurve, PathSeg, Point, QuadBez, Shape};
/// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable.
pub fn should_extend(
document: &DocumentMessageHandler,
goal: DVec2,
tolerance: f64,
layers: impl Iterator<Item = LayerNodeIdentifier>,
preferences: &PreferencesMessageHandler,
) -> Option<(LayerNodeIdentifier, PointId, DVec2)> {
closest_point(document, goal, tolerance, layers, |_| false, preferences)
pub fn should_extend(document: &DocumentMessageHandler, goal: DVec2, tolerance: f64, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Option<(LayerNodeIdentifier, PointId, DVec2)> {
closest_point(document, goal, tolerance, layers, |_| false)
}
/// Determine the closest point to the goal point under max_distance.
@ -40,7 +34,6 @@ pub fn closest_point<T>(
max_distance: f64,
layers: impl Iterator<Item = LayerNodeIdentifier>,
exclude: T,
preferences: &PreferencesMessageHandler,
) -> Option<(LayerNodeIdentifier, PointId, DVec2)>
where
T: Fn(PointId) -> bool,
@ -50,7 +43,7 @@ where
for layer in layers {
let viewspace = document.metadata().transform_to_viewport(layer);
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for id in vector.extendable_points(preferences.vector_meshes) {
for id in vector.extendable_points() {
if exclude(id) {
continue;
}

View File

@ -236,7 +236,6 @@ impl Fsm for FreehandToolFsmState {
global_tool_data,
input,
shape_editor,
preferences,
viewport,
..
} = tool_action_data;
@ -244,7 +243,7 @@ impl Fsm for FreehandToolFsmState {
let ToolMessage::Freehand(event) = event else { return self };
match (self, event) {
(_, FreehandToolMessage::Overlays { context: mut overlay_context }) => {
path_endpoint_overlays(document, shape_editor, &mut overlay_context, tool_action_data.preferences);
path_endpoint_overlays(document, shape_editor, &mut overlay_context);
self
}
@ -258,7 +257,7 @@ impl Fsm for FreehandToolFsmState {
// Extend an endpoint of the selected path
let selected_nodes = document.network_interface.selected_nodes();
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
if let Some((layer, point, position)) = should_extend(document, input.mouse.position, tolerance, selected_nodes.selected_layers(document.metadata()), preferences) {
if let Some((layer, point, position)) = should_extend(document, input.mouse.position, tolerance, selected_nodes.selected_layers(document.metadata())) {
tool_data.layer = Some(layer);
tool_data.end_point = Some((position, point));
@ -519,7 +518,7 @@ mod test_freehand {
initial_segment_count
);
let extendable_points = initial_vector.extendable_points(false).collect::<Vec<_>>();
let extendable_points = initial_vector.extendable_points().collect::<Vec<_>>();
assert!(!extendable_points.is_empty(), "No extendable points found in the path");
let endpoint_id = extendable_points[0];

View File

@ -823,7 +823,7 @@ impl PathToolData {
.filter(|handle| handle.length(&vector) < 1e-6)
.map(|handle| handle.to_manipulator_point())
.collect::<Vec<_>>();
let endpoint = vector.extendable_points(false).any(|anchor| point == anchor);
let endpoint = vector.extendable_points_no_vector_meshes().any(|anchor| point == anchor);
if drag_zero_handle && (handles.len() == 1 && !endpoint) {
shape_editor.deselect_all_points();
@ -2662,7 +2662,7 @@ impl Fsm for PathToolFsmState {
}
(_, PathToolMessage::ClosePath) => {
responses.add(DocumentMessage::AddTransaction);
shape_editor.close_selected_path(document, responses, tool_action_data.preferences.vector_meshes);
shape_editor.close_selected_path(document, responses);
responses.add(DocumentMessage::EndTransaction);
responses.add(OverlaysMessage::Draw);

View File

@ -589,15 +589,7 @@ impl PenToolData {
}
/// If the user places the anchor on top of the previous anchor, it becomes sharp and the outgoing handle may be dragged.
fn bend_from_previous_point(
&mut self,
snap_data: SnapData,
transform: DAffine2,
layer: LayerNodeIdentifier,
preferences: &PreferencesMessageHandler,
shape_editor: &mut ShapeState,
responses: &mut VecDeque<Message>,
) {
fn bend_from_previous_point(&mut self, snap_data: SnapData, transform: DAffine2, layer: LayerNodeIdentifier, shape_editor: &mut ShapeState, responses: &mut VecDeque<Message>) {
self.g1_continuous = true;
let document = snap_data.document;
self.next_handle_start = self.next_point;
@ -614,7 +606,7 @@ impl PenToolData {
self.handle_end = None;
self.handle_mode = HandleMode::Free;
self.store_clicked_endpoint(document, &transform, snap_data.input, snap_data.viewport, preferences);
self.store_clicked_endpoint(document, &transform, snap_data.input, snap_data.viewport);
if self.modifiers.lock_angle {
self.set_lock_angle(&vector, id, self.prior_segment);
@ -631,8 +623,8 @@ impl PenToolData {
}
// Closing path
let closing_path_on_point = self.close_path_on_point(snap_data, &vector, document, preferences, id, &transform);
if !closing_path_on_point && preferences.vector_meshes {
let closing_path_on_point = self.close_path_on_point(snap_data, &vector, document, id, &transform);
if !closing_path_on_point {
// Attempt to find nearest segment and close path on segment by creating an anchor point on it
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, transform.transform_point2(self.next_point), tolerance) {
@ -659,8 +651,8 @@ impl PenToolData {
}
}
fn close_path_on_point(&mut self, snap_data: SnapData, vector: &Vector, document: &DocumentMessageHandler, preferences: &PreferencesMessageHandler, id: PointId, transform: &DAffine2) -> bool {
for id in vector.extendable_points(preferences.vector_meshes).filter(|&point| point != id) {
fn close_path_on_point(&mut self, snap_data: SnapData, vector: &Vector, document: &DocumentMessageHandler, id: PointId, transform: &DAffine2) -> bool {
for id in vector.extendable_points().filter(|&point| point != id) {
let Some(pos) = vector.point_domain.position_from_id(id) else { continue };
let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(self.next_point));
let snap_point_tolerance_squared = crate::consts::SNAP_POINT_TOLERANCE.powi(2);
@ -670,7 +662,7 @@ impl PenToolData {
self.handle_end_offset = None;
self.path_closed = true;
self.next_handle_start = self.next_point;
self.store_clicked_endpoint(document, transform, snap_data.input, snap_data.viewport, preferences);
self.store_clicked_endpoint(document, transform, snap_data.input, snap_data.viewport);
self.handle_mode = HandleMode::Free;
if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) {
self.set_lock_angle(vector, prior_endpoint, self.prior_segment);
@ -682,7 +674,7 @@ impl PenToolData {
false
}
fn finish_placing_handle(&mut self, snap_data: SnapData, transform: DAffine2, preferences: &PreferencesMessageHandler, responses: &mut VecDeque<Message>) -> Option<PenToolFsmState> {
fn finish_placing_handle(&mut self, snap_data: SnapData, transform: DAffine2, responses: &mut VecDeque<Message>) -> Option<PenToolFsmState> {
let document = snap_data.document;
let next_handle_start = self.next_handle_start;
let handle_start = self.latest_point()?.handle_start;
@ -693,12 +685,12 @@ impl PenToolData {
let Some(handle_end) = self.handle_end else {
responses.add(DocumentMessage::EndTransaction);
self.handle_end = Some(next_handle_start);
self.place_anchor(snap_data, transform, mouse, preferences, responses);
self.place_anchor(snap_data, transform, mouse, responses);
self.latest_point_mut()?.handle_start = next_handle_start;
return None;
};
let next_point = self.next_point;
self.place_anchor(snap_data, transform, mouse, preferences, responses);
self.place_anchor(snap_data, transform, mouse, responses);
let handles = [handle_start - self.latest_point()?.pos, handle_end - next_point].map(Some);
// Get close path
@ -709,7 +701,7 @@ impl PenToolData {
let vector = document.network_interface.compute_modified_vector(layer)?;
let start = self.latest_point()?.id;
let transform = document.metadata().document_to_viewport * transform;
for id in vector.extendable_points(preferences.vector_meshes).filter(|&point| point != start) {
for id in vector.extendable_points().filter(|&point| point != start) {
let Some(pos) = vector.point_domain.position_from_id(id) else { continue };
let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(next_point));
let snap_point_tolerance_squared = crate::consts::SNAP_POINT_TOLERANCE.powi(2);
@ -1123,7 +1115,7 @@ impl PenToolData {
self.update_target_handle_pos(opposite_handle, self.next_point, responses, new_handle_position, layer);
}
fn place_anchor(&mut self, snap_data: SnapData, transform: DAffine2, mouse: DVec2, preferences: &PreferencesMessageHandler, responses: &mut VecDeque<Message>) -> Option<PenToolFsmState> {
fn place_anchor(&mut self, snap_data: SnapData, transform: DAffine2, mouse: DVec2, responses: &mut VecDeque<Message>) -> Option<PenToolFsmState> {
let document = snap_data.document;
let relative = if self.path_closed { None } else { self.latest_point().map(|point| point.pos) };
@ -1134,7 +1126,7 @@ impl PenToolData {
let layer = selected_layers.next().filter(|_| selected_layers.next().is_none()).or(self.current_layer)?;
let vector = document.network_interface.compute_modified_vector(layer)?;
let transform = document.metadata().document_to_viewport * transform;
for point in vector.extendable_points(preferences.vector_meshes) {
for point in vector.extendable_points() {
let Some(pos) = vector.point_domain.position_from_id(point) else { continue };
let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(self.next_point));
let snap_point_tolerance_squared = crate::consts::SNAP_POINT_TOLERANCE.powi(2);
@ -1230,7 +1222,6 @@ impl PenToolData {
responses: &mut VecDeque<Message>,
tool_options: &PenOptions,
append: bool,
preferences: &PreferencesMessageHandler,
shape_editor: &mut ShapeState,
) {
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
@ -1242,14 +1233,12 @@ impl PenToolData {
self.handle_end = None;
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
let extension_choice = should_extend(document, viewport_vec, tolerance, selected_nodes.selected_layers(document.metadata()), preferences);
let extension_choice = should_extend(document, viewport_vec, tolerance, selected_nodes.selected_layers(document.metadata()));
if let Some((layer, point, position)) = extension_choice {
self.current_layer = Some(layer);
self.extend_existing_path(document, layer, point, position);
return;
} else if preferences.vector_meshes
&& let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance)
{
} else if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance) {
let (point, segments) = closest_segment.adjusted_insert(responses);
let layer = closest_segment.layer();
let position = closest_segment.closest_point_document();
@ -1264,7 +1253,7 @@ impl PenToolData {
}
if append {
if let Some((layer, point, _)) = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences) {
if let Some((layer, point, _)) = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false) {
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
@ -1282,7 +1271,7 @@ impl PenToolData {
}
}
if let Some((layer, point, _position)) = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences) {
if let Some((layer, point, _position)) = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false) {
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
self.handle_mode = HandleMode::Free;
@ -1366,14 +1355,7 @@ impl PenToolData {
}
// Stores the segment and point ID of the clicked endpoint
fn store_clicked_endpoint(
&mut self,
document: &DocumentMessageHandler,
transform: &DAffine2,
input: &InputPreprocessorMessageHandler,
viewport: &ViewportMessageHandler,
preferences: &PreferencesMessageHandler,
) {
fn store_clicked_endpoint(&mut self, document: &DocumentMessageHandler, transform: &DAffine2, input: &InputPreprocessorMessageHandler, viewport: &ViewportMessageHandler) {
let mut manipulators = HashMap::with_hasher(NoHashBuilder);
let mut unselected = Vec::new();
let mut layer_manipulators = HashSet::with_hasher(NoHashBuilder);
@ -1389,7 +1371,7 @@ impl PenToolData {
self.prior_segment_layer = None;
self.prior_segments = None;
if let Some((layer, point, _position)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) {
if let Some((layer, point, _position)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false) {
self.prior_segment_endpoint = Some(point);
self.prior_segment_layer = Some(layer);
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
@ -1468,7 +1450,6 @@ impl Fsm for PenToolFsmState {
global_tool_data,
input,
shape_editor,
preferences,
viewport,
..
} = tool_action_data;
@ -1630,11 +1611,8 @@ impl Fsm for PenToolFsmState {
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
let close_to_point = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
if preferences.vector_meshes
&& !close_to_point
&& let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance)
{
let close_to_point = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false).is_some();
if !close_to_point && let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance) {
let pos = closest_segment.closest_point_to_viewport();
let perp = closest_segment.calculate_perp(document);
overlay_context.manipulator_anchor(pos, true, None);
@ -1689,10 +1667,8 @@ impl Fsm for PenToolFsmState {
}
// If a vector mesh then there can be more than one prior segments
else if let Some(segments) = tool_data.prior_segments.clone() {
if preferences.vector_meshes {
let selected_anchors_data = HashMap::from([(layer, segments)]);
path_overlays(document, DrawHandles::SelectedAnchors(selected_anchors_data), shape_editor, &mut overlay_context);
}
} else {
path_overlays(document, DrawHandles::None, shape_editor, &mut overlay_context);
};
@ -1755,12 +1731,12 @@ impl Fsm for PenToolFsmState {
overlay_context.manipulator_anchor(next_anchor, false, None);
}
if self == PenToolFsmState::PlacingAnchor && preferences.vector_meshes {
if self == PenToolFsmState::PlacingAnchor {
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false).is_some();
if !close_to_point && let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) {
let pos = closest_segment.closest_point_to_viewport();
let perp = closest_segment.calculate_perp(document);
@ -1779,7 +1755,7 @@ impl Fsm for PenToolFsmState {
if let Some(layer) = layer
&& let Some(mut vector) = document.network_interface.compute_modified_vector(layer)
{
let closest_point = vector.extendable_points(preferences.vector_meshes).filter(|&id| id != start).find(|&id| {
let closest_point = vector.extendable_points().filter(|&id| id != start).find(|&id| {
vector.point_domain.position_from_id(id).is_some_and(|pos| {
let dist_sq = transform.transform_point2(pos).distance_squared(transform.transform_point2(next_point));
dist_sq < crate::consts::SNAP_POINT_TOLERANCE.powi(2)
@ -1835,8 +1811,8 @@ impl Fsm for PenToolFsmState {
// Get the closest point and the segment it is on
let append = input.keyboard.key(append_to_selected);
tool_data.store_clicked_endpoint(document, &transform, input, viewport, preferences);
tool_data.create_initial_point(document, input, viewport, responses, tool_options, append, preferences, shape_editor);
tool_data.store_clicked_endpoint(document, &transform, input, viewport);
tool_data.create_initial_point(document, input, viewport, responses, tool_options, append, shape_editor);
// Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
@ -1860,8 +1836,8 @@ impl Fsm for PenToolFsmState {
if let Some(layer) = layer {
tool_data.buffering_merged_vector = false;
tool_data.handle_mode = HandleMode::ColinearLocked;
tool_data.bend_from_previous_point(SnapData::new(document, input, viewport), transform, layer, preferences, shape_editor, responses);
tool_data.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses);
tool_data.bend_from_previous_point(SnapData::new(document, input, viewport), transform, layer, shape_editor, responses);
tool_data.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses);
}
tool_data.buffering_merged_vector = false;
PenToolFsmState::DraggingHandle(tool_data.handle_mode)
@ -1876,7 +1852,7 @@ impl Fsm for PenToolFsmState {
let layers = LayerNodeIdentifier::ROOT_PARENT
.descendants(document.metadata())
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
if let Some((other_layer, _, _)) = should_extend(document, viewport_vec, crate::consts::SNAP_POINT_TOLERANCE, layers, preferences) {
if let Some((other_layer, _, _)) = should_extend(document, viewport_vec, crate::consts::SNAP_POINT_TOLERANCE, layers) {
let selected_nodes = document.network_interface.selected_nodes();
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
if let Some(current_layer) = selected_layers
@ -1906,7 +1882,7 @@ impl Fsm for PenToolFsmState {
(PenToolFsmState::DraggingHandle(_), PenToolMessage::DragStop) => {
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
tool_data
.finish_placing_handle(SnapData::new(document, input, viewport), transform, preferences, responses)
.finish_placing_handle(SnapData::new(document, input, viewport), transform, responses)
.unwrap_or(PenToolFsmState::PlacingAnchor)
}
(
@ -2025,7 +2001,7 @@ impl Fsm for PenToolFsmState {
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
};
let state = tool_data
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses)
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses)
.unwrap_or(PenToolFsmState::Ready);
// Auto-panning
@ -2132,7 +2108,7 @@ impl Fsm for PenToolFsmState {
// Confirm to end path
if let Some((vector, layer)) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)).zip(layer) {
let single_point_in_layer = vector.point_domain.ids().len() == 1;
tool_data.finish_placing_handle(SnapData::new(document, input, viewport), transform, preferences, responses);
tool_data.finish_placing_handle(SnapData::new(document, input, viewport), transform, responses);
let latest_points = tool_data.latest_points.len() == 1;
if latest_points && single_point_in_layer {
@ -2179,7 +2155,7 @@ impl Fsm for PenToolFsmState {
PenToolFsmState::Ready
} else {
tool_data
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses)
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses)
.unwrap_or(PenToolFsmState::Ready)
}
}
@ -2210,7 +2186,7 @@ impl Fsm for PenToolFsmState {
if tool_data.point_index > 0 {
tool_data.point_index -= 1;
tool_data
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses)
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses)
.unwrap_or(PenToolFsmState::PlacingAnchor)
} else {
responses.add(PenToolMessage::Abort);
@ -2219,7 +2195,7 @@ impl Fsm for PenToolFsmState {
}
(_, PenToolMessage::Redo) => {
tool_data.point_index = (tool_data.point_index + 1).min(tool_data.latest_points.len().saturating_sub(1));
tool_data.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, preferences, responses);
tool_data.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses);
match tool_data.point_index {
0 => PenToolFsmState::Ready,
_ => PenToolFsmState::PlacingAnchor,

View File

@ -556,7 +556,6 @@ impl Fsm for ShapeToolFsmState {
document,
global_tool_data,
input,
preferences,
shape_editor,
viewport,
..
@ -762,7 +761,6 @@ impl Fsm for ShapeToolFsmState {
SNAP_POINT_TOLERANCE,
document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface),
|_| false,
preferences,
) && clicked_on_line_endpoints(layer, document, input, tool_data)
&& !input.keyboard.key(Key::Control)
{

View File

@ -294,7 +294,6 @@ impl Fsm for SplineToolFsmState {
global_tool_data,
input,
shape_editor,
preferences,
viewport,
..
} = tool_action_data;
@ -303,7 +302,7 @@ impl Fsm for SplineToolFsmState {
match (self, event) {
(_, SplineToolMessage::CanvasTransformed) => self,
(_, SplineToolMessage::Overlays { context: mut overlay_context }) => {
path_endpoint_overlays(document, shape_editor, &mut overlay_context, preferences);
path_endpoint_overlays(document, shape_editor, &mut overlay_context);
tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
self
}
@ -351,7 +350,7 @@ impl Fsm for SplineToolFsmState {
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
// Extend an endpoint of the selected path
if let Some((layer, point, position)) = should_extend(document, viewport_vec, SNAP_POINT_TOLERANCE, layers, preferences) {
if let Some((layer, point, position)) = should_extend(document, viewport_vec, SNAP_POINT_TOLERANCE, layers) {
if find_spline(document, layer).is_some() {
// If the point is the part of Spline then we extend it.
tool_data.current_layer = Some(layer);
@ -417,7 +416,7 @@ impl Fsm for SplineToolFsmState {
extend_spline(tool_data, false, responses);
tool_data.preview_point = preview_point;
if try_merging_lastest_endpoint(document, tool_data, preferences).is_some() {
if try_merging_lastest_endpoint(document, tool_data).is_some() {
responses.add(SplineToolMessage::Confirm);
}
}
@ -427,7 +426,7 @@ impl Fsm for SplineToolFsmState {
(SplineToolFsmState::Drawing, SplineToolMessage::PointerMove) => {
let Some(layer) = tool_data.current_layer else { return SplineToolFsmState::Ready };
let ignore = |cp: PointId| tool_data.preview_point.is_some_and(|pp| pp == cp) || tool_data.points.last().is_some_and(|(ep, _)| *ep == cp);
let join_point = closest_point(document, input.mouse.position, PATH_JOIN_THRESHOLD, vec![layer].into_iter(), ignore, preferences);
let join_point = closest_point(document, input.mouse.position, PATH_JOIN_THRESHOLD, vec![layer].into_iter(), ignore);
// Endpoints snapping
if let Some((_, _, point)) = join_point {
@ -511,7 +510,7 @@ impl Fsm for SplineToolFsmState {
}
}
fn try_merging_lastest_endpoint(document: &DocumentMessageHandler, tool_data: &mut SplineToolData, preferences: &PreferencesMessageHandler) -> Option<()> {
fn try_merging_lastest_endpoint(document: &DocumentMessageHandler, tool_data: &mut SplineToolData) -> Option<()> {
if tool_data.points.len() < 2 {
return None;
};
@ -526,7 +525,7 @@ fn try_merging_lastest_endpoint(document: &DocumentMessageHandler, tool_data: &m
let exclude = |p: PointId| preview_point.is_some_and(|pp| pp == p) || *last_endpoint == p;
let position = document.metadata().transform_to_viewport(current_layer).transform_point2(*last_endpoint_position);
let (layer, endpoint, _) = closest_point(document, position, PATH_JOIN_THRESHOLD, layers, exclude, preferences)?;
let (layer, endpoint, _) = closest_point(document, position, PATH_JOIN_THRESHOLD, layers, exclude)?;
tool_data.merge_layers.insert(layer);
tool_data.merge_endpoints.push((EndpointPosition::End, endpoint));
@ -647,7 +646,7 @@ mod test_spline_tool {
let layer_to_viewport = document.metadata().transform_to_viewport(spline_layer);
let endpoints: Vec<(PointId, DVec2)> = first_vector
.extendable_points(false)
.extendable_points_no_vector_meshes()
.filter_map(|point_id| first_vector.point_domain.position_from_id(point_id).map(|pos| (point_id, layer_to_viewport.transform_point2(pos))))
.collect();

View File

@ -366,10 +366,20 @@ impl<Upstream> Vector<Upstream> {
/// Points that can be extended from.
///
/// This is usually only points with exactly one connection unless vector meshes are enabled.
pub fn extendable_points(&self, vector_meshes: bool) -> impl Iterator<Item = PointId> + '_ {
let point_ids = self.point_domain.ids().iter().enumerate();
point_ids.filter(move |(index, _)| vector_meshes || self.segment_domain.connected_count(*index) == 1).map(|(_, &id)| id)
/// This may be points with more than one connection because of vector meshes.
pub fn extendable_points(&self) -> impl Iterator<Item = PointId> + '_ {
self.point_domain.ids().iter().copied()
}
// TODO: Avoid needing this special function that's used in only one place. See: <https://github.com/GraphiteEditor/Graphite/commit/6e7f218068a55cc22659ee2cf4f0f2cf26d37774#r173283173>
/// Points that can be extended from.
///
/// This includes only points with exactly one connection because vector meshes are ignored.
pub fn extendable_points_no_vector_meshes(&self) -> impl Iterator<Item = PointId> + '_ {
self.extendable_points()
.enumerate()
.filter(|&(index, _)| self.segment_domain.connected_count(index) == 1)
.map(|(_, id)| id)
}
/// Computes if all the connected handles are colinear for an anchor, or if that handle is colinear for a handle.