* BOUNDARY: cap point list and aggregate output per call (#723) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * BOUNDARY: a call over the output budget restores the drawing The session does not roll back a command that returns an error, so the polylines created before the cap was hit stayed in the drawing. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
89c834155d
commit
8073453d86
|
|
@ -468,11 +468,27 @@ fn boundary_at(s: &mut Session, q: Vec2) -> Result<Vec<Handle>> {
|
|||
Ok(out)
|
||||
}
|
||||
|
||||
/// Most internal points one BOUNDARY call accepts (checked before the list is copied).
|
||||
const MAX_BOUNDARY_POINTS: usize = 256;
|
||||
/// Most polylines one BOUNDARY call may create. Each pick adds polylines the next pick traces
|
||||
/// against, so this bounds the aggregate tracing work of the batch.
|
||||
const MAX_BOUNDARY_OUTPUT: usize = 1024;
|
||||
|
||||
fn run_boundary(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let raw = p.get("points").and_then(Value::as_array).ok_or_else(|| bad("boundary", "`points` is required"))?;
|
||||
if raw.len() > MAX_BOUNDARY_POINTS {
|
||||
return Err(bad("boundary", format!("at most {MAX_BOUNDARY_POINTS} points per call (got {})", raw.len())));
|
||||
}
|
||||
let pts = points_param(p, "points").ok_or_else(|| bad("boundary", "`points` is required"))?;
|
||||
let before = s.state()?.doc.clone();
|
||||
let mut out = Vec::new();
|
||||
for q in pts {
|
||||
out.extend(boundary_at(s, q)?.into_iter().map(|h| h.hex()));
|
||||
if out.len() > MAX_BOUNDARY_OUTPUT {
|
||||
// The refused call leaves no partial result behind.
|
||||
s.state_mut()?.doc = before;
|
||||
return Err(bad("boundary", format!("more than {MAX_BOUNDARY_OUTPUT} boundary polylines in one call; split the work")));
|
||||
}
|
||||
}
|
||||
Ok(json!({ "handles": out, "message": format!("BOUNDARY created {} polyline(s)", out.len()) }))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -861,6 +861,21 @@ fn gradient_pick_point_keeps_island_of_separate_lines() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boundary_point_batch_is_capped() {
|
||||
let mut s = Session::new();
|
||||
let sq = [[0, 0], [20, 0], [20, 20], [0, 20]];
|
||||
for i in 0..4 {
|
||||
s.execute("line", &json!({"points": [sq[i], sq[(i + 1) % 4]]})).unwrap();
|
||||
}
|
||||
let many: Vec<_> = (0..100_000).map(|_| json!([2, 2])).collect();
|
||||
let t = std::time::Instant::now();
|
||||
assert!(s.execute("boundary", &json!({"points": many})).is_err());
|
||||
assert!(t.elapsed().as_secs() < 5);
|
||||
let ok: Vec<_> = (0..8).map(|_| json!([2, 2])).collect();
|
||||
assert!(s.execute("boundary", &json!({"points": ok})).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boundary_pick_point_keeps_island_of_separate_lines() {
|
||||
for lines in [true, false] {
|
||||
|
|
|
|||
Loading…
Reference in New Issue