Delete the dead StrokeId and FillId attributes from the Vector segment and region domains (#4461)

This commit is contained in:
Keavon Chambers
2026-08-18 17:51:29 -07:00
committed by GitHub
parent 48e776c1af
commit 39425ca7ea
10 changed files with 49 additions and 119 deletions

View File

@@ -197,7 +197,6 @@ impl MergeByDistanceExt for Vector {
let start = self.segment_domain.start_point()[segment_idx];
let end = self.segment_domain.end_point()[segment_idx];
let handles = self.segment_domain.handles()[segment_idx];
let stroke = self.segment_domain.stroke()[segment_idx];
// Get new indices for start and end points
let new_start = point_index_map[start].unwrap();
@@ -205,7 +204,7 @@ impl MergeByDistanceExt for Vector {
// Skip segments where start and end points were merged
if new_start != new_end {
new_segment_domain.push(id, new_start, new_end, handles, stroke);
new_segment_domain.push(id, new_start, new_end, handles);
}
}

View File

@@ -48,7 +48,7 @@ macro_rules! create_ids {
};
}
create_ids! { PointId, SegmentId, RegionId, StrokeId, FillId }
create_ids! { PointId, SegmentId, RegionId }
/// A no-op hasher that allows writing u64s (the id type).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
@@ -202,14 +202,13 @@ impl PointDomain {
#[derive(Clone, Debug, Default, PartialEq, graphene_hash::CacheHash, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Stores data which is per-segment. A segment is a bézier curve between two end points with a stroke. In future this will be extendable at runtime with custom attributes.
/// Stores data which is per-segment. A segment is a bézier curve between two end points. In future this will be extendable at runtime with custom attributes.
pub struct SegmentDomain {
#[cfg_attr(feature = "serde", serde(alias = "ids"))]
id: Vec<SegmentId>,
start_point: Vec<usize>,
end_point: Vec<usize>,
handles: Vec<BezierHandles>,
stroke: Vec<StrokeId>,
}
impl SegmentDomain {
@@ -219,7 +218,6 @@ impl SegmentDomain {
start_point: Vec::new(),
end_point: Vec::new(),
handles: Vec::new(),
stroke: Vec::new(),
}
}
@@ -229,7 +227,6 @@ impl SegmentDomain {
self.start_point.reserve(additional);
self.end_point.reserve(additional);
self.handles.reserve(additional);
self.stroke.reserve(additional);
}
pub(crate) fn retain(&mut self, f: impl Fn(&SegmentId) -> bool, points_length: usize) {
@@ -261,8 +258,6 @@ impl SegmentDomain {
self.end_point.retain(|_| keep.next().unwrap_or_default());
let mut keep = self.id.iter().map(can_delete());
self.handles.retain(|_| keep.next().unwrap_or_default());
let mut keep = self.id.iter().map(can_delete());
self.stroke.retain(|_| keep.next().unwrap_or_default());
let mut delete_iter = additional_delete_ids.iter().peekable();
self.id.retain(move |id| {
@@ -307,27 +302,22 @@ impl SegmentDomain {
&self.handles
}
pub fn stroke(&self) -> &[StrokeId] {
&self.stroke
}
pub fn push(&mut self, id: SegmentId, start: usize, end: usize, handles: BezierHandles, stroke: StrokeId) {
pub fn push(&mut self, id: SegmentId, start: usize, end: usize, handles: BezierHandles) {
#[cfg(debug_assertions)]
if self.id.contains(&id) {
warn!("Tried to push a duplicate segment to a segment domain");
return;
}
self.push_unchecked(id, start, end, handles, stroke);
self.push_unchecked(id, start, end, handles);
}
#[inline(always)]
pub fn push_unchecked(&mut self, id: SegmentId, start: usize, end: usize, handles: BezierHandles, stroke: StrokeId) {
pub fn push_unchecked(&mut self, id: SegmentId, start: usize, end: usize, handles: BezierHandles) {
self.id.push(id);
self.start_point.push(start);
self.end_point.push(end);
self.handles.push(handles);
self.stroke.push(stroke);
}
pub(crate) fn start_point_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut usize)> {
@@ -348,10 +338,6 @@ impl SegmentDomain {
nested.map(|((a, b), c)| (a, b, c))
}
pub(crate) fn stroke_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut StrokeId)> {
self.id.iter().copied().zip(self.stroke.iter_mut())
}
pub(crate) fn segment_start_from_id(&self, segment: SegmentId) -> Option<usize> {
self.id_to_index(segment).and_then(|index| self.start_point.get(index)).copied()
}
@@ -392,7 +378,6 @@ impl SegmentDomain {
self.start_point.extend(other.start_point.iter().map(|&index| id_map.point_offset + index));
self.end_point.extend(other.end_point.iter().map(|&index| id_map.point_offset + index));
self.handles.extend(other.handles.iter().map(|handles| handles.apply_transformation(|p| transform.transform_point2(p))));
self.stroke.extend(&other.stroke);
}
pub(crate) fn map_ids(&mut self, id_map: &IdMap) {
@@ -568,12 +553,11 @@ impl SegmentDomain {
#[derive(Clone, Debug, Default, PartialEq, Hash, graphene_hash::CacheHash, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Stores data which is per-region. A region is an enclosed area composed of a range of segments from the
/// [`SegmentDomain`] that can be given a fill. In future this will be extendable at runtime with custom attributes.
/// [`SegmentDomain`]. In future this will be extendable at runtime with custom attributes.
pub struct RegionDomain {
#[cfg_attr(feature = "serde", serde(alias = "ids"))]
id: Vec<RegionId>,
segment_range: Vec<std::ops::RangeInclusive<SegmentId>>,
fill: Vec<FillId>,
}
impl RegionDomain {
@@ -581,7 +565,6 @@ impl RegionDomain {
Self {
id: Vec::new(),
segment_range: Vec::new(),
fill: Vec::new(),
}
}
@@ -589,14 +572,11 @@ impl RegionDomain {
pub fn reserve(&mut self, additional: usize) {
self.id.reserve(additional);
self.segment_range.reserve(additional);
self.fill.reserve(additional);
}
pub(crate) fn retain(&mut self, f: impl Fn(&RegionId) -> bool) {
let mut keep = self.id.iter().map(&f);
self.segment_range.retain(|_| keep.next().unwrap_or_default());
let mut keep = self.id.iter().map(&f);
self.fill.retain(|_| keep.next().unwrap_or_default());
self.id.retain(&f);
}
@@ -608,26 +588,23 @@ impl RegionDomain {
let mut iter = keep.iter().copied();
self.segment_range.retain(|_| iter.next().unwrap());
let mut iter = keep.iter().copied();
self.fill.retain(|_| iter.next().unwrap());
let mut iter = keep.iter().copied();
self.id.retain(|_| iter.next().unwrap());
}
pub fn push(&mut self, id: RegionId, segment_range: std::ops::RangeInclusive<SegmentId>, fill: FillId) {
pub fn push(&mut self, id: RegionId, segment_range: std::ops::RangeInclusive<SegmentId>) {
#[cfg(debug_assertions)]
if self.id.contains(&id) {
warn!("Tried to push a duplicate region to a region domain");
return;
}
self.push_unchecked(id, segment_range, fill);
self.push_unchecked(id, segment_range);
}
#[inline(always)]
pub fn push_unchecked(&mut self, id: RegionId, segment_range: std::ops::RangeInclusive<SegmentId>, fill: FillId) {
pub fn push_unchecked(&mut self, id: RegionId, segment_range: std::ops::RangeInclusive<SegmentId>) {
self.id.push(id);
self.segment_range.push(segment_range);
self.fill.push(fill);
}
fn _resolve_id(&self, id: RegionId) -> Option<usize> {
@@ -642,10 +619,6 @@ impl RegionDomain {
self.id.iter().copied().zip(self.segment_range.iter_mut())
}
pub(crate) fn fill_mut(&mut self) -> impl Iterator<Item = (RegionId, &mut FillId)> {
self.id.iter().copied().zip(self.fill.iter_mut())
}
pub fn ids(&self) -> &[RegionId] {
&self.id
}
@@ -654,10 +627,6 @@ impl RegionDomain {
&self.segment_range
}
pub fn fill(&self) -> &[FillId] {
&self.fill
}
pub(crate) fn concat(&mut self, other: &Self, _transform: DAffine2, id_map: &IdMap) {
self.id.extend(other.id.iter().map(|id| *id_map.region_map.get(id).unwrap_or(id)));
self.segment_range.extend(
@@ -666,7 +635,6 @@ impl RegionDomain {
.iter()
.map(|range| *id_map.segment_map.get(range.start()).unwrap_or(range.start())..=*id_map.segment_map.get(range.end()).unwrap_or(range.end())),
);
self.fill.extend(&other.fill);
}
pub(crate) fn map_ids(&mut self, id_map: &IdMap) {
@@ -678,12 +646,11 @@ impl RegionDomain {
/// Iterates over regions in the domain.
///
/// Tuple is: (id, segment_range, fill)
pub fn iter(&self) -> impl Iterator<Item = (RegionId, std::ops::RangeInclusive<SegmentId>, FillId)> + '_ {
/// Tuple is: (id, segment_range)
pub fn iter(&self) -> impl Iterator<Item = (RegionId, std::ops::RangeInclusive<SegmentId>)> + '_ {
let ids = self.id.iter().copied();
let segment_range = self.segment_range.iter().cloned();
let fill = self.fill.iter().copied();
zip(ids, zip(segment_range, fill)).map(|(id, (segment_range, fill))| (id, segment_range, fill))
zip(ids, segment_range)
}
}

View File

@@ -95,8 +95,6 @@ pub(crate) struct SegmentModification {
handle_primary: HashMap<SegmentId, Option<DVec2>>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_hashmap", deserialize_with = "deserialize_hashmap"))]
handle_end: HashMap<SegmentId, Option<DVec2>>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_hashmap", deserialize_with = "deserialize_hashmap"))]
stroke: HashMap<SegmentId, StrokeId>,
}
impl SegmentModification {
@@ -167,17 +165,11 @@ impl SegmentModification {
};
}
for (id, stroke) in segment_domain.stroke_mut() {
let Some(&new) = self.stroke.get(&id) else { continue };
*stroke = new;
}
for &add_id in &self.add {
let Some(&start) = self.start_point.get(&add_id) else { continue };
let Some(&end) = self.end_point.get(&add_id) else { continue };
let Some(&handle_start) = self.handle_primary.get(&add_id) else { continue };
let Some(&handle_end) = self.handle_end.get(&add_id) else { continue };
let Some(&stroke) = self.stroke.get(&add_id) else { continue };
let Some(start_index) = point_domain.resolve_id(start) else {
warn!("invalid start id: {start:#?}");
@@ -204,7 +196,7 @@ impl SegmentModification {
continue;
}
segment_domain.push(add_id, start_index, end_index, handles, stroke);
segment_domain.push(add_id, start_index, end_index, handles);
}
assert!(
@@ -233,18 +225,16 @@ impl SegmentModification {
.segment_iter()
.map(|(id, segment, _, _)| (id, segment_to_handles(&segment).end().map(|handle| handle - point_to_dvec2(segment.end()))))
.collect(),
stroke: vector.segment_domain.ids().iter().copied().zip(vector.segment_domain.stroke().iter().cloned()).collect(),
}
}
fn push(&mut self, id: SegmentId, points: [PointId; 2], handles: [Option<DVec2>; 2], stroke: StrokeId) {
fn push(&mut self, id: SegmentId, points: [PointId; 2], handles: [Option<DVec2>; 2]) {
self.remove.remove(&id);
self.add.push(id);
self.start_point.insert(id, points[0]);
self.end_point.insert(id, points[1]);
self.handle_primary.insert(id, handles[0]);
self.handle_end.insert(id, handles[1]);
self.stroke.insert(id, stroke);
}
fn remove(&mut self, id: SegmentId) {
@@ -254,7 +244,6 @@ impl SegmentModification {
self.end_point.remove(&id);
self.handle_primary.remove(&id);
self.handle_end.remove(&id);
self.stroke.remove(&id);
}
}
@@ -267,8 +256,6 @@ pub(crate) struct RegionModification {
remove: HashSet<RegionId>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_hashmap", deserialize_with = "deserialize_hashmap"))]
segment_range: HashMap<RegionId, std::ops::RangeInclusive<SegmentId>>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_hashmap", deserialize_with = "deserialize_hashmap"))]
fill: HashMap<RegionId, FillId>,
}
impl RegionModification {
@@ -281,15 +268,9 @@ impl RegionModification {
*segment_range = new.clone(); // Range inclusive is not copy
}
for (id, fill) in region_domain.fill_mut() {
let Some(&new) = self.fill.get(&id) else { continue };
*fill = new;
}
for &add_id in &self.add {
let Some(segment_range) = self.segment_range.get(&add_id) else { continue };
let Some(&fill) = self.fill.get(&add_id) else { continue };
region_domain.push(add_id, segment_range.clone(), fill);
region_domain.push(add_id, segment_range.clone());
}
}
@@ -299,7 +280,6 @@ impl RegionModification {
add: vector.region_domain.ids().to_vec(),
remove: HashSet::new(),
segment_range: vector.region_domain.ids().iter().copied().zip(vector.region_domain.segment_range().iter().cloned()).collect(),
fill: vector.region_domain.ids().iter().copied().zip(vector.region_domain.fill().iter().cloned()).collect(),
}
}
}
@@ -382,13 +362,11 @@ impl VectorModification {
modified_segments.extend(self.segments.end_point.keys().filter(not_added_segment));
modified_segments.extend(self.segments.handle_primary.keys().filter(not_added_segment));
modified_segments.extend(self.segments.handle_end.keys().filter(not_added_segment));
modified_segments.extend(self.segments.stroke.keys().filter(not_added_segment));
// Count unique modified region IDs across all field maps
let mut modified_regions: HashSet<&RegionId> = HashSet::with_capacity(self.regions.segment_range.len());
let not_added_region = |id: &&RegionId| !add_regions.contains(id);
modified_regions.extend(self.regions.segment_range.keys().filter(not_added_region));
modified_regions.extend(self.regions.fill.keys().filter(not_added_region));
ModificationCategoryCounts {
points: [self.points.add.len(), self.points.remove.len(), point_modifications],
@@ -462,7 +440,7 @@ impl VectorModification {
/// Add a [`VectorModificationType`] to this modification.
pub fn modify(&mut self, vector_modification: &VectorModificationType) {
match vector_modification {
VectorModificationType::InsertSegment { id, points, handles } => self.segments.push(*id, *points, *handles, StrokeId::ZERO),
VectorModificationType::InsertSegment { id, points, handles } => self.segments.push(*id, *points, *handles),
VectorModificationType::InsertPoint { id, position } => self.points.push(*id, *position),
VectorModificationType::RemoveSegment { id } => self.segments.remove(*id),
@@ -685,14 +663,14 @@ impl<'a> AppendBezpath<'a> {
let next_segment_id = self.segment_id.next_id();
self.vector
.segment_domain
.push(next_segment_id, self.last_point_index.unwrap(), self.first_point_index.unwrap(), handle, StrokeId::ZERO);
.push(next_segment_id, self.last_point_index.unwrap(), self.first_point_index.unwrap(), handle);
// Create a new region.
let next_region_id = self.vector.region_domain.next_id();
let first_segment_id = self.first_segment_id.unwrap_or(next_segment_id);
let last_segment_id = next_segment_id;
self.vector.region_domain.push(next_region_id, first_segment_id..=last_segment_id, FillId::ZERO);
self.vector.region_domain.push(next_region_id, first_segment_id..=last_segment_id);
}
fn append_segment(&mut self, end_point: Point, handle: BezierHandles) {
@@ -704,9 +682,7 @@ impl<'a> AppendBezpath<'a> {
// Append the segment.
let next_segment_id = self.segment_id.next_id();
self.vector
.segment_domain
.push(next_segment_id, self.last_point_index.unwrap(), next_point_index, handle, StrokeId::ZERO);
self.vector.segment_domain.push(next_segment_id, self.last_point_index.unwrap(), next_point_index, handle);
// Update the states.
self.last_point = Some(end_point);

View File

@@ -75,7 +75,6 @@ impl core_types::transform::BakeTransform for Vector {
impl Vector {
/// Add a path of manipulator groups to this vector path.
pub fn append_manipulator_groups(&mut self, manipulator_groups: &[ManipulatorGroup], closed: bool, preserve_id: bool) {
let stroke_id = StrokeId::ZERO;
let mut point_id = self.point_domain.next_id();
let handles = |a: &ManipulatorGroup, b: &ManipulatorGroup| match (a.out_handle, b.in_handle) {
@@ -111,23 +110,21 @@ impl Vector {
let id = segment_id.next_id();
first_seg = Some(first_seg.unwrap_or(id));
last_seg = Some(id);
self.segment_domain.push(id, start, end_index, handles(&pair[0], &pair[1]), stroke_id);
self.segment_domain.push(id, start, end_index, handles(&pair[0], &pair[1]));
last_point = Some(end_index);
}
let fill_id = FillId::ZERO;
if closed {
if let (Some(last), Some(first), Some(first_id), Some(last_id)) = (manipulator_groups.last(), manipulator_groups.first(), first_point, last_point) {
let id = segment_id.next_id();
first_seg = Some(first_seg.unwrap_or(id));
last_seg = Some(id);
self.segment_domain.push(id, last_id, first_id, handles(last, first), stroke_id);
self.segment_domain.push(id, last_id, first_id, handles(last, first));
}
if let [Some(first_seg), Some(last_seg)] = [first_seg, last_seg] {
self.region_domain.push(self.region_domain.next_id(), first_seg..=last_seg, fill_id);
self.region_domain.push(self.region_domain.next_id(), first_seg..=last_seg);
}
}
}
@@ -157,7 +154,7 @@ impl Vector {
for (start, end) in segments_to_add {
let segment_id = self.segment_domain.next_id().next_id();
self.segment_domain.push(segment_id, start, end, BezierHandles::Linear, StrokeId::ZERO);
self.segment_domain.push(segment_id, start, end, BezierHandles::Linear);
}
}
@@ -280,7 +277,7 @@ impl Vector {
self.segment_domain.end_point().iter().map(|&index| self.point_domain.ids()[index])
}
pub fn push(&mut self, id: SegmentId, start: PointId, end: PointId, handles: (Option<DVec2>, Option<DVec2>), stroke: StrokeId) {
pub fn push(&mut self, id: SegmentId, start: PointId, end: PointId, handles: (Option<DVec2>, Option<DVec2>)) {
let [Some(start), Some(end)] = [start, end].map(|id| self.point_domain.resolve_id(id)) else {
return;
};
@@ -289,7 +286,7 @@ impl Vector {
(None, Some(handle)) | (Some(handle), None) => BezierHandles::Quadratic { handle },
(Some(handle_start), Some(handle_end)) => BezierHandles::Cubic { handle_start, handle_end },
};
self.segment_domain.push(id, start, end, handles, stroke)
self.segment_domain.push(id, start, end, handles)
}
pub fn handles_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut BezierHandles, PointId, PointId)> {