Skip to content
Open
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
1 change: 1 addition & 0 deletions crates/egui_demo_lib/src/demo/tests/tessellation_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ fn rect_shape_ui(ui: &mut egui::Ui, shape: &mut RectShape) {
blur_width,
round_to_pixels,
brush: _,
angle: _,
} = shape;

let round_to_pixels = round_to_pixels.get_or_insert(true);
Expand Down
2 changes: 2 additions & 0 deletions crates/epaint/src/shape_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub fn adjust_colors(
radius: _,
fill,
stroke,
angle: _,
})
| Shape::Rect(RectShape {
rect: _,
Expand All @@ -67,6 +68,7 @@ pub fn adjust_colors(
round_to_pixels: _,
blur_width: _,
brush: _,
angle: _,
}) => {
adjust_color(fill);
adjust_color(&mut stroke.color);
Expand Down
30 changes: 27 additions & 3 deletions crates/epaint/src/shapes/ellipse_shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -20,6 +23,7 @@ impl EllipseShape {
radius,
fill: fill_color.into(),
stroke: Default::default(),
angle: 0.0,
}
}

Expand All @@ -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())
}
}
}
Expand Down
37 changes: 35 additions & 2 deletions crates/epaint/src/shapes/rect_shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down Expand Up @@ -88,6 +91,7 @@ impl RectShape {
round_to_pixels: None,
blur_width: 0.0,
brush: Default::default(),
angle: 0.0,
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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())
}
}
}

Expand Down
20 changes: 20 additions & 0 deletions crates/epaint/src/tessellator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,7 @@ impl Tessellator {
radius,
fill,
stroke,
angle,
} = shape;

if radius.x <= 0.0 || radius.y <= 0.0 {
Expand Down Expand Up @@ -1595,6 +1596,14 @@ impl Tessellator {
points.push(center + Vec2::new(0.0, -radius.y));
points.extend(quarter.iter().rev().map(|p| center + Vec2::new(p.x, -p.y)));

// Apply rotation if angle is non-zero
if angle != 0.0 {
let rot = emath::Rot2::from_angle(angle);
for point in &mut points {
*point = center + rot * (*point - center);
}
}

let path_stroke = PathStroke::from(stroke).outside();
self.scratchpad_path.clear();
self.scratchpad_path.add_line_loop(&points);
Expand Down Expand Up @@ -1772,6 +1781,7 @@ impl Tessellator {
round_to_pixels,
mut blur_width,
brush: _, // brush is extracted on its own, because it is not Copy
angle,
} = *rect_shape;

let mut corner_radius = CornerRadiusF32::from(corner_radius);
Expand Down Expand Up @@ -1939,6 +1949,16 @@ impl Tessellator {
let path = &mut self.scratchpad_path;
path.clear();
path::rounded_rectangle(&mut self.scratchpad_points, rect, corner_radius);

// Apply rotation if angle is non-zero
if angle != 0.0 {
let rot = emath::Rot2::from_angle(angle);
let center = rect.center();
for point in &mut self.scratchpad_points {
*point = center + rot * (*point - center);
}
}

path.add_line_loop(&self.scratchpad_points);

let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind);
Expand Down
Loading