From 5b61f8f401c6022b8186840cb4cf1d88c3fbae1e Mon Sep 17 00:00:00 2001 From: Eris Date: Fri, 1 Mar 2024 09:39:09 +0000 Subject: [PATCH 01/10] Add `ElipseShape` --- crates/epaint/src/shape.rs | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 033d4dfc5edc..bedde1b9aeda 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -30,6 +30,9 @@ pub enum Shape { /// Circle with optional outline and fill. Circle(CircleShape), + /// Elipse with optional outline and fill. + Elipse(ElipseShape), + /// A line between two points. LineSegment { points: [Pos2; 2], stroke: Stroke }, @@ -324,6 +327,7 @@ impl Shape { rect } Self::Circle(circle_shape) => circle_shape.visual_bounding_rect(), + Self::Elipse(elipse_shape) => elipse_shape.visual_bounding_rect(), Self::LineSegment { points, stroke } => { if stroke.is_empty() { Rect::NOTHING @@ -372,6 +376,11 @@ impl Shape { circle_shape.radius *= transform.scaling; circle_shape.stroke.width *= transform.scaling; } + Self::Elipse(elipse_shape) => { + elipse_shape.center = transform * elipse_shape.center; + elipse_shape.size *= transform.scaling; + elipse_shape.stroke.width *= transform.scaling; + } Self::LineSegment { points, stroke } => { for p in points { *p = transform * *p; @@ -480,6 +489,60 @@ impl From for Shape { // ---------------------------------------------------------------------------- +/// How to paint an elipse. +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct ElipseShape { + pub center: Pos2, + /// Size is the vector (a, b) where the width of the Elipse is 2a and the height is 2b + pub size: Vec2, + pub fill: Color32, + pub stroke: Stroke, +} + +impl ElipseShape { + #[inline] + pub fn filled(center: Pos2, size: Vec2, fill_color: impl Into) -> Self { + Self { + center, + size, + fill: fill_color.into(), + stroke: Default::default(), + } + } + + #[inline] + pub fn stroke(center: Pos2, size: Vec2, stroke: impl Into) -> Self { + Self { + center, + size, + fill: Default::default(), + stroke: stroke.into(), + } + } + + /// The visual bounding rectangle (includes stroke width) + pub fn visual_bounding_rect(&self) -> Rect { + if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() { + Rect::NOTHING + } else { + Rect::from_center_size( + self.center, + self.size * 2.0 + Vec2::splat(self.stroke.width), + ) + } + } +} + +impl From for Shape { + #[inline(always)] + fn from(shape: ElipseShape) -> Self { + Self::Elipse(shape) + } +} + +// ---------------------------------------------------------------------------- + /// A path which can be stroked and/or filled (if closed). #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] From b37285cac3fab945f6c044ed643cfe1587dbb8b3 Mon Sep 17 00:00:00 2001 From: Eris Date: Fri, 1 Mar 2024 10:05:02 +0000 Subject: [PATCH 02/10] Start of tesselate method --- crates/epaint/src/lib.rs | 4 ++-- crates/epaint/src/shape.rs | 28 ++++++++++++++-------------- crates/epaint/src/shape_transform.rs | 6 ++++++ crates/epaint/src/tessellator.rs | 27 +++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/crates/epaint/src/lib.rs b/crates/epaint/src/lib.rs index c83be860a2aa..8746d5647c3f 100644 --- a/crates/epaint/src/lib.rs +++ b/crates/epaint/src/lib.rs @@ -47,8 +47,8 @@ pub use { mesh::{Mesh, Mesh16, Vertex}, shadow::Shadow, shape::{ - CircleShape, PaintCallback, PaintCallbackInfo, PathShape, RectShape, Rounding, Shape, - TextShape, + CircleShape, EllipseShape, PaintCallback, PaintCallbackInfo, PathShape, RectShape, + Rounding, Shape, TextShape, }, stats::PaintStats, stroke::Stroke, diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index bedde1b9aeda..2dde5e5f393f 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -30,8 +30,8 @@ pub enum Shape { /// Circle with optional outline and fill. Circle(CircleShape), - /// Elipse with optional outline and fill. - Elipse(ElipseShape), + /// Ellipse with optional outline and fill. + Ellipse(EllipseShape), /// A line between two points. LineSegment { points: [Pos2; 2], stroke: Stroke }, @@ -327,7 +327,7 @@ impl Shape { rect } Self::Circle(circle_shape) => circle_shape.visual_bounding_rect(), - Self::Elipse(elipse_shape) => elipse_shape.visual_bounding_rect(), + Self::Ellipse(ellipse_shape) => ellipse_shape.visual_bounding_rect(), Self::LineSegment { points, stroke } => { if stroke.is_empty() { Rect::NOTHING @@ -376,10 +376,10 @@ impl Shape { circle_shape.radius *= transform.scaling; circle_shape.stroke.width *= transform.scaling; } - Self::Elipse(elipse_shape) => { - elipse_shape.center = transform * elipse_shape.center; - elipse_shape.size *= transform.scaling; - elipse_shape.stroke.width *= transform.scaling; + Self::Ellipse(ellipse_shape) => { + ellipse_shape.center = transform * ellipse_shape.center; + ellipse_shape.size *= transform.scaling; + ellipse_shape.stroke.width *= transform.scaling; } Self::LineSegment { points, stroke } => { for p in points { @@ -489,18 +489,18 @@ impl From for Shape { // ---------------------------------------------------------------------------- -/// How to paint an elipse. +/// How to paint an ellipse. #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct ElipseShape { +pub struct EllipseShape { pub center: Pos2, - /// Size is the vector (a, b) where the width of the Elipse is 2a and the height is 2b + /// Size is the vector (a, b) where the width of the Ellipse is 2a and the height is 2b pub size: Vec2, pub fill: Color32, pub stroke: Stroke, } -impl ElipseShape { +impl EllipseShape { #[inline] pub fn filled(center: Pos2, size: Vec2, fill_color: impl Into) -> Self { Self { @@ -534,10 +534,10 @@ impl ElipseShape { } } -impl From for Shape { +impl From for Shape { #[inline(always)] - fn from(shape: ElipseShape) -> Self { - Self::Elipse(shape) + fn from(shape: EllipseShape) -> Self { + Self::Ellipse(shape) } } diff --git a/crates/epaint/src/shape_transform.rs b/crates/epaint/src/shape_transform.rs index c8edff1fcaff..eb69e637be50 100644 --- a/crates/epaint/src/shape_transform.rs +++ b/crates/epaint/src/shape_transform.rs @@ -20,6 +20,12 @@ pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) { fill, stroke, }) + | Shape::Ellipse(EllipseShape { + center: _, + size: _, + fill, + stroke, + }) | Shape::Path(PathShape { points: _, closed: _, diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index c753b7374dfe..9427303bc4fa 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1215,6 +1215,9 @@ impl Tessellator { Shape::Circle(circle) => { self.tessellate_circle(circle, out); } + Shape::Ellipse(ellipse) => { + self.tesselate_ellipse(ellipse, out); + } Shape::Mesh(mesh) => { crate::profile_scope!("mesh"); @@ -1315,6 +1318,28 @@ impl Tessellator { .stroke_closed(self.feathering, stroke, out); } + pub fn tesselate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) { + let EllipseShape { + center, + size, + fill, + stroke, + } = shape; + + if size == Vec2::ZERO { + return; + } + + if self.options.coarse_tessellation_culling + && !self + .clip_rect + .expand2(size + Vec2::splat(stroke.width)) + .contains(center) + { + return; + } + } + /// Tessellate a single [`Mesh`] into a [`Mesh`]. /// /// * `mesh`: the mesh to tessellate. @@ -1778,6 +1803,8 @@ impl Tessellator { Shape::QuadraticBezier(_) | Shape::CubicBezier(_) => true, + Shape::Ellipse(_) => true, + Shape::Noop | Shape::Text(_) | Shape::Circle(_) From 6ab7470ede911709e2927133528d91a335c162a6 Mon Sep 17 00:00:00 2001 From: Eris Date: Fri, 1 Mar 2024 12:42:26 +0000 Subject: [PATCH 03/10] Functional Tesselation --- crates/epaint/src/shape.rs | 10 ++++++++++ crates/epaint/src/stats.rs | 1 + crates/epaint/src/tessellator.rs | 22 ++++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 2dde5e5f393f..d82699517acf 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -239,6 +239,16 @@ impl Shape { Self::Circle(CircleShape::stroke(center, radius, stroke)) } + #[inline] + pub fn ellipse_filled(center: Pos2, size: Vec2, fill_color: impl Into) -> Self { + Self::Ellipse(EllipseShape::filled(center, size, fill_color)) + } + + #[inline] + pub fn ellipse_stroke(center: Pos2, size: Vec2, stroke: impl Into) -> Self { + Self::Ellipse(EllipseShape::stroke(center, size, stroke)) + } + #[inline] pub fn rect_filled( rect: Rect, diff --git a/crates/epaint/src/stats.rs b/crates/epaint/src/stats.rs index 56eaa24ac7a8..3bb51e14170a 100644 --- a/crates/epaint/src/stats.rs +++ b/crates/epaint/src/stats.rs @@ -201,6 +201,7 @@ impl PaintStats { } Shape::Noop | Shape::Circle { .. } + | Shape::Ellipse { .. } | Shape::LineSegment { .. } | Shape::Rect { .. } | Shape::CubicBezier(_) diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 9427303bc4fa..a02e515e3110 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1318,6 +1318,10 @@ impl Tessellator { .stroke_closed(self.feathering, stroke, out); } + /// Tessellate a single [`EllipseShape`] into a [`Mesh`]. + /// + /// * `shape`: the ellipse to tessellate. + /// * `out`: triangles are appended to this. pub fn tesselate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) { let EllipseShape { center, @@ -1338,6 +1342,21 @@ impl Tessellator { { return; } + + let radius_px = (size * self.pixels_per_point).max_elem() as u32; + + let mut points = Vec::new(); + for i in 0..=radius_px { + let t = (i as f32 / radius_px as f32) * std::f32::consts::TAU; + let point = Pos2::new(center.x + size.x * f32::cos(t), center.y + size.y * f32::sin(t)); + points.push(point); + } + + self.scratchpad_path.clear(); + self.scratchpad_path.add_line_loop(&points); + self.scratchpad_path.fill(self.feathering, fill, out); + self.scratchpad_path + .stroke_closed(self.feathering, stroke, out); } /// Tessellate a single [`Mesh`] into a [`Mesh`]. @@ -1803,11 +1822,10 @@ impl Tessellator { Shape::QuadraticBezier(_) | Shape::CubicBezier(_) => true, - Shape::Ellipse(_) => true, - Shape::Noop | Shape::Text(_) | Shape::Circle(_) + | Shape::Ellipse(_) | Shape::Mesh(_) | Shape::LineSegment { .. } | Shape::Rect(_) From ffbafdf4f3d1b8ebda1692f23a063190e3277c4a Mon Sep 17 00:00:00 2001 From: Eris Date: Fri, 1 Mar 2024 22:26:20 +0000 Subject: [PATCH 04/10] Optimise num of points --- crates/epaint/src/tessellator.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index a02e515e3110..b9102d623256 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1330,7 +1330,7 @@ impl Tessellator { stroke, } = shape; - if size == Vec2::ZERO { + if size.x == 0.0 || size.y == 0.0 { return; } @@ -1343,12 +1343,16 @@ impl Tessellator { return; } - let radius_px = (size * self.pixels_per_point).max_elem() as u32; + let max_radius = (size / 2.0).max_elem() * self.pixels_per_point; + let num_points = 2f32.powf(max_radius.log2().floor()); - let mut points = Vec::new(); - for i in 0..=radius_px { - let t = (i as f32 / radius_px as f32) * std::f32::consts::TAU; - let point = Pos2::new(center.x + size.x * f32::cos(t), center.y + size.y * f32::sin(t)); + let mut points = Vec::with_capacity(num_points as usize); + for i in 0..num_points as u32 { + let t = (i as f32 / num_points) * std::f32::consts::TAU; + let point = Pos2::new( + center.x + size.x * f32::cos(t), + center.y + size.y * f32::sin(t), + ); points.push(point); } @@ -1820,12 +1824,11 @@ impl Tessellator { Shape::Path(path_shape) => 32 < path_shape.points.len(), - Shape::QuadraticBezier(_) | Shape::CubicBezier(_) => true, + Shape::QuadraticBezier(_) | Shape::CubicBezier(_) | Shape::Ellipse(_) => true, Shape::Noop | Shape::Text(_) | Shape::Circle(_) - | Shape::Ellipse(_) | Shape::Mesh(_) | Shape::LineSegment { .. } | Shape::Rect(_) From a8e432d929fa897903b4062892223b094fc8953d Mon Sep 17 00:00:00 2001 From: Eris Date: Fri, 1 Mar 2024 23:27:54 +0000 Subject: [PATCH 05/10] Quartered ellipse --- crates/epaint/src/tessellator.rs | 41 +++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index b9102d623256..2262681c8528 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -5,6 +5,8 @@ #![allow(clippy::identity_op)] +use core::num; + use crate::texture_atlas::PreparedDisc; use crate::*; use emath::*; @@ -1343,18 +1345,35 @@ impl Tessellator { return; } + // Get the max pixel radius let max_radius = (size / 2.0).max_elem() * self.pixels_per_point; - let num_points = 2f32.powf(max_radius.log2().floor()); - - let mut points = Vec::with_capacity(num_points as usize); - for i in 0..num_points as u32 { - let t = (i as f32 / num_points) * std::f32::consts::TAU; - let point = Pos2::new( - center.x + size.x * f32::cos(t), - center.y + size.y * f32::sin(t), - ); - points.push(point); - } + // Get the power of two below the radius to limit the number of vertices + let floored_radius = 2_f32.powf(max_radius.log2().floor()); + // Ensure there is at least 8 points + let num_points = f32::max(8.0, floored_radius); + + let quarter_points = num_points as u32 / 4; + let total_points = quarter_points * 4 + 4; + + // Generate points between the 0 to pi/2 + let quarter: Vec = (1..quarter_points as u32) + .map(|i| { + let t = (i as f32 / total_points as f32) * std::f32::consts::TAU; + Vec2::new(size.x * f32::cos(t), size.y * f32::sin(t)) + }) + .collect(); + + // Build the ellipse from the 4 known vertices + // filling arcs between with mirrored + let mut points = Vec::new(); + points.push(center + Vec2::new(size.x, 0.0)); + points.extend(quarter.iter().map(|p| center + *p)); + points.push(center + Vec2::new(0.0, size.y)); + points.extend(quarter.iter().rev().map(|p| center + Vec2::new(-p.x, p.y))); + points.push(center + Vec2::new(-size.x, 0.0)); + points.extend(quarter.iter().map(|p| center - *p)); + points.push(center + Vec2::new(0.0, -size.y)); + points.extend(quarter.iter().rev().map(|p| center + Vec2::new(p.x, -p.y))); self.scratchpad_path.clear(); self.scratchpad_path.add_line_loop(&points); From 8fd4400e74675c65fb431d333d51349d923bcfa6 Mon Sep 17 00:00:00 2001 From: Eris Date: Sat, 2 Mar 2024 11:12:12 +0000 Subject: [PATCH 06/10] Conform with checks.sh --- crates/epaint/src/shape.rs | 1 + crates/epaint/src/tessellator.rs | 28 +++++++++++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index d82699517acf..6ba571783281 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -504,6 +504,7 @@ impl From for Shape { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct EllipseShape { pub center: Pos2, + /// Size is the vector (a, b) where the width of the Ellipse is 2a and the height is 2b pub size: Vec2, pub fill: Color32, diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 2262681c8528..109c8ee9e159 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -5,8 +5,6 @@ #![allow(clippy::identity_op)] -use core::num; - use crate::texture_atlas::PreparedDisc; use crate::*; use emath::*; @@ -1218,7 +1216,7 @@ impl Tessellator { self.tessellate_circle(circle, out); } Shape::Ellipse(ellipse) => { - self.tesselate_ellipse(ellipse, out); + self.tessellate_ellipse(ellipse, out); } Shape::Mesh(mesh) => { crate::profile_scope!("mesh"); @@ -1324,7 +1322,7 @@ impl Tessellator { /// /// * `shape`: the ellipse to tessellate. /// * `out`: triangles are appended to this. - pub fn tesselate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) { + pub fn tessellate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) { let EllipseShape { center, size, @@ -1346,9 +1344,9 @@ impl Tessellator { } // Get the max pixel radius - let max_radius = (size / 2.0).max_elem() * self.pixels_per_point; + let max_radius = size.max_elem() * self.pixels_per_point; // Get the power of two below the radius to limit the number of vertices - let floored_radius = 2_f32.powf(max_radius.log2().floor()); + let floored_radius = 2_f32.powf(max_radius.log2().floor() - 1.0); // Ensure there is at least 8 points let num_points = f32::max(8.0, floored_radius); @@ -1356,15 +1354,15 @@ impl Tessellator { let total_points = quarter_points * 4 + 4; // Generate points between the 0 to pi/2 - let quarter: Vec = (1..quarter_points as u32) - .map(|i| { - let t = (i as f32 / total_points as f32) * std::f32::consts::TAU; - Vec2::new(size.x * f32::cos(t), size.y * f32::sin(t)) - }) - .collect(); - - // Build the ellipse from the 4 known vertices - // filling arcs between with mirrored + let quarter: Vec = (1..quarter_points) + .map(|i| { + let t = (i as f32 / total_points as f32) * std::f32::consts::TAU; + Vec2::new(size.x * f32::cos(t), size.y * f32::sin(t)) + }) + .collect(); + + // Build the ellipse from the 4 known vertices filling arcs between + // them by mirroring the points between 0 and pi/2 let mut points = Vec::new(); points.push(center + Vec2::new(size.x, 0.0)); points.extend(quarter.iter().map(|p| center + *p)); From c9a7a7f90b438778ce13b249e53d541bec8a9517 Mon Sep 17 00:00:00 2001 From: Eris Date: Sun, 3 Mar 2024 13:22:34 +0000 Subject: [PATCH 07/10] Distribute points based on an easing function --- crates/epaint/src/tessellator.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 109c8ee9e159..7e9428cd9fde 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1330,7 +1330,7 @@ impl Tessellator { stroke, } = shape; - if size.x == 0.0 || size.y == 0.0 { + if size.x <= 0.0 || size.y <= 0.0 { return; } @@ -1346,17 +1346,23 @@ impl Tessellator { // Get the max pixel radius let max_radius = size.max_elem() * self.pixels_per_point; // Get the power of two below the radius to limit the number of vertices - let floored_radius = 2_f32.powf(max_radius.log2().floor() - 1.0); - // Ensure there is at least 8 points - let num_points = f32::max(8.0, floored_radius); + let floored_radius = 2_f32.powf(max_radius.log2().floor()) as u32; + // Ensure there is at least 16 points + let num_points = u32::max(16, floored_radius) / 4; - let quarter_points = num_points as u32 / 4; - let total_points = quarter_points * 4 + 4; + // Create an ease ratio based the ellipses a and b + let ratio = ((size.y / size.x) / 2.0).clamp(0.0, 1.0); // Generate points between the 0 to pi/2 - let quarter: Vec = (1..quarter_points) + let quarter: Vec = (1..num_points) .map(|i| { - let t = (i as f32 / total_points as f32) * std::f32::consts::TAU; + let percent = i as f32 / num_points as f32; + + // Ease the percent value, concentrating points around tight bends + let eased = 2.0 * (percent - percent.powf(2.0)) * ratio + percent.powf(2.0); + + // Scale the ease to the quarter + let t = eased * std::f32::consts::FRAC_PI_2; Vec2::new(size.x * f32::cos(t), size.y * f32::sin(t)) }) .collect(); From 6abcadaf63dca8941dd3ffad34879b2353384297 Mon Sep 17 00:00:00 2001 From: Eris Date: Sat, 9 Mar 2024 16:24:27 +0000 Subject: [PATCH 08/10] Rename to radius --- crates/epaint/src/shape.rs | 16 ++++++++-------- crates/epaint/src/shape_transform.rs | 2 +- crates/epaint/src/tessellator.rs | 20 ++++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 6ba571783281..8185664f6e25 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -388,7 +388,7 @@ impl Shape { } Self::Ellipse(ellipse_shape) => { ellipse_shape.center = transform * ellipse_shape.center; - ellipse_shape.size *= transform.scaling; + ellipse_shape.radius *= transform.scaling; ellipse_shape.stroke.width *= transform.scaling; } Self::LineSegment { points, stroke } => { @@ -505,28 +505,28 @@ impl From for Shape { pub struct EllipseShape { pub center: Pos2, - /// Size is the vector (a, b) where the width of the Ellipse is 2a and the height is 2b - pub size: Vec2, + /// Radius is the vector (a, b) where the width of the Ellipse is 2a and the height is 2b + pub radius: Vec2, pub fill: Color32, pub stroke: Stroke, } impl EllipseShape { #[inline] - pub fn filled(center: Pos2, size: Vec2, fill_color: impl Into) -> Self { + pub fn filled(center: Pos2, radius: Vec2, fill_color: impl Into) -> Self { Self { center, - size, + radius, fill: fill_color.into(), stroke: Default::default(), } } #[inline] - pub fn stroke(center: Pos2, size: Vec2, stroke: impl Into) -> Self { + pub fn stroke(center: Pos2, radius: Vec2, stroke: impl Into) -> Self { Self { center, - size, + radius, fill: Default::default(), stroke: stroke.into(), } @@ -539,7 +539,7 @@ impl EllipseShape { } else { Rect::from_center_size( self.center, - self.size * 2.0 + Vec2::splat(self.stroke.width), + self.radius * 2.0 + Vec2::splat(self.stroke.width), ) } } diff --git a/crates/epaint/src/shape_transform.rs b/crates/epaint/src/shape_transform.rs index eb69e637be50..7f4d335845f2 100644 --- a/crates/epaint/src/shape_transform.rs +++ b/crates/epaint/src/shape_transform.rs @@ -22,7 +22,7 @@ pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) { }) | Shape::Ellipse(EllipseShape { center: _, - size: _, + radius: _, fill, stroke, }) diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 7e9428cd9fde..90a6e241e160 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1325,33 +1325,33 @@ impl Tessellator { pub fn tessellate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) { let EllipseShape { center, - size, + radius, fill, stroke, } = shape; - if size.x <= 0.0 || size.y <= 0.0 { + if radius.x <= 0.0 || radius.y <= 0.0 { return; } if self.options.coarse_tessellation_culling && !self .clip_rect - .expand2(size + Vec2::splat(stroke.width)) + .expand2(radius + Vec2::splat(stroke.width)) .contains(center) { return; } // Get the max pixel radius - let max_radius = size.max_elem() * self.pixels_per_point; + let max_radius = radius.max_elem() * self.pixels_per_point; // Get the power of two below the radius to limit the number of vertices let floored_radius = 2_f32.powf(max_radius.log2().floor()) as u32; // Ensure there is at least 16 points let num_points = u32::max(16, floored_radius) / 4; // Create an ease ratio based the ellipses a and b - let ratio = ((size.y / size.x) / 2.0).clamp(0.0, 1.0); + let ratio = ((radius.y / radius.x) / 2.0).clamp(0.0, 1.0); // Generate points between the 0 to pi/2 let quarter: Vec = (1..num_points) @@ -1363,20 +1363,20 @@ impl Tessellator { // Scale the ease to the quarter let t = eased * std::f32::consts::FRAC_PI_2; - Vec2::new(size.x * f32::cos(t), size.y * f32::sin(t)) + Vec2::new(radius.x * f32::cos(t), radius.y * f32::sin(t)) }) .collect(); // Build the ellipse from the 4 known vertices filling arcs between // them by mirroring the points between 0 and pi/2 let mut points = Vec::new(); - points.push(center + Vec2::new(size.x, 0.0)); + points.push(center + Vec2::new(radius.x, 0.0)); points.extend(quarter.iter().map(|p| center + *p)); - points.push(center + Vec2::new(0.0, size.y)); + points.push(center + Vec2::new(0.0, radius.y)); points.extend(quarter.iter().rev().map(|p| center + Vec2::new(-p.x, p.y))); - points.push(center + Vec2::new(-size.x, 0.0)); + points.push(center + Vec2::new(-radius.x, 0.0)); points.extend(quarter.iter().map(|p| center - *p)); - points.push(center + Vec2::new(0.0, -size.y)); + points.push(center + Vec2::new(0.0, -radius.y)); points.extend(quarter.iter().rev().map(|p| center + Vec2::new(p.x, -p.y))); self.scratchpad_path.clear(); From 899bc659d76ee1ce94f2e32ba0ff98aef0be24ee Mon Sep 17 00:00:00 2001 From: Eris Date: Sat, 9 Mar 2024 16:25:57 +0000 Subject: [PATCH 09/10] Rename to radius --- crates/epaint/src/shape.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 8185664f6e25..b64ab857b9cd 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -240,13 +240,13 @@ impl Shape { } #[inline] - pub fn ellipse_filled(center: Pos2, size: Vec2, fill_color: impl Into) -> Self { - Self::Ellipse(EllipseShape::filled(center, size, fill_color)) + pub fn ellipse_filled(center: Pos2, radius: Vec2, fill_color: impl Into) -> Self { + Self::Ellipse(EllipseShape::filled(center, radius, fill_color)) } #[inline] - pub fn ellipse_stroke(center: Pos2, size: Vec2, stroke: impl Into) -> Self { - Self::Ellipse(EllipseShape::stroke(center, size, stroke)) + pub fn ellipse_stroke(center: Pos2, radius: Vec2, stroke: impl Into) -> Self { + Self::Ellipse(EllipseShape::stroke(center, radius, stroke)) } #[inline] From 8b54616df01812ebcbf8fc0c937365654f373051 Mon Sep 17 00:00:00 2001 From: Eris Date: Sun, 17 Mar 2024 12:23:21 +0000 Subject: [PATCH 10/10] Change number of points calculation --- crates/epaint/src/tessellator.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 90a6e241e160..bf419e99b583 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1344,11 +1344,10 @@ impl Tessellator { } // Get the max pixel radius - let max_radius = radius.max_elem() * self.pixels_per_point; - // Get the power of two below the radius to limit the number of vertices - let floored_radius = 2_f32.powf(max_radius.log2().floor()) as u32; - // Ensure there is at least 16 points - let num_points = u32::max(16, floored_radius) / 4; + let max_radius = (radius.max_elem() * self.pixels_per_point) as u32; + + // Ensure there is at least 8 points in each quarter of the ellipse + let num_points = u32::max(8, max_radius / 16); // Create an ease ratio based the ellipses a and b let ratio = ((radius.y / radius.x) / 2.0).clamp(0.0, 1.0);