MCPcopy Create free account
hub / github.com/GraphiteEditor/Graphite / auto_tangents

Function auto_tangents

node-graph/nodes/vector/src/vector_nodes.rs:994–1145  ·  view source on GitHub ↗
(
	_: impl Ctx,
	source: List<Vector>,
	/// The amount of spread for the auto-tangents, from 0 (sharp corner) to 1 (full spread).
	#[default(0.5)]
	#[range]
	#[soft(0..1)]
	spread: f64,
	/// If active

Source from the content-addressed store, hash-verified

992/// Automatically constructs tangents (Bézier handles) for anchor points in a vector path.
993#[node_macro::node(category("Vector: Modifier"), name("Auto-Tangents"), path(core_types::vector))]
994async fn auto_tangents(
995 _: impl Ctx,
996 source: List<Vector>,
997 /// The amount of spread for the auto-tangents, from 0 (sharp corner) to 1 (full spread).
998 #[default(0.5)]
999 #[range]
1000 #[soft(0..1)]
1001 spread: f64,
1002 /// If active, existing non-zero handles won't be affected.
1003 #[default(true)]
1004 preserve_existing: bool,
1005) -> List<Vector> {
1006 (0..source.len())
1007 .map(|index| {
1008 let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
1009 let attributes = source.clone_item_attributes(index);
1010 let source = source.element(index).unwrap();
1011
1012 let mut result = Vector {
1013 stroke: source.stroke.clone(),
1014 ..Default::default()
1015 };
1016
1017 for mut subpath in source.stroke_bezier_paths() {
1018 subpath.apply_transform(transform);
1019
1020 let manipulators_list = subpath.manipulator_groups();
1021 if manipulators_list.len() < 2 {
1022 // Not enough points for softening or handle removal
1023 result.append_subpath(subpath, true);
1024 continue;
1025 }
1026
1027 let mut new_manipulators_list = Vec::with_capacity(manipulators_list.len());
1028 // Track which manipulator indices were given auto-tangent (colinear) handles
1029 let mut auto_tangented = vec![false; manipulators_list.len()];
1030 let is_closed = subpath.closed();
1031
1032 for i in 0..manipulators_list.len() {
1033 let current = &manipulators_list[i];
1034 let is_endpoint = !is_closed && (i == 0 || i == manipulators_list.len() - 1);
1035
1036 if preserve_existing {
1037 // Check if this point has handles that are meaningfully different from the anchor
1038 let has_handles = (current.in_handle.is_some() && !current.in_handle.unwrap().abs_diff_eq(current.anchor, 1e-5))
1039 || (current.out_handle.is_some() && !current.out_handle.unwrap().abs_diff_eq(current.anchor, 1e-5));
1040
1041 // If the point already has handles, keep it as is
1042 if has_handles {
1043 new_manipulators_list.push(*current);
1044 continue;
1045 }
1046 }
1047
1048 // If spread is 0, remove handles for this point, making it a sharp corner
1049 if spread == 0. {
1050 new_manipulators_list.push(ManipulatorGroup {
1051 anchor: current.anchor,

Callers

nothing calls this directly

Calls 15

primaryFunction · 0.85
clone_item_attributesMethod · 0.80
elementMethod · 0.80
stroke_bezier_pathsMethod · 0.80
append_subpathMethod · 0.80
closedMethod · 0.80
is_someMethod · 0.80
lengthMethod · 0.80
append_bezpathMethod · 0.80
lenMethod · 0.45

Tested by

no test coverage detected