diff --git a/editor/src/messages/portfolio/document/data_panel/data_panel_message_handler.rs b/editor/src/messages/portfolio/document/data_panel/data_panel_message_handler.rs index 817b4bad0a..c9bda31717 100644 --- a/editor/src/messages/portfolio/document/data_panel/data_panel_message_handler.rs +++ b/editor/src/messages/portfolio/document/data_panel/data_panel_message_handler.rs @@ -312,7 +312,7 @@ impl TableItemLayout for List { } } -impl TableItemLayout for Artboard { +impl TableItemLayout for Artboard<'_> { fn type_name() -> &'static str { "Artboard" } @@ -328,7 +328,7 @@ impl TableItemLayout for Artboard { } } -impl TableItemLayout for Graphic { +impl TableItemLayout for Graphic<'_> { fn type_name() -> &'static str { "Graphic" } diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index f6807909af..75c99ccd7c 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -248,11 +248,11 @@ pub enum DocumentMessage { // `UpdateFillAttributes` and `UpdateStrokeAttributes` are produced inside `editor.handle_message` by `node_graph_executor.rs` and consumed in the same dispatch loop, so it never reaches that serialization point. #[serde(skip)] UpdateFillAttributes { - fill_attributes: HashMap>>, + fill_attributes: HashMap>>>, }, #[serde(skip)] UpdateStrokeAttributes { - stroke_attributes: HashMap>>, + stroke_attributes: HashMap>>>, }, Undo, UngroupSelectedLayers, diff --git a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs index 2676f75dc1..67bdf7624e 100644 --- a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs +++ b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs @@ -43,10 +43,10 @@ pub struct DocumentMetadata { pub layer_vector_data: HashMap>, /// Per-layer `ATTR_FILL` attribute, exposed so message handlers can read paint /// information that lives on the list. - pub layer_fill_attributes: HashMap>>, + pub layer_fill_attributes: HashMap>>>, /// Per-layer `ATTR_STROKE` attribute, exposed so message handlers can read /// stroke paint information that lives on the list. - pub layer_stroke_attributes: HashMap>>, + pub layer_stroke_attributes: HashMap>>>, /// Transform from document space to viewport space. pub document_to_viewport: DAffine2, } diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 573322ce51..7bb5e05bc1 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -3440,12 +3440,12 @@ impl NodeNetworkInterface { } /// Update the per-layer `ATTR_FILL` snapshot. - pub fn update_fill_attributes(&mut self, new_layer_fill_attributes: HashMap>>) { + pub fn update_fill_attributes(&mut self, new_layer_fill_attributes: HashMap>>>) { self.document_metadata.layer_fill_attributes = new_layer_fill_attributes; } /// Update the per-layer `ATTR_STROKE` snapshot. - pub fn update_stroke_attributes(&mut self, new_layer_stroke_attributes: HashMap>>) { + pub fn update_stroke_attributes(&mut self, new_layer_stroke_attributes: HashMap>>>) { self.document_metadata.layer_stroke_attributes = new_layer_stroke_attributes; } } diff --git a/editor/src/node_graph_executor/runtime.rs b/editor/src/node_graph_executor/runtime.rs index a919531aca..4aae271c77 100644 --- a/editor/src/node_graph_executor/runtime.rs +++ b/editor/src/node_graph_executor/runtime.rs @@ -618,7 +618,7 @@ impl NodeRuntime { /// Returns the union of the artboards' clipping rectangles, used as the thumbnail bounds for an artboard layer so the /// framing matches what's actually visible after clipping rather than the unclipped content extents. -fn artboard_clip_bounds>(artboards: &S) -> RenderBoundingBox { +fn artboard_clip_bounds<'a, S: graphene_std::core_types::lane::LaneSource>>(artboards: &S) -> RenderBoundingBox { use graphene_std::core_types::attribute::{Dimensions, Location}; let mut combined: Option<[DVec2; 2]> = None; for index in 0..artboards.lane_count() { diff --git a/node-graph/libraries/graphic-types/src/artboard.rs b/node-graph/libraries/graphic-types/src/artboard.rs index ebaea9709d..615731b758 100644 --- a/node-graph/libraries/graphic-types/src/artboard.rs +++ b/node-graph/libraries/graphic-types/src/artboard.rs @@ -12,10 +12,10 @@ use glam::DAffine2; /// enclosing `List`, not as fields here. This keeps `Artboard` a pure type-system boundary /// that prevents arbitrary `List>>` nesting. #[derive(Clone, Debug, Default, CacheHash, PartialEq, DynAny)] -pub struct Artboard(List); +pub struct Artboard<'e>(List>); -impl Artboard { - pub fn new(content: List) -> Self { +impl<'e> Artboard<'e> { + pub fn new(content: List>) -> Self { Self(content) } @@ -23,17 +23,17 @@ impl Artboard { &self.0 } - pub fn as_graphic_list_mut(&mut self) -> &mut List { + pub fn as_graphic_list_mut(&mut self) -> &mut List> { &mut self.0 } - pub fn into_graphic_list(self) -> List { + pub fn into_graphic_list(self) -> List> { self.0 } /// The artboard with every content group converted to its legacy form, so /// the value owns all of its content free of arena borrows. - pub fn with_legacy_groups(&self) -> Artboard { + pub fn with_legacy_groups(&self) -> Artboard<'e> { let mut content = self.0.clone(); for element in content.iter_element_values_mut() { *element = crate::graphic::map_groups_to_legacy(element); @@ -87,19 +87,19 @@ const _: () = { } }; -impl From> for Artboard { - fn from(content: List) -> Self { +impl<'e> From>> for Artboard<'e> { + fn from(content: List>) -> Self { Self(content) } } -impl From for List { - fn from(artboard: Artboard) -> Self { +impl<'e> From> for List> { + fn from(artboard: Artboard<'e>) -> Self { artboard.0 } } -impl BoundingBox for Artboard { +impl BoundingBox for Artboard<'_> { fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox { self.0.bounding_box(transform, include_stroke) } @@ -109,7 +109,7 @@ impl BoundingBox for Artboard { } } -impl RenderComplexity for Artboard { +impl RenderComplexity for Artboard<'_> { fn render_complexity(&self) -> usize { self.0.render_complexity() } diff --git a/node-graph/libraries/graphic-types/src/boundary.rs b/node-graph/libraries/graphic-types/src/boundary.rs index 5cf2e15ef2..d89e660862 100644 --- a/node-graph/libraries/graphic-types/src/boundary.rs +++ b/node-graph/libraries/graphic-types/src/boundary.rs @@ -16,15 +16,15 @@ use glam::{DAffine2, DVec2}; use vector_types::GradientStops; /// The outcome of materializing a leveled wire into a group. -pub enum LevelGroup { - Group(Group, Finality), +pub enum LevelGroup<'e> { + Group(Group<'e>, Finality), Pending, Error(GraphError), } /// The renderer's flip form: the wire's whole extent materialized into a /// group over the level's records, ready for the group render bridge. -pub fn materialize_group<'e, C, N>(node: &N, input: &C, arena: &Arena) -> LevelGroup +pub fn materialize_group<'a, 'e, C, N>(node: &'a N, input: &'a C, arena: &'a Arena) -> LevelGroup<'a> where C: InjectIndex + Copy, N: Node>, @@ -76,7 +76,7 @@ pub fn batch_to_legacy(layout: &core_types::record::Layout, batch: core_types::n } // SAFETY: the caller's batch is resident for the read. let item = unsafe { GroupItem::from_resident(batch) }; - fn typed(item: &GroupItem) -> Option> { + fn typed(item: &GroupItem) -> Option> { run_to_legacy_list::(item).map(|list| Box::new(list) as Box) } if item.typed_lanes::().is_some() { diff --git a/node-graph/libraries/graphic-types/src/graphic.rs b/node-graph/libraries/graphic-types/src/graphic.rs index 0725e36140..64c02e5d46 100644 --- a/node-graph/libraries/graphic-types/src/graphic.rs +++ b/node-graph/libraries/graphic-types/src/graphic.rs @@ -19,18 +19,18 @@ pub use vector_types::Vector; /// lane. Multi-element content is a [`core_types::record::Group`] run, or /// transitionally the legacy `Graphic` list. #[derive(Clone, Debug, CacheHash, PartialEq, DynAny)] -pub enum Graphic { - Graphic(List), +pub enum Graphic<'e> { + Graphic(List>), Vector(Vector), RasterCPU(Raster), RasterGPU(Raster), Color(Color), Gradient(GradientStops), Text(String), - Group(core_types::record::Group), + Group(core_types::record::Group<'e>), } -impl Default for Graphic { +impl Default for Graphic<'_> { fn default() -> Self { Self::Graphic(List::new()) } @@ -38,7 +38,7 @@ impl Default for Graphic { /// A typed legacy list as a legacy graphic list: each item de-tables to a /// leaf element, keeping its attributes on the containing lane. -fn detable_items(list: List, leaf: fn(T) -> Graphic) -> List { +fn detable_items<'e, T: Clone + Send + Sync + 'static>(list: List, leaf: fn(T) -> Graphic<'e>) -> List> { let mut out = List::new(); for item in list.into_iter() { let (element, attributes) = item.into_parts(); @@ -55,7 +55,10 @@ pub trait IntoGraphicElement: Clone + Send + Sync + CacheHash + 'static { fn into_graphic_element(self, arena: &core_types::arena::Arena) -> Option; } -fn list_group(list: List, arena: &core_types::arena::Arena) -> Option { +fn list_group(list: List, arena: &core_types::arena::Arena) -> Option +where + T::Static: Clone + Send + Sync, +{ Some(Graphic::Group(core_types::record::Group { row: None, content: core_types::record::GroupItem::from_list(list, arena)?, @@ -89,41 +92,41 @@ into_graphic_element! { Text: String; } -impl IntoGraphicElement for Graphic { +impl IntoGraphicElement for Graphic<'static> { fn into_graphic_element(self, _arena: &core_types::arena::Arena) -> Option { Some(self) } } -impl IntoGraphicElement for List { +impl IntoGraphicElement for List> { fn into_graphic_element(self, arena: &core_types::arena::Arena) -> Option { list_group(self, arena) } } // Vector -impl From for Graphic { +impl From for Graphic<'_> { fn from(vector: Vector) -> Self { Graphic::Vector(vector) } } // Raster -impl From> for Graphic { +impl From> for Graphic<'_> { fn from(raster: Raster) -> Self { Graphic::RasterCPU(raster) } } // Raster -impl From> for Graphic { +impl From> for Graphic<'_> { fn from(raster: Raster) -> Self { Graphic::RasterGPU(raster) } } // Color -impl From for Graphic { +impl From for Graphic<'_> { fn from(color: Color) -> Self { Graphic::Color(color) } @@ -131,14 +134,14 @@ impl From for Graphic { // Note: List -> Option is in gcore (Color is defined there) // GradientStops -impl From for Graphic { +impl From for Graphic<'_> { fn from(gradient: GradientStops) -> Self { Graphic::Gradient(gradient) } } // String -impl From for Graphic { +impl From for Graphic<'_> { fn from(text: String) -> Self { Graphic::Text(text) } @@ -218,10 +221,10 @@ pub fn is_paint_present(graphic_list: &List) -> bool { } /// Look up the paint graphics stored under the marker `A`, in the canonical `List` form. -pub fn paint_graphics<'a, A, S>(source: &'a S, index: usize) -> Option<&'a List> +pub fn paint_graphics<'a, A, S>(source: &'a S, index: usize) -> Option<&'a List>> where S: LaneSource, - A: Attribute = Option<&'a List>>, + A: Attribute = Option<&'a List>>>, { source .attr::(index) @@ -234,7 +237,7 @@ where pub fn has_paint<'a, A, S>(source: &'a S, index: usize) -> bool where S: LaneSource, - A: Attribute = Option<&'a List>>, + A: Attribute = Option<&'a List>>>, { paint_graphics::(source, index).is_some() } @@ -259,8 +262,8 @@ pub fn vector_can_reduce_to_clip_path>(source: & /// [`PaintOverlay`] threads down. #[derive(Clone, Copy, Default)] pub struct LanePaint<'a> { - pub fill: Option<&'a List>, - pub stroke: Option<&'a List>, + pub fill: Option<&'a List>>, + pub stroke: Option<&'a List>>, } impl<'a> LanePaint<'a> { @@ -287,7 +290,7 @@ impl<'a, S: LaneSource> PaintColumns<'a, S> { /// The lane's present, non-blank paint. pub fn read(&self, lane: usize) -> LanePaint<'a> { - let present = |value: Option>>| value.flatten().filter(|list| is_paint_present(list)); + let present = |value: Option>>>| value.flatten().filter(|list| is_paint_present(list)); LanePaint { fill: present(self.fill.try_get(lane)), stroke: present(self.stroke.try_get(lane)), @@ -472,7 +475,7 @@ impl TryFromGraphic for String { // Local trait to convert types to List (avoids orphan rule issues) pub trait IntoGraphicList: Clone + Send + Sync + Default + std::fmt::Debug + PartialEq + CacheHash + 'static { - fn into_graphic_list(self) -> List; + fn into_graphic_list(self) -> List>; /// Deeply flattens any content of type `T` within a `List`, discarding all other content, and returning a flat `List`. fn into_flattened_list(self) -> List @@ -483,70 +486,70 @@ pub trait IntoGraphicList: Clone + Send + Sync + Default + std::fmt::Debug + Par } } -impl IntoGraphicList for List { - fn into_graphic_list(self) -> List { +impl IntoGraphicList for List> { + fn into_graphic_list(self) -> List> { self } } impl IntoGraphicList for List { - fn into_graphic_list(self) -> List { + fn into_graphic_list(self) -> List> { detable_items(self, Graphic::Vector) } } impl IntoGraphicList for List> { - fn into_graphic_list(self) -> List { + fn into_graphic_list(self) -> List> { detable_items(self, Graphic::RasterCPU) } } impl IntoGraphicList for List> { - fn into_graphic_list(self) -> List { + fn into_graphic_list(self) -> List> { detable_items(self, Graphic::RasterGPU) } } impl IntoGraphicList for List { - fn into_graphic_list(self) -> List { + fn into_graphic_list(self) -> List> { detable_items(self, Graphic::Color) } } impl IntoGraphicList for List { - fn into_graphic_list(self) -> List { + fn into_graphic_list(self) -> List> { detable_items(self, Graphic::Gradient) } } impl IntoGraphicList for List { - fn into_graphic_list(self) -> List { + fn into_graphic_list(self) -> List> { detable_items(self, Graphic::Text) } } impl IntoGraphicList for DAffine2 { - fn into_graphic_list(self) -> List { + fn into_graphic_list(self) -> List> { List::new_from_element(Graphic::default()) } } // DAffine2 -impl From for Graphic { +impl From for Graphic<'_> { fn from(_: DAffine2) -> Self { Graphic::default() } } // DVec2 -impl From for Graphic { +impl From for Graphic<'_> { fn from(position: DVec2) -> Self { Graphic::Vector(Vector::from_anchor_position(position)) } } // Note: List conversions handled by blanket impl in gcore -impl Graphic { +impl<'e> Graphic<'e> { pub fn as_graphic(&self) -> Option<&List> { match self { Graphic::Graphic(graphic) => Some(graphic), @@ -554,7 +557,7 @@ impl Graphic { } } - pub fn as_graphic_mut(&mut self) -> Option<&mut List> { + pub fn as_graphic_mut(&mut self) -> Option<&mut List>> { match self { Graphic::Graphic(graphic) => Some(graphic), _ => None, @@ -726,7 +729,7 @@ fn group_bounding_box(group: &core_types::record::Group, transform: DAffine2, in } } } - fn typed_run(item: &core_types::record::GroupItem, transform: DAffine2, include_stroke: bool, thumbnail: bool) -> Option { + fn typed_run(item: &core_types::record::GroupItem, transform: DAffine2, include_stroke: bool, thumbnail: bool) -> Option { let lanes = item.typed_lanes::()?; let transform_offset = RunAttrs::of(item).transform; let mut combined = None; @@ -764,7 +767,7 @@ fn group_bounding_box(group: &core_types::record::Group, transform: DAffine2, in /// One typed run as an owned list, elements cloned and every attribute copied /// through its erased read. Content keeps its native form; the legacy /// conversions layer their mapping on top. -pub fn run_to_list(item: &core_types::record::GroupItem) -> Option> { +pub fn run_to_list(item: &core_types::record::GroupItem) -> Option> { let lanes = item.typed_lanes::()?; let mut list = List::new(); for lane in 0..lanes.len() { @@ -782,7 +785,7 @@ pub fn run_to_list(item: &core_types::record:: /// Converts the group content of the list's paint attribute values to legacy /// form, so a legacy product owns everything its attributes reach. -pub fn map_paint_attrs_to_legacy(list: &mut List) { +pub fn map_paint_attrs_to_legacy(list: &mut List) { for key in [ATTR_FILL, ATTR_STROKE, crate::markers::ATTR_EDITOR_MERGED_LAYERS] { let Some(values) = list.iter_attribute_values_mut::>>(key) else { continue }; for value in values.flatten() { @@ -795,7 +798,7 @@ pub fn map_paint_attrs_to_legacy(list: &mut List) { /// One typed run as a legacy list: [`run_to_list`] with the paint attribute /// contents converted to their legacy form. -pub(crate) fn run_to_legacy_list(item: &core_types::record::GroupItem) -> Option> { +pub(crate) fn run_to_legacy_list(item: &core_types::record::GroupItem) -> Option> { let mut list = run_to_list::(item)?; map_paint_attrs_to_legacy(&mut list); Some(list) @@ -848,8 +851,8 @@ impl FlattenScale { /// A graphic level in either of its two storages, as one lane source. #[derive(Clone, Copy)] pub enum GraphicLevel<'a> { - Legacy(&'a List), - Run(&'a core_types::record::GroupItem), + Legacy(&'a List>), + Run(&'a core_types::record::GroupItem<'a>), } pub enum GraphicLevelColumn<'a, A: Attribute> { @@ -867,7 +870,7 @@ impl<'a, A: Attribute> core_types::lane::LaneColumn<'a, A> for GraphicLevelColum } impl<'a> LaneSource for GraphicLevel<'a> { - type Element = Graphic; + type Element = Graphic<'a>; type Column<'b, A: Attribute> = GraphicLevelColumn<'b, A> where @@ -880,7 +883,7 @@ impl<'a> LaneSource for GraphicLevel<'a> { } } - fn element(&self, lane: usize) -> Option<&Graphic> { + fn element(&self, lane: usize) -> Option<&Graphic<'a>> { match self { GraphicLevel::Legacy(list) => list.element(lane), GraphicLevel::Run(item) => { @@ -935,7 +938,7 @@ enum RowSourceRef<'w> { /// A de-tabled vector leaf on a graphic lane: the lane is the row. Lane(GraphicLevel<'w>, usize), /// A lane of a vector run. - Run(&'w core_types::record::RunView<'w, Vector>, &'w core_types::record::GroupItem, usize), + Run(&'w core_types::record::RunView<'w, Vector>, &'w core_types::record::GroupItem<'w>, usize), } impl VectorRow<'_> { @@ -1126,23 +1129,29 @@ fn push_lane_paint_into_interiors(list: &mut List) { /// The graphic with every `Group` deep-copied to its owned form, which /// survives the arena generation but cannot be read until /// [`map_groups_to_resident`] re-parks it into a serving arena. -pub fn map_groups_to_owned(graphic: &Graphic) -> Graphic { +pub fn map_groups_to_owned<'out>(graphic: &Graphic<'_>) -> Graphic<'out> { match graphic { Graphic::Group(group) => Graphic::Group(group.copy_out()), Graphic::Graphic(children) => { - let mut children = children.clone(); - for child in children.iter_element_values_mut() { - *child = map_groups_to_owned(child); + let mut out = List::new(); + for item in children.clone().into_iter() { + let (element, attributes) = item.into_parts(); + out.push(Item::from_parts(map_groups_to_owned(&element), attributes)); } - Graphic::Graphic(children) + Graphic::Graphic(out) } - other => other.clone(), + Graphic::Vector(vector) => Graphic::Vector(vector.clone()), + Graphic::RasterCPU(raster) => Graphic::RasterCPU(raster.clone()), + Graphic::RasterGPU(raster) => Graphic::RasterGPU(raster.clone()), + Graphic::Color(color) => Graphic::Color(*color), + Graphic::Gradient(gradient) => Graphic::Gradient(gradient.clone()), + Graphic::Text(text) => Graphic::Text(text.clone()), } } /// The graphic with every owned `Group` re-parked into `arena`; `None` /// reports arena exhaustion. -pub fn map_groups_to_resident(graphic: &Graphic, arena: &core_types::arena::Arena) -> Option { +pub fn map_groups_to_resident<'a>(graphic: &Graphic<'a>, arena: &'a core_types::arena::Arena) -> Option> { match graphic { Graphic::Group(group) => group.replay(arena).map(Graphic::Group), Graphic::Graphic(children) => { @@ -1216,6 +1225,7 @@ fn deep_repark_graphic_list(value: &dyn core_types::list::AnyAttributeValue, are for element in list.iter_element_values_mut() { *element = map_groups_to_resident(element, arena)?; } + let list = unsafe { core_types::record::erase_static(list) }; Some(Some(Box::new(Some(list)))) } @@ -1253,25 +1263,31 @@ pub fn direct_vector_len(graphic: &Graphic) -> usize { } } -pub fn map_groups_to_legacy(graphic: &Graphic) -> Graphic { +pub fn map_groups_to_legacy<'out>(graphic: &Graphic<'_>) -> Graphic<'out> { match graphic { Graphic::Group(group) => group_to_legacy_graphic(group), Graphic::Graphic(children) => { - let mut children = children.clone(); - for child in children.iter_element_values_mut() { - *child = map_groups_to_legacy(child); + let mut out = List::new(); + for item in children.clone().into_iter() { + let (element, attributes) = item.into_parts(); + out.push(Item::from_parts(map_groups_to_legacy(&element), attributes)); } - map_paint_attrs_to_legacy(&mut children); - Graphic::Graphic(children) + map_paint_attrs_to_legacy(&mut out); + Graphic::Graphic(out) } - other => other.clone(), + Graphic::Vector(vector) => Graphic::Vector(vector.clone()), + Graphic::RasterCPU(raster) => Graphic::RasterCPU(raster.clone()), + Graphic::RasterGPU(raster) => Graphic::RasterGPU(raster.clone()), + Graphic::Color(color) => Graphic::Color(*color), + Graphic::Gradient(gradient) => Graphic::Gradient(gradient.clone()), + Graphic::Text(text) => Graphic::Text(text.clone()), } } /// The group as one legacy graphic. A bare (row-less) wrap of a single typed /// run keeps the run's typed variant, matching the `Into` the /// pre-flip wrap applied; everything else becomes the legacy group list. -pub fn group_to_legacy_graphic(group: &core_types::record::Group) -> Graphic { +pub fn group_to_legacy_graphic(group: &core_types::record::Group) -> Graphic<'static> { if group.row.is_none() { let item = &group.content; let typed = None @@ -1290,7 +1306,7 @@ pub fn group_to_legacy_graphic(group: &core_types::record::Group) -> Graphic { /// The group as a legacy `List`: a `Graphic` run becomes the items, /// another typed run becomes one item holding its typed list. -pub fn group_to_legacy_list(group: &core_types::record::Group) -> List { +pub fn group_to_legacy_list(group: &core_types::record::Group) -> List> { let item = &group.content; if let Some(mut list) = run_to_legacy_list::(item) { for element in list.iter_element_values_mut() { @@ -1309,7 +1325,7 @@ pub fn group_to_legacy_list(group: &core_types::record::Group) -> List } fn group_render_complexity(group: &core_types::record::Group) -> usize { - fn typed_run(item: &core_types::record::GroupItem) -> Option { + fn typed_run(item: &core_types::record::GroupItem) -> Option { let lanes = item.typed_lanes::()?; Some((0..lanes.len()).map(|lane| lanes.element_ref(lane).render_complexity()).sum()) } @@ -1324,7 +1340,7 @@ fn group_render_complexity(group: &core_types::record::Group) -> usize { .unwrap_or(item.len()) } -impl BoundingBox for Graphic { +impl BoundingBox for Graphic<'_> { fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox { match self { Graphic::Vector(vector) => BoundingBox::bounding_box(vector, transform, include_stroke), @@ -1352,23 +1368,23 @@ impl BoundingBox for Graphic { } } -impl ListConvert for Vector { - fn convert_item(self) -> Graphic { +impl<'e> ListConvert> for Vector { + fn convert_item(self) -> Graphic<'e> { Graphic::Vector(self) } } -impl ListConvert for Raster { - fn convert_item(self) -> Graphic { +impl<'e> ListConvert> for Raster { + fn convert_item(self) -> Graphic<'e> { Graphic::RasterCPU(self) } } -impl ListConvert for Raster { - fn convert_item(self) -> Graphic { +impl<'e> ListConvert> for Raster { + fn convert_item(self) -> Graphic<'e> { Graphic::RasterGPU(self) } } -impl RenderComplexity for Graphic { +impl RenderComplexity for Graphic<'_> { fn render_complexity(&self) -> usize { match self { Self::Graphic(list) => list.render_complexity(), @@ -1459,7 +1475,7 @@ mod tests { use super::*; use core_types::list::List; - fn vector_graphic() -> Graphic { + fn vector_graphic() -> Graphic<'static> { Graphic::Vector(Vector::default()) } @@ -1573,7 +1589,7 @@ mod run_tests { assert_eq!(group_to_legacy_list(group), expected); } - fn native_group_paint(vector: &Vector, arena: &core_types::arena::Arena) -> List { + fn native_group_paint<'a>(vector: &Vector, arena: &'a core_types::arena::Arena) -> List> { let mut builder = RunBuilder::new(arena, element_write_hashed::(), &[], 1).unwrap(); builder.push(vector.clone()).unwrap(); List::new_from_element(Graphic::Group(core_types::record::Group { @@ -1586,7 +1602,9 @@ mod run_tests { fn an_owned_run_deep_copies_graphic_list_fields() { let inner_vector = unit_square_at(DVec2::ZERO); let source = core_types::arena::Arena::new(1 << 16).unwrap(); - let paint = native_group_paint(&inner_vector, &source); + // SAFETY: the erased native list serves only while `source` is live; the + // deep glue under test replaces its borrows at the copy-out seam. + let paint = unsafe { core_types::record::erase_static(native_group_paint(&inner_vector, &source)) }; let vector = unit_square_at(DVec2::new(4., 4.)); let mut builder = RunBuilder::new(&source, element_write_hashed::(), &[FieldWrite::of::(0)], 1).unwrap(); @@ -1610,7 +1628,9 @@ mod run_tests { fn an_owned_record_deep_copies_graphic_list_fields() { let inner_vector = unit_square_at(DVec2::ZERO); let source = core_types::arena::Arena::new(1 << 16).unwrap(); - let paint = native_group_paint(&inner_vector, &source); + // SAFETY: the erased native list serves only while `source` is live; the + // deep glue under test replaces its borrows at the copy-out seam. + let paint = unsafe { core_types::record::erase_static(native_group_paint(&inner_vector, &source)) }; let vector = unit_square_at(DVec2::new(4., 4.)); let mut builder = RunBuilder::new(&source, element_write_hashed::(), &[FieldWrite::of::(0)], 1).unwrap(); @@ -1638,7 +1658,9 @@ mod run_tests { fn a_legacy_list_owns_its_paint_attr_content() { let inner_vector = unit_square_at(DVec2::ZERO); let source = core_types::arena::Arena::new(1 << 16).unwrap(); - let paint = native_group_paint(&inner_vector, &source); + // SAFETY: the erased native list serves only while `source` is live; the + // deep glue under test replaces its borrows at the copy-out seam. + let paint = unsafe { core_types::record::erase_static(native_group_paint(&inner_vector, &source)) }; let vector = unit_square_at(DVec2::new(4., 4.)); let mut builder = RunBuilder::new(&source, element_write_hashed::(), &[FieldWrite::of::(0)], 1).unwrap(); @@ -1707,9 +1729,10 @@ mod run_tests { set_paint_attribute_at(&mut top, 4, ATTR_FILL, List::new_from_element(Graphic::Color(Color::WHITE))); let legacy = { - let mut list = top.clone(); - for element in list.iter_element_values_mut() { - *element = map_groups_to_legacy(element); + let mut list = List::new(); + for item in top.clone().into_iter() { + let (element, attributes) = item.into_parts(); + list.push(Item::from_parts(map_groups_to_legacy(&element), attributes)); } push_lane_paint_into_interiors(&mut list); list.into_flattened_list::() @@ -1760,12 +1783,12 @@ mod graphic_is_opaque_tests { use super::*; - fn color_graphic(alpha: f64) -> Graphic { + fn color_graphic(alpha: f64) -> Graphic<'static> { let color = Color::from_rgbaf32(1., 0., 0., alpha as f32).unwrap(); Graphic::Color(color) } - fn gradient_graphic(gradient: GradientStops) -> Graphic { + fn gradient_graphic(gradient: GradientStops) -> Graphic<'static> { Graphic::Gradient(gradient) } diff --git a/node-graph/libraries/graphic-types/src/markers.rs b/node-graph/libraries/graphic-types/src/markers.rs index b827f3d1de..12bc494b99 100644 --- a/node-graph/libraries/graphic-types/src/markers.rs +++ b/node-graph/libraries/graphic-types/src/markers.rs @@ -12,14 +12,14 @@ use core_types::list::List; core_types::attribute! { /// Vector graphics object's filled area paint, a graphic list in the canonical paint form. /// An absent value means no fill. - pub Fill("fill"): Option<&List>; + pub Fill("fill"): Option<&List>>; /// Vector graphics object's stroke paint, a graphic list in the canonical paint form. /// An absent value means no stroke paint. - pub Stroke("stroke"): Option<&List>; + pub Stroke("stroke"): Option<&List>>; /// Snapshot of the upstream content that fed into a destructive merge (Boolean Operation, /// Rasterize, etc.), so the editor can still surface click targets for the original child /// layers after their content has been collapsed. - pub EditorMergedLayers("editor:merged_layers"): Option<&List>; + pub EditorMergedLayers("editor:merged_layers"): Option<&List>>; } pub const ATTR_FILL: &str = Fill::NAME; diff --git a/node-graph/libraries/rendering/src/render_ext.rs b/node-graph/libraries/rendering/src/render_ext.rs index 6f64c3de71..74da9ddcb3 100644 --- a/node-graph/libraries/rendering/src/render_ext.rs +++ b/node-graph/libraries/rendering/src/render_ext.rs @@ -234,7 +234,7 @@ impl RenderExt for Stroke { } } -impl RenderExt for List { +impl RenderExt for List> { type Output = String; fn render( diff --git a/node-graph/libraries/rendering/src/renderer.rs b/node-graph/libraries/rendering/src/renderer.rs index 1d9a335df6..6843ad9770 100644 --- a/node-graph/libraries/rendering/src/renderer.rs +++ b/node-graph/libraries/rendering/src/renderer.rs @@ -465,10 +465,10 @@ pub struct RenderMetadata { pub vector_data: HashMap>, /// Per-layer `ATTR_FILL` row attribute, exposed so message handlers can read it. #[cfg_attr(feature = "serde", serde(skip))] - pub fill_attributes: HashMap>>, + pub fill_attributes: HashMap>>>, /// Per-layer `ATTR_STROKE` row attribute, exposed so message handlers can read it. #[cfg_attr(feature = "serde", serde(skip))] - pub stroke_attributes: HashMap>>, + pub stroke_attributes: HashMap>>>, pub backgrounds: Vec, } @@ -549,7 +549,7 @@ pub trait Render: BoundingBox + RenderComplexity { fn new_ids_from_hash(&mut self, _reference: Option) {} } -impl Render for Graphic { +impl Render for Graphic<'_> { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { match self { Graphic::Graphic(list) => list.render_svg(render, render_params), @@ -676,7 +676,7 @@ fn collect_element_metadata<'a>( /// bare typed run serves its lane-0 transform (and source id for vectors) as /// the layer's local transform, matching the typed list the conversion made. fn collect_group_row_metadata(group: &Group, metadata: &mut RenderMetadata, element_id: NodeId) { - fn lane_zero_transform(item: &core_types::record::GroupItem) -> Option { + fn lane_zero_transform(item: &core_types::record::GroupItem) -> Option { RunView::::new(item).map(|run| run.attr::(0)) } @@ -833,7 +833,7 @@ fn read_artboard_attributes(source: &S, index: usize) -> (DVec2, (location, dimensions, background, clip) } -fn render_artboard_svg>(source: &S, render: &mut SvgRender, render_params: &RenderParams) { +fn render_artboard_svg<'a, S: LaneSource>>(source: &S, render: &mut SvgRender, render_params: &RenderParams) { for index in 0..source.lane_count() { let Some(content) = source.element(index).map(Artboard::as_graphic_list) else { continue }; let (location, dimensions, background, clip) = read_artboard_attributes(source, index); @@ -889,7 +889,7 @@ fn render_artboard_svg>(source: &S, render: &m } } -fn render_artboard_vello>(source: &S, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { +fn render_artboard_vello<'a, S: LaneSource>>(source: &S, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { use vello::peniko; for index in 0..source.lane_count() { @@ -921,7 +921,7 @@ fn render_artboard_vello>(source: &S, scene: & } } -fn collect_artboard_metadata>(source: &S, metadata: &mut RenderMetadata, footprint: Footprint) { +fn collect_artboard_metadata<'a, S: LaneSource>>(source: &S, metadata: &mut RenderMetadata, footprint: Footprint) { for index in 0..source.lane_count() { let Some(content) = source.element(index).map(Artboard::as_graphic_list) else { continue }; let (location, dimensions, _background, clip) = read_artboard_attributes(source, index); @@ -947,7 +947,7 @@ fn collect_artboard_metadata>(source: &S, meta } } -fn add_artboard_upstream_click_targets>(source: &S, click_targets: &mut Vec) { +fn add_artboard_upstream_click_targets<'a, S: LaneSource>>(source: &S, click_targets: &mut Vec) { for index in 0..source.lane_count() { let dimensions: DVec2 = source.attr::(index); let subpath_rectangle = Subpath::new_rectangle(DVec2::ZERO, dimensions); @@ -955,7 +955,7 @@ fn add_artboard_upstream_click_targets>(source } } -impl Render for List { +impl Render for List> { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { render_artboard_svg(self, render, render_params) } @@ -977,11 +977,11 @@ impl Render for List { } } -fn render_graphic_svg>(source: &S, render: &mut SvgRender, render_params: &RenderParams) { +fn render_graphic_svg<'e, S: LaneSource>>(source: &S, render: &mut SvgRender, render_params: &RenderParams) { render_graphic_svg_with(source, PaintReach::NONE, render, render_params) } -fn render_graphic_svg_with<'a, S: LaneSource>(source: &'a S, inherited: PaintReach<'a>, render: &mut SvgRender, render_params: &RenderParams) { +fn render_graphic_svg_with<'a, 'e, S: LaneSource>>(source: &'a S, inherited: PaintReach<'a>, render: &mut SvgRender, render_params: &RenderParams) { let paint_columns = PaintColumns::new(source); let mut mask_state = None; @@ -1039,11 +1039,11 @@ fn render_graphic_svg_with<'a, S: LaneSource>(source: &'a S, } } -fn render_graphic_vello>(source: &S, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { +fn render_graphic_vello<'e, S: LaneSource>>(source: &S, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { render_graphic_vello_with(source, PaintReach::NONE, scene, transform, context, render_params) } -fn render_graphic_vello_with<'a, S: LaneSource>(source: &'a S, inherited: PaintReach<'a>, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { +fn render_graphic_vello_with<'a, 'e, S: LaneSource>>(source: &'a S, inherited: PaintReach<'a>, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { let paint_columns = PaintColumns::new(source); let mut mask_element_and_transform = None; @@ -1123,11 +1123,11 @@ fn render_graphic_vello_with<'a, S: LaneSource>(source: &'a S } } -fn collect_graphic_metadata>(source: &S, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option) { +fn collect_graphic_metadata<'e, S: LaneSource>>(source: &S, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option) { collect_graphic_metadata_with(source, PaintReach::NONE, metadata, footprint, element_id) } -fn collect_graphic_metadata_with<'a, S: LaneSource>(source: &'a S, inherited: PaintReach<'a>, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option) { +fn collect_graphic_metadata_with<'a, 'e, S: LaneSource>>(source: &'a S, inherited: PaintReach<'a>, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option) { let paint_columns = PaintColumns::new(source); for index in 0..source.lane_count() { let item_transform: DAffine2 = source.attr::(index); @@ -1178,11 +1178,11 @@ fn collect_graphic_metadata_with<'a, S: LaneSource>(source: & } } -fn add_graphic_upstream_click_targets>(source: &S, click_targets: &mut Vec) { +fn add_graphic_upstream_click_targets<'e, S: LaneSource>>(source: &S, click_targets: &mut Vec) { add_graphic_upstream_click_targets_with(source, PaintReach::NONE, click_targets) } -fn add_graphic_upstream_click_targets_with<'a, S: LaneSource>(source: &'a S, inherited: PaintReach<'a>, click_targets: &mut Vec) { +fn add_graphic_upstream_click_targets_with<'a, 'e, S: LaneSource>>(source: &'a S, inherited: PaintReach<'a>, click_targets: &mut Vec) { let paint_columns = PaintColumns::new(source); for index in 0..source.lane_count() { let item_transform: DAffine2 = source.attr::(index); @@ -1200,11 +1200,11 @@ fn add_graphic_upstream_click_targets_with<'a, S: LaneSource> } } -fn add_graphic_upstream_outline_targets>(source: &S, outlines: &mut Vec) { +fn add_graphic_upstream_outline_targets<'e, S: LaneSource>>(source: &S, outlines: &mut Vec) { add_graphic_upstream_outline_targets_with(source, PaintReach::NONE, outlines) } -fn add_graphic_upstream_outline_targets_with<'a, S: LaneSource>(source: &'a S, inherited: PaintReach<'a>, outlines: &mut Vec) { +fn add_graphic_upstream_outline_targets_with<'a, 'e, S: LaneSource>>(source: &'a S, inherited: PaintReach<'a>, outlines: &mut Vec) { let paint_columns = PaintColumns::new(source); for index in 0..source.lane_count() { let item_transform: DAffine2 = source.attr::(index); @@ -1222,11 +1222,11 @@ fn add_graphic_upstream_outline_targets_with<'a, S: LaneSource>(source: &S) -> bool { +fn graphic_contains_artboard<'e, S: LaneSource>>(source: &S) -> bool { (0..source.lane_count()).any(|index| source.element(index).is_some_and(|element| element.contains_artboard())) } -impl Render for List { +impl Render for List> { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { render_graphic_svg(self, render, render_params) } @@ -1794,10 +1794,10 @@ fn collect_vector_metadata>(source: &S, metadata if let std::collections::hash_map::Entry::Vacant(e) = metadata.vector_data.entry(element_id) { e.insert(Arc::new(element.clone())); - if let Some(fill_graphic) = paint_graphics::(source, index) { + if let Some(fill_graphic) = source.attr::(index).filter(|list| is_paint_present(list)) { metadata.fill_attributes.insert(element_id, Arc::new(fill_graphic.clone())); } - if let Some(stroke_graphic) = paint_graphics::(source, index) { + if let Some(stroke_graphic) = source.attr::(index).filter(|list| is_paint_present(list)) { metadata.stroke_attributes.insert(element_id, Arc::new(stroke_graphic.clone())); } } @@ -2594,7 +2594,7 @@ pub fn text_list_bounding_box>(source: &S, outer /// Like `List::thumbnail_bounding_box`, but lays out `Graphic::Text` items, which the `BoundingBox` trait reports as `None`. /// Used for layer thumbnails so text layers (whose content is a `List` wrapping the text) frame their content. -pub fn graphic_list_bounding_box>(source: &S, transform: DAffine2) -> RenderBoundingBox { +pub fn graphic_list_bounding_box<'e, S: LaneSource>>(source: &S, transform: DAffine2) -> RenderBoundingBox { let mut combined: Option<[DVec2; 2]> = None; let mut any_infinite = false; @@ -2843,7 +2843,7 @@ impl Render for List { } } -impl Render for RunView<'_, Graphic> { +impl Render for RunView<'_, Graphic<'_>> { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { render_graphic_svg(self, render, render_params) } @@ -2929,7 +2929,7 @@ impl Render for RunView<'_, GradientStops> { } } -impl Render for RunView<'_, Artboard> { +impl Render for RunView<'_, Artboard<'_>> { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { render_artboard_svg(self, render, render_params) } @@ -3033,7 +3033,7 @@ mod group_walk_tests { Vector::from_subpath(Subpath::::new_rectangle(corner, corner + DVec2::ONE)) } - fn color_paint() -> List { + fn color_paint() -> List> { List::new_from_element(Graphic::Color(Color::from_rgbaf32(0.8, 0.2, 0.33, 1.).unwrap())) }