Add Shape::visual_bounding_rect()

This commit is contained in:
Emil Ernerfeldt 2022-02-21 21:40:46 +01:00
parent fd3fb726c1
commit 8f887e2ebd
4 changed files with 89 additions and 22 deletions

View file

@ -122,7 +122,7 @@ impl PaintBezier {
let shape =
QuadraticBezierShape::from_points_stroke(points, true, self.fill, self.stroke);
painter.add(epaint::RectShape::stroke(
shape.bounding_rect(),
shape.visual_bounding_rect(),
0.0,
self.bounding_box_stroke,
));
@ -133,7 +133,7 @@ impl PaintBezier {
let shape =
CubicBezierShape::from_points_stroke(points, true, self.fill, self.stroke);
painter.add(epaint::RectShape::stroke(
shape.bounding_rect(),
shape.visual_bounding_rect(),
0.0,
self.bounding_box_stroke,
));

View file

@ -74,8 +74,17 @@ impl CubicBezierShape {
pathshapes
}
/// Screen-space bounding rectangle.
pub fn bounding_rect(&self) -> Rect {
/// 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 {
self.logical_bounding_rect().expand(self.stroke.width / 2.0)
}
}
/// Logical bounding rectangle (ignoring stroke width)
pub fn logical_bounding_rect(&self) -> Rect {
//temporary solution
let (mut min_x, mut max_x) = if self.points[0].x < self.points[3].x {
(self.points[0].x, self.points[3].x)
@ -421,8 +430,17 @@ impl QuadraticBezierShape {
}
}
/// bounding box of the quadratic Bézier shape
pub fn bounding_rect(&self) -> Rect {
/// 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 {
self.logical_bounding_rect().expand(self.stroke.width / 2.0)
}
}
/// Logical bounding rectangle (ignoring stroke width)
pub fn logical_bounding_rect(&self) -> Rect {
let (mut min_x, mut max_x) = if self.points[0].x < self.points[2].x {
(self.points[0].x, self.points[2].x)
} else {
@ -755,7 +773,7 @@ mod tests {
fill: Default::default(),
stroke: Default::default(),
};
let bbox = curve.bounding_rect();
let bbox = curve.logical_bounding_rect();
assert!((bbox.min.x - 72.96).abs() < 0.01);
assert!((bbox.min.y - 27.78).abs() < 0.01);
@ -779,7 +797,7 @@ mod tests {
fill: Default::default(),
stroke: Default::default(),
};
let bbox = curve.bounding_rect();
let bbox = curve.logical_bounding_rect();
assert!((bbox.min.x - 10.0).abs() < 0.01);
assert!((bbox.min.y - 10.0).abs() < 0.01);
@ -848,7 +866,7 @@ mod tests {
stroke: Default::default(),
};
let bbox = curve.bounding_rect();
let bbox = curve.logical_bounding_rect();
assert_eq!(bbox.min.x, 10.0);
assert_eq!(bbox.min.y, 10.0);
assert_eq!(bbox.max.x, 270.0);
@ -866,7 +884,7 @@ mod tests {
stroke: Default::default(),
};
let bbox = curve.bounding_rect();
let bbox = curve.logical_bounding_rect();
assert_eq!(bbox.min.x, 10.0);
assert_eq!(bbox.min.y, 10.0);
assert!((bbox.max.x - 206.50).abs() < 0.01);
@ -884,7 +902,7 @@ mod tests {
stroke: Default::default(),
};
let bbox = curve.bounding_rect();
let bbox = curve.logical_bounding_rect();
assert!((bbox.min.x - 86.71).abs() < 0.01);
assert!((bbox.min.y - 30.0).abs() < 0.01);

View file

@ -170,6 +170,34 @@ impl Shape {
crate::epaint_assert!(mesh.is_valid());
Self::Mesh(mesh)
}
/// The visual bounding rectangle (includes stroke widths)
pub fn visual_bounding_rect(&self) -> Rect {
match self {
Self::Noop => Rect::NOTHING,
Self::Vec(shapes) => {
let mut rect = Rect::NOTHING;
for shape in shapes {
rect = rect.union(shape.visual_bounding_rect());
}
rect
}
Self::Circle(circle_shape) => circle_shape.visual_bounding_rect(),
Self::LineSegment { points, stroke } => {
if stroke.is_empty() {
Rect::NOTHING
} else {
Rect::from_two_pos(points[0], points[1]).expand(stroke.width / 2.0)
}
}
Self::Path(path_shape) => path_shape.visual_bounding_rect(),
Self::Rect(rect_shape) => rect_shape.visual_bounding_rect(),
Self::Text(text_shape) => text_shape.visual_bounding_rect(),
Self::Mesh(mesh) => mesh.calc_bounds(),
Self::QuadraticBezier(bezier) => bezier.visual_bounding_rect(),
Self::CubicBezier(bezier) => bezier.visual_bounding_rect(),
}
}
}
/// ## Inspection and transforms
@ -260,6 +288,18 @@ impl CircleShape {
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,
Vec2::splat(self.radius + self.stroke.width / 2.0),
)
}
}
}
impl From<CircleShape> for Shape {
@ -327,10 +367,14 @@ impl PathShape {
}
}
/// Screen-space bounding rectangle.
/// The visual bounding rectangle (includes stroke width)
#[inline]
pub fn bounding_rect(&self) -> Rect {
Rect::from_points(&self.points).expand(self.stroke.width)
pub fn visual_bounding_rect(&self) -> Rect {
if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
Rect::NOTHING
} else {
Rect::from_points(&self.points).expand(self.stroke.width / 2.0)
}
}
}
@ -379,10 +423,14 @@ impl RectShape {
}
}
/// Screen-space bounding rectangle.
/// The visual bounding rectangle (includes stroke width)
#[inline]
pub fn bounding_rect(&self) -> Rect {
self.rect.expand(self.stroke.width)
pub fn visual_bounding_rect(&self) -> Rect {
if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
Rect::NOTHING
} else {
self.rect.expand(self.stroke.width / 2.0)
}
}
}
@ -491,9 +539,9 @@ impl TextShape {
}
}
/// Screen-space bounding rectangle.
/// The visual bounding rectangle
#[inline]
pub fn bounding_rect(&self) -> Rect {
pub fn visual_bounding_rect(&self) -> Rect {
self.galley.mesh_bounds.translate(self.pos.to_vec2())
}
}

View file

@ -793,7 +793,7 @@ impl Tessellator {
let clip_rect = self.clip_rect;
if options.coarse_tessellation_culling
&& !quadratic_shape.bounding_rect().intersects(clip_rect)
&& !quadratic_shape.visual_bounding_rect().intersects(clip_rect)
{
return;
}
@ -816,7 +816,8 @@ impl Tessellator {
) {
let options = &self.options;
let clip_rect = self.clip_rect;
if options.coarse_tessellation_culling && !cubic_shape.bounding_rect().intersects(clip_rect)
if options.coarse_tessellation_culling
&& !cubic_shape.visual_bounding_rect().intersects(clip_rect)
{
return;
}
@ -870,7 +871,7 @@ impl Tessellator {
}
if self.options.coarse_tessellation_culling
&& !path_shape.bounding_rect().intersects(self.clip_rect)
&& !path_shape.visual_bounding_rect().intersects(self.clip_rect)
{
return;
}