use log::debug;
use orrery_core::{
draw,
geometry::{Bounds, Point, Size},
};
use crate::{
error::RenderError,
layout::{component, positioning::LayoutBounds, sequence},
};
#[derive(Debug)]
pub enum LayoutContent<'a> {
Component(ContentStack<component::Layout<'a>>),
Sequence(ContentStack<sequence::Layout<'a>>),
}
#[derive(Debug)]
pub struct Layer<'a> {
z_index: usize,
offset: Point,
clip_bounds: Option<Bounds>,
content: LayoutContent<'a>, }
impl<'a> Layer<'a> {
fn new(z_index: usize, content: LayoutContent<'a>) -> Self {
Self {
z_index,
offset: Point::zero(),
clip_bounds: None,
content,
}
}
pub fn z_index(&self) -> usize {
self.z_index
}
pub fn offset(&self) -> Point {
self.offset
}
pub fn clip_bounds(&self) -> Option<Bounds> {
self.clip_bounds
}
pub fn content(&self) -> &LayoutContent<'_> {
&self.content
}
fn set_offset(&mut self, offset: Point) {
self.offset = offset;
}
fn set_clip_bounds(&mut self, clip_bounds: Option<Bounds>) {
self.clip_bounds = clip_bounds;
}
}
#[derive(Debug)]
pub struct LayeredLayout<'a> {
layers: Vec<Layer<'a>>,
}
impl<'a> LayeredLayout<'a> {
pub fn new() -> Self {
Self { layers: Vec::new() }
}
pub fn add_layer(&mut self, content: LayoutContent<'a>) -> usize {
let z_index = self.layers.len();
self.layers.push(Layer::new(z_index, content));
z_index
}
pub fn adjust_relative_position(
&mut self,
container_idx: usize,
positioned_shape: &draw::PositionedDrawable<draw::ShapeWithText>,
embedded_idx: usize,
) -> Result<(), RenderError> {
let [container_layer, embedded_layer] =
self.layers
.get_disjoint_mut([container_idx, embedded_idx])
.map_err(|err| {
RenderError::Layout(format!(
"Invalid layer indices (container_idx={container_idx}, embedded_idx={embedded_idx}): {err}"
))
})?;
let content_bounds = positioned_shape.content_bounds().ok_or_else(|| {
RenderError::Layout(
"Container shape must have inner content size set for embedded diagram positioning"
.to_string(),
)
})?;
let embedded_layout_bounds = match embedded_layer.content() {
LayoutContent::Component(layout) => layout
.iter()
.last()
.map(|content| content.content().layout_bounds())
.unwrap_or_default(),
LayoutContent::Sequence(layout) => layout
.iter()
.last()
.map(|content| content.content().layout_bounds())
.unwrap_or_default(),
};
debug!(
positioned_shape:?, content_bounds:?,
container_idx, container_offset:?=container_layer.offset(), container_clip_bounds:?=container_layer.clip_bounds(),
embedded_idx, embedded_offset:?=embedded_layer.offset(), embedded_clip_bounds:?=embedded_layer.clip_bounds();
"Embedded layer before adjustment",
);
embedded_layer.set_offset(
embedded_layer
.offset()
.add_point(container_layer.offset())
.add_point(content_bounds.min_point())
.add_point(Point::new(
-embedded_layout_bounds.min_x(),
-embedded_layout_bounds.min_y(),
)),
);
embedded_layer.set_clip_bounds(Some(embedded_layout_bounds));
debug!(
offset:?=embedded_layer.offset(), clip_bounds:?=embedded_layer.clip_bounds();
"Adjusted embedded layer",
);
Ok(())
}
pub fn len(&self) -> usize {
self.layers.len()
}
pub fn iter_from_bottom(&'a self) -> impl Iterator<Item = &'a Layer<'a>> {
self.layers.iter().rev()
}
}
#[derive(Debug, Clone)]
pub struct ContentStack<T: LayoutBounds>(Vec<PositionedContent<T>>);
impl<T> ContentStack<T>
where
T: LayoutBounds,
{
pub fn new() -> Self {
Self(Vec::new())
}
pub fn get_unchecked(&self, index: usize) -> &PositionedContent<T> {
&self.0[index]
}
pub fn get_mut_unchecked(&mut self, index: usize) -> &mut PositionedContent<T> {
&mut self.0[index]
}
pub fn push(&mut self, positioned_content: PositionedContent<T>) {
self.0.push(positioned_content);
}
pub fn layout_size(&self) -> Size {
self.0
.last()
.map(|content| content.layout_size())
.unwrap_or_default()
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &PositionedContent<T>> {
self.0.iter()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug, Clone)]
pub struct PositionedContent<T>
where
T: LayoutBounds,
{
offset: Point,
content: T,
}
impl<T> PositionedContent<T>
where
T: LayoutBounds,
{
pub fn new(content: T) -> Self {
Self {
content,
offset: Point::zero(),
}
}
pub fn offset(&self) -> Point {
self.offset
}
pub fn content(&self) -> &T {
&self.content
}
pub fn set_offset(&mut self, offset: Point) {
self.offset = offset;
}
pub fn layout_size(&self) -> Size {
self.content.layout_size()
}
}