Files
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

83 lines
3.9 KiB
Rust

//! A board-like assembly for issue #883, the worst case for a large STEP import: every
//! component is its own product with a package body and its own pins (nothing shared).
//!
//! ```sh
//! SOLIDCRAFT_STEP_PCB_COMPONENTS=600 SOLIDCRAFT_STEP_PCB_PINS=15 \
//! cargo run --release -p solidcraft-kernel --example step_board_fixture -- board.step
//! ```
//!
//! 600 components of 15 pins give 52 MB. The defaults are 500 and 16; the path defaults to
//! `board.step`.
fn main() {
if let Err(e) = run::main() {
eprintln!("step_board_fixture: {e}");
std::process::exit(1);
}
}
mod run {
use solidcore_record::Vec3;
use solidcraft_kernel::{Body, ExportBody, ExportProduct, StepHeader, box_solid, cylinder, fillet, step_export_products};
/// A count from `SOLIDCRAFT_<name>`, or `default` when it is unset.
fn count(name: &str, default: usize) -> Result<usize, String> {
solidcraft_geom::env::var(name).map_or(Ok(default), |s| s.parse().map_err(|e| format!("SOLIDCRAFT_{name}: {e}")))
}
/// A package: a box with its vertical edges and then its top edges rounded (left sharp where
/// a round does not build).
fn package(w: f64, d: f64, h: f64) -> Result<Body, String> {
let b = box_solid(Vec3::ZERO, Vec3::new(w, d, h)).map_err(|e| e.to_string())?;
let vertical = [Vec3::new(0.0, 0.0, h / 2.0), Vec3::new(w, 0.0, h / 2.0), Vec3::new(w, d, h / 2.0), Vec3::new(0.0, d, h / 2.0)];
let top = [Vec3::new(w / 2.0, 0.0, h), Vec3::new(w, d / 2.0, h), Vec3::new(w / 2.0, d, h), Vec3::new(0.0, d / 2.0, h)];
let b = fillet(&b, &vertical, 0.3).unwrap_or(b);
Ok(fillet(&b, &top, 0.1).unwrap_or(b))
}
pub fn main() -> Result<(), String> {
let components = count("STEP_PCB_COMPONENTS", 500)?;
let pins = count("STEP_PCB_PINS", 16)?;
let path = std::env::args().nth(1).unwrap_or_else(|| "board.step".into());
let mut owned: Vec<Vec<Body>> = Vec::new();
for c in 0..components {
let w = 4.0 + (c % 7) as f64 * 0.37;
let d = 3.0 + (c % 5) as f64 * 0.29;
let mut bodies = vec![package(w, d, 1.2 + (c % 3) as f64 * 0.1)?];
for p in 0..pins {
let side = if p % 2 == 0 { -0.6 } else { d + 0.6 };
let x = 0.3 + (p / 2) as f64 * (w - 0.6) / (pins / 2).max(1) as f64 + c as f64 * 1e-4;
bodies.push(cylinder(Vec3::new(x, side, -1.5), Vec3::Z, 0.25, 2.0 + (p % 3) as f64 * 0.01).map_err(|e| e.to_string())?);
}
owned.push(bodies);
}
let mut products: Vec<ExportProduct> = owned
.iter()
.enumerate()
.map(|(c, bodies)| ExportProduct {
name: format!("U{c}"),
bodies: bodies
.iter()
.enumerate()
.map(|(k, b)| ExportBody { name: format!("U{c}_{k}"), body: b, color: Some([0.2, 0.2, 0.2]) })
.collect(),
children: Vec::new(),
})
.collect();
let side = (components as f64).sqrt().ceil() as usize;
let children = (0..components)
.map(|c| {
let (x, y) = ((c % side) as f64 * 12.0, (c / side) as f64 * 10.0);
(c, [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [x, y, 1.6, 1.0]], format!("U{c}"))
})
.collect();
products.push(ExportProduct { name: "Board".into(), bodies: Vec::new(), children });
let root = products.len() - 1;
let header = StepHeader { file_name: "board.step".into(), ..Default::default() };
let text = step_export_products(&products, root, &header).map_err(|e| e.to_string())?;
std::fs::write(&path, &text).map_err(|e| format!("{path}: {e}"))?;
println!("wrote {path}: {} bytes, {components} components", text.len());
Ok(())
}
}