Per-crate changelog
Every publishable crate versions and publishes independently (ADR 0067); this page collects each crate's own CHANGELOG.md, newest release first per crate. Workspace-wide narrative — breaking bumps and coordinated releases — stays in the top-level changelog.
axiolid
0.4.0 - 2026-09-28
Changed
- Breaking: requires
axiolid-ray-mesh0.4, soaxiolid::ray_mesh(re-exported under the ray features) carries the newRayMeshError::TriangleIndexOutOfRange, andRayIndexqueries refuse an out-of-range candidate instead of skipping it. An exhaustivematchonRayMeshErrorneeds the new arm. - The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-arrangement
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-backend-cpu
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-backend-gpu
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-brep
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Added
ExactBRepBuilder::append: copy another exact B-rep's vertices, edges, loops, faces and shells, with their curves, surfaces, intervals and names, and return the new shell handles; optionally with every face used reversed, which turns an outer shell into a void (#111).
axiolid-brep-audit
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Changed
- An implicit pcurve (ADR 0077) is parameterised by its cells, not in proportion to its edge. It passes when every lifted sample projects onto the edge within tolerance, inside the edge's span, in the order the use runs, and starting and ending at the use's ends. Every other pcurve is still checked against the edge at proportional parameters.
axiolid-brep-boolean
0.1.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.1.0 - 2026-09-27
Added
Surfaces tangent along a whole curve and crossing there: the line of contact is a section edge (the tangent-contact rule now applies to closed forms only), and pieces leaving a vertex with the same direction and bend are ordered by their chords a small way out.
A traced pair that only touches adds no section, as the closed forms' touching does. A pcurve whose trace on its face cannot be decided falls back to the section's own space curve read on the face, and failed traces are not repeated. Each surface pair's closed form is computed once per boolean.
Two B-spline faces meeting each other: their section is traced in both faces' parameter boxes and carried on both (
Curve3::PairSection), its pcurve on each read from the solve. The last refusal by face type is gone; sections whose branches cross where the surfaces touch stay refused by name.Faces that wind round their surface without a seam edge (a dome bounded by its rim alone, a can by its two rims), as files may deliver them. The boolean first gives each a seam edge along the iso-curve where its loops wrap, joining the lower loop, the seam, the upper loop or a pole, and the seam back into one loop.
Sections through a sphere's pole or a cone's apex are cut there, and the collapsed pole piece is split where they end. Meridians and latitudes, cone rulings and circles, and a torus's tube and ring circles get their straight pcurves, affine in the curve's own parameter.
B-spline faces (#167, ADR 0075 stage 3) against planes, quadrics and tori:
- The section is traced on the spline (ADR 0077).
- On the analytic face its pcurve is the same space curve read in the face's parameters (
Curve2::Lifted), so it shares the edge's parameter. - An edge next to a B-spline face is cut where it meets the section's other surface.
- Classification rays meet B-spline faces through the spline trace.
- Two B-spline faces meeting each other are refused by name (
UnsupportedSection).
Faces on spheres, cones, tori and elliptical cylinders (#167, ADR 0075 stage 2), meeting in any section #119 builds:
- A section with no line or conic is traced inside one face's parameter box (ADR 0077).
- Every section on every analytic face gets an exact implicit pcurve, cut out of the other surface's traced equation between the section's ends.
- A sphere's pole or a cone's apex closes loops as a collapsed piece that is no edge.
- Seam circles are cut by the cone of normals along them.
- Section branches that cross (a Steinmetz pair) are split where they meet.
- Frame components that are only rounding residue are cleared before intersecting.
Operands that touch (#167, ADR 0075 stage 2): faces on one surface share their overlap (each face's edges are imprinted on the other, and a region on the other solid's boundary is kept once by normal agreement); sections along an existing edge split only the other face; tangent contact adds no section; pieces leaving a vertex in one direction are ordered by curvature; solids meeting along an edge are paired radially around it so each stays manifold. Cavities go to the smallest solid around them, in results of several solids too.
section_edges(#167, ADR 0075 stage 1): the exact intersection curves of two exact B-reps' faces, each trimmed to where it lies inside both faces. Crossings with a boundary edge are found against the adjacent face's surface, or across a seam against the plane through the ruling.boolean(a, b, operator, tolerance)(#167, ADR 0075 stage 1): the exact union, intersection and difference of two exact solids whose faces lie on planes and cylinders and meet in lines, circles and ellipses -- not only vertical columns. Regions are classified by exact ray parity with certified face membership and sewn into shells; cavities become voids. Every result audits clean and measures exactly.split_face(#167): a plane or cylinder face cut along its section edges into regions, traced in the face's parameters with exact pcurves (lines, conics, rulings, circles about the axis,Sinusoid2for oblique cuts).
axiolid-capi
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-collide
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-construct
0.3.6 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.5 - 2026-09-28
Fixed
- Structural sections mesh (#193):
profile_ringsflattens everyProfile::Sectionfamily -- I, asymmetric I, L, T, U, C, Z, trapezium -- from the exact contoursection_contourbuilds, fillets and toe radii chorded within the budget, straight edges exact. It refused them withUnsupported { ProfileTriangulation }before. Rectangles with corner radii mesh with them, throughrectangle_contour: the mesh path used to drop the radii and mesh a sharp box. - Circles are chorded from half a step off the axes, not from angle 0 (#194): the same chords, turned, so a chord's middle, inside the circle, sits at every quarter turn. A circular void tangent to a face along an axis direction, as openings are, leaves a sliver of material under it instead of a chord point on the face, which pinched the solid.
0.3.4 - 2026-09-27
Added
bounding::minimum_enclosing_sphere(#118): the least sphere holding a point set, by Welzl's algorithm with exact in/out decisions (diametral, least sphere through three points, circumsphere through four), the centre enclosed from exact dyadic values and the radius rounded up, withSphereEvidence::errorbounding centre offset and radius excess.bounding::oriented_bounding_box(#118): a box holding every point, exactly (|(p - centre) . axes[i]| <= half_extents[i]for the returned doubles), never larger than the axis-aligned box. Tries the axis-aligned box, the principal axes, and each world axis, principal axis and exact hull face normal with the exact minimum-area rectangle across it. It does not claim the global minimum volume.- Depends on
axiolid-exactfor the exact decisions.
0.3.3 - 2026-09-27
Added
Exact full-turn revolution of circles, hollow circles, hollow rectangles and every section with holes (#111). Each hole revolves on its own and joins the solid as a void shell. Verified by Pappus on every case.
Composite profiles are unioned by
profile_lower::composite_regionsover oneArcArrangement(#111): members may carry arcs and their own openings, a member's opening stays open unless another member fills it, and members that do not touch become separate solids of oneExactBRep, for extrusion and revolution alike. Member vertices within the linear tolerance are welded first, so members authored to meet do meet.section_lower::circle_contour: a circle profile (and its bore) as exact quarter-arc contours. A circle moved off the origin by a derived profile now lowers to that contour instead of being refused.clip_arc_prism_exact(#120): an arc prism cut by a half-space whose plane passes between its caps, the "column under a sloped roof" case. Cylindrical walls stayCylinderfaces trimmed by an exactEllipse3edge with aSinusoid2pcurve (ADR 0071); planar walls get sloped edges; the cut cap is unnamed. A plane parallel to the axis is refused by name.boolean_prisms_exact_solidsandboolean_arc_prisms_exact_solids(#120): coaxial booleans whose result falls apart into separate pieces return one solid per piece, ordered by lowest vertex (x, then y), each audited on its own. An empty result is an empty list. The single-solid functions keep refusing a disconnected result, so callers that expect one solid are not silently handed the first piece.
Changed
boolean_arc_prisms_exactruns on the exact arc overlay (ADR 0070) and builds results it used to refuse: a result with interior holes becomes a solid with through-passages (#120), and a result starting abovez = 0is extruded from its own base height. Disconnected results go through the_solidsvariants.- Stepped coaxial booleans are built, not refused (#120, ADR 0072):
boolean_prisms_exact,boolean_arc_prisms_exactand their_solidsvariants return a union of prisms with different spans, a difference whose tool stops inside the subject (notch, counterbore, blind pocket, slot through the middle heights) as exact solids with their ledge faces. Walls are named after the operand edge they lie on, caps and ledges after the operand cap that made them. A result enclosing a cavity, and pieces touching only along an edge, are refused by name. clip_arc_prism_exactbuilds a plane that crosses a cap inside the section: the part of the old cap that survives keeps its name, next to the unnamed cut.boolean_steppeddocs: the bands are the lighter alternative to the stepped solid; their volumes are checked against it.
Changed
- Coaxial booleans whose result encloses a cavity return one solid with a void shell (#120) instead of refusing:
boolean_prisms_exact,boolean_arc_prisms_exactand their_solidsvariants. A cavity in a result of several pieces is still refused by name.
Fixed
A sharp rectangle revolved through
revolve_rectanglewrote its cap holes' pcurve intervals forwards although their uses run the edge backwards (ADR 0024), so each hole's pcurve ran against its edge: the geometric audit put it 8 off the edge and the solid measured 289 instead of 188.5. Found by the general boolean (#167); present before #125.Exact revolutions are no longer built inside out (#125).
revolve_profile_exactand the contour revolution put their surface frames at(x, y, z) = (X, Z, Y), which is left-handed; every loop is built anticlockwise in its parameters, so everyForwardface pointed into the solid. The topological and geometric audits compare faces with each other and passed it;exact_propertiesmeasured-2 pi R Afor every Pappus fixture. The frames are now(X, -Z, Y).
0.3.2 - 2026-09-25
Fixed
sweep::swept_diskcarries its section frame along the path by rotation-minimising frames (double reflection) instead of one fixed axis seeded from the first segment (#169). A pipe whose later leg ran along that axis was refused ("sweep reference direction must not be parallel to the directrix"), and a leg NEARLY along it projected the fixed axis to a residue of arbitrary direction, so the ring rotated between stations and the tube's volume collapsed with no error: in a real Revit rebar model 878 bent bars were refused and 6,644 compiled more than 0.5 % short, up to 64 %. The section is a circle, so the choice of perpendicular only rotates it about its own axis.fixed_reference_sweepis unchanged: there the reference is the author's.
0.3.1 - 2026-09-24
Fixed
extrude(and soextrude_profileand the reference mesh compiler) wound a solid inside-out when the extrusion direction pointed below the profile plane (direction.z < 0), e.g. an opening body extruded downward from its lintel (#166). The signed volume was-area * depth, so any boolean using the solid refused it as inside-out. Such a solid is now outward-oriented with the same magnitude, for outer and hole loops alike.half_space::bounded_half_space_in_framenow places the boundary at the authored frame's origin, projected onto the clip plane (#164). It used to take only the frame's axes and anchor the boundary at the clip plane's origin, so a boundary frame offset within the plane cut the wrong region with no error: the mesh stayed closed and correctly wound. The offset along the normal is still dropped, so the sweep starts on the clip plane and polarity and depth are unchanged.ReferenceMeshCompilerpassesBoundedHalfSpace.placement.translationas that origin, so compiled bounded half-spaces now honour the placement's translation as well as its rotation.
0.3.0 - 2026-09-23
Added
- Exact extrusion of rounded rectangles (
IfcRoundedRectangleProfileDef) and of hollow rectangles with outer and inner corner radii (IfcRectangleHollowProfileDef). Each corner is an exact quarter arc that extrudes to a cylinder wall.section_lower::rectangle_contourbuilds the contour through the same router the structural sections use (#111). - Exact full-turn revolution of a filled rounded rectangle; each corner sweeps a torus.
- Rounded and hollow rectangles are accepted as the basis of a derived profile, with the same similarity check every other contour goes through.
Changed
- Invalid rectangle radii are refused instead of clamped: negative, non-finite, wider than the half-extent, an inner radius on a filled rectangle, and a hollow section whose corners leave no wall.
axiolid-contracts
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-25
Added
ExecutionOptions::with_chord_errorandExecutionOptions::chord_error(#165): an explicit bound on how far a provider's straight chords may sit from the curve they replace, separate from the linear tolerance. The tolerance is a coincidence test; used as a chord budget it leaves a 5 mm arc a few chords atTolerance::MILLIMETRE, and small profiles mesh percent-level off.None(the default) keeps each provider's previous behaviour. A non-finite or non-positive budget is refused (None).
axiolid-core
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-curve
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Added
Field2::valueandSeriesField2::valueevaluate the value alone, without the jet, and agree withjetto the last bit.Bridge cells in
ImplicitCurve2(ImplicitCell::bridge, a cubic into a point where two branches cross, bounded by its Bezier control values),ImplicitCell::part,reversedandsolved_range,ImplicitCurve2::solve_cell, andField2::scale_at(the size of the terms that make up the value at a point, which its rounding scales with).Curve3::PairSection(PairSection3,PairNode): the section of two B-spline surfaces, carried by nodes on both and defined between them by the surfaces themselves (where both meet on the plane across the chord). It hassolve(the parameters on both surfaces and the point),rates,second_rates,sub,reversed,parameter_ofandside, andpair_section::solve4for the 4x4 systems behind it (ADR 0077).BSplineSurfaceis defined here, andaxiolid_surfacere-exports it unchanged, so a traced curve can carry a B-spline carrier (Carrier::Spline). It hasBSplineSurface::jet(point and first and second partials, rational) anddomain.Field2is now an enum ofSeriesField2(the former struct: powers and harmonics) andPatchField2(piecewise Bernstein polynomials on a grid, bounded by their coefficients over any box and continued past the grid by their edge polynomials).ImplicitCurve2::clippedcuts a curve to a box.Curve2::Lifted(LiftedCurve2): a space curve read in an analytic surface's parameters, sharing the curve's parameter. It is the pcurve, on the analytic face, of a section only a B-spline can carry.ImplicitCurve2::sub,rotated,reversed,shifted,closureandturning_points.implicit::{bound, bound_simple, partial}give interval bounds and partial derivatives of aField2over parameter boxes.Curve2::Implicit(ImplicitCurve2)andCurve3::ImplicitSection(ImplicitSection3)(#119, ADR 0077): a stretch of aField2's zero set in monotone cells, where each point is the field's unique root in its cell's bracket, and the same curve on its analyticCarrier(plane, ruled surface, sphere or torus).Curve2::QuadraticGraph(QuadraticGraph2)andCurve3::RuledSection(RuledSection3)(#119, ADR 0076): one root branch ofa(t) v^2 + b(t) v + c(t) = 0with degree-2 trigonometric coefficients (Trig2,Branch), and the same curve lifted onto a cylinder, elliptical cylinder or cone (RuledCarrier). The exact pcurve and edge of a quadric's cut across a ruled surface.Curve2::AngleGraph(AngleGraph2)andCurve3::TorusSection(TorusSection3)(#119, ADR 0076): the solutionu(t)ofa(t) cos u + b(t) sin u = c(t), and the same curve on a torus (TorusCarrier). The exact pcurve and edge of a plane's or sphere's cut across a torus.Curve2::Sinusoid(Sinusoid2): the graphv = mean + a cos(t) + b sin(t), the exact pcurve of a plane's cut across a cylinder in its (angle, height) parameters (ADR 0071). The parameter is the first coordinate. Additive:Curve2is#[non_exhaustive].
axiolid-curve-evaluate-contract
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-decimate
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-decompose
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-dispatch
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-evaluate
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Added
Evaluation, derivatives and inversion of
Curve3::PairSection. ACurve2::Liftedreading a pair section on one of its own B-spline surfaces takes that surface's parameters straight from the solve.surface::locate,curve::locate2andcurve::locate3: parameters of a point, iterating where no closed form exists (B-spline surfaces and curves: seeded Newton, verified by the round trip).invert,invert2andinvert3keep their closed-form-only contract.Evaluation, derivatives and inversion of
Curve2::Lifted. AnImplicitSectionon a B-spline carrier is inverted through the surface'slocate.Evaluation, derivatives and inversion of
Curve2::ImplicitandCurve3::ImplicitSection(ADR 0077).invert2andinvert3now also coverQuadraticGraph,AngleGraph,RuledSectionandTorusSection, reading the angle off the point and trying whole turns.Evaluation, first and second derivatives of
Curve2::QuadraticGraph,Curve3::RuledSection,Curve2::AngleGraphandCurve3::TorusSection(#119, ADR 0076); a parameter outside the graph's spans is refused, not extrapolated.Curve2::Sinusoidevaluation: point, first and second derivative, a one-turn domain, and exact inversion (the parameter is the point's first coordinate, then its height is checked) (ADR 0071).
axiolid-exact
0.1.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.1.1 - 2026-09-28
Added
FixedInterval: a real number between two big-integer bounds at a chosen number of fractional bits, every operation rounded outward, andFixedInterval::sin_cos, certifiedsinandcosof a dyadic angle (Taylor series with the Lagrange remainder, after halving the angle). For signs of values no finite arithmetic holds exactly, asked again at a higher precision until they show (#181).
0.1.0 - 2026-09-24
Added
- First release (ADR 0068, #154). Filtered exact arithmetic for constructions:
Interval(outward-roundedf64, the fast tier),Dyadic(big-integer mantissa times a power of two, the exact tier), andcertify, which runs oneSignExprin the first and falls back to the second only when the interval cannot decide. Root2,sign_root,sign_two_roots: signs and comparisons of(a + b*sqrt(c)) / d, including across different radicands, by squaring with case analysis. Exact zeros are reported as zeros.- Constructions:
crossing_orientation(which side of a line two lines cross),line_circle_hits(missed, tangent or two hits, with tangency decided exactly),LineHit::{cmp_param, orientation}andcompare_along. TowerandNested: values with any number of nested square roots (capped at depth 6), with exact signs by recursive case analysis and an interval filter throughArith::sqrt_enclosure.IntPolyandRealRoot: exact real roots of integer polynomials by square-free reduction and Sturm sequences, isolated in dyadic intervals.- Conics:
Conic, exact line/conic and conic/conic intersection points with multiplicity, and the side of a line a conic point lies on. Arith::from_dyadic,Dyadic::enclosure(a soundf64interval; a point for values a double holds exactly),Dyadic::approx_parts,Interval::{quotient, disjoint}.
axiolid-exact-compile-contract
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-field
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-field-ops
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-fixtures
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-guarantees
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-heal
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.0 - 2026-09-23
Added
- Repairs carry corner-indexed channels: weld leaves them untouched (they index values, not positions, so a seam is lossless), dropping and flipping triangles move their entries (#112).
Fixed
- Repairs keep attribute channels and normals in step with the geometry they rewrite (#114). Weld compacts per-vertex channels and normals; a seam drops the channel by name, a hard edge switches normals to corner-indexed. Dropping or flipping triangles moves corner-indexed normals with them.
RepairReport::attribute_fatesnames every input channel's fate.
axiolid-inspect
0.3.4 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.3 - 2026-09-27
Added
topology(#144): every connected component of a two-manifold triangle mesh, classified exactly from its connectivity -- counts, Euler characteristic, boundary loops, orientability and consistent winding, and the surface (SurfaceKind::Orientable { genus }orNonOrientable { crosscaps }). A closed orientable component also gets a basis of its first homology:2gsimple closed edge loops, by the tree-cotree construction. Meshes with an edge on three or more triangles, or a vertex whose triangles form several fans, are refused (TopologyError::NonManifold).genusis unchanged.
0.3.2 - 2026-09-27
Added
detect_planes(#131): the planar regions of a triangle mesh, grown over shared edges within an angle and a distance (PlaneTolerance) and then certified. EachDetectedPlanegives its triangles, a point and unit normal, adeviationthat is a proven upper bound on every member corner's distance from that plane (regions are peeled until it is within the requested distance), whether the region is exactly coplanar (byorient3d), and its area. Largest first; deterministic.
0.3.1 - 2026-09-27
Added
intersection_volume,difference_volumeandenclosed_volume(#183): the volume two closed triangle meshes share, the volume of the first outside the second, and a mesh's own volume, each as aVolumeIntervalcertified to contain the true value and never negative. No boolean is built: every face pair whose shadows overlap contributes the integral of the lower of the two planes over the overlap, with sides of lines and the lower plane decided exactly and the arithmetic rounded outward. Coplanar faces and touching bodies are exact cases. Open, non-manifold, self-intersecting or non-finite meshes are refused with the operand named (OverlapError); either winding is accepted.
axiolid-levelset
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-linear
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-linear-intersection
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-measure
0.3.3 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.2 - 2026-09-27
Added
- Fréchet distance between polylines (#147):
frechet_distance(the continuous distance: the least critical value of the free space that the Alt-Godau decision accepts),discrete_frechet_distance(Eiter-Mannila,O(nm)time,O(m)memory) and the decisionfrechet_at_most, each with a_2dform. Empty polylines, non-finite points and an invalid leash areFrechetErrors. Floating point, not certified.
0.3.1 - 2026-09-27
Added
Face domains trimmed by pair sections of two B-splines: the section's nodes are kept as breaks, where its parameter speed changes.
Face domains for B-spline and lifted pcurves. Turning points come from a dense scan of the derivative's signs with bisection, and a B-spline's knots are kept as breaks.
Turning points of
Curve2::Implicitpcurves, isolated with interval bounds, so faces trimmed by them get a certified domain.
Fixed
FaceDomain::containscounts winding -1 as inside (a face whose loops run clockwise in its parameters). It also moves a point by whole periods of a torus's second angle into the face's box.Face integrals split an implicit pcurve at its cell boundaries, where its parameter's speed jumps, so adaptive quadrature converges there.
FaceDomain::containsdecides a point whose ray passes through a boundary vertex or along a boundary piece, by simulation of simplicity (the ray runs just right of the point), instead of answeringNone. The centre of a diamond, whose every axis ray meets a corner, used to be undecidable.
Added
FaceDomain(#167): an exact face's parameter domain, answering whether a point lies in the face with a certificate or not at all; a point outside the domain's box in a coordinate that does not wrap is decided at once.boundary_distanceandboundary_clearance(#125, ADR 0074): an interval certain to contain the distance between the boundaries of two exact B-reps, with witness points on both, and a comparison with a limit that answersClearance::Below,AboveorIndeterminate. Branch and bound over face patches and edge spans; lower bounds from Lipschitz spheres and exact projection ranges, upper bounds only from points certified on the boundary.exact_propertiesmeasures curved faces (#125, ADR 0073): cylinders, cones, spheres, tori, elliptical cylinders, B-spline faces, and planar faces bounded by arcs or ellipses. Each face is integrated over its own parameter domain by Green's theorem round its pcurves, with adaptive Gauss-Kronrod quadrature held to a relative error of 1e-13; nothing is tessellated. Seams, poles and apexes, and torus faces bounded by meridians are handled; a boundary that encloses nothing in the surface's parameters is refused.
Changed
ExactMeasureErroris unchanged in shape (exhaustive, four variants), so this is a patch release. A face the module cannot integrate -- a surface family it has no integral for, pcurves that bound no domain, a surface it cannot evaluate there, an integral short of its error bound -- isNonPlanarFace, whose name predates curved faces, with the reason. The display text reads "cannot integrate a face (reason)".- The
exactfeature now also enablesaxiolid-evaluateandaxiolid-curve.
Fixed
FaceDomainand the certified distance read a point on a periodic face with a negative angle as outside it (#167): the whole-period shifts tried had the wrong sign, so-0.2was tried at-0.2 - 2 pi, not at2 pi - 0.2. Faces reaching a pole hid it, since the pole adds its own crossing.A planar face with a hole reported the hole's area added to its own: the fan summed triangle magnitudes. Areas are now summed as vectors, so a hole subtracts (a 4 x 4 plate with a 2 x 2 hole read 20 per cap, not 12).
exact_propertieshonours face, shell-use and bound orientation. It read loop winding alone, which is only right for faces used forward; aReversedcap off the planez = 0added its volume instead of subtracting it (a unit cube at2 <= z <= 3measured 7/3). Every solid tested before sat onz = 0, where the error vanishes.
axiolid-mesh
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.0 - 2026-09-23
Added
AttributeFate::then: the fate of a channel through two sequential steps (dropped wins and keeps the first reason; any interpolation interpolates).- Corner-indexed attribute channels (#112):
AttributeChannel::corner_indices, one entry per triangle corner, mirroringNormalAttribute::indices. Source formats store texture coordinates this way; positions stay shared, so UV seams no longer force a choice between splitting vertices (breaking closure) and smearing values. AttributeChannel::corner_indexed,is_corner_indexed,value_count,at_corner(reads either addressing,Nonefor an unmapped corner), andAttributeChannel::UNMAPPEDfor triangles that carry no value.validate_structurechecks corner channels: whole tuples, one entry per corner, entries in range, and each triangle fully mapped or fully unmapped. NewMeshValidationErrorvariants name the channel.DropReason::ConflictingValues: merged vertices carried different values, so a per-vertex channel could not keep both (#114).DropReason::IncompatibleChannels: inputs being combined define one channel name with a different width or blend (#115).
Changed
- Breaking (minor slot pre-1.0, ADR 0067):
AttributeChannelgains the public fieldcorner_indices, so struct-literal construction must addcorner_indices: None;AttributeChannel::newis unaffected.DropReasonis now#[non_exhaustive], so an exhaustivematchon it needs a wildcard arm. No caller in this workspace or in openbim does either.
axiolid-mesh-boolean-boolmesh
0.3.2 - 2026-09-28
Changed
- Behaviour change: a refusal inside the solve (an odd edge-point count in
pair_up, #101) is reported asGeomError::BackendContractViolationnamingboolmesh, notDegenerate. The operands passed every input gate, so the failure is this provider's defect and no longer reads as the caller's.tests/solve_failure.rspins it on a grid union that still reaches the refusal (#203). - The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Fixed
- The scratch probe discards one warmup boolean before measuring, and measures peaks above the bytes already live, so the first operation is no longer charged for process startup (#110). A
scratch_boundtest fails if any measured peak exceeds the declared 4 KiB per triangle.
0.3.0 - 2026-09-23
Added
- The pairwise boolean carries attribute channels (#116). Each result triangle's source triangle is tracked through the CSG core, so a corner that is a source corner copies its value and a corner on a cut is derived in its source triangle under the channel's
Blend. Output is corner-indexed; faces from a tool without the channel areUNMAPPED. Fates:Preservedwhen nothing was derived,Interpolatedotherwise,Dropped(NotBlendable)for aBlend::Nonechannel a cut needs. Every path carries channels (#116, completed): the analytic box path (sources recovered by plane lookup), groupedsubtract_many(fused cutters keep theirs), treeunion_many(each solid conformed to the first's channel set) and the empty result.
Fixed
- A result corner was sampled in its RECORDED source triangle, but simplification merges coplanar faces, so a result face can span several source triangles: corners were extrapolated (weights down to -0.65), wrong for piecewise data such as atlas UVs or per-face ids. Each corner is now located in the coplanar source region by a point just inside its face, which also picks the right side of a seam.
dedupe_edgepushed per-face data indexed by a vertex id when pinching a vertex, desynchronising face normals and provenance from the faces (inherited from upstream; Manifold pushes only per-vertex data there). TheProviderLimitationcomment claiming the boolean returns positions only, stale since ADR 0047, is corrected.
axiolid-mesh-boolean-contract
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.0 - 2026-09-23
Added
merge_fates: compose per-channel fates across sequential steps (#116).
Fixed
- Composed evidence reported only the last step's attribute fates:
BooleanEvidence::absorb(thesubtract_many/union_manydefaults) andsymmetric_difference_via_compositionnow compose them, so a channel a middle step derived or dropped is reported that way.
axiolid-mesh-compile
0.3.6 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.5 - 2026-09-28
Fixed
- A boolean whose result touches itself is refused, not returned (#194): where operands meet tangentially -- a void tangent to its host's face -- the solid has no material between two faces, and the mesh boolean keeps two copies of the vertices there, closed by index but pinched by position. Consumers welding by position saw an edge with four faces. The result is now checked, positions welded, for an edge with more than two faces or a vertex with separate fans, and refused with
GeomError::Degeneratenaming the edge or point of contact. Circular voids tangent along an axis direction no longer pinch at all (construct). - Structural sections and rounded rectangles extrude to meshes (#193, via construct).
0.3.4 - 2026-09-27
Added
- Curve-bounded planes (#192), as IFC
IfcCurveBoundedPlanespace-boundary connection surfaces carry them: aSurfaceRelation::CurveBoundedover a planar basis compiles to a triangulated planar region withMeshClosure::Surface. Boundaries are read in the plane's parameters and mapped through its frame; the first is the outer loop, the rest holes. Straight boundaries (2D or 3D polylines, 3D ones on the parameter plane) are exact, curved ones chorded within the chord budget; the mesh faces along the plane's normal whatever the loop's winding. Crossing or degenerate loops, holes outside the outer loop and open boundaries are refused; a non-planar basis andimplicit_outergetUnsupportedInputnaming the capability.
Changed
- Every solid of a B-rep is tessellated, not only the first (#111): a composite profile whose members do not touch is several solids in one B-rep.
- A solid's void shells are tessellated (#120). They were dropped as "boolean intent", which silently filled every cavity of an authored
IfcFacetedBrepWithVoids-style B-rep and forced the exact booleans to refuse cavities. Each void is emitted facing into the cavity, so the mesh encloses the outer volume less every cavity; a void authored facing out of it (the STEP convention, reversed on use) is turned round, since a cavity can only remove material. A void shell that is not closed is refused rather than leaving the mesh open.
0.3.3 - 2026-09-25
Added
- The reference compiler honours
ExecutionOptions::with_chord_error(#165) everywhere it flattens a curve: profile arcs, circles and ellipses, sweep directrices, curved B-rep faces and edges, and CSG primitives. An instance scales the budget with its transform, like the tolerance, so it stays a world-space distance. Without a budget the chord error is the linear tolerance, exactly as before. Measured on a 5 mm disc extruded 1 m atTolerance::MILLIMETRE: 10 % short by default, 2.6 % at a 0.1 mm budget, 0.16 % at 10 um and 0.01 % at 1 um.
Fixed
- A surface model with a face whose outer bound encloses no area tessellates (#171): that face covers nothing, so it is skipped instead of refusing the whole model with
planar face bound has zero or non-finite area. Real Nova MEP exports write pipe-fitting end caps as bowtie quads through the pipe axis (an annulus with a negative inner radius), each with signed area exactly 0; 42 fittings, pumps and valves in two models were refused over them. A declared solid still refuses such a face, and a zero-area hole or a non-finite bound is still refused everywhere. - Closed authored meshes stay closed (#170). Planar faces of a
PolygonMeshand of a B-rep were triangulated with earcut, which drops corners on a straight run and runs diagonals and hole bridges over corners of the same face; the neighbouring face still split that edge at the corner, so the mesh cracked (T-junctions). Every edge earcut invents is now split at each face corner on it, within a band of a thousandth of the linear tolerance (1 um atTolerance::MILLIMETRE), so export noise on shared corners (1e-8 to 5e-8 m on real files) is judged the same on both sides. Authored ring edges are never split, and a thin triangle whose long side is authored is kept. Closure of an authored mesh is now read from its index connectivity instead ofaudit_mesh, which dropped real faces below its area threshold before counting edges. On two real ArchiCAD models this turns 505 authored-closedIfcPolygonalFaceSetproducts fromSurfaceintoSolid; no product that compiled before is refused.
0.3.2 - 2026-09-25
Fixed
- A directrix trimmed from a circle or ellipse ACROSS its seam sweeps the arc the trim names (#168). The trimmed curve runs from
startthe waysense_agreementsays, wrapping past the seam if it must; the directrix path used to sort the two trims and sample the complementary arc. Standalone that was silently wrong geometry (a315 -> 45degree bend swept the 270 degree arc); inside a composite the ends no longer met and the sweep was refused ascomposite directrix has a N unit gap. In a real Revit rebar model that was 1,494 bent bars, all writing a bend as270 -> 45,270 -> 15or270 -> 360rounded just past the seam. A full turn rounded past the seam stays a full turn. A sweepparameter_rangeon such a trim is read in the trim's unwrapped interval, so(330, 30)and(330, 390)degrees name the same sub-arc of a315 -> 45trim; an end off the arc is refused as before. Profiles already honoured this.
0.3.1 - 2026-09-24
Added
PolygonMeshfaces that are not plain triangles compile (#160): n-gons, concave faces and faces with holes (IFC4IfcIndexedPolygonalFaceWithVoids) are triangulated in their own plane, keeping the authored positions and winding. Plain triangles keep their exact corner order as before. A face whose corners leave its plane by more than the linear tolerance, that has no area, or whose rings cross is refused with an error naming its index.- B-reps with shells but no solid tessellate (#161): every shell is tessellated as authored and the result is reported as
MeshClosure::Surfacethroughcompile_mesh_reported, even when the shell is closed. Collections areSolidonly if every member is, and a boolean with a surface operand is refused. Authored meshes reportSolidexactly when they are closed, consistently wound two-manifolds.
Changed
- A
PolygonMeshwith non-triangular faces used to fail withGeomError::Unsupported; it now compiles. A B-rep with no solid and no shell is refused as "neither a solid nor a shell" instead of "has no solid".
0.3.0 - 2026-09-23
Fixed
InstanceandCollectionnodes no longer drop attribute channels and normals (#115). Both used to rebuild the mesh from positions and indices only, so a textured item lost itsuvchannel as soon as a product had a second item or was instanced — silently.Instancecarries channels through unchanged and now transforms normals by the inverse transpose instead of dropping them. Under a mirroring transform, corner-indexed channels and normals swap corners with the triangle.Collectionmerges channels by name. A channel on only some members becomes corner-indexed, with the other members' trianglesUNMAPPED; a channel on every member as per-vertex stays per-vertex. Members defining one name with a different width or blend drop it asDropReason::IncompatibleChannels.- Booleans keep the provider's channel fates, composed onto what each operand already went through, instead of discarding the evidence.
Added
ReferenceMeshCompilerimplementsMeshCompiler::compile_mesh_reported: the compiled mesh plus each channel's fate on its way to the root (worst over parallel members, sequential through booleans).
axiolid-mesh-compile-contract
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-24
Added
MeshClosureandCompileOutcome::closure(#161): whether a compiled mesh bounds a solid (Solid), is a surface model with area but no volume (Surface), or was not reported (Unknown, the default foruntrackedandtracked, so existing compilers build unchanged).CompileOutcome::solid_meshreturns the mesh only forSolid, so volume readers refuse a surface model instead of measuring a closed shell the source never declared a solid.with_closuresets it.
0.3.0 - 2026-09-23
Added
CompileOutcomeand the provided methodMeshCompiler::compile_mesh_reported(#115): a compiled mesh with the fate of each attribute channel. The default wrapscompile_meshand reportsattribute_fates: None("not tracked", not "nothing dropped"), so existing implementations compile unchanged.
axiolid-mesh-contracts
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-mesh-section-contract
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-minkowski
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-model
0.3.3 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.2 - 2026-09-27
Fixed
- A
SolidOperation::BoundedHalfSpaceboundary must be a 2D curve (#162). The graph accepted a 3D curve, which the compiler refuses, so such a graph validated and then could never compile; it is now refused when the graph is built.
0.3.1 - 2026-09-27
Added
A
Curve2::QuadraticGraphorCurve2::AngleGraphwith finite coefficients is accepted as a trim basis (#119).A
Curve2::Sinusoidis a valid trim basis when its three coefficients are finite (ADR 0071).
axiolid-nurbs
0.3.4 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.3 - 2026-09-28
Changed
- Traced sections decide signs of analytic series fields with certified arithmetic where
f64cannot (#181): exact zeros by a Laurent identity in the harmonics' common angle, point signs by fixed-point intervals at rising precision, and box signs by the Bernstein coefficients of a certified Taylor form. The tier answers only where the field is flat (along a line of contact or at a singular point), so ordinary sections trace exactly as before. Lines of contact that rounding hides over more than a twentieth of the window, refused asUndecideduntil now, are traced on analytic fields as on B-spline ones (ADR 0077).
0.3.2 - 2026-09-27
Added
- Surface knot removal, degree change and iso-curves (#141):
remove_surface_knot_u/_vremove a knot up to a requested number of times, keeping the surface within a tolerance, and report how many copies were removed with a deviation bound (SurfaceKnotRemoval). The bound comes from the control nets after inserting the knot back, so it holds everywhere, not only at samples; a knot that carries shape is left in place.elevate_surface_degree_u/_vraise a degree exactly.reduce_surface_degree_u/_vlower one within a tolerance (BoundedSurface), or refuse; rational surfaces are refused, as for curves.iso_curve_at_u/_vextract the exact iso-parameter curve. Rational surfaces are handled in homogeneous coordinates.
0.3.1 - 2026-09-27
Added
An exact tier for B-spline fields: signs at points and over boxes (and of derivatives) decided in dyadic Bernstein arithmetic, with no division, behind an interval filter, wherever
f64rounding cannot decide. A B-spline field's trace excludes pieces, finds sides monotone, places roots and tells lines of contact apart on exact signs, so it has no precision limit: lines of contact of every order its degree allows are found (tested tox^21), their derivatives tried up to that degree. Series fields keepf64, split pieces below rounding, and refuse lines of contact rounding hides over more than a twentieth of the window.Surfaces tangent along a whole curve (a line of contact) are traced. A first or second derivative of the field crosses zero regularly on the line and is traced instead; its curves are kept where the field vanishes along them with its gradient, as crossing or touching by the field's sign across. The field is then traced with tubes about the lines left out, and branches running into a line are bridged to it at a vertex. Crossing lines are sections; touching lines add none. Derivatives of every order are tried (up to 16), the tubes sized to where the field clears its rounding; only lines rounding hides over more than a twentieth of the window stay
Undecided.A trace trusts a value's sign only clear of its rounding, on a piece's sides and across it (it could certify cells along noise near high-order contact).
Contact to higher order than a saddle (a singular Hessian: a tacnode, where two branches touch each other) is a vertex too. The ends are checked against the field's sign changes about the point, and a tacnode's bridges arrive along the Hessian's null direction. Tangency along a whole curve without crossing is touching; with crossing it is
ExactIntersectionRefusal::Undecided, a new variant that also names an exhausted budget (both wereNotRegularCurve, which now means touching only).A B-spline section tangent to a sub-patch edge (a knot line, a window's or a split's edge) is traced: the touching crossing, where no proof can exist, is found by damped Newton and seeds the curve; a regular crossing that cannot be proven is still refused.
Faster traces near contact: singular points are sought as soon as a small piece is irregular, sides use the tighter of the direct and the mean-value bound and split their piece rather than search deep, and
SeriesField2::valueevaluates the value alone (bit for bit the same).Sections of two B-splines are certified. Every crossing of a sub-patch edge with the other sub-patch is isolated by Krawczyk's test on Bernstein enclosures of the surfaces and their partials, so every component is seeded; every chord of a traced curve is proven, by a parametric Krawczyk test over the chord's sweeping plane, to follow one arc (a chord that cannot be is halved, a curve that still cannot is re-traced with shorter steps, then refused). Patches are cut to the windows first, so a curve crossing a window inside one patch pair is found. B-spline curve/surface crossings use the same isolation.
spline_pair_intersection: every component of the section of two B-spline surfaces within parameter windows, asPairSection3curves. Bezier sub-patch pairs are split until their normal cones are apart, so no closed loop hides in one; sub-patch edge crossings seed every component, and each is followed on both surfaces to the window's edge, where its last node is solved exactly.exact_surface_intersectionreturns it for two B-splines (Derivation::PairTrace).exact_curve_surface_intersectionfor a B-spline curve against a B-spline surface: hull pruning over the Bezier pieces of both, then Newton.
Changed
- The trace separates the singular points it meets. An isolated point where the surfaces touch without crossing is dropped, with the boxes about it that rounding cannot decide. A trace that finds only such points answers
NotRegularCurve, as the closed forms do for touching. Branches crossing at a saddle meet at a vertex: the pieces about the crossing that cannot be certified are dropped, and each of the four branch ends is joined to the crossing by a bridge cell (the cubic matching the branch's value and slope at its end and its tangent at the crossing). Rounding there is measured on the field's terms at the point, not its coefficients. Whole-turn windows start an irrational fraction of a radian past-pi, so a symmetric section's special points never sit on the window's edge.
Added
section_curve_curve_intersection2(B8, #119): lines, conics, sinusoids, quadratic and angle graphs and implicit curves meet each other, in any pairing. One piece is traced over its span and the other's defining field has its roots isolated along it; roots on a graph's other branch are filtered out.section_curve_curve_intersection3(B8): space curves of the section families meet where one crosses a surface the other lies on, kept within a tolerance.extrema::minimum_distance(B7, #119): the smallest distance between points, curve spans and surface patches of any analytic or B-spline family and traced sections, bracketed and certified, with witness points. It is a branch and bound whose lower bounds are exact image boxes and projections on the joining direction, in mean-value form so they close quadratically at a closest point.Sections of B-spline surfaces by planes, quadrics and tori (#119, ADR 0077). The analytic equation is read on the spline's rational Bezier patches as Bernstein polynomials (knot insertion, then Bernstein products) and traced with the same certified subdivision, clipped to the spline's domain.
implicit_surface_intersectionandtrace_section_pcurvestake a B-spline carrier.exact_curve_surface_intersectiontakes a B-spline surface for lines, circles and ellipses. The curve is written as the meeting of two analytic surfaces; the first is traced on the spline and the second's roots are found along it.A plane through a cone's apex cuts rays along the rulings of the modelled nappe (
Derivation::ConeApexRulings, spans[0, inf)). It was refused before. A plane flatter than the cone meets it only at the apex (NotRegularCurve).Curve/surface intersection for the section families (B9, #119):
exact_curve_surface_intersectiontakes anImplicitSection, andsection_curve_surface_intersectiontakes any ruled, torus or traced section over a span. Roots are isolated along the curve's cells with interval bounds. Parameters areExactCurveParameter::Certified.trace_section_pcurves,extract_stretchandimplicit_viewgive a section's implicit pcurve on any analytic face, cut out between given points, and a ruled or torus section as an implicit curve.Traced sections (#119, ADR 0077):
exact_surface_intersectionnow builds a torus against a cylinder, elliptical cylinder, cone or torus off its axis (Derivation::ImplicitTrace). The section is found in the torus's parameters as every component of the other surface's equation, by certified subdivision into monotone cells. There is no marching, so no loop is missed for want of a small step. A singular point (surfaces touching where branches cross) is refused asNotRegularCurve.implicit_surface_intersectionexposes it with an explicit window for any analytic pair, andsection_field_ofthe field itself.exact_surface_intersectionderives ruled quadric sections (#119, ADR 0076): a cylinder or elliptical cylinder against a plane, sphere, cylinder, elliptical cylinder or cone, and a cone against a plane, sphere or cone (on the modelled nappes, decided exactly), when no line, circle or ellipse applies -- pipe tees, off-axis sphere/cylinder junctions, oblique cone cuts, parabolas and hyperbolas. Which spans of angle carry the curve is decided by exact root isolation of the discriminant.ExactIntersectionCurvegainsspansandDerivationgainsRuledQuadricSection.exact_surface_intersectionderives a ring torus's section by a plane or sphere off its axis (#119, ADR 0076) -- a pipe bend meeting a wall, a ball against a ring -- asu(v)solvingA(v) cos u + B(v) sin u = C(v), with spans and wrap points decided exactly (Derivation::TorusAngleSection).Exact intersection of analytic curves (#119):
exact_curve_surface_intersection(line, circle or ellipse against plane, cylinder, elliptical cylinder, cone, sphere or torus) andexact_curve_curve_intersection2/3(lines, circles, ellipses). The equations become integer polynomials in the line parameter or the conic's half-angle parameter, solved withaxiolid-exact: each hit, its multiplicity (2 = touching) and "the curve lies on the surface" are exact decisions, and a hit at a conic's half-angle singularity (theta = pi) is reported asAntipode. A cone counts only its modelled nappe; a line on the cone through its apex is refused asPartialOverlap. Points are rounded tof64for output only. B-spline operands stay on the certified tier.
Fixed
exact_surface_intersectiondecides tangent, parallel and perpendicular cases exactly (#119). Inf64a plane exactly tangent to a sphere along a normal like (3, 2, 6) came out as a circle of radius about 1e-7, an exactly perpendicular oblique plane cut a cylinder in a near-circular ellipse, and an exactly parallel one produced a huge ellipse instead of rulings. Only output coordinates are rounded now.
axiolid-overlay
0.3.6 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.5 - 2026-09-28
Changed
- Straight-edge booleans are exact (#173).
overlay,union_soupandRegionno longer go throughi_overlay's integer grid, which snapped every output coordinate, including untouched input vertices, to a step of about 1.5e-8 of the operands' extent. They now run on the exact subdivision the arc path uses: every ring of both operands cut at once, each piece classified by exact signs, kept by the operand's winding number under the fill rule. An input vertex the operation does not move comes back bit-identical (so[0,4]x[0,0.2]clipped by a box around it has area0.8, not0.800000011920929), and a crossing of two segments is the double nearest to the exact crossing. Fill rules and ring orientation keep their meaning (tested against the old backend). A vertex where the boundary runs straight on is dropped only when exactly straight. - Arc and arrangement output: rational vertices (every segment crossing) are now correctly rounded instead of rounded to about 50 bits.
- The exact subdivision indexes edges and rings in box trees, so building it over thousands of rings (a projected mesh's triangles) is no longer quadratic in the ring count.
0.3.4 - 2026-09-27
Added
minimum_enclosing_circle(#118): the least circle holding a point set, by Welzl's algorithm in a fixed visiting order. Every in/out decision is exact, so the support points are the exact minimum circle's; the centre is enclosed from exact dyadic values and the radius rounded up, so the returned circle holds every point, andCircleEvidence::errorbounds both the centre's offset and the radius's excess. Refuses empty and non-finite input (CircleError).
0.3.3 - 2026-09-27
Fixed
- Unions of many small cells are operands again (#191): a hole touching its outer ring at a vertex was called outside whenever that vertex was the hole's first and lay where the boundary-exclusive ray test counts it out, so the next operation refused the region with
HoleOutsideOuter. A hole is now outside only if a vertex of it lies strictly outside. union_soupsettles its output like every other operation, so its polygons are accepted byRegion::new(#191).
0.3.2 - 2026-09-27
Added
minimum_area_rectangle(#182): the least-area rectangle enclosing a point set, by rotating calipers over the exact convex hull. Caliper steps and the area comparison (W H / |d|^2, cross-multiplied) are exact; ties are broken by the least angle of the first axis, turned into[0, 90)degrees, so the result does not depend on input order. Returns anOrientedRectangle(centre, unit axes, half extents) andRectangleEvidence(hull size, count of tied orientations, and a bound on the rounding of the output -- for an axis-aligned rectangle the rounding actually done, measured exactly, so zero for a box with representable coordinates). Collinear input gives an exactly zero-width rectangle; empty or non-finite input is aRectangleError.Region::visibility_polygon(#184): the part of a region with holes in sight of a point inside it. An angular sweep round the viewpoint with every decision exact -- the order of vertex directions, which edges a wedge's middle ray meets and which is nearest -- so walls and holes cast exact shadows; only the shadow ends are rounded, once, and the result is presented like the other region operations. A viewpoint on the boundary or outside is refused (VisibilityError::NotInside).
Fixed
- Every region an operation returns is now one
Region::newaccepts and the next operation takes: unions, intersections, differences, morphology, Minkowski sums and visibility polygons of shapes that share collinear edges, touch at vertices or pinch holes against their outer rings could come back with edges shorter than the tolerance or rings touching themselves, and were then refused asRepeatedVertexorSelfIntersection. Outputs are settled by the same tests validation applies -- short edges merged, a vertex touching another part of its ring put on it, rings split where they pass a point twice, holes given back to their outer rings -- each move within the tolerance.
0.3.1 - 2026-09-27
Added
Region::minkowski_sumandRegion::minkowski_erosion(#145): the Minkowski sum and erosion of a straight-edged region -- non-convex, with holes -- by a convex polygon, at any rotation. Built in oneArcArrangementwith exact decisions: the region translated by a vertex of the polygon united with the convex hull of the polygon at both ends of every boundary edge, and erosion as the region minus the sum of its complement (within a box) with the reflected polygon. Vertex sums are rounded once, output vertices once. A non-convex structuring polygon is refused asMinkowskiError::NotConvex.Region::dilate_inner,dilate_outer,erode_inneranderode_outer(#163): disc morphology on a stated side of the exact result, by inscribed and circumscribed 64-gons moved a margin further out or in (past vertex and output rounding and the presentation's dropping of edges shorter than the tolerance).Region::boundgives the side (BoundSide::InnerorOuter) and the greatest deviation from the exact disc morphology (MorphologyBound). An inner erosion proves reachability of a route found in it; an outer erosion proves unreachability where none is.dilateanderodekeep their round joins, side unstated. A 200-edge region takes about a second.ArcArrangement(#120): the plane cut by several arc rings at once. Every crossing, shared boundary piece and ring membership is decided exactly (the same predicates asarc_overlay); crossing points are rounded once, into one vertex table. Each piece records the input edges it came from and which rings contain the region on either side, andregions(predicate)links the pieces bounding any membership set into outer rings and holes. Faces built from one arrangement therefore share vertices by index, which is what a stepped or stacked solid needs.
Changed
ArcArrangementskips rings whose box holds neither a piece's sample nor an edge: winding numbers and crossings are computed only near each ring. Many-ring arrangements are several times faster.arc_overlayis exact (ADR 0070, #155). Crossings, their order along each edge, inside/outside/on classification, linking into rings and hole ownership are all exact sign decisions on the given input, viaaxiolid-exact; none reads the tolerance. Crossing points of two curves are rounded tof64once, in the output, and edges shorter than the tolerance after rounding are merged. The public API is unchanged.cavalier_contoursis no longer a dependency. The arc path costs about 70 to 100 us per boolean on typical sections instead of about 1 us (benches/arc_overlay.rs).arc_overlayskips edge pairs whose padded bounding boxes are apart, and links pieces through a sorted index instead of a scan. Decisions are unchanged (still exact); cost now grows close to linearly with edge count: two overlapping 256-edge rings went from 779 ms to 7 ms, a 4096-edge outline against a small disc from 53 ms to 17 ms.
Fixed
overlayno longer rejects a U-shape or comb asSelfIntersection. The ring check treated an endpoint on the infinite line through another edge as touching it, so two collinear edges that share a line without meeting (the two ends of a U) were refused. A touching endpoint must now lie on the edge itself. Rings that genuinely touch are still refused.arc_overlayresults no longer depend on drawing units. The arc backend's thresholds are fixed in drawing units, so a 5 um gap survived a union drawn in millimetres but vanished in metres. The drawing is now scaled by a power of two so those thresholds sit at the caller's linear tolerance, capped so coordinates stay within what f64 resolves (ADR 0069). Superseded by the exact core above, which needs no scaling.
axiolid-pointcloud
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-pointcloud-reconstruction-contract
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-pointcloud-reconstruction-sdf
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-predicates
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Fixed
incircleandinsphere: the exact fallback rounded the coordinate differences tof64before its exact expansion arithmetic, so on nearly cocircular (cospherical) points whose differences do not fit anf64-- exactly where the filter hands over -- it could return the wrong sign. Delaunay flips driven by it cycled for ever (#190). The differences are now exact two-term expansions and every product after them is an expansion product; checked against an exact dyadic determinant.
axiolid-primitive
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Added
Primitive::Torus(#142): a ring torus about local +z, by major and minor radius. Horn and spindle tori are not solids and are refused by the tessellator.Primitive::Wedge(#142): OCCT'sMakeWedgegeneral form with the height along local +z -- a base rectangle at z = 0 and a top rectangle, narrowed or shifted, at z = height. The top may collapse to a ridge or an apex.
axiolid-profile
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-project
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-ray-mesh
0.4.0 - 2026-09-28
Changed
- Breaking: a candidate triangle index at or beyond the mesh's triangle count is refused with the new
RayMeshError::TriangleIndexOutOfRangeinstead of being skipped bynearest_hit_among; a broad phase built over a different mesh would otherwise report "no hit" for triangles it never tested.triangle_hitrefuses the same index instead of panicking in the mesh view. - The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-reference
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Added
tessellate_primitivemeshesPrimitive::TorusandPrimitive::Wedge(#142). The torus is a grid of planar trapezoids sized by the chord budget, round the axis for the outer equator and round the tube for the tube; horn and spindle tori, and non-positive or non-finite radii, are refused by name. The wedge's faces are planar and shared corners of a collapsed top are merged, so a ridge or apex wedge is still a closed, outward-wound solid; a reversed or non-finite top range is refused.
axiolid-refine
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.0 - 2026-09-23
Fixed
- A refinement that creates no vertex returns the input's channels and normals. It previously reported them
Preservedand returned a mesh without them.
axiolid-route
0.3.6 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.5 - 2026-09-28
Added
- Seeded weighted maps (#198):
weighted_distance_map_seededandweighted_distance_map_seeded_withintake(point, weight)targets, each starting at its own non-negative cost, asdistance_map_weighteddoes for plain maps (#197); both bounds start there, andWeightedReach::costcounts the weight. With every weight zero the map and its answers areweighted_distance_map's exactly. weighted_forced_walkandweighted_forced_walk_within(#198): the cheapest walk that enters a polygon over two weighted maps with the same costs, bracketed with a witness asforced_walkis. The cell bound falls at twice the steepest factor meeting the cell, and the pair bound runs over both maps' vertices and cost-edge intervals with their lower bounds, at the cell's own factor when no cost edge meets it.WeightedForcedWalk::shortestbrackets the cheapest walk overall; the result is never narrower than the maps' brackets at the witness, and the search stops at the tolerance plus those.
Changed
- A cost region touching the region's boundary up to rounding is taken as touching it (#198), no longer refused with
MapError::CostCrossing: a cost vertex within 2^-24 of the region's extent (and a few ulps) of a region edge, on its free side, is moved just beyond it, and a cost edge may cross a region edge that near one of either edge's ends. An edge so left on or beyond a wall is wall-borne, as one exactly along it. So a footprint clipped to the free region, its corners rounded by the overlay, builds a map, and leaves no sliver along a wall costing 1.
Fixed
- Weighted maps on cost edges at an angle (#198). The points cutting such an edge are interpolated and lie off its line by rounding, which made three decisions go wrong: an interval's sides were taken of its rounded ends, so could both read the factor of one side; an edge could appear to cross every hop to its own intervals and block them -- both could put the lower bound above the distance; and every hop along the edge was charged the greatest factor above. Sides are now taken of the edge's exact ends, an edge never blocks hops from its own line, and a hop along an edge is costed above as the walk along the edge itself, which lies that close to it.
0.3.4 - 2026-09-28
Added
Weighted targets (#197):
distance_map_weightedanddistance_map_within_weightedtake(point, weight)targets, each starting at its own non-negative distance -- a stair landing carrying the rest of a walk beyond it.nearestanswers the least route length plus weight, in the newReach::distance, and names the target the route ends at, which may be a lighter target the heavier one is reached from;route.lengthis the route's own.farthest_pointandforced_walkkeep their brackets (the weighted distance is still 1-Lipschitz), andForcedWalk::shortestcounts the origins' weights. With every weight zero the map isdistance_map's.MapError::InvalidWeightrefuses a negative or non-finite weight.forced_walk(#196): the shortest walk from an origin to a target that enters a polygon,min over p of d_origin(p) + d_targets(p), bracketed to a tolerance from two distance maps over the same free space. Branch and bound as forfarthest_point, with a second lower bound from the maps' own vertices (D(u) + D'(v) + max(|u - v|, dist(T, u) + dist(T, v))over the vertices a cell may see), which is exact on every cell the walk runs straight through.ForcedWalk::shortestis the shortest walk overall; a lower end above it proves no shortest walk enters the polygon. A polygon no walk reaches is infinitely far, not an error.FarthestError::MismatchedMaps, for two maps over different region or barriers.Weighted distance maps (#195):
weighted_distance_maptakes cost regions (CostRegion: a polygon and a factor of at least 1; overlaps take the greatest, and a walk along a cost edge pays its cheaper free side) and points along cost edgesspacingapart.WeightedMap::nearestbrackets the weighted distance to the nearest target (WeightedReach::cost) and gives the walk whose cost is the upper end. The upper bound is the exact visibility graph with those points added, each edge costing its weighted length; the lower bound is a graph over vertices and intervals of cost edges whose hops cost no more than any piece of an optimal walk between them, with the states an optimal walk cannot take -- turning back at a cost edge, two pieces in a row along one line -- barred. Square crossings are exact; otherwise the gap is first order in the spacing.weighted_farthest_pointbrackets the farthest point of a subregion by the same branch and bound, each triangle's slope its greatest factor.MapError::InvalidFactor,InvalidSpacingandCostCrossing: cost regions may nest, touch, share edges and run along the region's boundary; one crossing an obstacle or another's edge, or running along a barrier, is refused.
Changed
- Segment crossing tests reject disjoint bounding boxes before any predicate, and a segment's pass-through check visits only vertices with obstacle rays:
distance_mapbuilds several times faster.
0.3.3 - 2026-09-27
Added
skeleton(#139): the corridors of a region with holes as a graph -- path ends, junctions and paths -- for circulation checks. Nodes are Voronoi vertices of the walls sampled at mostspacingapart, over a constrained Delaunay triangulation, pruned of spurs into corners as in the lambda-medial axis (prune; 1.5 drops spurs into right-angled corners). Every node is decided inside the region exactly and carries aclearanceinterval proven to contain its distance to the nearest wall; each path end names the wall it runs into (ahead). The skeleton's position approximates the medial axis and is not certified.
0.3.2 - 2026-09-27
Added
distance_mapanddistance_map_within(#186): shortest-path distances to the nearest of several targets, from one multi-source Dijkstra over the visibility graph.DistanceMap::nearestgives, for any point of the region, the nearest target's index and the route there (Reach). Refusals are typed:MapError::NoTargets,MapError::TargetOutside.farthest_pointandfarthest_point_within(#186): the greatest distance to the nearest target over a polygon subregion, as aLengthIntervalthat contains the true value, with a witness point (Farthest). Branch and bound over a constrained triangulation of the free space, bounding each cell by the 1-Lipschitz property of the distance from anchors proven inside it; widened for the rounding of lengths and midpoints. Part of the subregion that no target reaches is refused with the triangle as evidence (FarthestError::Unreachable); crossing barriers are refused (FarthestError::CrossingObstacles).
Fixed
- Routes no longer squeeze through a point where obstacles meet (#189): a barrier's foot on a wall, two holes touching at a corner, a barrier bent or joined at a vertex. Each graph vertex is split into the free sectors between the obstacle rays leaving it, ordered exactly; a route moves over (vertex, sector) states, each edge is taken on one side of travel and attached to the sector beside it at each end, and passes through a vertex only on a side with no obstacle ray and free space. Two polygons sharing an edge are walkable along it on either side.
shortest_pathandDistanceMapshare the graph.
0.3.1 - 2026-09-27
Fixed
- A route could run along one wall, through a vertex and on across a gap outside the region where two walls line up (#187): visibility tested only proper crossings and the segment's midpoint. A segment is now cut at every obstacle vertex lying on it, decided exactly, and each stretch is either along an obstacle edge or has its midpoint in the region. Two rooms whose corridor is cut are
DisconnectedComponentsagain.
axiolid-spatial
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Added
- Barycentric coordinates (#143):
triangle_barycentric2,triangle_barycentric3(for the point's projection onto the triangle's plane) andtetrahedron_barycentric, exact at corners; andmean_value_coordinates2for simple polygons, convex or not, which interpolate the boundary linearly and reproduce points inside. Shapes thinner than the linear tolerance, non-simple polygons and points where mean-value weights cancel are refused withBarycentricError.
axiolid-surface
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Changed
BSplineSurfaceis defined inaxiolid-curve(so a curve traced on a B-spline surface can carry its carrier, ADR 0077) and re-exported here unchanged: same fields, same derives. Requiresaxiolid-curve0.3.1.
axiolid-tessellation-contract
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-topology
0.3.1 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
axiolid-triangulate
0.3.2 - 2026-09-28
Changed
- The crates.io page is this crate's own
README.md, with links to its API documentation, its reference page and the source (ADR 0078).
0.3.1 - 2026-09-27
Fixed
triangulate: after recovering constraint edges, unconstrained edges are flipped back to locally Delaunay (Lawson's flips). Recovery used to leave long triangles whose circumcircles held points no constraint hid, so the result was not constrained Delaunay as documented -- with finely sampled walls, triangles spanned whole rooms (#139).triangulateno longer loops for ever on outlines like a square turned 45 degrees (#190): legalisation after inserting a point checked the new diagonal instead of the two edges across from the point, so the real edges were never checked and thin quadrilaterals flipped back and forth. It now checks those edges, flips only strictly convex quadrilaterals, splits the edge (and both triangles beside it) when a point lands on one, and carries a flip bound.- Constraint recovery no longer gives up at the first crossing edge it cannot flip (#190): it follows Anglada's queue, retrying edges that cannot flip yet and requeuing new diagonals that still cross, and reports
CrossingConstraintsonly when a full pass flips nothing.