Keep all solid geometry GPU-resident during navigation. Use vertex-rate instancing, compact untextured vertices, vertex-cache ordering, and cached silhouette sources without culling geometry.
The shaded surface of a curved solid was visibly coarser than its own
wireframe: the wire ran along the true rim while the facets cut inside it.
Two things were wrong, and only one was the tolerance.
`facet_res` is a resolution multiplier — the same one `scale_lod` divides the
fallback sampler's chord fraction by — and the kernel path was using it as a
sag in world units. At the default that asks for a whole unit of departure, so
nothing smaller than a metre subdivided at all and a face was only ever as
round as its own boundary. It is a fraction of each surface's own radius now,
which carries no assumption about the drawing's units.
And it is the *edges'* fraction, shared from `EDGE_CHORD_FRAC` rather than
copied. Those edges are drawn over these faces; sampling the two differently
is what put the wire across a facet instead of along its corners. `facet_res`
is deliberately not applied either, since the edges it must line up with are
built once at highest detail and never scaled.
A torus is measured by its tube rather than its ring — the tube is the tighter
bend, and sampling to the ring leaves the section a hexagon.
The primitive-volume test claimed a mesh can never read over its true size.
That holds for a convex solid, where every chord lies inside; a torus is not
one, and across the inside of its tube the chords fall outside the material.
It passed before only because the sampling was too coarse to show it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Faces were being dropped for sitting a nanometre off their own plane.
An edge is shared by two faces and in a real file cannot lie exactly on both:
the two surfaces were fitted separately and written to finite precision. Asked
for exactness, the kernel decides the edge is not on its plane, declines to
project it, and the face goes undrawn — silently, since a face that cannot be
expressed is reported as absent rather than as an error.
A micrometre in a drawing measured in metres recovers twenty-six walls of one
building. Loosening further buys almost nothing — a hundredth of this finds
one more face in sixty thousand — and past that it would start accepting
geometry that really is wrong, so it stops here.
With the tube fix that came with the kernel bump, VILLA_NEU_IFC3.dwg now
meshes 59015 of its 59033 faces, from 58922.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two faults kept faces out of a lifted solid, and both looked like a document
problem rather than a reader one — the lift reported no loss at all.
An edge's stored parameter pair can span the right length from the wrong
place, putting a straight curve beside the face it bounds instead of along
it. And a loop's coedges are not always listed in the order the ring runs,
which the old chaining could not fix: it reversed the next piece in the list,
where what was needed was a different piece.
Either one leaves a ring that crosses itself, which ear clipping declines, so
the face is dropped without a word.
Handle 176 of Annotation-2D-Mesh-Solid-BIM.dwg now meshes all fourteen of its
faces, where it managed ten. Across that file: 105 of 107 solids complete, 865
of 867 faces. Across VILLA_NEU_IFC3.dwg: 672 of 763 solids, 58922 of 59033
faces.
The fallback sampler stays until that reaches all of them — a complete mesh is
what stops it running, and 111 faces still need it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Faces with holes were drawing the hole filled and the material around it
missing. Handle 176 in Annotation-2D-Mesh-Solid-BIM.dwg is one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two things the truck removal broke without saying so, both found by auditing
what is left in the manifest rather than by anything failing.
The cfg gate on `acis_export` ended up on `acad_to_render` instead, when that
module was put back after being deleted by mistake. `acad_to_render` is what
every entity converts through, so gating it left the wasm build — which is
what `--no-default-features` stands in for — unable to resolve it from thirty
places. It built at the commit before the migration and builds again now.
And deleting TRUCK_CHORD_FRAC took the constant but left its doc comment and
its `#[cfg]` behind, so the attribute landed on the next constant down and
gated a boundary-sampling tolerance that has nothing to do with solids.
lzma-sys was the last dependency in the manifest with nothing using it: no
code references it and nothing else in the tree pulls it in. The `solid3d`
feature it hung off still gates four places, so the feature stays.
Everything else declared is used. `getrandom` and `iced_widget` look unused
by grep and are not — the first is declared to turn on the feature the wasm
target needs from a crate half the tree pulls in, and the second is a
`[patch]` entry rather than a dependency at all.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.
NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.
The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.
SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.
Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.
The two `automation` test failures are unchanged from before this and are not
caused by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>