Skip to main content

wisp_storybook/stories/
s_path_mask.rs

1//! Story: freehand path mask (M-MASK.10 / AUT-35).
2//!
3//! A five-pointed-star polygon clips a textured frame and a redacted
4//! frame side-by-side, demonstrating both `apply_path_clip` (alpha
5//! cutout) and `apply_solid_redaction_path` (filled redaction).
6
7use glam::Vec2;
8use wisp::application::Application;
9use wisp::math::Rect;
10use wisp::render::Renderer;
11use wisp::{Color, Fill, Graphics, RenderTexture, Sprite, Stage, Texture};
12// No Graphics backdrop — see s_ellipse for the rationale (Graphics
13// paints after Sprites in render_stage's batched pipeline order).
14
15use crate::story::Story;
16
17pub fn story() -> Story {
18    Story {
19        id: "path-mask",
20        category: "Mask",
21        title: "Freehand path mask",
22        milestone: "M-MASK.10 / AUT-35",
23        writeup: include_str!("writeups/path_mask.md"),
24        build,
25        tick: None,
26    }
27}
28
29fn build(app: &Application, stage: &mut Stage) {
30    let format = wgpu::TextureFormat::Rgba8Unorm;
31    let renderer = Renderer::new(app, format).expect("renderer");
32
33    let frame_rt = render_textured_frame(&renderer, app, format);
34    let star = star_polygon();
35
36    // Left: alpha-cutout via apply_path_clip.
37    let cutout = RenderTexture::with_format(app, 256, 256, format);
38    renderer.apply_path_clip(app, &star, &frame_rt, &cutout);
39    let cb = cutout.read_pixels(app);
40    let ct = Texture::from_rgba(app, 256, 256, &cb);
41    let mut left = Sprite::from_texture(ct).with_anchor(Vec2::splat(0.5));
42    left.container.transform.position = Vec2::new(-0.45, 0.0);
43    left.container.transform.scale = Vec2::splat(0.4);
44    let _ = stage.add_child(stage.root(), left);
45
46    // Right: solid redaction via apply_solid_redaction_path over the
47    // textured frame (which becomes the "base" here).
48    let redacted = RenderTexture::with_format(app, 256, 256, format);
49    renderer.apply_solid_redaction_path(
50        app,
51        &star,
52        Color::rgba_u8(20, 20, 20, 255),
53        &frame_rt,
54        &redacted,
55    );
56    let rb = redacted.read_pixels(app);
57    let rt = Texture::from_rgba(app, 256, 256, &rb);
58    let mut right = Sprite::from_texture(rt).with_anchor(Vec2::splat(0.5));
59    right.container.transform.position = Vec2::new(0.45, 0.0);
60    right.container.transform.scale = Vec2::splat(0.4);
61    let _ = stage.add_child(stage.root(), right);
62}
63
64fn star_polygon() -> Vec<Vec2> {
65    let mut pts = Vec::with_capacity(10);
66    for i in 0i16..10 {
67        let r = if i % 2 == 0 { 0.85 } else { 0.35 };
68        let theta = f32::from(i) * std::f32::consts::PI / 5.0 - std::f32::consts::FRAC_PI_2;
69        pts.push(Vec2::new(theta.cos() * r, theta.sin() * r));
70    }
71    pts
72}
73
74fn render_textured_frame(
75    renderer: &Renderer,
76    app: &Application,
77    format: wgpu::TextureFormat,
78) -> RenderTexture {
79    let mut s = Stage::new();
80    let mut tone = Graphics::new();
81    tone.fill(Fill::LinearGradient {
82        start: Vec2::new(-1.0, 1.0),
83        end: Vec2::new(1.0, -1.0),
84        color_a: Color::rgba_u8(255, 195, 150, 255),
85        color_b: Color::rgba_u8(150, 100, 80, 255),
86    });
87    tone.draw_rect(Rect::new(-1.0, -1.0, 2.0, 2.0));
88    let _ = s.add_child(s.root(), tone);
89    for i in -3i16..=3 {
90        let x = f32::from(i) * 0.25;
91        let mut line = Graphics::new();
92        line.fill(Fill::Solid(Color::rgba_u8(255, 255, 255, 180)));
93        line.draw_rect(Rect::new(x - 0.01, -1.0, 0.02, 2.0));
94        let _ = s.add_child(s.root(), line);
95    }
96    for j in -3i16..=3 {
97        let y = f32::from(j) * 0.25;
98        let mut line = Graphics::new();
99        line.fill(Fill::Solid(Color::rgba_u8(255, 255, 255, 180)));
100        line.draw_rect(Rect::new(-1.0, y - 0.01, 2.0, 0.02));
101        let _ = s.add_child(s.root(), line);
102    }
103    let rt = RenderTexture::with_format(app, 256, 256, format);
104    let _ = renderer.render_stage(app, rt.view(), Color::TRANSPARENT, &s);
105    rt
106}