diff --git a/crates/metafile/src/cliprect.rs b/crates/metafile/src/cliprect.rs
index 5ecfdfc2f..d4940475a 100644
--- a/crates/metafile/src/cliprect.rs
+++ b/crates/metafile/src/cliprect.rs
@@ -111,12 +111,10 @@ pub(crate) fn clip_polygon(poly: &[P], clip: &[P]) -> Vec
{
/// can gain a vertex at every edge, so its cost isn't known from its input size alone. `None`
/// once the budget is spent.
fn clip_polygon_within(poly: &[P], clip: &[P], budget: &mut usize) -> Option> {
- if poly.len().saturating_mul(clip.len()) > *budget {
- return None;
- }
let mut poly = poly.to_vec();
for i in 0..clip.len() {
- if poly.len().saturating_mul(clip.len() - i) > *budget {
+ // The work actually done: this edge visits the polygon as it now stands.
+ if poly.len() > *budget {
return None;
}
*budget -= poly.len();
diff --git a/crates/metafile/src/tests.rs b/crates/metafile/src/tests.rs
index 3ad3a7886..ba4d826fd 100644
--- a/crates/metafile/src/tests.rs
+++ b/crates/metafile/src/tests.rs
@@ -2075,6 +2075,17 @@ fn clip_polygon_work_bound() {
assert!(clipped.is_empty(), "excessive polygon clip product rejected");
}
+#[test]
+fn clip_polygon_charges_actual_work_not_projected_growth() {
+ // Two convex 1,000-gons: the product is exactly the budget, and a cut that adds a vertex must
+ // not make a later step look over budget.
+ let gon = |cx: f64| -> Vec<(f64, f64)> {
+ (0..1000).map(|i| (i as f64 / 1000.0 * std::f64::consts::TAU).sin_cos()).map(|(s, c)| (cx + 100.0 * c, 100.0 * s)).collect()
+ };
+ let clipped = crate::cliprect::clip_polygon(&gon(0.0), &gon(30.0));
+ assert!(clipped.len() > 100, "the intersection is kept: {}", clipped.len());
+}
+
#[test]
fn clip_work_is_bounded_across_subpaths() {
use crate::canvas::Raw;