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
//! Reading a surface's height back out of a frame (milestone 606): what the tests of every
//! widget that casts a shadow share, so each asks the same question the same way.
//!
//! A surface of any height casts **three** shadows, in the order
//! [`BoxShadow::for_elevation`](frus_core::BoxShadow::for_elevation) gives them. Their three
//! blurs together name the height: no two heights of the reference's table share them.
use frus_core::{BoxShadow, Color, Primitive};
/// Every shadow in `primitives`, innermost layers included: `(blur, colour)`, in paint order.
pub(crate) fn shadows(primitives: &[Primitive]) -> Vec<(f32, Color)> {
fn walk(primitives: &[Primitive], out: &mut Vec<(f32, Color)>) {
for p in primitives {
match p {
Primitive::Rect { blur, color, .. } if *blur > 0.0 => out.push((*blur, *color)),
Primitive::Layer { primitives, .. } => walk(primitives, out),
_ => {}
}
}
}
let mut out = Vec::new();
walk(primitives, &mut out);
out
}
/// How many surfaces cast a shadow: three shadows each.
pub(crate) fn casts(primitives: &[Primitive]) -> usize {
shadows(primitives).len() / 3
}
/// The height of the first surface that casts a shadow, to a twentieth; `0.0` when none
/// does.
pub(crate) fn height(primitives: &[Primitive]) -> f32 {
heights(primitives).first().copied().unwrap_or(0.0)
}
/// The height of every surface that casts a shadow, in paint order, to a twentieth.
pub(crate) fn heights(primitives: &[Primitive]) -> Vec<f32> {
let found = shadows(primitives);
found
.as_chunks::<3>()
.0
.iter()
.map(|[a, b, c]| from_blurs([a.0, b.0, c.0]))
.collect()
}
/// The height whose three shadows are blurred as `blurs` are.
fn from_blurs([a, b, c]: [f32; 3]) -> f32 {
(1..=480)
.map(|step| step as f32 / 20.0)
.min_by(|x, y| {
let off = |e: f32| {
let [p, q, r] = BoxShadow::for_elevation(e, Color::BLACK);
(p.blur - a).abs() + (q.blur - b).abs() + (r.blur - c).abs()
};
off(*x).total_cmp(&off(*y))
})
.unwrap_or(0.0)
}
/// The colour the first surface's shadow was cast in: its first shadow carries a fifth of
/// that colour's strength.
pub(crate) fn colour(primitives: &[Primitive]) -> Option<Color> {
shadows(primitives)
.first()
.map(|(_, c)| c.with_alpha(((c.a / 0.2).min(1.0) * 1000.0).round() / 1000.0))
}