use crate::{Error, Result, Wrench};
use super::{FloatingRobot, LinkId, Model, Robot};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct IndexedLoad {
pub link: LinkId,
pub wrench: Wrench,
}
#[derive(Debug)]
pub struct LoadBuffer {
owner: LinkId,
positions: Vec<usize>,
loads: Vec<IndexedLoad>,
}
impl LoadBuffer {
pub(super) fn new(model: &Model) -> Self {
Self {
owner: LinkId::new(model.model_id, 0),
positions: vec![usize::MAX; model.link_count()],
loads: Vec::with_capacity(model.link_count()),
}
}
fn validate_link(&self, link: LinkId) -> Result<()> {
if link.model_id == self.owner.model_id && link.index < self.positions.len() {
Ok(())
} else {
Err(Error::InvalidLinkId)
}
}
pub fn set(&mut self, link: LinkId, wrench: Wrench) -> Result<()> {
self.validate_link(link)?;
if !wrench.is_finite() {
return Err(Error::NonFiniteInput { input: "load" });
}
let position = &mut self.positions[link.index];
if *position == usize::MAX {
*position = self.loads.len();
self.loads.push(IndexedLoad { link, wrench });
} else {
self.loads[*position].wrench = wrench;
}
Ok(())
}
pub fn add(&mut self, link: LinkId, wrench: Wrench) -> Result<()> {
self.validate_link(link)?;
if !wrench.is_finite() {
return Err(Error::NonFiniteInput { input: "load" });
}
let position = self.positions[link.index];
let value = if position == usize::MAX {
wrench
} else {
let previous = self.loads[position].wrench;
Wrench::new(
previous.torque + wrench.torque,
previous.force + wrench.force,
)
};
if !value.is_finite() {
return Err(Error::NumericalFailure {
operation: "load aggregation",
});
}
self.set(link, value)
}
pub fn remove(&mut self, link: LinkId) -> Result<()> {
self.validate_link(link)?;
let position = self.positions[link.index];
if position != usize::MAX {
self.loads.swap_remove(position);
self.positions[link.index] = usize::MAX;
if let Some(moved) = self.loads.get(position) {
self.positions[moved.link.index] = position;
}
}
Ok(())
}
pub fn clear(&mut self) {
for load in &self.loads {
self.positions[load.link.index] = usize::MAX;
}
self.loads.clear();
}
pub fn as_slice(&self) -> &[IndexedLoad] {
&self.loads
}
pub fn len(&self) -> usize {
self.loads.len()
}
pub fn is_empty(&self) -> bool {
self.loads.is_empty()
}
}
impl Robot {
pub fn load_buffer(&self) -> LoadBuffer {
LoadBuffer::new(&self.model)
}
}
impl FloatingRobot {
pub fn load_buffer(&self) -> LoadBuffer {
LoadBuffer::new(&self.model)
}
}