1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-31 22:00:03 -04:00

Allow rotation of rectangles and ellipses (#7682)

<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md)
before opening a Pull Request!

* Keep your PR:s small and focused.
* The PR title is what ends up in the changelog, so make it descriptive!
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to test and add commits to your PR.
* Remember to run `cargo fmt` and `cargo clippy`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.

Please be patient! I will review your PR, but my time is limited!
-->

Added the ability to rotate rectangles and ellipses. Similar to the
existing text implementation

* [x ] I have followed the instructions in the PR template
This commit is contained in:
Ryan Bluth
2026-03-24 08:58:02 -04:00
committed by GitHub
parent 1e4619c5ef
commit 5d5f0dedcc
8 changed files with 208 additions and 5 deletions

View File

@@ -10,6 +10,9 @@ pub struct EllipseShape {
pub radius: Vec2,
pub fill: Color32,
pub stroke: Stroke,
/// Rotate ellipse by this many radians clockwise around its center.
pub angle: f32,
}
impl EllipseShape {
@@ -20,6 +23,7 @@ impl EllipseShape {
radius,
fill: fill_color.into(),
stroke: Default::default(),
angle: 0.0,
}
}
@@ -30,18 +34,38 @@ impl EllipseShape {
radius,
fill: Default::default(),
stroke: stroke.into(),
angle: 0.0,
}
}
/// Set the rotation of the ellipse (in radians, clockwise).
/// The ellipse rotates around its center.
#[inline]
pub fn with_angle(mut self, angle: f32) -> Self {
self.angle = angle;
self
}
/// Set the rotation of the ellipse (in radians, clockwise) around a custom pivot point.
#[inline]
pub fn with_angle_and_pivot(mut self, angle: f32, pivot: Pos2) -> Self {
self.angle = angle;
let rot = emath::Rot2::from_angle(angle);
self.center = pivot + rot * (self.center - pivot);
self
}
/// 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,
let rect = Rect::from_center_size(
Pos2::ZERO,
self.radius * 2.0 + Vec2::splat(self.stroke.width),
)
);
rect.rotate_bb(emath::Rot2::from_angle(self.angle))
.translate(self.center.to_vec2())
}
}
}

View File

@@ -54,13 +54,16 @@ pub struct RectShape {
/// Since most rectangles do not have a texture, this is optional and in an `Arc`,
/// so that [`RectShape`] is kept small..
pub brush: Option<Arc<Brush>>,
/// Rotate rectangle by this many radians clockwise around its center.
pub angle: f32,
}
#[test]
fn rect_shape_size() {
assert_eq!(
std::mem::size_of::<RectShape>(),
48,
56,
"RectShape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it."
);
assert!(
@@ -88,6 +91,7 @@ impl RectShape {
round_to_pixels: None,
blur_width: 0.0,
brush: Default::default(),
angle: 0.0,
}
}
@@ -157,6 +161,25 @@ impl RectShape {
self
}
/// Set the rotation of the rectangle (in radians, clockwise).
/// The rectangle rotates around its center.
#[inline]
pub fn with_angle(mut self, angle: f32) -> Self {
self.angle = angle;
self
}
/// Set the rotation of the rectangle (in radians, clockwise) around a custom pivot point.
#[inline]
pub fn with_angle_and_pivot(mut self, angle: f32, pivot: Pos2) -> Self {
self.angle = angle;
let rot = emath::Rot2::from_angle(angle);
let center = self.rect.center();
let new_center = pivot + rot * (center - pivot);
self.rect = self.rect.translate(new_center - center);
self
}
/// The visual bounding rectangle (includes stroke width)
#[inline]
pub fn visual_bounding_rect(&self) -> Rect {
@@ -168,7 +191,17 @@ impl RectShape {
StrokeKind::Middle => self.stroke.width / 2.0,
StrokeKind::Outside => self.stroke.width,
};
self.rect.expand(expand + self.blur_width / 2.0)
let expanded = self.rect.expand(expand + self.blur_width / 2.0);
if self.angle == 0.0 {
expanded
} else {
// Rotate around the rectangle's center and compute bounding box
let center = self.rect.center();
let rect_relative = Rect::from_center_size(Pos2::ZERO, expanded.size());
rect_relative
.rotate_bb(emath::Rot2::from_angle(self.angle))
.translate(center.to_vec2())
}
}
}