* claim: #13836
Co-Authored-By: Claude Code <noreply@anthropic.com>
* test(solidcore): plane-section trims on a cylinder are exact on the default kernel (#13836)
The tests of #2109 run on the default kernel instead of setting
`cylinder_plane_section_trims_exact`, and drop the two tests that pinned
the traced cubic trims (their values stay in the file's header). Two closed-form tests are added:
- the 2358 boss tilted 20 degrees: its union is 38601.117929444641;
- the 10x20x30 box with edges rounded r0.5 and mitred (the
piecewise-box-edges-* bodies of blend.fillet_executive_p3): its volume
is 6000 - r^2 (1 - pi/4) L + n r^3 (5/3 - pi/2).
Red on main: the default kernel still fits a cubic to each section on a
cylinder, so the three trim tests, the union test (3.6e-5 off) and the
box test (4.5e-6 off) fail.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* solidcore: plane sections on a cylinder get their exact UV trims by default (#13836)
Retire `UpstreamFixes::cylinder_plane_section_trims_exact` (WR-680):
`Surface::drop_curve_base` always tries `exact_uv_curve` once the domain
checks pass, so a curve lying on a cylinder (a plane section, a round's
rail) gets its exact rational UV curve instead of a traced cubic. A
curve it does not apply to is traced as before. The field, its preset
line in crates/smlib/src/core.rs and its kernel_cases/fixes.json entry
go; SolidCraft and the rig already ran with it on.
The #2358 boss's removed volumes were pinned to the fork's with this
flag off; they are re-pinned as this kernel's, tolerances unchanged
(1.7e-4 to 4.3e-4 from the fork's), and its union now equals the closed
form to 1e-12 relative. The WR-680 addendum records the old and new
values.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* goldens: bless the rows the exact cylinder trims move (#13836)
44 rows of two bodies move, values only. Rows that moved only within the
goldens' 1e-9 keep their blessed text. Every moved row with its old and
new values: plan/findings/intersect.surface_drop_lib_p3--exact-cylinder-trims-move-goldens-2026-11-10.md
- blend.fillet_executive_p3 piecewise-box-edges-* (8): the 10x20x30 box
rounded r0.5 with mitres. Integrated to 1e-10 its volume now equals
6000 - r^2 (1 - pi/4) L + n r^3 (5/3 - pi/2) to 1e-10..8e-10 (edges
0-1: 5998.402470018067 vs 5998.402470017965), where the traced trims
were 4.5e-6..8.1e-6 off (mitred_box_rounds_are_their_closed_form checks
it); the rounds' trims are within 2.1e-14 of their edges (traced:
4.3e-6). The unit integrates to 1e-4.
- sphere_diff_3cylinders.smb (36: fillet_executive_p3 10, face_p3 15,
edgeuse_p3 10, corpus.parity 1): with its trims rebuilt, 17 cylinder
trims become rational quartics within 1.6e-13 of their edges (traced
cubics 1.3e-5 off, two 9.6e-3). Volumes move by at most 2.6e-3 of
169497, areas by at most 2.2e-4 of 38237.
Details: plan/upstream-issues/WR-680--addendum-2026-11-10-default-and-flag-retired.md
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
A label run's ci job repeats the commit's newest run that ran checks. It counted cancelled
runs too, so a cancelled run (replaced by a newer one, or a fork run that waited for approval
and was cancelled with no jobs) made ci fail for good and blocked PRs that had passed
(#14633, #14759, #14760, #14441 today). Only runs that passed or failed are a result now.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
document.inspect / inspect_design take timeline: true | "summary" | false
and bodies: true | "names" | false; check_parameters takes bodies: [names]
and summary: true (measure as the measure tool's summary). Every default
reply, and what an app is sent when the new arguments are left out, is
unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brennen Herbruck <brennen.herbruck@gmail.com>
* Timeline: key features on the constructions and bodies they read (#6183)
Builds on #3134 (keep results of features whose inputs did not change),
ported here onto current main because that PR conflicts with it:
- Construction planes, axes and points no longer go into every feature's
key. Each feature hashes only the constructions it names (and those they
stand on), as evaluated, so editing A_top recomputes its users only.
- Wider read sets: fillets and chamfers read the body their edges lie on,
extrudes and revolves that cut or join read their sketch and targets,
and sketches on construction planes or with projections read what they
project. A cached fillet's blend records come back with its change.
Co-authored-by: jijinbei <hallucigenia2002@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline: a read set covers every blend pick, patterns placed by numbers alone, construction geometry and a join's kept-apart notice (CodeRabbit review on #6295)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline: a pattern of a cut reads the bodies its source cuts, as its copies cut them (CodeRabbit review on #6295)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline: test that a coil cut from a sketch follows the sketch and its target (#6183)
A coil sweeping a sketch's profile was keyed as a tool from numbers alone in
groove. The rebased #6295 keys only coils without a sketch that way; this test
fails without that and passes with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline: a sketch's read-based key holds its stored points exactly, so a sketch that only rounds alike keeps its own solve (#6295)
A sketch is fingerprinted with its stored solve on a grid. Deleting a dimension that had moved
its points by less than the grid gave back the solve from before the dimension, and the check
before a delete in a rolled-back history, which compares solves exactly, built the history
after the marker again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Key a feature linked to a construction point on the whole model
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline: an in-place sketch edit counts the later features made again from their changes as evaluated (#6295)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline: the overlapped checks hold a feature's whole result and its change together (#6295)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: jijinbei <hallucigenia2002@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #2980
* Sketch Move: entities held to what stays move instead of bending (#2980)
Moving a rectangle drawn from the origin moved half of it: the corner's
coincidence with the origin pulled that corner back and the solve bent
the rectangle. A move now lets go of the constraints that tie the moved
geometry to geometry that stays and that the move breaks, and returns
their ids as `released`; constraints among the moved entities (sides,
a typed angle) move with them and are kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Move: the status line names the constraints the move let go, a click undoes (#2980)
The move returns each released constraint as {id, kind}; after a Move that let go of any,
the status message lists their kinds and offers Undo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Move: judge a broken constraint by its own geometry, drop "Click to undo"
Whether a constraint still holds after a move was measured against the
largest coordinate in the whole sketch, so one far-away point could hide a
broken constraint. The tolerance now comes from the points the constraint
touches, with a fixed one for the sine or cosine between two lines.
The status message no longer says "Click to undo": a click on its text does
nothing, only its Undo button undoes the move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brennen Herbruck <brennen.herbruck@gmail.com>
* claim: #13739
Co-Authored-By: Claude Code <noreply@anthropic.com>
* test: the setback scale search's QP counts are held below least first's, not pinned
thousandth_scales_and_attempt_budget pinned the search's QP count per
corner (22, 26, 21, 30, 3, 2), measured on Linux x86-64. The count
depends on which failing attempts' degree-9 programme ends unproven: such
an attempt still solves degrees 5 to 8, four programmes more. That
differs between targets and machines: on Windows x86-64 (MSVC) the second
corner solves 22, the fourth 26, and on another Windows machine the first
solves 26, so the test failed there.
What the count is for is rejecting the old order, each attempt's degrees
from the least up, where every failing attempt solved five programmes.
Those counts (35, 35, 39, 34, 2, 10) are the same on Linux and Windows.
The search must now solve fewer on the four corners where degree 9 has
room to decide: it needs two of five or six failing attempts decided by
degree 9 (one of two where every edge is held), where Linux and Windows
decide all but at most one. The corner asked 4.25 gets no bound: its
eight certifying attempts each solve one programme more, and it falls
below 34 only where degree 9 decides three of its four failing attempts.
The r 5 corner's one certifying attempt is held at 3.
Refs #13739
Co-Authored-By: Claude Code <noreply@anthropic.com>
* test: the setback scale search's QP count has a floor, its comments the exact condition
Each attempt solves at least its degree-9 programme, so the count is held
at the attempts from below; the bound above alone let any under-count pass.
A failing attempt solves five where degree 9 is not proven infeasible (an
unproven programme or an uncertified solution), not only where it ends
unproven. The corner asked 4.25 needs at least three of its four failing
attempts decided by degree 9. The step budget's comment says the long
programme below 9 is caught by it alone. The per-machine counts move to
the pull request.
Refs #13739
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
* STEP export: test that characters outside the BMP are written as \X4\ (#9089)
ISO 10303-21 defines \X2\ as UCS-2, so a character above U+FFFF belongs in
\X4\ with eight hex digits. The writer emits a UTF-16 surrogate pair in
\X2\ instead; the exact-form assertions fail until it is fixed.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* STEP export: write characters beyond the BMP as \X4\, not as surrogate pairs (fixes#9089)
`write::string` encoded every non-ASCII character as UTF-16 inside \X2\. ISO
10303-21 defines \X2\ as UCS-2, so a character above U+FFFF (an emoji, some
CJK) came out as a surrogate pair, two invalid code points to a strict
reader. It now writes \X2\ with four hex digits up to U+FFFF and \X4\ with
eight above; BMP output is unchanged.
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
The bare sm_err / null_pointer / Upstream sites in the topology files that no
open PR touches (B-rep constructor, cache and data streams, face p1, edge,
edge uses p1/p3, loop uses, vertex uses, vertex corner, shell, trimming tools
and curve loops, cutting, mirror, planar section, vertex fans, assert_valid
seams, projection, traverser, database and file wrappers: 190 sites in 25
files) return a TopologyFailure under the new Reason::Topology, each with a
phrase saying what failed. Failed, Missing, InvalidValue, Inconsistent and
OutOfMemory keep the statuses SM_ERR, SM_ERR_NULL_POINTER,
SM_ERR_INVALID_INPUT, SM_ERR_ASSERT_FAILURE and SM_ERR_OUT_OF_MEMORY;
DropFailed keeps the edge use's recorded drop status. The operation path and
entity (the vertex use for the vertex-use routines, none elsewhere) are those
of the Upstream each replaces, so sm_status(), kind(), entity(),
is_port_failure() and is_status_answer() answer as before; no cause is kept
and no PortInvariant is wrapped. A failure about an entity names it in its
message, as Reason::Failed does.
upstream_status() answers for Reason::Topology as for an Upstream. The
corner's ignored more-than-three-faces status is matched by operation and
status instead of the Upstream variant. The goldens runners of the converted
units, and the constructor outcome, read upstream_status, so rows stay as
they were. Tests that matched the Upstream variant check upstream_status.
The error messages in these files that named SMLib types are worded in
solidcore terms; geometry_write's file text is unchanged.
SolidCraft's status() passes topology failures on as their status, as it did
the Upstream, with a test.
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(ui): a sweep takes a body's edge as its path (#2216)
The Path input of the Sweep dialog takes only a sketch's curves, so a
user cannot pick an edge of the model as the path of a sweep.
* Sweep: a body's edge can be the path of a sweep (#2216)
The Path input of the sweep dialog accepted only the curves of a sketch, so a
user who picked an edge of the model as the path of the sweep got "select the
path first" and the sweep was refused. The Path input now accepts a body's
edges as well: when the picks are edges of one body, the sweep is built with
that chain of edges as its path, each edge followed by the curve it is, and
the path starting at the end nearest the profile. The command takes the path
as path_body (the body) and path_edges (a point on each edge of the chain); a
path of sketch curves is unchanged. A curve of the profile's own sketch is
never a valid path, so the pick no longer stops on one and falls through to
the edge under the cursor.
* Sweep: a body's edge path is restored on its own body (#2216)
* test(engine): a closed body-edge path starts at the profile (#2216)
* Sweep: a closed body-edge path starts at the profile (#2216)
* style(test): format the closed path sweep test (#2216)
* Sweep: the contract knows the path of a body's edges (#2216)
Task: https://github.com/tangency-labs/solidcraft/issues/2216
* Sweep: format the contract's path of a body's edges (#2216)
Task: https://github.com/tangency-labs/solidcraft/issues/2216
---------
Co-authored-by: agent <agent@localhost>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: vdirienzo <vdirienzo@users.noreply.github.com>
Co-authored-by: Hermes Agent <hermes@nousresearch.com>
The setback corner's G1 fill (and its certified scale search, up to 12
fills) spent about 70% of its time in affine_fundamentals: every product
of every unknown's share with the same four cofactors re-scaled the
cofactor and evaluated two binomials per coefficient of each factor and
of the product, and the minors of H, Hu, Hv were recomputed for o, l, m
and n.
- t_mul_fast reads its binomials from a per-call table built by the same
binomial function; affine_fundamentals and fundamentals share one table,
scale each cofactor and each unknown's share and partials once, and take
the recurring minors once (det4_minors, t_det4_along).
- The dual QP's violation scans read each row as one slice.
The same operations on the same values in the same order: results are
bit-identical. Tests compare the products, affine_fundamentals and
fundamentals bit for bit against the previous per-coefficient code on a
rational patch, and count the binomial evaluations (406 against 219140).
Part of #14460.
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: claim incremental series evaluation in the feet Taylor models
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: G^n feet Taylor models evaluate each series coefficient once
gn_enclose_tm solves the feet's series order by order and evaluated G at
every order from scratch, recomputing every lower order's coefficients.
GnIncremental keeps every series gn_evaluate_vn takes and computes only
the coefficients whose quadrant of x changed (Trunc::skipping), with the
same operations in the same order: bit for bit the whole evaluation's
(tested in f64, intervals and Taylor models). gn_evaluate_vn is unchanged
for its other callers. WorkCounters::feet_tm_coefficients counts the
coefficients computed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
solidcore's Setback refuses a setback at which its fill is not certified convex (or, beside a curved face, no more concave than its faces), and it knows the least setback that is certified. It said so only in the refusal's comment. BlendRefusal::SetbackTooShort now carries a `why`, mirroring SetbackTooLong's: RailsCross (the existing refusal, a setback short of where the rails cross), ConvexFill and ConcaveFill. The two fill refusals are typed with the least setback where it lies within the corner's rooms. Where none fits they stay untyped, since there is no size to name. Comments, codes and fit_setback are unchanged.
SolidCraft maps the two fill reasons to smlib's new Error::SetbackTooShort { least, at, builds }:
- Where solidcore's fill is the corner (fillet_setback), the message names the smallest setback that builds, in the wording the fillet dialogs read (SETBACK_AT_LEAST).
- Where SolidCraft's G2 patch replaces that fill (fillet_g2_corners), it names only what solidcore's corner needs ("needs a setback of at least"). At that setback the patch is not shown to fit (tangency-labs/solidcraft#14427).
- RailsCross is not mapped: its setback passes that one test only.
Tests:
- solidcore: the 40 x 30 x 20 box rounded r3 at (40, 30, 20), set back 3.5, is refused with SetbackTooShort { ConvexFill, 3.5595 }, and set back 3.5595 it builds. The rails-cross refusal asserts RailsCross.
- SolidCraft: the plain path's 3.5 is refused naming the smallest that builds, 3.5595 mm, and that one builds a valid body. The G2 path names only the lower bound. The kernel case every-box-corner-three-edges-r3-curvature-setback-3-5 expects that text.
- fillet_setback_tests::a_setback_not_past_the_radius_is_refused_naming_the_least now passes, so its known-failing line goes.
Fixestangency-labs/solidcraft#13913.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Convert Mesh: a second placement's body converts under the name the world shows
mesh.convert looked the selected or given body up in the model's own namespace, so the body of a component's second placement, named "Mesh (Scan:2)" by the selection, the Browser and inspect.measure, answered "no body". The name is now mapped through own_body_name first, as appearance.assign maps it (#9740): the conversion edits the component's body and every placement shows the solid.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Browser: the card's header counts one body per placement, as its rows do
The header counted the model's bodies while the card lists a body row under each placement of its component, so one body placed twice showed two rows under "1 body". The count now comes from the world state, one per placement; when the design cannot be placed it counts the model's bodies as before.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Viewport: a feature's changed-faces highlight lights every placement of its component
When a later Offset Face, Replace Face or Delete Face renamed a feature's faces, the highlight found the faces it changed by comparing the states either side of it, then looked the body up by its definition name in the placed world and matched a face in the body's own coordinates against the placed body's faces by plane. A second placement of the component was never lit, and the plane match mixed the body's own coordinates with the placed body's. The match now runs in the body's own coordinates and the face is lit by its name on every copy the viewport shows, since placed copies share the body's face names; visibility is checked per copy.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
In a design that is not in millimetres, OK sent a typed Corner, Base or
Center through with_design_unit, which adds the unit only to a plain
number, so a decimal comma such as 1,5 was read as millimetres while the
size fields read it in the design's unit. The placement marker read the
same fields with doc.eval, so a unitless expression such as 2 * 1 put the
marker in millimetres although OK made the solid in the design's unit.
OK now writes those fields with length_input::expression and the marker
reads them with length_input::eval, as the size fields do, so the marker
stands where OK makes the solid.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6061
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PoD5bGb8KWxkNrvkJujMvE
* Assemblies: assembly.constraint.create places a part by a rule (Mate, Flush, Distance and more) (#6061)
A new command places one part against another by a geometric rule between
a reference on each: mate, flush, distance, concentric, insert, parallel,
perpendicular, angle, tangent or point on axis. Each rule is kept as a joint
of the kind it needs (a mate and a flush are planar, concentric cylindrical,
insert revolute, tangent line on plane...), tagged with the rule it was made
as (`Joint::constraint`, one pending format file). So the one joint solver
places, counts and checks it with the other joints: several rules between
one pair add up, and the part keeps every motion they leave free.
A constraint starts where its parts are, as a relation does, so the part
moves no further than the rule needs; joint.edit restarts it the same way.
A tangent's offset is its gap plus its round face's radius as the face is
now, so the part keeps resting on the plane when the radius changes. A rule
given the wrong kind of reference is refused with what it needs. The command
has a typed contract and reply; joint.list and joint.inspect name the rule
as `constraint`. joint.create's handler is split so both commands build a
joint the same way (`new_joint`, then `add_joint`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PoD5bGb8KWxkNrvkJujMvE
* Assemblies: a constraint's default name and a type change on joint.edit
A constraint given a blank or null name is named after its rule (`Mate1`),
as one given no name is, not after its joint kind.
joint.edit to another kind of joint makes a constraint a plain joint: the
tag goes, so it no longer restarts where its parts are or reads as its old
rule in joint.list, and a former tangent's offset stops following its round
face's radius.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PoD5bGb8KWxkNrvkJujMvE
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: a face on a spline closed in v gets a seam; spline surfaces written unclamped are clamped
A face winding round a B-spline surface closed in v (its loops turning
in v, no seam edge) was refused, and the part read as an open shell.
Kind::Iso now carries the direction the spline is closed in, its seam
line the boundary row at the first v knot for one closed in v, and
Kind::winds_in() names the parameter a face's loops wind in (v round a
torus's tube and a spline closed in v), which band_seam, seam_start
and the seam's interval read. A face covering a pole at an end of u
lies on the lower-u side of a loop running +v, so that loop's pole is
the one at u0.
Spline surfaces written with unclamped (periodic, uniform) knots are
clamped exactly on reading, row by row and column by column, with the
knot insertion curves already used (now the shared clamped()), so a
periodic surface's first and last lines are one and it is found
closed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: review: boundary line in the closed-spline refusal, start for u0, one clamping helper for rows and columns
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: clamped_lines collects without pre-reserving from the input's count (panic-site check)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The bare `sm_err`/`null_pointer`/`Upstream` sites in `query/` (edge and face
queries, point classification, contact and distance bounds, continuity,
normals, the face-surface intersection, the poly B-rep, its copy and
properties, tessellation, B-rep rays) become a new `QueryFailure` under
`Reason::Query` (`error/query.rs`): `Failed(phrase)` (SM_ERR),
`Missing(phrase)` (SM_ERR_NULL_POINTER) or `InvalidValue(phrase)`
(SM_ERR_INVALID_INPUT, the zero-vector statuses of `SER(Unitize)` and an
undefined poly scale). Behaviour-neutral: `sm_status`, `kind`, `operation`,
`entity`, `is_port_failure` and `is_status_answer` are those of the
`Upstream` each replaced, and `upstream_status()` covers the new reason. No
`PortInvariant` is wrapped as a cause.
Held bare: `query/api_queries.rs` and `query/brep.rs` (open PRs on them),
and the point classification's seam failure in `query/face/p2.rs`, which
names its face (blend's `without_entity` strips the entity of `Upstream`
only). A ratchet test pins these counts and reads files with CRLF
normalised before the test-module cut.
The query goldens runners read `upstream_status`; SolidCraft's `status()`
passes query failures on as their status, with a test.
Part of #13790.
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Cones and cylinders: end circles from the control points, and copies proved on their form (#13536)
The cone and cylinder recognisers fitted each end circle of a NURBS through
three samples and took the axis between the two centres. On the battery
case's r 40 corner (a 0.156 rad arc extruded 94 mm), the two fits differ by
1.35e-11 mm, so the axis tilted 1.44e-13 rad and the cylinder was off its own
form. The local reach found no exact class for the wall, and CaseShell was
refused.
The circles are now made from the iso curves' control points (The NURBS
Book, sections 7.3 and 7.5). The centre is the start point plus r along the
inward normal, with r = |chord| / (2 sin a). The circle passes through the
start point exactly, and rows that are translates give translated circles.
CopyAndAddAnalytics keeps a recognised plane, cylinder, cone, sphere or torus
only when the form lies on it at bound 0.
The three pleat tests pass again and their known-failing lines go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cones: the arc's radius guard reads as clippy asks (#13536)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cones: an arc's legs are made unit before their cross product; the copy's proof is left for later (#13536)
A 0.001 degree arc of radius 1 has legs 8.7e-6 long. Their raw cross product
(1.3e-15) fell under the unit vector's absolute floor, so the arc was not
recognised (issue1433_cyl_tiny_angle).
CopyAndAddAnalytics no longer proves a recognised label on its form: other
recognisers still return true cones a few 1e-11 off their forms, which the
proof would drop. It comes back once they are exact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cones: an arc's legs are made unit before their cross product (#13536)
A 0.001 degree arc of radius 1 has legs 8.7e-6 long. Their raw cross product
(1.3e-15) fell under the unit vector's absolute floor, so the arc was not
recognised (issue1433_cyl_tiny_angle). Micrometre cones whose sampled circles
failed before are recognised now too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cones: the circle checks allow the angle the points' rounding turns a circle by (#13536)
Control points with coordinates up to s are known to about 8 eps s. Over a
circle of radius r, that turns the circle's normal by up to 8 eps s / r. A
tolerance scaled below that decided on the rounding of the input, not on its
shape. A cylinder of r 2.94 reaching 1.2e6 along its axis, recognised at a
thousandth of the default tolerance, was refused (goldens gen0196). The
recogniser and the rebuild now take at least that angle.
(The previous commit's message is wrong: it holds this change's first draft,
not the unit-leg fix, which is in 224d8a44dd.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cones: an arc's radius is its leg over tan of the half sweep (#13536)
|chord| / (2 sin a) gave 5.000000000000001 for a quarter arc of r 5 (a
revolved line along its axis, face_surface's test). |leg| cos a / sin a,
both from the leg's and the chord's directions, gives 5 exactly there and
keeps the case wall's short arc on its form.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Goldens: bless the units the exact cone and cylinder circles move (#13536)
Every case moved here was compared, old against new, with the exact
geometry where there is one:
- Rounding or renumbering (most of it): blend.feature_executive,
blend.fillet_executive_p3 (but for the cases below), blend.fillet_removal,
blend.fillet_standard_solver_p2, boolean.api_brep, primitive.api_trim_ops,
query.brep, query.face_p1, query.face_p3, sweep.topology_sweep,
topology.api_files, topology.assert_valid, topology.brep_cutting,
topology.brep_p4, topology.edge, topology.edgeuse_p1, topology.edgeuse_p3,
topology.face_p3, topology.face_p4, topology.vertexuse. Counts, statuses
and type codes are the same, sorted values agree to 3e-11 relative, and
gaps stay at or below 3e-13. Written files differ only in doubles that
moved by at most 3.9e-14 relative.
- surface.cone_p2: 22 true cylinders and cones are now recognised (tight
tolerances, micrometre sizes, rigid moves, row-weight scalings), and a
cone frustum's rebuild (gen0230) succeeds. adv_reb_cyl_s0i1_id_neg is now
refused: a row with negative weights runs through infinity, so it is not a
cylinder.
- sweep.trans_sweep_geometry gen0411: a circle of r 1.5e-6 swept straight
is now its cylinder.
- offset.offset_executive_p2
review_box_smallest_buildable_translated_source_selected: the shell moved
by 1e6 now builds like the untranslated one (it was 3 regions and 8 faces).
- blend.hcr global_visibility_cylinder_z / cone_z: the underside arcs are
now hidden, as the symmetry needs.
- topology.api_projection cylinder and cone: a duplicate projected curve
is gone.
- Lost exact edges, not wrong shapes: in offset_executive_p2 gen0121 (and
the box shells api_shell_box_top_round_out and
review_box_smallest_buildable_source_selected), 8 straight edges and one
circular edge are now B-spline curves. They are still straight, to 1e-15.
They came out exact on main only because the tilted cylinders sent the
shell through the stitch's glue. In blend.fillet_executive_p3
source-file-box_on_box-all-edges, one circle edge is now a B-spline; it
has not been traced, and #14666 lists it as the same class. #14666
tracks both.
- Invalid before and after: offset_executive_p2
defeature_extrusion_face0_outset2_5_rounded and
extrude_holes1_face0_outset24_7_rounded (and the overlap_pc_extrudeHoles1
outsets) fail integrity::check ("cached face shell: face 1 not found") on
main and here; the corner sphere faces split differently. #14676.
- Within tolerance: fillet_executive_p3 source-file-box_diff_cyl-all-edges
and its curve-sih and overhang-union kin. Converged to 1e-8, the volume
is 950.6480958 on main and 950.6480122 here (8.8e-8 relative), and the
area moves 5e-7 relative. This is far below the fillet's tolerance and
follows from the cylinder's axis now being exact. It is not yet shown
which value is closer to exact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cones: an arc's circle comes from a well-conditioned segment, and every segment is checked on it (#13536)
A box round offset for the local operation's merge carries 1e-5 long
extension segments at both ends of its arc. Each one fixed a centre of its
own only to the rounding of its points over its 2e-5 rad sweep (5e-10),
past the recogniser's 1e-11 tolerance, so the offset round was no cylinder
(wr669 a_box_round_offsets_to_the_round_of_r_plus_d_on_the_offset_faces).
The circle now comes from the first segment at least half as wide as the
widest. Every segment is then checked as an arc of it: end points on the
circle, middle point r / cos a from the centre, plane parallel, and end to
end.
fork_eb883ad_keep_features: the kept cylinder hole, cone pit and astride
pocket at r 8 move by their last bit; the r 1.9 cylinder boss and hole move
by 1.1e-11 (5e-16 relative). They are re-pinned, with the note. The rig's
known failure report-peg-front-and-concave-chamfers-together-d2 records
its failing volume one ulp lower.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Goldens: bless the two units the well-conditioned circle segment moves (#13536)
- topology.api_files: 30 written-file digests move. These are the files of
cylinders, cones and extrusions whose frames now come from the base
segment's tangent; only the written doubles changed, as in the earlier
bless.
- blend.fillet_executive_p3 real-box-fillet12-r3-shell-open-top-issue-460:
the shell's volume moves by 1.3e-8 relative and its area by 4e-8,
within the integrator's 1e-4.
All 283 units pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(solidcore-ops): a dome 1e-8 off its circle joins a cylinder and its mirror (#13713)
A dome revolved from an arc drawn through a point rounded to eight decimals
(radius 10 + 1.01e-8, base 1.01e-8 above y = 0) joined flat to a cylinder of
radius 10, and to its own mirror about y = 0. Both unions give the closed-form
volume but fail face test 36 ("Face Near missing Seam") on main: the seams end
1.43e-8 and 2.03e-8 apart at the shared vertex, radially and along the seam,
not across it.
Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit 323b3f0931)
* fix(intersect): a vertex is a near miss of a seam only across it (#13713)
HasNearMisses counted a vertex end's whole 3D gap to the classified curve.
For a seam that includes the parts along the seam and off the surface, which
the vertex's tolerance bounds and which leave no sliver between the seam and
an edge. Two operands whose seams end 1e-8 apart along the seam (a dome whose
arc is 1e-8 off its circle, joined to a cylinder or to its mirror) were
refused with face test 36 although no construction can put one vertex on both
seams.
A vertex end whose gap reaches 1000 * EFF_ZERO is now measured again across
the curve within surface 1: the component along n x C'(t). The threshold is
unchanged, and so are coincident edges: a seam that misses an edge across the
surface (#13802's rounded slant plane) still reads as a near miss.
Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit c76f1a1bbb)
* plan(findings): the seam near miss at a vertex two seams share (#13713)
The cause with its measured gaps, a VectorCraft schematic of the gap at the
shared vertex split off the surface, along and across the seam, and why only
the part across the seam leaves a sliver.
Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit 79b205bc4e)
* fix(intersect): the seam near-miss refinement never fails, and is tested on a curved seam (#13713)
has_near_misses could not fail on main with in_surface_gaps false. The
refinement now resolves surface 1 without an error, and a failing curve, UV
curve, normal or vertex evaluation means the direction is not available: the
whole gap counts, as on main. Only Unported and cancellation propagate.
A unit test classifies a quarter circle on a plane ending at a vertex 1e-8
off its end: across the circle in the plane it is a near miss, along the
circle or off the plane it is not, and without a resolvable UV curve or
surface the whole gap counts. The circle's turn makes the end parameter and
the tangent's side matter. The two joins now also check their face, edge and
vertex counts and their volumes to 1e-7.
Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit 2e62ba0f8d)
* plan(findings): where the 1e-8 comes from, and what the change leaves as it was (#13713)
The gap is in the operands SolidCraft hands the kernel, not made by the
boolean. With the change on top of the slant-plane construction fix, its 16
perturbation rows report what they report without it, and the tube-end case
pinning face test 36 passes unchanged. Coincident edges keep upstream's gap.
Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit aa9b6b00c8)
* goldens: bless heal.face_props and topology.assert_valid for the seam near-miss change (#13713)
Only near-miss findings at vertex ends drop, where the vertex is off the
surface or along the seam, not across it. Nothing else in either unit moves.
topology.assert_valid, 7 cases (sphere_mutation6_v0.1_i0/_i3,
cylinder_mutation6_v0.1_i0/_i3, update_cylinder_m6_i0_v0.0001_l2,
update_cylinder_m6_i1_v1e-06_l2, gen0002): mutation 6 moves a vertex +x,
radially at a cylinder's seam vertex and along the seam at a sphere's pole.
In each case the one report that goes is SmFace test 36; every other report
stays (the vertex gap reports still flag the 0.1 and 1e-4 moves).
update_cylinder_m6_i1_v1e-06_l2, a vertex 1e-6 off with tolerance 1e-5, is
now valid.
heal.face_props: per face only bad_near_miss_seam and its direction change,
True to False with U, V or Both to Neither, or Both to U or V. Crossed and
missing seams, loops, poles and every other property are unchanged, and the
heal units that act on these properties do not move.
Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit df0697145b)
* plan(findings): every golden the seam near-miss change moves, old and new (#13713)
Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit 4563146311)
* Tests: boolean.rs keeps main's coil-cut cancel test and the dome seam tests side by side (#13536)
The merge's conflict markers split a test in two; this restores the file as
the dome seam tests followed by main's new test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: the rim round builds without the torus fix too, and rim_cut passes (#13536)
issue_417: with the corner rounds' circles made from their control points,
the top rim's round also builds with fillet_torus_center_on_axis off, at the
closed-form volume (to 1.1e-9); the test pinned that it failed.
solidcraft-engine rim_cut_after_the_round_on_the_other_end passes now; its
known-failing line goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cylinders only: the cylinder recogniser makes its circles from control points; the cone recogniser and rebuild keep IsArc (#13536)
The battery case's wall comes in through SolidCraft's import as a cylinder
(Cone::is_nurb_surface_cylinder). Giving that recogniser exact end circles
is what the pleats need. Doing the same in the cone recogniser and in the
cone rebuild put the chamfer revolutions of issue_420 1.8e-12 rad off
their edges' circles, which the curve recogniser still fits through
samples, and moved many goldens. So that part is left for a follow-up.
- surface/cone/p2.rs is main's, plus iso_arc_exact (the control-point
circle: built from a well-conditioned segment, every segment checked on
it) used only by is_nurb_surface_cylinder, and its regression test on the
case wall's record form.
- The goldens, the fork_eb883ad pins, issue_417 and the rig's known
failure go back to main's values. topology.assert_valid moves in 32
cases, all at rounding (gaps at or below 3e-13), and is blessed with
#14581's cases.
Pleats (pleat_join, both pleat_regressions), rim_cut, issue_420,
issue_417, wr669, fork_eb883ad, the kernel rig, the hair-thin hole and all
283 goldens units pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Markus Reichart <52602624+MarkusReichart@users.noreply.github.com>
* claim: #11340
* Construct: Plane, Axis and Point tools that choose the method from what you pick
* Construct: list Plane, Axis and Point in the kernel-cases EXEMPT list, tested in construct_tests.rs
* Construct: test that the old Construct layout comes back with the toolbar preference, follow the new buttons in the narrow-toolbar scenario, document the touched functions
* Construct: the Plane window test also takes the second plane out and checks Offset Plane is chosen again
* Construct: Plane, Axis and Point lead their entity's heading in the Construct menu, and plane_auto joins the requested-icon manifest
* solidcore-ops: cancel ops calls with a scoped token (solidcore#3247)
solidcore_ops::cancel::within runs work under a CancelToken. Every ops call
it makes on that thread runs with the token on the session's kernel, whose
long loops already poll it; once cancelled, the running call stops at the
kernel's next check and later calls are refused before they wait for the
kernel, each with Error::Cancelled.
A cancelled call leaves no partial state: an edited body is put back by the
session's rollback, no result is returned even if the kernel finished
first, the kernel is replaced as after a panic, and nothing reaches the
failure callback. Nested scopes stop with either token
(CancelToken::either in the kernel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cancel stops SMLib's full check and the tight box (solidcore#3247)
The full validity check of a feature's result was most of a long boolean's
time (64% of #3962's 20-turn coil cut) and ran with no token: Cancel left it
running for seconds and it returned a result. solidcore now polls the token
once per entity in the check's topology walk, in the coincident-topology
search and in the tight bounding box.
SolidCraft runs the check and the box under the work's token where the body
is the call's own: the step result or copy a deferred feature check takes
(which now carries the token of the work that detached it onto its thread),
and a copy measured for the tight box. A call on a body others read still
runs to its end. A stopped smlib call returns the typed Error::Cancelled and
never a result solidcore finished first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cancel on a computing feature stops it and applies nothing (solidcore#3247)
Session::execute answers EngineError::Cancelled whenever the work's watcher
cancelled during the command (doc::progress::cancelled), even if the kernel
finished first, and puts the design back as on any other error. With the
kernel now stopping at the token's next check, Cancel or Esc on a computing
OK, a changed input during a preview, closing the design and an agent's Stop
all end the computation within milliseconds, and the evaluation never caches
a result computed after the cancel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cancel: keep the tight box on the body itself; guard check_owned
The tight box measured on a copy under a token is dropped: a copy starts
with empty caches, and the box is read by later features, so it stays on
the body as before (still uncancellable in the app; the kernel polls it
for ops callers). Brep::check_owned joins the entry-point guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Opening a design keeps its own Cancel
doc.open, new and recover replace the session; under a cancelled watch they
skip the features not yet built and show what is built (#10577), so
execute answers Cancelled only for commands that keep the session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* issue_2371 cancel test: the section check point is asserted inside the sections
On a slower machine 4 s into the boss-root round is past its sections, in
the glue that follows (glue_edges_general), where the global solver's own
check ends the round as promptly. The test now times the round uncancelled
and asserts the fillet_section check point only for a cancel in the first
three quarters of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge origin/main into cancel-wire: progress::cancelled now comes from #3754
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch text: place the block of lines by its top, middle, baseline or bottom
Sketch text could only be placed by the first line's baseline. Text now has a
vertical alignment: baseline (the default, as before), top (the capitals' tops),
middle (halfway between those and the last line's baseline) or bottom (the
bottom of the last line's descenders). It works on a path too.
`sketch.text` and `sketch.edit_text` take `vertical_align`. The Edit Text
dialog shows both alignments as symbol buttons: paragraph lines for left,
centre and right, and "Ag" with a line at the anchor for the vertical ones.
`ui_kit::Segments` gets `glyphs` to draw a symbol in place of a label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch text: use the shared ui-text crate and format the alignment row
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch text: theme tokens and the semibold face from where main keeps them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9841
* ui: dialog buttons answer their first letter (#9841)
ui_kit::Confirm answers the first letter of each visible button's
translated label when the new dialog_first_letter_buttons preference is
on (Preferences > General > Dialogs). The preference defaults off, so
typing keeps its old behaviour of doing nothing to the buttons; the
kit's accelerator is off until the host turns it on from the preference.
On: D=Don't Save, C=Cancel, S=Save in English, N/A/S in German. Matching
is on the typed glyph (Event::Text), so it is layout-independent, and on
the label after translation, so it follows the interface language.
Ambiguous first letters do nothing; a focused text field inside the
dialog keeps the keystroke; Enter and Escape keep their existing
meanings; the matched keystroke is consumed so it cannot reach the app
behind the dialog.
Written by Qwen.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
The trim search of SmFace::CalculateTightBoundingBox (WR-588) gave every
cell's directed solve the face's whole surface. A solve's control-net
guesses read every control point of the surface it is given, so a G2
corner patch (a 129 x 131 rational bicubic net) read all 16,899 points
in each of its thousands of cells along the trim: 3-4 s a face, 46 s
for the tight box of a box with eight such corners.
Each cell of a B-spline face open both ways is now solved on its exact
subpatch over the cell, with the face's owner and zone tolerance, and a
cell whose subpatch's control points reach no higher than the box so
far is skipped without a solve (the convex hull bounds the cell). A
cell whose subpatch gives no solution is divided like an undecided one
and, at the depth or cell limit, counts with that hull bound.
The G2-cornered box's tight box drops from 25-30 s to 4.5 s in
solidcore and agrees with the old one to 1e-20. A new work counter,
face_extreme_control_points, counts the control points the solves read;
the regression test's trimmed 40 x 40 sheet reads 40,562 now against
1,433,600 before.
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The view's DXF is centred on the camera target, so its 0,0 sits in the air.
The new optional zero parameter (target by default, or origin) shifts the
drawing so the model origin is 0,0, for machining from a part corner (#14686).
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Curve::length_bound returns a Bracket [chord, chord/cos φ] over Bernstein
pieces of the hodograph, halved until as narrow as asked or refused, named
(CurveFailure::LengthBelowFloor / LengthPastPieceCap). The setback checks
(overlapping_setbacks, set_backs, gather_ends) take the bracket's lower side,
asked as wide as the corner's tolerance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: claim removal of SolidError::Nlib (part of #13790)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: remove SolidError::Nlib; crv_common reports typed NURBS failures
The last five SolidError::Nlib sites, in nurbs/crv_common.rs, now return
NurbsFailure::Input / NurbsFailure::Numerical through SolidError::nurbs, the
same kind (Failed), operation, comparison status (SM_ERR) and NL number (14,
11) as before. With no constructor left, SolidError::Nlib, SolidError::nlib
and NlibCode are removed, along with their arms in sm_status, kind,
operation, entity, is_nurbs_failure and the goldens hook nlib_number.
The NURBS ratchet now scans every library source of the crate (CRLF
normalised) for the variant, the builder and NlibCode, and crv_common pins
the status, kind and operation of its reachable NL_INP_ERR site.
Part of #13790.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The test's wall time was one design, oracle/77-fillet-variable-linear,
built twice back to back on one worker. Each design's two builds are now
two jobs, and a short measured list of the heaviest designs (kept next to
the test, checked against the corpus) is handed out first, so the two
builds of design 77 run at the same time on two workers while the rest of
the corpus runs beside them. What the test asserts is unchanged.
tests/action_matrix/costs.json is left alone: the action matrix shards by
it, and its load column is stale.
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: clippy-clean under Rust 1.97 (#14547)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Clippy-clean under Rust 1.97: Option::zip and range contains, behaviour kept (#14547)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Evaluating the flake printed "evaluation warning: stdenv.isLinux is
deprecated, use stdenv.hostPlatform.isLinux instead", once for each system
that was evaluated. The flake now reads stdenv.hostPlatform.isLinux in the
eight places that used stdenv.isLinux. Which systems count as Linux does
not change.
Cutting a body by an exact copy of itself failed (SM_ERR) or left
faces. Four kernel defects caused it; each is fixed behind a new
opt-in UpstreamFixes flag, turned on in SolidCraft's fix set and the
rig's fixes.json, with a regression test that fails without it:
- boolean_capture_keeps_operand_vertices_apart: IntersectInsertRelate's
capture distance, max(0.01, 2000 x intersection tolerance), stays
below half the smallest distance between two vertices of either
operand, so no proto vertex captures two distinct vertices of one
operand (with a STEP file's 0.01 tolerances it was 10 mm).
- radial_sector_step_off_coincidence_by_own_face: when stepping off
leaves a face, RadialSectorClassify reads a coincident face's side
against the face it lies on, as its coincidence test does, not
against the radial partner's.
- split_edge_definition_owns_its_curves: SmEdgeDefinition::Split gives
the new definition its own curves and trims both before
AddEdgeDefinition may reparameterize it.
- simple_coincidence_side_across_the_curve: SimpleCoincidenceChecker
takes the side across the curve, so coincident curves of slightly
different length are not taken for crossing ones.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A sketch, a construction plane or axis, and every feature that takes a plane
or an axis, can now pick the origin geometry of a placed component in the
Browser and the viewport: its O point, the X, Y and Z axes and the XY, XZ and
YZ planes. The reference names the exact placement by its complete occurrence
path and is found again on every evaluation, so it follows the component when
it moves, the way a face attachment re-finds its face. The shared core is the
one DatumRef model the joint sides use (#10056): `PlaneRef::Datum`,
`GeoRef::Datum`, `Document::placement_frame` and the placement digest's datum
paths are already `main`'s, and this change only wires them into the app's
resolvers, the Browser and the viewport.
- The Browser gives every placement its own Origin folder (`o:<ids>/origin`),
hidden by default and shown per placement
(`Visibility::shown_component_origins`), drawn like the design Origin's
quarter squares; the design Origin keeps its own controls. A browser row
can carry a handle, so a repeated placement's rows keep their automation
identity next to the design Origin's labels.
- The resolvers take a datum key at every site that takes a name: the
engine's construct and feature parameters, the dialog chips, the viewport
previews and Look At, and the visibility command. The one order everywhere:
a construction feature by name first, then the datum key, then the origin
aliases.
- A copy within one design rewrites datum paths to the copy's own walk; a
paste from another design refuses them, since the reference could not
follow.
- A hidden placement hides its Origin folder and its items too, and a
placement that is gone prunes its shown folder; undo brings both back.
Changes a default: no
Design-format change: `component-origin-visibility` (pending; the
`component-origin-datums` format change landed with #10056). New message
`dialog.component_origin` ships every complete catalog.
Tests: occurrence tests (picking, resolution, visibility, pruning of gone
placements), clone tests (path rewriting and refusal), frames tests (a datum
key is not frozen into a resolved plane), construct tests, schema tests and
UI scenarios in `occurrence_ui_tests.rs` (a sketch on a component origin
plane follows the component, the XZ square spans the placement's own Z, the
Origin folder stays hidden while a sketch is open).
When a new worker took over, it retired the old build's copy and swept before it claimed the pages the old worker had controlled. A page the old worker controlled has no controller in that gap, and a request it makes then goes straight to the network, which serves the newest build (the open v3 page was given v4's shared.js although its record named v3's copy, in run 42883099627). The worker now claims first. The offline test waited for the retire mark, which is written before the claim; it now also waits until the new worker is active and controls the page.
Changes a default: no
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-ops: cancel ops calls with a scoped token (solidcore#3247)
solidcore_ops::cancel::within runs work under a CancelToken. Every ops call
it makes on that thread runs with the token on the session's kernel, whose
long loops already poll it; once cancelled, the running call stops at the
kernel's next check and later calls are refused before they wait for the
kernel, each with Error::Cancelled.
A cancelled call leaves no partial state: an edited body is put back by the
session's rollback, no result is returned even if the kernel finished
first, the kernel is replaced as after a panic, and nothing reaches the
failure callback. Nested scopes stop with either token
(CancelToken::either in the kernel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cancel stops SMLib's full check and the tight box (solidcore#3247)
The full validity check of a feature's result was most of a long boolean's
time (64% of #3962's 20-turn coil cut) and ran with no token: Cancel left it
running for seconds and it returned a result. solidcore now polls the token
once per entity in the check's topology walk, in the coincident-topology
search and in the tight bounding box.
SolidCraft runs the check and the box under the work's token where the body
is the call's own: the step result or copy a deferred feature check takes
(which now carries the token of the work that detached it onto its thread),
and a copy measured for the tight box. A call on a body others read still
runs to its end. A stopped smlib call returns the typed Error::Cancelled and
never a result solidcore finished first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cancel: keep the tight box on the body itself; guard check_owned
The tight box measured on a copy under a token is dropped: a copy starts
with empty caches, and the box is read by later features, so it stays on
the body as before (still uncancellable in the app; the kernel polls it
for ops callers). Brep::check_owned joins the entry-point guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* issue_2371 cancel test: the section check point is asserted inside the sections
On a slower machine 4 s into the boss-root round is past its sections, in
the glue that follows (glue_edges_general), where the global solver's own
check ends the round as promptly. The test now times the round uncancelled
and asserts the fillet_section check point only for a cancel in the first
three quarters of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Kernel: keep a failed solidcore-ops conversion in the body's cache
ops_body kept only a conversion that worked, so a record solidcore-ops
refuses was converted again for every spline face planar_face asked
about. The cache now keeps the failure too, as smlib's refusal is: the
conversion runs unstoppable, so a stopped job cannot make it fail, and
the same record fails the same way again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
* Kernel: a refusal of the moment is not kept in the solidcore-ops cache
ops_body filled its OnceLock from inside get_or_init, so a solidcore-ops
failure callback that asked about the same body again would reenter the
cell while it was being filled, and a call refused because it came from
inside a kernel call or the callback (Error::Reentered), or a cancelled
one, was kept as a refusal of the record. The conversion now runs
outside the cell; Reentered and Cancelled are not kept, so a later query
tries again. A refusal of the record is still kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14128
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Extrude: started from an open sketch, its profiles can be picked even when the sketch is already used (#14128)
Extrude and Revolve finish the open sketch before their window opens. A
sketch that an earlier feature uses is hidden once it is finished, so a
rectangle drawn after reopening it could not be picked: the window showed
no profiles and a click inside the rectangle picked nothing.
The dialog now records the sketch it ended on its way, and
sync_feature_edit keeps that sketch shown while the dialog is open, the
way an edited feature's sketch is shown. Closing the dialog hides it again.
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
The 98 bare `sm_err`/`null_pointer` sites in `sweep/` (topology sweep, its
translational and rotational geometry, screw, cornered, segments, the sweep
API's surfaces) become `SweepFailure::Failed(phrase)` (status SM_ERR) or
`SweepFailure::Missing(phrase)` (SM_ERR_NULL_POINTER), extending the existing
`Reason::Sweep`. Behaviour-neutral: `sm_status`, `kind`, `operation`, `entity`,
`is_port_failure` and `is_status_answer` are those of the `Upstream` each
replaced, and `upstream_status()` covers the new variants
(`SweepFailure::replaces_upstream_status`). `RailPastStationCap` reports as
before (SM_ERR, not_within_tolerance, no upstream status).
A ratchet test keeps bare statuses out of `sweep/`. The trans/rotational
goldens runners read `upstream_status`; SolidCraft's `status()` passes the new
variants on as their status, with a test.
Part of #13790.
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
solidcore-step, SolidCraft's STEP reader (moved in by #13979), joins CRATES in check_panic_sites.py. (The scan's clippy run already copes with solidcore-record, a dependency of step's outside CRATES, through main's `--cap-lints warn` from #14194.)
solidcore-step's 75 sites are listed in plan/panic-sites.tsv, all proven, each with its reason. Fixed first:
- eval.rs `expanded`: the knots are built only for more control points than the degree, with multiplicities adding up to points + degree + 1, checked before anything is built. A file can no longer have the knot vector grow to a size it declares: the knots are at most twice the points, which are already in memory.
- read.rs `implied_knots`: a degree of 0 is refused before the Bezier segments divide by it.
- topology.rs `face_domain`: a natural range that leaves no room for the box (crossed, or not a number) is refused, naming it, before the clamp. A range narrower than the box's span takes its own start exactly, where `n1 - span` could round below `n0` and panic the clamp.
- write/topology.rs `outer_loop`: the ring grows as it is filled, rather than reserving edges x samples.
Main's other rows are left as they are. The scan notices 13 that no longer match a site (main's own drift), and that doesn't fail the check.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Project: mesh outline tolerances follow single precision and the outline's size (#13464)
A corner part way along a side is dropped when it lies within single
precision of the line through its neighbours (1e-6 of the coordinates'
size), measured as its distance from that line, so a rounded corner at
the loop's start no longer splits a side in two. The circle check for a
mesh outline is never looser than 1e-4 of the outline's own size, so a
small outline far from the origin is not taken for a circle. From
CodeRabbit's second review on #13482.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Project: a small mesh face far from the origin keeps its corners
A corner part way along a side of a mesh face's outline is dropped when
it lies within single precision of the line through its neighbours,
1e-6 of the coordinates' size. Far from the origin that grew past the
outline itself: a 1 x 1 square at x = 1 000 000 lost all four corners
and projected to nothing. The tolerance is now never more than 1e-4 of
the outline's own size, as the circle check is. From CodeRabbit's last
review on #13482.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
PBR product triangles carried their linear base colour and optical parameters
in every 48-byte vertex, in a stream of their own built again for any change.
They are now built triangles in the product blocks (TriClass::Pbr, 28 bytes a
vertex) whose palette entry names their material: the palette carries a
material table, uploaded as an Rgba32Float texture beside it and diffed by row,
and vs_pbr_palette reads it for the unchanged fs_pbr. A metallic, roughness or
IOR edit rewrites material rows and uploads no vertex. The design docs follow,
including the product preview's vertex bound (512 MiB on 64-bit desktop).
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* claim: #741
* Sketch: planar 3D-sketch loops become profiles (#741)
With 3D Sketch on, drawn curves (lines, splines) that close a loop lying
in a plane — possibly together with in-plane lines along the intersection
of that plane with the sketch plane — are now found as profiles in their
own plane. sketch.inspect lists them under "profiles_3d" (plane, area,
centroid, bounding curves), and surface.patch takes them by index: each
becomes a planar face in its own plane, so one sketch builds the sides of
a pyramid.
- crates/sketch: new profiles3d module — lifts the in-plane lines into
space, adds the wire segments, finds the planes the nodes span, and runs
the ordinary 2D face tracing on the edges lying in each plane (projected
into it), so holes nest as they do in the sketch plane. Loops bounded
only by in-plane curves stay in the 2D list; nothing is listed twice.
- SolvedSketch carries profiles_3d; sketch.inspect lists them.
- surface.patch gains profiles_3d: [indices]; the sketch-plane profiles
stay untouched unless `profiles` is given as well.
- stable_profiles leaves an empty index selection alone.
* Doc: pending format note for the surface patch's 3D profiles (#741)
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: #11986
* Agent chat: each message shows its time, the running item how long it has run (#11986)
Each message and tool card says when it came in ("3 min ago", the exact
time on hover). While the agent works, a running tool call counts how
long it has run ("running for 30 s") and otherwise the conversation shows
how long since the agent last did something ("Thinking for 4 min"). An
answer ends with how long it took ("Answered in 23 min").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: the time-length test installs the English catalog (#11986)
Under nextest each test runs in its own process, so the catalog is not
installed by an earlier test and the span labels read as their keys.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`design_link.rs` gives every kind of link to another design file one way to
read it and one set of state words (agreed on #3891, review on #5420):
- `open_linked(file)` reads through vfs, or returns a `LinkProblem`: its
`LinkState` (missing, unreadable, not_allowed, newer) and the reason;
`link_presence(file)` says the same without reading. The words also cover
up_to_date and out_of_date for each link's own comparison.
- `DocError::Newer` carries the format check's "saved by a newer SolidCraft",
same message, so `newer` doesn't depend on the text.
- `rebase(link, from, to)`: one Save As rule; `rebased` maps over it.
- `same_file`: one self-link rule (same path, or an alias on native hosts).
Insert Design, Update Links and Relink read through `open_linked` and use
`same_file` (only Relink caught an alias before); a design from a newer
SolidCraft says to update. `component.links` adds `state` and `reason` when a
file can't be reached; `found` is unchanged.
Rebased onto main f96b5e067:
- A design from a newer SolidCraft opens with a notice since #12166, so
the `newer` state and `DocError::Newer` are gone.
- `open_linked` is `read_linked_file` and returns the file's bytes: Update
Links keeps their digest (#9739) and reads a container's saved geometry.
It reads up to `limits::max_design_bytes()` (#7565).
- `rebased` keeps every `DesignLink` field.
- `component.links` reports `state` and `reason` in its typed reply
(`LinkInfo`, #12277), with a schema test for a missing file.
- Tests use `.sc` names (#7325).
Co-authored-by: Carel Theron <careltheron@Carels-MacBook-Pro.local>
Co-authored-by: Claude <noreply@anthropic.com>
a_parallel_rail_along_a_curved_path_keeps_the_section swept along a quarter
arc given as 181 points, which the sweep mitres at every vertex (180 corners,
about 895 booleans): 194 s locally, 209 s on CI. It now passes the quarter arc
as one arc path segment, keeps the scale rail, and checks the Pappus volume
within 1e-6 (was 2e-3): 1.1 s.
A 9-point polyline case beside it keeps a rail turning with a mitred path
covered, against the oblique-cylinder volume pi r^2 L cos(d/2) within 1e-6.
Part of #14451
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
After an update, a page the new worker claimed before the old worker's record of it was written was recorded as a network page, so its later files came from the network, which serves the newer build (the open v3 page was given v4's shared.js in a queue run). A new worker now records claimed pages as network pages only when no earlier build is kept (a first install, when open pages did come from the network); after an update a page with no record gets the visible failure. The offline test now prints the page records when that check fails.
Changes a default: no
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Joints: store a rest position and reset one or all joints to it (#14275)
Each joint can store its current values as a rest position (joint.set_rest)
and go back to it (joint.reset), one joint or every joint at once. The
joint's right-click menu has Set Current Pose as Rest and Reset to Rest;
the browser's Joints folder menu sets or resets every joint. joint.list
reports rest. The rest values save with the design (a pending format
change); a joint without one, as in every older file, loads unchanged and
a reset leaves it where it is, listed in skipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Joints: rest-position labels in every complete catalog and Korean (#14275)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Joints: captured-value helpers before the test module (clippy)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: mass, heal and geom failures carry a typed reason instead of bare statuses (part of #13790)
mass (integrator, properties, enclosure), heal (gap, face_props, heal_data,
stitch) and geom (vectors, matrix, transform, boxes, geom_utility, local_solve1d,
global_solver, grid, tol, attribute, a_object, tools, periodic_extent1d) return a
MassFailure, HealFailure or GeomFailure under the new Reason::Mass, Reason::Heal
and Reason::Geom instead of a bare Upstream status, each with a phrase or a
variant saying what failed. The operation path, entity (none), comparison status
and kind are those of the Upstream it replaces, so sm_status(), kind(),
is_port_failure() and is_status_answer() answer as before; no cause is kept.
The integrator's step-control, integrand and skipped-span failures are their own
MassFailure variants, which the integrator and typed_failure match instead of the
operation path; heal's not-yet-healable face is HealFailure::NotYetHealable, which
the heal sequence matches. geom's FALSE (coincident circle points), SM_ERR_WARNING
(nearly singular centre system) and SM_ERR_INVALID_INPUT (tolerance below
SM_EFF_ZERO) keep their statuses as GeomFailure variants. The stitch entry points'
"SmApiStitch..." messages are worded in solidcore terms.
upstream_status() answers for the new reasons as for an Upstream. The goldens
runners that record these statuses (the mass, heal and geom runners, and
blend.fillet_geom_p1, curve composite_curve_p1, curve_p5, curve_p9, hyperbola,
parabola, geom.local_solve_nd, surface volume_p1/p2, which reach geometry
utilities) read it, so every row stays as it was. The fuzz corpus keeps the grid's
voxel refusal under its new message. SolidCraft's status() passes the failures on
as their status, as it did the Upstream.
Held for open PRs: geom extent1d/extent2d (#13605), expansion (#13965),
local_solve_nd (#14102). The singular system of Matrix::solve_linear_system (and
solve_with_lu, which it calls) stays a bare status while local_solve_nd matches it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: the bare-status ratchets cut the test module on CRLF checkouts too
The ratchets find each file's `#[cfg(test)]` module by an LF-only string, so on a
Windows checkout with core.autocrlf=true it never matched and the `sm_err` calls
in test modules were counted (reported on #14605). Normalise line endings after
reading, in all six ratchets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: retire five topology UpstreamFixes flags (#14246, batch A16)
The corrected path becomes the only path, and the flag and the old path are
deleted, for:
- loop_orientation_floor_from_loop_box (WR-561): a loop's orientation floor
is relative to its own bounding box;
- trim_boundary_seam_wrap (WR-491): a UV gap of whole periods between
consecutive trim curves is a seam crossing;
- imprint_extension_keeps_analytic_curve (WR-601): a continued line, circle
or ellipse stays that kind;
- shell_inner_shell_of_statuses (WR-497): IsInnerShellOf passes on the
statuses upstream ignores, and the Checked::Ignored arms go;
- is_iso_curve_at_propagates_canonical_failure (WR-97): a failing
GetCanonical is an error. is_iso_curve_at propagates it itself, and
is_iso_curve_at_with_fixes is removed.
SolidCraft's preset (crates/smlib/src/core.rs) and the kernel rig's
fixes.json had all five on; their lines go, with the goldens' enable_fix arm
for imprint_extension_keeps_analytic_curve. Tests that compared the flag off
with on keep the corrected half.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* plan: the five A16 flags' records note that the flags are removed (#14246)
One addendum each for WR-561, WR-491, WR-601, WR-497 and WR-97: the
corrected path is the only path, what was removed, the tests, and the
goldens (no unit changes on Windows against main 87400c80e).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: austin62jake <340374338+austin62jake@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14256
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Sketch profiles: an outline whose arc meets lines tangentially through point-on-curve constraints closes (#14256)
Where a line meets an arc tangentially and the joint is held by two
point-on-curve constraints instead of one shared point, the solver fixes
the arc's end along the tangent only to about sqrt(2 * radius * 1e-9 mm).
On a 25 mm arc that leaves the ends up to 2.25e-4 mm apart, past the
1e-4 mm the profile search joins ends at, so the outline had no closed
profile and Extrude could not pick it.
The profile search now also joins the ends of a line and an arc, or of
two arcs, up to 1e-3 mm apart when each end lies within 1e-7 mm of the
other curve's line or circle. Two lines, and two arcs of one circle,
never join this way, so ends that are genuinely apart stay apart.
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* claim: model box hint that any model id can be typed (#14369)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Model box: the search hint says any model id can be typed (#14369)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Model box hint: Arabic, Hebrew, Japanese, Korean, Vietnamese and Chinese
The complete catalogs need every English message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Arabic: denoising terms, excluded-pixel labels, caustics names, texture filtering and metal reflectance help
* Arabic: construction line typed distance, agent pick pill, control port note, tanween, mouse and sketch terms in new strings
Re-translate five messages whose English changed (construction line help, agent chat pick tip, control channel note) and fix new pending strings: tanween spelling, mouse and sketch terms, Solid Fillet name, quoted button and menu names, design plural.
* claim: #13379
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UI kit: menu items can show a host's icon, not only a built-in one
MenuItem takes the kit's Icon: a kit icon name, a host texture or a
paint function. Item owns its data, so it holds an ItemIcon, which still
reads and reports as the kit name. Named icons keep their danger, ink and
plain painting, so existing menus draw as before.
Closes#13379
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Menu test: use the kit's own test context after the move to solidcraft-ui-kit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Control channel: control.cancel and the MCP cancel tool (#1989)
A client can cancel its own outstanding request (the one control.pending
reports). Each control request carries a ticket shared with its
connection; whoever moves it first wins:
- still waiting its turn: removed, never runs;
- computing off the window thread (design change, export, agent cursor):
stopped as Stop stops it, nothing applied, the queue goes on;
- running on the window thread: "cannot be cancelled", it finishes.
The worker is told to stop too: the evaluation skips features it has not
reached, kernel booleans give up at their next try, and work done after
a cancel is never cached. A connection can only cancel its own requests.
MCP: a `cancel` tool sends control.cancel on the bridge's connection;
headless refuses. With #2901's waits: `cancel` goes out at once, not
behind the request still running as other calls are, and once the app
has removed, stopped or answered it, later calls no longer wait for it.
The timeout reply names control.cancel next to control.pending.
With #3654's control.running: a request carries both its `started` flag
and its cancel ticket; one cancelled while it waits is refused before it
is marked started, and stays listed until the window drops it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Control protocol docs: a cancel's wait is 3600 s for engine.batch and the agent chat
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14263
* Viewport: highlight sketches on hover in the Browser and Timeline (#14263)
Hovering a sketch or feature row in the Browser tree or a sketch in the
History timeline highlights its geometry over the model in the 3D viewport,
clearing the highlight when the pointer moves away.
* Address CodeRabbit review on semantic_content feature hover ownership
* docs: add docstrings to touched functions for coverage threshold
* style: format touched lines with rustfmt
* Refuse a target name another component's body holds when this component's body asked for it (#12141)
A body asked to be called a name a linked component's body already has is
named otherwise (names stay unique), and a later feature that targeted the
name ran its boolean on the linked body, failing with a misleading kernel
error. The target is now refused as ambiguous, naming both bodies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Match a body to its own stored-name slot; refuse ambiguous targets before sheet machining (#12141)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Find the names bodies were asked for in one pass (#12141)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
sweep::railed::loft_rails takes sections (a plane point, its normal, the curves) and one or two
rails to the kernel's certified loft (sweep::loft_rails): the caller's tolerance and most wires,
capped into a solid or a sheet, the kernel's refusal past the wires as
Error::RailPastTolerance { asked, reached, wires }. sweep::railed::loft_blend makes the one-face
sheet of the kernel's section blend, which keeps its definition. Ops converts and maps; it
computes no geometry. RailFit (Scale, Stretch) is ops' own here.
The kernel: a section's line curve takes its NURB on a copy; a span is bounded on the surface
both its rail ends lie nearest (a rail along an edge between two faces lies on both); halving
stops where the rail's middle is itself off the surface past the tolerance (it only steers), and
a span past its most pieces is unproved (an infinite bound), not an error.
Measured at ops level on SolidCraft's rail-loft cases (crates/kernel/src/loft_rails_tests.rs and
crates/engine/src/cmd/loft_rails_tests.rs, rebuilt read-only, at SolidCraft's tolerance of 1e-4
of the sections' diagonal and its 33 wires): all sixteen keep their verdict. The twelve that
build match SolidCraft's closed forms within its bars (relative 7e-12 to 7.5e-5 against 1e-4 to
2e-3); the four it refuses are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move geom's pcurve, label and check into solidcore::eval (pure git mv)
crates/geom/src/brep/{pcurve,pcurve_tests,label,check}.rs move unchanged to
solidcore/crates/solidcore/src/eval/. The next commits retype them to the kernel's evaluation
types and switch their callers; this commit is the move alone so their history follows them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore::eval: pcurve, label and check on the kernel's evaluation types
The moved files take eval::geometry's Curve, Curve2, Surface and Frame (the record's kinds,
field for field) and the kernel's evaluator, in place of the record's types and
solidcraft-geom's evaluator. What reads a record as a record leaves: the structural checks
(counts, budgets, a use's interval in its curve's natural range), the edge and face readers
(edge_curve_interval, form_turns on a Face) and the trim request types go to solidcore-ops,
which converts. Results are unchanged: the pcurve keeps its own pole test (the evaluator's
speed rule), knot span search and B-spline cut, not the kernel's collapse; the 80 eval tests
pass, the pcurve suite among them.
Lint-driven rewrites that keep each result: checked indexing, no pre-reserving from input counts,
the loop rotation without a remainder, a clamp that cannot panic on a reversed domain. The
hostile-record cases that depend on the record's checks are kept as no-panic checks here and as
refusals in ops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-ops: pcurve, label and record_check over the record
The record's side of the moved kernel routines: each checks the record's structure as the
record's validation does (one budget per face, a use's interval in its curve's natural range, a
given pcurve as a 2D B-spline), converts through solidcore-convert, calls solidcore::eval and
converts back. The trim request types (TrimFace, TrimEdge, TrimUse, Trims) hold records, so they
live here. No arithmetic: ops' float-arithmetic and geometry-method lints pass unchanged.
The hostile-record refusals the record checks give (empty and non-finite intervals, a NaN
coordinate, a circle of no radius) are tested here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Call pcurve, label and the record checks through solidcore-ops
geom, kernel, smlib and step call solidcore_ops::{pcurve, label, record_check} where they called
solidcraft_geom::brep::{pcurve, label, check}, which moved into solidcore::eval. One
implementation of each remains, in the kernel; geom's brep module no longer declares them.
solidcore-ops becomes a workspace dependency of the four crates (the seam check's public list
already allows it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move geom's record agreement check into solidcore::eval (pure git mv)
crates/geom/src/brep/geometry.rs moves unchanged to solidcore/crates/solidcore/src/eval/agreement.rs
(the kernel's eval already has a geometry module). The next commit retypes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore::eval::agreement on a plain view; ops reads a record into it
The moved check reads a record through agreement::View (edges, vertices, faces and their uses,
in the kernel's evaluation types) in place of the record's Brep, which the kernel never sees.
solidcore-ops' record_check::agreement converts a record's curves, surfaces and pcurves into the
view and calls it; Mismatch, Problem and Record keep their wording. The pole test is the
pcurve's speed rule, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Check record agreement through solidcore-ops
kernel, smlib, step and geom's tests call solidcore_ops::record_check::agreement where they
called solidcraft_geom::brep::geometry::check, which moved into solidcore::eval::agreement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Leave kernel/src/tess on geom's pcurve path until the tess move
crates/kernel/src/tess is moving into solidcore-tess in its own pull request, so its three
pcurve::eval call sites are not edited here: geom::brep::pcurve re-exports
solidcore_ops::pcurve::eval for them, and goes when the tess move switches them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore/bench/step: the STEP reader bake-off harness
fetch.sh builds the corpus at run time from pinned sources (SolidCraft's STEP tests,
dan335/step-conformance, solidcore's fixtures, cadmpeg's fixtures, the NIST PMI and STEP Tools
sets through their SHA-256 pinned fetch scripts), folded by SHA-256; nothing downloaded is
committed. run.py runs readers in the solidcraft-cli eval shape with a timeout and writes
per-file.tsv and summary.md: opened, refused, timed out, all bodies closed, reference volumes
within 0.1% (references.tsv, from formulas), and wall time.
On main cc55f59bf2: 319 files, 195 opened, 135 with every body closed, 23 of 24 reference
volumes, median 0.20 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move SolidCraft's STEP reader into solidcore (pure git mv)
crates/step moves unchanged to solidcore/crates/solidcore-step, and its Part 21 parser to
solidcore-record/src/step/p21.rs, where it takes the place of record's earlier port of this same
file (from a5b71ca2). This commit is the move alone, so the files keep their history and
authors; the next commits make them build in their new places and switch SolidCraft to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-record: the Part 21 parser keeps record's hardening
The moved parser takes back what record's port added in review (#2591): typed errors with the
same messages, entities in id order (BTreeMap), capped parameter and record counts, linear-time
decoding of unterminated escapes, a byte order mark skipped. The file's content is record's as
before the move; its history now runs through SolidCraft's parser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: the moved reader as a solidcore workspace crate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: evaluate and form records through the kernel
The moved reader called solidcraft-geom's record evaluator, exact forms and vector helpers. It
now calls the kernel's: solidcore::eval's CurveEval, SurfaceEval, face_flux and form, through a
private `kernel` module that converts records with solidcore-convert, and the kernel's vector
measures on the record's Vec3 (`vec`). geom's form was a copy of the kernel's, function for
function; the face box (`extent`) is the kernel's with poles taken exactly. The B-spline cut is
the kernel's (eval::pcurve::segment), the pole test the pcurve's speed rule, both made public
for this. Profiling spans, which were SolidCraft's, are left out.
The crate keeps SolidCraft's formatting (its own rustfmt.toml) and lints under solidcore's hard
rules: no unsafe, unwrap, expect, panic or unchecked indexing (the 33 indexing sites are
rewritten with iterators and checked access, results unchanged). The step-conformance test reads
SolidCraft's tests/step-conformance where the tree has it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* SolidCraft reads and writes STEP through solidcore-step
crates/kernel's STEP import (step_in), STEP export (step_out) and IGES export (iges_out) call
solidcore_step where they called solidcraft_step; crates/step is gone. The Part 21 parse error is
the record's typed error, shown with the same message. solidcore-step joins the crates
SolidCraft may use directly (xtask/solidcore-public.txt) until its build is kernel primitives
under an ops recipe (RFC 0003, 1.6).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: bound faces with the pcurves' pole test (form::extent_by)
The bake-off showed the reader on the kernel's form::extent refusing 7 STEP Tools parts
(304-310 boxy_with_*: "loop encloses no (u, v) area") and reading the large parts 2-8x slower
(rear 37.8 s against 18.4 s): extent's exact collapse test classifies the boundary points
differently from the speed rule the reader's faces were always bounded with, and costs a
collapse search per point (the cost in #13655). extent's body now takes the pole test
(form::extent_by; extent keeps the exact one), and the reader passes the pcurves' speed rule.
On those files the result and time are base's again: boxy_with_cylindricity 174839.178,
nist_ftc_06 3323556.516, rear 14.0 s against 16.3 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Kernel rig: drop the gone crates/step paths
crates/step moved to solidcore/crates/solidcore-step, which the shared core (PATHS-ALL:
solidcore/**) already covers: the import and crash areas' PATHS and kernel-rig.yml drop it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: a seam round the tube for a torus face winding around its axis
A torus face bounded by two circles about its axis (a half torus between its rims, the fillet
round a bore) was refused: "a face winding around a torus ... without a seam edge". It now gets
a seam as a band on a cylinder or sphere does, a tube circle at a vertex's angle, running from
the loop the face lies above up the tube to the other loop, so on the face's side whichever half
of the tube that is. On the bake-off 9 bodies that were open shells close: torus_ring (4934.802,
its reference), turbine, nist_ctc_05 (both editions), nist_ftc_06 rd, nist_ftc_11 (both),
nist_stc_06, nist_stc_10; every body closed goes 135 -> 145, reference volumes 23 -> 24 of 24.
bench/step/run.py takes a probe-shaped reader (LABEL=probe:COMMAND, one JSON line) for a
library without SolidCraft's CLI, and runs every reader on a file together so they meet the same
load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: short entity names, POLY_LOOP, FACETED_BREP and plain FACE
What solidcore's step-import read and this reader refused:
- Part 21 short names (CRTPNT, FCSRF, PLYLP, ...) read as their long names, in the record's
parser (step-import's table, #2582). STEP Tools' clip and socks are written that way.
- A POLY_LOOP bound is a closed polygon: one vertex per point and one line edge per side, an edge
shared by every loop with the same two points.
- A FACETED_BREP is a manifold solid of such faces (calo, cylcub).
- A plain FACE, with no surface, lies on the plane of its loops (Newell's normal, refused where
the loop leaves the plane by more than the tolerance).
Tests: a faceted tetrahedron closes with 6 shared edges; short names parse to long ones; the
torus band seam from the commit before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP: read topology outside representations, lines by their vertices, inner loops by area
Topology-first reading, as solidcore's step-import and step-brep read files (#2583):
- Topology no representation lists (bare geometry files): crates/kernel reads the file's
unlisted solids, then its unlisted shells and faces (each face a one-face sheet), through
solidcore_step::topology::roots, in the file's one representation context. A representation's
own shell and face items stay out (a presentation's are not bodies).
- A LINE edge whose vertices lie off its LINE (a direction or magnitude written wrong) is the
segment between its vertices, which the faces it bounds share; other curves still refuse a far
vertex.
- An inner loop written the way its outer loop runs, on a face whose loops close in (u, v), is
run the other way.
- More Part 21 short names (product structure, representations, units, FACETED_BREP).
Bake-off (319 files, 220 opened by some reader): opened 195 -> 212, every body closed 135 ->
148, reference volumes 24 of 24 (core 22); median time unchanged (0.123 s -> 0.119 s).
kernel-cases/import 35/35; the kernel's step_in, IGES and lump tests pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: a sphere face bounded by vertex loops at its poles is the whole sphere
A face of a sphere whose bounds are vertex loops alone, at its poles (cadmpeg's
ap242_vertex_loop), was refused as "a face without an edge loop". It is the whole sphere: its
loop becomes a meridian from the south pole to the north and back, which the slit completion
keeps as its seam, as a ball bounded by its seam alone already is. Any other face without an
edge loop is still refused. Test: a ball bounded by vertex loops at its poles closes with one
seam edge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: a face SMLib refuses is named and left out; a face used twice is one piece
Opening a file never fails over faces that cannot be made: the face is left out, the rest come in
as an open body, and the import's warnings name each face left out with why.
- SMLib's refusal of a record face by its index ("flat arrays: face 90: a NURBS form that is not
its analytic surface") now names the file's face (face #1202), so it is left out like a face
the reader refuses. NIST CTC 02 (AP203) opens: 480 of its 487 faces as an open body, the 4
faces 622 mm off their surfaces and the one SMLib refuses named in the warnings.
- A face the solid's shells use more than once (cadmpeg br02: one face as the outer shell and two
voids) is read once, and its further uses are named as left out.
- One face read alone is the sheet itself (nothing to stitch).
bench/step/run.py counts the files opened with faces left out, from the import warnings.
Test: a face SMLib refuses by its index is named as the file's face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: a solid whose only face cannot be made says which face, why and how far
A file whose one face cannot be made (cadmpeg's pc01, pc03 and tp06 non-coplanar fixtures:
edges 1 to 10 mm off the face's surface) has nothing consistent to open. Its error now reads
"the solid's only face, #30, cannot be made: an edge lies 1 mm off the face's surface" in place of
the count of faces whose pcurves failed. Test: a disk whose circle lies 1.5 mm off its plane.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: an unclamped B-spline curve is read clamped to its domain
A closed curve written as an unclamped B-spline (uniform knots past its ends, its first points
repeated at its end: cadmpeg's N04 recipes, periodic writers) had no closed-form pcurve on a
plane, so its faces were left out: "no exact pcurve of a B-spline on a plane". It is now read
as the same curve over [knots[p], knots[n]], clamped there by exact knot insertion
(eval::pcurve::segment), as every evaluator and pcurve takes a spline. recipe N04a and N04b open
as 3 closed solids each (5000 and 6000 mm³, as solidcore's step-brep reads them).
Test: a disk bounded by an unclamped periodic quadratic reads, its edge clamped to [0, 4].
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: a seam round the axis for a torus face winding round its tube
A torus face bounded by two circles round its tube (an elbow's wall between its two ends) was
refused: "a loop winding around a surface more than once or in v". Its loops wind in v, so it
gets a seam the other way about: a circle about the axis at a vertex's angle round the tube,
running from the loop that winds up the tube towards −u (the face lies to the left of its loops)
to the other. band_seam and crossing take the parameter the loops wind in. In STEP Tools'
jack_in_the_box 14 bodies that were open shells close (34 -> 48 of 56).
Test: a quarter elbow closes with a quarter-circle seam about its axis.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move step-brep's export mapping into step-export, its fixtures into solidcore-step (pure git mv)
brep_to_model (a kernel body as step-import's model, for the writer) moves to step-export, which
RFC 0003 item 1.7 measures against SolidCraft's writer; the two STEP fixtures move to
solidcore-step's tests. The move alone, so their history follows them; the next commit deletes
step-brep's import side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Delete step-brep's STEP import: one STEP reader, solidcore-step
solidcore had two STEP readers; solidcore-step (SolidCraft's, moved in #13979) now reads what
step-brep's topology-first build read, but the faces it would have to build inconsistently
(#14038's gate). step-brep's import side goes: its build, constructor and UV trims (lib.rs,
constructor.rs, pcurves.rs) and their tests. What it kept for the writer, brep_to_model, is
step-export's now (RFC 0003 item 1.7); its round-trip cases that read back through the deleted
importer go with it. The panic-site list drops step-brep's rows and moves brep_to_model's.
Ported, with their names: the Discord fillet operand reads as one closed solid, and a face no
shell lists is a one-face sheet (crates/kernel step_in tests); rod_doubled_edge names the face
of its folded rim (solidcore-step). The fixtures moved with their history. The reading cases of
step-brep's tests, written by Brennen Herbruck (topology_first, issue_2580_winding_faces,
issue_2581_faceted_brep, issue_2583_topology_first, discord_fillet), are covered by
solidcore-step's tests of seams, tori, faceted solids, unlisted topology and the bake-off.
step-import stays for the goldens and step-export until #14123. Core's code for the 7
jack_in_the_box bodies still open (#14130) is at 2a96a046ca.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP: port step-brep's import panic fuzz onto solidcore-step
step-brep's tests/panic_fuzz.rs (#2490, by Brennen Herbruck) went with its import. Its cases
come back with their names:
- panic_fuzz_step_import (solidcore-step): the same mutations (truncation, flipped bytes,
hostile numbers, broken and cyclic references, dropped and repeated lines, swapped entity
names, deep nesting) of the Discord fillet and tetra fixtures and of primitives this crate's
writer writes. Each is parsed, read into records, checked as the kernel takes them, made into
kernel bodies and written again. Also entity_edit_lands_on_an_entity_name, and a check that
the written bases read back as solids, so the mutations reach the whole path.
- panic_fuzz_step_flat_decode (step-import, whose flat arrays it decodes).
Run: 3 seeds of 20 000 import cases and 5 000 flat cases, 0 findings, 0 hangs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: rustfmt the panic fuzz harness's doc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* panic fuzz harness: keep solidcore's formatting where solidcore-step includes it
solidcore-step formats with SolidCraft's rustfmt.toml, which reached the harness through its
#[path] include; the include is #[rustfmt::skip] now, and the harness is solidcore's as on main
but its doc naming the fuzzes that share it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP and pcurves: topology short names; a use's interval refused when empty or not finite
From review:
- The record's parser maps the AP203 short names of the topology too (ADVFC, EDGCRV, EDGLP,
FCBND, MNSLBR, ORNEDG, VRTPNT): a file written with them had its faces refused.
- solidcore::eval::pcurve refuses a use whose interval is empty, reversed or not finite before
anything else: a given pcurve was checked against the interval, which a NaN end passed, so
exact_pcurves returned it as a pcurve. Tests: those intervals are Invalid with and without a
given pcurve; VRTPNT and FCBND read as their long names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-record: short names checked against ISO's short-names file
Every pair of the table is as http://standards.iso.org/iso/10303/tech/short_names/short-names.txt
gives it, the source cited beside it. One was wrong, from step-import's table: CMCRSG is
COMPOSITE_CURVE_SEGMENT, not COMPOSITE_CURVE (CMPCRV, added).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: retire the step-import crate; one STEP parser, the record's p21
step-import's Part 21 parser is gone: the goldens' SMLib comparison, the
--step-corpus run and step-export read through solidcore-record's p21. The
neutral model moves to solidcore-record::step::model (plain data), the mapper
from p21 to that model moves to step-export (it computes frames and angles, so
it cannot live in the record), and the constructor script and flat arrays move
into the goldens crate, their only users.
p21's Exchange now keeps the HEADER's file name, authors and so on (step-export
reads them back). The brep_constructor golden is unchanged (185 of 185 cases).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cargo.lock: solidcraft-step is now solidcore-step
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* io: the model cache key no longer names crates/step
The reader moved to solidcore/crates/solidcore-step, which the cache already keys as part of the solidcore tree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: a whole turn a rounding short of a quarter line has its form from it (#12760)
Port of #13535's fix in crates/geom's form to the kernel's form, which the STEP reader now uses: a cylinder, cone, sphere or torus face of one whole turn whose seam reads back a rounding short of a quarter line started its form a quarter early, and a tube read back through STEP measured 14 % over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* pcurve docs: a pcurve without a closed form is refused, not fitted
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* bench/step: a malformed --reader is an error; a reader's bad JSON is a refusal, not a crash
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: SMLib's first refusal names the file's face, on a turned sheet too
A solid SMLib refused by a face's record index ("flat arrays: face 12") had that index named as a file face only on the reads that left faces out, not the first, so where every face read alone nothing was left out and the faces were stitched instead. solid_part names it, with the face order of the record SMLib was given (a sheet's turned faces reorder it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* bench/step: a reader or corpus file that cannot be run is a crashed row, not a stopped run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: a styled or layered loose shell or face is still read
roots() took any reference to a shell or face as its topology's, so a bare face a STYLED_ITEM or a layer assignment names was skipped and the file read as having no bodies. A presentation's references (styles, layers, invisibility) no longer count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
surface.ruled gets SurfaceRuledParams in a new schema/surface_ruled.rs and replies
with FeatureResult. mode and corners are keyword types in schema/types.rs with the
handler's messages; edges, direction, face and far get types of their own that keep
its messages (far is read in order, so the first bad item gives the same message).
angle and direction are checked whenever given; which mode needs them stays the
handler's.
Co-authored-by: Claude <noreply@anthropic.com>
solidcore_ops::cancel::within runs work under a CancelToken. Every ops call
it makes on that thread runs with the token on the session's kernel, whose
long loops already poll it; once cancelled, the running call stops at the
kernel's next check and later calls are refused before they wait for the
kernel, each with Error::Cancelled.
A cancelled call leaves no partial state: an edited body is put back by the
session's rollback, no result is returned even if the kernel finished
first, the kernel is replaced as after a panic, and nothing reaches the
failure callback. Nested scopes stop with either token
(CancelToken::either in the kernel).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Move geom's pcurve, label and check into solidcore::eval (pure git mv)
crates/geom/src/brep/{pcurve,pcurve_tests,label,check}.rs move unchanged to
solidcore/crates/solidcore/src/eval/. The next commits retype them to the kernel's evaluation
types and switch their callers; this commit is the move alone so their history follows them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore::eval: pcurve, label and check on the kernel's evaluation types
The moved files take eval::geometry's Curve, Curve2, Surface and Frame (the record's kinds,
field for field) and the kernel's evaluator, in place of the record's types and
solidcraft-geom's evaluator. What reads a record as a record leaves: the structural checks
(counts, budgets, a use's interval in its curve's natural range), the edge and face readers
(edge_curve_interval, form_turns on a Face) and the trim request types go to solidcore-ops,
which converts. Results are unchanged: the pcurve keeps its own pole test (the evaluator's
speed rule), knot span search and B-spline cut, not the kernel's collapse; the 80 eval tests
pass, the pcurve suite among them.
Lint-driven rewrites that keep each result: checked indexing, no pre-reserving from input counts,
the loop rotation without a remainder, a clamp that cannot panic on a reversed domain. The
hostile-record cases that depend on the record's checks are kept as no-panic checks here and as
refusals in ops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-ops: pcurve, label and record_check over the record
The record's side of the moved kernel routines: each checks the record's structure as the
record's validation does (one budget per face, a use's interval in its curve's natural range, a
given pcurve as a 2D B-spline), converts through solidcore-convert, calls solidcore::eval and
converts back. The trim request types (TrimFace, TrimEdge, TrimUse, Trims) hold records, so they
live here. No arithmetic: ops' float-arithmetic and geometry-method lints pass unchanged.
The hostile-record refusals the record checks give (empty and non-finite intervals, a NaN
coordinate, a circle of no radius) are tested here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Call pcurve, label and the record checks through solidcore-ops
geom, kernel, smlib and step call solidcore_ops::{pcurve, label, record_check} where they called
solidcraft_geom::brep::{pcurve, label, check}, which moved into solidcore::eval. One
implementation of each remains, in the kernel; geom's brep module no longer declares them.
solidcore-ops becomes a workspace dependency of the four crates (the seam check's public list
already allows it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move geom's record agreement check into solidcore::eval (pure git mv)
crates/geom/src/brep/geometry.rs moves unchanged to solidcore/crates/solidcore/src/eval/agreement.rs
(the kernel's eval already has a geometry module). The next commit retypes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore::eval::agreement on a plain view; ops reads a record into it
The moved check reads a record through agreement::View (edges, vertices, faces and their uses,
in the kernel's evaluation types) in place of the record's Brep, which the kernel never sees.
solidcore-ops' record_check::agreement converts a record's curves, surfaces and pcurves into the
view and calls it; Mismatch, Problem and Record keep their wording. The pole test is the
pcurve's speed rule, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Check record agreement through solidcore-ops
kernel, smlib, step and geom's tests call solidcore_ops::record_check::agreement where they
called solidcraft_geom::brep::geometry::check, which moved into solidcore::eval::agreement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Leave kernel/src/tess on geom's pcurve path until the tess move
crates/kernel/src/tess is moving into solidcore-tess in its own pull request, so its three
pcurve::eval call sites are not edited here: geom::brep::pcurve re-exports
solidcore_ops::pcurve::eval for them, and goes when the tess move switches them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore/bench/step: the STEP reader bake-off harness
fetch.sh builds the corpus at run time from pinned sources (SolidCraft's STEP tests,
dan335/step-conformance, solidcore's fixtures, cadmpeg's fixtures, the NIST PMI and STEP Tools
sets through their SHA-256 pinned fetch scripts), folded by SHA-256; nothing downloaded is
committed. run.py runs readers in the solidcraft-cli eval shape with a timeout and writes
per-file.tsv and summary.md: opened, refused, timed out, all bodies closed, reference volumes
within 0.1% (references.tsv, from formulas), and wall time.
On main cc55f59bf2: 319 files, 195 opened, 135 with every body closed, 23 of 24 reference
volumes, median 0.20 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move SolidCraft's STEP reader into solidcore (pure git mv)
crates/step moves unchanged to solidcore/crates/solidcore-step, and its Part 21 parser to
solidcore-record/src/step/p21.rs, where it takes the place of record's earlier port of this same
file (from a5b71ca2). This commit is the move alone, so the files keep their history and
authors; the next commits make them build in their new places and switch SolidCraft to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-record: the Part 21 parser keeps record's hardening
The moved parser takes back what record's port added in review (#2591): typed errors with the
same messages, entities in id order (BTreeMap), capped parameter and record counts, linear-time
decoding of unterminated escapes, a byte order mark skipped. The file's content is record's as
before the move; its history now runs through SolidCraft's parser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: the moved reader as a solidcore workspace crate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: evaluate and form records through the kernel
The moved reader called solidcraft-geom's record evaluator, exact forms and vector helpers. It
now calls the kernel's: solidcore::eval's CurveEval, SurfaceEval, face_flux and form, through a
private `kernel` module that converts records with solidcore-convert, and the kernel's vector
measures on the record's Vec3 (`vec`). geom's form was a copy of the kernel's, function for
function; the face box (`extent`) is the kernel's with poles taken exactly. The B-spline cut is
the kernel's (eval::pcurve::segment), the pole test the pcurve's speed rule, both made public
for this. Profiling spans, which were SolidCraft's, are left out.
The crate keeps SolidCraft's formatting (its own rustfmt.toml) and lints under solidcore's hard
rules: no unsafe, unwrap, expect, panic or unchecked indexing (the 33 indexing sites are
rewritten with iterators and checked access, results unchanged). The step-conformance test reads
SolidCraft's tests/step-conformance where the tree has it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* SolidCraft reads and writes STEP through solidcore-step
crates/kernel's STEP import (step_in), STEP export (step_out) and IGES export (iges_out) call
solidcore_step where they called solidcraft_step; crates/step is gone. The Part 21 parse error is
the record's typed error, shown with the same message. solidcore-step joins the crates
SolidCraft may use directly (xtask/solidcore-public.txt) until its build is kernel primitives
under an ops recipe (RFC 0003, 1.6).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: bound faces with the pcurves' pole test (form::extent_by)
The bake-off showed the reader on the kernel's form::extent refusing 7 STEP Tools parts
(304-310 boxy_with_*: "loop encloses no (u, v) area") and reading the large parts 2-8x slower
(rear 37.8 s against 18.4 s): extent's exact collapse test classifies the boundary points
differently from the speed rule the reader's faces were always bounded with, and costs a
collapse search per point (the cost in #13655). extent's body now takes the pole test
(form::extent_by; extent keeps the exact one), and the reader passes the pcurves' speed rule.
On those files the result and time are base's again: boxy_with_cylindricity 174839.178,
nist_ftc_06 3323556.516, rear 14.0 s against 16.3 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Kernel rig: drop the gone crates/step paths
crates/step moved to solidcore/crates/solidcore-step, which the shared core (PATHS-ALL:
solidcore/**) already covers: the import and crash areas' PATHS and kernel-rig.yml drop it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: a seam round the tube for a torus face winding around its axis
A torus face bounded by two circles about its axis (a half torus between its rims, the fillet
round a bore) was refused: "a face winding around a torus ... without a seam edge". It now gets
a seam as a band on a cylinder or sphere does, a tube circle at a vertex's angle, running from
the loop the face lies above up the tube to the other loop, so on the face's side whichever half
of the tube that is. On the bake-off 9 bodies that were open shells close: torus_ring (4934.802,
its reference), turbine, nist_ctc_05 (both editions), nist_ftc_06 rd, nist_ftc_11 (both),
nist_stc_06, nist_stc_10; every body closed goes 135 -> 145, reference volumes 23 -> 24 of 24.
bench/step/run.py takes a probe-shaped reader (LABEL=probe:COMMAND, one JSON line) for a
library without SolidCraft's CLI, and runs every reader on a file together so they meet the same
load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: short entity names, POLY_LOOP, FACETED_BREP and plain FACE
What solidcore's step-import read and this reader refused:
- Part 21 short names (CRTPNT, FCSRF, PLYLP, ...) read as their long names, in the record's
parser (step-import's table, #2582). STEP Tools' clip and socks are written that way.
- A POLY_LOOP bound is a closed polygon: one vertex per point and one line edge per side, an edge
shared by every loop with the same two points.
- A FACETED_BREP is a manifold solid of such faces (calo, cylcub).
- A plain FACE, with no surface, lies on the plane of its loops (Newell's normal, refused where
the loop leaves the plane by more than the tolerance).
Tests: a faceted tetrahedron closes with 6 shared edges; short names parse to long ones; the
torus band seam from the commit before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP: read topology outside representations, lines by their vertices, inner loops by area
Topology-first reading, as solidcore's step-import and step-brep read files (#2583):
- Topology no representation lists (bare geometry files): crates/kernel reads the file's
unlisted solids, then its unlisted shells and faces (each face a one-face sheet), through
solidcore_step::topology::roots, in the file's one representation context. A representation's
own shell and face items stay out (a presentation's are not bodies).
- A LINE edge whose vertices lie off its LINE (a direction or magnitude written wrong) is the
segment between its vertices, which the faces it bounds share; other curves still refuse a far
vertex.
- An inner loop written the way its outer loop runs, on a face whose loops close in (u, v), is
run the other way.
- More Part 21 short names (product structure, representations, units, FACETED_BREP).
Bake-off (319 files, 220 opened by some reader): opened 195 -> 212, every body closed 135 ->
148, reference volumes 24 of 24 (core 22); median time unchanged (0.123 s -> 0.119 s).
kernel-cases/import 35/35; the kernel's step_in, IGES and lump tests pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: a sphere face bounded by vertex loops at its poles is the whole sphere
A face of a sphere whose bounds are vertex loops alone, at its poles (cadmpeg's
ap242_vertex_loop), was refused as "a face without an edge loop". It is the whole sphere: its
loop becomes a meridian from the south pole to the north and back, which the slit completion
keeps as its seam, as a ball bounded by its seam alone already is. Any other face without an
edge loop is still refused. Test: a ball bounded by vertex loops at its poles closes with one
seam edge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: a face SMLib refuses is named and left out; a face used twice is one piece
Opening a file never fails over faces that cannot be made: the face is left out, the rest come in
as an open body, and the import's warnings name each face left out with why.
- SMLib's refusal of a record face by its index ("flat arrays: face 90: a NURBS form that is not
its analytic surface") now names the file's face (face #1202), so it is left out like a face
the reader refuses. NIST CTC 02 (AP203) opens: 480 of its 487 faces as an open body, the 4
faces 622 mm off their surfaces and the one SMLib refuses named in the warnings.
- A face the solid's shells use more than once (cadmpeg br02: one face as the outer shell and two
voids) is read once, and its further uses are named as left out.
- One face read alone is the sheet itself (nothing to stitch).
bench/step/run.py counts the files opened with faces left out, from the import warnings.
Test: a face SMLib refuses by its index is named as the file's face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: a solid whose only face cannot be made says which face, why and how far
A file whose one face cannot be made (cadmpeg's pc01, pc03 and tp06 non-coplanar fixtures:
edges 1 to 10 mm off the face's surface) has nothing consistent to open. Its error now reads
"the solid's only face, #30, cannot be made: an edge lies 1 mm off the face's surface" in place of
the count of faces whose pcurves failed. Test: a disk whose circle lies 1.5 mm off its plane.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: an unclamped B-spline curve is read clamped to its domain
A closed curve written as an unclamped B-spline (uniform knots past its ends, its first points
repeated at its end: cadmpeg's N04 recipes, periodic writers) had no closed-form pcurve on a
plane, so its faces were left out: "no exact pcurve of a B-spline on a plane". It is now read
as the same curve over [knots[p], knots[n]], clamped there by exact knot insertion
(eval::pcurve::segment), as every evaluator and pcurve takes a spline. recipe N04a and N04b open
as 3 closed solids each (5000 and 6000 mm³, as solidcore's step-brep reads them).
Test: a disk bounded by an unclamped periodic quadratic reads, its edge clamped to [0, 4].
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: a seam round the axis for a torus face winding round its tube
A torus face bounded by two circles round its tube (an elbow's wall between its two ends) was
refused: "a loop winding around a surface more than once or in v". Its loops wind in v, so it
gets a seam the other way about: a circle about the axis at a vertex's angle round the tube,
running from the loop that winds up the tube towards −u (the face lies to the left of its loops)
to the other. band_seam and crossing take the parameter the loops wind in. In STEP Tools'
jack_in_the_box 14 bodies that were open shells close (34 -> 48 of 56).
Test: a quarter elbow closes with a quarter-circle seam about its axis.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move step-brep's export mapping into step-export, its fixtures into solidcore-step (pure git mv)
brep_to_model (a kernel body as step-import's model, for the writer) moves to step-export, which
RFC 0003 item 1.7 measures against SolidCraft's writer; the two STEP fixtures move to
solidcore-step's tests. The move alone, so their history follows them; the next commit deletes
step-brep's import side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Delete step-brep's STEP import: one STEP reader, solidcore-step
solidcore had two STEP readers; solidcore-step (SolidCraft's, moved in #13979) now reads what
step-brep's topology-first build read, but the faces it would have to build inconsistently
(#14038's gate). step-brep's import side goes: its build, constructor and UV trims (lib.rs,
constructor.rs, pcurves.rs) and their tests. What it kept for the writer, brep_to_model, is
step-export's now (RFC 0003 item 1.7); its round-trip cases that read back through the deleted
importer go with it. The panic-site list drops step-brep's rows and moves brep_to_model's.
Ported, with their names: the Discord fillet operand reads as one closed solid, and a face no
shell lists is a one-face sheet (crates/kernel step_in tests); rod_doubled_edge names the face
of its folded rim (solidcore-step). The fixtures moved with their history. The reading cases of
step-brep's tests, written by Brennen Herbruck (topology_first, issue_2580_winding_faces,
issue_2581_faceted_brep, issue_2583_topology_first, discord_fillet), are covered by
solidcore-step's tests of seams, tori, faceted solids, unlisted topology and the bake-off.
step-import stays for the goldens and step-export until #14123. Core's code for the 7
jack_in_the_box bodies still open (#14130) is at 2a96a046ca.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP: port step-brep's import panic fuzz onto solidcore-step
step-brep's tests/panic_fuzz.rs (#2490, by Brennen Herbruck) went with its import. Its cases
come back with their names:
- panic_fuzz_step_import (solidcore-step): the same mutations (truncation, flipped bytes,
hostile numbers, broken and cyclic references, dropped and repeated lines, swapped entity
names, deep nesting) of the Discord fillet and tetra fixtures and of primitives this crate's
writer writes. Each is parsed, read into records, checked as the kernel takes them, made into
kernel bodies and written again. Also entity_edit_lands_on_an_entity_name, and a check that
the written bases read back as solids, so the mutations reach the whole path.
- panic_fuzz_step_flat_decode (step-import, whose flat arrays it decodes).
Run: 3 seeds of 20 000 import cases and 5 000 flat cases, 0 findings, 0 hangs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: rustfmt the panic fuzz harness's doc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* panic fuzz harness: keep solidcore's formatting where solidcore-step includes it
solidcore-step formats with SolidCraft's rustfmt.toml, which reached the harness through its
#[path] include; the include is #[rustfmt::skip] now, and the harness is solidcore's as on main
but its doc naming the fuzzes that share it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP and pcurves: topology short names; a use's interval refused when empty or not finite
From review:
- The record's parser maps the AP203 short names of the topology too (ADVFC, EDGCRV, EDGLP,
FCBND, MNSLBR, ORNEDG, VRTPNT): a file written with them had its faces refused.
- solidcore::eval::pcurve refuses a use whose interval is empty, reversed or not finite before
anything else: a given pcurve was checked against the interval, which a NaN end passed, so
exact_pcurves returned it as a pcurve. Tests: those intervals are Invalid with and without a
given pcurve; VRTPNT and FCBND read as their long names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-record: short names checked against ISO's short-names file
Every pair of the table is as http://standards.iso.org/iso/10303/tech/short_names/short-names.txt
gives it, the source cited beside it. One was wrong, from step-import's table: CMCRSG is
COMPOSITE_CURVE_SEGMENT, not COMPOSITE_CURVE (CMPCRV, added).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cargo.lock: solidcraft-step is now solidcore-step
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* io: the model cache key no longer names crates/step
The reader moved to solidcore/crates/solidcore-step, which the cache already keys as part of the solidcore tree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: a whole turn a rounding short of a quarter line has its form from it (#12760)
Port of #13535's fix in crates/geom's form to the kernel's form, which the STEP reader now uses: a cylinder, cone, sphere or torus face of one whole turn whose seam reads back a rounding short of a quarter line started its form a quarter early, and a tube read back through STEP measured 14 % over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* pcurve docs: a pcurve without a closed form is refused, not fitted
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* bench/step: a malformed --reader is an error; a reader's bad JSON is a refusal, not a crash
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STEP import: SMLib's first refusal names the file's face, on a turned sheet too
A solid SMLib refused by a face's record index ("flat arrays: face 12") had that index named as a file face only on the reads that left faces out, not the first, so where every face read alone nothing was left out and the faces were stitched instead. solid_part names it, with the face order of the record SMLib was given (a sheet's turned faces reorder it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* bench/step: a reader or corpus file that cannot be run is a crashed row, not a stopped run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #13537 and #13538, rim cut and overlapping-cylinder fillet
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: a rail's end at a side edge starts from the round's corner section (#13538)
A round of either crossing edge of two crossing cylinders (SolidCraft's
fillet_the_edges_between_two_overlapping_cylinders, R 13) was refused with
status 1001: CalcFilletIntSideFace found no intersection of the round with
the cap. With the walls' offsets continued as themselves (whole circles),
the faces hold two balls at each cap edge, the round of each crossing.
CalcRailIntEdgeuse's first guess on the small wall's cap edge, upstream's
radius fraction along the edge, solved the rail end onto the other
crossing's ball (contact at (4.02, 20.35) instead of (18.98, 8.37)), so the
cap curve between the two rail ends did not exist.
The first guess is now where the ball's section through the filleted
edge's end touches the side edge: the section is solved from the edge
point on both faces, so it is the ball of this round (the start of a
rolling-ball march; Choi and Ju 1989). The section solve is
ball_fit::edge_section, shared with ball_fits_its_faces. Upstream's other
guesses follow unchanged.
This also covers #3045 (the plate's arc end): its test passes with the
face-domain clamp on the drop removed, and that clamp is what broke this
case at a445bdaac6..c0d3cc0124 (b11c5554b3): it moved the drop's guess to
the face's end, from where the solve also found the other ball. One
construction, so the clamp goes.
Goldens, blend.fillet_executive_p3, 12 cases moved (every other blend
unit unchanged):
- radius-supported-at-limit-accepted: built (volume 2199.999313) ->
refused 1001 (input kept, 2200). The rail end moves one ulp
(29.999920000080003 -> 29.99992000008); the case sits at the radius
limit. The old body was off its closed form (2200 - 4e-8) by 6.9e-4.
- stubs4-coons_01_03_29-e0-r0.5: refused 1001 -> built (8 vertices, 12
edges, 5 faces; mass status 1007 before and after: not a solid).
- ten others: same status and counts; mass properties move at most
4e-9 relative; in four (issue_2176 tab side, issue-880 half-arc 1-1 and
1.4437, stubs4-coons e3) the curves' and surfaces' digests change too
(the rail ends' parameters converge to other roundings).
New test: solidcore-ops round_between_crossing_cylinders (R 6 at each
crossing, both, and R 13), closed-form volumes; fails without this change
(R 13: Status(1001)).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: a variable round too large for a box edge is refused with the radius that fits
a_failed_variable_radius_fillet_keeps_its_error_and_a_current_body (radius
0.5 to 30 along an edge of the 10 x 20 x 30 box) was refused with a bare
status 1001; with the rail end continued from the corner section it is
refused as FilletRefused, same status, naming radius 10 as the largest
that builds (the 10 wide side). The body is kept as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Known failing: the overlapping cylinders' round builds again (#13538)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: a rail end on an offset continued as itself is solved on its closed turn (#13538)
solidcore's issue_1945_rail_uv_trim top_edge_round_is_valid failed after
the rail end started from the round's corner section: the round of the
bar's top edge, ending on the boss's arc round, was refused ("a round's
rail leaves the face it rolls on into the body's material"). The rail
end's solve found the right point (side-edge parameter 0.99805 as before)
but named it on the arc round's offset one generator turn away: v
-3.3445 instead of 0.6555, which the face's trim reads as off the face.
LoadInitialValues solved the other surface on its base domain grown by
SetupOffsetExtension, several turns of a surface that continues as
itself. The extension names each point once inside its closed turn: round
the axis it wraps past the turn (WR-650), and along a circle generator (a
torus's, a revolved circle's) the closed circle holds every point. Those
directions are now solved there (closed_turn): round the axis periodic on
the turn, along the generator bounded half the gap past either end (its
extension spans are quarter turns, the base's need not be, so it is not
wrapped).
Every blend unit's goldens are unchanged from the previous commit's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: drop the import main's definition change left unused (#13538)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge origin/main; goldens: the round at the minimum radius is known failing (tangency-labs/solidcore#3245)
radius-supported-at-limit-accepted depends on rounding: the round and its
end arcs lie r(sqrt 2 - 1) = 3.3e-5 from the corner they replace, inside
the 5e-5 of the corner vertex's and the fillet's tolerances, so whether the
end arcs are imprinted flips with ulp-level changes (it came out
[1001,8,12,6] on the merge with main where it was blessed [1001,12,18,8]).
It is listed in known_failing.json, without a golden line, until #3245.
PASS blend.fillet_executive_p3 cases=798 changed=0 known_failing=5.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: building a cap face of a long outline stops walking the whole face for each edge (#14329)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: an unheard face edit skips walking the face's edge uses (#14329)
Each edge added to a face notifies the face (PostEdit), which walked every
edge use and vertex use of the face. An edge use's PostEdit changes nothing
and only a context notify callback can see it, so without one the edge-use
walk is skipped. Building the cap of an outline of N lines was O(N^2) in
that walk; the cap of a 12,000-line outline builds in 10.4 s instead of
15.2 s, with the same face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Each new face of an extrusion, revolution, sweep, loft or screw sweep is
named (start cap, end cap, or the wall swept from a sketch segment) so later
features find it again. The kernel meshed every new body to work the names
out; on extrusion-heavy designs that mesh was about a third of the rebuild.
The names now come from the exact body: a cap is a face on the start or end
plane, the start one where a profile point lies on it; a wall comes from each
segment whose middle lies on its edge in the sketch plane, or whose point
half-way along lies on the face (solidcore-ops `Body::face_distances`, the
kernel's trimmed face closest point). Edges and faces SMLib built to its own
tolerance are matched to it. The smlib_sweeps roles line leaves
xtask/mesh-decisions.txt (RFC 0003, item 3.3).
* claim: #519 thread flank continuity
* Threads: keep each flank as one curved face
* Threads: preserve saved flank faces when rebuilding old designs
Keep historical thread faces and construction order when a saved feature
has no continuous_flanks field. New threads use continuous flanks by
default, with a preference for the historical layout. Redefine and dialog
reopen retain the feature's saved choice. Generic coils and hole features
keep their existing construction.
Captured pre-change flank names and points remain on the original faces,
including both chamfered ends. All 49 engine thread tests, the UI dialog
regression, and both SMLib guard checks pass. Independent Astra review
found no correctness findings. Required cargo xtask ci passed repository
validators and strict workspace Clippy; its full native test build was
stopped because the available disk cannot hold the remaining binaries.
All 14 selected crates pass cargo xtask wasm. The sweep group retains one
documented baseline cylinder-section failure.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
* Threads: a new Thread dialog leaves the default flank layout out of the command
The Thread dialog put `continuous_flanks` into every new command, so the
untouched dialog no longer sent only its face and
`dialogs_thread::tests::every_option_reaches_the_command` failed. Continuous
flanks are the command's default, so the dialog now sends the option only when
the `thread_continuous_flanks` preference asks for the historical half-turn
faces. Editing a saved thread still sends its saved layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Threads: continuous flanks are not a preference; record the ladder's thread cases
New modeled threads always get continuous flank faces. The
`thread_continuous_flanks` preference, its Design page row, its message
catalog strings and its control-protocol entry are removed. A saved
thread still keeps its own `continuous_flanks` field, so an old design
reopens and rebuilds with its half-turn faces, and editing it keeps them.
The dialog test now checks that a new Thread dialog leaves the field out
and that reopening a saved thread keeps its layout.
Kernel ladder: 29 cases make fewer faces, edges and vertices because each
flank is one face: 06-threads/001 to 017 and 019 to 028, and
11-cumulative-parts/003 and 004. Their volume and area match the old
golden to 4e-9 relative and every check passes. Run with
`continuous_flanks: false`, all 29 make exactly the old counts. Only those
29 entries change; the ladder's order and report stay as on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Benjamin Davis <benjamindavis@Benjamins-Mac-mini.local>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* sweep: test that a pipe reaching past a bend's centre is refused (#13766)
A pipe whose radius reaches the centre of a bend in its path folds its wall
through itself on the inside of that bend. The test sweeps pipes along an
ellipse with semi-axes 2 and 4 (tightest bend radius 1) and a circle of
radius 2, and expects radii 1% or more past the bend refused as InvalidInput
and radii 1% within it built as manifold solids.
On main 3e2cc277e it fails at radius 1.01: create_pipe_sweep builds the pipe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* sweep: refuse a pipe whose radius reaches past a bend's centre (#13766)
create_pipe_sweep checked only that the radius is above zero, as upstream
does (SmApiBrep.cpp:1385). Where the path bends with a radius smaller than
the pipe's, the circles of neighbouring sections cross on the inside of the
bend and the swept wall folds through itself; the pipe surface is singular
wherever the radius reaches 1/kappa of the path (Maekawa, Patrikalakis,
Sakkalis and Yu, "Analysis and applications of pipe surfaces", CAGD 15(5),
1998). The panic fuzz's face_ops case 3447330831849305406 built such a
pipe (radius 5.05 along an ellipse whose tightest bend is 1.62), and its
silhouette ran past the hang limit.
path_bends_within measures the path's curvature at 16 samples per knot span,
both span ends included, and create_pipe_sweep refuses the pipe as
InvalidInput when a sample's bend radius is at most the pipe's. A refusal is
proved by the sample; a bend between two samples is missed and that pipe is
built as before. The upstream defect is recorded as WR-774.
general_path_sweep's ledger test swept its 0.75 pipe along the balance
wire's points fitted closed, which bends with radius 0.041 near its start
and 0.045 near its end. Fitted open, as the wire runs, the spline still
bends with radius 0.564 near its far end, so that case's pipe is now 0.5.
Its walls are bare B-spline faces as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: austin62jake <340374338+austin62jake@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: turn the control channel on and off at once, with a port (#5736)
"Let AI agents connect" moves to Preferences > Assistant > Agents through
MCP and gets a Port field (default 27878, as before). Turning it on or
off, or changing the port, now takes effect without a restart: the
listener closes with its connections, token file and discovery file, and
opens again where the setting asks. A port another program holds shows a
message under the field. --control / SOLIDCRAFT_CONTROL_PORT still win for
the run; the setting then waits for the next launch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: "Let AI agents connect" is off by default
A new install no longer opens the control channel at launch: the switch
in Preferences > Assistant > Agents through MCP starts off, and turning it
on opens the channel at once, on its port. Preferences saved with it on
keep it on; --control and SOLIDCRAFT_CONTROL_PORT still open it for a run.
The docs and tests follow the new default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: dudedagger42 <dudedagger42@users.noreply.github.com>
* claim: #10794
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Sketch: constraint glyphs can be drawn larger, and their label stays clear of the cursor
Constraint glyphs are a fixed 15-pixel badge with a small letter or
icon, so zooming never made them easier to read, and the label that
names a hovered glyph sat 12 px below and right of the pointer, where
the cursor arrow covers it.
Preferences > Sketch gets "Constraint glyph size" (constraint_glyph_scale,
100% to 300%): badge, letter, icon, spacing and the hover reach grow
together. The default of 100% is the size they always had. The hover
label now goes above the glyph and the pointer, or to their left near
the top of the view, and only below them past the cursor's reach when
neither fits.
Fixes#10794
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
volume_of() accepted Positive, Negative and Undecided enclosures alike, so a sign failure would pass. positive() panics unless soundness proves the volume Positive; the overlap and containment asserts are unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
sweep::cornered::cornered_sweep sweeps a closed planar profile along a path
with corners. Each piece is swept on its own (lines as degree-1 skins between
shared sections, arcs by the rotational sweep) and sewn as sweep::segments
sews; a corner's mitre section is asked once and shared by the runs it joins.
Twist, rails, helices, an arc with an end scale or ending at a corner, and a
closed path with an end scale or closing at a corner are refused by name.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14205
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
* Move: Ctrl/Shift+click adds bodies after the dialog goes on (#14205)
In Point to Point, Point to Position and Rotate, a body pick moves the
dialog on to From, Point or Axis (#6170), so each further body needed a
click back into Bodies. A Ctrl or Shift click on a body now adds it to
Bodies (or takes it out if picked already), as Ctrl/Shift+click adds to
the selection in the view, and the dialog stays on the input it was on.
Plain clicks behave as before.
Fixes#14205
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
* Move: add-only Shift keeps a picked body; Bodies hold while OK computes
Review follow-up for #14205. With Shift adding only (Preferences), a
Shift click on a body Move has picked already now keeps it in Bodies;
Ctrl still takes it out. While OK computes, a Ctrl or Shift click leaves
Bodies as it is, as dialog_pick holds the other inputs. The test setup
moves to a helper shared by the three tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10676
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Agent chat: Ctrl+A in the transcript selects the whole conversation
Each Markdown block of a reply is a label of its own, and egui's label
selection cannot be set from code, so Ctrl+A only selected the block the
selection was in. While the chat owns the keyboard and no text box has
focus, Ctrl+A (Cmd+A) now selects every prompt and answer of the retained
conversation: their text is drawn over the selection colour, and Copy
puts them on the clipboard in order, as they read, each under who wrote
it. Thoughts, tool cards, plans, notices and the composer are left out.
Esc or a click ends it; a focused composer keeps its own Select All.
Docked, floating and popped out.
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Agent chat: outline the select-all highlight so it shows on a prompt bubble
The selection fill is the same colour as the user bubble, so in the dark
theme a selected prompt looked unselected. Each highlighted block now also
gets a 1 px outline in the selection stroke colour.
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* App bar: Home follows the app's name as drawn
Home sat at a fixed 128 pt from the bar's edge, sized for "SolidCraft", so
a host with a longer name drew it under Home. The bar now lays the name
out in the font it is drawn in (semibold at the heading size) and places
Home a 6 pt gap after it, with 128 as the least place.
128 came from SolidCraft's name: it starts at 36 and is 65.1 pt wide in
Plex Sans semibold 14, ending at 101.1; Home's button starts at 108, 6.9
after it. The gap is rounded down to 6 so SolidCraft's bar does not move
(36 + 65.1 + 6 + 20 = 127.1 < 128).
Tests: app_bar::tests::home_follows_a_long_app_name (a long name's text
ends before Home; on the old code it ended at 249 with Home at 108) and
titlebar::tests::home_keeps_its_place_after_solidcrafts_name (SolidCraft's
Home stays 88 pt right of the name's start, as before). No expected
values changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Theme files: built-in themes load under any host's tab table
A theme's `tabs` names tabs of the host's tab table, and reading one that
named a tab the table lacked failed the whole file. The look's built-in
themes key Onyx's per-tab surfaces by SolidCraft's tab names, and one
failed built-in empties the list, so under another host's tab table
every built-in theme but Light and Dark was missing.
Design: the strictness now depends on who wrote the file. Built-ins
(read_builtin) still check every `tabs` entry's surfaces and colours, but
leave out an entry for a tab the host lacks. Files a user imports, saves
or loads from a profile (from_value, parse, set_tabs, resolve_for_tab)
are read exactly as before, so SolidCraft's errors for a misspelt or
extra tab are unchanged and saved themes load as before.
Keying the tints by accent ramp instead would let another host's tabs
pick up Onyx's surfaces, but it changes the theme file format and needs
migration of saved and imported files; this change needs neither. Its
cost: a host whose tab names differ gets no per-tab surfaces from Onyx.
Tests: theme_files::tests::a_builtin_keeps_only_the_hosts_tabs_and_a_
users_file_is_still_refused (old code: "theme tinted: `tabs` has too many
entries") and look's every_builtin_theme_loads_under_a_host_with_other_tabs
(old code: read_builtin failed). The look tests' host tab table changes
from SolidCraft's seven tabs, which only let the built-ins load, to three
tabs it does not have (DRAW, SHAPE, PAINT); install_gives_the_kit_...
asserted 25 built-ins against it and failed (0) on the old code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
---------
Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
* Move geom's pcurve, label and check into solidcore::eval (pure git mv)
crates/geom/src/brep/{pcurve,pcurve_tests,label,check}.rs move unchanged to
solidcore/crates/solidcore/src/eval/. The next commits retype them to the kernel's evaluation
types and switch their callers; this commit is the move alone so their history follows them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore::eval: pcurve, label and check on the kernel's evaluation types
The moved files take eval::geometry's Curve, Curve2, Surface and Frame (the record's kinds,
field for field) and the kernel's evaluator, in place of the record's types and
solidcraft-geom's evaluator. What reads a record as a record leaves: the structural checks
(counts, budgets, a use's interval in its curve's natural range), the edge and face readers
(edge_curve_interval, form_turns on a Face) and the trim request types go to solidcore-ops,
which converts. Results are unchanged: the pcurve keeps its own pole test (the evaluator's
speed rule), knot span search and B-spline cut, not the kernel's collapse; the 80 eval tests
pass, the pcurve suite among them.
Lint-driven rewrites that keep each result: checked indexing, no pre-reserving from input counts,
the loop rotation without a remainder, a clamp that cannot panic on a reversed domain. The
hostile-record cases that depend on the record's checks are kept as no-panic checks here and as
refusals in ops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-ops: pcurve, label and record_check over the record
The record's side of the moved kernel routines: each checks the record's structure as the
record's validation does (one budget per face, a use's interval in its curve's natural range, a
given pcurve as a 2D B-spline), converts through solidcore-convert, calls solidcore::eval and
converts back. The trim request types (TrimFace, TrimEdge, TrimUse, Trims) hold records, so they
live here. No arithmetic: ops' float-arithmetic and geometry-method lints pass unchanged.
The hostile-record refusals the record checks give (empty and non-finite intervals, a NaN
coordinate, a circle of no radius) are tested here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Call pcurve, label and the record checks through solidcore-ops
geom, kernel, smlib and step call solidcore_ops::{pcurve, label, record_check} where they called
solidcraft_geom::brep::{pcurve, label, check}, which moved into solidcore::eval. One
implementation of each remains, in the kernel; geom's brep module no longer declares them.
solidcore-ops becomes a workspace dependency of the four crates (the seam check's public list
already allows it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move geom's record agreement check into solidcore::eval (pure git mv)
crates/geom/src/brep/geometry.rs moves unchanged to solidcore/crates/solidcore/src/eval/agreement.rs
(the kernel's eval already has a geometry module). The next commit retypes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore::eval::agreement on a plain view; ops reads a record into it
The moved check reads a record through agreement::View (edges, vertices, faces and their uses,
in the kernel's evaluation types) in place of the record's Brep, which the kernel never sees.
solidcore-ops' record_check::agreement converts a record's curves, surfaces and pcurves into the
view and calls it; Mismatch, Problem and Record keep their wording. The pole test is the
pcurve's speed rule, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Check record agreement through solidcore-ops
kernel, smlib, step and geom's tests call solidcore_ops::record_check::agreement where they
called solidcraft_geom::brep::geometry::check, which moved into solidcore::eval::agreement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Leave kernel/src/tess on geom's pcurve path until the tess move
crates/kernel/src/tess is moving into solidcore-tess in its own pull request, so its three
pcurve::eval call sites are not edited here: geom::brep::pcurve re-exports
solidcore_ops::pcurve::eval for them, and goes when the tess move switches them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore/bench/step: the STEP reader bake-off harness
fetch.sh builds the corpus at run time from pinned sources (SolidCraft's STEP tests,
dan335/step-conformance, solidcore's fixtures, cadmpeg's fixtures, the NIST PMI and STEP Tools
sets through their SHA-256 pinned fetch scripts), folded by SHA-256; nothing downloaded is
committed. run.py runs readers in the solidcraft-cli eval shape with a timeout and writes
per-file.tsv and summary.md: opened, refused, timed out, all bodies closed, reference volumes
within 0.1% (references.tsv, from formulas), and wall time.
On main cc55f59bf2: 319 files, 195 opened, 135 with every body closed, 23 of 24 reference
volumes, median 0.20 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move SolidCraft's STEP reader into solidcore (pure git mv)
crates/step moves unchanged to solidcore/crates/solidcore-step, and its Part 21 parser to
solidcore-record/src/step/p21.rs, where it takes the place of record's earlier port of this same
file (from a5b71ca2). This commit is the move alone, so the files keep their history and
authors; the next commits make them build in their new places and switch SolidCraft to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-record: the Part 21 parser keeps record's hardening
The moved parser takes back what record's port added in review (#2591): typed errors with the
same messages, entities in id order (BTreeMap), capped parameter and record counts, linear-time
decoding of unterminated escapes, a byte order mark skipped. The file's content is record's as
before the move; its history now runs through SolidCraft's parser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: the moved reader as a solidcore workspace crate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: evaluate and form records through the kernel
The moved reader called solidcraft-geom's record evaluator, exact forms and vector helpers. It
now calls the kernel's: solidcore::eval's CurveEval, SurfaceEval, face_flux and form, through a
private `kernel` module that converts records with solidcore-convert, and the kernel's vector
measures on the record's Vec3 (`vec`). geom's form was a copy of the kernel's, function for
function; the face box (`extent`) is the kernel's with poles taken exactly. The B-spline cut is
the kernel's (eval::pcurve::segment), the pole test the pcurve's speed rule, both made public
for this. Profiling spans, which were SolidCraft's, are left out.
The crate keeps SolidCraft's formatting (its own rustfmt.toml) and lints under solidcore's hard
rules: no unsafe, unwrap, expect, panic or unchecked indexing (the 33 indexing sites are
rewritten with iterators and checked access, results unchanged). The step-conformance test reads
SolidCraft's tests/step-conformance where the tree has it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* SolidCraft reads and writes STEP through solidcore-step
crates/kernel's STEP import (step_in), STEP export (step_out) and IGES export (iges_out) call
solidcore_step where they called solidcraft_step; crates/step is gone. The Part 21 parse error is
the record's typed error, shown with the same message. solidcore-step joins the crates
SolidCraft may use directly (xtask/solidcore-public.txt) until its build is kernel primitives
under an ops recipe (RFC 0003, 1.6).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-step: bound faces with the pcurves' pole test (form::extent_by)
The bake-off showed the reader on the kernel's form::extent refusing 7 STEP Tools parts
(304-310 boxy_with_*: "loop encloses no (u, v) area") and reading the large parts 2-8x slower
(rear 37.8 s against 18.4 s): extent's exact collapse test classifies the boundary points
differently from the speed rule the reader's faces were always bounded with, and costs a
collapse search per point (the cost in #13655). extent's body now takes the pole test
(form::extent_by; extent keeps the exact one), and the reader passes the pcurves' speed rule.
On those files the result and time are base's again: boxy_with_cylindricity 174839.178,
nist_ftc_06 3323556.516, rear 14.0 s against 16.3 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Kernel rig: drop the gone crates/step paths
crates/step moved to solidcore/crates/solidcore-step, which the shared core (PATHS-ALL:
solidcore/**) already covers: the import and crash areas' PATHS and kernel-rig.yml drop it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cargo.lock: solidcraft-step is now solidcore-step
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* io: the model cache key no longer names crates/step
The reader moved to solidcore/crates/solidcore-step, which the cache already keys as part of the solidcore tree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: a whole turn a rounding short of a quarter line has its form from it (#12760)
Port of #13535's fix in crates/geom's form to the kernel's form, which the STEP reader now uses: a cylinder, cone, sphere or torus face of one whole turn whose seam reads back a rounding short of a quarter line started its form a quarter early, and a tube read back through STEP measured 14 % over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* pcurve docs: a pcurve without a closed form is refused, not fitted
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* bench/step: a malformed --reader is an error; a reader's bad JSON is a refusal, not a crash
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: a boolean skips curve intersections of proto edges whose boxes are apart (#14329)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: a boolean skips curve intersections of proto edges whose boxes are apart (#14329)
A boolean splits its proto edges where they cross by intersecting every pair
of them. Two lines go to IntersectWithLine, whose first test answers "no
solution" when the segments' boxes (one grown by the tolerance) are apart.
That test now runs before the intersection's setup, through the same helper,
so the pairs it rules out cost a box test instead of an intersection. With
two coincident outlines of thousands of lines nearly every pair is such a
pair.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: clippy for the proto edge box test, and the counts it saves in its test (#14329)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14388 (indent the licences under Licences in About)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* About: indent the licences under Licences so they read as its contents
NOTICE, MIT, Apache 2.0 and Third-party licences sat at the same indent as
Licences, so after opening it they looked like more sections beside it.
They are now indented by the theme's 14 pt spacing, as the Model
Parameters groups are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An agent driving the app through MCP asked for selection.get and got
"unknown command": the selection was only reachable inside the large
document.inspect output (raw) or the UI-only ui.selection control method.
selection.get returns {count, items}: each item as select.set takes it
(so it can be fed back), with kind and, unless detail: false, what it
resolves to: faces' persistent name, surface (plane normal / cylinder
axis+radius / cone / sphere / torus) and area; edges' persistent name,
curve (line/circle/arc), length, ends and centre/radius; bodies' volume,
creating feature and visibility; component and occurrence path in an
assembly; sketch entities' sketch and entity kind. It changes nothing
(no revision bump, no undo step) and the agent chat gate classes it as
read-only. Documented in docs/control-protocol.md; tests sit with the
other selection commands' in cmd/edit_tests.rs.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 14:38:08 +00:00
678 changed files with 31652 additions and 14990 deletions
"the halves of each swept wall lie level along every axis, so which is `#1` and which `#2` follows the order the kernel gives the faces in, which changes from one build to the next",
)];
/// The designs that take longest to build, heaviest first, with one build's time (ci profile,
/// CLI `run`, one design at a time, on P-cores, 2026-11-10). The test hands them out first, and
/// each design's two builds go to two workers, so a heavy design is never built twice in a row on
/// one worker while the rest wait. Every other design took under 3.6 s. Only the order of the
/// work depends on this list: a stale entry makes the test slower, never wrong. Re-measure when a
/// design here gets much faster, or one not listed takes over 5 s.
"ai.http.warning":"سيُرسَل مفتاح API والمحادثة دون تشفير. يمكن لأي شخص لديه وصول إلى الشبكة قراءتهما أو تغييرهما. استخدم هذا الخيار فقط على شبكة تثق بها.",
"ai.prefs.allow_control_note":"قناة محلية (MCP). لا شيء يغادر هذا الحاسوب.\n\nيفتح قناة تحكم على هذا الحاسوب فقط (127.0.0.1، المنفذ 27878 أو أول منفذ متاح بعده). على البرنامج الذي يتصل أن يقدّم أيضًا رمزًا سريًا لا يقرؤه إلا حساب المستخدم الخاص بك، ويُنشأ من جديد عند كل تشغيل. يُطبّق في المرة القادمة التي يبدأ فيها SolidCraft. عند الإيقاف: لا شيء يستمع ما لم تشغّل SolidCraft مع --control.",
"ai.prefs.allow_control_note":"قناة محلية (MCP). لا شيء يغادر هذا الحاسوب.\n\nيفتح قناة تحكم على هذا الحاسوب فقط (127.0.0.1، على المنفذ أدناه؛ وإذا كان 27878 مستخدمًا فيُستخدم المنفذ الحر التالي). على البرنامج الذي يتصل أن يقدّم أيضًا رمزًا سريًا لا يقرؤه إلا حساب المستخدم الخاص بك، ويُنشأ من جديد عند كل تشغيل. يسري فورًا. عند الإيقاف: لا شيء يستمع ما لم تشغّل SolidCraft مع --control.",
"ai.prefs.availability":"التوفر",
"ai.prefs.background_commands":"تشغيل الأوامر البطيئة في الخلفية",
"ai.prefs.connect_apps":"إضافة SolidCraft إلى تطبيقات الذكاء الاصطناعي",
@@ -1890,13 +1890,13 @@
"help.sketch.construction_line":"أنشئ خطًا إنشائيًا لا نهائيًا يمر بنقطتين. استخدمه لتخطيط الرسم دون تقسيم مقاطعه.",
"help.sketch.construction_line.angle":"أنشئ خطًا إنشائيًا لا نهائيًا بزاوية من محور X للرسم. اختر النقطة ثم اكتب الزاوية، أو اختر اتجاهه.",
"help.sketch.construction_line.bisector":"اختر خطين مستقيمين متقاطعين في الرسم، ثم اختر أحد منصفي الزاوية. يتبع الناتج الخطين اللذين أُنشئ منهما.",
"help.sketch.construction_line.horizontal":"أنشئ خطًا إنشائيًا أفقيًا لا نهائيًا يمر بنقطة. يحافظ القيد الأفقي على الاتجاه بعد التعديلات.",
"help.sketch.construction_line.parallel":"اختر خطًا مستقيمًا في الرسم ثم ضع خطًا إنشائيًا لا نهائيًا موازيًا له. انقر بزر الفأرة الأيمن لتغيير المرجع.",
"help.sketch.construction_line.horizontal":"أنشئ خطًا إنشائيًا أفقيًا لا نهائيًا يمر بنقطة، أو أدخل بعده عن المحور X للرسم. يصبح البعد المُدخل بُعدًا قائدًا. يحافظ القيد الأفقي على الاتجاه بعد التعديلات.",
"help.sketch.construction_line.parallel":"اختر خطًا مستقيمًا في الرسم ثم ضع خطًا إنشائيًا لا نهائيًا موازيًا له، أو أدخل إزاحته. تصبح الإزاحة المُدخلة مسافة قائدة. انقر بزر الفأرة الأيمن لتغيير المرجع.",
"help.sketch.construction_line.perpendicular":"اختر خطًا مستقيمًا في الرسم ثم ضع خطًا إنشائيًا لا نهائيًا عموديًا عليه. انقر بزر الفأرة الأيمن لتغيير المرجع.",
"help.sketch.construction_line.tangent_at":"أنشئ مماسًا عند نقطة على دائرة أو قوس دائري، أو عند نهاية منحنى شريحي مفتوح أو قطع مخروطي مفتوح. اختر المنحنى ثم نقطة التماس.",
"help.sketch.construction_line.tangent_external":"اختر دائرة أو قوسًا دائريًا، ثم نقطة خارجية وحل المماس. تعرض المعاينة نقاط التماس الواقعة على القوس المرسوم فقط.",
"help.sketch.construction_line.tangent_two":"اختر دائرتين أو قوسين دائريين ثم اختر مماسًا مشتركًا من المعاينة. يجب أن تقع نقطتا التماس على المنحنيين المرسومين.",
"help.sketch.construction_line.vertical":"أنشئ خطًا إنشائيًا رأسيًا لا نهائيًا يمر بنقطة. يحافظ القيد الرأسي على الاتجاه بعد التعديلات.",
"help.sketch.construction_line.vertical":"أنشئ خطًا إنشائيًا رأسيًا لا نهائيًا يمر بنقطة، أو أدخل بعده عن المحور Y للرسم. يصبح البعد المُدخل بُعدًا قائدًا. يحافظ القيد الرأسي على الاتجاه بعد التعديلات.",
"help.sketch.copy":"ينسخ منحنيات الرسم ونقاطه المحددة مع القيود والأبعاد بينها. الصقها في هذا الرسم أو في رسم آخر.",
"help.sketch.create":"يبدأ رسمًا ثنائي الأبعاد جديدًا على مستوى أو على وجه مسطح من النموذج. يبدأ كل بثق ودوران وكنس برسم.",
"help.sketch.cut":"ينسخ هندسة الرسم المحددة ويحذفها من الرسم بخطوة تراجع واحدة. الصقها في مكان آخر من هذا الرسم أو في رسم آخر.",
@@ -3224,10 +3224,10 @@
"render.category_paint":"طلاء",
"render.category_plastic":"بلاستيك",
"render.category_wood":"خشب",
"render.caustics_area_lights":"تأثيرات الضوء المركّز من الأضواء المساحية",
"render.caustics_area_lights":"تجمّع الضوء من الأضواء المساحية",
"render.caustics_depth":"عمق مسار تجمّع الضوء",
"render.caustics_enable":"تجمّع الضوء عبر الزجاج من مصادر نقطية واتجاهية",
"render.caustics_environment":"تأثيرات الضوء المركّز من البيئة",
"render.caustics_environment":"تجمّع الضوء من البيئة",
"render.caustics_help":"يتحكم عدد الفوتونات في الضجيج وكلفة الحساب. يوازن نصف قطر التجميع بين التفاصيل الدقيقة والتقدير الأنعم. يتطلب CPU إضاءة غير مباشرة.",
"render.caustics_path_required":"يتطلب تجمّع الضوء على CPU إضاءة غير مباشرة.",
"render.caustics_photons":"عدد الفوتونات",
@@ -3235,7 +3235,7 @@
"render.check_glass_cpu":"يدعم CPU صفائح زجاج مستوية وصناديق محاذية للمحاور بعد التحقق منها. تتطلب الشبكات المغلقة العامة والتعشيق والتشتت الداخلي إضاءة غير مباشرة. لا تزال الظلال النافذة عبر الزجاج المغلق غير مدعومة.",
"render.check_glass_gpu":"يدعم GPU أوساط زجاج منفصلة متحققًا منها، أو تعشيقًا متحققًا منه عند تفعيله صراحةً. يتطلب التعشيق أغلفة خارجية محدبة وأجسامًا داخلية محتواة بالكامل وحدودًا غير متلامسة.",
"render.check_media_remaining":"لا تزال شبكات الكثافة والإزاحة والتكامل الطيفي الكامل ونقل حالة الاستقطاب غير متاحة.",
"render.check_media_scope":"يتطلب التشتت الداخلي النشط وسطًا زجاجيًا مغلقًا مع إيقاف نفاذ أشعة الظل المستقيمة وإزالة الضجيج. ويتطلب GPU أيضًا طبقة عينات فرينل واحدة. لا يمكن جمع الغلاف الجوي الخارجي النشط مع الزجاج أو إزالة ضجيج الأسطح.",
"render.check_media_scope":"يتطلب التشتت الداخلي النشط وسطًا زجاجيًا مغلقًا مع إيقاف نفاذ أشعة الظل المستقيمة وإزالة التشويش. ويتطلب GPU أيضًا طبقة عينات فرينل واحدة. لا يمكن جمع الغلاف الجوي الخارجي النشط مع الزجاج أو إزالة تشويش الأسطح.",
"render.cpu_studio_enable_apply":"تفعيل الإضاءة غير المباشرة وتطبيقها",
"render.cpu_studio_indirect_help":"يحتاج توزيع الشمس هذا إلى إضاءة غير مباشرة بعمق 8 وظلال متتبعة وعينة كاميرا واحدة لكل مرور. إذا كان التصيير التدريجي معطلًا، يبدأ بـ 16 عينة لكل بكسل وبذرة 0. تُحفظ الإعدادات التدريجية الحالية وملف الموارد. تمنع الإعدادات غير المتوافقة التطبيق دون تغييرها.",
"render.cpu_studio_radiance_limit":"سطوع المصدر المساحي في هذا التوزيع هو {radiance}، ويتجاوز حد CPU البالغ 64. تُحفظ الإضاءة المؤلَّفة؛ اختر توزيعًا آخر أو GPU.",
"render.cpu_subsurface_denoise_reason":"يتطلب التشتت داخل الزجاج على CPU إيقاف إزالة الضجيج. تبقى إعداداتك محفوظة.",
"render.cpu_subsurface_help":"يغيّر التشتت اتجاه الضوء داخل الزجاج المغلق. تتطلب المعدلات الموجبة إضاءة غير مباشرة مع إيقاف إزالة الضجيج. المعدل هو عدد التصادمات لكل مم؛ لا يضيف الصفر أي تشتت. يفضّل انحياز الاتجاه الموجب الأمام، والسالب الخلف.",
"render.cpu_subsurface_denoise_reason":"يتطلب التشتت داخل الزجاج على CPU إيقاف إزالة التشويش. تبقى إعداداتك محفوظة.",
"render.cpu_subsurface_help":"يغيّر التشتت اتجاه الضوء داخل الزجاج المغلق. تتطلب المعدلات الموجبة إضاءة غير مباشرة مع إيقاف إزالة التشويش. المعدل هو عدد التصادمات لكل مم؛ لا يضيف الصفر أي تشتت. يفضّل انحياز الاتجاه الموجب الأمام، والسالب الخلف.",
"render.cpu_summary":"تتبع الأشعة على CPU · انعكاسات · HDR · أضواء اتجاهية ونقطية",
"render.cpu_threads":"خيوط المعالجة",
"render.cpu_threads_auto":"تلقائي",
@@ -3458,40 +3458,40 @@
"render.cpu_volume_method_incompatible":"لا يمكن نقل إعدادات الإضاءة أو أخذ العينات أو الغلاف الجوي هذه إلى هذه الطريقة. تبقى إعداداتك محفوظة.",
"render.cpu_volume_path_required":"فعّل الإضاءة غير المباشرة لاستخدام الغلاف الجوي على CPU.",
"render.denoise_diagnostics_grid":"أعداد البكسلات بالحجم الداخلي {width} × {height}.",
"render.denoise_diagnostics_help":"يجمع أعداد البكسلات المؤهلة والمتجاوزة حتى عند إيقاف إزالة الضوضاء. تعتمد الأعداد على حجم التصيير الداخلي، ويعتمد القناع على حجم الإخراج.",
"render.denoise_diagnostics_help":"يجمع أعداد البكسلات المؤهلة والمستبعدة حتى عند إيقاف إزالة التشويش. تعتمد الأعداد على حجم التصيير الداخلي، ويعتمد القناع على حجم الإخراج.",
"render.denoise_optical_filter_help":"تصفية الضوء المنعكس والنافذ المؤهّل. أوقف هذا الخيار لإزالة الضجيج من الأسطح المعتمة فقط.",
"render.denoise_passes":"مرات الترشيح",
"render.denoise_settings_help":"تُنعّم مرات الترشيح الإضافية مساحة أوسع. تحدد القوة مقدار الترشيح المطبّق في كل مرة. تبقى المسارات البصرية غير المدعومة دون ترشيح.",
"render.denoise_strength":"قوة إزالة الضوضاء",
"render.denoise_mask_copy_help":"صدّر قناع إزالة التشويش بصيغة PNG، أو عُد إلى الصورة المصيّرة لنسخها.",
"render.denoise_mask_unavailable":"فعّل تشخيصات إزالة التشويش وصيّر صورة لفحص قناعها.",
"render.denoise_optical_filter_help":"يرشّح الضوء المنعكس والنافذ المؤهّل. أوقف هذا الخيار لإزالة التشويش من الأسطح المعتمة فقط.",
"render.denoise_passes":"تمريرات الترشيح",
"render.denoise_settings_help":"تُنعّم تمريرات الترشيح الإضافية مساحة أوسع. تحدد القوة مقدار الترشيح الذي يطبّقه كل تمرير. تبقى المسارات البصرية غير المدعومة دون ترشيح.",
"render.denoise_strength":"قوة إزالة التشويش",
"render.denoise_view_image":"الصورة المصيّرة",
"render.denoise_view_mask":"قناع إزالة الضوضاء",
"render.denoise_view_mask":"قناع إزالة التشويش",
"render.diag_bodies":"الأجسام المرئية",
"render.diag_job_details":"تفاصيل المهمة الملتقطة",
"render.diag_last_check":"آخر فحص مسبق",
@@ -4047,7 +4047,7 @@
"render.material_v_up":"اتجاه V للنسيج يشير للأعلى في الصورة",
"render.texture_filtering_help":"استخدم مستويات التفاصيل والترشيح المتباين لتقليل تعرّج الخامات عند التصغير. يزيد استهلاك الذاكرة؛ يبقى الوضع القديم معطّلاً.",
"render.texture_filtering_help":"استخدم مستويات MIP والترشيح متباين الخواص لتقليل تعرّج الخامات عند التصغير. يستهلك ذاكرة أكبر؛ وعند إيقافه يبقى الترشيح السابق.",
"render.tone_aces_fitted":"ACES مُلاءَم",
"render.tone_reinhard":"راينهارد",
"render.transmission_scope":"يدعم زجاج GPU الأوساط المنفصلة المعتمدة. لا تتوفر الأوساط المتداخلة غير المقيدة ولا مقدّر نفاذ خالٍ من التشويش.",
@@ -4552,7 +4552,7 @@
"render.use_preview":"استخدام المعاينة المدمجة",
"render.volume_albedo":"بياض التشتت · RGB خطي",
"render.volume_anisotropy":"متوسط جيب تمام الطور",
"ui.agent_chat_pick_context_tip":"اختر أوجهًا وحوافًا وميزات وأجسامًا في النموذج ليطّلع عليها الوكيل، ثم اضغط «موافق»",
"ui.agent_chat_pick_context_tip":"اختر أوجهًا وحوافًا وميزات وأجسامًا في النموذج، ثم اضغط «موافق»: تُدرج في الرسالة كشارة في موضع المؤشر (كتابة @ تفعل الشيء نفسه)",
"ui.agent_chat_placeholder":"اطلب من الوكيل تغيير التصميم…",
"ui.agent_chat_placeholder_busy":"ضع رسالة في قائمة الانتظار لحين انتهاء الوكيل (Ctrl+Enter: أرسل الآن)",
"ui.agent_chat_pop_out":"فتح في نافذة مستقلة يمكنك نقلها إلى شاشة أخرى",
"ai.http.warning":"The API key and conversation will be sent unencrypted. Anyone with access to the network may read or change them. Use this only on a network you trust.",
"ai.prefs.allow_control":"Let AI agents connect",
"ai.prefs.allow_control_note":"A local channel (MCP). Nothing leaves this computer.\n\nOpens a control channel on this computer only (127.0.0.1, port 27878 or the next free one). A program that connects must also show a secret token that only your user account can read, made fresh at every start. Takes effect the next time SolidCraft starts. Off: nothing listens unless you start SolidCraft with --control.",
"ai.prefs.allow_control_note":"A local channel (MCP). Nothing leaves this computer.\n\nOpens a control channel on this computer only (127.0.0.1, on the port below; 27878 takes the next free one when it is in use). A program that connects must also show a secret token that only your user account can read, made fresh at every start. Takes effect at once. Off: nothing listens unless you start SolidCraft with --control.",
"ai.prefs.availability":"Availability",
"ai.prefs.background_commands":"Run slow commands in the background",
"ai.prefs.connect_apps":"Add SolidCraft to AI apps",
"prefs.constraint_glyph_size_note":"How large constraint glyphs and their letters or icons are drawn in a sketch. 100% is the usual size; glyphs keep this screen size at every zoom."
},
"ar":{
"prefs.constraint_glyph_size":"حجم رموز القيود",
"prefs.constraint_glyph_size_note":"حجم رسم رموز القيود وحروفها أو أيقوناتها في الرسم. 100% هو الحجم المعتاد؛ وتحتفظ الرموز بهذا الحجم على الشاشة عند أي مستوى تكبير."
},
"de":{
"prefs.constraint_glyph_size":"Größe der Bindungssymbole",
"prefs.constraint_glyph_size_note":"Wie groß Bindungssymbole samt Buchstaben oder Icons in einer Skizze gezeichnet werden. 100 % ist die übliche Größe; die Symbole behalten diese Bildschirmgröße bei jedem Zoom."
},
"fr":{
"prefs.constraint_glyph_size":"Taille des symboles de contraintes",
"prefs.constraint_glyph_size_note":"Taille à laquelle les symboles de contraintes, avec leurs lettres ou icônes, sont dessinés dans une esquisse. 100 % est la taille habituelle ; les symboles gardent cette taille à l'écran quel que soit le zoom."
"prefs.constraint_glyph_size_note":"הגודל שבו מצוירים סמלי האילוצים, עם האותיות או הסמלונים שלהם, בשרטוט. 100% הוא הגודל הרגיל; הסמלים שומרים על גודל המסך הזה בכל זום."
"prefs.constraint_glyph_size_note":"Kích thước vẽ biểu tượng ràng buộc cùng chữ cái hoặc biểu tượng bên trong trong bản phác. 100% là kích thước thông thường; biểu tượng giữ kích thước màn hình này ở mọi mức thu phóng."
"help.construct.axis":"Creates a construction axis with the method your picks call for: along an edge or line, through a hole or round face, where two planes meet, through two points, or square to a face through a point. Use it when you know the references and not the method.",
"help.construct.plane":"Creates a construction plane with the method your picks call for: offset from a plane, through a point, between two planes, through lines or points, tangent to a curved face, or square to a path. Use it when you know the references and not the method.",
"help.construct.point":"Creates a construction point with the method your picks call for: at a vertex, at the centre of a circle or sphere, along an edge, where two lines or three planes meet, or where a line crosses a plane. Use it when you know the references and not the method.",
"ui.construct_auto.chosen":"Chosen from your picks. Pick another reference, or take one out, to change the method.",
"ui.construct_auto.no_fit":"Pick other references: no method builds from these.",
"ui.construct_auto.pick":"Pick what to build from: the method follows from your picks.",
"ui.construct_auto.refs":"References",
"ui.construct_auto.select":"Select references to build from first"
"help.construct.axis":"ينشئ محورًا إنشائيًا بالطريقة التي تناسب ما تختاره: على امتداد حافة أو خط، أو عبر ثقب أو وجه دائري، أو عند تقاطع مستويين، أو عبر نقطتين، أو عموديًا على وجه عبر نقطة. استخدمه عندما تعرف المراجع ولا تعرف الطريقة.",
"help.construct.plane":"ينشئ مستوى إنشائيًا بالطريقة التي تناسب ما تختاره: إزاحة عن مستوى، أو عبر نقطة، أو بين مستويين، أو عبر خطوط أو نقاط، أو مماسًا لوجه منحنٍ، أو عموديًا على مسار. استخدمه عندما تعرف المراجع ولا تعرف الطريقة.",
"help.construct.point":"ينشئ نقطة إنشائية بالطريقة التي تناسب ما تختاره: عند رأس، أو في مركز دائرة أو كرة، أو على امتداد حافة، أو عند تقاطع خطين أو ثلاثة مستويات، أو حيث يقطع خط مستوى. استخدمه عندما تعرف المراجع ولا تعرف الطريقة.",
"ui.construct_auto.chosen":"اختيرت الطريقة من اختياراتك. اختر مرجعًا آخر أو أزل مرجعًا لتغيير الطريقة.",
"ui.construct_auto.no_fit":"اختر مراجع أخرى: لا توجد طريقة تبني من هذه المراجع.",
"ui.construct_auto.pick":"اختر ما تبني منه: الطريقة تتبع اختياراتك.",
"ui.construct_auto.refs":"المراجع",
"ui.construct_auto.select":"اختر أولًا المراجع التي تبني منها"
},
"he":{
"cmd.construct.axis":"ציר",
"cmd.construct.plane":"מישור",
"cmd.construct.point":"נקודה",
"help.construct.axis":"יוצר ציר עזר בשיטה שמתאימה למה שבחרת: לאורך קצה או קו, דרך חור או פאה עגולה, במפגש של שני מישורים, דרך שתי נקודות, או ניצב לפאה דרך נקודה. השתמש בו כשאתה יודע את ההפניות ולא את השיטה.",
"help.construct.plane":"יוצר מישור עזר בשיטה שמתאימה למה שבחרת: בהיסט ממישור, דרך נקודה, בין שני מישורים, דרך קווים או נקודות, משיק לפאה מעוקלת, או ניצב למסלול. השתמש בו כשאתה יודע את ההפניות ולא את השיטה.",
"help.construct.point":"יוצר נקודת עזר בשיטה שמתאימה למה שבחרת: בקודקוד, במרכז של מעגל או כדור, לאורך קצה, במפגש של שני קווים או שלושה מישורים, או במקום שבו קו חוצה מישור. השתמש בו כשאתה יודע את ההפניות ולא את השיטה.",
"ui.construct_auto.chosen":"השיטה נבחרה לפי הבחירות שלך. בחר הפניה נוספת, או הסר אחת, כדי לשנות את השיטה.",
"ui.construct_auto.no_fit":"בחר הפניות אחרות: אין שיטה שבונה מאלה.",
"ui.construct_auto.pick":"בחר ממה לבנות: השיטה נקבעת לפי הבחירות שלך.",
"ui.construct_auto.refs":"הפניות",
"ui.construct_auto.select":"בחר קודם את ההפניות לבנייה"
},
"vi":{
"cmd.construct.axis":"Trục",
"cmd.construct.plane":"Mặt phẳng",
"cmd.construct.point":"Điểm",
"help.construct.axis":"Tạo trục dựng theo phương pháp phù hợp với những gì bạn chọn: dọc theo cạnh hoặc đường, qua lỗ hoặc mặt tròn, nơi hai mặt phẳng giao nhau, qua hai điểm, hoặc vuông góc với mặt qua một điểm. Dùng khi bạn biết các tham chiếu nhưng không biết phương pháp.",
"help.construct.plane":"Tạo mặt phẳng dựng theo phương pháp phù hợp với những gì bạn chọn: cách một mặt phẳng, qua một điểm, giữa hai mặt phẳng, qua các đường hoặc điểm, tiếp xúc với mặt cong, hoặc vuông góc với đường dẫn. Dùng khi bạn biết các tham chiếu nhưng không biết phương pháp.",
"help.construct.point":"Tạo điểm dựng theo phương pháp phù hợp với những gì bạn chọn: tại đỉnh, tại tâm đường tròn hoặc mặt cầu, dọc theo cạnh, nơi hai đường hoặc ba mặt phẳng giao nhau, hoặc nơi một đường cắt mặt phẳng. Dùng khi bạn biết các tham chiếu nhưng không biết phương pháp.",
"ui.construct_auto.chosen":"Đã chọn phương pháp theo lựa chọn của bạn. Chọn thêm hoặc bỏ một tham chiếu để đổi phương pháp.",
"ui.construct_auto.no_fit":"Hãy chọn tham chiếu khác: không phương pháp nào dựng được từ các tham chiếu này.",
"ui.construct_auto.pick":"Chọn những gì để dựng: phương pháp theo lựa chọn của bạn.",
"ui.construct_auto.refs":"Tham chiếu",
"ui.construct_auto.select":"Hãy chọn tham chiếu để dựng trước"
"prefs.dialog_first_letter_buttons":"Dialog buttons answer their first letter",
"prefs.dialog_first_letter_buttons_help":"With a dialog open, typing the first letter of a button's label presses it: O for OK, C for Cancel. A letter that fits two buttons does nothing, and a focused text field keeps the keystroke. Off: typing does nothing to the buttons, as before."
"prefs.dialog_first_letter_buttons_help":"عند فتح نافذة حوار، كتابة الحرف الأول من تسمية الزر تضغطه: حرف «ا» يضغط زر «إلغاء» مثلًا. الحرف الذي يناسب زرَّين لا يفعل شيئًا، وحقل النص المركَّز عليه يحتفظ بالتضغطة. عند الإيقاف: الكتابة لا تفعل شيئًا للأزرار، كما كان سابقًا."
},
"de":{
"prefs.dialog_first_letter_buttons":"Dialogschaltflächen reagieren auf ihren Anfangsbuchstaben",
"prefs.dialog_first_letter_buttons_help":"Ist ein Dialog geöffnet, drückt der Anfangsbuchstabe einer Beschriftung die zugehörige Schaltfläche: O für OK, A für Abbrechen. Ein Buchstabe, der zu zwei Schaltflächen passt, tut nichts, und ein fokussiertes Textfeld behält die Tasteneingabe. Aus: Eingaben drücken keine Schaltfläche, wie bisher."
},
"he":{
"prefs.dialog_first_letter_buttons":"כפתורי תיבות דו-שיח נענים לאות הראשונה שלהם",
"prefs.dialog_first_letter_buttons_help":"כשתיבת דו-שיח פתוחה, הקלדת האות הראשונה של שם הכפתור לוחצת עליו: האות «ב» לוחצת על «ביטול», למשל. אות שמתאימה לשני כפתורים אינה עושה דבר, ושדה טקסט שבו נמצא הסמן שומר את ההקלדה. כבוי: הקלדה אינה משפיעה על הכפתורים, כמו קודם."
"prefs.dialog_first_letter_buttons":"다이얼로그 단추가 첫 글자에 반응",
"prefs.dialog_first_letter_buttons_help":"다이얼로그가 열려 있을 때 단추 레이블의 첫 글자를 입력하면 그 단추를 누릅니다: 예를 들어 「ㅊ」은 「취소」를 누릅니다. 두 단추에 모두 해당하는 글자는 아무 동작을 하지 않으며, 포커스된 텍스트 필드는 입력을 그대로 받습니다. 끔: 기존처럼 입력이 단추에 영향을 주지 않습니다."
},
"vi":{
"prefs.dialog_first_letter_buttons":"Nút trong hộp thoại trả lời chữ cái đầu của nhãn",
"prefs.dialog_first_letter_buttons_help":"Khi một hộp thoại đang mở, gõ chữ cái đầu của nhãn một nút sẽ bấm nút đó: chữ H bấm nút Hủy bỏ, chẳng hạn. Một chữ khớp với hai nút không thực hiện gì, và trường văn bản đang tiêu điểm giữ lại phím gõ. Tắt: gõ phím không bấm nút, như trước đây."
"prefs.export_name.body":"اسم الجسم المُصدَّر (الأول إن تعددت الأجسام)",
"prefs.export_name.design":"اسم التصميم",
"prefs.export_name_hint":"مثلاً {design}-{body}",
"prefs.export_name_hint":"مثلًا {design}-{body}",
"prefs.export_name_sample":"تصدير هذا التصميم يقترح: {name}",
"prefs.export_name_tip":"اسم الملف الذي يقترحه ملف › تصدير وحفظ كشبكة. تُستبدل هذه العناصر النائبة:\n{placeholders}\nتُحذف خاصية التصميم غير المعيّنة. اترك الحقل فارغاً لاقتراح اسم التصميم، أو اسم الجسم للشبكة."
"prefs.export_name_tip":"اسم الملف الذي يقترحه «ملف › تصدير» و«حفظ كشبكة». تُستبدل هذه العناصر النائبة:\n{placeholders}\nتُحذف خاصية التصميم غير المعيّنة. اترك الحقل فارغًا لاقتراح اسم التصميم، أو اسم الجسم للشبكة."
"prefs.fillet_legacy_radius":"استخدم نصف قطر Fillet الصلب الافتراضي السابق",
"prefs.fillet_legacy_radius.help":"مطفأ افتراضيًا. عند التشغيل، يفتح Fillet الصلب الجديد عند الرقم 2 مقروءًا بوحدات التصميم (\"2 بوصة\" في تصميم بالبوصة)، كما كان سابقًا؛ وعند الإيقاف، يفتح عند نصف القطر الذي تُسلَّم به الأداة، 2 مم، معروضًا بوحدات التصميم (\"0.07874 بوصة\" في تصميم بالبوصة)."
"prefs.fillet_legacy_radius":"استخدام نصف القطر الافتراضي السابق للتقويس المصمت",
"prefs.fillet_legacy_radius.help":"معطّل افتراضيًا. عند التفعيل، يفتح التقويس المصمت الجديد على الرقم 2 مقروءًا بوحدات التصميم («2 بوصة» في تصميم بالبوصة)، كما كان سابقًا؛ وعند الإيقاف، يفتح على نصف القطر الافتراضي للأداة، 2 مم، معروضًا بوحدات التصميم («0.07874 بوصة» في تصميم بالبوصة)."
},
"de":{
"prefs.fillet_legacy_radius":"Den vorherigen Standardradius für Solid-Fillet verwenden",
"ui.first_dimension_scales_about_origin_tip":"On: the drawing always grows or shrinks about the sketch origin, so a shape drawn away from the origin moves. Off: a drawing that nothing ties to the origin scales about its own middle and stays where it was drawn."
},
"ar":{
"ui.first_dimension_scales_about_origin":"تغيير المقياس حول أصل الرسم التخطيطي",
"ui.first_dimension_scales_about_origin_tip":"عند التفعيل: يكبر الرسم أو يصغر دائمًا حول أصل الرسم التخطيطي، فيتحرك الشكل المرسوم بعيدًا عن الأصل. عند الإيقاف: الرسم غير المرتبط بالأصل يتغير مقياسه حول منتصفه ويبقى في مكانه."
"ui.first_dimension_scales_about_origin":"تغيير المقياس حول أصل الرسم",
"ui.first_dimension_scales_about_origin_tip":"عند التفعيل: يكبر المرسوم أو يصغر دائمًا حول أصل الرسم، فإذا كان الشكل مرسومًا بعيدًا عن الأصل تحرّك. عند الإيقاف: المرسوم غير المرتبط بالأصل يتغير مقياسه حول منتصفه ويبقى حيث رُسم."
},
"de":{
"ui.first_dimension_scales_about_origin":"Um den Skizzenursprung skalieren",
"prefs.graphics_api_save_failed":"تعذّر حفظ اختيار واجهة الرسوميات: {error}",
@@ -29,7 +29,7 @@
"prefs.graphics_api_start_reverted":"لم يفتح التشغيل السابق باستخدام {requested} النافذة. استعاد SolidCraft الاختيار {previous}.",
"prefs.graphics_api_unavailable":"اختيار واجهة الرسوميات متاح في تطبيق سطح المكتب.",
"render.graphics_api_restart_vulkan":"إعادة التشغيل باستخدام Vulkan",
"render.graphics_api_restart_warning":"يؤدي تغيير واجهة الرسوميات إلى إعادة تشغيل SolidCraft ويطلب منك حفظ التصميمات غير المحفوظة. إذا تعذّر فتح النافذة بالواجهة الجديدة، فسيُستعاد الاختيار السابق عند التشغيل التالي.",
"render.graphics_api_restart_warning":"يؤدي تغيير واجهة الرسوميات إلى إعادة تشغيل SolidCraft ويطلب منك حفظ التصاميم غير المحفوظة. إذا تعذّر فتح النافذة بالواجهة الجديدة، فسيُستعاد الاختيار السابق عند التشغيل التالي.",
"render.graphics_api_vulkan_required":"يتطلب تتبّع المسارات بمعالج الرسوميات Vulkan في هذه الجلسة. أعد التشغيل باستخدام Vulkan، أو واصل استخدام طريقة صور بمعالج CPU."
"nav.maya_horizontal_zoom.help":"اسحب مع Alt وزر الفأرة الأيمن: إلى اليمين للتكبير وإلى اليسار للتصغير. عطّل هذا الخيار لاستعادة التكبير بالسحب العمودي.",
"ai.prefs.control_port_note":"The port AI agents connect to on this computer (127.0.0.1). 27878 is the default: when it is in use, the next free port is taken. Any other port is used exactly. Point your MCP client at the same port, for example solidcraft-cli mcp --connect 127.0.0.1:7878.",
"ai.prefs.control_listening":"Listening on 127.0.0.1:{port}",
"ai.prefs.control_not_listening":"Not listening",
"ai.prefs.control_port_taken":"Port {port} is in use by another program (or another SolidCraft window). Choose another port.",
"ai.prefs.control_failed":"The control channel could not start: {error}",
"ai.prefs.control_from_launch":"Started with --control on port {port} for this run. Changes here take effect the next time SolidCraft starts without --control."
},
"de":{
"ai.prefs.control_port":"Port",
"ai.prefs.control_port_note":"Der Port, über den sich KI-Agenten auf diesem Computer verbinden (127.0.0.1). Standard ist 27878: Ist er belegt, wird der nächste freie Port genommen. Jeder andere Port wird genau so verwendet. Richte deinen MCP-Client auf denselben Port, zum Beispiel solidcraft-cli mcp --connect 127.0.0.1:7878.",
"ai.prefs.control_listening":"Wartet auf 127.0.0.1:{port}",
"ai.prefs.control_not_listening":"Nicht aktiv",
"ai.prefs.control_port_taken":"Port {port} wird von einem anderen Programm (oder einem anderen SolidCraft-Fenster) verwendet. Wähle einen anderen Port.",
"ai.prefs.control_failed":"Der Steuerkanal konnte nicht starten: {error}",
"ai.prefs.control_from_launch":"Für diese Sitzung mit --control auf Port {port} gestartet. Änderungen hier gelten beim nächsten Start von SolidCraft ohne --control."
"ai.prefs.control_port_note":"이 컴퓨터에서 AI 에이전트가 연결하는 포트(127.0.0.1)입니다. 기본값은 27878이며, 사용 중이면 다음 빈 포트를 사용합니다. 다른 포트는 그대로 사용합니다. MCP 클라이언트도 같은 포트를 가리키게 하세요(예: solidcraft-cli mcp --connect 127.0.0.1:7878).",
"ai.prefs.control_listening":"127.0.0.1:{port}에서 대기 중",
"ai.prefs.control_not_listening":"대기하지 않음",
"ai.prefs.control_port_taken":"포트 {port}을(를) 다른 프로그램(또는 다른 SolidCraft 창)이 사용 중입니다. 다른 포트를 선택하세요.",
"ai.prefs.control_failed":"제어 채널을 시작할 수 없습니다: {error}",
"ai.prefs.control_from_launch":"이번 실행은 --control로 포트 {port}에서 시작되었습니다. 여기서 바꾼 내용은 다음에 --control 없이 SolidCraft를 시작할 때 적용됩니다."
},
"vi":{
"ai.prefs.control_port":"Cổng",
"ai.prefs.control_port_note":"Cổng mà các tác tử AI kết nối trên máy tính này (127.0.0.1). Mặc định là 27878: nếu cổng đang được dùng, cổng trống tiếp theo sẽ được dùng. Mọi cổng khác được dùng đúng như vậy. Hãy trỏ ứng dụng MCP của bạn đến cùng cổng, ví dụ solidcraft-cli mcp --connect 127.0.0.1:7878.",
"ai.prefs.control_listening":"Đang lắng nghe trên 127.0.0.1:{port}",
"ai.prefs.control_port_taken":"Cổng {port} đang được một chương trình khác (hoặc một cửa sổ SolidCraft khác) sử dụng. Hãy chọn cổng khác.",
"ai.prefs.control_failed":"Không thể khởi động kênh điều khiển: {error}",
"ai.prefs.control_from_launch":"Lần chạy này được khởi động với --control trên cổng {port}. Các thay đổi ở đây có hiệu lực vào lần tới SolidCraft khởi động mà không có --control."
},
"ar":{
"ai.prefs.control_port":"المنفذ",
"ai.prefs.control_port_note":"المنفذ الذي يتصل عبره وكلاء الذكاء الاصطناعي على هذا الحاسوب (127.0.0.1). الافتراضي هو 27878: إذا كان مستخدمًا يُستخدم المنفذ الحر التالي. أي منفذ آخر يُستخدم كما هو. وجّه عميل MCP إلى المنفذ نفسه، مثل solidcraft-cli mcp --connect 127.0.0.1:7878.",
"ai.prefs.control_listening":"يستمع على 127.0.0.1:{port}",
"ai.prefs.control_not_listening":"لا يستمع",
"ai.prefs.control_port_taken":"المنفذ {port} يستخدمه برنامج آخر (أو نافذة SolidCraft أخرى). اختر منفذًا آخر.",
"ai.prefs.control_failed":"تعذّر بدء قناة التحكم: {error}",
"ai.prefs.control_from_launch":"بدأ هذا التشغيل باستخدام --control على المنفذ {port}. تسري التغييرات هنا عند تشغيل SolidCraft في المرة القادمة دون --control."
},
"he":{
"ai.prefs.control_port":"פורט",
"ai.prefs.control_port_note":"הפורט שאליו סוכני AI מתחברים במחשב זה (127.0.0.1). ברירת המחדל היא 27878: אם הוא תפוס, נלקח הפורט הפנוי הבא. כל פורט אחר משמש בדיוק כפי שהוא. כוונו את לקוח ה-MCP לאותו פורט, למשל solidcraft-cli mcp --connect 127.0.0.1:7878.",
"ui.right_click_confirms":"النقر بزر الماوس الأيمن يؤكد مدخلات مربع الحوار",
"ui.right_click_confirms_tip":"عندما يكون مربع حوار مفتوحاً، ينتقل النقر بزر الماوس الأيمن في العرض إلى المدخل الفارغ التالي، وعند المدخل الأخير ينفّذ موافق"
"dialog.right_click_pick":"حدد {input} أولًا",
"ui.right_click_confirms":"النقر بزر الفأرة الأيمن يؤكد مدخلات مربع الحوار",
"ui.right_click_confirms_tip":"عندما يكون مربع حوار مفتوحًا، ينتقل النقر بزر الفأرة الأيمن في العرض إلى المدخل الفارغ التالي، وعند المدخل الأخير ينفّذ «موافق»"
},
"he":{
"dialog.right_click_pick":"יש לבחור תחילה {input}",
"prefs.sketch.max_points_note":"أكبر عدد من النقاط أو المنحنيات في رسم تخطيطي واحد: 20000 افتراضيًا، و200000 كحد أقصى. يرفض إدراج SVG وإدراج DXF رسمًا أكبر. الرسم التخطيطي الكبير يُحل ويُعرض بشكل أبطأ.",
"prefs.sketch.max_points":"أكبر حجم للرسم",
"prefs.sketch.max_points_note":"أكبر عدد من النقاط أو المنحنيات في رسم واحد: 20000 افتراضيًا، و200000 كحد أقصى. يرفض «إدراج SVG» و«إدراج DXF» رسمًا أكبر. الرسم الكبير يُحل ويُعرض بشكل أبطأ.",
| assets/locales/pending/agent-chat-select-all.json | SolidCraft contributors | Original assistant chat select-all strings and translations | MIT OR Apache-2.0 |
| assets/locales/pending/constraint-glyph-size.json | SolidCraft contributors | Original constraint glyph size preference label and help text | MIT OR Apache-2.0 |
constPARAMS: &str="refs: 1-3 refs in any order; with?: {the method's other parameters: offset, angle, t, name…}. Builds with the first of the family's CONSTRUCT commands that takes the refs in its reference parameters (and every key of `with`). Returns method: the command used";
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.