Curvature Comb Analysis (FusionCurvatureCombAnalysisCommand): combs on sketch curves (exact, from their Bézier/conic pieces) and model edges, with minimum radius; interactive tool draws teeth and envelope; ignored large-sketch timing test (sketch_perf)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 04:40:33 +00:00
1 parent 7382ebd6e8
commit 4c64ceed5c
5 files changed
+289 -1

No files matched your search

@@ -0,0 +1,85 @@
//! Timings for large sketches (run with `cargo test -p solvecraft-engine --release sketch_perf -- --ignored --nocapture`;
//! numbers are recorded in docs/perf.md).
use std::time::Instant;
use serde_json::json;
use crate::Session;
fn ms(t: Instant) -> f64 {
t.elapsed().as_secs_f64() * 1000.0
}
/// A sketch of `n` constrained rectangles (4n lines, each dimensioned) plus `n` circles inside
/// them, built straight into the document.
fn big(n: usize) -> Session {
let mut s = Session::default();
s.execute("SketchCreate", &json!({"plane": "XY"})).unwrap();
let id = s.active_sketch.unwrap();
let mut doc = (*s.doc).clone();
let sk = doc.sketch_mut(id).unwrap();
use solvecraft_geom::Vec2;
use solvecraft_sketch::ConstraintKind::*;
let side = (n as f64).sqrt().ceil() as usize;
for k in 0..n {
let (x, y) = ((k % side) as f64 * 30.0, (k / side) as f64 * 30.0);
let p: Vec<usize> = [(0.0, 0.0), (20.0, 0.3), (20.2, 15.0), (-0.1, 15.1)]
.iter()
.map(|(dx, dy)| sk.add_point(Vec2::new(x + dx, y + dy), None).unwrap())
.collect();
let l: Vec<usize> = (0..4).map(|i| sk.add_line_pts(p[i], p[(i + 1) % 4], None).unwrap()).collect();
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: 20.0 }, None).unwrap();
sk.add_constraint(Length { l: l[1], value: 15.0 }, None).unwrap();
sk.add_circle(Vec2::new(x + 10.0, y + 7.5), 3.0, None, None).unwrap();
}
*s.doc_mut() = doc;
s.refresh();
s
}
#[test]
#[ignore]
fn sketch_perf() {
for n in [250usize, 500, 1000] {
let t = Instant::now();
let mut s = big(n);
let build = ms(t);
let id = s.active_sketch.unwrap();
let sk = s.doc.sketch(id).unwrap().clone();
let entities = sk.curves.len() + sk.points.len();
let t = Instant::now();
let mut k = sk.clone();
let rep = solvecraft_sketch::solve(&mut k);
let solve = ms(t);
let t = Instant::now();
let prof = solvecraft_sketch::find_profiles(&k);
let profiles = ms(t);
let ss = s.model.state().sketch(id).unwrap().clone();
let t = Instant::now();
let lines = crate::view::sketch_lines(&ss.sketch, &ss.plane, true, &ss.report.curve_determined);
let draw = ms(t);
let t = Instant::now();
s.execute("sketch.snap", &json!({"at": [10.0, 0.2], "from": [0, 0], "radius": 1})).unwrap();
let snap = ms(t);
let t = Instant::now();
s.execute("DrawPolyline", &json!({"points": [[-50, -50], [-40, -45]]})).unwrap();
let add = ms(t);
let t = Instant::now();
s.execute("sketch.move_point", &json!({"point": "p1", "to": [1, 1]})).unwrap();
let drag = ms(t);
eprintln!(
"PERF n={n} curves={} points={} entities={entities} constraints={} dof={} profiles={}: build {build:.0} ms, solve {solve:.0} ms, profiles {profiles:.0} ms, draw lists {draw:.1} ms ({} polylines), snap {snap:.1} ms, add a line {add:.0} ms, drag a point {drag:.0} ms",
sk.curves.len(),
sk.points.len(),
sk.constraints.len(),
rep.dof,
prof.len(),
lines.len()
);
}
}