Render root levels natively and remove the unconstructed stack form

This commit is contained in:
Dennis Kobert
2026-08-27 14:44:19 +00:00
parent 50911a6a25
commit 68a2fb2be3
9 changed files with 194 additions and 258 deletions

View File

@@ -39,7 +39,7 @@ pub fn create_artboard(
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
let content = core_types::list::List::new_from_element(Graphic::Group(core_types::record::Group {
row: None,
content: core_types::record::GroupContent::Run(item),
content: item,
}));
// Normalize so `location` is the top-left corner and `dimensions` are positive (allowing negative input

View File

@@ -462,7 +462,7 @@ pub fn wrap_graphic<T: Clone + Send + Sync + core_types::CacheHash + 'static>(
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
Ok(Graphic::Group(core_types::record::Group {
row: None,
content: core_types::record::GroupContent::Run(item),
content: item,
}))
}
@@ -532,7 +532,7 @@ pub fn to_graphic_typed<T: Clone + Send + Sync + core_types::CacheHash + 'static
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
Ok(Graphic::Group(core_types::record::Group {
row: None,
content: core_types::record::GroupContent::Run(item),
content: item,
}))
}

View File

@@ -13,40 +13,26 @@ use graphic_types::graphic::Graphic;
use raster_types::{CPU, Raster};
use vector_types::{GradientStop, GradientStops};
/// Whether the walk can descend into a group: every run holds `Graphic`
/// Whether the walk can descend into a group: the run holds `Graphic`
/// elements.
pub(crate) fn group_expands(group: &core_types::record::Group) -> bool {
match &group.content {
core_types::record::GroupContent::Run(item) => item.typed_lanes::<Graphic>().is_some(),
core_types::record::GroupContent::Stack(children) => children.iter().all(group_expands),
}
group.content.typed_lanes::<Graphic>().is_some()
}
pub(crate) fn group_leaf_count(group: &core_types::record::Group, fully_flatten: bool, depth: usize) -> usize {
match &group.content {
core_types::record::GroupContent::Run(item) => {
let lanes = item.typed_lanes::<Graphic>().expect("guarded by group_expands");
(0..lanes.len()).map(|lane| leaf_count(lanes.element_ref(lane), fully_flatten, depth + 1)).sum()
}
core_types::record::GroupContent::Stack(children) => children.iter().map(|child| group_leaf_count(child, fully_flatten, depth)).sum(),
}
let lanes = group.content.typed_lanes::<Graphic>().expect("guarded by group_expands");
(0..lanes.len()).map(|lane| leaf_count(lanes.element_ref(lane), fully_flatten, depth + 1)).sum()
}
pub(crate) fn group_locate(group: &core_types::record::Group, transform: DAffine2, fully_flatten: bool, depth: usize, remaining: &mut usize) -> Option<(Graphic, DAffine2)> {
match &group.content {
core_types::record::GroupContent::Run(item) => {
let lanes = item.typed_lanes::<Graphic>().expect("guarded by group_expands");
let offset = item.layout().offset_of(ATTR_TRANSFORM, 0);
(0..lanes.len()).find_map(|lane| {
// SAFETY: the offset comes from the item's own layout.
let lane_transform = offset.map(|offset| unsafe { item.lanes().get(lane).rec().read::<DAffine2>(offset) }).unwrap_or(DAffine2::IDENTITY);
locate(lanes.element_ref(lane), transform * lane_transform, fully_flatten, depth + 1, remaining)
})
}
core_types::record::GroupContent::Stack(children) => children
.iter()
.find_map(|child| group_locate(child, transform * graphic_types::graphic::group_row_transform(child), fully_flatten, depth, remaining)),
}
let item = &group.content;
let lanes = item.typed_lanes::<Graphic>().expect("guarded by group_expands");
let offset = item.layout().offset_of(ATTR_TRANSFORM, 0);
(0..lanes.len()).find_map(|lane| {
// SAFETY: the offset comes from the item's own layout.
let lane_transform = offset.map(|offset| unsafe { item.lanes().get(lane).rec().read::<DAffine2>(offset) }).unwrap_or(DAffine2::IDENTITY);
locate(lanes.element_ref(lane), transform * lane_transform, fully_flatten, depth + 1, remaining)
})
}
/// Leaf rows a graphic expands to: its children's counts when the walk
@@ -120,7 +106,7 @@ fn wrap(_: impl Ctx, content: IList<Graphic>) -> Result<IList<Graphic>, Interrup
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
Ok(Graphic::Group(core_types::record::Group {
row: None,
content: core_types::record::GroupContent::Run(item),
content: item,
}))
}
@@ -728,9 +714,7 @@ mod tests {
panic!("expected a group element");
};
assert!(group.row.is_none());
let record::GroupContent::Run(item) = &group.content else {
panic!("expected a single run");
};
let item = &group.content;
assert_eq!(item.len(), 2);
let lanes = item.typed_lanes::<Graphic>().expect("the run holds the adopted graphic lanes");
let offset = item.layout().offset_of(ATTR_TRANSFORM, 0).unwrap();
@@ -770,9 +754,7 @@ mod tests {
let Graphic::Group(group) = (unsafe { record::borrow_element::<Graphic>(record::Rec::new(slot.as_ptr())) }) else {
panic!("the replay restores the group element");
};
let record::GroupContent::Run(item) = &group.content else {
panic!("expected a single run");
};
let item = &group.content;
assert_eq!(item.len(), 2);
let lanes = item.typed_lanes::<Graphic>().expect("the run holds the adopted graphic lanes");
let offset = item.layout().offset_of(ATTR_TRANSFORM, 0).unwrap();

View File

@@ -76,25 +76,25 @@ fn render_intermediate<T: 'static + Render + WasmNotSend + Send + Sync>(
}
/// The leveled form of `render_intermediate`: the wire's records materialize
/// into a run and render through the legacy conversion.
/// into a run, which renders directly.
#[node_macro::node(category(""))]
fn render_intermediate_leveled<T: Clone + Send + Sync + core_types::CacheHash + 'static>(
ctx: impl Ctx + ExtractVarArgs + ExtractIndex + InjectIndex + Copy,
#[implementations(Artboard, Graphic, Vector, Raster<CPU>, Color, GradientStops, String)] data: IList<T>,
) -> Result<RenderIntermediate, Interrupt>
where
List<T>: Render,
for<'a> core_types::record::RunView<'a, T>: Render,
{
// SAFETY: a materialized input's frames are arena-resident.
let item = unsafe { core_types::record::GroupItem::from_resident(data.batch()) };
let data = graphic_types::graphic::run_to_render_list::<T>(&item).expect("the run holds the row's element type");
let run = core_types::record::RunView::<T>::new(&item).expect("the run holds the row's element type");
let render_params = ctx
.vararg(0)
.expect("Did not find var args")
.downcast_ref::<RenderParams>()
.expect("Downcasting render params yielded invalid type");
Ok(intermediate_of(&data, render_params))
Ok(intermediate_of(&run, render_params))
}
#[node_macro::node(category(""))]