summaryrefslogtreecommitdiff
path: root/src/library/graphics/line.rs
blob: de2e4aa1df4eb771f9e79da55708708cadc0f7d3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use crate::library::prelude::*;

/// Display a line without affecting the layout.
#[derive(Debug, Hash)]
pub struct LineNode {
    /// Where the line starts.
    origin: Spec<Relative<RawLength>>,
    /// The offset from the `origin` where the line ends.
    delta: Spec<Relative<RawLength>>,
}

#[node]
impl LineNode {
    /// How to stroke the line.
    #[property(resolve, fold)]
    pub const STROKE: RawStroke = RawStroke::default();

    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
        let origin = args.named("origin")?.unwrap_or_default();

        let delta = match args.named::<Spec<Relative<RawLength>>>("to")? {
            Some(to) => to.zip(origin).map(|(to, from)| to - from),
            None => {
                let length = args
                    .named::<Relative<RawLength>>("length")?
                    .unwrap_or(Length::cm(1.0).into());

                let angle = args.named::<Angle>("angle")?.unwrap_or_default();
                let x = angle.cos() * length;
                let y = angle.sin() * length;

                Spec::new(x, y)
            }
        };

        Ok(Content::inline(Self { origin, delta }))
    }
}

impl Layout for LineNode {
    fn layout(
        &self,
        _: &mut Context,
        regions: &Regions,
        styles: StyleChain,
    ) -> TypResult<Vec<Arc<Frame>>> {
        let stroke = styles.get(Self::STROKE).unwrap_or_default();

        let origin = self
            .origin
            .resolve(styles)
            .zip(regions.base)
            .map(|(l, b)| l.relative_to(b));

        let delta = self
            .delta
            .resolve(styles)
            .zip(regions.base)
            .map(|(l, b)| l.relative_to(b));

        let target = regions.expand.select(regions.first, Size::zero());
        let mut frame = Frame::new(target);

        let shape = Geometry::Line(delta.to_point()).stroked(stroke);
        frame.push(origin.to_point(), Element::Shape(shape));

        Ok(vec![Arc::new(frame)])
    }
}

castable! {
    Spec<Relative<RawLength>>,
    Expected: "array of two relative lengths",
    Value::Array(array) => {
        let mut iter = array.into_iter();
        match (iter.next(), iter.next(), iter.next()) {
            (Some(a), Some(b), None) => Spec::new(a.cast()?, b.cast()?),
            _ => Err("point array must contain exactly two entries")?,
        }
    },
}