use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{elem, Content, NativeElement, Packed, Show, StyleChain};
use crate::introspection::Locator;
use crate::layout::{
Abs, Angle, Axes, BlockElem, Frame, FrameItem, Length, Region, Rel, Size,
};
use crate::utils::Numeric;
use crate::visualize::{Geometry, Stroke};
#[elem(Show)]
pub struct LineElem {
#[resolve]
pub start: Axes<Rel<Length>>,
#[resolve]
pub end: Option<Axes<Rel<Length>>>,
#[resolve]
#[default(Abs::pt(30.0).into())]
pub length: Rel<Length>,
pub angle: Angle,
#[resolve]
#[fold]
pub stroke: Stroke,
}
impl Show for Packed<LineElem> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(BlockElem::single_layouter(self.clone(), layout_line)
.pack()
.spanned(self.span()))
}
}
#[typst_macros::time(span = elem.span())]
fn layout_line(
elem: &Packed<LineElem>,
_: &mut Engine,
_: Locator,
styles: StyleChain,
region: Region,
) -> SourceResult<Frame> {
let resolve = |axes: Axes<Rel<Abs>>| axes.zip_map(region.size, Rel::relative_to);
let start = resolve(elem.start(styles));
let delta = elem.end(styles).map(|end| resolve(end) - start).unwrap_or_else(|| {
let length = elem.length(styles);
let angle = elem.angle(styles);
let x = angle.cos() * length;
let y = angle.sin() * length;
resolve(Axes::new(x, y))
});
let stroke = elem.stroke(styles).unwrap_or_default();
let size = start.max(start + delta).max(Size::zero());
if !size.is_finite() {
bail!(elem.span(), "cannot create line with infinite length");
}
let mut frame = Frame::soft(size);
let shape = Geometry::Line(delta.to_point()).stroked(stroke);
frame.push(start.to_point(), FrameItem::Shape(shape, elem.span()));
Ok(frame)
}