use std::cmp::Ordering;
use bitflags::bitflags;
use crate::sdf::{LayerOffset, Path};
use super::layer_stack::LayerStackId;
use super::mapping::MapFunction;
use super::LayerId;
pub(crate) fn is_class_based_arc(arc: ArcType) -> bool {
matches!(arc, ArcType::Inherit | ArcType::Specialize)
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ArcType {
Root,
Inherit,
Variant,
Relocate,
Reference,
Payload,
Specialize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeId(pub(crate) u32);
impl NodeId {
pub const INVALID: Self = Self(u32::MAX);
pub fn is_valid(self) -> bool {
self.0 != u32::MAX
}
pub(crate) fn idx(self) -> usize {
self.0 as usize
}
}
impl Default for NodeId {
fn default() -> Self {
Self::INVALID
}
}
bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct NodeFlags: u16 {
const INERT = 1 << 0;
const CULLED = 1 << 1;
const PROHIBITED_CHILDREN = 1 << 5;
const IMPLIED_CLASS = 1 << 6;
const DIRECT = 1 << 7;
const HAS_SPECIALIZES = 1 << 8;
const RELOCATE_SOURCE = 1 << 9;
const VARIANT_BRANCH = 1 << 10;
}
}
#[derive(Debug, Clone)]
pub struct Node {
pub(crate) layer_stack: LayerStackId,
pub(crate) representative: LayerId,
pub(crate) path: Path,
pub(crate) arc: ArcType,
pub(crate) map_to_parent: MapFunction,
pub(crate) map_to_root: MapFunction,
pub(crate) parent: Option<NodeId>,
pub(crate) children: Vec<NodeId>,
pub(crate) origin: Option<NodeId>,
pub(crate) namespace_depth: u16,
pub(crate) sibling_num_at_origin: u16,
pub(crate) has_specs: bool,
pub(crate) restriction_depth: u16,
pub(crate) flags: NodeFlags,
}
impl Node {
pub(crate) fn new(
layer_stack: LayerStackId,
representative: LayerId,
path: Path,
arc: ArcType,
map_to_parent: MapFunction,
map_to_root: MapFunction,
introduced_by_specialize: bool,
) -> Self {
let flags = if introduced_by_specialize {
NodeFlags::HAS_SPECIALIZES
} else {
NodeFlags::empty()
};
Self {
layer_stack,
representative,
path,
arc,
map_to_parent,
map_to_root,
parent: None,
children: Vec::new(),
origin: None,
namespace_depth: 0,
sibling_num_at_origin: 0,
has_specs: true,
restriction_depth: 0,
flags,
}
}
pub fn layer_id(&self) -> LayerId {
self.representative
}
pub(crate) fn layer_stack_id(&self) -> LayerStackId {
self.layer_stack
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn arc(&self) -> ArcType {
self.arc
}
pub fn map_to_parent(&self) -> &MapFunction {
&self.map_to_parent
}
pub fn map_to_root(&self) -> &MapFunction {
&self.map_to_root
}
pub fn parent(&self) -> Option<NodeId> {
self.parent
}
pub fn children(&self) -> &[NodeId] {
&self.children
}
pub fn origin(&self) -> Option<NodeId> {
self.origin
}
pub fn namespace_depth(&self) -> u16 {
self.namespace_depth
}
pub fn has_specs(&self) -> bool {
self.has_specs
}
pub fn flags(&self) -> NodeFlags {
self.flags
}
pub fn is_inert(&self) -> bool {
self.flags.contains(NodeFlags::INERT)
}
pub fn is_culled(&self) -> bool {
self.flags.contains(NodeFlags::CULLED)
}
pub(crate) fn introduced_by_specialize(&self) -> bool {
self.flags.contains(NodeFlags::HAS_SPECIALIZES)
}
pub(crate) fn is_relocate_source(&self) -> bool {
self.flags.contains(NodeFlags::RELOCATE_SOURCE)
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct SpecSite {
pub(crate) node: NodeId,
pub(crate) layer: LayerId,
pub(crate) offset: LayerOffset,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct PrimIndexGraph {
pub(crate) nodes: Vec<Node>,
pub(crate) strength_order: Vec<NodeId>,
pub(crate) root: NodeId,
pub(crate) muted_external_targets: Vec<LayerId>,
pub(crate) muted_unloaded_targets: Vec<String>,
}
impl PrimIndexGraph {
pub(crate) fn local_root(&self) -> NodeId {
if !self.root.is_valid() {
return NodeId::INVALID;
}
self.nodes[self.root.idx()]
.children
.iter()
.copied()
.find(|&c| self.nodes[c.idx()].arc == ArcType::Root)
.unwrap_or(NodeId::INVALID)
}
pub(crate) fn init_root(&mut self, layer_stack: LayerStackId, representative: LayerId, path: Path) -> NodeId {
debug_assert!(self.nodes.is_empty(), "synthetic root must be the first node");
let id = NodeId(self.nodes.len() as u32);
let depth = path.prim_element_count() as u16;
let mut node = Node::new(
layer_stack,
representative,
path,
ArcType::Root,
MapFunction::identity(),
MapFunction::identity(),
false,
);
node.namespace_depth = depth;
node.flags |= NodeFlags::INERT;
self.nodes.push(node);
self.root = id;
id
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn add_child(
&mut self,
parent: NodeId,
layer_stack: LayerStackId,
representative: LayerId,
path: Path,
arc: ArcType,
map_to_parent: MapFunction,
introduced_by_specialize: bool,
) -> NodeId {
let root_site = !parent.is_valid();
let struct_parent = if root_site { self.root } else { parent };
let map_to_root = if struct_parent.is_valid() {
self.nodes[struct_parent.idx()].map_to_root.compose(&map_to_parent)
} else {
map_to_parent.clone()
};
let namespace_depth = if root_site {
path.prim_element_count()
} else {
self.nodes[parent.idx()].path.prim_element_count()
} as u16;
let idx = NodeId(self.nodes.len() as u32);
let mut node = Node::new(
layer_stack,
representative,
path,
arc,
map_to_parent,
map_to_root,
introduced_by_specialize,
);
node.namespace_depth = namespace_depth;
if struct_parent.is_valid() {
node.parent = Some(struct_parent);
node.origin = Some(struct_parent);
self.nodes[struct_parent.idx()].children.push(idx);
}
self.nodes.push(node);
idx
}
pub(crate) fn node_using_site(&self, layer_stack: LayerStackId, path: &Path) -> Option<NodeId> {
self.nodes
.iter()
.position(|node| {
!node.flags.intersects(NodeFlags::INERT | NodeFlags::CULLED)
&& node.layer_stack == layer_stack
&& &node.path == path
})
.map(|i| NodeId(i as u32))
}
pub(crate) fn append_child_name_to_all_sites(&mut self, child_path: &Path) {
let Some(parent_path) = child_path.parent() else {
return;
};
let Some(child_name) = child_path.name() else {
return;
};
for node in &mut self.nodes {
if node.path == parent_path {
node.path = child_path.clone();
} else if let Ok(deeper) = node.path.append_path(child_name) {
node.path = deeper;
}
}
}
pub(crate) fn is_ancestor_of(&self, x: NodeId, y: NodeId) -> bool {
let root = self.local_root();
let mut cur = y;
while cur.is_valid() && cur != root && cur != self.root {
if cur == x {
return true;
}
cur = self.nodes[cur.idx()].parent.unwrap_or(NodeId::INVALID);
}
false
}
fn chain_to_root(&self, id: NodeId) -> Vec<NodeId> {
let mut chain = vec![id];
let mut cur = id;
while let Some(parent) = self.nodes[cur.idx()].parent {
chain.push(parent);
cur = parent;
}
chain
}
pub(crate) fn compare_sibling_node_strength(&self, a: NodeId, b: NodeId) -> Ordering {
let na = &self.nodes[a.idx()];
let nb = &self.nodes[b.idx()];
if na.arc != nb.arc {
return na.arc.cmp(&nb.arc);
}
if na.introduced_by_specialize() && nb.introduced_by_specialize() {
return self.compare_specialize_siblings(a, b);
}
if na.namespace_depth != nb.namespace_depth {
return nb.namespace_depth.cmp(&na.namespace_depth);
}
let oa = na.origin.unwrap_or(a);
let ob = nb.origin.unwrap_or(b);
if oa != ob && (oa != a || ob != b) {
let ord = self.compare_node_strength(oa, ob);
if ord != Ordering::Equal {
return ord;
}
}
self.sibling_then_index(a, b)
}
fn compare_specialize_siblings(&self, a: NodeId, b: NodeId) -> Ordering {
let (a_root, a_hops) = self.origin_root_node(a);
let (b_root, b_hops) = self.origin_root_node(b);
if !self.origins_are_nested(a_root, b_root) {
let da = self.nodes[a.idx()].namespace_depth;
let db = self.nodes[b.idx()].namespace_depth;
if da != db {
return db.cmp(&da);
}
}
let oa = self.origin_of(a);
let ob = self.origin_of(b);
let a_authored = oa == self.parent_of(a);
let b_authored = ob == self.parent_of(b);
if oa == ob {
if !a_authored && !b_authored {
return self
.implied_beats_propagated(a, oa, b, ob)
.unwrap_or_else(|| a.0.cmp(&b.0));
}
} else if a_root != b_root {
return self.compare_node_strength(a_root, b_root).then(a.0.cmp(&b.0));
} else {
let a_depth = if a_authored {
0
} else {
self.namespace_depth_for_class_hierarchy(oa)
};
let b_depth = if b_authored {
0
} else {
self.namespace_depth_for_class_hierarchy(ob)
};
if a_depth != b_depth {
return a_depth.cmp(&b_depth);
}
if a_hops != b_hops {
return b_hops.cmp(&a_hops);
}
if !a_authored && !b_authored && self.same_layer_stack_as_root(a) && self.same_layer_stack_as_root(b) {
if let Some(ord) = self.implied_beats_propagated(a, oa, b, ob) {
return ord;
}
}
return self.compare_node_strength(oa, ob).then(a.0.cmp(&b.0));
}
self.sibling_then_index(a, b)
}
fn implied_beats_propagated(&self, a: NodeId, oa: NodeId, b: NodeId, ob: NodeId) -> Option<Ordering> {
match (!self.same_site(a, oa), !self.same_site(b, ob)) {
(true, false) => Some(Ordering::Less),
(false, true) => Some(Ordering::Greater),
_ => None,
}
}
fn sibling_then_index(&self, a: NodeId, b: NodeId) -> Ordering {
self.nodes[a.idx()]
.sibling_num_at_origin
.cmp(&self.nodes[b.idx()].sibling_num_at_origin)
.then(a.0.cmp(&b.0))
}
fn arc_of(&self, id: NodeId) -> ArcType {
self.nodes[id.idx()].arc
}
fn parent_of(&self, id: NodeId) -> NodeId {
self.nodes[id.idx()].parent.unwrap_or(NodeId::INVALID)
}
fn origin_of(&self, id: NodeId) -> NodeId {
self.nodes[id.idx()].origin.unwrap_or(NodeId::INVALID)
}
pub(crate) fn depth_below_introduction(&self, id: NodeId) -> u16 {
let parent = self.parent_of(id);
if !parent.is_valid() {
return 0;
}
(self.nodes[parent.idx()].path.prim_element_count() as u16).saturating_sub(self.nodes[id.idx()].namespace_depth)
}
pub(crate) fn is_due_to_ancestor(&self, id: NodeId) -> bool {
self.depth_below_introduction(id) > 0
}
pub(crate) fn path_at_introduction(&self, id: NodeId) -> Path {
let mut path = self.nodes[id.idx()].path.clone();
for _ in 0..self.depth_below_introduction(id) {
match path.parent() {
Some(parent) => path = parent,
None => break,
}
}
path
}
pub(crate) fn same_site(&self, a: NodeId, b: NodeId) -> bool {
a.is_valid()
&& b.is_valid()
&& self.nodes[a.idx()].layer_stack == self.nodes[b.idx()].layer_stack
&& self.nodes[a.idx()].path == self.nodes[b.idx()].path
}
fn same_layer_stack_as_root(&self, a: NodeId) -> bool {
let root = self.local_root();
root.is_valid() && self.nodes[a.idx()].layer_stack == self.nodes[root.idx()].layer_stack
}
pub(crate) fn is_propagated_specializes(&self, id: NodeId) -> bool {
self.arc_of(id) == ArcType::Specialize
&& self.parent_of(id) == self.local_root()
&& self.same_site(id, self.origin_of(id))
}
fn origin_root_node(&self, id: NodeId) -> (NodeId, usize) {
let mut cur = id;
let mut hops = 0;
loop {
let origin = self.origin_of(cur);
if !origin.is_valid() || origin == self.parent_of(cur) {
break;
}
cur = origin;
hops += 1;
}
(cur, hops)
}
fn origins_are_nested(&self, a: NodeId, b: NodeId) -> bool {
self.is_nested_under(a, b) || self.is_nested_under(b, a)
}
fn is_nested_under(&self, x: NodeId, y: NodeId) -> bool {
let mut n = x;
while n.is_valid() {
if n == y {
return true;
}
n = if self.is_propagated_specializes(n) {
self.origin_of(n)
} else {
self.parent_of(n)
};
}
false
}
fn namespace_depth_for_class_hierarchy(&self, n: NodeId) -> u16 {
let (mut instance, _class) = self.starting_node_of_class_hierarchy(n);
while instance.is_valid() && self.arc_of(instance) == ArcType::Relocate {
instance = self.parent_of(instance);
}
if instance.is_valid() {
self.nodes[instance.idx()].namespace_depth
} else {
0
}
}
pub(crate) fn starting_node_of_class_hierarchy(&self, n: NodeId) -> (NodeId, NodeId) {
let mut instance = n;
if self.is_propagated_specializes(instance) {
instance = self.origin_of(instance);
}
let mut class_node = NodeId::INVALID;
let depth = self.depth_below_introduction(instance);
while instance.is_valid()
&& is_class_based_arc(self.arc_of(instance))
&& self.depth_below_introduction(instance) == depth
{
class_node = instance;
let parent = self.parent_of(instance);
if !parent.is_valid() {
break;
}
instance = parent;
if self.is_propagated_specializes(instance) {
instance = self.origin_of(instance);
}
}
(instance, class_node)
}
pub(crate) fn get_propagated_specializes_node(&self, node: NodeId) -> Option<NodeId> {
if self.arc_of(node) != ArcType::Specialize {
return None;
}
let root = self.local_root();
if !root.is_valid() {
return None;
}
self.nodes[root.idx()]
.children
.iter()
.copied()
.find(|&rc| self.origin_of(rc) == node && self.is_propagated_specializes(rc))
}
pub(crate) fn compare_node_strength(&self, a: NodeId, b: NodeId) -> Ordering {
if a == b {
return Ordering::Equal;
}
let chain_a = self.chain_to_root(a);
let chain_b = self.chain_to_root(b);
let mut ia = chain_a.len();
let mut ib = chain_b.len();
while ia > 0 && ib > 0 {
let ca = chain_a[ia - 1];
let cb = chain_b[ib - 1];
if ca != cb {
return self.compare_sibling_node_strength(ca, cb);
}
ia -= 1;
ib -= 1;
}
match (ia, ib) {
(0, 0) => Ordering::Equal,
(0, _) => Ordering::Less,
(_, 0) => Ordering::Greater,
_ => Ordering::Equal,
}
}
pub(crate) fn finalize_strength_order(&mut self) {
if !self.root.is_valid() {
return;
}
for i in 0..self.nodes.len() {
let mut children = std::mem::take(&mut self.nodes[i].children);
children.sort_by(|&a, &b| self.compare_sibling_node_strength(a, b));
self.nodes[i].children = children;
}
let mut dfs = Vec::with_capacity(self.nodes.len());
let mut stack = vec![self.root];
while let Some(id) = stack.pop() {
dfs.push(id);
for &child in self.nodes[id.idx()].children.iter().rev() {
stack.push(child);
}
}
self.strength_order = dfs;
}
}
impl std::ops::Deref for PrimIndexGraph {
type Target = [Node];
fn deref(&self) -> &[Node] {
&self.nodes
}
}
#[cfg(test)]
mod tests {
use super::*;
fn arc_graph(local: &str, arc: ArcType, target: &str, deepen: &[&str]) -> (PrimIndexGraph, NodeId) {
let stack = LayerStackId::from_raw(1);
let layer = LayerId::from_raw(0);
let mut g = PrimIndexGraph::default();
g.init_root(stack, layer, Path::from(local));
let root = g.add_child(
NodeId::INVALID,
stack,
layer,
Path::from(local),
ArcType::Root,
MapFunction::identity(),
false,
);
let id = g.add_child(
root,
stack,
layer,
Path::from(target),
arc,
MapFunction::identity(),
false,
);
let mut prim = Path::from(local);
for name in deepen {
prim = prim.append_path(*name).expect("child path");
g.append_child_name_to_all_sites(&prim);
}
(g, id)
}
#[test]
fn introduction_depth_and_path() {
let (g, id) = arc_graph("/Model", ArcType::Inherit, "/_class_Model", &[]);
assert_eq!(g.depth_below_introduction(id), 0);
assert!(!g.is_due_to_ancestor(id));
assert_eq!(g.path_at_introduction(id), Path::from("/_class_Model"));
let (g, id) = arc_graph("/Model", ArcType::Inherit, "/_class_Model", &["Rig", "Anim"]);
assert_eq!(g.depth_below_introduction(id), 2);
assert!(g.is_due_to_ancestor(id));
assert_eq!(g.path_at_introduction(id), Path::from("/_class_Model"));
}
#[test]
fn introduction_depth_uneven_target() {
let (g, id) = arc_graph("/World/G", ArcType::Reference, "/Ref", &[]);
assert_eq!(g.depth_below_introduction(id), 0);
assert_eq!(g.path_at_introduction(id), Path::from("/Ref"));
let (g, id) = arc_graph("/World/G", ArcType::Reference, "/Ref", &["A"]);
assert_eq!(g.depth_below_introduction(id), 1);
assert_eq!(g.path_at_introduction(id), Path::from("/Ref"));
let (g, id) = arc_graph("/World", ArcType::Reference, "/Ref/Inner", &["A"]);
assert_eq!(g.depth_below_introduction(id), 1);
assert_eq!(g.path_at_introduction(id), Path::from("/Ref/Inner"));
}
}