Skip to main content

wisp_storybook/stories/
s_spotlight.rs

1//! Story: spotlight / highlight mask (M-MASK.6 / AUT-28).
2//!
3//! Pretend "screen capture" with grid lines, plus a yellow "highlight
4//! me" rectangle in the lower right. The spotlight primitive dims
5//! everything outside a focus shape over the highlight, drawing the
6//! viewer's eye there.
7
8use glam::Vec2;
9use wisp::application::Application;
10use wisp::math::Rect;
11use wisp::render::Renderer;
12use wisp::{Color, Fill, Graphics, MaskShape, RenderTexture, Sprite, Stage, Texture};
13
14use crate::story::Story;
15
16pub fn story() -> Story {
17    Story {
18        id: "spotlight",
19        category: "Mask",
20        title: "Spotlight / highlight",
21        milestone: "M-MASK.6 / AUT-28",
22        writeup: include_str!("writeups/spotlight.md"),
23        build,
24        tick: None,
25    }
26}
27
28fn build(app: &Application, stage: &mut Stage) {
29    let format = wgpu::TextureFormat::Rgba8Unorm;
30    let renderer = Renderer::new(app, format).expect("renderer");
31
32    let base_rt = render_capture_with_target(&renderer, app, format);
33
34    let output_rt = RenderTexture::with_format(app, 256, 256, format);
35    // Focus on the lower-right "important button" area.
36    let focus = Rect::new(0.18, -0.6, 0.55, 0.45);
37    renderer.apply_spotlight(
38        app,
39        MaskShape::rounded_rect(focus, 0.06),
40        Color::rgba(0.0, 0.0, 0.0, 0.7),
41        &base_rt,
42        &output_rt,
43    );
44
45    let bytes = output_rt.read_pixels(app);
46    let tex = Texture::from_rgba(app, 256, 256, &bytes);
47    let mut shown = Sprite::from_texture(tex).with_anchor(Vec2::splat(0.5));
48    shown.container.transform.scale = Vec2::splat(0.9);
49    let _ = stage.add_child(stage.root(), shown);
50}
51
52fn render_capture_with_target(
53    renderer: &Renderer,
54    app: &Application,
55    format: wgpu::TextureFormat,
56) -> RenderTexture {
57    let mut s = Stage::new();
58    let mut bg = Graphics::new();
59    bg.fill(Fill::LinearGradient {
60        start: Vec2::new(-1.0, 1.0),
61        end: Vec2::new(1.0, -1.0),
62        color_a: Color::rgba_u8(80, 130, 210, 255),
63        color_b: Color::rgba_u8(220, 110, 80, 255),
64    });
65    bg.draw_rect(Rect::new(-1.0, -1.0, 2.0, 2.0));
66    let _ = s.add_child(s.root(), bg);
67    for i in -4i16..=4 {
68        let x = f32::from(i) * 0.2;
69        let mut line = Graphics::new();
70        line.fill(Fill::Solid(Color::rgba_u8(255, 255, 255, 200)));
71        line.draw_rect(Rect::new(x - 0.005, -1.0, 0.01, 2.0));
72        let _ = s.add_child(s.root(), line);
73    }
74    for j in -4i16..=4 {
75        let y = f32::from(j) * 0.2;
76        let mut line = Graphics::new();
77        line.fill(Fill::Solid(Color::rgba_u8(255, 255, 255, 200)));
78        line.draw_rect(Rect::new(-1.0, y - 0.005, 2.0, 0.01));
79        let _ = s.add_child(s.root(), line);
80    }
81    // The "highlight me" target — a yellow button-like shape inside
82    // the focus area.
83    let mut button = Graphics::new();
84    button.fill(Fill::Solid(Color::rgba_u8(255, 230, 80, 255)));
85    button.draw_rounded_rect(Rect::new(0.25, -0.5, 0.4, 0.25), 0.06);
86    let _ = s.add_child(s.root(), button);
87    let rt = RenderTexture::with_format(app, 256, 256, format);
88    let _ = renderer.render_stage(app, rt.view(), Color::BLACK, &s);
89    rt
90}