mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 21:08:12 +08:00
Add molding segments to the Path tool (#2660)
* Moulding of cubic bezier * Implemented falloff with interpolation * remove conflict * Move falloff to consts * Spelling * Refine falloff param and bug fix * Code review * Add colinear disable modes to molding degment feat * Clean comments and unused code * Code refactor * Fix error * Change colinear toggle behaviour * Code review * Remove KeyC feat + Fix overlay * Dynamic hints in path tool * Revamp molding logic * Code review * Remove unused Bezier algorithms (maybe useful for future reference) --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
4696004dc9
commit
f72263f4f8
@@ -1,6 +1,6 @@
|
||||
use super::graph_modification_utils::merge_layers;
|
||||
use super::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint};
|
||||
use super::utility_functions::calculate_segment_angle;
|
||||
use super::utility_functions::{adjust_handle_colinearity, calculate_segment_angle, restore_g1_continuity, restore_previous_handle_position};
|
||||
use crate::consts::HANDLE_LENGTH_FACTOR;
|
||||
use crate::messages::portfolio::document::overlays::utility_functions::selected_segments;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
@@ -282,6 +282,71 @@ impl ClosestSegment {
|
||||
.unwrap_or(DVec2::ZERO);
|
||||
tangent.perp()
|
||||
}
|
||||
|
||||
/// Molding the bezier curve.
|
||||
/// Returns adjacent handles' [`HandleId`] if colinearity is broken temporarily.
|
||||
pub fn mold_handle_positions(
|
||||
&self,
|
||||
document: &DocumentMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
(c1, c2): (DVec2, DVec2),
|
||||
new_b: DVec2,
|
||||
break_colinear_molding: bool,
|
||||
temporary_adjacent_handles_while_molding: Option<[Option<HandleId>; 2]>,
|
||||
) -> Option<[Option<HandleId>; 2]> {
|
||||
let transform = document.metadata().transform_to_viewport(self.layer);
|
||||
|
||||
let start = self.bezier.start;
|
||||
let end = self.bezier.end;
|
||||
|
||||
// Apply the drag delta to the segment's handles
|
||||
let b = self.bezier_point_to_viewport;
|
||||
let delta = transform.inverse().transform_vector2(new_b - b);
|
||||
let (nc1, nc2) = (c1 + delta, c2 + delta);
|
||||
|
||||
let handle1 = HandleId::primary(self.segment);
|
||||
let handle2 = HandleId::end(self.segment);
|
||||
let layer = self.layer;
|
||||
|
||||
let modification_type = handle1.set_relative_position(nc1 - start);
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
|
||||
let modification_type = handle2.set_relative_position(nc2 - end);
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
|
||||
// If adjacent segments have colinear handles, their direction is changed but their handle lengths is preserved
|
||||
// TODO: Find something which is more appropriate
|
||||
let vector_data = document.network_interface.compute_modified_vector(self.layer())?;
|
||||
|
||||
if break_colinear_molding {
|
||||
// Disable G1 continuity
|
||||
let other_handles = [
|
||||
restore_previous_handle_position(handle1, c1, start, &vector_data, layer, responses),
|
||||
restore_previous_handle_position(handle2, c2, end, &vector_data, layer, responses),
|
||||
];
|
||||
|
||||
// Store other HandleId in tool data to regain colinearity later
|
||||
if temporary_adjacent_handles_while_molding.is_some() {
|
||||
temporary_adjacent_handles_while_molding
|
||||
} else {
|
||||
Some(other_handles)
|
||||
}
|
||||
} else {
|
||||
// Move the colinear handles so that colinearity is maintained
|
||||
adjust_handle_colinearity(handle1, start, nc1, &vector_data, layer, responses);
|
||||
adjust_handle_colinearity(handle2, end, nc2, &vector_data, layer, responses);
|
||||
|
||||
if let Some(adjacent_handles) = temporary_adjacent_handles_while_molding {
|
||||
if let Some(other_handle1) = adjacent_handles[0] {
|
||||
restore_g1_continuity(handle1, other_handle1, nc1, start, &vector_data, layer, responses);
|
||||
}
|
||||
if let Some(other_handle2) = adjacent_handles[1] {
|
||||
restore_g1_continuity(handle2, other_handle2, nc2, end, &vector_data, layer, responses);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Consider keeping a list of selected manipulators to minimize traversals of the layers
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::messages::tool::tool_messages::path_tool::PathOverlayMode;
|
||||
use glam::DVec2;
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::text::{FontCache, load_face};
|
||||
use graphene_std::vector::{ManipulatorPointId, PointId, SegmentId, VectorData};
|
||||
use graphene_std::vector::{HandleId, ManipulatorPointId, PointId, SegmentId, VectorData, VectorModificationType};
|
||||
|
||||
/// 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(
|
||||
@@ -95,6 +95,65 @@ pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data:
|
||||
required_handle.map(|handle| -(handle - anchor_position).angle_to(DVec2::X))
|
||||
}
|
||||
|
||||
pub fn adjust_handle_colinearity(handle: HandleId, anchor_position: DVec2, target_control_point: DVec2, vector_data: &VectorData, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
|
||||
let Some(other_handle) = vector_data.other_colinear_handle(handle) else { return };
|
||||
let Some(handle_position) = other_handle.to_manipulator_point().get_position(vector_data) else {
|
||||
return;
|
||||
};
|
||||
let Some(direction) = (anchor_position - target_control_point).try_normalize() else { return };
|
||||
|
||||
let new_relative_position = (handle_position - anchor_position).length() * direction;
|
||||
let modification_type = other_handle.set_relative_position(new_relative_position);
|
||||
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
}
|
||||
|
||||
pub fn restore_previous_handle_position(
|
||||
handle: HandleId,
|
||||
original_c: DVec2,
|
||||
anchor_position: DVec2,
|
||||
vector_data: &VectorData,
|
||||
layer: LayerNodeIdentifier,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Option<HandleId> {
|
||||
let other_handle = vector_data.other_colinear_handle(handle)?;
|
||||
let handle_position = other_handle.to_manipulator_point().get_position(vector_data)?;
|
||||
let direction = (anchor_position - original_c).try_normalize()?;
|
||||
|
||||
let old_relative_position = (handle_position - anchor_position).length() * direction;
|
||||
let modification_type = other_handle.set_relative_position(old_relative_position);
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
|
||||
let handles = [handle, other_handle];
|
||||
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
|
||||
Some(other_handle)
|
||||
}
|
||||
|
||||
pub fn restore_g1_continuity(
|
||||
handle: HandleId,
|
||||
other_handle: HandleId,
|
||||
control_point: DVec2,
|
||||
anchor_position: DVec2,
|
||||
vector_data: &VectorData,
|
||||
layer: LayerNodeIdentifier,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) {
|
||||
let Some(handle_position) = other_handle.to_manipulator_point().get_position(vector_data) else {
|
||||
return;
|
||||
};
|
||||
let Some(direction) = (anchor_position - control_point).try_normalize() else { return };
|
||||
|
||||
let new_relative_position = (handle_position - anchor_position).length() * direction;
|
||||
let modification_type = other_handle.set_relative_position(new_relative_position);
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
|
||||
let handles = [handle, other_handle];
|
||||
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: true };
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
}
|
||||
|
||||
/// Check whether a point is visible in the current overlay mode.
|
||||
pub fn is_visible_point(
|
||||
manipulator_point_id: ManipulatorPointId,
|
||||
|
||||
Reference in New Issue
Block a user