use alloc::vec::Vec;
use crate::skeleton::{NodeId, Skeleton};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Chain {
pub nodes: Vec<NodeId>,
pub children: Vec<usize>,
}
impl Chain {
pub fn tip(&self) -> NodeId {
self.nodes[0]
}
pub fn base(&self) -> NodeId {
*self.nodes.last().unwrap()
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ChainTree {
pub chains: Vec<Chain>,
pub roots: Vec<usize>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Mark {
None,
Base,
Section,
}
impl ChainTree {
pub fn build(skel: &Skeleton) -> ChainTree {
let n = skel.len();
let mut tree = ChainTree::default();
if n == 0 {
return tree;
}
let mut marks = alloc::vec![Mark::None; n];
for (i, node) in skel.nodes().iter().enumerate() {
let Some(eff) = &node.effector else { continue };
let mut counter: i64 = if eff.chain_length == 0 {
-1
} else {
eff.chain_length as i64
};
let mut cur = Some(NodeId(i as u32));
while let Some(id) = cur {
let marking = if counter == 0 {
Mark::Base
} else {
Mark::Section
};
let slot = &mut marks[id.index()];
if *slot == Mark::None {
*slot = marking;
} else if counter != 0 {
*slot = marking;
}
let stop = counter == 0;
counter -= 1;
if stop {
break;
}
cur = skel.node(id).parent();
}
}
struct Frame {
node: NodeId,
chain: Option<usize>,
base: NodeId,
}
let root = NodeId(0);
let mut stack = alloc::vec![Frame {
node: root,
chain: None,
base: root
}];
while let Some(Frame { node, chain, base }) = stack.pop() {
let mut node_base = base;
let mut child_base = base;
let mut child_chain = chain;
let mut current_chain = chain;
let marking = core::mem::replace(&mut marks[node.index()], Mark::None);
let mut section_logic = false;
match marking {
Mark::Base => {
child_base = node;
current_chain = None;
child_chain = None;
}
Mark::None => {
node_base = node;
child_base = node;
section_logic = true;
}
Mark::Section => {
section_logic = true;
}
}
if section_logic {
let marked_children = skel
.node(node)
.children()
.iter()
.filter(|c| marks[c.index()] == Mark::Section)
.take(2)
.count();
let has_effector = skel.node(node).effector.is_some();
if (marked_children == 2 || has_effector) && node != node_base {
let mut nodes = Vec::new();
let mut cur = node;
while cur != node_base {
nodes.push(cur);
cur = skel.node(cur).parent().expect("base must be an ancestor");
}
nodes.push(node_base);
let idx = tree.chains.len();
tree.chains.push(Chain {
nodes,
children: Vec::new(),
});
match current_chain {
None => tree.roots.push(idx),
Some(parent) => tree.chains[parent].children.push(idx),
}
child_chain = Some(idx);
child_base = node;
}
}
for &c in skel.node(node).children().iter().rev() {
stack.push(Frame {
node: c,
chain: child_chain,
base: child_base,
});
}
}
tree
}
pub fn len(&self) -> usize {
self.chains.len()
}
pub fn is_empty(&self) -> bool {
self.chains.is_empty()
}
}