Files
solvecraft/crates/engine/examples/sketch_target_bench.rs
jyviko dc38349cbf Layer split stage 1: the B-rep record, native body file and Vec/Plane come from solidcore-record (#11496)
* Kernel: the B-rep record and the native body file come from solidcore-record

SolidCraft's portable B-rep record (`solidcraft_geom::brep`: brep.rs, topology, history,
envelope, part, curve2) and the native body file (`solidcraft_kernel::native`: the tagged binary
codec and the container) move to solidcore's `solidcore-record` crate: geometry as data, with
structural validation and no maths (solidcore's docs/layers.md, stage 1). Every caller uses
`solidcore_record::brep` and `solidcore_record::native`; the old copies are deleted.

What computes from coordinates stays with the kernel, in geom's `brep`:
- `brep::check`: the record's geometric checks (orthonormal frames, non-degenerate planes,
  unit revolution axes, non-singular placements). `check::validate` and
  `check::validate_built` are the record's validation and these together, and every caller of
  `Brep::validate` uses them, so what is accepted is unchanged;
- `brep::label`: a surface's natural ranges (a cone's apex), `curve_interval`,
  `edge_curve_interval` and `form_turns`;
- `brep::pcurve::eval`: a pcurve's point;
- `geometry::check`, `placement::moved` and `split_edges` become functions on the record.

The kernel's native module keeps the conversion between a body and its saved record
(metadata.rs) and checks the decoded record's geometry. `native/analytic.rs`, which no module
declared, is deleted.

solidcore-record is pinned to its rev on solidcore's `layer-record` branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT

* Geometry: Vec2, Vec3, Plane and the profile types come from solidcore-record

The plain data of vectors and points, sketch planes and planar profiles (`Seg2`, `Loop2`,
`Region2`) moves to `solidcore-record`, with its constants and component-wise operators only
(owner decision, solidcore layer split). Their geometry stays in geom as traits:
`Vec2Geom`, `Vec3Geom`, `PlaneGeom`, `Seg2Geom`, `Loop2Geom`, `Region2Geom`, `Seg2ArcFit`,
`Loop2ArcFit` and `Region2ArcFit`, in scope with `use solidcraft_geom::prelude::*`. Every caller
names the types from `solidcore_record`; geom no longer defines or re-exports them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT

* Kernel: pin solidcore-record to solidcore main f5e8e9a29

solidcore-record is merged on solidcore main (tangency-labs/solidcore#2519); its crate there is
identical to the rev pinned before but for its clippy configuration. The lock is regenerated
against the merged rev.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 09:04:26 -05:00

54 lines
2.6 KiB
Rust

//! Matched command timings and analytic cursor-distance error for the reported four-bar.
#[path = "../tests/support/four_bar.rs"]
mod four_bar;
use four_bar::Result;
use serde_json::json;
use solidcore_record::Vec2;
use solidcraft_sketch::profiling::capture;
use std::{io::Write, time::Instant};
fn main() -> Result<()> {
let mut args = std::env::args().skip(1);
let path = args.next().ok_or("usage: sketch_target_bench OUTPUT.json [REPEATS]")?;
let repeats: usize = args.next().map(|v| v.parse()).transpose()?.unwrap_or(10);
if !(1..=100).contains(&repeats) {
return Err("repeats must be in 1..100".into());
}
let mut cases = Vec::new();
for target in [Vec2::new(-10.0, 8.0), Vec2::new(-10.0, -8.0), Vec2::new(20.0, 20.0), Vec2::new(20.0, 0.0), Vec2::new(-8.0, 6.0)] {
for events in [1, 16] {
let mut samples = Vec::new();
for i in 0..repeats + 2 {
let mut s = four_bar::session()?;
let from = four_bar::point(&s, "crank.end")?;
let side = four_bar::branch(&s)?;
let start = Instant::now();
let (result, profile) = capture(|| -> Result<()> {
for k in 1..=events {
let q = from + (target - from) * (k as f64 / events as f64);
let at = four_bar::point(&s, "crank.end")?;
s.execute("sketch.drag", &json!({"entity":"crank.end","from":[at.x,at.y],"to":[q.x,q.y],"live":true}))?;
}
Ok(())
});
let elapsed_ns = start.elapsed().as_nanos().min(u64::MAX as u128) as u64;
result?;
let actual = four_bar::point(&s, "crank.end")?;
let error = four_bar::length_error(&s)?;
if error > 1e-6 || four_bar::branch(&s)? != side {
return Err("length or branch violation".into());
}
if i >= 2 {
samples.push(json!({"elapsed_ns":elapsed_ns,"actual":[actual.x,actual.y],"length_error":error,"cursor_distance":actual.dist(target),"nearest_error":actual.dist(target*(10.0/target.len())),"profile":profile}));
}
}
cases.push(json!({"target":[target.x,target.y],"events":events,"samples":samples}));
}
}
let mut out = std::io::BufWriter::new(std::fs::File::create(path)?);
serde_json::to_writer_pretty(&mut out, &json!({"schema":"solidcraft/sketch-target-bench/1","repeats":repeats,"warmup":2,"cases":cases}))?;
writeln!(out)?;
out.flush()?;
Ok(())
}