Sketch: drawing infers tangent (from an arc end) and perpendicular (from a line end) constraints; profile finding prefilters crossings by bounding box (text and free-form sketches 30x faster); large-sketch solver tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
346be40377
commit
3ab3e961de
4 files changed
+118
-6
No files matched your search
@@ -160,9 +160,24 @@ pub fn find_profiles(sk: &Sketch) -> Vec<Profile> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if shapes.len() <= 2000 {
|
||||
// Bounding boxes: only shapes whose boxes overlap can cross.
|
||||
let boxes: Vec<(Vec2, Vec2)> = shapes
|
||||
.iter()
|
||||
.map(|(_, sh)| match *sh {
|
||||
Shape::Line { a, b } => (Vec2::new(a.x.min(b.x) - tol, a.y.min(b.y) - tol), Vec2::new(a.x.max(b.x) + tol, a.y.max(b.y) + tol)),
|
||||
Shape::Round { c, r, .. } => (Vec2::new(c.x - r - tol, c.y - r - tol), Vec2::new(c.x + r + tol, c.y + r + tol)),
|
||||
})
|
||||
.collect();
|
||||
let overlap = |i: usize, j: usize| match (boxes.get(i), boxes.get(j)) {
|
||||
(Some((a0, a1)), Some((b0, b1))) => a0.x <= b1.x && b0.x <= a1.x && a0.y <= b1.y && b0.y <= a1.y,
|
||||
_ => false,
|
||||
};
|
||||
if shapes.len() <= 20_000 {
|
||||
for i in 0..shapes.len() {
|
||||
for j in (i + 1)..shapes.len() {
|
||||
if !overlap(i, j) {
|
||||
continue;
|
||||
}
|
||||
let (Some((_, x)), Some((_, y))) = (shapes.get(i), shapes.get(j)) else { continue };
|
||||
let mut pts = intersections(x, y);
|
||||
// A tangent touch gives two nearly equal roots: one point.
|
||||
@@ -172,12 +187,12 @@ pub fn find_profiles(sk: &Sketch) -> Vec<Profile> {
|
||||
pts = vec![(p + q) * 0.5];
|
||||
}
|
||||
for p in pts {
|
||||
// Crossings at (or next to) curve ends are already cuts.
|
||||
if ends.iter().any(|e| e.dist(p) < tol * 100.0) {
|
||||
continue;
|
||||
}
|
||||
let on_x = x.param_on(p, tol).or_else(|| end_param(x, p, tol));
|
||||
let on_y = y.param_on(p, tol).or_else(|| end_param(y, p, tol));
|
||||
// Crossings at (or next to) curve ends are already cuts.
|
||||
if on_x.is_none() || on_y.is_none() || ends.iter().any(|e| e.dist(p) < tol * 100.0) {
|
||||
continue;
|
||||
}
|
||||
if let (Some(tx), Some(ty)) = (on_x, on_y) {
|
||||
if x.param_on(p, tol).is_some()
|
||||
&& let Some(c) = cuts.get_mut(i)
|
||||
|
||||
@@ -362,3 +362,48 @@ fn links_update_in_place_rebuild_and_break() {
|
||||
assert_eq!(back, sk);
|
||||
assert_eq!(back.link(&j3).unwrap().kind, LinkKind::Intersect);
|
||||
}
|
||||
|
||||
/// Many independent constrained rectangles solve quickly (per-component work).
|
||||
#[test]
|
||||
fn large_sketch_solves_fast() {
|
||||
let mut sk = Sketch::new();
|
||||
for i in 0..150 {
|
||||
let o = v(i as f64 * 50.0, 0.0);
|
||||
let l = rect(&mut sk, [o, o + v(40.0, 0.5), o + v(40.5, 30.0), o + v(-0.3, 30.2)]);
|
||||
use ConstraintKind::*;
|
||||
sk.add_constraint(Horizontal { l: l[0] }, None).unwrap();
|
||||
sk.add_constraint(Horizontal { l: l[2] }, None).unwrap();
|
||||
sk.add_constraint(Vertical { l: l[1] }, None).unwrap();
|
||||
sk.add_constraint(Vertical { l: l[3] }, None).unwrap();
|
||||
sk.add_constraint(Length { l: l[0], value: 40.0 }, None).unwrap();
|
||||
sk.add_constraint(Length { l: l[1], value: 30.0 }, None).unwrap();
|
||||
}
|
||||
let t = std::time::Instant::now();
|
||||
let rep = solve(&mut sk);
|
||||
let ms = t.elapsed().as_millis();
|
||||
assert!(rep.ok());
|
||||
assert_eq!(rep.dof, 300);
|
||||
assert!(ms < 10_000, "{ms} ms");
|
||||
eprintln!("large sketch: {ms} ms");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_connected_chain_solves() {
|
||||
let mut sk = Sketch::new();
|
||||
let n = 300;
|
||||
let mut prev = 0usize;
|
||||
for i in 0..n {
|
||||
let p = sk.add_point(v((i + 1) as f64 * 10.0 + 0.3, if i % 2 == 0 { 0.4 } else { -0.2 }), None).unwrap();
|
||||
let l = sk.add_line_pts(prev, p, None).unwrap();
|
||||
sk.add_constraint(ConstraintKind::Horizontal { l }, None).unwrap();
|
||||
sk.add_constraint(ConstraintKind::Length { l, value: 10.0 }, None).unwrap();
|
||||
prev = p;
|
||||
}
|
||||
let t = std::time::Instant::now();
|
||||
let rep = solve(&mut sk);
|
||||
let ms = t.elapsed().as_millis();
|
||||
eprintln!("chain: {ms} ms, iters {}", rep.iterations);
|
||||
assert!(rep.ok(), "{rep:?}");
|
||||
assert_eq!(rep.dof, 0);
|
||||
assert!(ms < 10_000, "{ms} ms");
|
||||
}
|
||||
Reference in new issue
Block a user