From ba092f03e1d29dca2d97d334b6839aa4fcb94816 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 6 Nov 2025 11:42:34 +0100 Subject: [PATCH] :tada: Use Vec instead of Indexset --- render-wasm/src/main.rs | 27 ++++++----- render-wasm/src/math/bools.rs | 7 +-- render-wasm/src/render.rs | 11 +++-- render-wasm/src/shapes.rs | 46 ++++++++++--------- .../src/shapes/modifiers/grid_layout.rs | 3 +- render-wasm/src/state.rs | 25 ++++++---- render-wasm/src/wasm/paths/bools.rs | 5 +- 7 files changed, 63 insertions(+), 61 deletions(-) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index e5743bd5f5..d78b15482b 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -14,7 +14,6 @@ mod view; mod wapi; mod wasm; -use indexmap::IndexSet; use math::{Bounds, Matrix}; use mem::SerializableResult; use shapes::{StructureEntry, StructureEntryType, TransformEntry}; @@ -303,8 +302,8 @@ pub extern "C" fn add_shape_child(a: u32, b: u32, c: u32, d: u32) { }); } -fn set_children_set(entries: IndexSet) { - let mut deleted = IndexSet::new(); +fn set_children_set(entries: Vec) { + let mut deleted = Vec::new(); let mut parent_id = None; with_current_shape_mut!(state, |shape: &mut Shape| { @@ -326,13 +325,13 @@ fn set_children_set(entries: IndexSet) { #[no_mangle] pub extern "C" fn set_children_0() { - let entries = IndexSet::from_iter(vec![]); + let entries = vec![]; set_children_set(entries); } #[no_mangle] pub extern "C" fn set_children_1(a1: u32, b1: u32, c1: u32, d1: u32) { - let entries = IndexSet::from_iter(vec![uuid_from_u32_quartet(a1, b1, c1, d1)]); + let entries = vec![uuid_from_u32_quartet(a1, b1, c1, d1)]; set_children_set(entries); } @@ -347,10 +346,10 @@ pub extern "C" fn set_children_2( c2: u32, d2: u32, ) { - let entries = IndexSet::from_iter(vec![ + let entries = vec![ uuid_from_u32_quartet(a1, b1, c1, d1), uuid_from_u32_quartet(a2, b2, c2, d2), - ]); + ]; set_children_set(entries); } @@ -369,11 +368,11 @@ pub extern "C" fn set_children_3( c3: u32, d3: u32, ) { - let entries = IndexSet::from_iter(vec![ + let entries = vec![ uuid_from_u32_quartet(a1, b1, c1, d1), uuid_from_u32_quartet(a2, b2, c2, d2), uuid_from_u32_quartet(a3, b3, c3, d3), - ]); + ]; set_children_set(entries); } @@ -396,12 +395,12 @@ pub extern "C" fn set_children_4( c4: u32, d4: u32, ) { - let entries = IndexSet::from_iter(vec![ + let entries = vec![ uuid_from_u32_quartet(a1, b1, c1, d1), uuid_from_u32_quartet(a2, b2, c2, d2), uuid_from_u32_quartet(a3, b3, c3, d3), uuid_from_u32_quartet(a4, b4, c4, d4), - ]); + ]; set_children_set(entries); } @@ -428,13 +427,13 @@ pub extern "C" fn set_children_5( c5: u32, d5: u32, ) { - let entries = IndexSet::from_iter(vec![ + let entries = vec![ uuid_from_u32_quartet(a1, b1, c1, d1), uuid_from_u32_quartet(a2, b2, c2, d2), uuid_from_u32_quartet(a3, b3, c3, d3), uuid_from_u32_quartet(a4, b4, c4, d4), uuid_from_u32_quartet(a5, b5, c5, d5), - ]); + ]; set_children_set(entries); } @@ -442,7 +441,7 @@ pub extern "C" fn set_children_5( pub extern "C" fn set_children() { let bytes = mem::bytes_or_empty(); - let entries: IndexSet = bytes + let entries: Vec = bytes .chunks(size_of::<::BytesType>()) .map(|data| Uuid::from_bytes(data.try_into().unwrap())) .collect(); diff --git a/render-wasm/src/math/bools.rs b/render-wasm/src/math/bools.rs index 4474387d75..123e1b262a 100644 --- a/render-wasm/src/math/bools.rs +++ b/render-wasm/src/math/bools.rs @@ -5,7 +5,6 @@ use crate::state::ShapesPoolRef; use crate::uuid::Uuid; use bezier_rs::{Bezier, BezierHandles, ProjectionOptions, TValue}; use glam::DVec2; -use indexmap::IndexSet; use skia_safe as skia; use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap}; @@ -384,11 +383,7 @@ fn beziers_to_segments(beziers: &[(BezierSource, Bezier)]) -> Vec { result } -pub fn bool_from_shapes( - bool_type: BoolType, - children_ids: &IndexSet, - shapes: ShapesPoolRef, -) -> Path { +pub fn bool_from_shapes(bool_type: BoolType, children_ids: &[Uuid], shapes: ShapesPoolRef) -> Path { if children_ids.is_empty() { return Path::default(); } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index afc74c2d25..e8a102f92f 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -30,8 +30,6 @@ use crate::uuid::Uuid; use crate::view::Viewbox; use crate::wapi; -use indexmap::IndexSet; - pub use fonts::*; pub use images::*; @@ -1492,11 +1490,14 @@ impl RenderState { // We only need first level shapes let mut valid_ids: Vec = ids .iter() - .filter_map(|id| root_ids.get(id).map(|_| *id)) + .filter(|id| root_ids.contains(id)) + .copied() .collect(); // These shapes for the tile should be ordered as they are in the parent node - valid_ids.sort_by_key(|id| root_ids.get_index_of(id)); + valid_ids.sort_by_key(|id| { + root_ids.iter().position(|x| x == id).unwrap_or(usize::MAX) + }); self.pending_nodes.extend(valid_ids.into_iter().map(|id| { NodeRenderState { @@ -1687,7 +1688,7 @@ impl RenderState { /// /// This is useful when you have a pre-computed set of shape IDs that need to be refreshed, /// regardless of their relationship to other shapes (e.g., ancestors, descendants, or any other collection). - pub fn update_tiles_shapes(&mut self, shape_ids: &IndexSet, tree: ShapesPoolMutRef<'_>) { + pub fn update_tiles_shapes(&mut self, shape_ids: &[Uuid], tree: ShapesPoolMutRef<'_>) { performance::begin_measure!("invalidate_and_update_tiles"); let mut all_tiles = HashSet::::new(); for shape_id in shape_ids { diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index c83e009052..dfd7796ca3 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -48,7 +48,6 @@ pub use text::*; pub use transform::*; use crate::math::{self, Bounds, Matrix, Point}; -use indexmap::IndexSet; use crate::state::ShapesPoolRef; @@ -161,7 +160,7 @@ pub struct Shape { pub id: Uuid, pub parent_id: Option, pub shape_type: Type, - pub children: IndexSet, + pub children: Vec, pub selrect: math::Rect, pub transform: Matrix, pub rotation: f32, @@ -201,16 +200,21 @@ pub fn all_with_ancestors( shapes: &[Uuid], shapes_pool: ShapesPoolRef, include_hidden: bool, -) -> IndexSet { +) -> Vec { let mut pending = Vec::from_iter(shapes.iter()); - let mut result = IndexSet::new(); + let mut result = Vec::new(); + let mut seen = HashSet::new(); while !pending.is_empty() { let Some(current_id) = pending.pop() else { break; }; - result.insert(*current_id); + if !seen.insert(*current_id) { + continue; + } + + result.push(*current_id); let Some(parent_id) = shapes_pool.get(current_id).and_then(|s| s.parent_id) else { continue; @@ -220,7 +224,7 @@ pub fn all_with_ancestors( continue; } - if result.contains(&parent_id) { + if seen.contains(&parent_id) { continue; } @@ -244,7 +248,7 @@ impl Shape { id, parent_id: None, shape_type: Type::Rect(Rect::default()), - children: IndexSet::::new(), + children: Vec::new(), selrect: math::Rect::new_empty(), transform: Matrix::default(), rotation: 0., @@ -560,15 +564,15 @@ impl Shape { } pub fn add_child(&mut self, id: Uuid) { - self.children.insert(id); + self.children.push(id); } - pub fn compute_children_differences( - &mut self, - children: &IndexSet, - ) -> (IndexSet, IndexSet) { - let added = children.difference(&self.children).cloned().collect(); - let removed = self.children.difference(children).cloned().collect(); + pub fn compute_children_differences(&mut self, children: &[Uuid]) -> (Vec, Vec) { + let current_set: HashSet = self.children.iter().copied().collect(); + let new_set: HashSet = children.iter().copied().collect(); + + let added: Vec = new_set.difference(¤t_set).copied().collect(); + let removed: Vec = current_set.difference(&new_set).copied().collect(); (added, removed) } @@ -946,26 +950,26 @@ impl Shape { self.children.first() } - pub fn children_ids(&self, include_hidden: bool) -> IndexSet { + pub fn children_ids(&self, include_hidden: bool) -> Vec { if include_hidden { - return self.children.clone().into_iter().rev().collect(); + return self.children.iter().rev().copied().collect(); } if let Type::Bool(_) = self.shape_type { - IndexSet::::new() + Vec::new() } else if let Type::Group(group) = self.shape_type { if group.masked { self.children .iter() .rev() .take(self.children.len() - 1) - .cloned() + .copied() .collect() } else { - self.children.clone().into_iter().rev().collect() + self.children.iter().rev().copied().collect() } } else { - self.children.clone().into_iter().rev().collect() + self.children.iter().rev().copied().collect() } } @@ -992,7 +996,7 @@ impl Shape { shapes: ShapesPoolRef, include_hidden: bool, include_self: bool, - ) -> IndexSet { + ) -> Vec { let all_children = self.children_ids_iter(include_hidden).flat_map(|id| { shapes .get(id) diff --git a/render-wasm/src/shapes/modifiers/grid_layout.rs b/render-wasm/src/shapes/modifiers/grid_layout.rs index f1ceacdccb..2c3d19df1d 100644 --- a/render-wasm/src/shapes/modifiers/grid_layout.rs +++ b/render-wasm/src/shapes/modifiers/grid_layout.rs @@ -6,7 +6,6 @@ use crate::shapes::{ }; use crate::state::ShapesPoolRef; use crate::uuid::Uuid; -use indexmap::IndexSet; use std::collections::{HashMap, VecDeque}; use super::common::GetBounds; @@ -538,7 +537,7 @@ fn cell_bounds( pub fn create_cell_data<'a>( layout_bounds: &Bounds, - children: &IndexSet, + children: &[Uuid], shapes: ShapesPoolRef<'a>, cells: &Vec, column_tracks: &[TrackData], diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index 5d4673e235..dc984c7b52 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -136,19 +136,24 @@ impl<'a> State<'a> { /// invalidated and recalculated to include the new child. This ensures that frames /// and groups properly encompass their children. pub fn set_parent_for_current_shape(&mut self, id: Uuid) { - let shape = { - let Some(shape) = self.current_shape_mut() else { - panic!("Invalid current shape") - }; - shape.set_parent(id); - - // TODO this clone doesn't seem necessary - shape.clone() + let Some(shape) = self.current_shape_mut() else { + panic!("Invalid current shape") }; - if let Some(parent) = shape.parent_id.and_then(|id| self.shapes.get_mut(&id)) { + // If the shape already has the same parent, do nothing + if shape.parent_id == Some(id) { + return; + } + + shape.set_parent(id); + + // Note: We don't call parent.add_child() here because we are + // asuming the parent is updating its children list via set_children() calls. + // Calling add_child here would create duplicates. + + // Invalidate parent's extrect so it gets recalculated to include the new child + if let Some(parent) = self.shapes.get_mut(&id) { parent.invalidate_extrect(); - parent.add_child(shape.id); } } diff --git a/render-wasm/src/wasm/paths/bools.rs b/render-wasm/src/wasm/paths/bools.rs index 08b852d511..e3c7b15c59 100644 --- a/render-wasm/src/wasm/paths/bools.rs +++ b/render-wasm/src/wasm/paths/bools.rs @@ -1,13 +1,12 @@ use macros::ToJs; -use indexmap::IndexSet; - use super::RawSegmentData; use crate::math; use crate::shapes::BoolType; use crate::uuid::Uuid; use crate::{mem, SerializableResult}; use crate::{with_current_shape_mut, with_state, STATE}; +use std::mem::size_of; #[derive(Debug, Clone, Copy, PartialEq, ToJs)] #[repr(u8)] @@ -47,7 +46,7 @@ pub extern "C" fn set_shape_bool_type(raw_bool_type: u8) { pub extern "C" fn calculate_bool(raw_bool_type: u8) -> *mut u8 { let bytes = mem::bytes_or_empty(); - let entries: IndexSet = bytes + let entries: Vec = bytes .chunks(size_of::<::BytesType>()) .map(|data| Uuid::from_bytes(data.try_into().unwrap())) .collect();