mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 20:02:16 +00:00
dc38349cbf
* 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>
55 lines
2.4 KiB
Rust
55 lines
2.4 KiB
Rust
//! Lines thinner than a pixel (the grid's minor lines are 0.7 px) are drawn wherever they fall,
|
||
//! without multisampling (#2823). A quad narrower than a pixel covers no pixel centre when it
|
||
//! falls between two rows, so such lines came and went as the view zoomed. Skipped, with the
|
||
//! reason printed, without a GPU adapter (`SOLIDCRAFT_REQUIRE_GPU=1` makes that a failure).
|
||
|
||
use solidcore_record::Vec3;
|
||
use solidcraft_engine::render::{Camera, Rgb, Scene, SceneLine, StandardView};
|
||
use solidcraft_gpu::GpuError;
|
||
use solidcraft_gpu::capture::Capturer;
|
||
|
||
/// Pictures drawn with one sample per pixel: with 4× multisampling a thin line covers some
|
||
/// samples wherever it falls, which hides the fault.
|
||
fn capturer() -> Option<&'static Capturer> {
|
||
static SINGLE: std::sync::OnceLock<solidcraft_gpu::Result<Capturer>> = std::sync::OnceLock::new();
|
||
match SINGLE.get_or_init(Capturer::headless_single_sample).as_ref() {
|
||
Ok(c) => {
|
||
assert_eq!(c.samples(), 1);
|
||
Some(c)
|
||
}
|
||
Err(e @ GpuError::NoAdapter(_)) if std::env::var_os("SOLIDCRAFT_REQUIRE_GPU").is_none() => {
|
||
eprintln!("skipping: {e}");
|
||
None
|
||
}
|
||
Err(e) => panic!("{e}"),
|
||
}
|
||
}
|
||
|
||
/// Lines `width` px wide across a front view, each drawn alone at its own height so it falls at
|
||
/// its own fraction of a pixel (0.23 mm apart, about 1.15 px at 5 px per mm); returns the ones
|
||
/// that left no pixel.
|
||
fn missing(c: &Capturer, width: f64) -> Vec<usize> {
|
||
const N: usize = 40;
|
||
let mut cam = Camera { distance: 62.5, fov: 0.0, ..Default::default() };
|
||
cam.set_view(StandardView::Front);
|
||
(0..N)
|
||
.filter(|&i| {
|
||
let z = -4.0 + i as f64 * 0.23;
|
||
let line =
|
||
SceneLine { points: vec![Vec3::new(-8.0, 0.0, z), Vec3::new(8.0, 0.0, z)], color: Rgb(255, 0, 0), alpha: 1.0, width, on_top: true };
|
||
let scene = Scene { lines: vec![line], radius: 30.0, ..Default::default() };
|
||
let img = c.rgba(&scene, &cam, 200, 200).unwrap();
|
||
!img.as_chunks::<4>().0.iter().any(|p| p[0] > 60 && p[0] > p[1].saturating_add(40) && p[0] > p[2].saturating_add(40))
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
#[test]
|
||
fn lines_thinner_than_a_pixel_are_drawn_wherever_they_fall() {
|
||
let Some(c) = capturer() else { return };
|
||
for width in [0.4, 0.7, 1.0, 1.5] {
|
||
let gone = missing(c, width);
|
||
assert!(gone.is_empty(), "{width} px lines: {} of 40 left no pixel ({gone:?})", gone.len());
|
||
}
|
||
}
|