use crate::data::{Attachment, AttachmentId, BoneId, ConstraintId, SkinKey, SlotId};
use crate::math::Color;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AttachmentRef {
Data(AttachmentId),
Owned(u32),
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct SkinKeys {
keys: Vec<(SlotId, Box<str>)>,
by_slot: Vec<Vec<SkinKey>>,
}
impl SkinKeys {
pub fn intern(&mut self, slot: SlotId, placeholder: &str) -> SkinKey {
if let Some(key) = self.find(slot, placeholder) {
return key;
}
let key = SkinKey(self.keys.len() as u32);
self.keys.push((slot, placeholder.into()));
if self.by_slot.len() <= slot.index() {
self.by_slot.resize_with(slot.index() + 1, Vec::new);
}
self.by_slot[slot.index()].push(key);
key
}
#[must_use]
pub fn find(&self, slot: SlotId, placeholder: &str) -> Option<SkinKey> {
self.slot_keys(slot)
.iter()
.copied()
.find(|k| *self.keys[k.index()].1 == *placeholder)
}
#[must_use]
pub fn slot(&self, key: SkinKey) -> SlotId {
self.keys[key.index()].0
}
#[must_use]
pub fn placeholder(&self, key: SkinKey) -> &str {
&self.keys[key.index()].1
}
#[must_use]
pub fn slot_keys(&self, slot: SlotId) -> &[SkinKey] {
self.by_slot.get(slot.index()).map_or(&[], Vec::as_slice)
}
#[must_use]
pub fn len(&self) -> usize {
self.keys.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Skin {
pub name: String,
pub bones: Vec<BoneId>,
pub constraints: Vec<ConstraintId>,
pub color: Color,
entries: Vec<Option<AttachmentRef>>,
extra: Vec<(SlotId, Box<str>, AttachmentRef)>,
owned: Vec<Attachment>,
}
impl Skin {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
color: Color::new(0.996_078_43, 0.619_607_87, 0.309_803_93, 1.0),
..Self::default()
}
}
#[inline]
#[must_use]
pub fn get(&self, key: SkinKey) -> Option<AttachmentRef> {
self.entries.get(key.index()).copied().flatten()
}
pub fn set(&mut self, key: SkinKey, attachment: AttachmentRef) {
if let AttachmentRef::Owned(i) = attachment {
assert!(
(i as usize) < self.owned.len(),
"owned attachment {i} out of range"
);
}
if self.entries.len() <= key.index() {
self.entries.resize(key.index() + 1, None);
}
self.entries[key.index()] = Some(attachment);
}
pub fn remove(&mut self, key: SkinKey) {
if let Some(e) = self.entries.get_mut(key.index()) {
*e = None;
}
}
#[must_use]
pub fn get_named(
&self,
keys: &SkinKeys,
slot: SlotId,
placeholder: &str,
) -> Option<AttachmentRef> {
match keys.find(slot, placeholder) {
Some(key) => self.get(key),
None => self
.extra
.iter()
.find(|(s, n, _)| *s == slot && **n == *placeholder)
.map(|e| e.2),
}
}
pub fn set_named(
&mut self,
keys: &SkinKeys,
slot: SlotId,
placeholder: &str,
attachment: AttachmentRef,
) {
if let Some(key) = keys.find(slot, placeholder) {
self.set(key, attachment);
return;
}
if let AttachmentRef::Owned(i) = attachment {
assert!(
(i as usize) < self.owned.len(),
"owned attachment {i} out of range"
);
}
self.set_extra(slot, placeholder, attachment);
}
pub fn remove_named(&mut self, keys: &SkinKeys, slot: SlotId, placeholder: &str) {
match keys.find(slot, placeholder) {
Some(key) => self.remove(key),
None => self
.extra
.retain(|(s, n, _)| !(*s == slot && **n == *placeholder)),
}
}
pub fn extra_on(&self, slot: SlotId) -> impl Iterator<Item = (&str, AttachmentRef)> + '_ {
self.extra
.iter()
.filter(move |e| e.0 == slot)
.map(|(_, n, a)| (&**n, *a))
}
pub fn add_owned(&mut self, attachment: Attachment) -> AttachmentRef {
self.owned.push(attachment);
AttachmentRef::Owned(self.owned.len() as u32 - 1)
}
#[must_use]
pub fn owned(&self) -> &[Attachment] {
&self.owned
}
#[must_use]
pub fn owned_mut(&mut self) -> &mut [Attachment] {
&mut self.owned
}
pub fn entries(&self) -> impl Iterator<Item = (SkinKey, AttachmentRef)> + '_ {
self.entries
.iter()
.enumerate()
.filter_map(|(i, e)| e.map(|a| (SkinKey(i as u32), a)))
}
#[must_use]
pub fn attachment_count(&self) -> usize {
self.entries.iter().flatten().count() + self.extra.len()
}
pub fn add_skin(&mut self, other: &Skin) {
self.add_requirements(other);
let base = self.owned.len() as u32;
self.owned.extend_from_slice(&other.owned);
let remap = |a: AttachmentRef| match a {
AttachmentRef::Owned(i) => AttachmentRef::Owned(base + i),
data @ AttachmentRef::Data(_) => data,
};
for (key, attachment) in other.entries() {
self.set(key, remap(attachment));
}
for (slot, name, attachment) in &other.extra {
self.set_extra(*slot, name, remap(*attachment));
}
}
pub fn copy_skin(&mut self, other: &Skin, attachments: &[Attachment]) {
self.add_requirements(other);
for (key, attachment) in other.entries() {
let copy = self.copy_of(other, attachments, attachment);
self.set(key, copy);
}
for (slot, name, attachment) in &other.extra {
let copy = self.copy_of(other, attachments, *attachment);
self.set_extra(*slot, name, copy);
}
}
fn copy_of(
&mut self,
other: &Skin,
attachments: &[Attachment],
attachment: AttachmentRef,
) -> AttachmentRef {
let copy = other.resolve(attachments, attachment).copy(attachment);
self.add_owned(copy)
}
fn add_requirements(&mut self, other: &Skin) {
for &b in &other.bones {
if !self.bones.contains(&b) {
self.bones.push(b);
}
}
for &c in &other.constraints {
if !self.constraints.contains(&c) {
self.constraints.push(c);
}
}
}
fn set_extra(&mut self, slot: SlotId, placeholder: &str, attachment: AttachmentRef) {
match self
.extra
.iter_mut()
.find(|(s, n, _)| *s == slot && **n == *placeholder)
{
Some(e) => e.2 = attachment,
None => self.extra.push((slot, placeholder.into(), attachment)),
}
}
pub(crate) fn compact(&mut self, keep: &mut [bool]) -> Vec<Option<u32>> {
let mark = |keep: &mut [bool], a: AttachmentRef| {
if let AttachmentRef::Owned(i) = a {
keep[i as usize] = true;
}
};
for a in self.entries.iter().flatten() {
mark(keep, *a);
}
for e in &self.extra {
mark(keep, e.2);
}
let mut remap = vec![None; self.owned.len()];
let mut next = 0;
let mut i = 0;
self.owned.retain(|_| {
let kept = keep[i];
if kept {
remap[i] = Some(next);
next += 1;
}
i += 1;
kept
});
let apply = |a: &mut AttachmentRef| {
if let AttachmentRef::Owned(i) = a {
*i = remap[*i as usize].expect("referenced attachments are kept");
}
};
for a in self.entries.iter_mut().flatten() {
apply(a);
}
for e in &mut self.extra {
apply(&mut e.2);
}
remap
}
#[inline]
#[must_use]
pub fn resolve<'a>(
&'a self,
attachments: &'a [Attachment],
r: AttachmentRef,
) -> &'a Attachment {
match r {
AttachmentRef::Data(id) => &attachments[id.index()],
AttachmentRef::Owned(i) => &self.owned[i as usize],
}
}
}
#[inline]
#[must_use]
pub fn resolve<'a>(
attachments: &'a [Attachment],
skin: Option<&'a Skin>,
r: AttachmentRef,
) -> &'a Attachment {
match r {
AttachmentRef::Data(id) => &attachments[id.index()],
AttachmentRef::Owned(i) => {
&skin.expect("owned attachment without a skin").owned[i as usize]
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::PointAttachment;
#[test]
fn keys_intern_once_per_slot_and_name() {
let mut keys = SkinKeys::default();
let a = keys.intern(SlotId(2), "head");
let b = keys.intern(SlotId(0), "head");
assert_ne!(a, b);
assert_eq!(keys.intern(SlotId(2), "head"), a);
assert_eq!(keys.find(SlotId(0), "head"), Some(b));
assert_eq!(keys.find(SlotId(1), "head"), None);
assert_eq!(keys.slot(a), SlotId(2));
assert_eq!(keys.placeholder(b), "head");
}
#[test]
fn add_skin_remaps_owned_attachments() {
let (k0, k1) = (SkinKey(0), SkinKey(3));
let mut base = Skin::new("base");
let p = base.add_owned(Attachment::Point(PointAttachment::new("p")));
base.set(k0, p);
let mut other = Skin::new("other");
let q = other.add_owned(Attachment::Point(PointAttachment::new("q")));
other.set(k1, q);
other.set(k0, AttachmentRef::Data(AttachmentId(5)));
base.add_skin(&other);
assert_eq!(base.get(k0), Some(AttachmentRef::Data(AttachmentId(5))));
let q = base.get(k1).unwrap();
assert_eq!(base.resolve(&[], q).name(), "q");
assert_eq!(base.attachment_count(), 2);
base.remove(k1);
assert_eq!(base.get(k1), None);
}
}