pub enum Extent {
Sum {
px: f64,
inches: f64,
percent: f64,
},
Min(Box<Extent>, Box<Extent>),
Max(Box<Extent>, Box<Extent>),
TrackOf {
grid: CellId,
axis: Axis,
track: u16,
span: u16,
},
}Expand description
A length value. Internally either a linear combination of pixels, inches,
and percentage of the containing axis, a deferred min/max of two
sub-lengths (because min(absolute, percent) cannot be reduced without
knowing the axis size), or a reference to a tagged grid’s resolved track
size (which is only known after solve).
Construct via the px / mm / cm / inch / pt / percent
associated functions, Extent::min / Extent::max, or
Extent::track_of / Extent::tracks_of. Lengths compose with +,
-, unary -, * f64, and / f64; addition through Min/Max
distributes exactly (min(a, b) + c = min(a+c, b+c)), so arithmetic
stays closed without losing structure. TrackOf is opaque to arithmetic
— it composes with Min/Max transparently but + / - / * on a tree
containing TrackOf panics. Reach a multi-segment sum via
Extent::tracks_of’s span parameter.
Physical units (mm, cm, inch, pt) are resolved to pixels via the
dpi passed to Grid::solve. percent is taken as a fraction of the
relevant axis of the parent’s grid cell area; the constructor argument is
0.0..=1.0 (so Extent::percent(0.5) is “50%”).
Variants§
Sum
Linear combination: px + inches * dpi + percent * axis.
Fields
Min(Box<Extent>, Box<Extent>)
Pointwise minimum of two lengths, evaluated at resolution time.
Max(Box<Extent>, Box<Extent>)
Pointwise maximum of two lengths, evaluated at resolution time.
TrackOf
Resolves at solve time to the summed resolved size of span
consecutive tracks starting at track (1-indexed) on the given
axis of the Grid tagged with id == grid. For span > 1
the corresponding gaps between tracks are included.
The solver runs as a damped fixed-point iteration over its width
and height passes; on the first iteration TrackOf evaluates to
0 (no prior data); on subsequent iterations it picks up the
resolved track size from the previous iteration. Forward
references (a track that references a track later in the solve)
are handled by iteration; cycles will not converge and exhaust
MAX_ITER.
Implementations§
Source§impl Extent
impl Extent
Sourcepub const fn cm(v: f64) -> Self
pub const fn cm(v: f64) -> Self
Centimeters — v / 2.54 inches.
Examples found in repository?
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}Sourcepub const fn percent(v: f64) -> Self
pub const fn percent(v: f64) -> Self
A fraction of the containing axis. 0.5 is 50%.
Examples found in repository?
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}Sourcepub const fn track_of(grid: CellId, axis: Axis, track: u16) -> Self
pub const fn track_of(grid: CellId, axis: Axis, track: u16) -> Self
Reference the resolved size of a single track in a tagged grid.
track is 1-indexed. See Extent::TrackOf.
Sourcepub const fn tracks_of(grid: CellId, axis: Axis, start: u16, span: u16) -> Self
pub const fn tracks_of(grid: CellId, axis: Axis, start: u16, span: u16) -> Self
Reference the resolved summed size of span consecutive tracks in
a tagged grid, starting at start (1-indexed). Gaps between
tracks are included. See Extent::TrackOf.
Sourcepub fn is_absolute(&self) -> bool
pub fn is_absolute(&self) -> bool
True if this length has no percent term anywhere in its tree and
no Extent::TrackOf reference (whose value isn’t known without
a prior solve pass). Lengths that are absolute can be resolved to
pixels without an axis size or prior resolved tracks (used for
intrinsic-size computation in Track::Auto).
Trait Implementations§
impl StructuralPartialEq for Extent
Auto Trait Implementations§
impl Freeze for Extent
impl RefUnwindSafe for Extent
impl Send for Extent
impl Sync for Extent
impl Unpin for Extent
impl UnsafeUnpin for Extent
impl UnwindSafe for Extent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> Brush for T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.