Sync projection controls from the active camera, preserve the UCS icon in perspective views, and use visible or selected 3D geometry for orbit pivots. Dense mesh vertex grips remain hidden.
Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.
Round things came out with too few sides in drawings measured in metres, and
zooming in did not help.
The chord tolerance was floored at five thousandths of a world unit. That is
a fine sampling for a part measured in millimetres and a coarse one for a
building measured in metres: a ten-centimetre pipe got about ten sides and a
five-centimetre bolt about seven. The floor was also what stopped the frame
asking for better, so closing the camera on one changed nothing.
A chord height is an absolute length, so any fixed value carries an assumption
about the drawing's units. Bounded against the curve's own radius instead,
which carries none. The two ends are really segment counts: a circle gets at
least about fifty sides and never more than about a hundred and sixty,
wherever the camera is and whatever the drawing is measured in. Between them
the frame still decides, so a curve fills in as the camera closes on it.
Straight runs keep the frame's own tolerance — there is no curvature to
sample, and two ends are all a line has.
Co-Authored-By: Claude Opus 5 <[email protected]>
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 <[email protected]>
The kernel gained a B-rep layer — topology, analytic surfaces, surface
intersection, face splitting, point classification, booleans — and
acadifc gained the bridge that lifts an ACIS document into it and lowers
it back, provenance-driven so an untouched body saves byte for byte.
The reachability test is a compile-time one on purpose. A dependency
chain that stops resolving is caught where it breaks rather than the
next time somebody reaches for the far end of it.
Co-Authored-By: Claude Opus 5 <[email protected]>
A hatch's area was not the area of anything. Its boundary was flattened
into a ring by pushing an arc's *centre* into it, and a spline's control
points — neither of which is on the boundary — and the shoelace formula
was run over the result. Boundary edges now go through the kernel, which
measures each for what it encloses: an arc contributes its sector, a
spline is integrated.
AREA had three measurements between them. The bulged-polyline one
carried its own arc corrections; the planar entities it did not cover
fell through to mass properties. Both are now one call on the entity's
curve, which gets the bulges, the ellipse and the spline right in the
same place. What is left is the genuinely spatial — a 3D polyline, a
spline whose points wander off any plane — measured as rings of points
by Newell's method rather than by projecting them flat.
Regions were doing the same Newell sum by hand. They share it now.
Co-Authored-By: Claude Opus 5 <[email protected]>
Two places were left describing geometry for themselves.
A fit-point SPLINE was drawn from a C² cubic solved in spline.rs while
TRIM and snap cut a separate interpolation from the kernel. Near enough
to look right, far enough apart that a trim landed beside the line it
was aimed at. It now draws the curve those commands work on, and falls
back to the local solve only where the kernel declines — a fit spline
through points in space is not a planar curve, so there is nothing for
it to say.
Closed fit splines were worse than that: the kernel's interpolation is a
clamped solve with no wrap in it, so the curve TRIM used never returned
to its start and the seam cut nothing. Repeating the first fit point
closes it. The seam is C¹ where the rest of the curve is C², which is
the honest limit of a clamped solve — the alternative was a curve with a
gap in it.
OFFSET's preview sampling had drifted the same way TRIM's had: an arc
and a circle read `center.x/.y` as world coordinates when the entity
stores them in its OCS, so an extruded one previewed on the wrong side;
every curved kind was cut at a fixed twenty segments per radian whatever
its size; and an ellipse rebuilt its axis frame from the major axis' X
and Y alone, dropping the Z the entity stores. A hundred lines of that
become one tessellation. A RAY now previews as far as an XLINE does
rather than as a stub the length of its direction vector.
Co-Authored-By: Claude Opus 5 <[email protected]>
The point lists were built three different ways and none of them scaled
with the geometry. A full ellipse was cut into sixteen segments per half
and an elliptical arc into thirty-two, whatever its size, so anything
large read as a polygon; a circle worked out its own segment count from
the chord tolerance; an arc was handed to truck's arc-through-three-
points builder, which cancels at survey coordinates.
All three now sample the entity's curve at the render pass's chord
tolerance, so how round something looks follows how big it is and how
far in the view is zoomed. The ellipse also stops casting its local
coordinates to f32 on the way in — the vertex path splits f64 into a
double-single pair precisely so that precision survives to the shader,
and throwing it away first defeated that.
What does not change is the kind of object each entity hands over.
EXTRUDE, REVOLVE and SWEEP read their profile and path out of Contour or
Curve and have no arm for a bare point list, so an arc or an ellipse
given as Lines would quietly stop being usable as either. The arc keeps
its circle_arc edge for that reason; only the ellipse's control points
change, and they keep the same two-half-edge wire.
The ellipse now decides it is closed from its own endpoints rather than
from its stored parameters, so a file that writes an end a hair short of
a full turn still produces a wire a plane can attach to.
Co-Authored-By: Claude Opus 5 <[email protected]>
Each entity built its own, and what they offered had drifted apart from
what the geometry actually has:
- An arc offered no quadrants at all. It now offers the ones its sweep
covers, and no more.
- A spline offered its control points as endpoints. For a control-point
spline those are not on the curve, so the cursor landed in empty space
beside the geometry. It now offers its two real ends and its midpoint,
with the fit points — which are on the curve — still on offer.
- An elliptical arc had no midpoint and no ends.
A new SnapHint::Endpoint carries an end that cannot go in key_vertices.
Consecutive entries there are taken to be joined by a straight segment
and their midpoint offered, which is right for a line or a polyline and
wrong for anything curved: an arc's two ends would also produce a snap
out on the chord. Splines lose their key_vertices for the same reason —
the chord between two fit points of a curvy spline does not run along
it.
A polyline's midpoints and arc-segment centres stay out of snap_pts
deliberately. The engine already derives a chain's midpoints from its
vertices, and a wire carrying a centre is treated as round elsewhere,
which a polyline with one bulge in it is not.
Co-Authored-By: Claude Opus 5 <[email protected]>
The same shape was being read out of an entity's fields in three places
and the three had drifted. An arc's tessellation cut it at 20 segments
per radian, TRIM's sampling at 16, and its snap points came from a
fourth expression again; a spline was sampled at a fixed 96 points
however long it was; TRIM read an arc's centre as world coordinates
when the entity stores it in its OCS, so an arc carrying the (0, 0, −1)
normal a MIRROR leaves behind acted as a boundary on the wrong side of
the drawing.
entity_curve returns a PlanarCurve: the shape, plus the plane its
coordinates are read in. The plane comes from the entity's OCS — its
extrusion normal through the DXF arbitrary-axis algorithm — not from the
UCS. A UCS is the working plane a user drew in and an entity does not
remember it: draw a circle in a UCS turned 30° about Z and the stored
normal is still +Z, which is why the header keeps the UCS separately.
The algorithm itself stays in transform.rs, where the format is
understood and where #142's degenerate case was fixed once.
Entities that store world coordinates and may not be planar at all —
LINE, SPLINE — are checked rather than assumed. A spline whose points
wander in space returns None instead of being flattened onto XY, which
would move it.
entity_curve_xy folds the plane away for the editing commands, which
work in plan view and need every boundary in one frame before two of
them can be intersected.
Co-Authored-By: Claude Opus 5 <[email protected]>
The offset core moves out: normalising the source, the cavalier_contours
call, the sharp-corner join fixup and the conversion back. What stays is
entity work — reading an LwPolyline in, writing offset LwPolylines out,
and the per-type offsets for lines, arcs, circles, ellipses and splines,
which are analytic and never needed the polyline machinery.
`BulgeArc` moves with it, since the offset preprocessing splits over-half-
turn arcs and cannot work without it. `entities::common` now re-exports it
from the kernel, so the twelve modules already reaching for
`entities::common::BulgeArc` are untouched.
`norm_rad` was a fourth copy of angle normalisation, after the three
removed from trim, fillet and explode. It now aliases the kernel's.
cavalier_contours leaves this crate's manifest: nothing here calls it any
more, and it arrives through the kernel's `offset` feature instead, which
acadifc forwards.
Co-Authored-By: Claude Opus 5 <[email protected]>
A two-line angular dimension stores two lines, not two rays:
first_point→second_point is one, angle_vertex→definition_point the other,
and the angle is the one they make where they cross. The renderer read
angle_vertex as the centre and the other two points as the rays from it,
which measures something else entirely — a ten degree angle came out as
two hundred and seventy, and a shallow one as very nearly a full circle.
The lines are now intersected for the real vertex, and the arc point
picks which of the four angles at that crossing is meant. Each line
offers its direction and its reverse, and the sweep is tried from either
line: taking only the first→second order leaves out half the angles, and
the half left out is the one wanted whenever the lines are nearly
parallel. Parallel lines cross nowhere, so only the extension lines are
drawn.
A zero sweep was also being turned into a full turn, because the wrap
that pushes a negative sweep forwards ran on zero too. Two rays pointing
the same way enclose no angle and now draw none.
Checked against the three angular dimensions in a drawing that carries
its own baked arcs: the vertex and sweep computed from the stored line
endpoints reproduce the baked centre and angle to the decimal — 7.48,
10.61 and 2.29 degrees — from entirely separate inputs.
This is the path taken when a dimension has no baked block: one OCS drew
itself, or one whose block was dropped by an edit. Both paths now agree.
Co-Authored-By: Claude Opus 5 <[email protected]>
A text style with a non-zero height fixes the size of everything drawn in
it — the CAD that writes the file skips the height prompt for such a
style, and shows the height without letting it be changed. OCS offered
the field either way, so a drawing could be edited into disagreeing with
its own style.
The height row is now read-only when the style fixes it, for TEXT, MTEXT
and both attribute kinds. `num_prop` already draws a numeric row either
way, so the value still reads the same, just grey.
The panel builders are handed an entity, not the document it came from,
and widening that trait would touch every entity type. The lookup rides
beside the unit context instead, seeded from the same place, since it
answers the same kind of question: something the drawing settles that a
row needs in order to render itself.
Co-Authored-By: Claude Opus 5 <[email protected]>
The readout learned to write lengths as 5'-6 1/2" and angles as
N 45d0'0" E, but typed input was still a bare parse::<f64>() — a
coordinate could be displayed and not entered. Give the formatters
their inverses.
parse_length takes feet and inches separated by a dash, a space, or
nothing, with the closing quote optional, and whole-plus-fraction in
any of those forms: 5', 60", 5'-9", 5'9-1/2, 9-1/2, 1/2. A leading
minus is a sign; the one inside 5'-9" is a separator, since after feet
there is nothing left to subtract from.
parse_angle reads the mark that says which convention an angle is in —
g for grads, r for radians, d/'/" for degrees-minutes-seconds, compass
letters for a surveyor's bearing — and reads a bare number in whichever
convention the drawing is set to.
ANGBASE and ANGDIR now apply, but only where they mean something. Which
way a thing points is measured from the drawing's zero and runs the way
it says; how wide an arc opens is the same number whatever zero is
counted from. format_direction and parse_direction are the pair for
directions, and the polar readout and polar input both go through them,
so the bearing shown is the bearing accepted. format_angle stays for
sizes, where applying a base angle would corrupt the value.
The command prompts follow the same rule, each classified rather than
swept: OFFSET distance, FILLET radius, CHAMFER distances, ARRAY and
MINSERT spacing, TRACE width, HELIX, and EXTRUDE height read lengths;
the four dimension text-rotation prompts, ARRAY's total angle and
INSERT's rotation read angles. MLINE's scale and INSERT's scale factor
are left alone — a multiplier has no unit to be written in. INSERT read
both through one parse; they are now separate.
The typed variants also take a decimal comma, which a single-value
prompt has no other use for. parse_length itself does not: inside a
coordinate the comma separates the axes.
Co-Authored-By: Claude Opus 5 <[email protected]>
The status-bar units button wrote INSUNITS, which nothing on screen reads.
INSUNITS only tells the drawing how to scale content arriving from
elsewhere, so picking a unit relabelled the drawing and changed nothing
visible — the selector looked broken because the variable it moved was
invisible.
Point the button at LUNITS instead, the setting that decides how every
coordinate and distance is written, and give the three questions the word
"units" runs together a place each:
- The button offers the five length formats, each shown beside a sample of
itself, so Engineering and Architectural are told apart by sight.
- UNITS / DDUNITS opens a real dialog: length and angle format and
precision, the direction angle zero points, whether positive angles run
clockwise, and the insertion unit. Its sample comes from the same
formatters the drawing uses, so it cannot drift from the readout.
- DWGUNITS converts. Model geometry, viewport framing, the header's length
defaults and each dimension style's scale all move together in one undo
step; paper space is left alone, since the sheet is still the same sheet.
Locked layers are converted too — a half-converted drawing would be at
two scales at once.
The coordinate readout formatted at four decimal places whatever the
drawing asked for; it now goes through the same format_length the
properties panel uses. That made the angle formats reachable in turn, so
degrees-minutes-seconds now carries the d/'/" marks that say which
convention it is, and surveyor's units are written at all.
Typed input is still plain decimal: a length the readout writes as
5'-6 1/2" cannot yet be typed back. ANGBASE and ANGDIR are stored but not
yet applied to displayed directions.
Closes#668
Co-Authored-By: Claude Opus 5 <[email protected]>
The merged fix added the rotation update on this side, matching only
`EntityTransform::Rotate`. That left `Affine` -- which several entity
paths and the UCS world transform emit -- still dropping the angle, and
it sat against the contract this crate already relies on: MIRROR is
delegated precisely because acadrust's entity-aware paths "handle
direction flags, stored-angle conventions and bulges themselves", and
`mirror_text` did exactly that for the same field.
Moved upstream (acadrust 9ecaffc), where one helper carries the angle for
every entity that keeps one. That covers rotation, affine and reflection
in a single place, and closes the same gap for SHAPE and the ATTRIB pair,
which a rotation left facing the old way too -- attributes noticeably so,
since INSERT already turned itself while its attributes did not.
Co-Authored-By: Claude Opus 5 <[email protected]>
Route model, paper, viewport and nested block content through one traversal while preserving leaf renderers. Carry decoded material state into mesh shading and harden solid geometry conversion.
Keep new vertices in grip placement until click and restore the original entity on Escape. Correct reverse direction and segment metadata across polyline variants.\n\nRefs #610