Skip to main content

layout_demo/
layout_demo.rs

1//! Visual smoke test for the layout engine. Solves a small nested grid with
2//! the headline-example placement (inner 2×2 in row 2, cols 3..=5 of a 5×3
3//! outer, inset 1cm from the left and ending at 75% width) plus a 2:1
4//! aspect-ratio cell, then draws every resolved rect with `VelloRenderer`.
5//!
6//! Writes `examples/layout_demo.png` next to this file.
7
8use hephaestus::backend::vello::VelloRenderer;
9use hephaestus::color::{rgb8, Color};
10use hephaestus::layout::{CellId, Extent, Grid, Inset, Placement, Track};
11use hephaestus::stroke::Stroke;
12use hephaestus::{Affine, Brush, FillRule, Path, PickId, Renderer, SceneBuilder};
13use kurbo::Shape;
14
15fn main() {
16    let (w, h) = (800u32, 600u32);
17    let dpi = 96.0;
18
19    // Outer: 5 columns × 3 rows of fr(1).
20    let mut root = Grid::new(vec![Track::Fr(1.0); 5], vec![Track::Fr(1.0); 3]);
21
22    // Tag every outer cell so we can outline the underlying grid.
23    let mut outer_id = 1u64;
24    for r in 1..=3u16 {
25        for c in 1..=5u16 {
26            root.place_mut(Placement::at(r, c), Grid::cell().id(CellId(outer_id)));
27            outer_id += 1;
28        }
29    }
30
31    // Inner 2×2 in row 2, cols 3..=5, with 1cm left + 25% right insets.
32    let mut inner = Grid::new(
33        [Track::Fr(1.0), Track::Fr(1.0)],
34        [Track::Fr(1.0), Track::Fr(1.0)],
35    );
36    for r in 1..=2u16 {
37        for c in 1..=2u16 {
38            inner.place_mut(
39                Placement::at(r, c),
40                Grid::cell().id(CellId(100 + (r as u64) * 2 + c as u64)),
41            );
42        }
43    }
44    root.place_mut(
45        Placement::at(2, 3).span(1, 3).inset(
46            Inset::default()
47                .left(Extent::cm(1.0))
48                .right(Extent::percent(0.25)),
49        ),
50        inner,
51    );
52
53    // A 2:1 cell in the top-left, expressed via respect + fr weights.
54    root.place_mut(
55        Placement::at(1, 1),
56        Grid::new([Track::Fr(2.0)], [Track::Fr(1.0)])
57            .respect()
58            .id(CellId(200)),
59    );
60
61    let layout = root.solve(hephaestus::Size::new(w as f64, h as f64), dpi);
62
63    // Render.
64    let mut renderer = VelloRenderer::new().expect("vello renderer init");
65    {
66        let scene = renderer.scene();
67        let outer_brush: Brush = rgb8(80, 90, 110).into();
68        let outer_stroke = Stroke::new(1.0);
69        for id in 1..=15u64 {
70            if let Some(rect) = layout.rect(CellId(id)) {
71                let path: Path = rect.to_path(0.1);
72                scene.stroke(
73                    &outer_stroke,
74                    Affine::IDENTITY,
75                    &outer_brush,
76                    None,
77                    &path,
78                    PickId::Skip,
79                );
80            }
81        }
82
83        let inner_brush: Brush = rgb8(240, 180, 60).into();
84        let inner_stroke = Stroke::new(2.0);
85        for id in [103u64, 104, 105, 106] {
86            if let Some(rect) = layout.rect(CellId(id)) {
87                let path: Path = rect.to_path(0.1);
88                scene.fill(
89                    FillRule::NonZero,
90                    Affine::IDENTITY,
91                    &Brush::Solid(rgb8(245, 220, 160)),
92                    None,
93                    &path,
94                    PickId::Skip,
95                );
96                scene.stroke(
97                    &inner_stroke,
98                    Affine::IDENTITY,
99                    &inner_brush,
100                    None,
101                    &path,
102                    PickId::Skip,
103                );
104            }
105        }
106
107        let aspect_brush: Brush = rgb8(60, 160, 220).into();
108        if let Some(rect) = layout.rect(CellId(200)) {
109            let path: Path = rect.to_path(0.1);
110            scene.fill(
111                FillRule::NonZero,
112                Affine::IDENTITY,
113                &aspect_brush,
114                None,
115                &path,
116                PickId::Skip,
117            );
118        }
119    }
120
121    let mut pixels = vec![0u8; (w * h * 4) as usize];
122    let bg: Color = rgb8(20, 22, 28);
123    renderer
124        .render_to_buffer(w, h, bg, &mut pixels)
125        .expect("render");
126
127    let path = std::env::current_dir()
128        .unwrap()
129        .join("examples/layout_demo.png");
130    hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
131    println!("wrote {}", path.display());
132}