Rename the Group type to Graphic everywhere (#3009)

This commit is contained in:
Keavon Chambers
2025-08-05 20:55:15 -07:00
committed by GitHub
parent 309a64340b
commit 0f638314dc
44 changed files with 267 additions and 277 deletions

View File

@@ -122,7 +122,7 @@ impl ClickTarget {
}
// Check if shape is entirely within selection
let any_point_from_subpath = subpath.manipulator_groups().first().map(|group| group.anchor);
let any_point_from_subpath = subpath.manipulator_groups().first().map(|manipulators| manipulators.anchor);
any_point_from_subpath.is_some_and(|shape_point| bezier_iter().map(|bezier| bezier.winding(shape_point)).sum::<i32>() != 0)
}
ClickTargetType::FreePoint(point) => bezier_iter().map(|bezier: bezier_rs::Bezier| bezier.winding(point.position)).sum::<i32>() != 0,

View File

@@ -188,9 +188,9 @@ pub fn bezpath_to_manipulator_groups(bezpath: &BezPath) -> (Vec<ManipulatorGroup
ManipulatorGroup::new(point_to_dvec2(point2), Some(point_to_dvec2(point1)), None)
}
kurbo::PathEl::ClosePath => {
if let Some(last_group) = manipulator_groups.pop() {
if let Some(first_group) = manipulator_groups.first_mut() {
first_group.out_handle = last_group.in_handle;
if let Some(last_manipulators) = manipulator_groups.pop() {
if let Some(first_manipulators) = manipulator_groups.first_mut() {
first_manipulators.out_handle = last_manipulators.in_handle;
}
}
is_closed = true;

View File

@@ -811,13 +811,13 @@ impl Vector {
/// Construct a [`bezier_rs::Bezier`] curve from an iterator of segments with (handles, start point, end point) independently of discontinuities.
pub fn subpath_from_segments_ignore_discontinuities(&self, segments: impl Iterator<Item = (BezierHandles, usize, usize)>) -> Option<bezier_rs::Subpath<PointId>> {
let mut first_point = None;
let mut groups = Vec::new();
let mut manipulators_list = Vec::new();
let mut last: Option<(usize, BezierHandles)> = None;
for (handle, start, end) in segments {
first_point = Some(first_point.unwrap_or(start));
groups.push(ManipulatorGroup {
manipulators_list.push(ManipulatorGroup {
anchor: self.point_domain.positions()[start],
in_handle: last.and_then(|(_, handle)| handle.end()),
out_handle: handle.start(),
@@ -827,13 +827,13 @@ impl Vector {
last = Some((end, handle));
}
let closed = groups.len() > 1 && last.map(|(point, _)| point) == first_point;
let closed = manipulators_list.len() > 1 && last.map(|(point, _)| point) == first_point;
if let Some((end, last_handle)) = last {
if closed {
groups[0].in_handle = last_handle.end();
manipulators_list[0].in_handle = last_handle.end();
} else {
groups.push(ManipulatorGroup {
manipulators_list.push(ManipulatorGroup {
anchor: self.point_domain.positions()[end],
in_handle: last_handle.end(),
out_handle: None,
@@ -842,7 +842,7 @@ impl Vector {
}
}
Some(bezier_rs::Subpath::new(groups, closed))
Some(bezier_rs::Subpath::new(manipulators_list, closed))
}
/// Construct a [`bezier_rs::Bezier`] curve for each region, skipping invalid regions.
@@ -905,7 +905,7 @@ impl Vector {
/// Construct a [`bezier_rs::Bezier`] curve for stroke.
pub fn stroke_bezier_paths(&self) -> impl Iterator<Item = bezier_rs::Subpath<PointId>> {
self.build_stroke_path_iter().map(|(group, closed)| bezier_rs::Subpath::new(group, closed))
self.build_stroke_path_iter().map(|(manipulators_list, closed)| bezier_rs::Subpath::new(manipulators_list, closed))
}
/// Construct and return an iterator of Vec of `(bezier_rs::ManipulatorGroup<PointId>], bool)` for stroke.
@@ -916,15 +916,15 @@ impl Vector {
/// Construct a [`kurbo::BezPath`] curve for stroke.
pub fn stroke_bezpath_iter(&self) -> impl Iterator<Item = kurbo::BezPath> {
self.build_stroke_path_iter().map(|(group, closed)| {
self.build_stroke_path_iter().map(|(manipulators_list, closed)| {
let mut bezpath = kurbo::BezPath::new();
let mut out_handle;
let Some(first) = group.first() else { return bezpath };
let Some(first) = manipulators_list.first() else { return bezpath };
bezpath.move_to(dvec2_to_point(first.anchor));
out_handle = first.out_handle;
for manipulator in group.iter().skip(1) {
for manipulator in manipulators_list.iter().skip(1) {
match (out_handle, manipulator.in_handle) {
(Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(manipulator.anchor)),
(None, None) => bezpath.line_to(dvec2_to_point(manipulator.anchor)),
@@ -954,7 +954,7 @@ impl Vector {
pub fn manipulator_group_id(&self, id: impl Into<PointId>) -> Option<ManipulatorGroup<PointId>> {
let id = id.into();
self.manipulator_groups().find(|group| group.id == id)
self.manipulator_groups().find(|manipulators| manipulators.id == id)
}
pub fn transform(&mut self, transform: DAffine2) {
@@ -1047,13 +1047,13 @@ impl Iterator for StrokePathIter<'_> {
// There will always be one (seeing as we checked above)
let mut point_index = current_start;
let mut groups = Vec::new();
let mut manipulators_list = Vec::new();
let mut in_handle = None;
let mut closed = false;
loop {
let Some(val) = self.points[point_index].take_first() else {
// Dead end
groups.push(ManipulatorGroup {
manipulators_list.push(ManipulatorGroup {
anchor: self.vector.point_domain.positions()[point_index],
in_handle,
out_handle: None,
@@ -1072,7 +1072,7 @@ impl Iterator for StrokePathIter<'_> {
} else {
self.vector.segment_domain.end_point()[val.segment_index]
};
groups.push(ManipulatorGroup {
manipulators_list.push(ManipulatorGroup {
anchor: self.vector.point_domain.positions()[point_index],
in_handle,
out_handle: handles.start(),
@@ -1085,12 +1085,12 @@ impl Iterator for StrokePathIter<'_> {
self.points[next_point_index].take_eq(val.flipped());
if next_point_index == current_start {
closed = true;
groups[0].in_handle = in_handle;
manipulators_list[0].in_handle = in_handle;
break;
}
}
Some((groups, closed))
Some((manipulators_list, closed))
}
}

View File

@@ -50,7 +50,7 @@ async fn assign_colors<T>(
_: impl Ctx,
#[implementations(Table<Graphic>, Table<Vector>)]
#[widget(ParsedWidgetOverride::Hidden)]
/// The vector elements, or group of vector elements, to apply the fill and/or stroke style to.
/// The content with vector paths to apply the fill and/or stroke style to.
mut content: T,
#[default(true)]
/// Whether to style the fill.
@@ -118,7 +118,7 @@ async fn fill<F: Into<Fill> + 'n + Send, V>(
Table<Graphic>,
Table<Graphic>
)]
/// The vector elements, or group of vector elements, to apply the fill to.
/// The content with vector paths to apply the fill style to.
mut content: V,
#[implementations(
Fill,
@@ -156,7 +156,7 @@ where
async fn stroke<C: Into<Option<Color>> + 'n + Send, V>(
_: impl Ctx,
#[implementations(Table<Vector>, Table<Vector>, Table<Graphic>, Table<Graphic>)]
/// The vector elements, or group of vector elements, to apply the stroke to.
/// The content with vector paths to apply the stroke style to.
mut content: Table<V>,
#[implementations(
Option<Color>,
@@ -447,7 +447,7 @@ async fn round_corners(
let source_node_id = source.source_node_id;
let source = source.element;
let upstream_group = source.upstream_group.clone();
let upstream_nested_layers = source.upstream_nested_layers.clone();
// Flip the roundness to help with user intuition
let roundness = 1. - roundness;
@@ -532,7 +532,7 @@ async fn round_corners(
result.append_bezpath(rounded_subpath);
}
result.upstream_group = upstream_group;
result.upstream_nested_layers = upstream_nested_layers;
TableRow {
element: result,
@@ -687,18 +687,18 @@ async fn auto_tangents(
for mut subpath in source.stroke_bezier_paths() {
subpath.apply_transform(transform);
let groups = subpath.manipulator_groups();
if groups.len() < 2 {
let manipulators_list = subpath.manipulator_groups();
if manipulators_list.len() < 2 {
// Not enough points for softening or handle removal
result.append_subpath(subpath, true);
continue;
}
let mut new_groups = Vec::with_capacity(groups.len());
let mut new_manipulators_list = Vec::with_capacity(manipulators_list.len());
let is_closed = subpath.closed();
for i in 0..groups.len() {
let curr = &groups[i];
for i in 0..manipulators_list.len() {
let curr = &manipulators_list[i];
if preserve_existing {
// Check if this point has handles that are meaningfully different from the anchor
@@ -706,15 +706,15 @@ async fn auto_tangents(
|| (curr.out_handle.is_some() && !curr.out_handle.unwrap().abs_diff_eq(curr.anchor, 1e-5));
// If the point already has handles, or if it's an endpoint of an open path, keep it as is.
if has_handles || (!is_closed && (i == 0 || i == groups.len() - 1)) {
new_groups.push(*curr);
if has_handles || (!is_closed && (i == 0 || i == manipulators_list.len() - 1)) {
new_manipulators_list.push(*curr);
continue;
}
}
// If spread is 0, remove handles for this point, making it a sharp corner.
if spread == 0. {
new_groups.push(ManipulatorGroup {
new_manipulators_list.push(ManipulatorGroup {
anchor: curr.anchor,
in_handle: None,
out_handle: None,
@@ -724,12 +724,12 @@ async fn auto_tangents(
}
// Get previous and next points for auto-tangent calculation
let prev_idx = if i == 0 { if is_closed { groups.len() - 1 } else { i } } else { i - 1 };
let next_idx = if i == groups.len() - 1 { if is_closed { 0 } else { i } } else { i + 1 };
let prev_idx = if i == 0 { if is_closed { manipulators_list.len() - 1 } else { i } } else { i - 1 };
let next_idx = if i == manipulators_list.len() - 1 { if is_closed { 0 } else { i } } else { i + 1 };
let prev = groups[prev_idx].anchor;
let prev = manipulators_list[prev_idx].anchor;
let curr_pos = curr.anchor;
let next = groups[next_idx].anchor;
let next = manipulators_list[next_idx].anchor;
// Calculate directions from current point to adjacent points
let dir_prev = (prev - curr_pos).normalize_or_zero();
@@ -738,7 +738,7 @@ async fn auto_tangents(
// Check if we have valid directions (e.g., points are not coincident)
if dir_prev.length_squared() < 1e-5 || dir_next.length_squared() < 1e-5 {
// Fallback: keep the original manipulator group (which has no active handles here)
new_groups.push(*curr);
new_manipulators_list.push(*curr);
continue;
}
@@ -758,7 +758,7 @@ async fn auto_tangents(
let out_length = (next - curr_pos).length() / 3. * spread;
// Create new manipulator group with calculated auto-tangents
new_groups.push(ManipulatorGroup {
new_manipulators_list.push(ManipulatorGroup {
anchor: curr_pos,
in_handle: Some(curr_pos + handle_dir * in_length),
out_handle: Some(curr_pos - handle_dir * out_length),
@@ -766,7 +766,7 @@ async fn auto_tangents(
});
}
let mut softened_bezpath = bezpath_from_manipulator_groups(&new_groups, is_closed);
let mut softened_bezpath = bezpath_from_manipulator_groups(&new_manipulators_list, is_closed);
softened_bezpath.apply_affine(Affine::new(transform.inverse().to_cols_array()));
result.append_bezpath(softened_bezpath);
}
@@ -985,7 +985,7 @@ where
output.element.style = row.element.style.clone();
}
}
Graphic::Group(graphic) => {
Graphic::Graphic(graphic) => {
let mut graphic = graphic.clone();
for row in graphic.iter_mut() {
*row.transform = *current_element.transform * *row.transform;
@@ -1032,7 +1032,7 @@ async fn sample_polyline(
region_domain: Default::default(),
colinear_manipulators: Default::default(),
style: std::mem::take(&mut row.element.style),
upstream_group: std::mem::take(&mut row.element.upstream_group),
upstream_nested_layers: std::mem::take(&mut row.element.upstream_nested_layers),
};
// Transfer the stroke transform from the input vector content to the result.
result.style.set_stroke_transform(row.transform);
@@ -1343,7 +1343,7 @@ async fn spline(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
let mut segment_domain = SegmentDomain::default();
for (manipulator_groups, closed) in row.element.stroke_manipulator_groups() {
let positions = manipulator_groups.iter().map(|group| group.anchor).collect::<Vec<_>>();
let positions = manipulator_groups.iter().map(|manipulators| manipulators.anchor).collect::<Vec<_>>();
let closed = closed && positions.len() > 2;
// Compute control point handles for Bezier spline.
@@ -2075,7 +2075,14 @@ mod test {
let bounding_box = super::bounding_box((), vector_node_from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY))).await;
let bounding_box = bounding_box.iter().next().unwrap().element;
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
let manipulator_groups_anchors = bounding_box.region_manipulator_groups().next().unwrap().1.iter().map(|group| group.anchor).collect::<Vec<DVec2>>();
let manipulator_groups_anchors = bounding_box
.region_manipulator_groups()
.next()
.unwrap()
.1
.iter()
.map(|manipulators| manipulators.anchor)
.collect::<Vec<DVec2>>();
assert_eq!(&manipulator_groups_anchors[..4], &[DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.),]);
@@ -2086,7 +2093,14 @@ mod test {
let bounding_box = BoundingBoxNode { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
let bounding_box = bounding_box.iter().next().unwrap().element;
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
let manipulator_groups_anchors = bounding_box.region_manipulator_groups().next().unwrap().1.iter().map(|group| group.anchor).collect::<Vec<DVec2>>();
let manipulator_groups_anchors = bounding_box
.region_manipulator_groups()
.next()
.unwrap()
.1
.iter()
.map(|manipulators| manipulators.anchor)
.collect::<Vec<DVec2>>();
let expected_bounding_box = [DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.)];
for i in 0..4 {
@@ -2108,7 +2122,7 @@ mod test {
for (index, (_, manipulator_groups)) in flattened_copy_to_points.region_manipulator_groups().enumerate() {
let offset = expected_points[index];
let manipulator_groups_anchors = manipulator_groups.iter().map(|group| group.anchor).collect::<Vec<DVec2>>();
let manipulator_groups_anchors = manipulator_groups.iter().map(|manipulators| manipulators.anchor).collect::<Vec<DVec2>>();
assert_eq!(
&manipulator_groups_anchors,
&[offset + DVec2::NEG_ONE, offset + DVec2::new(1., -1.), offset + DVec2::ONE, offset + DVec2::new(-1., 1.),]

View File

@@ -29,8 +29,10 @@ pub struct Vector {
pub segment_domain: SegmentDomain,
pub region_domain: RegionDomain,
// Used to store the upstream group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
pub upstream_group: Option<Table<Graphic>>,
/// Used to store the upstream group/folder of nested layers during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved for the child layers.
/// Without this, the tools would be working with a collapsed version of the data which has no reference to the original child layers that were booleaned together, resulting in the inner layers not being editable.
#[serde(alias = "upstream_group")]
pub upstream_nested_layers: Option<Table<Graphic>>,
}
impl Default for Vector {
@@ -41,7 +43,7 @@ impl Default for Vector {
point_domain: PointDomain::new(),
segment_domain: SegmentDomain::new(),
region_domain: RegionDomain::new(),
upstream_group: None,
upstream_nested_layers: None,
}
}
}
@@ -468,15 +470,12 @@ pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resu
pub style: PathStyle,
/// A list of all manipulator groups (referenced in `subpaths`) that have colinear handles (where they're locked at 180° angles from one another).
/// This gets read in `graph_operation_message_handler.rs` by calling `inputs.as_mut_slice()` (search for the string `"Shape does not have both `subpath` and `colinear_manipulators` inputs"` to find it).
pub colinear_manipulators: Vec<[HandleId; 2]>,
pub point_domain: PointDomain,
pub segment_domain: SegmentDomain,
pub region_domain: RegionDomain,
// Used to store the upstream group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
pub upstream_graphic_group: Option<Table<Graphic>>,
}
@@ -498,7 +497,7 @@ pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resu
point_domain: old.point_domain,
segment_domain: old.segment_domain,
region_domain: old.region_domain,
upstream_group: old.upstream_graphic_group,
upstream_nested_layers: old.upstream_graphic_group,
});
*vector_table.iter_mut().next().unwrap().transform = old.transform;
*vector_table.iter_mut().next().unwrap().alpha_blending = old.alpha_blending;