Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow cutout to be rounded #140

Merged
merged 1 commit into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 36 additions & 16 deletions crates/yakui-widgets/src/shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::f32::consts::TAU;

use yakui_core::geometry::{Color, Rect, Vec2};
use yakui_core::paint::{PaintDom, PaintMesh, PaintRect, Vertex};
use yakui_core::TextureId;

pub fn cross(output: &mut PaintDom, rect: Rect, color: Color) {
static POSITIONS: [[f32; 2]; 12] = [
Expand Down Expand Up @@ -167,6 +168,7 @@ pub struct RoundedRectangle {
pub rect: Rect,
pub radius: f32,
pub color: Color,
pub texture: Option<(TextureId, Rect)>,
}

impl RoundedRectangle {
Expand All @@ -175,16 +177,12 @@ impl RoundedRectangle {
rect,
radius,
color: Color::WHITE,
texture: None,
}
}

pub fn add(&self, output: &mut PaintDom) {
if self.radius <= 0.0 {
return PaintRect::new(self.rect).add(output);
}

let rect = self.rect;
let color = self.color.to_linear();

// We are not prepared to let a corner's radius be bigger than a side's
// half-length.
Expand All @@ -193,22 +191,43 @@ impl RoundedRectangle {
.min(rect.size().x / 2.0)
.min(rect.size().y / 2.0);

let slices = if radius >= 1.0 {
f32::ceil(TAU / 8.0 / f32::acos(1.0 - 0.2 / radius)) as u32
} else {
1
// Fallback to a rectangle if the radius is too small.
if radius < 1.0 {
let mut p = PaintRect::new(rect);
p.texture = self.texture;
p.color = self.color;
return p.add(output);
}

let color = self.color.to_linear();

let slices = f32::ceil(TAU / 8.0 / f32::acos(1.0 - 0.2 / radius)) as u32;

// 3 rectangles and 4 corners
let mut vertices = Vec::with_capacity(4 * 3 + (slices + 2) as usize * 4);
let mut indices = Vec::with_capacity(6 * 3 + slices as usize * (3 * 4));

let (uv_offset, uv_factor) = self
.texture
.map(|(_, texture_rect)| (texture_rect.pos(), texture_rect.size() / rect.size()))
.unwrap_or((Vec2::ZERO, Vec2::ZERO));

let calc_uv = |position| {
if self.texture.is_none() {
return Vec2::ZERO;
}
(position - rect.pos()) * uv_factor + uv_offset
};

let mut vertices = Vec::new();
let mut indices = Vec::new();
let create_vertex = |pos| Vertex::new(pos, calc_uv(pos), color);

let mut rectangle = |min: Vec2, max: Vec2| {
let base_vertex = vertices.len();

let size = max - min;
let rect_vertices = RECT_POS
.map(Vec2::from)
.map(|vert| Vertex::new(vert * size + min, Vec2::ZERO, color));
.map(|vert| create_vertex(vert * size + min));

let rect_indices = RECT_INDEX.map(|index| index + base_vertex as u16);

Expand All @@ -231,17 +250,17 @@ impl RoundedRectangle {

let mut corner = |center: Vec2, start_angle: f32| {
let center_vertex = vertices.len();
vertices.push(Vertex::new(center, Vec2::ZERO, color));
vertices.push(create_vertex(center));

let first_offset = radius * Vec2::new(start_angle.cos(), -start_angle.sin());
vertices.push(Vertex::new(center + first_offset, Vec2::ZERO, color));
vertices.push(create_vertex(center + first_offset));

for i in 1..=slices {
let percent = i as f32 / slices as f32;
let angle = start_angle + percent * TAU / 4.0;
let offset = radius * Vec2::new(angle.cos(), -angle.sin());
let index = vertices.len();
vertices.push(Vertex::new(center + offset, Vec2::ZERO, color));
vertices.push(create_vertex(center + offset));

indices.extend_from_slice(&[
center_vertex as u16,
Expand All @@ -265,7 +284,8 @@ impl RoundedRectangle {
3.0 * TAU / 4.0,
);

let mesh = PaintMesh::new(vertices, indices);
let mut mesh = PaintMesh::new(vertices, indices);
mesh.texture = self.texture;
output.add_mesh(mesh);
}
}
9 changes: 6 additions & 3 deletions crates/yakui-widgets/src/widgets/cutout.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::shapes::RoundedRectangle;
use yakui_core::geometry::{Color, Constraints, Rect, Vec2};
use yakui_core::paint::PaintRect;
use yakui_core::widget::{LayoutContext, PaintContext, Widget};
use yakui_core::{Response, TextureId};

Expand All @@ -18,6 +18,7 @@ pub struct CutOut {
pub image_color: Color,
pub overlay_color: Color,
pub min_size: Vec2,
pub radius: f32,
}

impl CutOut {
Expand All @@ -30,6 +31,7 @@ impl CutOut {
image_color: Color::WHITE,
overlay_color,
min_size: Vec2::ZERO,
radius: 0.0,
}
}

Expand Down Expand Up @@ -60,6 +62,7 @@ impl Widget for CutOutWidget {
image_color: Color::WHITE,
overlay_color: Color::CLEAR,
min_size: Vec2::ZERO,
radius: 0.0,
},
}
}
Expand Down Expand Up @@ -91,13 +94,13 @@ impl Widget for CutOutWidget {
layout_node.rect.size() / ctx.layout.viewport().size(),
);

let mut rect = PaintRect::new(layout_node.rect);
let mut rect = RoundedRectangle::new(layout_node.rect, self.props.radius);
rect.color = self.props.image_color;
rect.texture = Some((image, texture_rect));
rect.add(ctx.paint);
}

let mut rect = PaintRect::new(layout_node.rect);
let mut rect = RoundedRectangle::new(layout_node.rect, self.props.radius);
rect.color = self.props.overlay_color;
rect.add(ctx.paint);

Expand Down
12 changes: 7 additions & 5 deletions crates/yakui/examples/blur.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ pub fn run(state: &mut ExampleState) {
let mut col = List::column();
col.cross_axis_alignment = CrossAxisAlignment::Stretch;
col.show(|| {
CutOut::new(state.monkey_blurred, Color::hex(0x5cc9ff).with_alpha(0.25))
.show_children(|| {
Pad::all(16.0).show(|| {
text(48.0, "Blur Demo");
});
let mut cut_out =
CutOut::new(state.monkey_blurred, Color::hex(0x5cc9ff).with_alpha(0.25));
cut_out.radius = 25.0;
cut_out.show_children(|| {
Pad::all(16.0).show(|| {
text(48.0, "Blur Demo");
});
});

CutOut::new(state.monkey_blurred, Color::hex(0x444444).with_alpha(0.1))
.show_children(|| {
Expand Down
Loading