fix(geom): calculate tight ellipse and arc bounds analytically (#211)
This commit is contained in:
parent
a560048dd0
commit
f38b3a6447
|
|
@ -256,9 +256,22 @@ impl Ellipse {
|
|||
}
|
||||
}
|
||||
pub fn bounds(&self) -> Bounds2 {
|
||||
let mut pts = Vec::new();
|
||||
self.tessellate(self.major.len() * 1e-4, &mut pts);
|
||||
Bounds2::from_points(pts)
|
||||
// Sampled bounds can cut off the true extrema, especially on a small arc or
|
||||
// a large drawing. The derivative of each coordinate gives two exact
|
||||
// candidate parameters (opposite sides of the ellipse).
|
||||
let minor = self.minor();
|
||||
let mut bounds = Bounds2::from_points([self.at_param(self.start), self.at_param(self.end)]);
|
||||
for angle in [
|
||||
minor.x.atan2(self.major.x),
|
||||
minor.x.atan2(self.major.x) + PI,
|
||||
minor.y.atan2(self.major.y),
|
||||
minor.y.atan2(self.major.y) + PI,
|
||||
] {
|
||||
if angle_in_sweep(angle, self.start, self.end) {
|
||||
bounds.add(self.at_param(angle));
|
||||
}
|
||||
}
|
||||
bounds
|
||||
}
|
||||
/// Parameter of the point on the ellipse nearest the direction of `p` (approximate).
|
||||
pub fn param_of(&self, p: Vec2) -> f64 {
|
||||
|
|
|
|||
|
|
@ -206,3 +206,33 @@ fn circle_three_points_accepts_small_valid_triangles() {
|
|||
assert!(Circle::from_3_points(Vec2::ZERO, Vec2::new(r, 0.0), Vec2::new(2.0 * r, 0.0)).is_none());
|
||||
assert!(Circle::from_3_points(Vec2::ZERO, Vec2::X, Vec2::new(f64::NAN, 0.0)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ellipse_bounds_find_analytic_extrema() {
|
||||
// A tilted ellipse's extrema generally fall between tessellated samples.
|
||||
let center = Vec2::new(7.0, -11.0);
|
||||
let e = Ellipse::full(center, Vec2::new(3.0, 4.0), 0.5);
|
||||
let bounds = e.bounds();
|
||||
let dx = 13.0_f64.sqrt(); // hypot(major.x, minor.x)
|
||||
let dy = 18.25_f64.sqrt(); // hypot(major.y, minor.y)
|
||||
assert!((bounds.min.x - (center.x - dx)).abs() < 1e-12);
|
||||
assert!((bounds.max.x - (center.x + dx)).abs() < 1e-12);
|
||||
assert!((bounds.min.y - (center.y - dy)).abs() < 1e-12);
|
||||
assert!((bounds.max.y - (center.y + dy)).abs() < 1e-12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elliptical_arc_bounds_exclude_extrema_outside_the_sweep() {
|
||||
let e = Ellipse {
|
||||
center: Vec2::ZERO,
|
||||
major: Vec2::new(3.0, 4.0),
|
||||
ratio: 0.5,
|
||||
start: 0.0,
|
||||
end: PI / 4.0,
|
||||
};
|
||||
let bounds = e.bounds();
|
||||
assert!((bounds.max.x - 3.0).abs() < 1e-12);
|
||||
assert!((bounds.min.x - e.at_param(e.end).x).abs() < 1e-12);
|
||||
assert!((bounds.min.y - e.at_param(e.end).y).abs() < 1e-12);
|
||||
assert!((bounds.max.y - 18.25_f64.sqrt()).abs() < 1e-12);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue