SMLib holds both sides of a sheet's faces and gives each back on its surface's
natural side, so a sheet whose file uses faces the other way came back
renumbered. The reader no longer turns them (that lost the file's side);
instead SMLib is given the sheet with those faces turned over exactly on the
record, and they are turned back on SMLib's body, each face mapped through.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A solid the reader or SMLib refuses was shown as a mesh of its faces (#8223):
exactness lost, cracks between faces, and no modelling. It now comes in as an
open surface body of the faces that read, sharing their edges as the file has
them; each face left out is named.
- The reader reads a solid with a set of faces left out (read_solid_without):
a sheet whose faces face their surfaces' natural normals, as SMLib gives a
sheet's faces back, so SMLib's body numbers the faces as the record does.
- Tier 1: the faces that cannot be read alone and the face each refusal names
(named_face) are left out, up to 8 reads. Tier 2, when a refusal names no
face of the solid: the one-face bodies stitched by SMLib. Tier 3, when SMLib
cannot stitch them: the mesh of them, as before.
- Report: "imported as surfaces: <why>" and "left out: face #..."; the
ImportedBody field shown_as_mesh is now refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the reader or SMLib refuses a solid, each of its faces is read on its
own as a one-face surface body (solidcraft_step::topology::read_faces_with)
and meshed by the planner; the triangles together are a mesh body (the
existing kind STL/3MF imports make), marked ImportedBody::shown_as_mesh with
the refusal. The report says 'shown as a mesh: <reason>' and names each face
left out. Modelling operations refuse it as any mesh body.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The check that a loop does not jump across a spline surface's parameters took a
pole only within the face's tolerance. A corner patch written with one side
shrunk to a few µm (Poppy Humanoid's connectors: 5 µm in a 1e-5 mm file) was
refused, though the record check takes the same junction as a pole at the
tolerance of the vertex the uses meet at. The import check now uses that
vertex's tolerance; the loop runs along the collapsed side, its boundary iso
line. A jump that is still refused says whether the surface closes there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An edge raised to cover its pcurves' gap could get past SMLib's limit
(Congé19: 0.98 mm off, tolerance 1.47 mm) and its whole solid then failed
as "SMLib rejected the input". The tolerance an edge or vertex would get,
not only its gap, is now held to that 1 mm: farther off is refused with
how far. The record mapping refuses a tolerance past it with a message
rather than handing SMLib what it rejects.
The margin over a surface gap moves to solidcraft_geom::brep::pcurve
(SURFACE_GAP_MARGIN) for SMLib's pcurves to use too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The nearest point on a spline (5cb015fae measured spline faces up front too) may settle on
another part of the surface, or on its boundary for a point a hair past it, and reported gaps
the edges do not have: 2.9 mm on Poppy Humanoid's leg shells, whose edges lie within 0.003 mm
of their faces, and widened tolerances past what SMLib takes (it then refused
models/vertices-off-curves.step). The up-front measure is again for faces whose nearest point
is in closed form (plane, cylinder, cone, sphere, torus); a spline face's gaps are measured
on the pcurves its kernel makes, as the record check measures them.
The corpus survey is back to 53 of 56 files; Poppy imports 238 bodies (10 solids refused,
most for SMLib's pcurves on spline faces: solidcore#1383).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A corner ball's edge may run from its equator over the sphere's pole to a vertex just past
it (Poppy Humanoid's creative_commons_license: 1.8° past, 0.016 mm off the pole). On the
sphere's parameters the pole is a line, and the edge's pcurve would have to jump half a turn
along it: "pcurve starts 2 from the end of the use before it, on another period". Each
edge that passes through a pole of a face it bounds (a sphere's, a revolved face's on its
axis) inside its interval is now split at the nearest point to the pole (golden-section
search from 16 samples), the new vertex placed on the pole; the loop's half turn is then at
a vertex on the pole, where a loop may run along it. Each solid's notes say how many edges
were split. The geometry check takes a vertex at a pole to within the vertex's tolerance, as
the reader already does (SurfaceEval::pole_within).
Without the split the edge's jump read as a 0.024 mm gap and was covered by a tolerance:
the measured gap now only covers what the file's geometry really has.
Test: a spherical triangle (radius 0.5) with an edge over the pole to 1.8° past it reads
with its spherical-excess area and its edge split.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
When an edge's tolerance is raised to the gap it has, the vertices at its ends now cover it
too, as in any tolerant record (SMLib takes the vertex's zone as at least its edges'). The
edge measures sample the interval's end as t1 itself: t0 + (t1 − t0)·1 may round past it.
(This commit also measured spline faces up front by nearest point; 04de97cd9 takes that out.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A closed-form pcurve (a B-spline edge's control points projected onto its plane) is
accepted within half its edge's tolerance, and the edge carried the file's: Poppy
Humanoid has planes whose B-spline edges stand 9 µm off them in a file that states
10 µm. The closed form was refused, and a plane has no parameter box for SMLib to make
pcurves in: "a face on an unbounded surface without closed-form pcurves" on 11 of its
solids. On a plane, cylinder, cone, sphere or torus (nearest points in closed form)
each edge's distance from the surface is now measured along it first, and its
tolerance covers three times that; a spline face's gaps stay measured on its kernel's
pcurves, with the 1.5 × margin of a vertex's. The pcurve code reads each use's own
tolerance.
Poppy: 194 → 195 bodies, the unbounded-surface refusals 11 → 3 (the solids they let
through now stop at later steps); the corpus survey and the kernel tests are as before.
Test: a plane face whose B-spline edges stand 0.8e-6 mm above it in a file stating
1e-6 mm imports with its closed forms; without the measure it fails as Poppy's did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
A TOROIDAL_SURFACE whose tube reaches its axis (major radius at most the minor, or
written with a negative major radius) and a DEGENERATE_TOROIDAL_SURFACE were refused.
They are read as what they are: the tube's meridian circle (centre C + R·X, its angle
from X towards Z) revolved about the axis, the same points and normal as ISO 10303-42's
formula, a negative R putting the tube's centre across the axis.
A revolved circle or ellipse that meets its axis is now its whole arc on the face's
side, pole to pole, in closed form (where s0 + M cos(t - phi) = 0), and its NURBS form
is cut there exactly (Boehm knot insertion, bspline::segment): the parameter stays the
quarter grid's, so the edges' forms and straight pcurves are unchanged, and the surface
ends at the poles instead of running on to whole quarters past the axis (which folded
the surface over itself, and left a face with one bound around the axis without its
pole). The face's boundary picks the apple or the lemon.
On Poppy Humanoid 22 solids were refused for these tori; with the SMLib fixes before
this none of their torus faces fails: Poppy 42 -> 27 solids not imported, 194 -> 215
bodies.
Tests (hand-written files, analytic answers): a horn torus round of radius 1 on a pin
(V = 8 pi + pi(5/3 + pi/2)); a spindle torus pin end (major 1, minor 2, its apex in a
dimple at z = sqrt 3, V = 3 sqrt3 pi + 8 pi^2/3 on the cylinder) as TOROIDAL and
DEGENERATE_TOROIDAL, with a seam to the pole and with one bound; a nearly round end
(0.005 / 0.345) in two halves with SolidWorks's apex vertex; the lemon of a spindle torus
as one seam-bounded face with major radius -1 and as a degenerate torus with its lemon
selected (V = pi(6 sqrt3 - 8 pi/3)). Volumes and areas within 1e-3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
The pcurves close a face's loop through a pole to within the face's tolerance (a
point where crossing half a period moves less than that): there the periodic parameter
is free and the loop jumps across. The reader's check that no loop crosses a seam
without a seam edge took a pole only exactly (1e-9 relative), so it refused the loops
the pcurves had closed near one. Poppy Humanoid's nearly round pin ends (a spindle
torus of major radius 0.005, minor 0.345) have their apex vertex at the tube's top,
3.6e-5 mm above the true pole: their meridians end 1.7e-6 rad short of it, and 13
faces were refused. Both now use one test, SurfaceEval::pole_within.
Test: a point 2e-7 mm from a sphere's axis is a pole within 1e-6 mm, not within 1e-7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
An edge's curve as a file writes it may lie off a face's surface by more than the
file's stated uncertainty: Poppy Humanoid's intersection curves lie up to 9 µm off
surfaces in a file that states 0.01 µm, and 24 of its solids were refused by the
record check ("pcurve … lifts off its edge's curve"). Each use's gap, between its
pcurve on the surface and its edge's curve, is now measured along the edge at 17
points as the record check measures it, and the edge's tolerance covers it (1.5 ×
the gap, as a vertex's covers its gap; more than 1 mm is refused). The pcurves are as
made, and each solid's notes say how many edges were raised and how far.
The pcurve fit's tenfold retries went in 6cc0401fa; this measures instead of
retrying. On the corpus (the repository's STEP/IGES files, the models repository,
Poppy): 50 → 53 of 56 files import; Poppy 132 → 194 bodies.
Test: a hand-written domed patch whose edges are written 0.2 µm above it (SMLib makes
its pcurves) imports with them covered; 1.5 mm off is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
- Session::execute is a command span; Model::evaluate and each feature computed
(by its type) are spans; io's import, export and design read/write too; the
STEP reader's topology, pcurves and solid record.
- diagnostics.profile (contract + output schema): report, start/stop, reset,
Chrome trace file.
- A STEP/IGES import's reply carries its own profile while recording.
- Every app compiles the profiler in by default (feature `profile`);
SOLIDCRAFT_PROFILE=1 starts it.
- solidcraft-cli run/eval/bench-open --profile print the table on stderr,
--profile-out writes the trace; run --profile works on designs and STEP files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cone face bounded by its base circle and its seam out to the apex and back
turns once round the axis and closes at the apex. add_seams counted the turn
as a loop without a seam and added another, out and back from the same
vertex: the drill point of a turned pin read back from STEP had a sliver
whose boundary crossed itself, and the body would not tessellate (#5193). A
winding loop with a vertex on a pole is left as it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113gDVmStT5tCGKW9Fxgc5K
A use with no closed-form pcurve (an approximated intersection curve on a cone,
a spline edge on a spline surface) no longer refuses its solid. Its face goes to
SMLib alone, an open sheet with the trims it has and without the others, and
SMLib makes those as it makes every trim it needs (GetOrCreateUVTrimCurve on
export); they come back on the record's parameters and the face is closed and
checked as before. The STEP reader takes the trim maker from the kernel
(read_solids_with, read_solid_with); read_solids stays exact-only.
Kernel lib tests: 490 pass, 239 fail (were 468 / 261; pushed head 475 / 254),
none newly failing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnQawySUFx57iaJZq7z1Fm
sew and the STEP reader build a revolved face's NURBS form over `form::extent` (its boundary on
its label) instead of a pass of pcurves on the label, and compute its pcurves within the form's
box. The STEP reader gives revolutions their form too, and a trimmed swept profile its file's
trim range exactly (points on the axis fit either side of it). The STEP seam tests read edge
angles through `Edge::curve_interval`, intervals and domains being in the forms' parameters.
step 37/37.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnQawySUFx57iaJZq7z1Fm
`On::fit` and everything it used (samples, least squares, breaks, filled poles) are deleted, and
the STEP reader no longer retries a face at ten times its tolerance. A pcurve is exact (closed
form, an affine image, or a straight line along a form's parameters) or the face is refused:
"not supported yet: no exact pcurve of <curve> on <surface>".
The rig and ladder are as with the fit (333/372, 101/116; none lost; 3806, already failing, now
refused on import). 16 unit tests that build sphere meridians, torus seams, closed spline
bands, intersection curves and surfaces of revolution on their labels now refuse: those paths
need meridians labelled on their face's meridian plane and revolutions on the quarter grid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnQawySUFx57iaJZq7z1Fm
A surface of linear extrusion or of revolution over a circle or ellipse built its profile with the
reader's own conic spline, on a parameter of its own; the profile is now `form::curve`, the form
its edges carry, so the surface's parameter along the profile is theirs and their pcurves are
straight. The reader's conic spline is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnQawySUFx57iaJZq7z1Fm
`form::edge` is the one rule sew and the STEP reader share: a circle about a revolved face's
axis is labelled on that face's frame, and a circle or ellipse edge gets its form. The STEP
reader now gives every imported edge its label and form before any pcurve, and every
cylinder, cone, sphere and torus face its form over the extent its label's pcurves give; the
pcurves, the outer-loop choice and the turn-over test then run on the forms, and the record
carries them.
With the pcurve fit turned off, the STEP and IGES circle cases pass (8 of the 9 import cases
that needed it; 3208's ellipse on an extruded spline surface remains).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnQawySUFx57iaJZq7z1Fm
The edge mirror of a face's NURBS form: Edge::nurbs keeps the NURBS SMLib holds a conic as, and
the edge's interval and its pcurves' parameter are its. Edge::parameters is the curve every
evaluation at a parameter uses; curve says what the edge is. Nothing writes the form yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every SMLib analytic surface is a B-spline surface, and its trims are in that NURBS's
parameters. Face::nurbs keeps that NURBS: the face's domain and pcurves are its (u, v), and
`surface` says what the face is. Face::parameters is the surface every (u, v) evaluation uses.
The SMLib conversion reads and writes it (fork FACE_NURBS_FORM, SMLIB_REV c4483f6); the STEP
writer writes the analytic surface and the face's edges without pcurves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
Exporters write a plane as a CONICAL_SURFACE whose semi-angle falls a whisker
short of 90 degrees (#4006: pi/2 - 7e-12): its height over the face spans
4e-11, a parameterisation ill-conditioned for everything downstream. The
reader now measures the cone's deviation from its limit plane (the plane
through the placement across the axis) over the face's boundary points and,
should the face hold the axis, the apex; within the file's distance
uncertainty the face's surface is that plane, oriented as STEP's normal.
One criterion, the deviation over the extent against the tolerance; no
semi-angle threshold.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
write::topology::layout says which shells make each solid (a material region's outer shell
and its voids) and which are sheets, and outer_loop which loop of a face is its outer one;
the STEP writer uses both, and the IGES writer can too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
A placed revolution is written exactly whenever its placement maps revolutions about the
axis onto revolutions (one scale across the axis, another along it, then a rotation or
reflection), its profile placed with it; a conic profile stays a conic under a similarity.
A void's CLOSED_SHELL keeps the record's sides (normals out of the material, into the void)
through an ORIENTED_CLOSED_SHELL .T., as SolidCraft has written voids. Reader::frame reads an
assembly placement (AXIS2_PLACEMENT_3D, or _2D in the XY plane).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
write::topology writes each material region as a MANIFOLD_SOLID_BREP (BREP_WITH_VOIDS
with its voids as ORIENTED_CLOSED_SHELLs), open shells as a SHELL_BASED_SURFACE_MODEL,
faces as ADVANCED_FACEs on their exact surface with FACE_OUTER_BOUND/FACE_BOUND of
EDGE_LOOPs and VERTEX_LOOPs, edges as EDGE_CURVEs on their exact curve over the record's
interval, and the record's exact pcurves as PCURVEs of SURFACE_CURVE/SEAM_CURVE. A record
surface's (u, v) map linearly onto STEP's (planes on their orthonormal frame, narrowing
cones turned over, revolutions angle first), and pcurves go through the same map.
Writer::add_distinct writes topology entities unshared. topology::Solid lists the faces
the reader turned over (their loops disagree with their same_sense).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
On a B-spline face the export now takes each edgeuse's UV trim exactly as
SMLib holds it (EDGEUSE_NURBS_*): the face's parameters are SMLib's, so the
trim is the pcurve, reversed exactly (knots t -> a + b - t) where the
edgeuse runs against its edge, its knots mapped onto the edge record's
parameter where that is an affine image of SMLib's (identity for spline
edges, as_line's map for NURBS lines, the C side's map for lines), and its
end knots set to the edge interval where they differ by rounding (within
the schema's 1e-9 slack, else an error). Changed: export.rs `curves`
(per-edge TrimMap), new `trims`, `mapped`, `Face::given`,
`Edge::trim_map`, the face loop in `native_ordered`, `pcurves`,
`outer_area`.
Analytic faces and circle/ellipse edges keep brep::pcurve: SMLib keeps an
analytic face's trims on its internal rational B-spline (SmCylinder.h:
"the map from one to the other is not linear") and a circle's on its NURBS
parameter, neither an exact B-spline in the record's angle parameters.
brep::pcurve::EdgeUse gains `given`, a producer's exact pcurve taken as it
is (structure and interval checked, moved by whole periods with its loop,
never refitted). Existing construction sites (STEP reader, tests, truck
adapter) pass `given: None`; the existing tests change only for the new
field.
Built, clippy-clean and tested on 4b2c0eaf before the rebase onto
kernel-smlib/no-truck (not built: base in progress).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
A face on a B-spline surface whose first and last control columns are the
same (closed around u), bounded by loops that go once around without a seam
edge, was refused ("a loop across a closed spline surface's seam"). It now
gets a seam as cylinders and cones do, along the surface's own boundary
column (u at the start of its knots, an exact iso-curve of the net), each
loop split where it crosses that column when it has no vertex on it; the
period is the knots' width. At a pole a loop may jump across u, where the
seam's two uses meet a period apart.
The step-conformance NURBS cylinder, dome and torus ring now read. With
SOLIDCRAFT_KERNEL=smlib all 17 parts import into SMLib with their exact
volumes and face counts. A spline closed in v only, or both ways, is still
refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Solid::face_origin lists the ADVANCED_FACE or FACE_SURFACE entity of each
of the record's faces, in the record's face order, so a body made from the
record (in another kernel) can trace its faces to the file, as the truck
import's face_origin does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SolidCraft's own record of a body SMLib made (the rig's record check, the saved body, the next
SMLib import) re-fitted every pcurve at the kernel's working tolerance, 1e-5 mm. SMLib's fillet
and chamfer surfaces lie some 2e-5 to 2e-4 mm off their faces, so 15 fillet and chamfer cases
failed that check although SMLib's own passed. And SMLib reported one tolerance for a whole body,
the largest any part of it needed, so one loose edge (a pocket cut left one at 0.23 mm) made the
whole body loose on its way back into SMLib.
Now the tolerances go per edge and per vertex, both ways:
- solidcraft-smlib reads and writes the fork's new SMLIB_FLAT_EDGE_TOLERANCE and
VERTEX_TOLERANCE arrays. Export gives each edge and vertex record its own (never below the
floor, which becomes the body's). Where those understate what an approximated edge needs on
its face (SMLib holds a blend's end curve 2.3e-5 mm off its plane at 2e-5), the export falls
back to SMLib's largest everywhere, as before. Import writes each record edge's and vertex's
tolerance.
- `geom::brep::pcurve::EdgeUse` carries its edge record's tolerance, and a pcurve is fitted to
it where it is larger than the face's.
- The kernel's decoded body keeps its record's edge and vertex tolerances
(`Body::tolerances`, in its edge and vertex order) after an SMLib operation and a saved body's
decode, and keeps them through a rigid placement. Its record writes them again; anything that
builds a new solid drops them.
SMLIB_REV moves to the fork commit that adds the arrays (smlib-blends-abi2 4a67471, ABI 2).
Rig and ladder fillet and chamfer cases on SMLib: 43 pass before, 60 now, none lost. Tests: a
pcurve fitted to its use's own tolerance, and the ladder's 08/006 cylinder rim chamfer whose
record, saved body and placed copy all stay valid and SMLib's check takes back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
SURFACE_OF_REVOLUTION reads as a placed revolution (u the angle, v the profile's parameter,
STEP's normal); SURFACE_OF_LINEAR_EXTRUSION as an exact spline surface, the profile as a
(rational) B-spline times degree 1 along the vector; a circle extruded along its axis or
revolved in a plane through the axis as the cylinder or ring torus it is. Line, conic and
open-conic profiles are bounded by the face (a closed conic's seam outside it, a revolved
profile stopped at the axis). OFFSET_SURFACE of the analytic kinds reads as the moved
surface, checked at each level. Hyperbola and parabola edges read as exact rational
quadratic pieces and a Bezier on STEP's parameter. Faces winding around a revolution get
seams along its profile. Every mapping is checked point by point and normal by normal
against STEP's own surface.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A face winding around a closed (non-periodic) spline surface without a seam edge has loops
that jump across the surface's domain; it is refused for now instead of making a record whose
loops do not close. The step-conformance parts are read in a test, and the kernel's ignored
corpus comparison builds each record back into a truck solid to compare volumes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A face on a cylinder, cone or sphere bounded by loops that go round the axis (a band
between two circles, a dome on its equator, a cone on its base and apex) gets a seam:
a ruling or meridian from one loop to the other (split where it has no vertex at that
angle) or to the pole it covers, so every loop closes in (u, v) as schema 4 requires.
Pcurves may be fitted up to 1 mm off for edges that lie off their surfaces. The kernel
gets an ignored corpus test comparing truck's face and edge counts with schema 4's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
read_solids walks MANIFOLD_SOLID_BREP, BREP_WITH_VOIDS and SHELL_BASED_SURFACE_MODEL
into solidcraft_geom::brep records: lines, conics, B-splines and polylines; planes, the
analytic surfaces and B-spline surfaces; edge intervals from the vertices' parameters;
loops relative to the surface's natural normal; outer loops, regions, voids and sheets;
pcurves from the 3D edges; the canonical order. Unsupported geometry refuses its solid
with a reason and the rest of the file is still read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new crate, solidcraft-step (L1, no modelling backend), holds the Part 21
parser (moved from the kernel, which now reads through it) and STEP geometry
as plain data: lines, conics, B-splines (rational and implied knots),
polylines, trimmed curves, surface curves with their pcurves, 3D offset
curves; planes, cylinders, cones, spheres, tori (degenerate too), B-spline
surfaces, revolutions, linear extrusions, offset and rectangular trimmed
surfaces. Each keeps STEP's own definition and parameterisation, in mm and
radians, with pcurves scaled by their surface's parameter units. Shared
entities stay shared. A writer emits the same entities back, deduplicated.
Tests per entity check values against analytic points, units, round trips
and hostile input; an ignored test reads and round-trips every edge curve
and face surface of the tangency-labs/models STEP files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>