use crate::axes::Limits;
use crate::edit::{Edit, PropertyPath, Transaction, Value};
use crate::error::IrError;
use crate::figure::Figure;
use crate::ids::NodeId;
use crate::link::{AxisLink, Dimension};
pub fn set_limits(
figure: &Figure,
axes: NodeId,
dimension: Dimension,
limits: Limits,
) -> Result<Transaction, IrError> {
if figure.axes(axes).is_none() {
return Err(IrError::UnknownAxes(axes));
}
if let Limits::Manual { min, max } = limits
&& !(min.is_finite() && max.is_finite() && min < max)
{
return Err(IrError::InvalidLimits { min, max });
}
let edits = figure
.linked_axes(axes, dimension)
.into_iter()
.map(|id| set_limits_edit(id, dimension, limits))
.collect();
Ok(Transaction { edits })
}
pub fn link(
figure: &Figure,
dimension: Dimension,
axes: &[NodeId],
) -> Result<Transaction, IrError> {
if let Some(&unknown) = axes.iter().find(|&&id| figure.axes(id).is_none()) {
return Err(IrError::UnknownAxes(unknown));
}
let mut distinct: Vec<NodeId> = Vec::with_capacity(axes.len());
for &id in axes {
if !distinct.contains(&id) {
distinct.push(id);
}
}
let [reference, _, ..] = distinct[..] else {
return Ok(Transaction::default());
};
let links = figure.link_groups(dimension, &distinct);
let group: Vec<NodeId> = links
.iter()
.find(|link| link.dimension == dimension && link.axes.contains(&reference))
.map(|link| link.axes.clone())
.unwrap_or_default();
let limits = axis_of(figure, reference, dimension).limits;
let mut edits = vec![Edit::Set {
node: figure.id,
path: PropertyPath::of(&["links"]),
value: Value::Links(links),
}];
edits.extend(
group
.into_iter()
.map(|id| set_limits_edit(id, dimension, limits)),
);
Ok(Transaction { edits })
}
pub fn remove_axes(figure: &Figure, axes: NodeId) -> Result<Transaction, IrError> {
if figure.axes(axes).is_none() {
return Err(IrError::UnknownAxes(axes));
}
let links: Vec<AxisLink> = figure
.links
.iter()
.filter_map(|link| {
let members: Vec<NodeId> = link.axes.iter().copied().filter(|&id| id != axes).collect();
(members.len() >= 2).then_some(AxisLink {
dimension: link.dimension,
axes: members,
})
})
.collect();
let mut edits = Vec::new();
if links != figure.links {
edits.push(Edit::Set {
node: figure.id,
path: PropertyPath::of(&["links"]),
value: Value::Links(links),
});
}
edits.push(Edit::Remove { node: axes });
Ok(Transaction { edits })
}
fn set_limits_edit(axes: NodeId, dimension: Dimension, limits: Limits) -> Edit {
let field = match dimension {
Dimension::X => "x",
Dimension::Y => "y",
Dimension::Z => "z",
};
Edit::Set {
node: axes,
path: PropertyPath::of(&[field, "limits"]),
value: Value::Limits(limits),
}
}
fn axis_of(figure: &Figure, axes: NodeId, dimension: Dimension) -> &crate::axes::Axis {
let axes = figure.axes(axes).expect("the axes is in the figure");
match dimension {
Dimension::X => &axes.x,
Dimension::Y => &axes.y,
Dimension::Z => &axes.z,
}
}