mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 01:38:11 +08:00
Declare which index levels a node reads and nullify the rest
This commit is contained in:
@@ -505,7 +505,13 @@ impl ProtoNetwork {
|
|||||||
|
|
||||||
let (extract, inject, own_deps) = {
|
let (extract, inject, own_deps) = {
|
||||||
let dependencies = &self.nodes[node_index].1.context_features;
|
let dependencies = &self.nodes[node_index].1.context_features;
|
||||||
let own_deps = ContextModification::from_sources(dependencies.extract, dependencies.sources());
|
// Documents predating the level mask deserialize as all-levels, so a
|
||||||
|
// node that declares no index read must not contribute one.
|
||||||
|
let index_levels = match dependencies.extract.contains(core_types::context::ContextFeatures::INDEX) {
|
||||||
|
true => dependencies.index_levels,
|
||||||
|
false => core_types::context::IndexLevels::empty(),
|
||||||
|
};
|
||||||
|
let own_deps = ContextModification::from_sources(dependencies.extract, dependencies.sources()).with_index_levels(index_levels);
|
||||||
(dependencies.extract, dependencies.inject, own_deps)
|
(dependencies.extract, dependencies.inject, own_deps)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ pub trait ExtractPointerPosition {
|
|||||||
pub trait ExtractPosition {
|
pub trait ExtractPosition {
|
||||||
fn try_position(&self) -> Option<impl Iterator<Item = DVec2>>;
|
fn try_position(&self) -> Option<impl Iterator<Item = DVec2>>;
|
||||||
}
|
}
|
||||||
pub trait ExtractIndex {
|
/// The level sentinel for a reader whose level is not known at compile time,
|
||||||
|
/// so every level of the index chain must survive nullification.
|
||||||
|
pub const ALL_INDEX_LEVELS: u8 = u8::MAX;
|
||||||
|
|
||||||
|
pub trait ExtractIndices {
|
||||||
fn try_index(&self) -> Option<impl Iterator<Item = usize>>;
|
fn try_index(&self) -> Option<impl Iterator<Item = usize>>;
|
||||||
fn innermost_index(&self) -> u64 {
|
fn innermost_index(&self) -> u64 {
|
||||||
self.try_index().and_then(|mut indices| indices.next()).unwrap_or(0) as u64
|
self.try_index().and_then(|mut indices| indices.next()).unwrap_or(0) as u64
|
||||||
@@ -52,6 +56,18 @@ pub trait ExtractIndex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `LEVEL` is the level of the index chain a node reads, counted outwards from
|
||||||
|
/// the innermost. Declaring it narrows the cache key to that level, and reading
|
||||||
|
/// through `index` keeps the declaration and the read from drifting apart.
|
||||||
|
/// Repeat the bound to declare several, then spell the level at each read.
|
||||||
|
pub trait ExtractIndex<const LEVEL: u8 = 0>: ExtractIndices {
|
||||||
|
fn index(&self) -> u64 {
|
||||||
|
self.try_index().and_then(|mut levels| levels.nth(LEVEL as usize)).unwrap_or(0) as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExtractIndices, const LEVEL: u8> ExtractIndex<LEVEL> for T {}
|
||||||
pub trait ExtractVarArgs {
|
pub trait ExtractVarArgs {
|
||||||
// TODO: Consider returning a slice or something like that
|
// TODO: Consider returning a slice or something like that
|
||||||
|
|
||||||
@@ -154,7 +170,8 @@ pub enum ContextFeature {
|
|||||||
ExtractAnimationTime,
|
ExtractAnimationTime,
|
||||||
ExtractPointerPosition,
|
ExtractPointerPosition,
|
||||||
ExtractPosition,
|
ExtractPosition,
|
||||||
ExtractIndex,
|
/// Carries the declared level, counted outwards from the innermost.
|
||||||
|
ExtractIndex(u8),
|
||||||
ExtractVarArgs,
|
ExtractVarArgs,
|
||||||
InjectFootprint,
|
InjectFootprint,
|
||||||
InjectRealTime,
|
InjectRealTime,
|
||||||
@@ -187,6 +204,93 @@ impl graphene_hash::CacheHash for ContextFeatures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Which levels of the index chain survive nullification, as a mask over levels
|
||||||
|
/// counted outwards from the innermost. Levels past 31 collapse to "all".
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, dyn_any::DynAny)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
pub struct IndexLevels(u32);
|
||||||
|
|
||||||
|
impl IndexLevels {
|
||||||
|
pub const fn empty() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn all() -> Self {
|
||||||
|
Self(u32::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The innermost level alone, which is what every reader but `read_index`
|
||||||
|
/// addresses, so documents predating the mask migrate to it.
|
||||||
|
pub const fn innermost() -> Self {
|
||||||
|
Self(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn is_empty(self) -> bool {
|
||||||
|
self.0 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_level(self, level: u8) -> Self {
|
||||||
|
match level {
|
||||||
|
ALL_INDEX_LEVELS => Self::all(),
|
||||||
|
level if (level as u32) < u32::BITS => Self(self.0 | 1 << level),
|
||||||
|
_ => Self::all(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn contains(self, other: Self) -> bool {
|
||||||
|
self.0 & other.0 == other.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn is_all(self) -> bool {
|
||||||
|
self.0 == u32::MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Levels at or past `u32::BITS` are only readable through the all-levels
|
||||||
|
/// sentinel, so they count as declared whenever the mask is saturated.
|
||||||
|
pub const fn contains_level(self, level: usize) -> bool {
|
||||||
|
match level < u32::BITS as usize {
|
||||||
|
true => self.0 & 1 << level != 0,
|
||||||
|
false => self.is_all(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Zeroes every index level outside `levels`, preserving the chain's depth so
|
||||||
|
/// outer levels keep their positions for readers that do address them. Returns
|
||||||
|
/// `None` only when the arena is exhausted.
|
||||||
|
pub fn nullify_index_levels<'s>(head: IndexLink<'s>, levels: IndexLevels, arena: &'s crate::arena::Arena) -> Option<IndexLink<'s>> {
|
||||||
|
if levels.is_all() {
|
||||||
|
return Some(head);
|
||||||
|
}
|
||||||
|
let indices: Vec<u64> = core::iter::successors(Some(&head), |link| link.outer).map(|link| link.index).collect();
|
||||||
|
let masked = |level: usize| match levels.contains_level(level) {
|
||||||
|
true => indices[level],
|
||||||
|
false => 0,
|
||||||
|
};
|
||||||
|
if (0..indices.len()).all(|level| levels.contains_level(level) || indices[level] == 0) {
|
||||||
|
return Some(head);
|
||||||
|
}
|
||||||
|
// Built outermost inwards so each link can reference the one already placed.
|
||||||
|
let mut outer: Option<&'s IndexLink<'s>> = None;
|
||||||
|
for level in (1..indices.len()).rev() {
|
||||||
|
let link = IndexLink { index: masked(level), outer };
|
||||||
|
outer = Some(arena.alloc(link)?.0);
|
||||||
|
}
|
||||||
|
Some(IndexLink { index: masked(0), outer })
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::BitOrAssign for IndexLevels {
|
||||||
|
fn bitor_assign(&mut self, other: Self) {
|
||||||
|
self.0 |= other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl graphene_hash::CacheHash for IndexLevels {
|
||||||
|
fn cache_hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
core::hash::Hash::hash(self, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ContextFeatures {
|
impl ContextFeatures {
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
@@ -211,13 +315,31 @@ impl ContextFeatures {
|
|||||||
pub struct ContextDependencies {
|
pub struct ContextDependencies {
|
||||||
pub extract: ContextFeatures,
|
pub extract: ContextFeatures,
|
||||||
pub inject: ContextFeatures,
|
pub inject: ContextFeatures,
|
||||||
|
/// Which index levels the node reads; empty means it reads none. Documents
|
||||||
|
/// written before the field existed default to the whole chain.
|
||||||
|
#[cfg_attr(feature = "serde", serde(default = "IndexLevels::innermost"))]
|
||||||
|
pub index_levels: IndexLevels,
|
||||||
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_sorted_sources"))]
|
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_sorted_sources"))]
|
||||||
sources: Vec<SourceId>,
|
sources: Vec<SourceId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextDependencies {
|
impl ContextDependencies {
|
||||||
pub fn new(extract: ContextFeatures, inject: ContextFeatures) -> Self {
|
pub fn new(extract: ContextFeatures, inject: ContextFeatures) -> Self {
|
||||||
Self { extract, inject, sources: Vec::new() }
|
let index_levels = match extract.contains(ContextFeatures::INDEX) {
|
||||||
|
true => IndexLevels::innermost(),
|
||||||
|
false => IndexLevels::empty(),
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
extract,
|
||||||
|
inject,
|
||||||
|
index_levels,
|
||||||
|
sources: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_index_levels(mut self, index_levels: IndexLevels) -> Self {
|
||||||
|
self.index_levels = index_levels;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sources(&self) -> &[SourceId] {
|
pub fn sources(&self) -> &[SourceId] {
|
||||||
@@ -239,6 +361,9 @@ impl ContextDependencies {
|
|||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct ContextModification {
|
pub struct ContextModification {
|
||||||
pub features: ContextFeatures,
|
pub features: ContextFeatures,
|
||||||
|
/// Which index levels survive; consulted only when `features` keeps `INDEX`.
|
||||||
|
#[cfg_attr(feature = "serde", serde(default = "IndexLevels::innermost"))]
|
||||||
|
pub index_levels: IndexLevels,
|
||||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_sorted_sources"))]
|
#[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_sorted_sources"))]
|
||||||
sources: Vec<SourceId>,
|
sources: Vec<SourceId>,
|
||||||
}
|
}
|
||||||
@@ -253,10 +378,23 @@ impl ContextModification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_sources(features: ContextFeatures, sources: &[SourceId]) -> Self {
|
pub fn from_sources(features: ContextFeatures, sources: &[SourceId]) -> Self {
|
||||||
let mut modification = Self { features, sources: Vec::new() };
|
let index_levels = match features.contains(ContextFeatures::INDEX) {
|
||||||
|
true => IndexLevels::all(),
|
||||||
|
false => IndexLevels::empty(),
|
||||||
|
};
|
||||||
|
let mut modification = Self {
|
||||||
|
features,
|
||||||
|
index_levels,
|
||||||
|
sources: Vec::new(),
|
||||||
|
};
|
||||||
modification.add_sources(sources);
|
modification.add_sources(sources);
|
||||||
modification
|
modification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_index_levels(mut self, index_levels: IndexLevels) -> Self {
|
||||||
|
self.index_levels = index_levels;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restores the sorted-and-deduplicated invariant that `contains` and `difference`
|
/// Restores the sorted-and-deduplicated invariant that `contains` and `difference`
|
||||||
@@ -273,6 +411,7 @@ fn deserialize_sorted_sources<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
|||||||
impl core::ops::BitOrAssign<&ContextModification> for ContextModification {
|
impl core::ops::BitOrAssign<&ContextModification> for ContextModification {
|
||||||
fn bitor_assign(&mut self, other: &Self) {
|
fn bitor_assign(&mut self, other: &Self) {
|
||||||
self.features |= other.features;
|
self.features |= other.features;
|
||||||
|
self.index_levels |= other.index_levels;
|
||||||
merge_sorted_sources(&mut self.sources, &other.sources);
|
merge_sorted_sources(&mut self.sources, &other.sources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,13 +437,14 @@ impl core::ops::BitAndAssign<ContextFeatures> for ContextModification {
|
|||||||
impl ContextModification {
|
impl ContextModification {
|
||||||
pub fn contains(&self, other: &Self) -> bool {
|
pub fn contains(&self, other: &Self) -> bool {
|
||||||
debug_assert!(self.sources.is_sorted() && other.sources.is_sorted());
|
debug_assert!(self.sources.is_sorted() && other.sources.is_sorted());
|
||||||
self.features.contains(other.features) && other.sources.iter().all(|id| self.sources.binary_search(id).is_ok())
|
self.features.contains(other.features) && self.index_levels.contains(other.index_levels) && other.sources.iter().all(|id| self.sources.binary_search(id).is_ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn difference(&self, other: &Self) -> Self {
|
pub fn difference(&self, other: &Self) -> Self {
|
||||||
debug_assert!(other.sources.is_sorted());
|
debug_assert!(other.sources.is_sorted());
|
||||||
Self {
|
Self {
|
||||||
features: self.features.difference(other.features),
|
features: self.features.difference(other.features),
|
||||||
|
index_levels: self.index_levels,
|
||||||
sources: self.sources.iter().copied().filter(|id| other.sources.binary_search(id).is_err()).collect(),
|
sources: self.sources.iter().copied().filter(|id| other.sources.binary_search(id).is_err()).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,14 +466,18 @@ impl From<&[ContextFeature]> for ContextDependencies {
|
|||||||
fn from(features: &[ContextFeature]) -> Self {
|
fn from(features: &[ContextFeature]) -> Self {
|
||||||
let mut extract = ContextFeatures::empty();
|
let mut extract = ContextFeatures::empty();
|
||||||
let mut inject = ContextFeatures::empty();
|
let mut inject = ContextFeatures::empty();
|
||||||
|
let mut index_levels = IndexLevels::empty();
|
||||||
for feature in features {
|
for feature in features {
|
||||||
|
if let ContextFeature::ExtractIndex(level) = feature {
|
||||||
|
index_levels = index_levels.with_level(*level);
|
||||||
|
}
|
||||||
extract |= match feature {
|
extract |= match feature {
|
||||||
ContextFeature::ExtractFootprint => ContextFeatures::FOOTPRINT,
|
ContextFeature::ExtractFootprint => ContextFeatures::FOOTPRINT,
|
||||||
ContextFeature::ExtractRealTime => ContextFeatures::REAL_TIME,
|
ContextFeature::ExtractRealTime => ContextFeatures::REAL_TIME,
|
||||||
ContextFeature::ExtractAnimationTime => ContextFeatures::ANIMATION_TIME,
|
ContextFeature::ExtractAnimationTime => ContextFeatures::ANIMATION_TIME,
|
||||||
ContextFeature::ExtractPointerPosition => ContextFeatures::POINTER_POSITION,
|
ContextFeature::ExtractPointerPosition => ContextFeatures::POINTER_POSITION,
|
||||||
ContextFeature::ExtractPosition => ContextFeatures::POSITION,
|
ContextFeature::ExtractPosition => ContextFeatures::POSITION,
|
||||||
ContextFeature::ExtractIndex => ContextFeatures::INDEX,
|
ContextFeature::ExtractIndex(_) => ContextFeatures::INDEX,
|
||||||
ContextFeature::ExtractVarArgs => ContextFeatures::VARARGS,
|
ContextFeature::ExtractVarArgs => ContextFeatures::VARARGS,
|
||||||
_ => ContextFeatures::empty(),
|
_ => ContextFeatures::empty(),
|
||||||
};
|
};
|
||||||
@@ -348,7 +492,12 @@ impl From<&[ContextFeature]> for ContextDependencies {
|
|||||||
_ => ContextFeatures::empty(),
|
_ => ContextFeatures::empty(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Self { extract, inject, sources: Vec::new() }
|
Self {
|
||||||
|
extract,
|
||||||
|
inject,
|
||||||
|
index_levels,
|
||||||
|
sources: Vec::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +537,7 @@ impl<T: ExtractPosition + Sync> ExtractPosition for Option<T> {
|
|||||||
self.as_ref().and_then(|x| x.try_position())
|
self.as_ref().and_then(|x| x.try_position())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ExtractIndex> ExtractIndex for Option<T> {
|
impl<T: ExtractIndices> ExtractIndices for Option<T> {
|
||||||
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
||||||
self.as_ref().and_then(|x| x.try_index())
|
self.as_ref().and_then(|x| x.try_index())
|
||||||
}
|
}
|
||||||
@@ -446,7 +595,7 @@ impl<T: ExtractPosition + Sync> ExtractPosition for Arc<T> {
|
|||||||
(**self).try_position()
|
(**self).try_position()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ExtractIndex> ExtractIndex for Arc<T> {
|
impl<T: ExtractIndices> ExtractIndices for Arc<T> {
|
||||||
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
||||||
(**self).try_index()
|
(**self).try_index()
|
||||||
}
|
}
|
||||||
@@ -739,7 +888,7 @@ pub trait DeriveCtx {
|
|||||||
fn with_varargs<'s>(&'s self, varargs: &'s VarArgLink<'s>) -> Derived<'s, Self>;
|
fn with_varargs<'s>(&'s self, varargs: &'s VarArgLink<'s>) -> Derived<'s, Self>;
|
||||||
fn with_position<'s>(&'s self, position: &'s PositionLink<'s>) -> Derived<'s, Self>;
|
fn with_position<'s>(&'s self, position: &'s PositionLink<'s>) -> Derived<'s, Self>;
|
||||||
fn with_scope<'s>(&'s self, scope: &'s EvalScope<'s>) -> Derived<'s, Self>;
|
fn with_scope<'s>(&'s self, scope: &'s EvalScope<'s>) -> Derived<'s, Self>;
|
||||||
fn nullified<'s>(&'s self, keep: ContextFeatures, scope: &'s EvalScope<'s>) -> Derived<'s, Self>;
|
fn nullified<'s>(&'s self, keep: ContextFeatures, index: IndexLink<'s>, scope: &'s EvalScope<'s>) -> Derived<'s, Self>;
|
||||||
|
|
||||||
fn modify_footprint(&self, modify: impl FnOnce(&mut Footprint)) -> ModifiedFootprint<'_, Self>
|
fn modify_footprint(&self, modify: impl FnOnce(&mut Footprint)) -> ModifiedFootprint<'_, Self>
|
||||||
where
|
where
|
||||||
@@ -850,7 +999,7 @@ impl ExtractPointerPosition for CtxSnapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtractIndex for CtxSnapshot {
|
impl ExtractIndices for CtxSnapshot {
|
||||||
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
||||||
self.index.as_ref().map(|levels| levels.iter().copied())
|
self.index.as_ref().map(|levels| levels.iter().copied())
|
||||||
}
|
}
|
||||||
@@ -975,15 +1124,12 @@ impl<'a> ContextImpl<'a> {
|
|||||||
ContextImpl { position: Some(position), ..*self }
|
ContextImpl { position: Some(position), ..*self }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nullified<'s>(&self, keep: ContextFeatures, scope: &'s EvalScope<'s>) -> ContextImpl<'s>
|
pub fn nullified<'s>(&self, keep: ContextFeatures, index: IndexLink<'s>, scope: &'s EvalScope<'s>) -> ContextImpl<'s>
|
||||||
where
|
where
|
||||||
'a: 's,
|
'a: 's,
|
||||||
{
|
{
|
||||||
ContextImpl {
|
ContextImpl {
|
||||||
index: match keep.contains(ContextFeatures::INDEX) {
|
index,
|
||||||
true => self.index,
|
|
||||||
false => IndexLink { index: 0, outer: None },
|
|
||||||
},
|
|
||||||
position: self.position.filter(|_| keep.contains(ContextFeatures::POSITION)),
|
position: self.position.filter(|_| keep.contains(ContextFeatures::POSITION)),
|
||||||
varargs: self.varargs.filter(|_| keep.contains(ContextFeatures::VARARGS)),
|
varargs: self.varargs.filter(|_| keep.contains(ContextFeatures::VARARGS)),
|
||||||
footprint: self.footprint.filter(|_| keep.contains(ContextFeatures::FOOTPRINT)),
|
footprint: self.footprint.filter(|_| keep.contains(ContextFeatures::FOOTPRINT)),
|
||||||
@@ -1060,7 +1206,7 @@ impl ExtractPointerPosition for ContextImpl<'_> {
|
|||||||
self.scope.pointer_position
|
self.scope.pointer_position
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ExtractIndex for ContextImpl<'_> {
|
impl ExtractIndices for ContextImpl<'_> {
|
||||||
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
|
||||||
Some(std::iter::successors(Some(&self.index), |link| link.outer).map(|link| link.index as usize))
|
Some(std::iter::successors(Some(&self.index), |link| link.outer).map(|link| link.index as usize))
|
||||||
}
|
}
|
||||||
@@ -1161,8 +1307,8 @@ impl<'a> DeriveCtx for ContextImpl<'a> {
|
|||||||
ContextImpl::with_scope(self, scope)
|
ContextImpl::with_scope(self, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nullified<'s>(&'s self, keep: ContextFeatures, scope: &'s EvalScope<'s>) -> ContextImpl<'s> {
|
fn nullified<'s>(&'s self, keep: ContextFeatures, index: IndexLink<'s>, scope: &'s EvalScope<'s>) -> ContextImpl<'s> {
|
||||||
ContextImpl::nullified(self, keep, scope)
|
ContextImpl::nullified(self, keep, index, scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ pub(crate) struct Input {
|
|||||||
pub(crate) pat_ident: PatIdent,
|
pub(crate) pat_ident: PatIdent,
|
||||||
pub(crate) ty: Type,
|
pub(crate) ty: Type,
|
||||||
pub(crate) implementations: Punctuated<Type, Comma>,
|
pub(crate) implementations: Punctuated<Type, Comma>,
|
||||||
pub(crate) context_features: Vec<Ident>,
|
pub(crate) context_features: Vec<ContextFeatureDecl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Implementation {
|
impl Parse for Implementation {
|
||||||
@@ -868,8 +868,46 @@ fn parse_read_tuple(pat_tuple: &syn::PatTuple, ty: &Type, attrs: &[Attribute], i
|
|||||||
Ok(field)
|
Ok(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A declared context feature; `ExtractIndex` carries the index level it reads.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub(crate) struct ContextFeatureDecl {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) level: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextFeatureDecl {
|
||||||
|
pub(crate) fn new(ident: Ident) -> Self {
|
||||||
|
Self { ident, level: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl quote::ToTokens for ContextFeatureDecl {
|
||||||
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
|
let ident = &self.ident;
|
||||||
|
match self.level {
|
||||||
|
Some(level) => tokens.extend(quote::quote!(#ident(#level))),
|
||||||
|
None => ident.to_tokens(tokens),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The level of an `ExtractIndex<N>` bound, defaulting to the innermost.
|
||||||
|
fn parse_index_level(segment: &syn::PathSegment) -> u8 {
|
||||||
|
let syn::PathArguments::AngleBracketed(arguments) = &segment.arguments else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
for argument in &arguments.args {
|
||||||
|
if let syn::GenericArgument::Const(syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(int), .. })) = argument
|
||||||
|
&& let Ok(level) = int.base10_parse::<u8>()
|
||||||
|
{
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse context feature identifiers from the trait bounds of a context parameter.
|
/// Parse context feature identifiers from the trait bounds of a context parameter.
|
||||||
fn parse_context_feature_idents(ty: &Type) -> Vec<Ident> {
|
fn parse_context_feature_idents(ty: &Type) -> Vec<ContextFeatureDecl> {
|
||||||
let mut features = Vec::new();
|
let mut features = Vec::new();
|
||||||
|
|
||||||
// Check if this is an impl trait (impl Ctx + ...)
|
// Check if this is an impl trait (impl Ctx + ...)
|
||||||
@@ -879,12 +917,20 @@ fn parse_context_feature_idents(ty: &Type) -> Vec<Ident> {
|
|||||||
// Extract the last segment of the trait path
|
// Extract the last segment of the trait path
|
||||||
if let Some(segment) = path.segments.last() {
|
if let Some(segment) = path.segments.last() {
|
||||||
match segment.ident.to_string().as_str() {
|
match segment.ident.to_string().as_str() {
|
||||||
|
"ExtractIndex" => features.push(ContextFeatureDecl {
|
||||||
|
ident: segment.ident.clone(),
|
||||||
|
level: Some(parse_index_level(segment)),
|
||||||
|
}),
|
||||||
|
// Reading the chain without a statically known level keeps every level.
|
||||||
|
"ExtractIndices" => features.push(ContextFeatureDecl {
|
||||||
|
ident: format_ident!("ExtractIndex"),
|
||||||
|
level: Some(u8::MAX),
|
||||||
|
}),
|
||||||
"ExtractFootprint"
|
"ExtractFootprint"
|
||||||
| "ExtractRealTime"
|
| "ExtractRealTime"
|
||||||
| "ExtractAnimationTime"
|
| "ExtractAnimationTime"
|
||||||
| "ExtractPointerPosition"
|
| "ExtractPointerPosition"
|
||||||
| "ExtractPosition"
|
| "ExtractPosition"
|
||||||
| "ExtractIndex"
|
|
||||||
| "ExtractVarArgs"
|
| "ExtractVarArgs"
|
||||||
| "InjectFootprint"
|
| "InjectFootprint"
|
||||||
| "InjectRealTime"
|
| "InjectRealTime"
|
||||||
@@ -892,7 +938,7 @@ fn parse_context_feature_idents(ty: &Type) -> Vec<Ident> {
|
|||||||
| "InjectPointerPosition"
|
| "InjectPointerPosition"
|
||||||
| "InjectPosition"
|
| "InjectPosition"
|
||||||
| "InjectVarArgs" => {
|
| "InjectVarArgs" => {
|
||||||
features.push(segment.ident.clone());
|
features.push(ContextFeatureDecl::new(segment.ident.clone()));
|
||||||
}
|
}
|
||||||
// InjectIndex stays undeclared: a record node's injection
|
// InjectIndex stays undeclared: a record node's injection
|
||||||
// re-addresses lanes derived from the incoming index, so it
|
// re-addresses lanes derived from the incoming index, so it
|
||||||
@@ -1605,7 +1651,7 @@ mod tests {
|
|||||||
pat_ident: pat_ident("_"),
|
pat_ident: pat_ident("_"),
|
||||||
ty: parse_quote!(impl Ctx + ExtractFootprint),
|
ty: parse_quote!(impl Ctx + ExtractFootprint),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
context_features: vec![format_ident!("ExtractFootprint")],
|
context_features: vec![ContextFeatureDecl::new(format_ident!("ExtractFootprint"))],
|
||||||
},
|
},
|
||||||
output_type: parse_quote!(Vector),
|
output_type: parse_quote!(Vector),
|
||||||
output_depth: 0,
|
output_depth: 0,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use core_types::gpoll::{Extent, GPoll, GraphError, Interrupt};
|
use core_types::gpoll::{Extent, GPoll, GraphError, Interrupt};
|
||||||
use core_types::list::List;
|
use core_types::list::List;
|
||||||
use core_types::{Color, ExtractVarArgs};
|
use core_types::{Color, ExtractVarArgs};
|
||||||
use core_types::{Ctx, ExtractIndex, ExtractPosition};
|
use core_types::{Ctx, ExtractIndex, ExtractIndices, ExtractPosition};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graphic_types::vector_types::GradientStops;
|
use graphic_types::vector_types::GradientStops;
|
||||||
use graphic_types::{Graphic, Vector};
|
use graphic_types::{Graphic, Vector};
|
||||||
@@ -64,7 +64,7 @@ fn vararg_lanes<T: 'static>(ctx: &impl ExtractVarArgs, level: u8) -> GPoll<Exten
|
|||||||
|
|
||||||
fn vararg_element<T: Clone + 'static>(ctx: &(impl ExtractVarArgs + ExtractIndex)) -> Result<T, Interrupt> {
|
fn vararg_element<T: Clone + 'static>(ctx: &(impl ExtractVarArgs + ExtractIndex)) -> Result<T, Interrupt> {
|
||||||
vararg_list::<T>(ctx)
|
vararg_list::<T>(ctx)
|
||||||
.and_then(|list| list.element(ctx.innermost_index() as usize))
|
.and_then(|list| list.element(ctx.index() as usize))
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| GraphError::new("vararg row addressed past its items").into())
|
.ok_or_else(|| GraphError::new("vararg row addressed past its items").into())
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,9 @@ fn read_position(
|
|||||||
/// Nested loops can enable 2D or higher-dimensional iteration by using the *Loop Level* parameter to read the index from outer levels of loops.
|
/// Nested loops can enable 2D or higher-dimensional iteration by using the *Loop Level* parameter to read the index from outer levels of loops.
|
||||||
#[node_macro::node(category("Context"), path(core_types::vector))]
|
#[node_macro::node(category("Context"), path(core_types::vector))]
|
||||||
fn read_index(
|
fn read_index(
|
||||||
ctx: impl Ctx + ExtractIndex,
|
// `loop_level` is a runtime input, so no level is statically known and the
|
||||||
|
// whole chain has to survive nullification.
|
||||||
|
ctx: impl Ctx + ExtractIndices,
|
||||||
_primary: (),
|
_primary: (),
|
||||||
/// The number of nested loops to traverse outwards (from the innermost loop) to get the index from. The most upstream loop is level 0, and downstream loops add levels.
|
/// The number of nested loops to traverse outwards (from the innermost loop) to get the index from. The most upstream loop is level 0, and downstream loops add levels.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use core_types::context::{ContextModification, Ctx, DeriveCtx};
|
use core_types::context::{ContextFeatures, ContextModification, Ctx, DeriveCtx, IndexLink, nullify_index_levels};
|
||||||
use core_types::gpoll::Interrupt;
|
use core_types::gpoll::{ErrorKind, GraphError, Interrupt};
|
||||||
|
|
||||||
/// Filters out what should be unused components of the context based on the specified requirements.
|
/// Filters out what should be unused components of the context based on the specified requirements.
|
||||||
/// This node is inserted by the compiler to "zero out" unused context components.
|
/// This node is inserted by the compiler to "zero out" unused context components.
|
||||||
@@ -12,5 +12,15 @@ fn context_modification<T>(
|
|||||||
modification: ContextModification,
|
modification: ContextModification,
|
||||||
) -> Result<T, Interrupt> {
|
) -> Result<T, Interrupt> {
|
||||||
let scope = ctx.scope().nullified(modification.features, Some(modification.sources()));
|
let scope = ctx.scope().nullified(modification.features, Some(modification.sources()));
|
||||||
value.eval(&ctx.nullified(modification.features, &scope))
|
let exhausted = || {
|
||||||
|
Interrupt::from(GraphError {
|
||||||
|
kind: ErrorKind::ArenaExhausted,
|
||||||
|
trace: Vec::new(),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let index = match modification.features.contains(ContextFeatures::INDEX) {
|
||||||
|
true => nullify_index_levels(ctx.index_head(), modification.index_levels, scope.arena()).ok_or_else(exhausted)?,
|
||||||
|
false => IndexLink { index: 0, outer: None },
|
||||||
|
};
|
||||||
|
value.eval(&ctx.nullified(modification.features, index, &scope))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ fn memoize<'e>(
|
|||||||
// keys with the lane normalized away.
|
// keys with the lane normalized away.
|
||||||
let leveled = content.layout().depth > 0;
|
let leveled = content.layout().depth > 0;
|
||||||
let lane = match leveled {
|
let lane = match leveled {
|
||||||
true => ctx.innermost_index() as usize,
|
true => ctx.index() as usize,
|
||||||
false => 0,
|
false => 0,
|
||||||
};
|
};
|
||||||
let key = match leveled {
|
let key = match leveled {
|
||||||
@@ -203,7 +203,7 @@ fn monitor<'e>(
|
|||||||
// computes lane by lane. Serving THIS lane out of that batch rather than
|
// computes lane by lane. Serving THIS lane out of that batch rather than
|
||||||
// evaluating the content separately is what keeps the cost linear: the extra
|
// evaluating the content separately is what keeps the cost linear: the extra
|
||||||
// eval would double the work under every enclosing monitor.
|
// eval would double the work under every enclosing monitor.
|
||||||
if content.layout().depth > 0 && ctx.innermost_index() == 0 {
|
if content.layout().depth > 0 && ctx.index() == 0 {
|
||||||
return match content.materialize_level(ctx, ctx.arena()) {
|
return match content.materialize_level(ctx, ctx.arena()) {
|
||||||
LevelStatus::Batch(batch, finality) => {
|
LevelStatus::Batch(batch, finality) => {
|
||||||
// SAFETY: the batch came from this edge, so it carries the edge's layout.
|
// SAFETY: the batch came from this edge, so it carries the edge's layout.
|
||||||
@@ -235,7 +235,7 @@ fn monitor<'e>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
let result = content.eval(&ctx);
|
let result = content.eval(&ctx);
|
||||||
if ctx.innermost_index() == 0
|
if ctx.index() == 0
|
||||||
&& let GPoll::Final(value) | GPoll::Partial(value) = &result
|
&& let GPoll::Final(value) | GPoll::Partial(value) = &result
|
||||||
{
|
{
|
||||||
// SAFETY: the value came from this edge, so it carries the edge's layout.
|
// SAFETY: the value came from this edge, so it carries the edge's layout.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
use core_types::attribute::{Attr, EditorLayerPath, Opacity, RemoveAttr, Transform};
|
use core_types::attribute::{Attr, EditorLayerPath, Opacity, RemoveAttr, Transform};
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use core_types::context::{DeriveCtx, ExtractIndex, IndexLink, InjectIndex};
|
use core_types::context::{DeriveCtx, ExtractIndex, ExtractIndices, IndexLink, InjectIndex};
|
||||||
use core_types::extent::{ExtentIn, LevelIn, ListIn, ValueIn};
|
use core_types::extent::{ExtentIn, LevelIn, ListIn, ValueIn};
|
||||||
use core_types::gpoll::{ErrorKind, Extent, GPoll, GraphError, Interrupt, Level};
|
use core_types::gpoll::{ErrorKind, Extent, GPoll, GraphError, Interrupt, Level};
|
||||||
use core_types::node::Lane;
|
use core_types::node::Lane;
|
||||||
@@ -58,8 +58,8 @@ fn fade<T>(_: impl Ctx, (element, opacity): (T, Attr<Opacity>), factor: f64) ->
|
|||||||
/// writes a per-copy opacity indexed by the copy's own index.
|
/// writes a per-copy opacity indexed by the copy's own index.
|
||||||
#[node_macro::node(category("Test"), extent(repeat_opacity_extent))]
|
#[node_macro::node(category("Test"), extent(repeat_opacity_extent))]
|
||||||
fn repeat_opacity(ctx: impl Ctx + ExtractIndex, element: f64, count: u32) -> IList<(f64, Attr<Opacity>)> {
|
fn repeat_opacity(ctx: impl Ctx + ExtractIndex, element: f64, count: u32) -> IList<(f64, Attr<Opacity>)> {
|
||||||
debug_assert!(ctx.innermost_index() < count as u64, "repeat addressed past its copy count");
|
debug_assert!(ctx.index() < count as u64, "repeat addressed past its copy count");
|
||||||
emit(element, Attr(ctx.innermost_index() as f64))
|
emit(element, Attr(ctx.index() as f64))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Test"))]
|
#[node_macro::node(category("Test"))]
|
||||||
@@ -155,7 +155,7 @@ fn extend<T>(
|
|||||||
GPoll::Pending => return Err(Interrupt::Pending),
|
GPoll::Pending => return Err(Interrupt::Pending),
|
||||||
_ => return Err(GraphError::new("extend over a non-exact base extent").into()),
|
_ => return Err(GraphError::new("extend over a non-exact base extent").into()),
|
||||||
};
|
};
|
||||||
let lane = ctx.innermost_index();
|
let lane = ctx.index();
|
||||||
match lane < split {
|
match lane < split {
|
||||||
true => base.eval(ctx),
|
true => base.eval(ctx),
|
||||||
false => {
|
false => {
|
||||||
@@ -210,7 +210,7 @@ fn omit_element<T>(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, content: i
|
|||||||
GPoll::Pending => return Err(Interrupt::Pending),
|
GPoll::Pending => return Err(Interrupt::Pending),
|
||||||
_ => return Err(GraphError::new("omit over a non-exact extent").into()),
|
_ => return Err(GraphError::new("omit over a non-exact extent").into()),
|
||||||
};
|
};
|
||||||
let lane = ctx.innermost_index();
|
let lane = ctx.index();
|
||||||
let source = match resolve_index(index, total) {
|
let source = match resolve_index(index, total) {
|
||||||
Some(omitted) if lane >= omitted => lane + 1,
|
Some(omitted) if lane >= omitted => lane + 1,
|
||||||
_ => lane,
|
_ => lane,
|
||||||
@@ -270,7 +270,7 @@ fn extract_element(_: impl Ctx + InjectIndex + Copy, list: IList<f64>, index: f6
|
|||||||
#[node_macro::node(category("Test"), extent(mirror_extent))]
|
#[node_macro::node(category("Test"), extent(mirror_extent))]
|
||||||
fn mirror(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<f64>, keep_original: bool) -> Result<IList<(f64, Attr<Transform>)>, Interrupt> {
|
fn mirror(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<f64>, keep_original: bool) -> Result<IList<(f64, Attr<Transform>)>, Interrupt> {
|
||||||
let total = content.len() as u64;
|
let total = content.len() as u64;
|
||||||
let lane = ctx.innermost_index();
|
let lane = ctx.index();
|
||||||
let (source, mirrored) = match (keep_original, lane < total) {
|
let (source, mirrored) = match (keep_original, lane < total) {
|
||||||
(true, true) => (lane, false),
|
(true, true) => (lane, false),
|
||||||
(true, false) => (lane - total, true),
|
(true, false) => (lane - total, true),
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ pub fn omit_element<T>(
|
|||||||
GPoll::Pending => return Err(Interrupt::Pending),
|
GPoll::Pending => return Err(Interrupt::Pending),
|
||||||
_ => return Err(GraphError::new("omit over a non-exact extent").into()),
|
_ => return Err(GraphError::new("omit over a non-exact extent").into()),
|
||||||
};
|
};
|
||||||
let lane = ctx.innermost_index();
|
let lane = ctx.index();
|
||||||
let source = match resolve_index(index, total) {
|
let source = match resolve_index(index, total) {
|
||||||
Some(omitted) if lane >= omitted => lane + 1,
|
Some(omitted) if lane >= omitted => lane + 1,
|
||||||
_ => lane,
|
_ => lane,
|
||||||
@@ -115,7 +115,7 @@ fn map<Row: Clone + Send + Sync + CacheHash + 'static, T>(
|
|||||||
#[implementations(Graphic, Vector, Raster<CPU>, Color, GradientStops, String)] content: IList<Row>,
|
#[implementations(Graphic, Vector, Raster<CPU>, Color, GradientStops, String)] content: IList<Row>,
|
||||||
mapped: impl Node<Context<'_>, Output = IList<T>>,
|
mapped: impl Node<Context<'_>, Output = IList<T>>,
|
||||||
) -> Result<IList<T>, Interrupt> {
|
) -> Result<IList<T>, Interrupt> {
|
||||||
let mut remaining = ctx.innermost_index();
|
let mut remaining = ctx.index();
|
||||||
for row in 0..content.len() {
|
for row in 0..content.len() {
|
||||||
let item = crate::record::vararg_row(content, row);
|
let item = crate::record::vararg_row(content, row);
|
||||||
let scoped = ctx.push_vararg(&item);
|
let scoped = ctx.push_vararg(&item);
|
||||||
@@ -276,7 +276,7 @@ fn mirror<'e>(
|
|||||||
mirror_lane(
|
mirror_lane(
|
||||||
ctx.arena(),
|
ctx.arena(),
|
||||||
legacy_render_list_of(content),
|
legacy_render_list_of(content),
|
||||||
ctx.innermost_index() as usize,
|
ctx.index() as usize,
|
||||||
relative_to_bounds,
|
relative_to_bounds,
|
||||||
offset,
|
offset,
|
||||||
angle,
|
angle,
|
||||||
@@ -334,7 +334,7 @@ fn mirror_vector<'e>(
|
|||||||
mirror_lane(
|
mirror_lane(
|
||||||
ctx.arena(),
|
ctx.arena(),
|
||||||
legacy_render_list_of(content),
|
legacy_render_list_of(content),
|
||||||
ctx.innermost_index() as usize,
|
ctx.index() as usize,
|
||||||
relative_to_bounds,
|
relative_to_bounds,
|
||||||
offset,
|
offset,
|
||||||
angle,
|
angle,
|
||||||
@@ -403,7 +403,7 @@ pub fn extend<T>(
|
|||||||
GPoll::Pending => return Err(Interrupt::Pending),
|
GPoll::Pending => return Err(Interrupt::Pending),
|
||||||
_ => return Err(GraphError::new("extend over a non-exact base extent").into()),
|
_ => return Err(GraphError::new("extend over a non-exact base extent").into()),
|
||||||
};
|
};
|
||||||
let lane = ctx.innermost_index();
|
let lane = ctx.index();
|
||||||
match lane < split {
|
match lane < split {
|
||||||
true => base.eval(ctx),
|
true => base.eval(ctx),
|
||||||
false => {
|
false => {
|
||||||
@@ -586,7 +586,7 @@ pub use _to_graphic_unit_mod::to_graphic_unit_entries;
|
|||||||
/// Removes a level of nesting from a `Graphic[]`, or all nesting if "Fully Flatten" is enabled.
|
/// Removes a level of nesting from a `Graphic[]`, or all nesting if "Fully Flatten" is enabled.
|
||||||
#[node_macro::node(category("General"), extent(flatten_graphic_extent))]
|
#[node_macro::node(category("General"), extent(flatten_graphic_extent))]
|
||||||
pub fn flatten_graphic(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<Graphic>, fully_flatten: bool) -> Result<IList<(Graphic, Attr<TransformAttr>)>, Interrupt> {
|
pub fn flatten_graphic(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<Graphic>, fully_flatten: bool) -> Result<IList<(Graphic, Attr<TransformAttr>)>, Interrupt> {
|
||||||
let mut remaining = ctx.innermost_index() as usize;
|
let mut remaining = ctx.index() as usize;
|
||||||
for row in 0..content.len() {
|
for row in 0..content.len() {
|
||||||
let graphic = content.element_ref(row);
|
let graphic = content.element_ref(row);
|
||||||
let count = crate::record::leaf_count(graphic, fully_flatten, 0);
|
let count = crate::record::leaf_count(graphic, fully_flatten, 0);
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ pub(crate) fn locate(graphic: &Graphic, transform: DAffine2, fully_flatten: bool
|
|||||||
/// rides as a leaf with its embedded transforms untouched.
|
/// rides as a leaf with its embedded transforms untouched.
|
||||||
#[node_macro::node(category("Test"), extent(flatten_extent))]
|
#[node_macro::node(category("Test"), extent(flatten_extent))]
|
||||||
fn flatten(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<Graphic>, fully_flatten: bool) -> Result<IList<(Graphic, Attr<Transform>)>, Interrupt> {
|
fn flatten(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<Graphic>, fully_flatten: bool) -> Result<IList<(Graphic, Attr<Transform>)>, Interrupt> {
|
||||||
let mut remaining = ctx.innermost_index() as usize;
|
let mut remaining = ctx.index() as usize;
|
||||||
for row in 0..content.len() {
|
for row in 0..content.len() {
|
||||||
let graphic = content.element_ref(row);
|
let graphic = content.element_ref(row);
|
||||||
let count = leaf_count(graphic, fully_flatten, 0);
|
let count = leaf_count(graphic, fully_flatten, 0);
|
||||||
@@ -157,7 +157,7 @@ fn map<Row: Clone + Send + Sync + core_types::CacheHash + 'static, T>(
|
|||||||
#[implementations(Graphic, Vector, Raster<CPU>, Color, GradientStops, String)] content: IList<Row>,
|
#[implementations(Graphic, Vector, Raster<CPU>, Color, GradientStops, String)] content: IList<Row>,
|
||||||
mapped: impl Node<Context<'_>, Output = IList<T>>,
|
mapped: impl Node<Context<'_>, Output = IList<T>>,
|
||||||
) -> Result<IList<IList<T>>, Interrupt> {
|
) -> Result<IList<IList<T>>, Interrupt> {
|
||||||
let mut remaining = ctx.innermost_index();
|
let mut remaining = ctx.index();
|
||||||
for row in 0..content.len() {
|
for row in 0..content.len() {
|
||||||
let item = vararg_row(content, row);
|
let item = vararg_row(content, row);
|
||||||
let scoped = ctx.push_vararg(&item);
|
let scoped = ctx.push_vararg(&item);
|
||||||
@@ -181,7 +181,7 @@ fn flat_map<Row: Clone + Send + Sync + core_types::CacheHash + 'static, T>(
|
|||||||
#[implementations(Graphic, Vector, Raster<CPU>, Color, GradientStops, String)] content: IList<Row>,
|
#[implementations(Graphic, Vector, Raster<CPU>, Color, GradientStops, String)] content: IList<Row>,
|
||||||
mapped: impl Node<Context<'_>, Output = IList<T>>,
|
mapped: impl Node<Context<'_>, Output = IList<T>>,
|
||||||
) -> Result<IList<T>, Interrupt> {
|
) -> Result<IList<T>, Interrupt> {
|
||||||
let mut remaining = ctx.innermost_index();
|
let mut remaining = ctx.index();
|
||||||
for row in 0..content.len() {
|
for row in 0..content.len() {
|
||||||
let item = vararg_row(content, row);
|
let item = vararg_row(content, row);
|
||||||
let scoped = ctx.push_vararg(&item);
|
let scoped = ctx.push_vararg(&item);
|
||||||
@@ -201,7 +201,7 @@ fn flat_map<Row: Clone + Send + Sync + core_types::CacheHash + 'static, T>(
|
|||||||
#[node_macro::node(category("Test"), extent(flatten_levels_extent))]
|
#[node_macro::node(category("Test"), extent(flatten_levels_extent))]
|
||||||
fn flatten_levels<T>(ctx: impl Ctx + DeriveCtx + ExtractIndex, content: impl Node<Context<'_>, Output = IList<IList<T>>>) -> Result<IList<T>, Interrupt> {
|
fn flatten_levels<T>(ctx: impl Ctx + DeriveCtx + ExtractIndex, content: impl Node<Context<'_>, Output = IList<IList<T>>>) -> Result<IList<T>, Interrupt> {
|
||||||
let head = ctx.index_head();
|
let head = ctx.index_head();
|
||||||
content.eval(&ctx.promoted(&head, ctx.innermost_index()))
|
content.eval(&ctx.promoted(&head, ctx.index()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The collapsed level's extent is the sum of the inner extents across the
|
/// The collapsed level's extent is the sum of the inner extents across the
|
||||||
@@ -237,7 +237,7 @@ mod tests {
|
|||||||
use core_types::SourceId;
|
use core_types::SourceId;
|
||||||
use core_types::arena::Arena;
|
use core_types::arena::Arena;
|
||||||
use core_types::attribute::Attribute as AttributeMarker;
|
use core_types::attribute::Attribute as AttributeMarker;
|
||||||
use core_types::context::{ContextImpl, EvalScope, ExtractArena};
|
use core_types::context::{ContextImpl, EvalScope, ExtractArena, ExtractIndices};
|
||||||
use core_types::list::{Item, List};
|
use core_types::list::{Item, List};
|
||||||
use core_types::node::Node;
|
use core_types::node::Node;
|
||||||
use core_types::record::{self, Layout, Rec, RecordSource, RecordValue, stack};
|
use core_types::record::{self, Layout, Rec, RecordSource, RecordValue, stack};
|
||||||
|
|||||||
@@ -811,7 +811,7 @@ fn hsla_to_color(_: impl Ctx, _primary: (), hue: Fraction, #[default(1.)] satura
|
|||||||
#[node_macro::node(category("Color"), name("Hex to Color"))]
|
#[node_macro::node(category("Color"), name("Hex to Color"))]
|
||||||
fn hex_to_color(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, hex_code: String) -> Result<IList<Color>, Interrupt> {
|
fn hex_to_color(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, hex_code: String) -> Result<IList<Color>, Interrupt> {
|
||||||
// An invalid input serves an empty level: no color
|
// An invalid input serves an empty level: no color
|
||||||
match (core_types::misc::parse_css_color(&hex_code), ctx.innermost_index()) {
|
match (core_types::misc::parse_css_color(&hex_code), ctx.index()) {
|
||||||
(Some(color), 0) => Ok(color),
|
(Some(color), 0) => Ok(color),
|
||||||
_ => Err(GraphError::past_end().into()),
|
_ => Err(GraphError::past_end().into()),
|
||||||
}
|
}
|
||||||
@@ -839,7 +839,7 @@ fn spread_method(_: impl Ctx, gradient: GradientStops, spread_method: vector_typ
|
|||||||
#[node_macro::node(category("Color"))]
|
#[node_macro::node(category("Color"))]
|
||||||
fn sample_gradient(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, _primary: (), gradient: IList<GradientStops>, position: Fraction) -> Result<IList<Color>, Interrupt> {
|
fn sample_gradient(ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, _primary: (), gradient: IList<GradientStops>, position: Fraction) -> Result<IList<Color>, Interrupt> {
|
||||||
// An unwired gradient serves an empty level: no color
|
// An unwired gradient serves an empty level: no color
|
||||||
if gradient.is_empty() || ctx.innermost_index() != 0 {
|
if gradient.is_empty() || ctx.index() != 0 {
|
||||||
return Err(GraphError::past_end().into());
|
return Err(GraphError::past_end().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1035,7 +1035,7 @@ mod graphene_test {
|
|||||||
type Output = f64;
|
type Output = f64;
|
||||||
|
|
||||||
fn eval(&self, input: &Input) -> GPoll<f64> {
|
fn eval(&self, input: &Input) -> GPoll<f64> {
|
||||||
GPoll::Final(input.innermost_index() as f64)
|
GPoll::Final(input.index() as f64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ fn image_color_palette(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
palette.get(ctx.innermost_index() as usize).copied().ok_or_else(|| GraphError::past_end().into())
|
palette.get(ctx.index() as usize).copied().ok_or_else(|| GraphError::past_end().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ pub fn combine_channels<'e>(
|
|||||||
)>,
|
)>,
|
||||||
Interrupt,
|
Interrupt,
|
||||||
> {
|
> {
|
||||||
let lane = ctx.innermost_index() as usize;
|
let lane = ctx.index() as usize;
|
||||||
let max_len = red.len().max(green.len()).max(blue.len()).max(alpha.len());
|
let max_len = red.len().max(green.len()).max(blue.len()).max(alpha.len());
|
||||||
if lane >= max_len {
|
if lane >= max_len {
|
||||||
return Err(GraphError::past_end().into());
|
return Err(GraphError::past_end().into());
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ mod test {
|
|||||||
type Output = RecordValue<'e>;
|
type Output = RecordValue<'e>;
|
||||||
|
|
||||||
fn eval(&self, input: &ContextImpl<'e>) -> GPoll<RecordValue<'e>> {
|
fn eval(&self, input: &ContextImpl<'e>) -> GPoll<RecordValue<'e>> {
|
||||||
use core_types::context::{ExtractArena, ExtractIndex};
|
use core_types::context::{ExtractArena, ExtractIndices};
|
||||||
let (vector, transform) = &self.rows[input.innermost_index() as usize % self.rows.len()];
|
let (vector, transform) = &self.rows[input.innermost_index() as usize % self.rows.len()];
|
||||||
let dst = stack::push(self.layout.frame_bytes());
|
let dst = stack::push(self.layout.frame_bytes());
|
||||||
// SAFETY: dst is the claimed frame of this layout; offsets are the layout's own.
|
// SAFETY: dst is the claimed frame of this layout; offsets are the layout's own.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ fn path_modify<'e>(
|
|||||||
node_path: Vec<NodeId>,
|
node_path: Vec<NodeId>,
|
||||||
) -> Result<(Vector, Attr<'e, EditorLayerPath>, RemoveAttr<EditorClickTarget>), Interrupt> {
|
) -> Result<(Vector, Attr<'e, EditorLayerPath>, RemoveAttr<EditorClickTarget>), Interrupt> {
|
||||||
let mut element = element;
|
let mut element = element;
|
||||||
if ctx.innermost_index() == 0 {
|
if ctx.index() == 0 {
|
||||||
modification.apply(&mut element);
|
modification.apply(&mut element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ fn assign_colors<'e>(
|
|||||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
|
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
|
||||||
repeat_every: u32,
|
repeat_every: u32,
|
||||||
) -> Result<IList<(Vector, Attr<'e, TransformAttr>, Attr<'e, Fill>, Attr<'e, StrokeAttr>, Attr<'e, EditorLayerPath>)>, Interrupt> {
|
) -> Result<IList<(Vector, Attr<'e, TransformAttr>, Attr<'e, Fill>, Attr<'e, StrokeAttr>, Attr<'e, EditorLayerPath>)>, Interrupt> {
|
||||||
let lane = ctx.innermost_index() as usize;
|
let lane = ctx.index() as usize;
|
||||||
if lane >= content.len() {
|
if lane >= content.len() {
|
||||||
return Err(GraphError::past_end().into());
|
return Err(GraphError::past_end().into());
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ fn assign_colors_graphic<'e>(
|
|||||||
seed: SeedValue,
|
seed: SeedValue,
|
||||||
repeat_every: u32,
|
repeat_every: u32,
|
||||||
) -> Result<IList<(Graphic, Attr<'e, TransformAttr>, Attr<'e, EditorLayerPath>)>, Interrupt> {
|
) -> Result<IList<(Graphic, Attr<'e, TransformAttr>, Attr<'e, EditorLayerPath>)>, Interrupt> {
|
||||||
let lane = ctx.innermost_index() as usize;
|
let lane = ctx.index() as usize;
|
||||||
if lane >= content.len() {
|
if lane >= content.len() {
|
||||||
return Err(GraphError::past_end().into());
|
return Err(GraphError::past_end().into());
|
||||||
}
|
}
|
||||||
@@ -1549,7 +1549,7 @@ fn solidify_stroke<'e>(
|
|||||||
)>,
|
)>,
|
||||||
Interrupt,
|
Interrupt,
|
||||||
> {
|
> {
|
||||||
solidify_lane(ctx.arena(), legacy_graphic_list_of(content), ctx.innermost_index() as usize)
|
solidify_lane(ctx.arena(), legacy_graphic_list_of(content), ctx.index() as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A fill-bearing row splits into a fill lane and a solidified stroke lane,
|
/// A fill-bearing row splits into a fill lane and a solidified stroke lane,
|
||||||
@@ -1588,7 +1588,7 @@ fn solidify_stroke_vector<'e>(
|
|||||||
)>,
|
)>,
|
||||||
Interrupt,
|
Interrupt,
|
||||||
> {
|
> {
|
||||||
solidify_lane(ctx.arena(), legacy_graphic_list_of(content), ctx.innermost_index() as usize)
|
solidify_lane(ctx.arena(), legacy_graphic_list_of(content), ctx.index() as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solidify_stroke_vector_extent(content: ListIn<'_, Vector>, level: LevelIn) -> GPoll<Extent> {
|
fn solidify_stroke_vector_extent(content: ListIn<'_, Vector>, level: LevelIn) -> GPoll<Extent> {
|
||||||
@@ -1655,7 +1655,7 @@ fn separate_subpaths<'e>(
|
|||||||
Interrupt,
|
Interrupt,
|
||||||
> {
|
> {
|
||||||
let output = separate_subpaths_core(legacy_vector_list_of(content));
|
let output = separate_subpaths_core(legacy_vector_list_of(content));
|
||||||
emit_legacy_lane(ctx.arena(), output, ctx.innermost_index() as usize)
|
emit_legacy_lane(ctx.arena(), output, ctx.index() as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A row splits into one lane per subpath, so the count depends on the
|
/// A row splits into one lane per subpath, so the count depends on the
|
||||||
@@ -1726,7 +1726,7 @@ fn map_points<'e>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_legacy_lane(ctx.arena(), content, ctx.innermost_index() as usize)
|
emit_legacy_lane(ctx.arena(), content, ctx.index() as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_points_extent(content: ListIn<'_, Vector>, _mapped: ExtentIn<'_>, level: LevelIn) -> GPoll<Extent> {
|
fn map_points_extent(content: ListIn<'_, Vector>, _mapped: ExtentIn<'_>, level: LevelIn) -> GPoll<Extent> {
|
||||||
@@ -2176,7 +2176,7 @@ fn cut_path<'e>(
|
|||||||
Interrupt,
|
Interrupt,
|
||||||
> {
|
> {
|
||||||
let output = cut_path_core(legacy_vector_list_of(content), progression, reverse, parameterized_distance);
|
let output = cut_path_core(legacy_vector_list_of(content), progression, reverse, parameterized_distance);
|
||||||
emit_legacy_lane(ctx.arena(), output, ctx.innermost_index() as usize)
|
emit_legacy_lane(ctx.arena(), output, ctx.index() as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cut_path_extent(content: ListIn<'_, Vector>, _progression: ValueIn<'_, f64>, _reverse: ValueIn<'_, bool>, _parameterized_distance: ValueIn<'_, bool>, level: LevelIn) -> GPoll<Extent> {
|
fn cut_path_extent(content: ListIn<'_, Vector>, _progression: ValueIn<'_, f64>, _reverse: ValueIn<'_, bool>, _parameterized_distance: ValueIn<'_, bool>, level: LevelIn) -> GPoll<Extent> {
|
||||||
|
|||||||
Reference in New Issue
Block a user