From eca5543b648a80458b67c9d62643d5c08db4a632 Mon Sep 17 00:00:00 2001 From: 4JX <79868816+4JX@users.noreply.github.com> Date: Fri, 4 Feb 2022 17:38:57 +0100 Subject: [PATCH 1/5] Add the ability to adress each corner's radius individually --- egui/src/containers/frame.rs | 10 +-- egui/src/containers/window.rs | 32 +++++----- egui/src/lib.rs | 4 +- egui/src/painter.rs | 24 ++++--- egui/src/style.rs | 43 ++++++++----- egui/src/widgets/button.rs | 7 ++- egui/src/widgets/color_picker.rs | 10 ++- egui/src/widgets/plot/items/bar.rs | 4 +- egui/src/widgets/plot/items/box_elem.rs | 4 +- egui/src/widgets/plot/mod.rs | 2 +- epaint/src/lib.rs | 2 +- epaint/src/shadow.rs | 18 ++++-- epaint/src/shape.rs | 83 ++++++++++++++++++++++--- epaint/src/tessellator.rs | 57 +++++++++++------ 14 files changed, 212 insertions(+), 88 deletions(-) diff --git a/egui/src/containers/frame.rs b/egui/src/containers/frame.rs index fd85819d89b..26dcf34e077 100644 --- a/egui/src/containers/frame.rs +++ b/egui/src/containers/frame.rs @@ -9,7 +9,7 @@ use epaint::*; pub struct Frame { /// On each side pub margin: Vec2, - pub corner_radius: f32, + pub corner_radius: Rounding, pub shadow: Shadow, pub fill: Color32, pub stroke: Stroke, @@ -33,7 +33,7 @@ impl Frame { pub(crate) fn side_top_panel(style: &Style) -> Self { Self { margin: Vec2::new(8.0, 2.0), - corner_radius: 0.0, + corner_radius: Rounding::none(), fill: style.visuals.window_fill(), stroke: style.visuals.window_stroke(), ..Default::default() @@ -43,7 +43,7 @@ impl Frame { pub(crate) fn central_panel(style: &Style) -> Self { Self { margin: Vec2::new(8.0, 8.0), - corner_radius: 0.0, + corner_radius: Rounding::none(), fill: style.visuals.window_fill(), stroke: Default::default(), ..Default::default() @@ -103,8 +103,8 @@ impl Frame { self } - pub fn corner_radius(mut self, corner_radius: f32) -> Self { - self.corner_radius = corner_radius; + pub fn corner_radius(mut self, corner_radius: impl Into) -> Self { + self.corner_radius = corner_radius.into(); self } diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index 62a620f4e27..c2794e7f1c1 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -705,36 +705,36 @@ fn paint_frame_interaction( let mut points = Vec::new(); if interaction.right && !interaction.bottom && !interaction.top { - points.push(pos2(max.x, min.y + cr)); - points.push(pos2(max.x, max.y - cr)); + points.push(pos2(max.x, min.y + cr.ne)); + points.push(pos2(max.x, max.y - cr.se)); } if interaction.right && interaction.bottom { - points.push(pos2(max.x, min.y + cr)); - points.push(pos2(max.x, max.y - cr)); - add_circle_quadrant(&mut points, pos2(max.x - cr, max.y - cr), cr, 0.0); + points.push(pos2(max.x, min.y + cr.ne)); + points.push(pos2(max.x, max.y - cr.se)); + add_circle_quadrant(&mut points, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); } if interaction.bottom { - points.push(pos2(max.x - cr, max.y)); - points.push(pos2(min.x + cr, max.y)); + points.push(pos2(max.x - cr.se, max.y)); + points.push(pos2(min.x + cr.sw, max.y)); } if interaction.left && interaction.bottom { - add_circle_quadrant(&mut points, pos2(min.x + cr, max.y - cr), cr, 1.0); + add_circle_quadrant(&mut points, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); } if interaction.left { - points.push(pos2(min.x, max.y - cr)); - points.push(pos2(min.x, min.y + cr)); + points.push(pos2(min.x, max.y - cr.sw)); + points.push(pos2(min.x, min.y + cr.nw)); } if interaction.left && interaction.top { - add_circle_quadrant(&mut points, pos2(min.x + cr, min.y + cr), cr, 2.0); + add_circle_quadrant(&mut points, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); } if interaction.top { - points.push(pos2(min.x + cr, min.y)); - points.push(pos2(max.x - cr, min.y)); + points.push(pos2(min.x + cr.nw, min.y)); + points.push(pos2(max.x - cr.ne, min.y)); } if interaction.right && interaction.top { - add_circle_quadrant(&mut points, pos2(max.x - cr, min.y + cr), cr, 3.0); - points.push(pos2(max.x, min.y + cr)); - points.push(pos2(max.x, max.y - cr)); + add_circle_quadrant(&mut points, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); + points.push(pos2(max.x, min.y + cr.ne)); + points.push(pos2(max.x, max.y - cr.se)); } ui.painter().add(Shape::line(points, visuals.bg_stroke)); } diff --git a/egui/src/lib.rs b/egui/src/lib.rs index 5055095bc7f..646780f39b7 100644 --- a/egui/src/lib.rs +++ b/egui/src/lib.rs @@ -387,8 +387,8 @@ pub use epaint::{ color, mutex, text::{FontData, FontDefinitions, FontFamily, FontId}, textures::TexturesDelta, - AlphaImage, ClippedMesh, Color32, ColorImage, ImageData, Rgba, Shape, Stroke, TextureHandle, - TextureId, + AlphaImage, ClippedMesh, Color32, ColorImage, ImageData, Rgba, Rounding, Shape, Stroke, + TextureHandle, TextureId, }; pub mod text { diff --git a/egui/src/painter.rs b/egui/src/painter.rs index 111e130e6b5..119903d20cf 100644 --- a/egui/src/painter.rs +++ b/egui/src/painter.rs @@ -6,7 +6,7 @@ use crate::{ use epaint::{ mutex::{Arc, RwLockReadGuard, RwLockWriteGuard}, text::{Fonts, Galley}, - CircleShape, RectShape, Shape, Stroke, TextShape, + CircleShape, RectShape, Rounding, Shape, Stroke, TextShape, }; /// Helper to paint shapes and text to a specific region on a specific layer. @@ -278,31 +278,41 @@ impl Painter { pub fn rect( &self, rect: Rect, - corner_radius: f32, + corner_radius: impl Into, fill_color: impl Into, stroke: impl Into, ) { self.add(RectShape { rect, - corner_radius, + corner_radius: corner_radius.into(), fill: fill_color.into(), stroke: stroke.into(), }); } - pub fn rect_filled(&self, rect: Rect, corner_radius: f32, fill_color: impl Into) { + pub fn rect_filled( + &self, + rect: Rect, + corner_radius: impl Into, + fill_color: impl Into, + ) { self.add(RectShape { rect, - corner_radius, + corner_radius: corner_radius.into(), fill: fill_color.into(), stroke: Default::default(), }); } - pub fn rect_stroke(&self, rect: Rect, corner_radius: f32, stroke: impl Into) { + pub fn rect_stroke( + &self, + rect: Rect, + corner_radius: impl Into, + stroke: impl Into, + ) { self.add(RectShape { rect, - corner_radius, + corner_radius: corner_radius.into(), fill: Default::default(), stroke: stroke.into(), }); diff --git a/egui/src/style.rs b/egui/src/style.rs index 084f777289b..fa678efcc51 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -3,7 +3,7 @@ #![allow(clippy::if_same_then_else)] use crate::{color::*, emath::*, FontFamily, FontId, Response, RichText, WidgetText}; -use epaint::{mutex::Arc, Shadow, Stroke}; +use epaint::{mutex::Arc, Rounding, Shadow, Stroke}; use std::collections::BTreeMap; // ---------------------------------------------------------------------------- @@ -344,7 +344,7 @@ pub struct Visuals { /// Background color behind code-styled monospaced labels. pub code_bg_color: Color32, - pub window_corner_radius: f32, + pub window_corner_radius: Rounding, pub window_shadow: Shadow, pub popup_shadow: Shadow, @@ -453,7 +453,7 @@ pub struct WidgetVisuals { pub bg_stroke: Stroke, /// Button frames etc. - pub corner_radius: f32, + pub corner_radius: Rounding, /// Stroke and text color of the interactive part of a component (button text, slider grab, check-mark, …). pub fg_stroke: Stroke, @@ -566,7 +566,7 @@ impl Visuals { faint_bg_color: Color32::from_gray(24), extreme_bg_color: Color32::from_gray(10), code_bg_color: Color32::from_gray(64), - window_corner_radius: 6.0, + window_corner_radius: Rounding::same(6.0), window_shadow: Shadow::big_dark(), popup_shadow: Shadow::small_dark(), resize_corner_size: 12.0, @@ -629,35 +629,35 @@ impl Widgets { bg_fill: Color32::from_gray(27), // window background bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // separators, indentation lines, windows outlines fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // normal text color - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 0.0, }, inactive: WidgetVisuals { bg_fill: Color32::from_gray(60), // button background bg_stroke: Default::default(), fg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // button text - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 0.0, }, hovered: WidgetVisuals { bg_fill: Color32::from_gray(70), bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button fg_stroke: Stroke::new(1.5, Color32::from_gray(240)), - corner_radius: 3.0, + corner_radius: Rounding::same(3.0), expansion: 1.0, }, active: WidgetVisuals { bg_fill: Color32::from_gray(55), bg_stroke: Stroke::new(1.0, Color32::WHITE), fg_stroke: Stroke::new(2.0, Color32::WHITE), - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 1.0, }, open: WidgetVisuals { bg_fill: Color32::from_gray(27), bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), fg_stroke: Stroke::new(1.0, Color32::from_gray(210)), - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 0.0, }, } @@ -669,35 +669,35 @@ impl Widgets { bg_fill: Color32::from_gray(235), // window background bg_stroke: Stroke::new(1.0, Color32::from_gray(190)), // separators, indentation lines, windows outlines fg_stroke: Stroke::new(1.0, Color32::from_gray(100)), // normal text color - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 0.0, }, inactive: WidgetVisuals { bg_fill: Color32::from_gray(215), // button background bg_stroke: Default::default(), fg_stroke: Stroke::new(1.0, Color32::from_gray(80)), // button text - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 0.0, }, hovered: WidgetVisuals { bg_fill: Color32::from_gray(210), bg_stroke: Stroke::new(1.0, Color32::from_gray(105)), // e.g. hover over window edge or button fg_stroke: Stroke::new(1.5, Color32::BLACK), - corner_radius: 3.0, + corner_radius: Rounding::same(3.0), expansion: 1.0, }, active: WidgetVisuals { bg_fill: Color32::from_gray(165), bg_stroke: Stroke::new(1.0, Color32::BLACK), fg_stroke: Stroke::new(2.0, Color32::BLACK), - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 1.0, }, open: WidgetVisuals { bg_fill: Color32::from_gray(220), bg_stroke: Stroke::new(1.0, Color32::from_gray(160)), fg_stroke: Stroke::new(1.0, Color32::BLACK), - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), expansion: 0.0, }, } @@ -949,7 +949,12 @@ impl WidgetVisuals { } = self; ui_color(ui, bg_fill, "bg_fill"); stroke_ui(ui, bg_stroke, "bg_stroke"); - ui.add(Slider::new(corner_radius, 0.0..=10.0).text("corner_radius")); + + ui.add(Slider::new(&mut corner_radius.nw, 0.0..=10.0).text("corner_radius_nw")); + ui.add(Slider::new(&mut corner_radius.ne, 0.0..=10.0).text("corner_radius_ne")); + ui.add(Slider::new(&mut corner_radius.sw, 0.0..=10.0).text("corner_radius_sw")); + ui.add(Slider::new(&mut corner_radius.se, 0.0..=10.0).text("corner_radius_se")); + stroke_ui(ui, fg_stroke, "fg_stroke (text)"); ui.add(Slider::new(expansion, -5.0..=5.0).text("expansion")) .on_hover_text("make shapes this much larger"); @@ -1024,7 +1029,13 @@ impl Visuals { // Common shortcuts ui_color(ui, &mut widgets.noninteractive.bg_fill, "Fill"); stroke_ui(ui, &mut widgets.noninteractive.bg_stroke, "Outline"); - ui.add(Slider::new(window_corner_radius, 0.0..=20.0).text("Rounding")); + + ui.label("Rounding"); + ui.add(Slider::new(&mut window_corner_radius.nw, 0.0..=20.0).text("Top Left")); + ui.add(Slider::new(&mut window_corner_radius.ne, 0.0..=20.0).text("Top Right")); + ui.add(Slider::new(&mut window_corner_radius.sw, 0.0..=20.0).text("Bottom Left")); + ui.add(Slider::new(&mut window_corner_radius.se, 0.0..=20.0).text("Bottom Right")); + shadow_ui(ui, window_shadow, "Shadow"); shadow_ui(ui, popup_shadow, "Shadow (small menus and popups)"); }); diff --git a/egui/src/widgets/button.rs b/egui/src/widgets/button.rs index 919957eccc5..f1373a60f37 100644 --- a/egui/src/widgets/button.rs +++ b/egui/src/widgets/button.rs @@ -457,7 +457,12 @@ impl Widget for ImageButton { if ui.is_rect_visible(rect) { let (expansion, corner_radius, fill, stroke) = if selected { let selection = ui.visuals().selection; - (-padding, 0.0, selection.bg_fill, selection.stroke) + ( + -padding, + Rounding::none(), + selection.bg_fill, + selection.stroke, + ) } else if frame { let visuals = ui.style().interact(&response); let expansion = if response.hovered { diff --git a/egui/src/widgets/color_picker.rs b/egui/src/widgets/color_picker.rs index 1cc31497fed..4afa47c43e6 100644 --- a/egui/src/widgets/color_picker.rs +++ b/egui/src/widgets/color_picker.rs @@ -61,7 +61,7 @@ fn show_hsva(ui: &mut Ui, color: Hsva, desired_size: Vec2) -> Response { } else { ui.painter().add(RectShape { rect, - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), fill: color.into(), stroke: Stroke::new(3.0, color.to_opaque()), }); @@ -90,7 +90,13 @@ fn color_button(ui: &mut Ui, color: Color32, open: bool) -> Response { ui.painter().rect_filled(left_half, 0.0, color); ui.painter().rect_filled(right_half, 0.0, color.to_opaque()); - let corner_radius = visuals.corner_radius.at_most(2.0); + let corner_radius = Rounding { + nw: visuals.corner_radius.nw.at_most(2.0), + ne: visuals.corner_radius.ne.at_most(2.0), + sw: visuals.corner_radius.sw.at_most(2.0), + se: visuals.corner_radius.se.at_most(2.0), + }; + ui.painter() .rect_stroke(rect, corner_radius, (2.0, visuals.bg_fill)); // fill is intentional, because default style has no border } diff --git a/egui/src/widgets/plot/items/bar.rs b/egui/src/widgets/plot/items/bar.rs index 4453e9aeccf..5c0546ae3a3 100644 --- a/egui/src/widgets/plot/items/bar.rs +++ b/egui/src/widgets/plot/items/bar.rs @@ -1,5 +1,5 @@ use crate::emath::NumExt; -use crate::epaint::{Color32, RectShape, Shape, Stroke}; +use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke}; use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement}; use crate::plot::{BarChart, ScreenTransform, Value}; @@ -129,7 +129,7 @@ impl Bar { let rect = transform.rect_from_values(&self.bounds_min(), &self.bounds_max()); let rect = Shape::Rect(RectShape { rect, - corner_radius: 0.0, + corner_radius: Rounding::none(), fill, stroke, }); diff --git a/egui/src/widgets/plot/items/box_elem.rs b/egui/src/widgets/plot/items/box_elem.rs index 451cba48aba..8a9fe54e34d 100644 --- a/egui/src/widgets/plot/items/box_elem.rs +++ b/egui/src/widgets/plot/items/box_elem.rs @@ -1,5 +1,5 @@ use crate::emath::NumExt; -use crate::epaint::{Color32, RectShape, Shape, Stroke}; +use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke}; use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement}; use crate::plot::{BoxPlot, ScreenTransform, Value}; @@ -152,7 +152,7 @@ impl BoxElem { ); let rect = Shape::Rect(RectShape { rect, - corner_radius: 0.0, + corner_radius: Rounding::none(), fill, stroke, }); diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 2cc54bf75be..d3aae10c459 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -479,7 +479,7 @@ impl Plot { if show_background { ui.painter().sub_region(rect).add(epaint::RectShape { rect, - corner_radius: 2.0, + corner_radius: Rounding::same(2.0), fill: ui.visuals().extreme_bg_color, stroke: ui.visuals().widgets.noninteractive.bg_stroke, }); diff --git a/epaint/src/lib.rs b/epaint/src/lib.rs index 48df065bb69..55e6b7b7672 100644 --- a/epaint/src/lib.rs +++ b/epaint/src/lib.rs @@ -110,7 +110,7 @@ pub use { image::{AlphaImage, ColorImage, ImageData, ImageDelta}, mesh::{Mesh, Mesh16, Vertex}, shadow::Shadow, - shape::{CircleShape, PathShape, RectShape, Shape, TextShape}, + shape::{CircleShape, PathShape, RectShape, Rounding, Shape, TextShape}, stats::PaintStats, stroke::Stroke, tessellator::{tessellate_shapes, TessellationOptions, Tessellator}, diff --git a/epaint/src/shadow.rs b/epaint/src/shadow.rs index 65cbb2577df..90987ea35ae 100644 --- a/epaint/src/shadow.rs +++ b/epaint/src/shadow.rs @@ -46,17 +46,23 @@ impl Shadow { } } - pub fn tessellate(&self, rect: emath::Rect, corner_radius: f32) -> Mesh { + pub fn tessellate(&self, rect: emath::Rect, corner_radius: impl Into) -> Mesh { // tessellator.clip_rect = clip_rect; // TODO: culling let Self { extrusion, color } = *self; + let cr: Rounding = corner_radius.into(); + let half_ext = 0.5 * extrusion; + + let ext_corner_radius = Rounding { + nw: cr.nw + half_ext, + ne: cr.ne + half_ext, + sw: cr.sw + half_ext, + se: cr.se + half_ext, + }; + use crate::tessellator::*; - let rect = RectShape::filled( - rect.expand(0.5 * extrusion), - corner_radius + 0.5 * extrusion, - color, - ); + let rect = RectShape::filled(rect.expand(half_ext), ext_corner_radius, color); let mut tessellator = Tessellator::from_options(TessellationOptions { aa_size: extrusion, anti_alias: true, diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index 4bdf242a466..59e3ea533de 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -114,12 +114,20 @@ impl Shape { } #[inline] - pub fn rect_filled(rect: Rect, corner_radius: f32, fill_color: impl Into) -> Self { + pub fn rect_filled( + rect: Rect, + corner_radius: impl Into, + fill_color: impl Into, + ) -> Self { Self::Rect(RectShape::filled(rect, corner_radius, fill_color)) } #[inline] - pub fn rect_stroke(rect: Rect, corner_radius: f32, stroke: impl Into) -> Self { + pub fn rect_stroke( + rect: Rect, + corner_radius: impl Into, + stroke: impl Into, + ) -> Self { Self::Rect(RectShape::stroke(rect, corner_radius, stroke)) } @@ -321,28 +329,36 @@ impl From for Shape { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct RectShape { pub rect: Rect, - /// How rounded the corners are. Use `0.0` for no rounding. - pub corner_radius: f32, + /// How rounded the corners are. Use `Rounding::none()` for no rounding. + pub corner_radius: Rounding, pub fill: Color32, pub stroke: Stroke, } impl RectShape { #[inline] - pub fn filled(rect: Rect, corner_radius: f32, fill_color: impl Into) -> Self { + pub fn filled( + rect: Rect, + corner_radius: impl Into, + fill_color: impl Into, + ) -> Self { Self { rect, - corner_radius, + corner_radius: corner_radius.into(), fill: fill_color.into(), stroke: Default::default(), } } #[inline] - pub fn stroke(rect: Rect, corner_radius: f32, stroke: impl Into) -> Self { + pub fn stroke( + rect: Rect, + corner_radius: impl Into, + stroke: impl Into, + ) -> Self { Self { rect, - corner_radius, + corner_radius: corner_radius.into(), fill: Default::default(), stroke: stroke.into(), } @@ -362,6 +378,57 @@ impl From for Shape { } } +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +/// How rounded the corners of things should be +pub struct Rounding { + pub nw: f32, + pub ne: f32, + pub sw: f32, + pub se: f32, +} + +impl Default for Rounding { + fn default() -> Self { + Self::none() + } +} + +impl From for Rounding { + fn from(radius: f32) -> Self { + Self { + nw: radius, + ne: radius, + sw: radius, + se: radius, + } + } +} + +impl Rounding { + pub fn new(nw: f32, ne: f32, sw: f32, se: f32) -> Self { + Self { nw, ne, sw, se } + } + + pub fn same(radius: f32) -> Self { + Self { + nw: radius, + ne: radius, + sw: radius, + se: radius, + } + } + + pub fn none() -> Self { + Self { + nw: 0.0, + ne: 0.0, + sw: 0.0, + se: 0.0, + } + } +} + // ---------------------------------------------------------------------------- /// How to paint some text on screen. diff --git a/epaint/src/tessellator.rs b/epaint/src/tessellator.rs index 04137960b4d..358c54dcc3e 100644 --- a/epaint/src/tessellator.rs +++ b/epaint/src/tessellator.rs @@ -179,33 +179,23 @@ impl Path { pub mod path { //! Helpers for constructing paths + use crate::shape::Rounding; + use super::*; /// overwrites existing points - pub fn rounded_rectangle(path: &mut Vec, rect: Rect, corner_radius: f32) { + pub fn rounded_rectangle(path: &mut Vec, rect: Rect, corner_radius: Rounding) { path.clear(); let min = rect.min; let max = rect.max; - let cr = corner_radius - .min(rect.width() * 0.5) - .min(rect.height() * 0.5); - - if cr <= 0.0 { - let min = rect.min; - let max = rect.max; - path.reserve(4); - path.push(pos2(min.x, min.y)); - path.push(pos2(max.x, min.y)); - path.push(pos2(max.x, max.y)); - path.push(pos2(min.x, max.y)); - } else { - add_circle_quadrant(path, pos2(max.x - cr, max.y - cr), cr, 0.0); - add_circle_quadrant(path, pos2(min.x + cr, max.y - cr), cr, 1.0); - add_circle_quadrant(path, pos2(min.x + cr, min.y + cr), cr, 2.0); - add_circle_quadrant(path, pos2(max.x - cr, min.y + cr), cr, 3.0); - } + let cr = clamp_radius(corner_radius, rect); + + add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); + add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); + add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); + add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); } /// Add one quadrant of a circle @@ -242,6 +232,35 @@ pub mod path { path.push(center + radius * Vec2::angled(angle)); } } + + // Ensures the radius of each corner is within a valid range + fn clamp_radius(corner_radius: Rounding, rect: Rect) -> Rounding { + let half_width = rect.width() * 0.5; + let half_height = rect.height() * 0.5; + + Rounding { + nw: corner_radius + .nw + .min(half_width) + .min(half_height) + .at_least(0.0), + ne: corner_radius + .ne + .min(half_width) + .min(half_height) + .at_least(0.0), + sw: corner_radius + .sw + .min(half_width) + .min(half_height) + .at_least(0.0), + se: corner_radius + .se + .min(half_width) + .min(half_height) + .at_least(0.0), + } + } } // ---------------------------------------------------------------------------- From 2d0c2798f7e4e9f0a9383d280ba80ded49022af4 Mon Sep 17 00:00:00 2001 From: 4JX <79868816+4JX@users.noreply.github.com> Date: Sat, 5 Feb 2022 13:32:21 +0100 Subject: [PATCH 2/5] Add inlining Co-authored-by: Emil Ernerfeldt --- epaint/src/shape.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index 59e3ea533de..48ad1e00e96 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -389,12 +389,14 @@ pub struct Rounding { } impl Default for Rounding { + #[inline] fn default() -> Self { Self::none() } } impl From for Rounding { + #[inline] fn from(radius: f32) -> Self { Self { nw: radius, @@ -410,6 +412,7 @@ impl Rounding { Self { nw, ne, sw, se } } + #[inline] pub fn same(radius: f32) -> Self { Self { nw: radius, @@ -419,6 +422,7 @@ impl Rounding { } } + #[inline] pub fn none() -> Self { Self { nw: 0.0, From a46499855b1e539ca1c7a1867bdf11cd13f5780d Mon Sep 17 00:00:00 2001 From: 4JX <79868816+4JX@users.noreply.github.com> Date: Sat, 5 Feb 2022 13:53:49 +0100 Subject: [PATCH 3/5] (Re)optimise the tesellator code --- epaint/src/tessellator.rs | 43 +++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/epaint/src/tessellator.rs b/epaint/src/tessellator.rs index 358c54dcc3e..0989dc9c937 100644 --- a/epaint/src/tessellator.rs +++ b/epaint/src/tessellator.rs @@ -192,10 +192,20 @@ pub mod path { let cr = clamp_radius(corner_radius, rect); - add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); - add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); - add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); - add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); + if cr == Rounding::none() { + let min = rect.min; + let max = rect.max; + path.reserve(4); + path.push(pos2(min.x, min.y)); + path.push(pos2(max.x, min.y)); + path.push(pos2(max.x, max.y)); + path.push(pos2(min.x, max.y)); + } else { + add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); + add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); + add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); + add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); + } } /// Add one quadrant of a circle @@ -237,28 +247,13 @@ pub mod path { fn clamp_radius(corner_radius: Rounding, rect: Rect) -> Rounding { let half_width = rect.width() * 0.5; let half_height = rect.height() * 0.5; + let max_cr = half_width.min(half_height); Rounding { - nw: corner_radius - .nw - .min(half_width) - .min(half_height) - .at_least(0.0), - ne: corner_radius - .ne - .min(half_width) - .min(half_height) - .at_least(0.0), - sw: corner_radius - .sw - .min(half_width) - .min(half_height) - .at_least(0.0), - se: corner_radius - .se - .min(half_width) - .min(half_height) - .at_least(0.0), + nw: corner_radius.nw.min(max_cr), + ne: corner_radius.ne.min(max_cr), + sw: corner_radius.sw.min(max_cr), + se: corner_radius.se.min(max_cr), } } } From 30495de55280e344dd81cba56e482a5041856562 Mon Sep 17 00:00:00 2001 From: 4JX <79868816+4JX@users.noreply.github.com> Date: Sat, 5 Feb 2022 13:55:34 +0100 Subject: [PATCH 4/5] Slight readability tweak --- epaint/src/tessellator.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/epaint/src/tessellator.rs b/epaint/src/tessellator.rs index 0989dc9c937..7eac496b6cc 100644 --- a/epaint/src/tessellator.rs +++ b/epaint/src/tessellator.rs @@ -250,10 +250,10 @@ pub mod path { let max_cr = half_width.min(half_height); Rounding { - nw: corner_radius.nw.min(max_cr), - ne: corner_radius.ne.min(max_cr), - sw: corner_radius.sw.min(max_cr), - se: corner_radius.se.min(max_cr), + nw: corner_radius.nw.at_most(max_cr), + ne: corner_radius.ne.at_most(max_cr), + sw: corner_radius.sw.at_most(max_cr), + se: corner_radius.se.at_most(max_cr), } } } From 69829312bb7bffc058d3e9b2a6ca799787dee1e3 Mon Sep 17 00:00:00 2001 From: 4JX <79868816+4JX@users.noreply.github.com> Date: Sat, 5 Feb 2022 14:01:19 +0100 Subject: [PATCH 5/5] Remove `new` for `Rounding` Co-authored-by: Emil Ernerfeldt --- epaint/src/shape.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/epaint/src/shape.rs b/epaint/src/shape.rs index 48ad1e00e96..33d4ad1f7e8 100644 --- a/epaint/src/shape.rs +++ b/epaint/src/shape.rs @@ -408,10 +408,6 @@ impl From for Rounding { } impl Rounding { - pub fn new(nw: f32, ne: f32, sw: f32, se: f32) -> Self { - Self { nw, ne, sw, se } - } - #[inline] pub fn same(radius: f32) -> Self { Self {