* test: the default kernel's variable rounds are the union of their balls (failing, #2273)
On main the default kernel sweeps great circles: the 20 cube's top front
edge rounded by the linear law 3, 2, 4, 2, 3 comes back with a round whose
net doubles back (check_brep #5), and the folded law 2, 2, 40, 3, 2 fails
in the surface (1001) with no reason, where it is a fold of the balls
(NoRoom, BallsFold, naming 19.63563).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: the G^n section and the generic blend's checks are the default; their flags are retired (WR-685, WR-696, #2273)
`fillet_gn_section_generator` is removed and every read site takes its
side: a blend-curve round is the G^n section, a variable round is the
union of its balls (touch circles, law corners), the ease takes the
order-2 G^n section, and BallIsRegular, BandCutsThePart, the collapsed
offset and the refusals' fit sizes run on every kernel.
Retired with it, each now dead or a prerequisite of the G^n path:
- fillet_refuse_ball_not_fitting (WR-462): BallIsRegular replaced
BallFitsItsFaces, which never ran with it; ball_fit.rs is removed.
- the executive's blend_curve_at_thirds option (WR-525), which the fork
removed with its G^n section, and the ease's at-thirds target.
- fillet_blend_curve_revolved_on_arc (WR-581): only a blend-curve
generator took it, and the executive builds none now.
- fillet_surface_plane_curve_on_plane_equation (WR-682): without it a
variable round's side-face curve is not found (1001).
- fillet_corner_face_curve_halves (WR-659): an eased corner's patch does
not close against the G^n ease without the halves.
The ExecutiveAccess adapter (the orchestration mocks, which hold no
solver geometry) reports nothing to judge, as its rail check already did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend tests: the default kernel's rounds re-derived (WR-685, WR-696, WR-581, WR-659, WR-682, #2273)
The tests that set the retired flags drop the setting and keep their
on halves. Values pinned to the great-circle sweep are re-derived from
the union of the balls: wr646 against a quadrature of the envelope
(64.15953 removed; the round 64.1638, 6.7e-5 off), wr609 likewise
(5831.254415), wr631 against the fork's G^n builds. The refusals that
BallIsRegular now finds first name the same sizes (BallDoesNotFit).
The blend curve's own tests (at thirds, the revolved dome, the
great-circle marks) are removed with it. Known failures, recorded:
wr643's mirrored r 2 knife edge (#2963) and the r 9.99 G2 rim (#2964).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: the surface-plane trace stays on its surface and is bounded (WR-682, #2273)
With its flag retired the trace runs on every kernel, and the goldens'
generated blend.fillet_geom_p2 cases showed two defects it carried from
the fork:
- gen0014: the march left the surface's domain (u = -6e5 on [0, 1]),
marching the B-spline's extrapolation, and the refinement doubled
its stations each pass until the allocation aborted (4.9 GB). A
station off the domain, in a direction the surface isn't closed in,
is the curve leaving the surface: refused (1001, as main's golden).
The trace also holds at most MAX_STATIONS (the march's own 100 000).
- gen0123: start and end coincide, no curve between two points; it is
not the trace's event, and the intersectors refuse it as before (1007).
(gen0015 and gen0170, whose spans at an end never met the check while
the curve ended at the given points, build since main's #2907 ends the
curve on the crossing.)
gen0014_the_trace_stays_on_its_surface aborts without the domain event.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* style: clippy's items_after_statements and field_reassign_with_default (#2273)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: a band on a sheet has no part to cut (WR-696, #2273)
BandCutsThePart and the break-through check ask sm_BlendAddsMaterial,
whose IsConcaveEdge looks for a use of the edge on a material side and
fails (SM_ERR from fillet_solver::p5) where there is none: a sheet's
edges bound no material. The fillet failed with it on every open sheet,
on SolidCraft's set and, with the generic blend's checks now the
default, on the default kernel (the goldens' stubs4 rounds of
upstream's test8, shell_fillet, shell_fillet2, sphere_plane,
coons_01_03_29, test5 and open_shell, which main's default built).
An edge that bounds no material is its own event: nothing to judge
(edge_bounds_material). Not the fork's, which fails there.
a_round_on_a_sheet_has_no_part_to_cut fails without it with the same
"Fillet Surface Computation Failure: blend::fillet_solver::p5".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: one half of #880's rim chamfers to a valid solid on the default kernel (#2273)
The issue's own pick, one half-ellipse rim edge chamfered by the offsets
of setbacks 1 and 2, was refused with 1001 on main's default kernel; with
the generic blend's sections and checks the default it builds a closed
solid (4 faces to 7) whose check_brep is clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* records: the trace's and the band check's fixes, and the known failing rollover case (WR-682, WR-685, #2273)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: the gn flag's uses main added since are retired too (WR-685, WR-696, #2273)
- fillet_corner::topology::join: the unequal-blend test needs only the
round's definition.
- session::finish: the refusal of great circles over a law corner
(#2727) ran only without the G^n section; every kernel now builds the
round as the union of its balls there, so it is removed, and its test
keeps the union half.
- trim_at_creases: the strip's fit is noted on every kernel.
- Tests drop the setting; fillet_variable_radius_closes_onto_vertex's
default kernel removes the balls' union (22.594877) as every fix does.
- The WR-682 record drops the span-end fix, superseded by #2907.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* style: cargo fmt (#2273)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: the move to SolidCraft's kernel (#2273)
- The kernel's own later uses of fillet_gn_section_generator and
fillet_surface_plane_curve_on_plane_equation (exact_pieces'
certified-lift test, issue_2972's bare-net kernel) are dropped.
- The rollover case cyl_on_box1 edge 12 r 2 is known failing again in
blend.fillet_executive_p3 (lost in the last rebase), and its golden
line is dropped. Its issue moved to tangency-labs/solidcraft#13694.
- fillet_law_face_owned_drop_point pins what kernel builds: one face,
not the law corner's three it built on solidcore main 99ed57c4d. Why
the split no longer happens is still open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: the blend-curve generator is deleted with its flag (WR-685, #2273)
SmBlendCurveCrossSectionFSG was reachable only from a caller that
builds it directly: every blend-curve request is the G^n section since
fillet_gn_section_generator is retired, and nothing in solidcore, the
ops or SolidCraft builds the old generator. docs/generic-blend.md
deletes the old path in the same change, so it goes here (review of
tangency-labs/solidcore#2936, point 3):
- BlendCurveCrossSectionFsg, its CreateSurface and CreateSurfaceFromCurves,
FilletSurfaceGenerator::BlendCurve, blend_curve, blend_curve_default and
blend_scale.
- The corners' copies of a blend-curve generator take the G^n section
only (fillet_corner p1 and p2); sm_SectionContactOrder refuses a
blend-curve type, whose order gn_section_contact_order gives.
- Goldens runners: blend.fillet_standard_solver_p1's blend-curve generator
cases are still drawn (so the cases after them stay as they were) and
dropped; blend.fillet_solver_p1, blend.fillet_corner_p1 and
blend.fillet_corner_p3 build the G^n section where they built a blend
curve.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: the variable rounds are their definitions; stale flag mentions (#2273)
Review of tangency-labs/solidcore#2936, points 2 and 4:
- wr696_default_variable_rounds: with SolidCraft's fix set every face the
law-cornered rounds make (6 for 3, 2, 4, 2, 3; 3 for 4, 2, 4: the
pieces and the corner balls' zones) is a procedural blend, and
definition_check finds none bare. The default kernel builds the same
faces as their nets, since fillet_round_as_definition is a fix of its
own and stays off by default.
- wr_659 and issue_2704's headers point at the WR-685 addendum for the
retired flags.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: a smooth law's round is one face (#2273)
The law 4, 2, 4 of fillet_law_face_owned_drop_point is a quadratic
Bezier, smooth, with no corner: its round has no C0 knot to be split at,
so it adds one face. The rebased commit's +3 was wrong, and kernel's
expectation is kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* smlib: drop the retired fillet flags from SolidCraft's preset
UpstreamFixes no longer has fillet_refuse_ball_not_fitting,
fillet_corner_face_curve_halves, fillet_blend_curve_revolved_on_arc,
fillet_surface_plane_curve_on_plane_equation or
fillet_gn_section_generator: their behaviour is the kernel's default, so
the preset stops setting them (it did not build: E0609). The comments
that named the flag as the condition now read as the default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* blend: review points (comments, gen0014's test builds only its case)
- fillet_corner p1: the face-curve-halves comment says what an easing
corner does, and no longer calls the halves opt-in.
- fillet_executive p3: why the adapter over ExecutiveAccess passes the
ball, fit and band checks (upstream's executive, the goldens' runners).
- goldens fillet_geom_p2: gen0014's test draws that case from its own
seed instead of generating the unit's 1100 cases.
- WR-659 addendum: "the 40 mm box".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* goldens: fillet_geom_p2's Rng is in scope (clippy unused_qualifications)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* goldens: bless the five blend units the retirement moves (#2273)
Blessed on Ubuntu 24.04 (CI's glibc) at 39548d6e49. The counts match
CI's goldens on that head exactly; every other unit passes. Only the
cases that move past the tolerance take new lines: the other cases a
bless rewrites differ by about 1e-15 (another CPU's last digits) and
keep main's lines, which CI passes.
- fillet_executive_p3, 67 past the tolerance (69 lines):
- six law-cornered rounds and issue-880's rim, refused before, build,
each check_brep clean: the round is the union of its balls (WR-696);
- law rounds that built move 1e-6 to 7e-5 in volume, the same union
of balls in place of great circles (WR-685, WR-696);
- constant rounds move 1e-10 to 1.3e-7: their side curves come from
the surface-plane trace on the plane's equation (WR-682) and their
corners' face curves are those curves' halves (WR-659);
- blend-curve requests with G1 or thumbweight 1 are invalid input,
1007 (WR-685 addendum): attributed-box-edge0-blend-g2, the eight
issue919 rim cases, law-cube20-e0-deg2-smooth-xsect-blend-curve;
- two rounds that built bodies check_brep rejects (bad loop
orientation) are refused with a fit size: sweep4-brepForFillet2-
all-r0.1 (CutsThePart) and stubs4-degenerate-caps_coons-e5-r0.5
(BallDoesNotFit);
- refused on both sides, the body left after the refusal or the
retry step moves (corner3x2, stubs3, nurb_cone, sphere_diff).
- fillet_executive_p2, 1: set_params_single_fsg_3's blend curve is
invalid input (WR-685 addendum).
- fillet_geom_p2, 12: the surface-plane trace runs on every kernel
(WR-682); gen0014 refuses its station off the surface.
- fillet_solver_p1, 3: the runner's blend-curve kind builds the G^n
section, which takes its thumbweight, not the old generator's scale.
- fillet_standard_solver_p1, 220 removed: the retired blend-curve
generator's cases (their seeds are still drawn).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: flaky Insert Design menu test (#14390)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: the Insert Design menu test waits for its first file read (#14390)
The first step picks a file that is read in the background since #13445, but only waited three frames. On a busy runner the read was still going when the test ran a command, and run() refuses while a file loads. Settle as the later steps do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: solid.coil schemas (part of #2829)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agents and scripts: Coil describes its exact parameters and reply (part of #2829)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: point docs say text naming no parameter leaves the point free
A coordinate typed as text that names no parameter ("5") is just that
number and the point stays free; only text naming a parameter fixes it.
sketch.line, sketch.point, the rectangle and circle commands and the
point and corner schemas now say so. No behaviour change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: point docs say text naming no parameter is in the design unit and does not itself fix the point (#14290)
Review follow-up: text such as "5" is read in the design's length unit
(a JSON number is mm), and a point can still be fixed by other means
(e.g. a default connection to the origin), so the docs now say such text
does not itself fix the point rather than that the point is free.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: claim the rest of nurbs and every nl_ser collapse (part of #13790)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: B-spline method failures carry a typed reason and NLib collapses keep their cause (part of #13790)
The B-spline curve and surface methods (b_spline_curve_n_lib, b_spline_surface_n_lib,
b_spline_surface, b_spline_curve/p2, nurbs_crv's guard) return a BSplineFailure under
Reason::Nurbs instead of a bare Upstream status, with the same operation path and the
same comparison status (SM_ERR, SM_ERR_NULL_POINTER for a missing input,
SM_ERR_INVALID_INPUT for a zero axis), so kind() and sm_status() are unchanged.
nl_ser's replacement, SolidError::nurbs_routine, keeps the NLib error as the cause of a
BSplineFailure::RoutineFailed and passes Unported, Cancelled and PreparationRequired
through unwrapped, as nl_ser does. Every nl_ser site but the seven in
b_spline_curve/p1.rs (held for #13946) uses it; nl_ser goes with that file.
The callers that branch on an Upstream status read upstream_status() instead, which
answers alike for an Upstream and a BSplineFailure, so control flow is unchanged. The
InvalidInput reasons that cited NL_MAXDEG/NL_DEGREE describe the limit in solidcore's
terms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* goldens, smlib: read a B-spline failure's status as the Upstream it replaced (part of #13790)
The goldens runners that recorded an Upstream code read
solidcore::error::nurbs::upstream_status, so every row stays as it was. SolidCraft's
blend status() passes a BSplineFailure on as its status, as it did the Upstream.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a B-spline failure that keeps a port failure as its cause is one too, as #13988's cause chain reads it (part of #13790)
nurbs_routine passes Unported, Cancelled and PreparationRequired through unwrapped; any
other error becomes the cause of a RoutineFailed, so a PortInvariant there makes the
method's failure a port failure and no status answer, which the test now pins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore tests: the intersection-curve and four-arc sphere refusals are read as B-spline failures (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>
* bench: linear_sweep_scaling, one extrusion timed against its side-face count (#13844)
A regular N-gon of line segments, or a square with N/4 square holes, swept by
create_linear_sweep; prints the face count and the time. On main (78d1eb12d), release:
125 faces 63 ms, 250 0.24 s, 500 1.1 s, 1,000 5.4 s, 2,000 24 s, about N^2.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: a reset of the notify records visits only what changed since the last (#13844)
Every edit of a stitch (kernel_named_edit's reseed per vertex glue, the arena store per
edge glue) reset the record store to a default record for every live object of the
B-rep. tangency-labs/solidcore#2324 reset only the records handed out since, but still built the key list of
every object and matched it against every key of the map, and forgot the seeding
progress, so each edit was O(B-rep) and a stitch of N faces O(N^2).
The store now remembers the B-rep value and key scheme it was last made exact for. A
B-rep value logs the objects it removes (arenas only append and empty slots for good;
a copy, a snapshot or a value put back starts a log of its own). So another reset for
the same value and scheme drops the records of the objects removed since, adds those of
the slots added since, and settles each record handed out since, live or not. Every
other record is still the default record of a live object, so the store ends exactly as
before. Any other case (another scheme or value, a record removed by hand, a bulk
access) walks everything, as before. The reset keeps which keys passed the reserved-key
check, which does not depend on the records, and marks the B-rep's objects as seeded.
seed_records_once adds its records through the store's own methods, so a reset sees
them.
linear_sweep_scaling, release: 1,000 faces 5.4 s to 1.7 s, 2,000 24 s to 7.2 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: a sweep's faces reuse the kernel's arena record store (#13844)
with_face_env made a fresh ArenaNotifyState for each face of a sweep: a record for
every object of the B-rep, then a key check and seeding of all of them. It now takes
the kernel's arena store, as the stitch's glues do, reset to the same records
(ArenaNotifyState::reusing), so each face costs what it adds.
linear_sweep_scaling, release: 1,000 faces 1.7 s to 0.73 s, 2,000 7.2 s to 2.9 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: rebinding geometry owners after an edit reads first and writes what it rebinds (#13844)
rebind_geometry_owners ran at the end of every kernel-named edit (each vertex glue of a
stitch) and took each edge, edge use and face mutably to compare its owner, so every
glue copied the id lists and went through the copy-on-write arenas' make_mut for the
whole B-rep. It now finds the geometry still owned under the session identity by
reading, and takes only that mutably, asking again there. The result is the same: the
arenas a query snapshot shares are never edited.
linear_sweep_scaling, release: 1,000 faces 0.73 s to 0.43 s, 2,000 2.9 s to 1.8 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
sketch.edit without restore_marker left the timeline in place, so every
sketch command rebuilt every later feature (their fingerprints chain
through the sketch). Now it rolls to the sketch and restores the current
position on Finish/Cancel, as the app's Edit Sketch does; restore_marker:
false keeps the old in-place edit. A save during the edit writes the
restored position, and the rename an unnamed design gets on save no
longer breaks Finish folding the edit or Cancel.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Face edits: change the body the user picked where two bodies touch flush
Offset Face, Delete Faces, Replace Face, Shell, Draft and Split Face take
picked points. When two bodies touch flush, a point on the shared face lies
on a face of each body. Without `body`, evaluation took the body whose
bounding box was nearest the point, so the first body in the list won the
tie and the other body was never changed.
The engine now works out the body when one of these commands runs without
`body` and the model has more than one body. If every pick lies on faces of
one body, the feature saves that body. If a pick lies on coincident faces of
several bodies, the command is refused and the error names them, so MCP
and script callers no longer change the wrong body without notice.
The dialogs already send the body of the picked faces since #7174. A new
dialog test checks that Offset Face from the dialog changes the body that
was clicked where two bodies touch flush.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Replace Face: check the picked body's face is planar, not any face there
Replace Face checked that each picked face is planar before it worked out
the body. On a face flush with another body's, that lookup tied between the
two bodies and failed, so Replace Face reported "replacing a face that is
not planar" for a flat face, even with `body` given.
Replace Face now works out the body first and checks that body's face.
Without `body`, a pick on the shared face is refused with the error that
names both bodies, as the other face edits do.
Found by CodeRabbit's review of #12387.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Face edits: warn about a pick on a face shared by two bodies instead of refusing it
A face edit given no `body` refused a pick on coincident faces of two
bodies. That turned a call that worked, though on the first body, into an
error, which changes what the command does when used the usual way.
The command now runs as before: evaluation chooses the body, as it always
has. Its result carries a warning that names the bodies, the body that
changed, and asks for `body`. A pick on one body's face still saves that
body in the feature. Replace Face looks up the planar face of a shared pick
on one of its bodies, so it no longer reports a flat face as not planar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Face edits: decide which bodies a pick lies on from exact geometry (#12385)
The body lookup compared display-mesh distances. Two discs touching
along a line (gears in mesh) both hold a point on that line, but one
tessellation came out nearer, so the edit saved that disc as `body` and
changed it, where main changes the first. Caught by
an_editor_accepts_the_ratios_rolling_the_marker_works_out.
faces_body now asks each body's exact boundary (classify_point ==
OnBoundary), culled by its exact box. Both discs hold the point, so the
pick is shared: evaluation decides as before and the result warns.
Test: a_face_pick_where_two_round_walls_touch_on_a_line_is_on_both
fails without this change (changes the second disc, no warning).
cargo xtask ci: 21 failed, all in tests/KNOWN_FAILING_TESTS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Face edits tests: doc comments on the new tests and helper (#12385)
cargo xtask ci: 21 failed, all in tests/KNOWN_FAILING_TESTS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14346
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
* Planar faces: a flat B-spline face (a loft side through three or more sections) is planar (#14346)
#13546 builds a two-section loft of lines with flat sides from plane
faces, which fixed the design in #14346. A loft through three or more
sections still skins every side as a B-spline patch, flat or not, and
Body::planar_face took only plane surfaces, so an extrude from such a
side or a sketch on it failed: "the selected planar face no longer
exists".
planar_face also takes a Spline or RationalSpline face whose control
points all lie on one plane within the body's tolerance: a B-spline
surface with positive weights lies in its control points' convex hull,
so the whole face is on that plane. The normal comes from the net's
oriented area (along dS/du x dS/dv), turned with the face as a plane
surface's is. The work is one pass over at most 65,536 points.
Fixes#14346
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>
* solidcore: claim #14381 (check_brep per-face cache map and test 38 all-pairs)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: check_brep's face cache and loop test stop scaling with the body
face_surface_cache built a map of every loop, edge and vertex use of the
whole B-rep for each face it cached, only to turn a use handle back into
its entity; check_brep caches every face, so the check was O(faces x uses).
The handle is the use's notification reference, so it is now decoded
directly (cache_entity), with the same rejection of anything that is not
a use reference.
Face test 38 intersected every UV trim of each loop with every trim of
each other loop. It now skips loop pairs whose boxes are apart and only
intersects trim pairs a box index finds within twice the UV tolerance;
GlobalCurveIntersect has no solution for the pairs skipped. Trims are
still read in SMLib's order, so the trims built, the verdict and the
first error are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: test 38 borrows its loop pair without split_at_mut
Take the second loop's trims out of their slot while the first is
borrowed and put them back afterwards, so the loop-pair walk has no
panic-prone slice split; read the cached UV tolerance with if-let.
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>
* claim: #13448
Co-Authored-By: Claude Code <noreply@anthropic.com>
* Tests: hold the Offset Plane edit's result until OK is seen computing (#13448)
`an_edit_without_a_preview_adopts_its_suffix_without_window_evaluation`
clicked OK and then asserted that the dialog was still computing. The
click settles over at least four frames, and from the second one OK
applies a result that is already in, so a worker finishing within those
frames left `applying` empty at the assertion. The test now holds the
real worker's result with `apply_gate` until the assertion has run, as
`ok_computing_off_the_window_thread_does_not_apply_in_the_frame_it_starts`
does.
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
* claim: #5927#5928
* Sheet metal: flange segments on one edge (#5927, #5928)
An edge flange's `edges` entry can now split the picked edge into segments:
`{point, segments: [{from, to, angle?, height?, flip?}]}`. `from` and `to` are
fractions of the stretch the sheet still has after cut-outs; they must tile it
(first from 0, last to 1, each to the next from), so a segment never leaves
material out. Each segment takes its own angle, height and side; consecutive
segments that match make one bend. The base is trimmed back by each segment's
own setback over that stretch only, so its outline steps where the angles
differ, and the flange's far edge steps where the heights differ. Two bends of
one split edge butt at the junction: no corner is closed there and no bend
relief is cut.
Refused, with a message naming the segment: a partition that does not tile the
edge, more than 64 segments, an unknown key, a non-bool flip, an angle outside
0..180 degrees, a height below the bend's minimum, `segments` on a base or
contour flange, `then` with `segments`, and a split curved edge.
The Flange dialog has Segments rows (equal stretches with their own angle,
height and turns) and restores them when the feature is edited. The design
format gains one pending change, sheet-flange-segments, with no step: an older
design reads an edge as having no segments.
Tests (crates/engine/src/cmd/sheet_tests.rs) derive the numbers by hand for the
AL2 rule (2 mm, K 0.5, R 1 mm): a partition that matches builds the same part
as a whole-edge flange (46856.637 mm3, flat Y 117.1415926535898), per-segment
heights 45856.637, per-segment angles 46245.422 with the base stepping by
1.7573593128807148, per-segment sides 46856.637 in one closed body, expressions
following parameters through save/reopen and undo, and every refusal.
* Sheet metal: the solidcraft-sheetmetal agent skill (#5946, #5947, #5948, #5949)
Adds skills/solidcraft-sheetmetal with SKILL.md and reference.md: when to load it, the rule-first
workflow, every sheet metal command shape on this branch including `segments`, the segment contract
with its refusals verbatim, the bend arithmetic the engine uses, the FR-17 to FR-20 inference
recipes (segment inference, angles with confidence and an alternative, natural language to
parameters, a bounded optimisation loop) and a worked example that turns a request in words into
one `sheet.flange` call.
The MCP server embeds the skill in `skill_reference`; AGENTS.md routes to it and skills/README.md
lists it. The worked example runs in CI (`sheetmetal_skill_example_builds_as_documented`), which
builds the segmented flange, checks the volume against the hand formula and checks the bend
table's lengths, angles and flat centre lines.
AGENTS.md is trimmed to stay under the 5000-character router cap.
Verified: cargo xtask skills ("AGENTS.md and 18 skills are consistent"), cargo check
-p solidcraft-mcp, the skill tests (8 passed, including the worked example), the full mcp lib
suite (109 passed), clippy on solidcraft-mcp with -D warnings, cargo fmt --check, cargo xtask
lists.
* Sheet metal: the segment strings in the five complete catalogs (#5927, #5928)
* Sheet metal: the dialog keeps each edge's segments, a shortened straight edge splits, one junction tolerance (#5927, #5928)
The Flange dialog rebuilt a segmented edge from the first edge's list alone and gave every
picked edge equal stretches, so opening a feature and pressing OK silently repartitioned it.
The dialog now holds the segments of each picked edge (fractions, angle, height and the side
as an explicit or inherited value) and emits them per edge, so an edit sends the design back
as it was.
A straight edge that a cut-out has taken part of was refused as curved, because the split
check only matched exact outline endpoints. A stretch that lies inside one straight line of
the outline is now accepted; a chord of an arc is still refused.
`sheet.flange` accepted segment junctions within 1e-9 while the build merged matching
stretches only within 1e-12, so a list the command accepted could stay split. Both sides
read SEGMENT_JOIN_EPS = 1e-9, and a first `from` or last `to` inside it snaps to the edge's
end, so every accepted list builds exactly what the whole-edge flange builds.
* Sheet metal: a deleted stretch keeps the partition, `then` is refused, a split edge covers one line, a junction snaps (#5927, #5928)
Deleting a stretch in the Flange dialog now leaves a partition the command accepts: the stretch
before the one removed reaches to its end (the one after it, when the removed one was first,
starts at its start), the ends stay at 0 and 1, and removing the only stretch left makes the
whole edge.
`then` with a split edge is refused, naming the edge, instead of being dropped without a word;
the guide carries the same message.
A split edge's stretch must lie on one straight line of the outline: the outline's lines on that
line must cover it with no gap, so a notch or a step across it is refused while a cut-out that
shortened the line still splits.
A junction within SEGMENT_JOIN_EPS is snapped to the previous run whatever the two stretches'
angle, height or side are; only the 1e-12 comparison decides whether they are one bend, so no
interior gap reaches trimming or the flat pattern.
* Sheet metal: deleting a stretch keeps the other boundaries, and a junction never builds a reversed stretch (#5927, #5928)
The Flange dialog's delete bridged the gap from the removed row's own `to`/`from`. A
row mid-edit has those fields empty, so the repair fell back to equal stretches and moved
boundaries the user never touched (the untouched 0.8 became 2/3). The gap is now bridged
from the neighbours: the stretch before the removed one reaches to where the stretch after
it starts, or to the edge's end when none follows. Equal stretches remain the fallback only
when a neighbour's value does not parse.
The build snapped every accepted junction to the previous run's end without checking the
stretch's own end. A stretch narrower than `SEGMENT_JOIN_EPS` then came out reversed (for
the spans 0…0.5, 0.4999999995…0.4999999997, 0.4999999997…1 the second run was
(0.5, 0.4999999997)), and `trim_stretch` measured it as a positive length. Such a stretch
is no bend of its own now: the junction closes over it, so the build tiles the edge. The
span handling also refuses a reversed span instead of measuring it as a distance, at the
edge check and in the stretch loop.
Tests: a rendered-dialog delete with the removed row's `to` cleared and with its values
intact, and the three-span case at `position` inside and outside compared with the exact
partition. The skill reference and the sheet metal guide say what a delete and a junction
do, and the reference states that the volume check assumes K 0.5.
* Sheet metal: a collapsed stretch is absorbed so the edge stays covered (#5927, #5928)
A stretch narrower than `SEGMENT_JOIN_EPS` that carries another bend's attributes was
dropped without moving the run before it, so the next stretch no longer joined that run
and started one of its own: the interval between them was left out of trimming and the
flat pattern. For the spans 0…0.5, 0.5…0.50000000075, 0.50000000075…0.5000000015 and
0.5000000015…1 with heights 20/15/10/20 mm the flat pattern had one bend ending at 100
and the next starting at 100.0000003, and three slivers in a row did not build at all
(`SMLib failed (status 1001)`).
The run before a sliver now absorbs it: its end moves on to the sliver's end, never back
before it, so the bends tile the edge and the final end still snaps to 1 within the
tolerance. A sliver with no run before it still starts its own run, the sliver's angle and
height still do not build, and a stretch wide enough to be its own bend still starts where
the run before it ends.
Tests: the four-span list at `position` inside and outside, asserting the bends cover the
200 mm edge with no gap or overlap and build the merged partition's part (same volume, same
bends), and three consecutive slivers doing the same. The skill reference and the sheet
metal guide say the run before a sliver takes its stretch.
* Sheet metal: runs start where the edge is covered, so a backward junction never overlaps (#5927, #5928)
* Sheet metal: a segmented flange's first bend starts at the edge's start (#5927, #5928)
Leading stretches that cannot be a bend of their own are still absorbed, but
the run that covers them now begins at the edge's start (0) however far the
absorbed prefix advanced `covered`. Before, the run began at the absorbed
prefix's end: for [0, 7.5e-10], [7.5e-10, 1.5e-9], [1.5e-9, 1] it began at
1.5e-9, past the first-end snap's tolerance, so the edge's first 1.5e-9 never
built and the flange was shortened. A list every stretch of which is absorbed
(a stored design; `sheet.flange` cannot write one) now builds the whole edge
instead of losing the flange.
The invariant is stated in the code: the first run starts at 0, the last ends
at 1, runs are sorted, contiguous and non-overlapping, and every accepted
stretch is covered. reference.md and the sheet-metal guide say the same.
Tests: the exact three-stretch prefix (inside and outside) must build the
whole-edge flange (volume, flat size, bend table); the same prefix before a
differing-height stretch must not shift the junction; a single leading sliver
must build the whole edge; a sliver-only stored list must build the whole
edge. Three of the four fail on d6b69517 (bend starts at 3e-7 mm; the
sliver-only list builds no flange); the single-sliver one already passed there
because the old first-end snap fired within 1e-9, and it guards that path now.
* Sheet metal: each picked edge's stretches keep their own row ids (#5927, #5928)
The Flange dialog drew every picked edge's `Segment N from/to/angle/height` rows in one scope,
and a row's scrub (label drag) id is that scope id plus the label. Two edges with the same number
of stretches therefore shared ids: dragging one edge's `Segment 1 height` moved every edge's
value, and OK persisted geometry the user did not ask for.
Each edge's block is its own scope now, so the id carries the edge; the labels the user sees are
unchanged.
Tests: two edges and three edges with matching stretch counts, dragging one field per edge and
asserting every other edge does not move, and a byte-identical round trip after a drag (the other
edge comes back exactly as the design wrote it).
* Sheet metal: the reference states what the junction tolerance guarantees (#5927, #5928)
Judge round 8 flagged that "every list the command accepts builds exactly what the whole-edge
flange builds" overstates it: matching stretches build the whole-edge bend, differing stretches
build one bend each. The sentence now says what the shared tolerance actually guarantees: the
accepted list tiles the edge with no gap and no overlap.
* Sheet metal: opposite-side junctions are cut in the blank, segment names stay on their stretch, stored bounds are refused (#5927, #5928)
CodeRabbit's review of #12804, the real items (local only, not pushed):
- A junction between stretches that bend to opposite sides has no slit in the flat
pattern or its DXF: the blank stays joined where the folded part separates, so a
blank cut from that DXF cannot fold. `flat_regions` now carries a zero-width cut
along each such junction, from the bend line out to the shorter stretch's far edge
(`SheetBody::junction_slits`, `slit_loop`, `split_loop_at`). Same-side junctions and
a whole-edge flange stay joined. `flat_material` keeps the material's loops (no
self-touching loop), so the kernel booleans, the flat solid, machining and the flat
prism cuts are unchanged.
- Every segment's angle and height shared one label ("Segment angle"/"Segment height"),
so `timeline.redefine` matched a stored name by position and inserting or reordering
stretches moved a named height onto another stretch. `input_keys` now keys a segment
input by its edge and by the stretch it describes (`from`/`to`), matched before the
label-and-kind pass, so a name stays on its own stretch.
- A stored segment list bypassed the command's partition checks: `eval_more` builds the
runs from `from`/`to` as stored. A `from` or `to` outside 0…1, a gap wider than
`SEGMENT_JOIN_EPS` or a last `to` short of the edge's end is now refused with the edge
and segment named; the all-sliver fallback still builds the edge whole.
- The `flip` doc comments on `FlangeSegment` and `EdgeFlange` said a segment's flip turns
the bend the other way than the feature's: it is an absolute override
(`seg.flip.unwrap_or(*flip)`). Both comments, the guide and the skill reference now
say so.
- A top-level `segments` beside `edges` was silently ignored with parameter checking off.
`FlangeParams` takes it as a typed field and `sheet.flange` refuses it, naming where it
belongs; the schema still lists the key.
- The skill reference said any numeric field can be an expression: `segments[].from` and
`segments[].to` are plain numbers. Fixed, with the junction cut and the per-segment flip
documented there and in the guide; SKILL.md stays under 3600 characters (3565).
- The CI skill-example test now checks the volumes the reference publishes against the
arithmetic it already computes, so a wrong reference fails it.
Tests: engine sheet suite 260 passed; doc sheet tests 107; schema sheet tests 10; edit
tests 55 passed (the one known-failing variable-fillet test is in KNOWN_FAILING_TESTS);
ui-egui sheet dialog tests 43; the mcp skill-example test passes. The five new or extended
tests fail on the base commit and pass here. The documented example still builds at
46051.02928860431 mm3 (three stretches) and 46551.02929133257 mm3 (two).
* Sheet metal: a junction cut ends on a stepped far edge's corner (#5927, #5928)
split_loop_at refused a point that already sat at a segment's end, so slit_loop read the
far edge's step corner as off the loop and a blank whose opposite-side stretches differ in
height stayed joined. A point at a segment's end is a vertex of the loop as it stands; both
the line and the arc arm now say so, and the differing-height junction carries its cut.
* Sheet metal: the junction test asserts the differing-height cut and its DXF lines (#5927, #5928)
The far edge steps at that junction, so the cut ends on the step's corner. The test now
checks the flat loop and both DXF lines for 20/20, for 20/15, and that a same-side junction
stays joined.
* Sheet metal: a junction cut trimmed away is clipped to the material left, a name follows the surviving stretch (#5927, #5928)
* Sheet metal: a junction cut ends on a circular notch's arc exactly, a split edge's names stay on the edge's pick point (#5927, #5928)
clip_junction_slit walked the 0.001 mm chords of every boundary segment, so where a round
cut-out took an opposite-side junction's far edge away the crossing it returned sat up to
0.001 mm inside the arc. slit_loop's 1e-6 anchor tolerance then missed it and the required
junction cut was dropped again, leaving the blank joined where the folded part separates. A
line and a circular arc are now intersected exactly (the circle-line roots within the arc's
sweep, the crossing computed on the arc at the angle split_loop_at splits it at), so the cut
ends on the curve itself; only free-form segments still follow chords.
input_keys keyed a segment input by the edge's index in the edges list, so swapping two edges
with identical spans and different heights moved their names between the physical edges: an
expression reading the 20 mm edge read 15 mm. A segment input is now keyed by the edge's pick
point (the point the flange was made on), and an edge's own angle and height the same way, so
reordering the list keeps every name on its own edge.
Tests: a round notch over a junction asserts the flat loop and the DXF carry the cut to the
arc (the cut ends on the circle to 1e-9 and the DXF splits the arc there), and swapping two
edges asserts each named height still reads its own point's height; both failed before the fix.
* Sheet metal: the hole dialog's preview test handles egui's texture delta
epaint 0.37 panics when a TexturesDelta with unapplied deltas is dropped, and this test dropped
the FullOutput run_ui handed back. The crate's other tests clear the delta before dropping it;
this one now does the same, so `cargo test -p solidcraft-ui-egui --lib dialog` is green.
The panic was there before the junction-cut and edge-key fixes (it fails on 04f31e0a too); the
test-only change touches no product code.
* Sheet metal: an arc's ends are judged by distance, every junction bridge is cut, an edge's names follow its pick point (#5927, #5928)
- `split_loop_at` decides "already a vertex" by the distance `slit_loop` anchors with (1e-6 mm), for
a line's and an arc's ends alike, instead of a share of the arc's sweep: on an R = 100 mm arc with
a 1 rad sweep, share 5e-7 is 5e-5 mm from the start, so the split said "vertex" and the caller's
distance test then dropped the cut. Regression: the slit at exactly that anchor is inserted.
- `clip_junction_slits` returns every stretch of the junction line that still runs through material,
ordered outward from the bend line, and `flat_regions` slits each one. A re-entrant notch pattern
leaves the line crossing material more than once, and only the first bridge was parted before.
Regression: two notches leave two bridges at x = 50 and both are cut in the flat loop and the DXF.
- `redefine` matches a `sheet.flange` edge's own `angle`/`height` by the pick point its key carries
and by nothing else, so a deleted edge's name cannot land on an edge inserted in the same redefine.
Regression: two named edges, one deleted and another inserted; the survivor keeps its own name and
the deleted name is gone.
- The `flip` docs (guide, skill reference, both doc comments) state that a segment's `flip` replaces
the feature's outright: `false` is the default direction and `true` the other one, independent of
the feature's `flip`.
* Sheet metal: a moved pick keeps its edge's names, a junction cut stays in its own segment (#5927, #5928)
- `redefine` matches a `sheet.flange` edge's and its stretches' pick points by the document's own
pick tolerance (`appearance::pick_tolerance` of the sheet body the feature is on, 1e-6 mm where the
model does not hold it yet) instead of by the f64 bits their keys carry, so redefining the same
physical edge with a pick that moved by a hair keeps every name on it and the expressions that read
them. The nearest old point wins among the candidates; when none is within the tolerance, the
nearest edge point on the same feature is taken within a sane bound, so a pick that moved further
along the same edge still keeps its names, while a genuinely different edge keeps the
constructor's name. Regression: a named edge height and a named stretch survive a 0.001 mm move,
a downstream expression still reads 20/15 mm, and an inserted edge gets its own name.
- `clip_junction_slits` accepts a crossing only within the junction's own length from the bend line
(plus 1e-6 mm), in the line, arc and free-form arms alike. The junction line is a ray: past its tip
it runs on through material the bend never reaches (a C-shaped blank's foot, for one), and slitting
there cut unrelated material. Regression: the C-shaped blank keeps its foot whole in the flat loop
and the DXF while the junction's own bridge is cut; the notched, two-notch, round-notch and
20/20, 20/15, same-side cases still pass.
- The documented examples are a test now (`crates/engine/tests/sheetmetal_documented_examples.rs`):
46551.02929133257 mm³ for the two-stretch part and 46051.02928860431 mm³ for the three-stretch one.
- The guide and the skill reference say both: a moved pick is the same edge within the document's
pick tolerance, and a junction cut stops at the junction's own tip.
* Sheet metal: an opposite-side junction cut starts at the inner bend line (#5927, #5928)
Stretches that bend to opposite sides at different angles have different setbacks, so their
bend lines are offset. The folded part separates over the strip between those lines, and the
flat pattern and its DXF have to cut it too: the cut starts at the inner bend line, not the
farther-out one, which left the strip joined. The outer end stays at the nearer far edge.
* Sheet metal dialog: follow upstream's label and layout paths after the merge
i18n::tr_label moved to solidcraft_ui_text::tr_label, and shell's LayoutMode import became
private: the dialog uses the public ui_kit re-export. Both were merge drift, not behavior.
* AGENTS.md: route the sheet-metal skill from the router, inside the size cap
Upstream's router rewrite dropped the row the branch had added, and adding it back alone
pushed the file past 5000 characters: the row is one word and the optional plan/ note goes.
* Sheet metal: a stored overlap is refused, and the docs say what a zero-width slit and a moved setback mean (#5927, #5928)
A stored segment list that overlaps by more than the junction tolerance is now refused at the
build, like a gap: the second bend used to start at the first's end and silently drop the
overlap. The skill says an unchanged volume does not prove a bend failed when it only turns the
other way; the reference says different angles can offset the bend lines depending on the rule's
position, and that the slit is zero-width, so the DXF carries two coincident outline lines.
* Extrude convention test: the release format is below this build's pending changes (#5927, #5928)
The branch adds a pending design-format change (edge flange segments), so the build writes one
number past the last folded format. The older-extrude test simulated the last release by
subtracting one from that number, which then pointed at the pending format instead of the
release, so the extrude skipped its migration and built with the wrong sides. Subtract the
pending changes too.
* AGENTS.md: keep the router inside its 5000-character cap after the merge
Upstream's merge added a line and left the router at 5001: one redundant word goes.
* solidcore: retire five flags that no code reads
fillet_corner_statuses, fillet_edge_curve_domain_statuses, offset_tangent_gap_statuses, ssi_alignment_statuses and offset_closed_curve_trim_arc were already unread; remove the fields, every setting of them, and record where each fix lives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: drop the kernel helpers' now-empty let bindings in two thicken tests
Removing the unread offset_closed_curve_trim_arc setting left a mutable binding returned at once, which clippy denies.
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>
A detached `git gc --auto` could still be writing into .git while tearDown removed the
directory, failing the test with "Directory not empty: '.git'" (merge-queue run 42751096942).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* inspect.measure: a face or edge by index needs no point (#14011)
A face or edge item in inspect.measure can now be named by body and
index alone, as model.faces and model.edges list it. Before, `point`
was required even with `index`, so a caller whose body a joint had
moved had to compute a point on the face just to name it. A point
still works as before; an unknown index or neither field is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* inspect.measure: params text says a face or edge takes a point, an index or both (#14011)
The merge of main kept main's wording here, which still said `point` was
required; this puts back the text the rest of the PR documents.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: solid.pattern.circular schemas (part of #2829)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Give solid.pattern.circular input and output schemas (part of #2829)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Close the circular pattern reply test the merge left open
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: claim sc#13774 (twisted-disc side rounds as their definition)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a round by its definition rolls on the extension its trace rolled on
A round whose rails run on past its faces (the twisted disc's side rounds,
past the side edges' ends) rolled its definition's ball on the walls' plain
B-spline bases, whose end spans continue otherwise than the extension the
trace's offsets evaluated there. The first station's feet missed the rails
(by 0.89 and 1.15), so the round fell back to the trace's skinned B-spline,
which missed the 2 mm round of its walls by up to 1.9e-3 between sections.
Where the offset of a B-spline, surface of revolution or surface of
extrusion holds an extension (pending or stored), the definition now rolls
on it. Stations given on a knot line of a face (the extension's old boundary,
only G1) take their sides as a crossing station does; without them the net
left the definition by 2e-3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a blend by its definition takes the side inside its domain at its ends
At an end of its domain a definition took its partials on the side its
caller's flags named. Where the end station's foot is on a support's knot
line (the old boundary of the support's extension, only G1 there) that is
the side past it: the round's tangent along s at its end was the
extension's. The B-spline block's R1.5 round over its arc, whose ends are
there now that the definition rolls on the extension, integrated with a
1001 at 1e-11 and moved its volume 4.5e-10 further from the closed form.
At its ends the side is now the one inside the domain, as a B-spline's end
is its end span's.
The twisted disc's top round R2 is now the ball's piece over each of its 4
side rounds (it was refused naming 1.9586): its test is retitled and
asserts that, with check_brep Level2 clean and a manifold solid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a sided station solve keeps the pieces its seed's sides name
A station whose foot is within the margin of a face's knot line is solved
again on the piece its side names. That piece was chosen at each Newton
iterate, so an iterate just past the margin's edge took the other piece and
Newton cycled between the two. On the teardrop boss's foot round, now its
definition on the wall's extension, feet 2e-9 off the extension's old
boundary (only G1) failed in a band of 2e-9 in s, and the body could not be
integrated at 1e-11. The solve now keeps, for every iterate, the pieces the
sides name at its seed, which every caller places on the knot line.
Two pinned results move because their rounds are now definitions (#13774):
the teardrop boss's foot volumes (+3.1e-4, +4.9e-4; the rounds are now the
wall's and plate's rounds to rounding, where the skinned B-splines were
off by 1.5e-4 at their 90th percentile) and the hull rim chamfer's gain at
its 1e-6 pin (80.0551 to 80.0532; integrated to 1e-10, 80.11434 to
80.11271).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a G^n certificate computes only the coefficients it solves, without subnormal assists
The definition path's time is the G^n certificate's enclosures (gn_enclose_over):
6925 of them for the r0.5 round after the boss foot, 2 to 9 ms each. Three changes
make each one cheaper and leave every value bit for bit the same:
- The feet's Taylor coefficients are solved order by order, and each order evaluated
G at the whole truncation. A coefficient of a sum or product depends only on the
operands' coefficients at lower or equal powers of sigma and eps, so G is now
evaluated only up to that order's total degree and the powers of eps it solves
(`Trunc::computing`), and not at all at an order with none to solve. Same in the
station solve's jets.
- An interval end about 1e-300 from 0 (every interval that holds 0) made the
widening's product `|x| 2.3e-16` subnormal, and a product of such an end with a
number under about 2e-8 subnormal too. An Intel core takes a microcode assist for
each subnormal result (28 ns and 41 ns against about 2 ns). Both are now computed in
integers there, rounded as the floating operations round them; unit tests check
them bit for bit against the floating operations over every binade concerned.
- The composition's 324 scale-and-add steps per face are one in-place step each.
The certificate's output (every span's bound, in the same order) is identical before
and after on the boss-on-bar case. Its round takes 5.7 s instead of 11.5 s (goldens
profile, back to back on one core).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: test the twisted disc's top round below its side rounds' radius (#13772)
The 12 top edges rounded at 1 and at 1.101826756514697 over the exact side rounds:
Level2 clean and manifold. On main the side rounds' skinned nets left top-round
vertices 2.5e-5 and 5.6e-5 off their faces (6 and 4 Level2 lines).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a G^n certificate tightens each proved feet's box before its Taylor solve
Krawczyk's test proved a piece's feet in a box and stopped. The box was still as wide as
the iterations left it, and the feet's Taylor coefficients over it are enclosed with a
factor 1 / (1 - q) at every order, q the contraction over that box. On wide pieces q was
near 1, so the enclosures blew up and the certificate went to finer levels (pieces) to
prove its bound: on the boss-on-bar round, 4 to 6 orders of magnitude between one level
and the next.
Now, once proved, Krawczyk's operator runs again about the box's own centre, with the
Jacobian over the box, and the box becomes its intersection with K, while that shrinks
it by a tenth (at most 2 steps). This is sound by construction: every root in a box is
in its K, so the box keeps the feet, and the C used for the coefficients is over a box
that holds the final one. One step is enough: 4 steps add 26 Jacobians on the case and
no fewer enclosures.
The boss-on-bar r0.5 round: 6925 enclosures before, 3946 now (-43%), and one level
fewer on most spans. Checks:
- a new unit test samples 201 stations over a piece (plane and NURBS cylinder) and finds
every foot and Taylor coefficient (orders 0 to 4, which the floating solve reaches)
inside the piece's enclosure;
- the twisted disc's side rounds: the net is within its certified bound at all 11011
samples per round (new assertion), the ball-centre miss is 2.3e-14 to 2.9e-14 (as
before), Level2 clean and manifold, and the #13772 top rounds are clean;
- goldens blend.*, corpus.parity and surface.offset_surface: 0 rows changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a halved G^n span's halves start from its finest proved level
The section build halves a span whose certified bound is over the target and
certified each half afresh from level 0. gn_certify_span_reusing takes the
halved span's finest proved level instead: its pieces over a half cover it and
their bounds hold over every point of them, so they are the half's level one
coarser, proved without an enclosure, and the levels go on from the next one.
Only pieces inside the half steer the least-value stop; row terms are taken
again about the half's first station. gn_certify_span keeps its behaviour (no
inheritance), operation for operation (3946 enclosures on boss-on-bar before
and after the refactor).
boss-on-bar round: 3946 -> 2758 enclosures, 3.90 s -> 2.98 s (rig case,
P-core 2, three runs each). Goldens 27/27 unchanged, twisted-disc miss
2.26e-14..2.93e-14 unchanged, issue_2652/wr697/2907c pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: G^n series leave out the terms that are zero by construction
The feet's composition through the faces' partials forms du^a dv^b for every
a + b up to the truncation's degree, and the inverse square root forms z^m;
du, dv and z have no constant term, so those powers are 0 below their degree.
Ser::mul_valued and Ser::add_scaled_from leave those terms out (in mul's
order), and the monomials past the degree a masked truncation computes are not
formed. In f64 the results are bit for bit the old ones (the terms left out
are signed zeros; unit test over several truncations and every masked degree);
in intervals they enclose the same values and are the same or narrower
(unit test against the old product and the f64 one).
boss-on-bar round: 2.95 s -> 2.00 s (rig case, P-core 2, alternating runs),
enclosures unchanged at 2758. Goldens 27/27 unchanged, twisted-disc miss
2.26e-14..2.93e-14 unchanged, issue_2652/wr697/2907c pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a G^n span's levels start where the span before proved
Every span tried levels 0, 1, 2, ... and each coarse level failed on its
first piece after solving all its end stations: a span that proves at level 5
first solves 26 stations and fails 5 enclosures. Where a span inherits no
level, the section build now starts it one level coarser than the level the
span before first proved (SpanReuse::start). A level not tried bounds nothing,
so the returned bound is as sound as before. A level's end stations that the
level before solved (every other one, the same s from the same seed) are taken
from it, bit for bit what solving them again gives.
boss-on-bar round: enclosures 2758 -> 2681, enclosures on levels that fail
174 -> 97, time in end-station solves 300 ms -> 184 ms; wall time within
noise of the previous commit (best of five 2.49 s -> 2.44 s). Goldens 27/27
unchanged, twisted-disc miss unchanged, issue_2652/wr697/2907c pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: a G^n re-certificate goes on to its target, not by the halving guess
Re-certifying a blend proves its net within the bound stored when it was
built. The certificate stops refining a span once a level fails to halve the
bound, a guess at the next level's cost. Where the re-certificate's rounding
differs from the build's (it works in the round's own frame), its bounds
differ in the last digits and the guess can stop it a level short of one that
certifies.
wr698 `a_circle_round_collapses_at_its_radius`, the offset of the circle root
round by -2.5, span [69.92, 76.57]. Built by levels 2..5 at 9.55e-4, 8.61e-6,
3.09e-6, 2.39e-6, so stored 2.92e-6. Re-certified at 8.61e-5, 6.04e-6,
3.05e-6: 3.05e-6 is over half of 6.04e-6, so it stopped there, over its
2.92e-6 target, and the offset was refused ("the net is not within its bound
of its definition").
SpanReuse::to_target goes on while the bound is over the target. The least
value still stops it, since past it no level certifies the span. recertify
uses it. The build's certificates are unchanged. The test fails without this
on this branch: the tighter certificate moved the base round's bound and
exposed the stop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: pins of the G^n certificate follow its tighter bounds
Four tests pinned the fork's certified bounds and span counts. The box
tightening and the structural zeros make each bound smaller, and still sound:
every test keeps its checks of the net against the definition. With fewer
spans halved, the span counts drop.
- wr698 root round: bound 9.034561390963572e-5 -> 7.805948063924082e-5, 16 spans
(the net within the bound at all 121 points checked, as before)
- wr698 square corner: 1.0369852135855476e-24 -> 1.0012759448840962e-24
(at rounding either way), 1 span
- wr698 gate ball build: 5.4032051671095375e-9 -> 5.402814802230933e-9, 40 spans
- query_normal_definition rounded wedge: 181 -> 169 inner knots
- fork_7959c0e round on a round: 45 -> 31 spans (32 from the tightening, 31
with the halves' reuse), the net at 0.36 of its bound
Each pin keeps the new exact value, with the fork's in its comment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: the rim chamfer's quoted gain is this branch's
Main's #14224 checks the rim chamfer's gain by its flux, cross-checked by
sections, and quotes the flux. Two of the chamfers are their definitions on
this branch (#13774). The flux is 80.12239 at 1e-11 and 80.12228 at 1e-9, where
it was 80.12092 and 80.12081. The independent estimate is 79.82 +- 0.53 on both.
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>
* claim: #13336
* MCP server crate: an app can serve prompts, resource templates, completions and Markdown or binary resources (#13336)
The shared crate (`solidcraft-mcp-server`) served only tools and JSON resources: `prompts/list`
and `resources/templates/list` always answered empty, anything else was method-not-found, the
`initialize` capabilities always named tools and resources, and every resource read went out as a
JSON string.
- `Tools` gains provided methods for resource templates, prompts (`prompts/list`, `prompts/get`)
and completion (`completion/complete`); the default is none.
- `initialize` names only what the app serves: resources when it has resources or templates,
prompts when it has prompts, completions when it has either.
- `read_resource` returns `Contents`: text or bytes, each with its MIME type; bytes go out as a
base64 blob.
- `prompts/get` checks the arguments against the prompt's listing before the app fills it in;
completion refuses an unlisted prompt and sends the first 100 suggestions with the total.
SolidCraft's replies stay byte-identical: its two resources are JSON (`Contents::json`) and it
serves no prompts or templates, so `prompts/get` and `completion/complete` stay method-not-found.
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* ci: run the kernel rig's cases 16 at a time
The rig can run each case in a process of its own (#14277). Set
KERNEL_CASES_JOBS=16 for the rig step, one per vCPU of the
blacksmith-16vcpu-ubuntu-2404 runner (64 GB), so the rig no longer
runs its 1081 cases one after another, and a hung case is killed at
its CPU-time limit while the rest still run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore ci: run the kernel rig's cases 4 at a time
The standalone workflow's kernel-rig job runs on a 4-vCPU runner. Set
KERNEL_CASES_JOBS=4 there too, one case process per vCPU, so the
mirror's rig also stops running its cases one after another.
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>
* claim: #14384 (flaky add_agent_opens_its_dialog_from_home)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: Add agent from Home no longer races the agent search (#14384)
The test opened Settings › AI, which starts the background search for
installed agents. A search finishing between the frame that drew Add
agent and the click replaced the Detecting line above the button, so
the button moved and the click missed. The test now marks the search
as done first, as the page's other tests do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14362
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
* CI: the slowest test starts first, and the slow-test priority list gains the tests over 100 s (#14362)
thread_tests::a_long_thread_cuts_without_a_deep_tree (median 950 s on
the 32 vCPU runner) was in no priority list: it started about 4 minutes
into Native Linux tests and every full run ended when it did. It now
has its own priority 100, beside the action matrix sample, so it starts
at once. The slow-test list moves to 99 and gains the 17 tests of 100 s
or more it lacked (medians from 24 full runs). Every test still runs
once; no filter, timeout or shard changes.
Fixes#14362
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>
* Add Revolve command guide
Adds a practical guide for Revolve: Profile, Axis, Type (Angle or To Object), Direction, Angle and Operation, with a stepped pin example and a cut groove. Tested on v0.40.0-rc.3.
* Fix Revolve example steps
Add Finish Sketch before Revolve, set Operation to New Body for the pin, use the round side of the pin (not the flat end) as the axis, and give the groove rectangle coordinates that cut into the shaft. Per CodeRabbit review.
* Add Shell command guide
Adds a practical guide for Shell: Faces/Body, Thickness, Direction (Inside, Outside, Both) and Tangent chain, with an open-top box example. Tested on v0.40.0-rc.3.
* Round the rim after Shell
Advise rounding the edges around the opening after Shell, matching the features guide, since Shell can fail on a round tangent to the opening (#13910). Removed the untested wall-vs-fillet tip. Per CodeRabbit review.
* Add Rectangular Pattern command guide
Adds a practical guide for Rectangular Pattern: Object Type, Direction and Direction 2, Quantity and Spacing (center-to-center, counts the original) and Alignment, with a 5 x 3 hole grid example. Tested on v0.40.0-rc.3.
* Clarify Center alignment
Center puts an extra copy on one side when Quantity is even; Flip moves it. Added a tip to check Alignment if copies land off the part. Per CodeRabbit review.
* Add Circular Pattern command guide
Adds a practical guide for Circular Pattern: Object Type, Axis, Quantity (counts the original), Total angle (partial arcs include both ends) and Alignment, with a bolt circle example. Tested on v0.40.0-rc.3
* Clarify Center alignment and confirm Hole step
Center puts an extra copy on one side when Quantity is even; Flip moves it. Step 2 now says to click OK so the Hole is in the History before patterning. Per CodeRabbit review.
A joint pose for a value the slider has left is cancelled once it has
run 500 ms, so the latest value starts and the preview follows the
slider. The latest value still solves to the end.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On an OpenCore Legacy Patcher Mac (Radeon R9 M370X, Iris Pro 5200,
Metal) the for loops of the NURBS, revolved and normal-fallback paths
hang the GPU: WindowServer's watchdog resets the machine after about
three minutes. The same loops written as while loops run. The
arithmetic is unchanged; only the loop form is.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #6609
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cylinder and Sphere: Edit Feature shows the diameter, with a Diameter | Radius choice (#6609)
A diameter given to Cylinder or Sphere is kept as the radius `(D) / 2`; Edit
Feature now shows that diameter on Diameter, as the window that made it asked.
Any other radius opens on Radius as stored. A Size row (Diameter | Radius)
switches between them and converts the value, so OK sends the one shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Mirror: test that a cut starting on a mirrored feature's face mirrors with it (#11792)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Mirror and patterns: copies find the faces their sources name before any copy is made (#11792)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* patterns_tests: no blank line at the end of the file
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #13751
Co-Authored-By: Claude Code <noreply@anthropic.com>
* Test: a B-spline surface net mixing rational and non-rational points is refused where it is built (#13751)
The new unit test builds nets with one weighted point among non-rational
ones (and the reverse) through Surface::new and Surface::with_indices and
expects InvalidInput; storage past the indices in use stays outside the
check. The fuzz corpus now expects fuzz00295 (one weight 1 among -f64::MAX
markers) to be refused by Surface::new instead of building the face cache.
Both fail on main: the constructors accept the net.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* srf_basic: Surface::new and Surface::with_indices refuse a net mixing rational and non-rational points (#13751)
A control point's weight is either a real weight or the non-rational
marker NOW (-f64::MAX). A net that mixes both was accepted by the
constructors; only counts_are_valid noticed it, and paths that never call
it (the face's trimmed-surface cache, fuzz00295) took -f64::MAX as a weight
and subdivided past the fuzz time limit.
Both constructors now refuse such a net with InvalidInput, over the points
in use only. counts_are_valid shares the check, for nets mixed later
through the mutable accessors.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* goldens: nlib.srf_basic records a surface its constructor refuses as rejected (#13751)
The generator makes a net mixing rational and non-rational points in about
5 % of its cases. Surface::with_indices now refuses such a net, so the
runner records the constructor's reason as `rejected` instead of a harness
error; no op runs on the refused surface.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* Test: a rational and a non-rational half of a surface join back into it (#13751)
Splitting the sample surface, giving one half weights 1.0 and joining the
halves must remove the joint knot again and give back the surface, with
every weight 1.0. On main the joint knot removal runs on a net that mixes
weights with the non-rational marker NOW (-f64::MAX).
Co-Authored-By: Claude Code <noreply@anthropic.com>
* srf_tool::p3::join: a rational and a non-rational surface join with real weights (#13751)
join loaded both nets as they are, so the joined net mixed real weights with
the non-rational marker NOW (-f64::MAX); the joint knot removal ran on that
net, and only then was NOW set to 1.0. Surface::new now refuses such a net.
The non-rational piece now gets weights 1.0 before its points are loaded
(make_rat_compatible). The joined net before the removal is upstream's, bit
for bit (0.5 * w1 + 0.5 * w2 equals the old 0.5 * (w1 + w2)), and the
removal sees real weights.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* goldens: nlib.bezier records a surface its constructor refuses as rejected (#13751)
The generator mixes rational and non-rational control points in about 5 %
of its point sets, surfaces included. Surface::with_indices now refuses
such a net, so the surface-strip extension cases record the constructor's
reason as `rejected` instead of a harness error.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* plan: WR-773, N_SrfJoin removes the joint knot on a net mixing weights with NL_NOW (#13751)
Records the upstream order that srf_tool::p3::join no longer follows, with
the nlib.srf_tool_p3 cases it moves: 12 joins with tol -1 that removed the
joint knot anyway (0.23 to 7.0e3 off the exact join) and 2 with tol 1e3
that remove a different number of knots, within tol before and after.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* goldens: bless nlib.srf_basic, nlib.bezier and nlib.srf_tool_p3 for the refused mixed nets (#13751)
nlib.srf_basic, 179 of 3220 cases: each input net mixes rational and
non-rational control points. Old: the op ran on it (23 inspect cases with
counts_are_valid NL_SUR_ERR, 156 op results, 63 of them holding inf or NaN).
New: rejected, "Surface::with_indices: invalid input: control points mix
rational and non-rational". The net is no NURBS surface; the constructor
refuses it now.
nlib.bezier, 7 of 2223 cases: surface-strip extensions of such a net. Old:
status 0 with inf or NaN in all 7 records. New: rejected, same reason.
nlib.srf_tool_p3, 14 of 2524 cases: joins of a rational and a non-rational
surface (WR-773). 12 with tol -1: old removed the joint knot once, 0.23 to
7.0e3 off the exact join; new keeps it and is the exact join (distance 0
on a 41 x 41 grid). 2 with tol 1e3: old 174 / 0, new 0 / 1.63 off the exact
join, all within tol.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* srf_basic, srf_tool::p3: tests check the refusal's reason and a join with real weights; docs name what still mixes (#13751)
- The constructor test uses a 3 x 3 net, so the odd point also sits inside
the net and on an edge, and checks the reason, not only InvalidInput.
- The join test adds a rational piece whose weights are not 1.0: with
tol < 0 the joined net keeps its points and weights, the averaged joint
column, then the other piece's points with weight 1.0.
- The srf_basic header no longer says no path sees a mixed net: the mutable
accessors, set_cpts_and_knots and set_size_indices can still make one, and
counts_are_valid reports it.
- The p3 header says what is equal to upstream: the net after its NOW loop,
with the joint weight equal while w1 + w2 is finite.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* plan: WR-773 gets a schematic of the join's order, and says what equals upstream (#13751)
Co-Authored-By: Claude Code <noreply@anthropic.com>
* fuzz corpus: fuzz01010 of blend.fillet_custom_solver is refused by Surface::new too (#13751)
Its surface has one control point whose rationality differs from the
rest (found as a 343 GB allocation abort; the C++ reference refused it as
"mixed rationality"). Surface::new now refuses it before the solver runs.
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
* Naming: a stored piece name finds its face through the blend that replaced the edge it named (#4325)
A piece of a face keeps the names of the faces that tell it apart
(`F2:side:l1#[F2:top]`). A fillet or chamfer replaces the edge between two
faces with a face of its own, named after that edge
(`F3:blend:F2:side:l1|F2:top`), so the two no longer touch: the stored name
found nothing, and the feature using it failed and asked for its face again.
The blend's name holds the edge it replaced, so the face it separated from is
still there to read, and the name now finds its face. A blend face's own name
is that edge, so a piece of one is qualified inside it
(`F4:blend:F2:side:c1#[F2:side:l3]|F2:side:l3`); that is read too, so a blend
finds its pieces once it is one face again.
The design in #5040 now rebuilds its timeline to the draft instead of asking
for a reselect, and the Create Sketch the issue reports attaches exactly to a
rim a join left flush with a boss.
* Tests: a blend piece name is refused when two pieces answer, and a copy's qualifier keeps its instance (#4325)
The blend-aware piece matching lets a stored qualifier name a face that a fillet
replaced. Two guard tests hold its edges:
- two pieces of one face that both answer to the stored name still ask for the
face again, instead of one of them being picked;
- a pattern copy's blend piece matches with the copy's instance in the
qualifier, as the faces around the copy carry it, and not in another copy.
* Tests: the two-piece refusal test shows the same name resolves once the slot is suppressed (#4325)
The ambiguity guard asserted only the reselect, which a name that finds nothing
would also produce. Suppressing the slot leaves one piece, and the same stored
name then finds it, so the reselect is the second piece answering rather than an
empty lookup.
* Tests: two pieces told apart by one blend both answer a stored name, and the lookup picks neither (#4325)
The refusal in the engine test could hold for a name that finds nothing. This
asserts the ambiguity itself, on a real body: a box with its top front edge
rounded and a slot cut through the front wall leaves two pieces of that wall,
each keeping the round along its own top edge. Both answer `F1:-y#[F1:+z]` as
`Match::Piece` or better, and the lookup chooses neither.
* claim: surface.blend schemas (#2829)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* surface.blend: input and output schemas, part of #2829
The contract keeps the handler's messages. In strict mode it also refuses
a malformed edge point and an unknown key inside `from` or `to`, which the
handler silently drops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #12426
* Assemblies: a test that an angle relation on a hinge keeps its side and carries on past 180 degrees (#12426)
* Assemblies: an Angle joint keeps its part on the same side of the hinge as its angle changes, and carries on past 180 degrees (#12426)
An Angle relation ties only the angle between two faces, so at every angle its part has two places
to be: one on each side. joint.edit started a relation whose tilt changed again from where its
parts were, taking the values that move the part least. Past 180 degrees that is the way back:
170 to 190 turned a hinged leaf back to where 170 had it, and 260 landed where 100 does. From
straight up both sides are as near, so successive values in the Edit Joint dialog (which sends
the type, offset and flip with every edit) landed on either side.
A tilt change alone now turns on from the relation's values: the solver closes the loop from
there, so the part keeps to its side and carries on round past 180 degrees. A change of type,
offset, flip or sides still starts again from where the parts are.
* Assemblies: a suppressed Angle joint whose angle is edited starts again from where its part is (#12426)
Its part may have moved while the joint was off; keeping the joint's values would carry the part
back to where it was once the joint is turned on again. Only an active Angle joint keeps its
values on an angle-only edit. Found in review.
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* UI kit: chord labels are kit code; the text menu no longer reads the keymap
The text fields' menu showed its shortcuts through `keymap::display`, a pure
formatter (Ctrl as Cmd and Alt as Option on macOS). It is now the kit's
`chord_label` in ui_kit/shortcuts.rs, beside the chord keycaps; the keymap
calls it too, so there is one implementation (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Navigation bar: the slot catalogue takes the canvas caps as data
navigation_bar.rs read the app to decide what the canvas allows
(`canvas_caps`, from dialogs_sheet and render_workspace) and the keymap for the
zoom slot's command. `canvas_caps` moves to the host's navigation_bar_view.rs,
`Slot::available` takes `orbit` and `pick`, and the zoom command is the
catalogue's own `ZOOM_TOOL`, which the keymap's `DRAG_ZOOM` names (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Preferences window: a reveal expires on the kit's clock
prefs_window.rs compared a reveal's `until` with the app's `now_ms`, while the
kit set `until` from its own clock. `Reveal::expired` reads the kit's clock for
both (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Tree rows: the host installs how row labels are shown
ui_kit/tree.rs called SolidCraft's `i18n::tr_name`/`tr_dyn`, which translate
its default names (Sketch1, Body2, Untitled) and browser labels. The kit now
reads a host-installed `RowNames` translator (`install_row_names`, once per
process, like the icon set); `i18n::install` installs SolidCraft's, so Browser
rows translate as before. With none installed a given name shows as typed (in
display order) and any other label through the catalogs (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Menus: draw menu icons through the kit's icon set
ui_kit/menu.rs painted its icons through the app's `crate::icons`; it now
uses `ui_kit::icons`, which draws from the set the host installs (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Theme: the host lists its tabs; the theme defines only the accent ramps
The theme crate named SolidCraft's sixteen workspace tabs (`TAB_NAMES`) and mapped each to its
colours (`TabAccent::for_tab`, `icon_accent_for_tab`). It now defines the accent ramps
(`AccentRamp`: the token slots of each ramp) and how a tab's accent is resolved from a table the
host gives (`TabTable` of `TabStyle { name, ramp, icon_accent }`, `TabAccent::for_ramp`), and
holds the host's table once installed (`install_tabs`, `installed_tabs`) for theme files keyed by
tab name. `TabAccent::set_current` takes the icon accent and the ramp a focused number editor
takes, instead of a tab name and whether a sketch is open.
SolidCraft's table is `workspace::TAB_ACCENTS`, with the same ramp and icon accent for every tab;
`workspace::tab_accent` replaces `TabAccent::for_tab` at every call site and a test pins each
tab's colours. The app installs the table next to its icons (`workspace::install_tabs`), and
`theme_files` validates per-tab tables against the installed table. A history chip takes its
icon accent as data (`HistoryChip::icon_accent`); the kind-to-accent mapping moved to the host
(`workspace::feature_icon_accent`). Tests that read theme files without an app or test context
install the host's data first (`test_support::host_data`). No colour changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Theme files: icon roles and ramps come from the installed icon set
`theme_files` read SolidCraft's icon vocabulary (`icon_svg::ROLE_NAMES`, `ACCENT_NAMES`) to
import and export a theme's icon palette. It now reads the roles and ramps of the icon set the
host installed (`ui_kit::icons::installed()`), and its palette type is the kit's
(`ui_kit::icons::Palette`), so the module no longer names SolidCraft's icon table. Same palettes
for SolidCraft, whose set is installed before any theme file is read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* UI kit: Render and workspace-menu names come from the host
Three kit components named SolidCraft's workflows. The inspector header published Render's close
handle and remove entry (`render:appearance:close`, `render:appearance:remove`,
`render.inspector_remove`); it now takes them as `InspectorNames`. The workspace dock's grip menu
entry was the `view.render_dock` command; `WorkspaceOptions::move_command` names it. The review
card labelled its impact rows and secondary action with Render's messages and published Render's
acknowledgement handle; `ReviewAndApply` takes `replaces_label`, `kept_label`, `secondary` and
`acknowledge_handle`. The workspace menu matched SolidCraft's six workspaces by label and carried
their descriptions, tab lists, Render accent and footer; it now draws a `WorkspaceMenu` whose
`style` gives each entry's `WorkspaceStyle` (slug, description, tabs, accent ramp), with
SolidCraft's table in `context_menu::workspace_menu`. The host passes the same names, so handles,
labels and pixels are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit gallery: neutral sample data in the kit's specimens
The kit's gallery specimens drew SolidCraft data: command ids (`solid.fillet`, `sketch.create`,
...), the Model and Surface tabs, a ribbon path and an engine lookup (`find_command`), which a
shared kit cannot reach. They now use sample ids (`sample.action_a`...), tabs "Tab A" and "Tab B"
and the placeholder icon; SolidCraft-flavoured specimens stay in the host's `kit_gallery_tests`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit guard: shared code holds no host tab names or command ids
`shared_code_holds_no_host_tab_names_or_command_ids` reads the non-test code of the theme crate,
the kit, the components and the theme-file modules, and fails on a string literal (macro bodies
included) that is one of SolidCraft's tab names (`workspace::TAB_ACCENTS`) or an engine command
id (`command_specs`), whole or as a `:`/`/`-separated part. The navigation bar's slot catalogue
is still in its component: its ten command ids are listed in `PENDING_HOST_NAMES`, which only
shrinks (a listed name the file no longer holds fails). On the code before the previous commits
it reports the theme's tab names, the history chip's tab mapping and `view.render_dock`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* UI kit: SolidCraft's preference text and page figures come from the host
The kit's widgets named SolidCraft's message ids. Each now takes its text as
data: text emphasis (EmphasisLabels), the History Play control (PlayLabels,
carried by history_bar::Bar), shortcut keycaps (the unassigned text), the theme
browser's arrows (PageLabels), the layout preview's note, the theme options list
and notice (ThemeOptionsText, the dismiss label), the plugin card
(PluginCardLabels) and the axis table (AxisColumns). SolidCraft's own page
figures (the scale specimen, the status colour chips, the autosave timeline and
the agent pointer preview) move to the host as prefs::figures, with their
gallery specimens in kit_gallery_tests. Same text and pixels as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Navigation bar: SolidCraft's slot catalogue is host data
The navigation bar component held SolidCraft's slots and their commands. The
catalogue (and the zoom tool id) moves to the host module navigation_slots,
which installs it once per process next to the tab table (SolveApp::new,
test_support::host_data, both apps' main); the component checks stored bars
against the installed table. Which canvases a slot works on is now slot data
(needs_orbit, needs_pick), with the same results. The kit guard's pending list
of host names is empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit gallery: a test file by name, like the kit's test support
ui_kit/gallery.rs is a #[cfg(test)] module, but the source guards tell test code
by the `_tests.rs` name, so font_sizes_and_dense_form_metrics_come_from_tokens
read its specimen sizes as form metrics. It becomes gallery_tests.rs, loaded as
`gallery` through #[path] as test_support is; the guards' gallery.rs special
cases go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit guard: kit code names no SolidCraft message ids
kit_code_names_no_host_message_ids fails when non-test code under ui_kit/ holds a
message id (`area.thing`) of SolidCraft's English catalog outside the kit's own
vocabulary: ui., color., search. and navbar.custom. (KIT_MESSAGE_PREFIXES). On
the kit before the previous commits it listed choice, history, preferences*,
shortcuts and theme_picker; a companion test pins what it finds and skips.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Viewport layout tests: measure the bar with SolidCraft's slots installed
The navigation bar reads the host's installed slot catalogue (f6836614d); the
viewport layout tests measured the default bar without it. Their measuring
helper installs SolidCraft's slots first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Theme options: SolidCraft names the standard options and why one is off
theme_options held SolidCraft's message ids for the standard options' names
and help and for an unsupported option. A Provider is now behaviour only; the
host names the standard options (prefs::standard_option_text, passed to
theme_option_rows) and words an unavailable reason (theme_option_reason), with
Availability::Unsupported for a standard option without a provider. Same text
in Preferences and in the ui.prefs reply. The message-id guard also scans
theme_files.rs and theme_options.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Navigation bar: SolidCraft's view styles and default bar are host data
The bar component kept SolidCraft's choices: the visual styles and their
icons, the visual-style slot's icon switch, which menus show their value
and the slots a new bar holds. They move to navigation_slots, installed
with the slot catalogue as one Catalogue; the component reads it: a slot's
icon follows its menu value through value_icons, menu_display reads the
host's value and icon-only menus, and the default bar is the host's list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit guard: the navigation bar names no SolidCraft message ids
The message-id guard now scans navigation_bar.rs with the other
components that move with the kit; the host-names guard already did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Selection ink test: read SolidCraft's built-in themes with the host's data installed
The test resolves SolidCraft's built-in themes, whose tab accents come from the host's tab
table now; under nextest (one process per test) nothing had installed it, so no theme
resolved and the list came back empty.
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>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Properties: click a value to copy it (#12218)
Each value in the Properties card (mass, volume, area, centre of mass,
bounding box, counts, first moments and the moment of inertia cells)
copies itself as shown when clicked, and the status bar says so. The
cells' tooltip says they copy.
* Properties: copy cells read as buttons and show keyboard focus; Korean strings (#12218)
* Properties: a copy cell reads as what it is and its value (#12218)
A copy cell's accessible name was its value only. It is now the row's label and the value ("Mass: 12.500 g"), and an inertia cell's its entry ("Ixy: …"), or the row's label and the cell's place for the principal moments.
volume_discontinuity_checks_vw_unaligned, seam_point_solutions_are_one,
is_point_on_curve_propagates_eval_failure and ssi_start_loop_trace_budget are removed:
the code each turned on is now the only path, with the field, preset line, fixes.json
entry and the tests' flag-off halves gone.
Part of #14246
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14360
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
* solidcore: no deprecation warning from fetch_update on Rust 1.99, keeping rust-version 1.85 (#14360)
Rust 1.99 deprecates AtomicU64::fetch_update as a rename of try_update,
so every build with it printed a warning from geom/context.rs. The
rename would break rust-version 1.85, which has no try_update (E0599;
solidcore builds on 1.85 today), so the call stays and its statement
allows the deprecation with a reason, as solidcore's other allows do.
Fixes#14360
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>
* Add Mirror command guide
Adds a practical guide for Mirror: Object Type (Bodies or Features), Mirror Plane, and New Body vs Join, with a model-half-and-mirror example and a feature mirror. Tested on v0.40.0-rc.3.
* Fix block size in Mirror example
Put the 1.5 in short edge on the Y axis so the mirrored block is 4 in wide, per CodeRabbit review.
* claim: #13511
* Motion Study: a test that a step putting a part back where the design has it shows it there (#13511)
* Motion Study: a part that reaches its saved position at a step is shown there, not left at the previous step's pose (#13511)
The dialog worked out the pose to show as the moves it showed last plus every part whose place
at the step differs from the design. A part the step puts back where the design has it was left
out, so it stayed at the pose of the step shown before: Go to End showed 90/90 with the part at
step 89, or at step 0 right after Go to Start. motion.play itself was right.
The pose is now worked out over the moves from before the dialog showed anything (the ones it
keeps to put back when it closes), so a part back at its saved position shows there.
* Motion Study test: let the compiler deref the shown move (clippy explicit_auto_deref)
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* Sketch: a horizontal or vertical distance edited from 0 to a positive value solves
A DistanceX or DistanceY dimension is solved as the magnitude of the offset, which has no
slope where the two points line up, so a value edited from 0 to 0.5 left the solver without
a direction and the sketch failed as a conflict. Give it the same opening slope a point
distance from coincidence already has.
Co-Authored-By: Claude <noreply@anthropic.com>
* Parameters tests: a horizontal distance from the origin changed from 0 moves the point
Co-Authored-By: Claude <noreply@anthropic.com>
* Sketch profiling test: stall on a linear diameter, not a horizontal distance
A horizontal distance from coincident points now opens and solves. A linear diameter of a point
on its line still has a zero slope and no opening direction, so the stalled-solve example uses it.
Co-Authored-By: Claude <noreply@anthropic.com>
* Parameters tests: check the point moves, not only that the change succeeds
The disc's volume doesn't change when it moves, so the test also checks
the middle of its box in x after each edit: 0.5 for 0.5 mm, 0 for 0 mm.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #13438
* Agent chat: the conversation refers to design versions by an opaque handle, so the panel no longer needs the engine's document (#13438)
The chat kept whole engine documents in its conversation state: `Item::Changes` (the design right
after a message's changes, to offer Undo while it is still the open design), `Turn::start_doc`
(the design before a message, to merge its undo steps) and `AgentChat::seen` (the design the agent
last saw, for the changed-design note, #10854). `PanelView::doc` existed only to compare with
`Item::Changes`. That tied the panel to `solidcraft_engine::doc`, so it could not move to a shared
crate (#12948).
`Revision` is an opaque handle the host makes from its design (`Revision::of`). The chat stores it
and compares it by identity (`is`, `same`), the same pointer identity `Arc::ptr_eq` gave before,
and never reads it. The host functions get the design back with `Revision::get` where they need
it (the changed-design note) and keep merging undo and undoing to a revision as before.
No behaviour changes: the existing agent chat tests are unchanged; one new test pins the handle's
identity rule (an equal design is another design, and only the host's type comes back).
* Agent chat: merge_undo keeps taking the host's design; its callers get it back from the turn's revision
The existing test merging_finds_the_turns_start_when_the_history_was_trimmed calls merge_undo with
a design, so the helper stays a host function on documents and the agent chat tests stay unchanged.
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* claim: #13413
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a point placed on a midpoint's guide stays lined up with it (#13413)
With "Line up with other sketch points while drawing" on, a hovered
line's midpoint guide is now one of the points to line up with. A click
on that guide adds the Horizontal or Vertical constraint to a Midpoint
point on the line, in the same undo step as the point.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A face of a linked design (Insert Design) is named after its name in that
design (`link:…`), so joints on it follow it when Update Links brings in an
edit (#3330). A sketch placed on such a face also carries the imported
face's certificate, which holds the digest of the whole imported file: any
edit of the source, even a body added far away, changes that digest, and
the sketch failed with "immutable import face certificate no longer exists",
taking every feature after it along.
When the certificate no longer matches and the face has a link name, the
face is now found by that name on the same Import feature, as joints find
it. A face named only by its place (`face:<k>`) is not followed, and a face
the source no longer has still fails.
Co-authored-by: Mek <mf@linescode.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The reply's top-level `sketch` came from the shape command, before the
dimensions were added, and `locate` started from the last per-role reply in
role-name order rather than placement order. Both now read the sketch as
solved after everything the command did.
Co-authored-by: Claude <noreply@anthropic.com>
* mcp: `running` answers at once while a long `batch` runs (#14007)
The stdio server read its next request only after the previous call
returned, and a bridged `batch` waits up to an hour for the app, so a
`running` call sent meanwhile sat unread until the batch finished.
stdin is now read on a thread of its own: calls are still handled one
at a time, in order, but a `running` call that arrives while another
call is in progress is answered at once on a fresh connection to the
app, which answers `control.running` on any connection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* mcp: `running` right behind a `batch` is not queued behind it (#14007)
Count a message as in flight when the stdin reader queues it, not when
the serve loop takes it, so a `running` read before the loop has picked
up the `batch` ahead of it is still answered apart; and know how to
reach the app before the first message. The tests now hold the batch
until the first reply has been read, so the reply order is not a race,
and check that the app was asked `control.running` while it held the
batch (only possible on another connection), for `running` sent after
the app has the batch and in the same write as it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Agents and scripts: Open and the recovery commands describe their exact parameters and reply (#12279)
doc.open, doc.recover, doc.recovery_discard and doc.recovery_list now publish
typed input and output schemas (part of #2829). The schemas read each key the
way the commands read it today and give the same error messages, and the
replies are built from the typed values, so what the commands do and return is
unchanged. Their lines leave unconverted.txt.
* Open: the reply's newer_file and warnings in the schema test (#12279)
* Schema: doc.open takes folder, as main added it
* Schema test: doc.open keys include origin
* claim: #6548
* Revolve: resolve a cross-sketch axis line in its owning sketch (#6548)
The Revolve and Surface Revolve dialog builders dropped the owning-sketch
qualifier #4081's curve picks carry, so a bare line id resolved in the
profile sketch and a colliding id picked the wrong axis line. The builders
now emit {sketch, curve}; axis_param accepts it, AxisRef::SketchLine keeps
the owning sketch, and evaluation resolves the line there and follows it
when that sketch is edited. Bare ids keep resolving in the profile sketch,
so old designs replay identically.
Fixes#6548
Written by Qwen.
* fmt: apply rustfmt to revolve axis changes (#6548)
* clippy: use expect_err in revolve axis sketch test (#6548)
* revolve: keep bare axis id when the line is in the profile sketch itself (#6548)
* fmt: apply rustfmt to revolve axis changes (#6548)
* chore: re-trigger checks
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Move SolidCraft's mesher and its evaluator into solidcore (RFC 0003, item 1.1)
A move only, so history follows the files: SolidCraft's planner
(crates/kernel/src/tess/) becomes solidcore-tess's src/mesher/, and its plan
evaluator (crates/geom/src/tess.rs and tess/) becomes the kernel's
src/eval/plan.rs and src/eval/plan/, next to solidcore::eval. No content
changes; the next commits wire them in and remove solidcore-tess's own
planner, so one mesher remains.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: remove its own planner and plan, replaced by SolidCraft's
solidcore-tess's planner (src/planner/) and plan (src/plan.rs) were a port
of SolidCraft's; SolidCraft's own moved in with the previous commit, so one
mesher remains (RFC 0003, item 1.1). The next commit wires the moved code in
behind solidcore-tess's public API.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Wire in SolidCraft's mesher and evaluator; SolidCraft meshes through solidcore-tess
The moved plan evaluator is the kernel's `solidcore::eval::plan`, in the
kernel's vectors, beside `solidcore::eval` (where the two overlap, they are
unified in a later step). The moved planner is solidcore-tess's `mesher`,
behind its public API: `plan`, `TessPlan` and `FacePlan` (SolidCraft's plan
types, now in solidcore-tess's `plan`, with its `poles` field), `evaluate`
returning the mesh and the faces left out, `PlanStats` and its records,
`MeshTolerance`, `preview` and `simplify`.
- The mesher evaluates surfaces with the kernel's plan evaluator and edge
curves and pcurves with the kernel through solidcore-convert; it works in
the kernel's vectors and hands the plan the record's. It keeps SolidCraft's
text (rustfmt skips it) and allows the lints it does not meet yet, with the
reason; a panic in planning is caught at `plan` until the next step brings
checked access.
- SolidCraft's geom re-exports the evaluator as `geom::tess`, through one line
in xtask/solidcore-seam.txt (until RFC 0003 item 1.8 moves model-evaluation
callers to ops and tess queries), with converters between its vectors and
the kernel's and its partials in its own vectors; the B-spline basis
functions it shares with geom's splines moved into `eval::plan::basis`.
- The kernel crate meshes with `solidcore_tess::plan`; its `MeshTolerance` and
`DISPLAY_ANGLE` are solidcore-tess's, and its worker map that only the
mesher used is gone.
- Tests move with what they test: the planner's tests that build bodies with
SolidCraft's kernel are crates/kernel/src/display_plan_tests.rs, the plan
types' are in solidcore-tess, the evaluator's cross-check with geom's NURBS
surface is in geom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Kernel rig: the shared core names the mesher's new home
tests/kernel-cases/PATHS-ALL still listed crates/kernel/src/tess/**, which the
move emptied, and the rig plan's check fails a glob that matches no file. The
mesher and its plan evaluator are now solidcore-tess's src/mesher/ and the
kernel's src/eval/plan.rs and plan/; they are inside solidcore/** already, and
are named there so the move stays visible in the list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: the kernel decides the moved mesher's poles, once per iso-line
SolidCraft's mesher, moved into solidcore-tess, told a pole by the surface's
derivative speeds at the point, evaluated afresh at every question. It now asks
the kernel's certified classifier (solidcore::eval::collapse), which decides on
the iso-line through the point, and keeps each answer under its iso-line
(direction, other parameter, reach), so the kernel is asked once per iso-line
however many nodes and passes look at it. There is one pole check: where the
kernel cannot tell, the face is refused, named with the point and the kernel's
reason, and nothing is taken for an answer. On the 1,101 kernel-case bodies,
the primitives and 26 STEP files the kernel refused no point at either
tolerance. The speed-based checks the mesher used (`degenerate`,
`degenerate_within` in eval::plan::uv) are deleted.
Pole questions and kernel classifier calls (export): coil split by a plane
(793) 1,084,906 -> 1,393; coil through a cylinder wall (4505) 996,266 ->
12,877; boss-root fillet 54,244 -> 310; twisted closed sweep 80,112 -> 562.
The 160 spline-heavy bodies mesh in 8.0 s instead of 13.2-13.7 s
single-threaded, with the same triangles; all 1,107 kernel-case bodies and
primitives keep their counts.
solidcore-tess's pole pass is back on this mesher: a pole of higher order
gets the kernel's exact limit normal per triangle, or the mean of its
triangles' normals where the kernel has none (named in
PlanStats::averaged_poles); a normal neither rule gives refuses the face in
`evaluate`. At a degenerate vertex the CPU evaluation takes the kernel's limit
normal instead of a step inside. tests/pole.rs runs again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: a node just past the range is classified on it, within the face's tolerance
The kernel's pole classifier refuses a point off the surface's parameter
range, and the mesher meets boundary nodes a little past it: pcurves made to a
STEP file's uncertainty end there. A node past the range by no more than the
face's own tolerance (the largest of its edges' and vertices') is classified at
its nearest point of the range, where it lies on the face to within that
tolerance. Farther out, the face is refused with the distance named; nothing is
extrapolated and there is no other pole check.
On the 319 files of solidcore's STEP bench corpus this classifies the six
faces the classifier refused: tork's faces 217, 243, 250 and 269 (nodes at most
9.8e-7 mm off, tolerances 1e-3 mm and up), nist_ctc_02_asme1_rc's face 647
(0.113 mm, tolerance 0.119 mm and up) and nist_ctc_05_asme1_ap203's face 143
(0.034 mm, tolerance 0.051 mm and up). No face of the corpus is refused, and
the 1,107 kernel-case bodies and primitives keep their triangle counts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: a face the mesher cannot read is left out, not the body
SolidCraft's mesher, moved into solidcore-tess, refused a whole body when one
face's surface did not evaluate, one edge could not be sampled or split, or one
face's boundary could not be read. Each of these now leaves out only the faces
concerned, each named with why in PlanStats::unmeshed, and the rest of the
body is meshed, as solidcore-tess's own planner did:
- a face whose surface does not evaluate;
- every face an edge bounds that cannot be sampled, or split further for a
thin loop or a crossing boundary (an edge that bounds no face is named in
PlanStats::unsampled_edges);
- a face whose boundary cannot be read;
- an edge whose coarser sampling for a crowded face fails keeps its nodes,
named in PlanStats::kept_strict.
`rod_doubled_edge` (tests/step-regressions), refused whole before ("edge 7:
more than 20000 nodes"), now meshes four of its five faces and names the
fifth; nothing else in the 1,101 kernel-case bodies, the primitives or the 26
STEP files changes. Tests: that body in the kernel's plan tests, and
solidcore-tess's refusal tests on its fixtures (a face off its pcurve, a free
edge, an edge both of whose faces are left out, a label without its form).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: checked access in the moved mesher and evaluator, no panic guard
The moved mesher and plan evaluator indexed slices, arrays and Vecs directly,
within bounds they checked beforehand. That is now checked access: get/get_mut,
zips and iterators, slice patterns for windows(2), and a rotate for a
triangle's three corners. A direction index into a (u, v) pair goes through
one small helper that reads NaN out of range, so no comparison on it holds.
Arithmetic and its order are unchanged. clippy::indexing_slicing is out of
both modules' allow lists.
plan() no longer wraps plan_at in the panic guard: planning returns its
failures as errors, and the doc comment says so. The worker pool's per-item
guard in par::map is untouched.
Triangle counts are identical, body for body, on the 1,107 kernel-case bodies
and primitives, at both the display and export settings. solidcore-tess tests
(62 passed, 2 ignored, plus tests/pole.rs) and solidcore eval:: tests pass;
clippy -D warnings is clean on solidcore, solidcore-tess and diagnostics.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: ask the moved mesher's pole question once per node
The sag refinement and the flips ask, at every triangle corner, side and
round, whether a node is on a pole, and the self-crossing check asked it twice
per node, once for each chord the node ends. Whether a node is on a pole is a
property of its parameters: a `sag::Poles` memo, shared by refinement and the
flips of a face, asks it once per node, and `tangle::crossing` keeps each
node's answer for both its chords.
This is #13559's fix for solidcore-tess's own planner, which the move replaced,
carried over to the moved mesher with its test
(`the_pole_question_is_asked_once_per_node`: 27,452 questions for 349 nodes
without the memo, at most two per node with it). Triangle counts are unchanged
on the 1,107 kernel-case bodies and primitives, and the coil's test
(`the_spline_coil_keeps_its_triangles`) still passes.
Ported from #13559 by @bherbruck (co-authored by jyviko).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: drop the plan reports SolidCraft's mesher never writes
`PlanStats::over_tolerance` (faces a memory bound limited) and
`PlanStats::edges_capped` (edges a sampling bound stopped), with their record
types `OverTolerance`, `Cap`, `CappedEdge` and `EdgeCap`, came from
solidcore-tess's own planner, which the move replaced. SolidCraft's mesher has
no such reports, so after the move they were always empty, read as "nothing
was limited" whatever happened. They are removed; nothing reads them. The
other fields are written: the face counts, `lenient` and `unmeshed` by the
mesher, `averaged_poles` by the pole pass, `unsampled_edges` and
`kept_strict` by the per-face refusals.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: keep the over-tolerance and capped-edge reports
This reverts 0836c4b02f. PlanStats::over_tolerance and PlanStats::edges_capped
are solidcore-tess's own reports and part of its public API; the next commit
wires them into the moved mesher instead of dropping them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: the moved mesher reports capped edges and faces left over tolerance
SolidCraft's mesher stopped at its bounds without saying so: an edge's
sampling at its depth (24 halvings) or node bound (20,000) with a chord still
wanting a split, a face's grid thinned to fit its point bound, a face's sag
refinement at the points it may add. These now fill solidcore-tess's own
reports:
- `PlanStats::edges_capped`: the edge, the bound, the farthest its chords lie
from its curve, how far its chords exceed their rules, and whether it is
within the tolerance. A capped segment is the only one whose rules are now
checked at the bound; the nodes are the same.
- `PlanStats::over_tolerance`: a face whose grid was thinned or whose
refinement ran out of points, with the bounds and the sag its final
triangles reach (measured as refinement measures it, at each triangle's
middle and sides' middles), and how many middles could not be measured.
Tests, ported from solidcore-tess's own planner:
an_edge_stopped_at_its_depth_bound_is_named_within_its_tolerance and
a_face_whose_refinement_reaches_its_bound_is_named_with_its_sag. Triangle
counts are unchanged on the 1,107 kernel-case bodies and primitives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: shape flat faces with no size, and stop shaping crowding a corner
A flat face with no size asked of it was meshed from its boundary nodes alone, which fans a
disc's rim into slivers (sheet_disc: 7,668 triangles under 1 degree). Such a face now gets
Ruppert refinement for shape alone, to 15 degrees, with at most as many interior points as it
has boundary nodes and its boundary kept as it is, so neighbours' nodes are untouched.
Shaping (both this and the curved faces' pass) no longer splits triangles smaller than the
tolerance squared: round a sharp corner where two pieces' cuts meet it crowded points ever
closer together, down to triangles with no area (the extra open edge on face 6 of
851-chamfer-arc-top-edge-9-5).
A face's vertices are matched to ones already placed through a grid as fine as the tolerance
rather than a search of all of them, and the triangulation's bookkeeping drops two hash maps:
the 50,000-hole toothed plate plans in 8.8 s (103 s before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: no panic-prone calls in the moved plan evaluator
f64::clamp panics on NaN bounds, so Domain::clamp and wrap_into take max then min. The
derivatives and starting-grid vectors grow as they are filled instead of preallocating a
product that could overflow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: shaping's floor is a thousandth of the tolerance, squared
At the tolerance squared, display's coarser tolerance left the slivers of flat faces with no
size unshaped (sheet_disc's p99 aspect 5.6, the toothed plate's 99,536 slivers). A side a
thousandth of the tolerance, where sag refinement stops too, still ends the crowding round a
sharp corner (851-chamfer-arc-top-edge-9-5 has no open edge) and shapes flat faces fully.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: keep the reasons a face is refused for
- A face whose pole question the kernel could not answer is refused with that reason even
when planning then failed on it: the refusal is read before the plan's own error, which may
be a later symptom ("boundary crosses itself").
- A node the kernel's evaluator refuses (other than a pole node, which has its own rule)
refuses the face in TessPlan::evaluate with the node and the reason, instead of taking the
plan evaluator's normal, which can be made up where the derivatives do not evaluate.
- The poles' range check takes max then min: f64::clamp panics on a NaN bound.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
* smlib export tests: call the pcurve evaluator through solidcore-ops
A test main added after the move still named solidcraft_geom::brep::pcurve, which moved into
solidcore::eval (this PR); it calls solidcore_ops::pcurve::eval like every other caller.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* smlib export tests: sort the imports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: step arrows on the whole-number Design fields (#11829)
* Preferences: a step arrow never pulls a value past its limits the other way (#11829)
* UI kit: gallery entry for the whole-number field (#11829)
* Coincident: put a sketch point on an origin axis, picked in the view or the browser
Either Coincident pick may now be the X, Y or Z axis: clicked in the view,
clicked in the browser while the tool runs, or preselected. An axis along the
sketch's own x/y uses that axis; any other is projected (linked) into the
sketch, so the constraint stays parametric.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dimension: a line picked with an origin axis measures their distance
The Dimension tool ignored an axis pick, so a line then an axis gave the
line's length. An axis (in the view or the browser) is now projected and
measured against the other pick. A free line dimensioned to a linked line
is also kept parallel to it, unless its direction is already set, so the
whole line holds the distance instead of tilting about one end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Coincident and Dimension with an origin axis: either pick order, the measured value, preselection and a typed value (#10669)
A line dimensioned to an origin axis takes the axis as the reference in either pick order, so
the line is kept parallel to it; the command reports what the dimension measures once solved,
found by its id, not the Parallel added after it. A selected point and origin axis go to
Coincident from the toolbar. An axis picked in the browser no longer replaces a value already
typed into the open dimension.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Origin axis picks: lowercase references, a centerline first, a set angle kept, no axis for Horizontal/Vertical, an unambiguous Coincident axis (#10669)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Coincident axis: a driven angle does not count as setting the line's direction (#10669)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge main into #10669: world_state is a Result; an origin axis object is X, Y or Z, as main's references are
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Origin axis dimension: Horizontal/Vertical on the line's two points sets its direction too (#10669)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Axis picks and dimensions: keep old behaviour outside origin axes
A free line dimensioned to a projected edge keeps its old dimension again:
only an origin or sketch axis is the reference in either pick order and
keeps the line parallel. A driven dimension now reports its solved value
(its id was not recorded). Symmetry no longer takes an axis whose x / y a
curve or point of the sketch has as its id, as Coincident already did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Dimension: a click on an origin axis places a full dimension
Once a dimension has its two picks, a click on an origin axis was taken as
a third pick and refused, so it never placed the dimension. The axis is a
pick only while the dimension takes one; then the click places it, as a
click on empty space does (midpoint_pairs_dimension_sketch_and_projected_model_edges).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Dimension: keep a line parallel to the axis unless its direction is fixed
A line dimensioned to an origin axis got no Parallel to the axis when it
was parallel (or at an angle) to another line, even a free one; both lines
could then turn together and only one end kept the distance. Parallel is
now skipped only when a chain of such relations reaches a line whose
direction is fixed (Horizontal, Vertical, an angle) or that is linked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Latvian interface language
* Latvian interface language
Add lv.json with every English message translated into Latvian and register
Latviešu in the language list (English first, then by id), so it can be
chosen in Preferences > General > Language. Latvian stays out of the
COMPLETE catalogs, so a new English label doesn't need a Latvian one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Latvian: follow English messages that changed since the translation
Update 37 Latvian messages whose English wording changed after the
translation was made, including the macOS update step that now says to
quit SolidCraft first, the shorter assistant permission labels and the
Preferences page headings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Latvian: the render messages main added since
Translate the 56 messages main has added since (CPU workers, denoising
diagnostics and its mask, supersampling, texture filtering, area and
environment caustics), and follow the path depth help's new range (1–64).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: guntarsS <guntars@udlive.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
conic_evaluate_uses_computed_second_deriv and curve_max_distance_single_sample_midpoint
are removed: the code each turned on is now the only path, the field, its preset line,
its fixes.json entry and the tests' flag-off halves are gone. Conic::evaluate_with_fixes
is folded into Conic::evaluate.
Part of #14246
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: claim the kernel rig's per-case processes (#14060)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: the kernel rig runs one process per case, in parallel, with a CPU-time hang limit
KERNEL_CASES_JOBS=N grades each selected case in a process of its own, N at
a time: the test re-runs its own binary for one case, the case process
hands its grade back as JSON, and the parent merges the grades in the
selection's order into the same report. A case past KERNEL_CASES_TIMEOUT_S
of CPU time (from /proc on Linux, wall time elsewhere; 600 s by default in
this mode, with a 4x wall backstop) is killed and graded a hang, and the
other cases still run. A process that crashes or ends without a whole grade
for its case is a failure, or a moved known-failing case. The processes are
killed and their files removed when the parent ends or panics; a case
process also ends when the parent's stdin pipe closes.
KERNEL_CASES_SHARD=i/n selects every n-th case from the i-th.
Unset, KERNEL_CASES_JOBS keeps today's single-process loop.
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>
* Move SolidCraft's mesher and its evaluator into solidcore (RFC 0003, item 1.1)
A move only, so history follows the files: SolidCraft's planner
(crates/kernel/src/tess/) becomes solidcore-tess's src/mesher/, and its plan
evaluator (crates/geom/src/tess.rs and tess/) becomes the kernel's
src/eval/plan.rs and src/eval/plan/, next to solidcore::eval. No content
changes; the next commits wire them in and remove solidcore-tess's own
planner, so one mesher remains.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: remove its own planner and plan, replaced by SolidCraft's
solidcore-tess's planner (src/planner/) and plan (src/plan.rs) were a port
of SolidCraft's; SolidCraft's own moved in with the previous commit, so one
mesher remains (RFC 0003, item 1.1). The next commit wires the moved code in
behind solidcore-tess's public API.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Wire in SolidCraft's mesher and evaluator; SolidCraft meshes through solidcore-tess
The moved plan evaluator is the kernel's `solidcore::eval::plan`, in the
kernel's vectors, beside `solidcore::eval` (where the two overlap, they are
unified in a later step). The moved planner is solidcore-tess's `mesher`,
behind its public API: `plan`, `TessPlan` and `FacePlan` (SolidCraft's plan
types, now in solidcore-tess's `plan`, with its `poles` field), `evaluate`
returning the mesh and the faces left out, `PlanStats` and its records,
`MeshTolerance`, `preview` and `simplify`.
- The mesher evaluates surfaces with the kernel's plan evaluator and edge
curves and pcurves with the kernel through solidcore-convert; it works in
the kernel's vectors and hands the plan the record's. It keeps SolidCraft's
text (rustfmt skips it) and allows the lints it does not meet yet, with the
reason; a panic in planning is caught at `plan` until the next step brings
checked access.
- SolidCraft's geom re-exports the evaluator as `geom::tess`, through one line
in xtask/solidcore-seam.txt (until RFC 0003 item 1.8 moves model-evaluation
callers to ops and tess queries), with converters between its vectors and
the kernel's and its partials in its own vectors; the B-spline basis
functions it shares with geom's splines moved into `eval::plan::basis`.
- The kernel crate meshes with `solidcore_tess::plan`; its `MeshTolerance` and
`DISPLAY_ANGLE` are solidcore-tess's, and its worker map that only the
mesher used is gone.
- Tests move with what they test: the planner's tests that build bodies with
SolidCraft's kernel are crates/kernel/src/display_plan_tests.rs, the plan
types' are in solidcore-tess, the evaluator's cross-check with geom's NURBS
surface is in geom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Kernel rig: the shared core names the mesher's new home
tests/kernel-cases/PATHS-ALL still listed crates/kernel/src/tess/**, which the
move emptied, and the rig plan's check fails a glob that matches no file. The
mesher and its plan evaluator are now solidcore-tess's src/mesher/ and the
kernel's src/eval/plan.rs and plan/; they are inside solidcore/** already, and
are named there so the move stays visible in the list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: the kernel decides the moved mesher's poles, once per iso-line
SolidCraft's mesher, moved into solidcore-tess, told a pole by the surface's
derivative speeds at the point, evaluated afresh at every question. It now asks
the kernel's certified classifier (solidcore::eval::collapse), which decides on
the iso-line through the point, and keeps each answer under its iso-line
(direction, other parameter, reach), so the kernel is asked once per iso-line
however many nodes and passes look at it. There is one pole check: where the
kernel cannot tell, the face is refused, named with the point and the kernel's
reason, and nothing is taken for an answer. On the 1,101 kernel-case bodies,
the primitives and 26 STEP files the kernel refused no point at either
tolerance. The speed-based checks the mesher used (`degenerate`,
`degenerate_within` in eval::plan::uv) are deleted.
Pole questions and kernel classifier calls (export): coil split by a plane
(793) 1,084,906 -> 1,393; coil through a cylinder wall (4505) 996,266 ->
12,877; boss-root fillet 54,244 -> 310; twisted closed sweep 80,112 -> 562.
The 160 spline-heavy bodies mesh in 8.0 s instead of 13.2-13.7 s
single-threaded, with the same triangles; all 1,107 kernel-case bodies and
primitives keep their counts.
solidcore-tess's pole pass is back on this mesher: a pole of higher order
gets the kernel's exact limit normal per triangle, or the mean of its
triangles' normals where the kernel has none (named in
PlanStats::averaged_poles); a normal neither rule gives refuses the face in
`evaluate`. At a degenerate vertex the CPU evaluation takes the kernel's limit
normal instead of a step inside. tests/pole.rs runs again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: a node just past the range is classified on it, within the face's tolerance
The kernel's pole classifier refuses a point off the surface's parameter
range, and the mesher meets boundary nodes a little past it: pcurves made to a
STEP file's uncertainty end there. A node past the range by no more than the
face's own tolerance (the largest of its edges' and vertices') is classified at
its nearest point of the range, where it lies on the face to within that
tolerance. Farther out, the face is refused with the distance named; nothing is
extrapolated and there is no other pole check.
On the 319 files of solidcore's STEP bench corpus this classifies the six
faces the classifier refused: tork's faces 217, 243, 250 and 269 (nodes at most
9.8e-7 mm off, tolerances 1e-3 mm and up), nist_ctc_02_asme1_rc's face 647
(0.113 mm, tolerance 0.119 mm and up) and nist_ctc_05_asme1_ap203's face 143
(0.034 mm, tolerance 0.051 mm and up). No face of the corpus is refused, and
the 1,107 kernel-case bodies and primitives keep their triangle counts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: a face the mesher cannot read is left out, not the body
SolidCraft's mesher, moved into solidcore-tess, refused a whole body when one
face's surface did not evaluate, one edge could not be sampled or split, or one
face's boundary could not be read. Each of these now leaves out only the faces
concerned, each named with why in PlanStats::unmeshed, and the rest of the
body is meshed, as solidcore-tess's own planner did:
- a face whose surface does not evaluate;
- every face an edge bounds that cannot be sampled, or split further for a
thin loop or a crossing boundary (an edge that bounds no face is named in
PlanStats::unsampled_edges);
- a face whose boundary cannot be read;
- an edge whose coarser sampling for a crowded face fails keeps its nodes,
named in PlanStats::kept_strict.
`rod_doubled_edge` (tests/step-regressions), refused whole before ("edge 7:
more than 20000 nodes"), now meshes four of its five faces and names the
fifth; nothing else in the 1,101 kernel-case bodies, the primitives or the 26
STEP files changes. Tests: that body in the kernel's plan tests, and
solidcore-tess's refusal tests on its fixtures (a face off its pcurve, a free
edge, an edge both of whose faces are left out, a label without its form).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: checked access in the moved mesher and evaluator, no panic guard
The moved mesher and plan evaluator indexed slices, arrays and Vecs directly,
within bounds they checked beforehand. That is now checked access: get/get_mut,
zips and iterators, slice patterns for windows(2), and a rotate for a
triangle's three corners. A direction index into a (u, v) pair goes through
one small helper that reads NaN out of range, so no comparison on it holds.
Arithmetic and its order are unchanged. clippy::indexing_slicing is out of
both modules' allow lists.
plan() no longer wraps plan_at in the panic guard: planning returns its
failures as errors, and the doc comment says so. The worker pool's per-item
guard in par::map is untouched.
Triangle counts are identical, body for body, on the 1,107 kernel-case bodies
and primitives, at both the display and export settings. solidcore-tess tests
(62 passed, 2 ignored, plus tests/pole.rs) and solidcore eval:: tests pass;
clippy -D warnings is clean on solidcore, solidcore-tess and diagnostics.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: ask the moved mesher's pole question once per node
The sag refinement and the flips ask, at every triangle corner, side and
round, whether a node is on a pole, and the self-crossing check asked it twice
per node, once for each chord the node ends. Whether a node is on a pole is a
property of its parameters: a `sag::Poles` memo, shared by refinement and the
flips of a face, asks it once per node, and `tangle::crossing` keeps each
node's answer for both its chords.
This is #13559's fix for solidcore-tess's own planner, which the move replaced,
carried over to the moved mesher with its test
(`the_pole_question_is_asked_once_per_node`: 27,452 questions for 349 nodes
without the memo, at most two per node with it). Triangle counts are unchanged
on the 1,107 kernel-case bodies and primitives, and the coil's test
(`the_spline_coil_keeps_its_triangles`) still passes.
Ported from #13559 by @bherbruck (co-authored by jyviko).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: drop the plan reports SolidCraft's mesher never writes
`PlanStats::over_tolerance` (faces a memory bound limited) and
`PlanStats::edges_capped` (edges a sampling bound stopped), with their record
types `OverTolerance`, `Cap`, `CappedEdge` and `EdgeCap`, came from
solidcore-tess's own planner, which the move replaced. SolidCraft's mesher has
no such reports, so after the move they were always empty, read as "nothing
was limited" whatever happened. They are removed; nothing reads them. The
other fields are written: the face counts, `lenient` and `unmeshed` by the
mesher, `averaged_poles` by the pole pass, `unsampled_edges` and
`kept_strict` by the per-face refusals.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: keep the over-tolerance and capped-edge reports
This reverts 0836c4b02f. PlanStats::over_tolerance and PlanStats::edges_capped
are solidcore-tess's own reports and part of its public API; the next commit
wires them into the moved mesher instead of dropping them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: the moved mesher reports capped edges and faces left over tolerance
SolidCraft's mesher stopped at its bounds without saying so: an edge's
sampling at its depth (24 halvings) or node bound (20,000) with a chord still
wanting a split, a face's grid thinned to fit its point bound, a face's sag
refinement at the points it may add. These now fill solidcore-tess's own
reports:
- `PlanStats::edges_capped`: the edge, the bound, the farthest its chords lie
from its curve, how far its chords exceed their rules, and whether it is
within the tolerance. A capped segment is the only one whose rules are now
checked at the bound; the nodes are the same.
- `PlanStats::over_tolerance`: a face whose grid was thinned or whose
refinement ran out of points, with the bounds and the sag its final
triangles reach (measured as refinement measures it, at each triangle's
middle and sides' middles), and how many middles could not be measured.
Tests, ported from solidcore-tess's own planner:
an_edge_stopped_at_its_depth_bound_is_named_within_its_tolerance and
a_face_whose_refinement_reaches_its_bound_is_named_with_its_sag. Triangle
counts are unchanged on the 1,107 kernel-case bodies and primitives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: no panic-prone calls in the moved plan evaluator
f64::clamp panics on NaN bounds, so Domain::clamp and wrap_into take max then min. The
derivatives and starting-grid vectors grow as they are filled instead of preallocating a
product that could overflow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: keep the reasons a face is refused for
- A face whose pole question the kernel could not answer is refused with that reason even
when planning then failed on it: the refusal is read before the plan's own error, which may
be a later symptom ("boundary crosses itself").
- A node the kernel's evaluator refuses (other than a pole node, which has its own rule)
refuses the face in TessPlan::evaluate with the node and the reason, instead of taking the
plan evaluator's normal, which can be made up where the derivatives do not evaluate.
- The poles' range check takes max then min: f64::clamp panics on a NaN bound.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A ball joint's yaw and roll turn about the same axis at pitch 0, so the
reach solver found no way to tilt it sideways. Reach now solves a ball
joint over a small turn from its start pose (a quaternion), converts it
back to yaw, pitch and roll nearest the start, and holds each within its
limits, so pitch limits act as a cone.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #13939
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gr84VuQWHKtTwJ7cC3CP3W
* Exploded views: move a part inside a turned component in the direction given (#13939)
An exploded view stores each move as a world translation, but an
occurrence's placement is relative to its parent component, so a part
inside a turned component moved along the component's axes: after turning
Frame 90 degrees, a move of 80 mm along x sent its Pin along y.
explode_moves now turns each translation into the parent's frame before
it is applied, so showing a view, its animation and the check for a shown
view all move the part the way the view gives. Top-level parts, and so the
automatic explode, are unchanged; a component placed more than once moves
its parts the way given in its first placement.
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>
* feat(ci): add automated build and atomic deployment scripts for Windows and Linux
* feat(ci): add automated build and atomic deployment scripts for Windows and Linux
Summary of Changes:
This PR introduces cross-platform automation scripts (update-solidcraft.sh for Linux and update-solidcraft.ps1 for Windows) designed for local scheduled task runners (such as cron or Windows Task Scheduler).
Key Features & Improvements:
- Isolated Worktrees: Fetches and compiles updates inside a temporary detached Git worktree, leaving the operator's active checkout and uncommitted changes completely untouched.
- Incremental & Force Builds: Tracks commit and configuration state under target/.solidcraft-build-state to skip redundant builds, with an explicit -ForceBuild / --force override.
- Independent Target Toggles: GUI (solidcraft), CLI (solidcraft-cli), and Web (solidcraft-web) can be enabled or disabled independently.
- Reliable Deployment & Backups: Uses staging files and backup rotation under target/<profile>/backup/ prior to binary replacement.
- CodeRabbit Review Hardening:
* Aligned Linux BUILD_WEB default to false.
* Corrected WebAssembly artifact paths to target solidcraft-web.wasm.
* Added strict error checking for git fetch operations and improved PowerShell binary swap rollback safety.
Validation:
- Test Suite Verification: Ran cargo test --workspace successfully across environments.
- Deployment Resilience: Simulated locked-file scenarios and cron/Task Scheduler runs to verify clean fallback and error propagation.
* docs: remove sudo examples and clarify non-privileged web deployment paths
* refactor: address CodeRabbit feedback on locking and remote fetch logic
* Update scripts/update-solidcraft.ps1
Resolves cleanup when force build is used a bit better and will clean up with various cargo artifacts.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix(windows): align test failure handling and exit code with Linux script
* fix(windows): protect Wasm target dir and check WebInstallDir overlap
* fix(windows): validate WEB_INSTALL_DIR overlap before any forced profile cleanup
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* claim: #13112
* Export View as DXF: a camera without fov is an orthographic view, as the docs say (#13112)
file.export_view_dxf read its camera into the render Camera type, whose
fov field had no serde default, so a camera without fov was refused with
"invalid camera". docs/mcp.md says a camera without fov is orthographic.
Camera::fov now defaults to 0 like roll and up, and the view commands'
Camera schema lists fov as optional with a default of 0, so the schema
and the reader still agree. Saved render cameras (SavedCamera) still
require fov.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch offset: tests for naming the driving dimensions (#12295)
They fail on main: `param` comes back as d1.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Sketch offset: param and param2 name the driving dimensions (#12295)
sketch.offset documented `param`/`param2` but never read them, so the
dimension was always dN. Name it as asked; refuse a taken or invalid name,
the same name twice, and param2 without distance2.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Sketch offset: refuse a non-string param name; a chosen param2 is made before a generated one (#12295)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #12179
* Test: a Line started on an arc or circle snaps along its tangent (#12179)
* Sketch: a Line started on an arc or circle snaps along its tangent there (#12179)
The Line tool's tangent inference worked only from a point outside the
curve. From a point on an arc (its end) or a circle, the cursor near the
curve's tangent direction now snaps to it with the T hint, and placing the
point adds the Tangent constraint, as the existing tangent snap does. A
harness test draws a line off a 45-degree arc end, where no H or V snap can
stand in.
---------
Co-authored-by: Ahmad Khan <ahmad.khan@shiritai.co>
* fix: A point or direction written {x, y, z} maps into a moved component like [x, y, z]
- Mapper::make reads the object form that point_param accepts, keeping expressions
- Mapper::dir and the axis direction read both forms with params::vec3
- test: object-form revolve axis origin (numbers, expressions) and direction, moved and turned
* fix: A pick written {x, y, z} sets the frame a direction turns with
- Mapper::pick reads both forms with params::vec3, so solid.hole's direction turns with the picked body (CodeRabbit)
- test: an object-form hole in a turned component picked from the root
* fix: A pick written {x, y, z} in a list maps into a moved component
- Mapper::picks takes a {x, y, z} element as a pick, as it takes [x, y, z]
- test: a fillet edge picked with {x, y, z} in a moved component
* test: The object-form hole is saved where and as the array-form one is
---------
Co-authored-by: P'tite Tête <asso@ptitetete.com>
* claim: #12048
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Measure: show a picked face's perimeter, split into outer and inner on a face with holes
The Measure tool and the quick measure readout showed only the area of a
picked face. They now also show its perimeter: the length of the visible
edges around the face, where each edge counts what Measure shows for that
edge on its own (r x angle for a circle or arc). A flat face with holes also
shows its outer perimeter and the total length around its holes.
inspect.measure returns the same values as perimeter_mm, outer_perimeter_mm
and inner_perimeter_mm.
Fixes#12048
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Measure: find a face's outer loop from boxes built in one pass
Each loop's box is now gathered in a single pass over the border points,
instead of rescanning every border edge for each loop the comparison
looks at. A face with many holes no longer pays loops x points to find
its outer loop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Group a face's border loops by their exact shared vertex points
The kernel ends every edge polyline on its B-rep vertex points, so two
edges of one loop share an end to the last bit. A bounds-scaled
tolerance could join a hole to the outer loop where they pass close.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Measure tests: use the flat_face_at helper the area tests already have
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: type an offset while placing parallel, horizontal, and vertical construction lines
A locked Offset box places the line at that distance and stores it as a driving dimension. Parallel lines get a point-to-line distance from the picked line; horizontal lines get a Y distance from the sketch origin; vertical lines get an X distance from the origin. The side follows the cursor, and editing the parameter later keeps that side.
* Sketch: a changed construction offset keeps its reference and reaches the new distance
Editing the parameter a point-to-line distance names holds that line, so the offset point takes the new distance instead of tilting the reference. The axis and parallel hints are translated for the complete catalogs.
* Sketch: a typed construction offset stays an infinite line
The field beside the pointer is the distance from the reference. Typing it places an infinite construction line at that distance, with the driving constraint in the same undo step, and does not pin the click as a segment start. The length box remains the control that makes a dimensioned segment.
* Sketch: keep a typed length as the construction default
A missing or null offset is through the click, and an empty Offset box no longer blocks placement. A bare digit still fills Length, so two clicks draw a dimensioned segment; Offset takes a digit only after that box is focused. Parameter edits hold a construction offset's reference, and an expression match that holds nothing no longer hides a later direct dimension.
---------
Co-authored-by: railconstr <335242259+railconstr@users.noreply.github.com>
* claim: #9035
Co-authored-by: Cursor <cursoragent@cursor.com>
* Round dragged Distance values to Length decimals (#9035)
Snapped arrow/extrude/fillet drags wrote raw f64 text (1.2000000000000002 mm). Format through design_length micrometre clean-up and Length decimals (never coarser than the snap step). Also covers #9036.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ui): only snapped drag writes use Length decimals (#9035)
Restore full-precision model() for computed field writes; keep model_drag for snapped arrows. Update the extrude selection test for padded drag text.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Keep the exact depth when the extrude arrow stops on a face
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Project: an edge saved between numbered faces of an older design is found again (#13164)
0.30.1 split each round face of a revolve into numbered pieces, so a projected
edge was saved as `F4:side:l6#2|F4:side:l7#2`. The faces now evaluate whole and
the same edge is `F4:side:l6|F4:side:l7`, so the sketch failed and everything
built from it. A Sketch Project edge link now finds it by the whole faces' name,
only when the saved point lies on the edge and the faces have no other piece;
other edge references keep the stored name as picked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Project: tell whole faces apart by name, not by the mesh (#13164)
The edge is verified by its name, so the faces it separates are the
whole stems of that name: check each has one face, with no mesh lookup
(mesh-decisions refused `.mesh()` here). Suggested in review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(blend): rounds whose ball rolls over a smooth far edge of their face, the whole length (failing)
Concave: an L step, a wall standing on a strip whose far rim is rounded first, the wall's foot
then rounded larger than the flat the rim's round leaves, so the ball rolls off the flat onto the
rim's round along the whole edge and the strip is consumed. Convex: a block whose top's front rim
is rounded first, the back edge then rounded larger than the flat left, the ball touching the
rim's round from inside. Today both are refused (the rail leaves its face).
Expected: valid at CheckLevel::All. Concave: the volume the closed form of the section (Green's
theorem over its arcs, checked against a polygon) to 1e-6. Convex: every face the closed form to
1e-9 (the round's area and centroid, the rim's round's band, the back face), and the volume within
the end faces' trims' tolerance. Controls (the round within the flat; no rim round) build today.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLpvyVCQEUJSVmETqNuK5e
* fix(blend): a round's rail rolls over a smooth far edge of its face, the whole length
A rolling ball's support on each side is a chain of faces meeting smoothly, not one face. Where
the face beside a lone constant round is narrower than the ball reaches and its far edge (running
the filleted edge's whole length) is smooth to the face beyond, the solver now rolls the ball on
the face beyond before its trace: that side's offset surface is the face beyond's, the rail is
traced, classified and trimmed on the face it lies on, and the face it rolled off is consumed.
The end corners solve such a rail's end on the next side edge, across the rolled-over edge's
vertex, as a rail continues across a seam's vertex. Before, the ball rolled on the face continued
and the round was refused as a blend leaving its face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLpvyVCQEUJSVmETqNuK5e
* fix(blend): a rollover onto a face whose offset by the ball folds is declined
A convex round larger than the convex round beyond the face beside it cannot roll onto that
round: its ball, inside the material, would have to fit inside it. The rollover now checks the
face beyond before taking it: the face's offset by the ball's signed offset must not fold across
the edge (1 - kappa d > 0, kappa the face's normal curvature square to the edge). When it folds
the side is left to the refusals as before, so a round too large for its flat is refused as too
large with the flat as its fit (the crease trim's CreaseAcrossTangentEdge where that applies),
not sent down the rollover to fail there with BallDoesNotFit, NoRoom or a bare 1001.
Restores wr696_fit_sizes::a_round_onto_a_tangent_round_fits_where_its_rail_stays_on_the_top
(a large front round on a block whose back edge is rounded smaller). New test: two convex rounds larger
than the round beyond, refused with the flat as their fit (failed before: NoRoom, 1001).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLpvyVCQEUJSVmETqNuK5e
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #13962
* Kernel: report knots in correct order and interval for reversed offset curves (#13962)
Co-authored-by: Antigravity <antigravity-agent@google.com>
* Kernel: preserve boundary knots for reversed offset curve queries (#13962)
Expand base query interval by EFF_ZERO to guard against floating-point roundoff dropping boundary knots when mapping reversed query intervals. Filter mirrored knots directly in the target interval.
Co-authored-by: Antigravity <antigravity-agent@google.com>
* Bless curve.offset_curve golden for reversed knot order (#13962)
OffsetCurve::knots now mirrors and orders knot sequences and filters against the mapped query interval when reversed. 16 generated test cases (gen0073, gen0078, gen0237, gen0441, gen0480, gen0605, gen0680, gen0701, gen0791, gen0969, gen1274, gen1381, gen1406, gen1670, gen1673, gen1742) in curve.offset_curve changed outputs to reflect the correctly ordered, mapped knots and multiplicities on reversed curves.
Co-authored-by: Antigravity <antigravity-agent@google.com>
* Avoid panic-prone Vec::with_capacity in OffsetCurve::knots (#13962)
Replace explicit Vec::with_capacity and manual push loop with iterator filter_map and unzip to satisfy the zero-panic site check.
Co-authored-by: Antigravity <antigravity-agent@google.com>
---------
Co-authored-by: Antigravity <antigravity-agent@google.com>
* frame: publish the drag band through the registry; dialog tests to the host
frame.rs called `crate::scenario::publish_handle`, the app's re-export of
`solidcraft_ui_registry::publish_handle`; it now calls the registry directly.
Its tests drive the app (Harness, dialogs), so they move to the host module
dock_dialog_ui_tests.rs unchanged. frame.rs now uses only egui and the
registry, ready to move with the kit (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Menus: the list-menu drawing moves into the kit's menu module
`menu_frame`, `button`, `button_enabled`, `list_row` and `list_row_lit` drew
kit rows from `context_menu.rs`; they now live in `ui_kit::menu` with the same
signatures, and the Item-to-row mapping is `MenuItem::from_item`. context_menu
keeps SolidCraft's menu contents and dispatch and calls the kit's drawing.
Every caller uses `crate::ui_kit::menu::…`. The i18n literal scanner reads
`menu::button`/`button_enabled` as kit calls, so its context_menu carrier rule
goes (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit gallery: move it out of ui_kit into the host
The gallery draws the kit with SolidCraft's data (tab icons, the workspace
and radial menus, a sample app), so it is now the host test module
kit_gallery.rs, same elements and test names. The kit opens what it reads:
`file_view`, `preferences_cards`, `preferences_figure_data`,
`preferences_figures` and the test fixtures become `pub(crate)` modules, and
`Window::content`, `Confirm::content`, `panel::page_list`, `card_look` and
`card_width` become `pub(crate)` (#14251).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Format the moved gallery and dock dialog tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* UI kit: draw icons from a host-installed icon set
The kit ships no artwork. Its renderer (ui_kit::icons) parses and
rasterises SVG masters from an IconSet the host builds from its own
tables (colour roles, palette, accents, colour-vision variants, masters,
aliases, default accents, the danger ramp) and installs once per process,
like the message catalogs. With no set installed the painters draw
nothing; an unknown name draws the set's placeholder master, as before.
SolidCraft keeps compiling its masters in build.rs into icon_svg::SET
and installs it in both app mains, in SolveApp::new and in
fonts::test_context. icon_svg and icons keep their names as SolidCraft's
lookups and re-exports, so host call sites are unchanged. The renderer's
internal test moves to the kit on a tiny test set of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* UI kit: kit and components draw icons through ui_kit::icons
The kit modules, the ribbon, the application bar and the view compass
call the kit's own icon painters and primary-stroke lookup instead of
reaching into SolidCraft's icons and icon_svg modules. Only the paths
change; the code-drawn glyphs (assets/icons/ui/source) are still reached
through crate::icons until where they live is settled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Sphere tile: size the name's label from a laid-out row
The tile reserved two rows for the material name from the font's raw row height, while layout
rounds each row to the pixel grid. With egui's default faces the two-row name (26.0) overflowed
its label (25.3); a laid-out row gives the height the galley will have, whatever face the host
installs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit tests: run on a kit test context; tests of SolidCraft's use move to SolidCraft
The kit's and the components' tests no longer reach the app (#14251):
- `ui_kit::test_support::context()` is egui's default faces with the kit's weight families bound
(`type_face::install_weight_families`), no Plex, no icon set. It still installs the English
catalog through `crate::i18n` (asked in B-1). The generic frame, pointer, key and shape helpers
move there; `crate::test_support` re-exports them for SolidCraft's tests.
- Kit tests read the widget registry from `solidcraft_ui_registry` instead of `crate::scenario`.
- 35 tests that drive the whole app (scenario::Harness or SolveApp) move unchanged:
19 Preferences search tests to `prefs::settings_search_tests`; the Preferences pages/footer and
recent-colour persistence tests (3) to `prefs::kit_use_tests`; 6 universal panel chrome tests
to `panel_runtime/tests.rs`; the save prompt frames test to `prompt_cards_tests.rs`; the
dialogs/Preferences/menu checkbox parity test to `ui_kit_guard_tests::checkbox_in_use`; 5
navigation bar tests to `navigation_bar_view::tests`.
- 3 tests that compare against SolidCraft's icon artwork (the host's set) move unchanged to
`ui_kit_guard_tests::disclosure_artwork`: the two disclosure tests and the panel disclosure
crossfade.
Path-only edits in moved bodies: `super::super::checks::tests::*` and `ui_kit::panel_host::*`
become the `crate::ui_kit` re-exports.
Expected values changed: none (notes/B-expectations.md). The two tests that installed
SolidCraft's fonts directly (layout label column, growing form) use the kit context and keep
their expectations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit gallery: name the host module kit_gallery_tests
It is a test-only module, so it follows the crate's `*_tests.rs` naming; the
source guards that skip the gallery by name (colour literals) skip it again.
docs/ui-kit.md names the new module path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Colour guard: the menu swatch literal moved with the menu drawing
The one colour literal context_menu.rs was allowed (a menu entry's swatch,
labelled as the radial menu's shadow) now lives in ui_kit/menu.rs with
`MenuItem::from_item`; the allowance moves with it, same count, with the
right reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Colour guard: the icon renderer's literals moved into the kit
The white opacity tint moved from icons.rs to ui_kit/icons/mod.rs with
the painters, and the role colour conversion (the installed set's own
palette data) now lives in ui_kit/icons/svg.rs: the allowances move
with them, one each. The renderer's tests are named svg_tests.rs so the
guard skips them like every other test file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* UI kit: code-drawn glyphs are host-supplied too
The kit and components drew SolidCraft's code-drawn glyphs (transport,
window caption, badge, pin, cross, breadcrumb, compass axis tip, agent
pointer, brand mark) straight from assets/icons/ui/source. They now call
ui_kit::icons::glyphs, which dispatches to a Glyphs table of painters
(and the compass hit radius as data) the host installs beside its icon
set; before that every glyph draws nothing. The caption type is the
kit's own window_chrome::Caption.
SolidCraft fills icons::GLYPHS with its existing painters (nothing under
assets/ moves) and icon_svg::install installs both. The host's scale-1
axis_tip wrapper is gone; its two tests call axis_tip_scaled at 1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit gallery: kit specimens back in ui_kit, SolidCraft's in the host
The kit's gallery (ui_kit/gallery.rs) holds every specimen drawn from plain
data and moves with the kit; the radial and workspace menus and the settings
tab chips there are drawn from plain items and icon names. The host's
kit_gallery_tests.rs keeps what needs SolveApp: the radial and workspace
menus from context_menu, the history strip and browser, the continuity rows,
the command-search, menu, artwork and File view boards. It checks its
elements with the kit's `check_elements`. card_look and card_width are
pub(super) again; their tests stay in the kit gallery.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit tests: the kit's own English test catalog; translated tests move to SolidCraft
Message catalogs are host data, like fonts and icons (B-1). The kit test context no longer
installs SolidCraft's catalogs: `ui_kit::test_support::install_catalog()` installs the kit's
English test catalog (`ui_kit/test_support/catalog.json`), holding the one id the kit's tests read
(`ui.expand`, "Expand"), with the same English. The prefs window tests drop their own
`crate::i18n::install()` and use it.
Tests that assert SolidCraft's catalogs move to SolidCraft (12):
- `i18n_ui_tests::kit_translation`: German labels translated once, Japanese segment options,
the theme notice fitting German text.
- `fonts::cjk_loader_tests`: the four CJK font loader tests (SolidCraft's languages decide CJK).
- `prefs::settings_search_tests`: four search tests over French, Hebrew, Arabic and every
locale's Preferences labels.
- `navigation_bar_view::tests`: the slot descriptions in English, Japanese and Chinese.
They run on `crate::fonts::test_context()` as before. Only `a_label_is_translated_once` changes
its body: it reads the label a row paints instead of calling the kit-private `indented_label`,
with the same three expected strings. No expected value changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M2MmcDrkcWhQeHS2K8
* Kit gallery: every gallery item is test code, as the module is
The font-literal guard reads every ui_kit item not marked as test code. The gallery module is
test-only, but the items made pub(crate) for the host's gallery test lost their own
#[cfg(test)], so their specimen sizes were read as form metrics.
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>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #12569 (Section Analysis Distance slider)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Section Analysis: a slider on Distance moves the cut live
Once a plane is picked, a slider under the Distance field spans the
model's extent along the plane's normal. Dragging writes the field, so
the existing live preview follows and a typed expression is left alone
until the slider is touched. A value typed outside the model widens
the range instead of being clamped.
Part of #12569.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: part of #2829 (Press Pull, Replace Face schemas)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agents and scripts: Press Pull and Replace Face describe their exact parameters and replies
solid.press_pull and solid.replace_face get typed input contracts and
output schemas, so strict parameter checking refuses unknown keys and
agents can read each command's exact shape. Missing or malformed values
keep the handlers' own error messages. Both rows leave unconverted.txt.
Part of #2829.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The test pressed Insert Design for a saved part and then ran exactly three frames before asserting the linked component existed. The file pick and insert finish on later frames, so on a loaded merge-queue runner the assertion ran too early and failed a whole batch. It now polls for the body like the STEP half of the same test already does.
Changes a default: no
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #12406
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Mouse gestures: the standard views and Look At can go on the ring of tools (#12406)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Mouse gestures: Add a tool keeps its automatic choice with the views on offer (#12406)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Joint sweep: stop at a time or work budget, exclude pairs, name pairs by occurrence (#5963)
joint.sweep takes budget_s and returns stopped_by: budget with the last
value checked clear instead of failing on its work caps; joint.sweep,
joint.find_limits, motion.interference and inspect.interference take
exclude_pairs; reported pairs carry a_part/b_part places. Contact drives
stop at their budget only when asked (stop_at_budget).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Format the #5963 changes with rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A planar face picked as To went in as to_face, which brings the From point
onto the picked point, so the part slid across the face as well as onto it.
With To Snap Onto Plane (bodies only) the face goes in as to_plane {origin,
normal}: the bodies turn to meet it and move along its normal only. A To
that is not a planar face or a plane is refused.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Test: a sketch on a cap the join left uncertified is found by its name (#13323)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Test: import the plane trait (#13323)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Test: report the first feature that fails (#13323)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Test: the fixture carries its parameters (#13323)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* A sketch attached to an extrude cap by 0.30.1 keeps its plane when the join left the cap uncertified (#13323)
When the cap certificate finds no face, the attachment falls back on the face name it also
stores, with guards: the name finds a face made by the certified extrude, a saved point lies
on it, it still lies in the saved plane, and no body still has the certified cap.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Name the Vec3 type in full (#13323)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Test: pin the origin and x direction of the found plane (#13323)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Copy the saved plane instead of cloning it (#13323)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Old-cap fallback: an offset body stays lost
The fallback compared the cap with the body as built, before its offset, and
returned the saved plane. A body offset along the cap's own plane passed every
check and left the sketch at its old place. An offset body now keeps the
explicit lost-face error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* Component commands: tests for a feature named like another feature's id
move_features and browser.reparent given "2" act on the feature with id 2 even when another feature is named "2", and move_sketches.to_parts looks at the sketch numbered 2. These tests fail on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Component commands: a feature named like a number is that feature, not the one with that id
component.move_features, browser.reparent and component.move_sketches.to_parts read their feature keys with name_or_id_values and resolve them as the timeline commands do since #9748: a string is an exact name first, a number is an id. name_or_id_list, which turned every key into a string and left the lookup to try an id first, has no callers left and is removed.
The browser's Move Sketches to Their Parts item sends the sketches' ids as numbers: as strings they would now be read as names first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* component.move_sketches: tests for a sketch named like another sketch's id
component.move_sketches given "2" moves the sketch with id 2 even when another sketch is named "2"; that test fails on main. Two more pass today and guard the command's callers: Move Sketches to Their Parts moves the sketch it was given, and dragging a sketch onto a part moves the dragged one, when another sketch is named like its id. The to_parts test shares a fixture with the existing one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* component.move_sketches: a sketch named like a number is that sketch, not the one with that id
component.move_sketches reads its keys with name_or_id_values and resolves them as the other component commands now do: a string is an exact name first, a number is an id. Its two callers that passed ids as strings send numbers: Move Sketches to Their Parts, and the browser's drag of sketches onto a component.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move Sketches to Component: the parameter check takes sketch ids as numbers, as the handler now does
Main typed component.move_sketches' sketches as names only; this PR's handler reads names and ids (#12327), so the check and the schema follow it, with the handler's messages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: an intersected face's section keeps its link (#13471)
A face taken with `Intersect` is the face's section with the sketch plane: an open chain of
segments, not a closed boundary. The refresh that follows closing the sketcher read every
face link as a projected boundary, and the closed-boundary gates never accept an open
chain, so the link was marked lost and the sketch stopped following the body.
The closed-boundary rules now apply only to links whose geometry is a projected boundary:
`Project` and `Include` on a face. An intersected section keeps its link across a refresh
and follows the body when it changes.
https://github.com/tangency-labs/solidcraft/issues/13471
* test(loft): a loft between parallel squares has planar sides and converts to sheet metal (#13331)
Kernel: lofts of a 110 mm square to a 200 mm square and to an equal square,
120 mm apart, have six plane faces and the frustum's volume. Engine: the
reporter's hopper (loft, shell 1.5 mm open at both ends, Convert to Sheet
Metal with three corner bends) converts, and the prism has six planes. Both
fail on main: the skin gives B-spline walls, counted as "other".
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(loft): a two-section loft of lines with flat sides is built from planar faces (#13331)
The skin primitive returns every wall of a loft as a B-spline surface, flat
or not; in the record that is a spline surface, which nothing downstream
reads as a plane: inspect.measure counts it as "other" and Convert to Sheet
Metal refuses it as curved. A plane may not carry a NURBS form in the
record, and a tapered duct's sides are trapezoids that no plane
parameterises affinely, so the walls are built as planes rather than
relabelled: `planar_loft`, for two sections of lines in equal number, each
one loop, every side quad planar within 1e-9 of the loft's size, makes the
sides as planar faces on their 3D lines and the sections as the caps, then
stitches and orients them, as the pyramid loft does. Every other loft goes
to the skin as before.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* style(loft tests): rustfmt (#13331)
The test module's mount in alphabetical order, three long lines wrapped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* claim: sc#13790 boolean slice of the typed errors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore: Boolean failures carry a typed reason and cause chain, and is_port_failure follows it (part of #13790)
The Boolean operation no longer reports bare SMLib statuses. Its stage
failures return SolidError::Failed with a solidcore operation path and a
phrase, or a typed BooleanFailure (error/boolean.rs) for the relate, imprint,
split, close-loop and coincidence stages and the opt-in union solid-leak
refusal. The comparison status each site recorded stays the same (1006 for
what was null_pointer, 1001 for sm_err), so difftest and the goldens read the
same numbers. The collapsed tree failure keeps its cause through failed_after.
SolidError::is_port_failure now follows the cause chain, and solidcore-ops
session::answer uses it: a status error whose chain holds an unported call,
a missing preparation or a cancellation fails the call instead of being
skipped as an answer.
The SMLib names in the Boolean operation paths and messages are replaced
by solidcore names. The boolean goldens units read statuses through
sm_status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* solidcore tests: Boolean failures are read through sm_status, kind and operation, not through the Upstream variant or status text
Eight integration tests matched SolidError::Upstream or the text of a
Boolean error ("upstream status 1001", "code: 1001"). They now read the
comparison status, the operation path and the kind, which the typed errors
keep. The session answer test uses is_err.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* smlib: boolean failures keep their SMLib status and the ops answer rule is shared with the app (part of #13790)
solidcore now reports Boolean errors as typed `Failed` errors with a cause chain, so two app-side
readers of the old `Upstream` shape changed behaviour:
- `smlib::core::answer` skipped any error with an SMLib status, including a status error whose
cause chain holds an unported call or a cancellation. It now applies the rule of solidcore-ops'
`session::answer`, both through the new `SolidError::is_status_answer`.
- `blend::status` turned `Upstream{code}` into `Error::Status(code)`; a Boolean failure reaching it
from slice or sweep fell through to a message. A `Failed` from a `boolean::` stage is again the
status it stands for, and one with unported code in its chain is not supported.
Tests: the two rules in boolean_tests.rs, mirroring the session test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEtwSrdwZxqTQp5MfnU
* goldens: sweep.topology_sweep reads a typed failure's status, so Boolean failures inside the whole-B-rep sweep keep status 1001
The runner's status mapping had no arm for SolidError::Failed, so the three
non-manifold whole cases whose Boolean step now fails with a typed
BooleanFailure reported -1 instead of the SMLib status 1001.
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>
* CI: the format job no longer builds the app to check translations
The "Format and repository checks" job ran `cargo xtask i18n --check`, which built a release solidcraft-cli (now with the vendored solidcore kernel) to read the command registry: 514 s and 788 s on two runs today. Every pull request and merge-queue batch waits on that job.
The check is split. The format job runs `--no-registry` (catalog coverage only). The Native Linux tests job, which already builds the workspace in the ci profile, builds the CLI there and runs `--cli <binary>` for the command labels, when a package that feeds the command table is selected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n check: say command labels were not checked under --no-registry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* blend: a round its certificate cannot prove keeps no definition and is built, as #13695 describes (#14000)
A round's exact definition is certified to rounding. Where the certificate cannot prove
the net on its class's surface but the net lies within the body's tolerance of it, the
round is built and keeps no definition (#13695); only a net further off is an error.
`keep_round_if_proved` did that. Since #13900 `keep_definition` calls
`round.recertify(..)?` instead, for the circle class's certificate on its own torus, and
any other class's failed certificate refuses the fillet.
A round of the canal class depends on it: an offset plane cut by an offset cylinder not
square to it, so the spine is an ellipse. `check_exact_net` proves the spine on both
offsets to rounding (`spine_on_offsets`, no tolerance of its own), and a spine computed
in floating point fails it in a band of tilts. A boss turned 0.5 to 3 degrees off square
to the block it stands on, its root rounded, was refused with "Fillet Surface
Computation Failure" (status 1001); it built on the parent.
The documented behaviour is back for a failed certificate: `keep_round_if_proved`, with
`ExactRound::miss`, `net_miss` and `Implicit::distance` that it needs. The circle class keeps #13900's certificate on its own torus:
its refusal is not `check_exact_net`'s and stays an error, as does a net further off its
class than the tolerance.
Test: `a_round_on_a_boss_tilted_a_few_degrees_is_built` (solidcraft-kernel), the boss at
0, 0.5, 1, 2 and 3 degrees: refused at 0.5 before, every tilt built after, valid, and
meshed at a 1e-4 chord to the same volume; 0 degrees against Pappus's closed form to
1e-9 of the body, the tilts within 1 % of it (the corner's area changes to first order
in the tilt and cancels round the root). The kernel cases (1081) report identically
before and after.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLpvyW6gE7DcVNPHLaqorN
* blend: a chord's miss measured against its plane, as its certificate checks it (#14000 review)
`ExactRound::miss` measured every class against the supports' class surface, while
`recertify` checks a chord against the plane through its contacts (`chord_implicit`).
`keep_round_if_proved` reads `miss` only after `recertify` refused, so a chord net off its
plane could be kept bare if it lay near the other surface. `miss` now takes the surface
`recertify` checks. The mismatch predates 59a85a164; the restored function carried it.
`net_miss`'s doc now states it is sampled: it can miss a deviation between its samples,
bounds nothing, and decides only whether a refused net is kept bare or is an error.
Tests unchanged and green: a_round_on_a_boss_tilted_a_few_degrees_is_built, fillet_exact
(13), issue_2489_blend_faces_are_definitions (2), noisy_round_tests (2).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLpvyW6gE7DcVNPHLaqorN
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #13623
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: tests that the panic-site check reads Windows paths and its list inside SolidCraft (#13623)
On Windows, clippy names files with backslashes, so no span is a library
site, and git show does not resolve plan\panic-sites.tsv, so --base skips
the ratchet. Inside SolidCraft, git show <rev>:plan/panic-sites.tsv names
the repository's root, not solidcore/, so --base skips it on Linux too.
Three of the four new tests fail; the fourth, CI's base commit of
solidcore/ alone, passes and stays so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: the panic-site check reads Windows paths and its base list inside SolidCraft (#13623)
library_span matches clippy's file names with backslashes turned into
slashes. base_list asks git for the list with a POSIX path, relative to
the root it checks first (solidcore/ inside SolidCraft), then at the top
of the base commit (CI's commit of solidcore/ alone).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(fillet): a blend whose leg runs past a wall's foot builds by the trim at creases
A chamfer on a step's rim wider than the step has its leg on the step's plane past
the step's foot, under the wall. The subtraction builds it (the step's plane carried
on to the leg, d^2/2 per length taken off), but the trim at creases refused it, for
two reasons in sm_Extend's extension of the step's plane:
- takes_off probed the material a hundredth of the run's length beside the run. On
a strip narrower than that (the leg just past the foot) the probe landed on the
strip's far side, on the body's boundary, and the one loop that closes was
refused ("does not close on the face in one way"). The probe now stays within
half the loop's width across the run, measured on the loop's own points.
- the extension's runs from the chain's ends to its feet lie on the walls beside it
(the bar's end faces), but only the sheet's section was imprinted there. That
section ends inside the wall, so nothing fenced what the blend takes off it and
the trim refused ("reaches every face of the body"). The runs are now imprinted
with the section.
The trim and the subtraction now give the same solid there.
Tests:
- chamfer_past_wall_foot_trimmed (new): the stepped bar of
wr631_cliff_overflow_choice, chamfers 3.2, 4 and 4.9 on the 3 wide step's rim, at
the closed form d^2/2 times the length within 1e-6 relative, a manifold solid,
check_brep Level2 clean. Before: refused.
- wr695_overflow_trim_at_creases: chamfer_reaching_every_face_is_refused becomes
chamfer_past_the_step_foot_builds_as_the_subtraction (both paths at 1540).
- issue_2715_refused_fillet_keeps_geometry: its refused-trim example moves to
wr696's backed block (a round onto a tangent round, CreaseAcrossTangentEdge),
still refused by the trim.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLpvyUWYrVisqsvztWRkGu
* fix(fillet): an end whose rail runs past its side edge's far end overflows its end face
A round ending at an Nx1 corner against a riser lower than its reach has its rail
past the far end P of the side edge between its face and the end face. The End
refused it: "the round's contact line runs past the end of its side edge at the
corner (onto the faces beyond)".
The cliff overflow already builds this end: an overflowing blend's End is left open
and the trim at creases cuts it by its end face's plane. But EndSectionsFitTheirFaces
(the fork's a307480), which makes an end that runs off its end face an overflow,
locates each rail's crossing of its side edge at the vertex, and leaves the end
unjudged when there is none, as for a rail past P. Now a rail with no crossing
whose end beside the vertex projects past P on the side edge's line, where the end
face has a concave edge at P (something stands on it, so the section runs under
material), makes the fillet overflow its end face.
Test round_end_past_side_edge (new): a stepped block whose raised top edge ends
against a riser 2 high, radii 2.5 to 6; and a pocket's wall as the end face with a
hole beside it cutting the rail's face away past P, radii 3 and 4. Each at the
closed form r^2 (1 - pi/4) times the edge's length within 1e-6 relative, a
manifold solid, check_brep Level2 clean; radius 1.5 (the rail stays above P)
unchanged. Before: refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLpvyUWYrVisqsvztWRkGu
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: native Line definitions enclose their projected points
The projected pair search needs an enclosure of a Line edge's native
definition over a parameter interval. AffineDefinition::jet_over gives it
for analytic Lines, in curve/native_affine.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: projected curve-pair maxima return the pair's true maximum
The projected curve/curve maximum returned points that a strictly better
feasible pair beat (#13698). A branch-and-bound search over both
parameter domains now returns a candidate only when the global upper bound
fits that candidate's own arithmetic enclosure, and refuses otherwise.
35 curve.curve_p9 and 12 topology_solver_p2 golden records change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: refuse a projected face maximum its surface does not attain
A face answer comes from a traced silhouette point. On the full torus that
point measures 1.94e-6 below the value it reports (#13678). The solver now
evaluates the point on the native surface and refuses with Unsupported
when the two differ by more than 8 ulp of the objective's scale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record the pair maxima, their native witnesses and the torus refusal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: picture two curve pairs, add the gen1839 oracle, check both All pairs
Review findings on #13963 (CodeRabbit):
- Pictures: gen0011 and gen1086 before and after, same view per row,
drawn from the case inputs; each drawn pair re-measures exactly to its
golden value.
- The gen1839 comment cites projected_pair_monotone_witness_3060.py, which
was missing. It is added; it reads projected_pair_all_cases_3060.json
and reproduces 4.4080155996034689 at the expected corner.
- The finding said every value is a certified maximum and counted 39
answers. An All request's further answers are retained points, not
maxima, and the 35 rows hold 37 answers.
- original_affine_all_cases_have_complete_near_best_corner_sets now checks
each answer's native distance and both returned pairs of gen2050 and
gen2585 directly, not only their count and values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* query: whether an edge's faces meet tangentially, proved, and the turn and convexity at a vertex (#3141)
query: tangent_edge's verdict is enclosed between its points; convexity read on the material side.
ops: tangent chains on record order, and the face chain across smooth edges.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* query: tangent_edge grows its vectors as it fills them (panic-site inventory)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* sweep: stations from laws, exact on lines, arcs, helices and corners (#3139)
Moved from tangency-labs/solidcore#3188.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* sweep: stations from laws, exact on lines, arcs, helices and corners (#3139)
Moved from tangency-labs/solidcore#3188.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fmt tangent_edge.rs after merge
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #3188 follow-up: the caller's twist and scale limits in Laws; rail helpers crate-visible
- Laws::max_twist and Laws::end_scale_within: the caller's limits (SolidCraft's four
turns and 0.01..100), refused past by name; defaults take any finite twist and
positive scale. Ops passes its constants, no float checks there.
- rail_crossing (now returning the rail's parameter too), rail_frame, Map and apply
are pub(crate) for the rail-guided loft (#3140).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #11471
* Customize Toolbar: each command in the list shows its ribbon icon beside its name (#11471)
The Customize Toolbar window listed commands by name only. Each row now
draws the command's ribbon icon between the check box and the label, so a
row matches the button it adds. ui_kit gains checkbox_icon for this; the
icon is part of the click target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Customize Toolbar: a row shows the icon its ribbon button shows now
A row took the command's static icon. A ribbon button can show another
one while its command's state changes it: Play History shows Stop while
History plays. The row now resolves its icon through
interactive_command_info, as the ribbon does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10749
* Batches refused before their first step say the design is back where it started (#10749)
A batch refused before its first step (an unknown command, a step without a command, a broken
contract under strict checking) replied `rolled_back: false`, which docs/control-protocol.md
reads as "the checkpoint could not restore it". Nothing ran, so the design is where the batch
started: the reply now says `rolled_back` as a rollback that went back does, `true`, or `false`
when the batch asked for no rollback, in the control channel's `engine.batch` (document and
layout batches), the MCP `batch` tool (in-process, headless layout batches and bridged at strict
checking), the MCP's refusal of a step that can't be one and its refusal of an atomic layout
batch it can't run. Both docs now give `rolled_back` the same meaning.
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* claim: 10353
* Hole dialog: the tap drill follows the chosen inch tap size (#10353)
A UNC or UNF size in the Hole dialog showed its tap drill only in
millimetres (1/4-20 UNC: "5.1054 mm"), so an inch user could not
recognise the #7 drill. Unified sizes now read in inches ("0.201 in"),
with the millimetre bore on hover; both side by side did not fit the
form. The catalogue, the bore and saved designs are unchanged; metric
and G/Rp sizes keep millimetres.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10046
* geom: a NURBS curve checked when made, split into exact Bézier pieces in one pass; knot insertion keeps a weight's precision (#10046)
- insert_knot takes both factors from knot differences: 1 − α near 1 lost a
heavy weight's precision (8.3e-4 mm off on a 30 mm curve beside a weight of 1e12).
- NurbsCurve (private fields, from_homogeneous checks degree, knot count and order,
domain, finite points within 1e12 mm, positive weights) and pieces(): one pass
(NURBS Book A5.6), linear in length, rational or not, clamped or not.
- Clamping and the split run in outward-rounded intervals, so each piece holds the
exact curve as given; one implementation of insertion and clamping serves f64
and interval points (Blend).
- The hidden-line views use pieces() in place of their own quadratic split.
Tests against an independent double-double de Boor evaluation, including a fuzz
over 3000 random curves, valid and not.
* geom: a NURBS curve refuses a broken inner knot, a knot vector with no room to clamp and a weight too small to divide by when made; the heavy-weight insertion tested (#10046)
Review of #10047:
- `insert_knot`'s factors from knot differences had no test of their own: the heavy-weight
case used a curve already clamped. `boehm_insertion_keeps_the_curve` now inserts beside a
weight of 10^12, which `1 - α` puts 8.3e-4 mm off.
- `from_homogeneous` refuses an inner knot repeated more than the degree (before, only
`pieces()` did), knots too many to clamp (`pieces()` refused those as "too many knots"),
and a subnormal weight, which was reported as a point that isn't finite.
- The random test checks every piece, not the first two.
`Seg2::closest_param` took the lowest of 65 samples and ran Newton from it. The lowest sample can
sit on the wrong part of the curve, Newton could walk away from a right answer at an end, and a
closed segment could be held at the wrong end by the clamp.
Arcs are now exact, from the angle. Other curves are refined from every dip in the samples, the
ends counted, each within its neighbours. Lines are unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Main (#5576) already hides sketch and construction rows past the marker,
and keeps feature rows listed after a manual Roll Back Here. This adds the
missing regression test for sketches after timeline.roll_to.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Tabs: a middle click closes a design's tab, as its × does
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tabs: a middle click on a tab's × closes it too (#7875)
The × lies over the tab and takes the clicks on it, so a middle click
there reached the × rather than the tab and left the tab open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7677
* Sketch: a rebuilt projection keeps what the sketch held on it (#7677)
When a projection changes shape, update_link rebuilt it by deleting the old
projected points and curves first, and every constraint on them went too: a
point held on a projected edge, a corner coincident with a projected point.
A line that shared a projected corner was left on a free point, so the sketch's
lines no longer followed the projection.
The rebuild now makes the new entities first, moves the sketch's own curves and
constraints onto the new entities that match the old ones (the same corner, the
same circle, the same side or the longest piece of it), and removes the old
ones last. Matched entities keep their ids, so the solve after it sees where
they moved from and names still find them. Curve points with no match keep a
free point where they were; only constraints on entities with no match go. A
failed rebuild leaves the sketch as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* link_rebuild: import Vec2 from solidcore_record and the geom prelude
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Review follow-ups on the render export: a synthetic case without a name
writes nothing instead of panicking; a rerun removes the numbered files a
previous run left past the current bodies; a failed write is printed
instead of turning the case into "panicked", and the directory is checked
before the replay; an unexpected refusal writes its inputs too.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The bottom and left History hand a clicked feature to an open dialog whose active input takes features (Pattern, Mirror). The History rows in the Browser only selected it.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
replay() handled fillets, chamfers, moves and tool features; an Offset
Face fell through to "cannot be patterned or mirrored yet". Each copy
now moves the picked face points and offsets the faces found there.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #11430
* History controls: step through the features the bottom bar shows (#11430)
While a component is edited or picked, the bottom History bar shows only
its features (timeline_scope), but First, Back, Forward and Last stepped
over every feature of the design. They now step through the bar's own
timeline_fold::View, never move against their direction from a marker
outside the shown features, and stop playback when they leave the
marker. Preference history_steps_component (Preferences > Layout, on by
default) keeps the old stepping through the whole design.
Rebased onto main after #8886 rewrote the History bar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Move SolidCraft's mesher and its evaluator into solidcore (RFC 0003, item 1.1)
A move only, so history follows the files: SolidCraft's planner
(crates/kernel/src/tess/) becomes solidcore-tess's src/mesher/, and its plan
evaluator (crates/geom/src/tess.rs and tess/) becomes the kernel's
src/eval/plan.rs and src/eval/plan/, next to solidcore::eval. No content
changes; the next commits wire them in and remove solidcore-tess's own
planner, so one mesher remains.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: remove its own planner and plan, replaced by SolidCraft's
solidcore-tess's planner (src/planner/) and plan (src/plan.rs) were a port
of SolidCraft's; SolidCraft's own moved in with the previous commit, so one
mesher remains (RFC 0003, item 1.1). The next commit wires the moved code in
behind solidcore-tess's public API.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Wire in SolidCraft's mesher and evaluator; SolidCraft meshes through solidcore-tess
The moved plan evaluator is the kernel's `solidcore::eval::plan`, in the
kernel's vectors, beside `solidcore::eval` (where the two overlap, they are
unified in a later step). The moved planner is solidcore-tess's `mesher`,
behind its public API: `plan`, `TessPlan` and `FacePlan` (SolidCraft's plan
types, now in solidcore-tess's `plan`, with its `poles` field), `evaluate`
returning the mesh and the faces left out, `PlanStats` and its records,
`MeshTolerance`, `preview` and `simplify`.
- The mesher evaluates surfaces with the kernel's plan evaluator and edge
curves and pcurves with the kernel through solidcore-convert; it works in
the kernel's vectors and hands the plan the record's. It keeps SolidCraft's
text (rustfmt skips it) and allows the lints it does not meet yet, with the
reason; a panic in planning is caught at `plan` until the next step brings
checked access.
- SolidCraft's geom re-exports the evaluator as `geom::tess`, through one line
in xtask/solidcore-seam.txt (until RFC 0003 item 1.8 moves model-evaluation
callers to ops and tess queries), with converters between its vectors and
the kernel's and its partials in its own vectors; the B-spline basis
functions it shares with geom's splines moved into `eval::plan::basis`.
- The kernel crate meshes with `solidcore_tess::plan`; its `MeshTolerance` and
`DISPLAY_ANGLE` are solidcore-tess's, and its worker map that only the
mesher used is gone.
- Tests move with what they test: the planner's tests that build bodies with
SolidCraft's kernel are crates/kernel/src/display_plan_tests.rs, the plan
types' are in solidcore-tess, the evaluator's cross-check with geom's NURBS
surface is in geom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Kernel rig: the shared core names the mesher's new home
tests/kernel-cases/PATHS-ALL still listed crates/kernel/src/tess/**, which the
move emptied, and the rig plan's check fails a glob that matches no file. The
mesher and its plan evaluator are now solidcore-tess's src/mesher/ and the
kernel's src/eval/plan.rs and plan/; they are inside solidcore/** already, and
are named there so the move stays visible in the list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: the kernel decides the moved mesher's poles, once per iso-line
SolidCraft's mesher, moved into solidcore-tess, told a pole by the surface's
derivative speeds at the point, evaluated afresh at every question. It now asks
the kernel's certified classifier (solidcore::eval::collapse), which decides on
the iso-line through the point, and keeps each answer under its iso-line
(direction, other parameter, reach), so the kernel is asked once per iso-line
however many nodes and passes look at it. There is one pole check: where the
kernel cannot tell, the face is refused, named with the point and the kernel's
reason, and nothing is taken for an answer. On the 1,101 kernel-case bodies,
the primitives and 26 STEP files the kernel refused no point at either
tolerance. The speed-based checks the mesher used (`degenerate`,
`degenerate_within` in eval::plan::uv) are deleted.
Pole questions and kernel classifier calls (export): coil split by a plane
(793) 1,084,906 -> 1,393; coil through a cylinder wall (4505) 996,266 ->
12,877; boss-root fillet 54,244 -> 310; twisted closed sweep 80,112 -> 562.
The 160 spline-heavy bodies mesh in 8.0 s instead of 13.2-13.7 s
single-threaded, with the same triangles; all 1,107 kernel-case bodies and
primitives keep their counts.
solidcore-tess's pole pass is back on this mesher: a pole of higher order
gets the kernel's exact limit normal per triangle, or the mean of its
triangles' normals where the kernel has none (named in
PlanStats::averaged_poles); a normal neither rule gives refuses the face in
`evaluate`. At a degenerate vertex the CPU evaluation takes the kernel's limit
normal instead of a step inside. tests/pole.rs runs again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: a node just past the range is classified on it, within the face's tolerance
The kernel's pole classifier refuses a point off the surface's parameter
range, and the mesher meets boundary nodes a little past it: pcurves made to a
STEP file's uncertainty end there. A node past the range by no more than the
face's own tolerance (the largest of its edges' and vertices') is classified at
its nearest point of the range, where it lies on the face to within that
tolerance. Farther out, the face is refused with the distance named; nothing is
extrapolated and there is no other pole check.
On the 319 files of solidcore's STEP bench corpus this classifies the six
faces the classifier refused: tork's faces 217, 243, 250 and 269 (nodes at most
9.8e-7 mm off, tolerances 1e-3 mm and up), nist_ctc_02_asme1_rc's face 647
(0.113 mm, tolerance 0.119 mm and up) and nist_ctc_05_asme1_ap203's face 143
(0.034 mm, tolerance 0.051 mm and up). No face of the corpus is refused, and
the 1,107 kernel-case bodies and primitives keep their triangle counts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: ask the moved mesher's pole question once per node
The sag refinement and the flips ask, at every triangle corner, side and
round, whether a node is on a pole, and the self-crossing check asked it twice
per node, once for each chord the node ends. Whether a node is on a pole is a
property of its parameters: a `sag::Poles` memo, shared by refinement and the
flips of a face, asks it once per node, and `tangle::crossing` keeps each
node's answer for both its chords.
This is #13559's fix for solidcore-tess's own planner, which the move replaced,
carried over to the moved mesher with its test
(`the_pole_question_is_asked_once_per_node`: 27,452 questions for 349 nodes
without the memo, at most two per node with it). Triangle counts are unchanged
on the 1,107 kernel-case bodies and primitives, and the coil's test
(`the_spline_coil_keeps_its_triangles`) still passes.
Ported from #13559 by @bherbruck (co-authored by jyviko).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: no panic-prone calls in the moved plan evaluator
f64::clamp panics on NaN bounds, so Domain::clamp and wrap_into take max then min. The
derivatives and starting-grid vectors grow as they are filled instead of preallocating a
product that could overflow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore-tess: keep the reasons a face is refused for
- A face whose pole question the kernel could not answer is refused with that reason even
when planning then failed on it: the refusal is read before the plan's own error, which may
be a later symptom ("boundary crosses itself").
- A node the kernel's evaluator refuses (other than a pole node, which has its own rule)
refuses the face in TessPlan::evaluate with the node and the reason, instead of taking the
plan evaluator's normal, which can be made up where the derivatives do not evaluate.
- The poles' range check takes max then min: f64::clamp panics on a NaN bound.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7908
* Sketch: every slot adds a middle point (#7908)
Straight slots (centre to centre, overall, centre point) add a construction
line between the arc centres and a point held at its midpoint. Arc slots add
a point on the construction centre arc with equal construction chords to the
two cap centres, so it stays halfway along the arc. The middle takes no
freedom away and adds none; commands return it as `middle` (and
`centre_line` for straight slots).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: the slot previews show the middle's construction geometry
While a slot is placed, the preview draws what the command will make: the
centre line and middle of a straight slot, and the middle's chords on an
arc slot's centre arc.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: count an arc slot's freedom right with its middle; update slot scenario
Make the arc slot's middle point after the centre-line ends and before the
side ends, so the rank analysis takes the middle's two constraints on its own
coordinates before the cap tangencies (which hold only to the solve's
accuracy). Made last, a radius edit left the slot reported with one degree of
freedom too few; made right after the centre, `locate` placed the middle
before the start and fixed the start's x where it barely moves, so the slot
shifted. The slot_preview scenario counts the middle's construction curves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: the rounded rectangle adds a centre point (#7908)
The rounded rectangle now gets a construction diagonal between its
bottom-left and top-right corner arc centres and a point held at its
midpoint, so its centre stays in the middle when it is edited or
dragged. It adds no freedom. The slot tests now take Vec2 from
solidcore_record, where main moved it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch shapes: preserve typed replies for construction centers
Preserve current upstream behavior while applying the reviewed correction. Central native builds and strict workspace linting pass; remaining runtime and screenshot checks are recorded in the PR.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #13536 and #13541, offset reach refused by the certified-reach gate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Offset: a face with no neighbour is not refused for a reach it doesn't need (#13541)
A whole torus offset as one face meets only itself across its seams, so the
local reach has nothing to bound and returns None, as its docs say. It asked
the face's exact class first, at its seam vertex and for its move, and a torus
has none, so the move was refused as ReachUncertified. The neighbours are now
found before any bound is asked.
The torus face offsets again (offset_torus_face passes; its known-failing line
goes). Kernel test: a torus given a coaxial torus 0.5 wider moves to it, which
fails without the change with ReachUncertified.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #14227
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: Trim shows the piece a click removes on splines (#14227)
The hover keeps a free-form curve's cuts once per sketch state and shares
the code that splits the curve with sketch.trim, so both name the same piece.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: part of #2829 (surface.extend and surface.trim schemas)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Give surface.extend and surface.trim input and output schemas
Part of #2829.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Move: tests for a click on an arrow and for the total from the start (#GABE)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Move: test a click whose press and release arrive in one frame (#13101)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Move: a click on a triad handle shows the value it drives (#13101)
gizmo::show reports nothing until a drag starts, so a press and release that
arrive in one frame never reached dialogs_move::drag and the box stayed on X.
Record a press on a handle that starts no drag, and show its value in the box;
a click moves nothing.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Review: doc comment back on show(); drop the unrelated total test (#13101)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gizmo: take the clicked handle from the press position
A press and release in one frame can end with the pointer elsewhere, so
the handle under the press event itself is the one clicked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gizmo: take the pressed handle even when the pointer ends off the triad
The press hit-test now runs before the early returns for a pointer off
every handle, and only for a press in the gizmo's own layer and view, as
hover already required. The arrow click test gains a one-frame case that
moves the pointer off the triad after the release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Only the outermost command saves an undo step and clears redo, for
itself or for an undoable command it ran. A command run inside another
saves none, so a combine into a new component is one step and a failed
command leaves the undo and redo lists as they were. A command that
replaces the session (opening a design) keeps its fresh history.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* STEP export: test that a spline curve's closed_curve and self_intersect are written when known (#12147)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* STEP export: write closed_curve and self_intersect where the curve knows them (#12147)
A clamped spline now says whether its ends meet (.T./.F.), and a straight
two-point pcurve says it does not cross itself. Anything else stays .U.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* MCP export: accept iges, igs and stp as the format argument (#11806)
The export tool's format enum omitted iges, igs and stp although
file.export reads all three from the extension. Adds them to the enum
and the description, with a test that exports each.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* MCP docs: export table lists stp, iges and igs (#11806)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs: scx, a script-first MCP server for SolidCraft, with its benchmark results
Adds docs/scx-hybrid: a prototype MCP server (scx.py) that lets a model drive
SolidCraft with short Python scripts instead of single tool calls, the task
files it was measured on, and a write-up of the results (23-task benchmark on
Haiku 5.5, difficulty ladder L1-L9, L8 also on Sonnet 5 and Opus 5.5).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(scx): say what the check covers, the real stopping sequence and the code alias; scx.py reports bridge errors
- The 69/69 row is "passing the check": volume and bounding box per body, not each feature's place.
- `code` is the schema's name; the other names work only for clients that do not check the schema.
- The stopping rule paragraph lists the versions kept under the 5% bar (v10, v11) and where tuning stopped.
- scx.py: a JSON-RPC `error` from the SolidCraft bridge is raised as SCError, for scx's own tools
and for passed-through calls, instead of an empty reply that is not marked as an error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(scx): say which comparison the 5% bar is read on (v10's -6% is against a mixed baseline)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Faces: Context Menu Cleanup (#11795)
A curved face, or several selected faces, offer Press Pull in place of
Create Sketch, Extrude and Offset Plane. The face menu leaves out items
that cannot act on the selection instead of greying them out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Faces: test that the face menu lists only what acts on the faces (#11795)
A curved face, or several faces, offer Press Pull in place of Create
Sketch, Extrude and Offset Plane. Tests that expected greyed-out items
now expect them left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #11680
* Quitting with several unsaved designs asks once, in one list (#11680)
Quitting, quitting to install an update, and closing several tabs at once
now show one "Save changes to N designs?" window listing every unsaved design
in this window's tabs, each with a tick box, instead of one prompt per design.
Save Selected saves the ticked designs in tab order and closes nothing if a
location picker is cancelled or a save fails. One unsaved design keeps the
existing prompt, and a General preference keeps the old one-at-a-time prompts.
The control channel lists the rows and takes save_indices through ui.documents.
The headless test helpers now clear texture updates and use the test clock.
* Save changes to N designs: each path sits under its name, and a long list scrolls (#11680)
* Control protocol: say that an empty save_indices list still closes and discards (#11680)
* Save changes to N designs: each row has its own Save and Discard (#11680)
Each row now has Save and Discard buttons beside its tick box. Answering a row
closes only that tab; a cancelled picker or failed save keeps the row. The
window stays until the last row is answered, then the close or quit carries on.
The control channel takes save_row and discard_row.
* Control protocol: list save_prompt_one_at_a_time with the other ui.prefs settings (#11680)
* Save changes to N designs: follow main's theme tokens and preferences window (#11680)
The quit-one-dialog-per-tab test turns on the preference that keeps that flow,
since several unsaved designs are now asked about once, in one list.
* claim: #1458
* Agent chat: in the Snap, say why no agent is found instead of listing installs (#1458)
The strictly confined Snap can neither see nor start the coding agents
installed on the computer, so the picker came up empty with only install
commands that cannot help. Like the Flatpak, the panel and Preferences now
explain the sandbox, pointing to the AppImage/.deb/.rpm or an
OpenAI-compatible API agent, and the Snap shows no install list. The Snap
check is shared with the updater's (update::is_snap).
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
Review follow-ups to #13555 (issue #13629, from tangency-labs/solidcore#3217).
The test's time was in its three booleans (cut in turn 34 s, union of the
sweeps 46 s, cut by the union 37 s unoptimised), not the volume. The default
test keeps the cut in turn, the sequence SolidCraft runs, the corpus case
records and the kernel rig replays; it still fails without #13555 (NotSolid).
The cut by the union moves to its own #[ignore]d test, as kernel_cases.rs does
for its slow unions; it passes with --ignored. One helper grades both: one
solid, valid at CheckLevel::All, within the case's 1e-6 of its closed form,
the volume measured to 1e-7.
create_proto_edge now says at the trim_unsnapped call when it refuses (a
joined curve's own trim snaps further than the common interval allows, or it
is no B-spline to reparametrise) and why: the old code built an edge running
past its 3D curve there.
No change to what the kernel computes.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #12482
* Sketch: an unplaced dimension's value clears the line it measures (#12482)
The default place of a linear dimension's value was a fixed 22 px across from
the measured points. The value is drawn level on screen, so on a vertical or
slanted line a wide value reached back over the line. The default place now
moves out by the value box's extent across the line plus 8 px, and never less
than the old 22 px.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #11622
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Web app: the 3D view takes the Anti-aliasing setting, 4x by default (#11622)
The web app drew the view straight into egui's window pass, which has one
sample, so the view was 1x whatever Graphics said, and supersampling did
nothing either. It now installs the view the desktop's way
(install_viewport -> set_wgpu_split): offscreen, with its own 32-bit depth
buffer and the saved anti-aliasing, then copied into egui's pass, which has
no depth buffer now. WebGL draws 1x, as desktop OpenGL does, and Graphics
shows 2x-8x as not available there.
The desktop's sample-count rule moves to ui-egui's gpu::scene_samples so both
apps share it; gpu::scene_sample_choices leaves out the counts a backend
would draw at 1x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* defeature: a feature whose base patch is not built is kept and named, never left open (#3093)
Moved from tangency-labs/solidcore#3179.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* smlib: drop the retired defeature_refuses_open_base fix (tangency-labs/solidcore#3179)
The carried change retires UpstreamFixes::defeature_refuses_open_base: a feature whose base patch is not built is kept and named on every kernel, so the app no longer sets the flag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Port issue_3093_extrusion_defeature to main's certified volume sign
Soundness::volume is a VolumeSign enclosure on main (#13593), not an f64; the
test compares the enclosures (overlap across reaches, containment of the
measured value) instead of the numbers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* solidcore: SolidCraft #13519 case (a prism joined with its side leaning a hair off a wall)
Six cases for tangency-labs/solidcraft#13519: a box and a prism extruded
from XY whose side starts on the box's face and leans 0 to 1e-3 mm off it
at its far end, joined (leon-vv's reduction of the report). On SolidCraft
main the 7e-5 mm gap fails in make_faceuse and 3e-5 is refused by the
check. Volumes are exact: 4200 - 25 g by the shoelace formula.
* solidcore: #13519 case notes give the replay on SolidCraft main and kernel
solidcraft-cli built from main 0d490ffb and kernel 8f13e948 gives the same
results; at 1e-4 and 1e-3 the join comes out as two bodies.
* solidcore: #13519 control case's profile corner is 0.0, not -0.0
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* test: a whole turn's form from the quarter line its seam is a rounding short of; a tube measures the same after a STEP round trip (#12760)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: a whole turn's form starts on the quarter line its seam is within reach of (#12760)
A cylinder, cone, sphere or torus face of one whole turn had its NURBS form from the quarter
its seam's angle lies in, without the reach the partial-turn path ends on quarter lines with. A
STEP file's seam on its frame's x axis reads back a rounding short of the quarter line
(2pi - 2e-12), so the form started a quarter early: its four quarters ended a quarter short of
the seam's second use, the face's last quarter was off its form, and mass properties
integrated past the form's end (a tube read back measured 14 % over).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: the read-back centre at the measure's accuracy (#12760)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(kernel): a sweep's twist turns the profile about the path whatever plane it was drawn on (#12337)
A line along Y swept 40 along X with a quarter turn is the same helicoid
strip drawn on the XY plane (the path's own plane) as on the YZ plane (square
to the path): its far end stands along Z and its area is the strip's, 875.9.
Fails before the fix: on the XY plane the twist turned the line about the
sketch plane's normal, leaving a flat sheet.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(kernel): a sweep's twist turns the profile about the path, not the sketch plane (#12337)
`controlled_stations` twisted (and scaled) the profile about the profile
plane's normal, pointing the way the path leaves it. For a profile square
to its path (every solid sweep) that is the path's direction; for a surface
sweep of a curve drawn on the path's own plane it is not: the issue's line
along Y on the XY plane, swept along X with a 90 degree twist, spun in its
plane and gave a flat sheet (rc.2 refuses it), while the same line on the
YZ plane twisted about the path.
The axis is now the path's start tangent, as `SweepControl` describes the
twist ("about the path"); `direction_offset` measures its correction after a
corner about the run's direction too, as its own comment says. Profiles
square to their path sweep as before (`twisted_sweep_turns_in_proportion`).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* STEP header: test that a malformed header record does not stop the import (#11650)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* STEP header: skip a header record that does not parse (#11650)
The header loop failed the whole import on the first malformed record. The
header only describes the file, so skip to the next line that starts with a
keyword; with none left, the error stands.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* STEP header: stop skipping bad records at DATA, count lines once (#11650)
A header missing its ENDSEC; no longer reads the DATA section as header
records to skip, and line numbers for errors met while skipping are
counted from the last error instead of the start of the file.
Also cargo fmt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Fixes#11838
Changes a default: no
The surface loft dialog restricted section picks to PROFILES | CURVES and required sections to come from two or more sketches. Users could not loft between surface body boundaries, open sheet edges, or between a body edge and a sketch curve. Furthermore, when re-editing an existing surface loft feature created with body edge sections, the dialog failed to restore the edge selections.
1. Update SelInput for sections in Surf::Loft to accept PROFILES | CURVES | EDGES.
2. In commands(), chain picked edges alongside sketch curves and map Sel::Edge into {"body": body, "edges": [points]}.
3. Replace the strict sketch-count check with a check for >= 2 total valid sections.
4. In for_feature(), resolve section body edges back to Sel::Edge when re-opening the dialog.
5. Add unit regression test surface_loft_with_body_edges_and_sketch_sections in loft_rails_dialog_tests.rs.
* claim: #12075
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* wip: Project accepts construction points (#12075)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* wip: count construction-point links in the read set (#12075)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Project: a construction point is brought into the sketch's frame and its component's placement is read (#12075)
Review: a point owned by a moved component projected in the root's frame, and moving its component did not refresh a root sketch's projection. The sketch's frame is now passed to the link resolver, and the point's component is in the sketch's placement reads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Project construction point test: unground the part before moving it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Project: carry the construction-point frame through main's lost-name handling and the point reference form
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Sketch projection: keep the author occurrence with its frame
Preserve current upstream behavior while applying the reviewed correction. Central native builds and strict workspace linting pass; remaining runtime and screenshot checks are recorded in the PR.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brennen Herbruck <brennen.herbruck@gmail.com>
The extra navigator style, its drawing and picking code, its Preferences and
right-click menu choices and its view.navigator value are removed. The
signed-axes and custom-body navigators are unchanged (Custom shows the signed
axes until a body is chosen). A navigator style this version does not know
loads as Axes; ui.prefs and view.navigator refuse one with an error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:09:48 -05:00
978 changed files with 62175 additions and 17414 deletions
SolidCraft is a clean-room, pure-Rust parametric 3D CAD app (sketch solver, feature timeline, B-rep kernel, an egui UI that agents drive). This file is a router: the hard rules, then the skill to read for your task. Humans: CONTRIBUTING.md.
## Start here
Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelled `help wanted`, `good first issue`, `needs-triage`; architecture in `docs/architecture.md`. Local `plan/` notes are optional.
Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelled `help wanted`, `good first issue`, `needs-triage`; architecture in `docs/architecture.md`.
## Hard rules (what enforces each)
- Independent: never copy another product's code, icons, text or templates; never drive or take data from commercial CAD. `skills/solidcraft-ground-rules/SKILL.md`.
@@ -15,7 +15,7 @@ Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelle
- Layering; only `crates/kernel` uses truck; solidcore only via `solidcore-ops`/`-tess`/`-record` (`xtask/solidcore-seam.txt`): `cargo xtask layers`; see `docs/layers.md`.
- One outer UI container, one camera path (`navigation::handle`), colours from theme tokens: `cargo xtask ui-containers`.
- Application code is Rust only and builds for wasm: `cargo xtask wasm`, `cargo xtask web-offline-test`.
- The product name is SolidCraft; the old name lives only in `docs/compatibility.md`.
- The product name is SolidCraft; the old name lives in `docs/compatibility.md`.
- Sorted lists (message catalogs, `mod` lines, languages) stay sorted: `cargo xtask lists`. Never edit generated files (`docs/features.md`, `THIRD-PARTY-LICENSES.txt`) in a PR.
- A design-format change is ONE new file in `crates/doc/src/format/pending/`: `scripts/test-format-pending.sh`.
- A command has input and output schemas (will become required; `schema/unconverted.txt` only shrinks): `skills/solidcraft-commands/SKILL.md`.
@@ -37,6 +37,7 @@ Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelle
| A command, a crate boundary | `skills/solidcraft-commands/SKILL.md` |
| SolidCraft or solidcore, kernel, geometry | `skills/solidcraft-layers/SKILL.md` |
| A dialog, panel, camera, icon, colour | `skills/solidcraft-ui/SKILL.md` |
| Sheet metal | `skills/solidcraft-sheetmetal/SKILL.md` |
@@ -706,8 +706,6 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
| docs/design/preferences/figures/mouse-mapping.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-axes.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-axes.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-cube.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-cube.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-centre.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-centre.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-cursor.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
"prefs.invert_curved_roll_arrows_note":"سهما التدوير المنحنيان عند زاوية المكعب يدوران العرض داخل مستوى الشاشة. عند التشغيل: سهم التدوير مع عقارب الساعة يدور عكسها، وسهم العكس يدور معها. عند الإيقاف: يدور كل سهم بالعرض حسب انحنائه.",
"prefs.invert_navigator_arrows_note":"تدوّر الأسهم المحيطة بالمكعب الكاميرا في الاتجاه الذي تشير إليه، كما في Fusion وInventor: السهم الموجود على اليسار يشير إلى اليمين ويدوّر الكاميرا إلى اليمين. عند الإيقاف: يدوّر السهم المكعب نحو الجهة التي يقع فيها، فيدوّر السهم الأيسر الكاميرا إلى اليسار. لا يتأثر سهما الدوران المنحنيان.",
"prefs.invert_curved_roll_arrows_note":"سهما التدوير المنحنيان عند زاوية أداة التنقل يدوران العرض داخل مستوى الشاشة. عند التشغيل: سهم التدوير مع عقارب الساعة يدور عكسها، وسهم العكس يدور معها. عند الإيقاف: يدور كل سهم بالعرض حسب انحنائه.",
"prefs.invert_navigator_arrows_note":"تدوّر الأسهم المحيطة بأداة التنقل الكاميرا في الاتجاه الذي تشير إليه، كما في Fusion وInventor: السهم الموجود على اليسار يشير إلى اليمين ويدوّر الكاميرا إلى اليمين. عند الإيقاف: يدوّر السهم العرض نحو الجهة التي يقع فيها، فيدوّر السهم الأيسر الكاميرا إلى اليسار. لا يتأثر سهما الدوران المنحنيان.",
"prefs.keep_unsaved_also_asks":"السؤال أيضًا عند إغلاق علامة التبويب",
"prefs.keep_unsaved_also_asks_tip":"عند إيقاف هذا الخيار، تُغلق علامة التبويب دون مربع حوار المتصفح بعد تحديث نسخة كل تصميم غير محفوظ.",
"prefs.keep_unsaved_in_browser":"الاحتفاظ بالعمل غير المحفوظ في هذا المتصفح",
@@ -2745,7 +2744,6 @@
"prefs.move_drag_highlights_tip":"عند الإيقاف: لا يُميَّز أي شيء تحت المؤشر أثناء سحب مكوّن بأسهم أداة التحريك، فتبقى التجميعات الكبيرة سلسة. مع عرض مقطع أو تحليل يبقى التمييز كما هو، ولا يُميَّز شيء أبدًا عند سحب جزء على امتداد وصلاته.",
"prefs.navigator_fit":"ملاءمة النموذج بعد تغيير العرض من متصفح العرض",
"prefs.navigator_fit_note":"النقر على وجه أو ركن أو سهم في المكعب، أو على محور في أداة المحاور، يوجّه العرض أيضًا نحو منتصف النموذج من المسافة الواحدة التي تُظهره كاملًا من كل جهة (كرته المحيطة). يستخدم العرض الرئيسي التكبير والمركز ذاتهما ما دام هذا مفعّلًا، فلا تغيّر العروض المختارة بعده سوى الاتجاه. عند الإيقاف: يدور العرض في مكانه محتفظًا بالهدف والتكبير.",
"prefs.navigator_fit_note":"النقر على وجه أو ركن أو سهم في أداة التنقل، أو على محور في أداة المحاور، يوجّه العرض أيضًا نحو منتصف النموذج من المسافة الواحدة التي تُظهره كاملًا من كل جهة (كرته المحيطة). يستخدم العرض الرئيسي التكبير والمركز ذاتهما ما دام هذا مفعّلًا، فلا تغيّر العروض المختارة بعده سوى الاتجاه. عند الإيقاف: يدور العرض في مكانه محتفظًا بالهدف والتكبير.",
"prefs.network_host_remove":"إزالة",
"prefs.network_locations":"السماح بمواقع الشبكة في التصاميم",
"prefs.network_locations_note":"للمؤسسات التي تحفظ التصاميم على خوادم الملفات. عند الإيقاف: الخوادم التي تسمح بها فقط.",
"prefs.interface_note":"Kako SolidCraft izgleda i gdje su njegovi dijelovi.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Obrni zakrivljene strelice za valjanje",
"prefs.invert_curved_roll_arrows_note":"Dvije zakrivljene strelice u uglu kocke valjaju prikaz unutar ravni ekrana. Uključeno: strelica u smjeru kazaljke na satu valja prikaz suprotno od smjera kazaljke na satu, a strelica suprotno od smjera kazaljke na satu valja ga u smjeru kazaljke na satu. Isključeno: svaka strelica valja prikaz onako kako je zakrivljena.",
"prefs.invert_navigator_arrows":"Obrni strelice za rotaciju kocke prikaza",
"prefs.invert_navigator_arrows_note":"Strelice oko kocke orbitiraju kameru u smjeru u kojem pokazuju, kao u programima Fusion i Inventor: strelica s lijeve strane pokazuje udesno i zakreće kameru udesno. Isključeno: strelica okreće kocku prema strani na kojoj se nalazi, pa lijeva strelica zakreće kameru ulijevo. Postavka ne utiče na zakrivljene strelice za valjanje.",
"prefs.keep_work_on_tab_switch":"Zadrži dijaloge i alate otvorenima pri prebacivanju između dizajna",
"prefs.keep_work_on_tab_switch_note":"Svaka kartica dizajna zadržava svoj otvoreni dijalog ili alat, s onim što ste u njemu izabrali i upisali, te svoju karticu trake s alatima. Kada se vratite na dizajn, ponovo su tu. Isključeno: prebacivanje između dizajna otkazuje ih i prikazuje karticu Model.",
"prefs.labels":"Oznake",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigacija",
"prefs.navigation_note":"Kako miš, trackpad i 3D miš pomjeraju prikaz.",
"prefs.navigator_fit":"Uklopi model nakon promjene prikaza u navigatoru",
"prefs.navigator_fit_note":"Lice, ugao ili strelica kocke prikaza, odnosno osa alata za orijentaciju, također usmjerava pogled na sredinu modela s jedne udaljenosti na kojoj se uklapa iz svakog smjera (njegova granična sfera). Početni prikaz koristi isti zum i centar dok je ova opcija uključena, pa se prikazi koje izaberete poslije njega samo okreću. Isključeno: prikaz se okreće na mjestu i zadržava svoj cilj i zum.",
"prefs.network_host_remove":"Ukloni",
"prefs.network_locations":"Dozvoli mrežne lokacije u dizajnima",
"prefs.network_locations_note":"Isključeno: povezane datoteke dizajna na drugom računaru (\\\\server\\share) otvaraju se samo sa servera navedenih ovdje ili nakon što ga dozvolite kada se to zatraži.",
"Ctrl+Enter continues on GitHub.":"Ctrl+Retorn continua a GitHub.",
"Cube":"Cub",
"Current orthographic view (1:1 mm)":"Vista ortogràfica actual (1:1 mm)",
"Current perspective view":"Vista en perspectiva actual",
"Current plane":"Pla actual",
@@ -2659,9 +2658,6 @@
"prefs.interface_note":"Com es veu SolidCraft i on se situen les seves parts.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Invertir les fletxes de gir corbes",
"prefs.invert_curved_roll_arrows_note":"Les dues fletxes corbes a la cantonada del cub giren la vista dins del pla de la pantalla. Activades: la fletxa horària gira en sentit antihorari i l'antihorària gira en sentit horari. Desactivades: cada fletxa gira la vista cap on es corba.",
"prefs.invert_navigator_arrows":"Invertir les fletxes de rotació del cub de vista",
"prefs.invert_navigator_arrows_note":"Les fletxes al voltant del cub orbiten la càmera cap on apunten, com a Fusion i Inventor: la fletxa de l'esquerra apunta a la dreta i gira la càmera a la dreta. Desactivades: una fletxa gira el cub cap al costat on és, així la fletxa de l'esquerra gira la càmera a l'esquerra. Les fletxes de gir corbes no en queden afectades.",
"prefs.keep_unsaved_also_asks":"Continuar preguntant quan es tanca la pestanya",
"prefs.keep_unsaved_also_asks_tip":"Amb això desactivat, la pestanya es tanca sense la finestra del navegador un cop la còpia de cada disseny sense desar està al dia.",
"prefs.keep_unsaved_in_browser":"Conservar la feina sense desar en aquest navegador",
@@ -2721,7 +2717,6 @@
"prefs.move_drag_highlights_tip":"Desactivat: mentre les fletxes de Moure arrosseguen un component, no es ressalta res del que hi ha sota el punter, cosa que manté àgils els ensamblatges grans. Amb una secció o una anàlisi mostrades, es ressalta igualment, i una peça arrossegada al llarg de les seves unions no ho ressalta mai.",
"prefs.navigation":"Navegació",
"prefs.navigation.axes_signed":"Eixos",
"prefs.navigation.cube_compact":"Cub",
"prefs.navigation.device":"Dispositiu",
"prefs.navigation.free":"Lliure",
"prefs.navigation.orbit_style":"Estil d'òrbita",
@@ -2729,7 +2724,6 @@
"prefs.navigation.turntable":"Plat giratori",
"prefs.navigation_note":"Com mouen la vista el ratolí, el panel tàctil i el ratolí 3D.",
"prefs.navigator_fit":"Ajustar el model després d'un canvi de vista del navegador",
"prefs.navigator_fit_note":"Una cara, cantonada o fletxa del cub de vista, o un eix del gizmo, també miren el centre del model des de la distància que l'ajusta des de tots els costats (la seva esfera envolupant). Inici fa servir el mateix zoom i centre mentre això està activat, així les vistes triades després només giren. Desactivat: la vista gira al seu lloc, mantenint el seu objectiu i zoom.",
"prefs.network_host_remove":"Eliminar",
"prefs.network_locations":"Permetre ubicacions de xarxa als dissenys",
"prefs.network_locations_note":"Per a organitzacions que desen dissenys en servidors de fitxers. Desactivat: només els servidors que permeteu.",
"prefs.invert_curved_roll_arrows_note":"Dvě zakřivené šipky v rohu krychle natáčejí pohled v rovině obrazovky. Zapnuto: šipka po směru hodinových ručiček natáčí pohled proti směru hodinových ručiček a šipka proti směru hodinových ručiček ho natáčí po směru. Vypnuto: každá šipka natáčí pohled tak, jak je zakřivená.",
"prefs.invert_navigator_arrows_note":"Šipky kolem krychle otáčejí kameru tím směrem, kam míří, jako ve Fusion a Inventor: šipka vlevo míří doprava a stáčí kameru doprava. Vypnuto: šipka otáčí krychli na stranu, na které leží, takže šipka vlevo stáčí kameru doleva. Zakřivené šipky natočení tím nejsou dotčeny.",
"prefs.keep_unsaved_also_asks":"Přesto se ptát při zavření karty",
"prefs.keep_unsaved_also_asks_tip":"Když je vypnuto, karta se zavře bez dotazu prohlížeče, jakmile je kopie každého neuloženého návrhu aktuální.",
"prefs.keep_unsaved_in_browser":"Uchovávat neuloženou práci v tomto prohlížeči",
@@ -2721,7 +2717,6 @@
"prefs.move_drag_highlights_tip":"Vypnuto: když šipky Přesunout táhnou komponentu, nic pod ukazatelem se nezvýrazní, takže velké sestavy zůstanou plynulé. Při zobrazeném řezu nebo analýze se zvýrazňuje i tak a díl tažený po svých spojích nezvýrazňuje nikdy.",
"prefs.navigation":"Navigace",
"prefs.navigation.axes_signed":"Osy",
"prefs.navigation.cube_compact":"Krychle",
"prefs.navigation.device":"Zařízení",
"prefs.navigation.free":"Volné",
"prefs.navigation.orbit_style":"Styl otáčení",
@@ -2729,7 +2724,6 @@
"prefs.navigation.turntable":"Otočný stůl",
"prefs.navigation_note":"Jak myš, trackpad a 3D myš pohybují pohledem.",
"prefs.navigator_fit":"Přizpůsobit model po změně pohledu v navigátoru",
"prefs.navigator_fit_note":"Plocha, roh nebo šipka krychle pohledu, případně osa orientačního ovladače, také zaměří pohled na střed modelu z jediné vzdálenosti, ze které je celý vidět ze všech stran (jeho ohraničující koule). Domů při zapnutí používá stejné přiblížení a střed, takže pohledy zvolené po něm se jen otáčejí. Vypnuto: pohled se otáčí na místě a zachovává svůj cíl a přiblížení.",
"prefs.network_host_remove":"Odebrat",
"prefs.network_locations":"Povolit síťová umístění v návrzích",
"prefs.network_locations_note":"Vypnuto: propojené soubory návrhu na jiném počítači (\\\\server\\share) se otevírají jen ze zde uvedených serverů nebo poté, co jeden povolíte na výzvu.",
"prefs.interface_note":"SolidCrafts udseende og placeringen af dets dele.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Vend de buede rulningspile om",
"prefs.invert_curved_roll_arrows_note":"De to buede pile i kubens hjørne ruller visningen i skærmplanet. Slået til: Pilen med uret ruller mod uret, og pilen mod uret ruller med uret. Slået fra: Hver pil ruller visningen den vej, den krummer.",
"prefs.invert_navigator_arrows_note":"Pilene rundt om kuben roterer kameraet i den retning, de peger, som i Fusion og Inventor: Pilen til venstre peger mod højre og svinger kameraet til højre. Slået fra: En pil drejer kuben mod den side, den sidder på, så venstre pil svinger kameraet til venstre. De buede rulningspile påvirkes ikke.",
"prefs.keep_work_on_tab_switch":"Behold dialoger og værktøjer åbne, når du skifter design",
"prefs.keep_work_on_tab_switch_note":"Hver designfane beholder sin åbne dialog eller sit åbne værktøj med det, du har valgt og skrevet i den, og sin fane på båndet. De er der igen, når du vender tilbage til designet. Slået fra: Skift af design annullerer dem og viser fanen Model.",
"prefs.labels":"Etiketter",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigation",
"prefs.navigation_note":"Hvordan musen, pegefeltet og 3D-musen styrer visningen.",
"prefs.navigator_fit":"Tilpas modellen efter et skift af visning i navigatoren",
"prefs.navigator_fit_note":"En flade, et hjørne eller en pil på visningskuben eller en akse på orienteringsgizmoen ser også mod modellens midte fra den ene afstand, der får den til at passe fra alle sider (dens omsluttende kugle). Hjem bruger samme zoom og centrum, mens dette er slået til, så visninger, der vælges bagefter, kun drejer. Slået fra: Visningen drejer på stedet og beholder sit mål og sin zoom.",
"prefs.network_host_remove":"Fjern",
"prefs.network_locations":"Tillad netværksplaceringer i designs",
"prefs.network_locations_note":"Slået fra: Et designs linkede filer på en anden computer (\\\\server\\share) åbnes kun fra de servere, der er angivet her, eller efter at du har tilladt en, når du bliver spurgt.",
"prefs.interface_note":"Legt das Erscheinungsbild von SolidCraft und die Anordnung seiner Bereiche fest.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_navigator_arrows":"Drehpfeile des Ansichtswürfels umkehren",
"prefs.invert_navigator_arrows_note":"Die Pfeile um den Würfel drehen die Kamera in die Richtung, in die sie zeigen, wie in Fusion und Inventor: Der linke Pfeil zeigt nach rechts und schwenkt die Kamera nach rechts. Aus: Ein Pfeil dreht den Würfel zu der Seite hin, an der er sitzt, sodass der linke Pfeil die Kamera nach links schwenkt. Die gebogenen Rollpfeile sind davon nicht betroffen.",
"prefs.keep_work_on_tab_switch":"Dialoge und Werkzeuge beim Wechsel zwischen Konstruktionen offen lassen",
"prefs.keep_work_on_tab_switch_note":"Jeder Konstruktions-Tab behält seinen offenen Dialog oder sein Werkzeug mit allem, was darin ausgewählt und eingegeben wurde, sowie seinen Werkzeugleisten-Tab. Beim Zurückkehren zur Konstruktion ist alles wieder da. Aus: Ein Wechsel bricht sie ab und zeigt den Tab Modell.",
"prefs.labels":"Beschriftungen",
@@ -2526,7 +2523,6 @@
"prefs.move_drag_highlights_tip":"Aus: Solange die Pfeile von Verschieben eine Komponente ziehen, wird nichts unter dem Zeiger hervorgehoben; so bleiben große Baugruppen flüssig. Mit einem Schnitt oder einer Analyse wird es trotzdem hervorgehoben, und ein entlang seiner Gelenke gezogenes Teil hebt nie etwas hervor.",
"prefs.navigation":"Navigation",
"prefs.navigation.axes_signed":"Achsen",
"prefs.navigation.cube_compact":"Würfel",
"prefs.navigation.device":"Gerät",
"prefs.navigation.free":"Frei",
"prefs.navigation.orbit_style":"Drehstil",
@@ -2534,7 +2530,6 @@
"prefs.navigation.turntable":"Drehteller",
"prefs.navigation_note":"Legt fest, wie Maus, Trackpad und 3D-Maus die Ansicht steuern.",
"prefs.navigator_fit":"Modell nach einem Ansichtswechsel am Navigator einpassen",
"prefs.navigator_fit_note":"Eine Fläche, Ecke oder ein Pfeil des Würfels oder eine Achse des Gizmos blickt auch auf die Mitte des Modells, aus dem einen Abstand, der es von jeder Seite einpasst (seine Hüllkugel). Home nutzt denselben Zoom und dieselbe Mitte, solange dies an ist, sodass danach gewählte Ansichten sich nur drehen. Aus: Die Ansicht dreht sich an Ort und Stelle und behält Ziel und Zoom.",
"prefs.network_host_remove":"Entfernen",
"prefs.network_locations":"Netzwerkspeicherorte in Designs zulassen",
"prefs.network_locations_note":"Für Organisationen, die Entwürfe auf Dateiservern speichern. Aus: nur erlaubte Server.",
"help.sketch.construction_line":"Create an infinite construction line through two points. Use it to lay out a sketch without splitting profiles.",
"help.sketch.construction_line.angle":"Create an infinite construction line at an angle from sketch X. Pick the point and type the angle, or pick its direction.",
"help.sketch.construction_line.bisector":"Pick two intersecting straight sketch lines, then choose either angle bisector. The result follows its source lines.",
"help.sketch.construction_line.horizontal":"Create an infinite horizontal construction line through a point. Its horizontal constraint follows later edits.",
"help.sketch.construction_line.parallel":"Pick a straight sketch line, then place a parallel infinite construction line. Right-click to choose another reference.",
"help.sketch.construction_line.horizontal":"Create an infinite horizontal construction line through a point, or type its distance from the sketch X axis. A typed distance becomes a driving dimension. Its horizontal constraint follows later edits.",
"help.sketch.construction_line.parallel":"Pick a straight sketch line, then place a parallel infinite construction line or type its offset. A typed offset becomes a driving distance. Right-click to choose another reference.",
"help.sketch.construction_line.perpendicular":"Pick a straight sketch line, then place a perpendicular infinite construction line. Right-click to choose another reference.",
"help.sketch.construction_line.tangent_at":"Create a tangent at a point on a circle or circular arc, or an endpoint of an open spline or conic. Pick the curve, then its contact point.",
"help.sketch.construction_line.tangent_external":"Pick a circle or circular arc, then an external point and a tangent solution. The previews show only contacts on the drawn arc.",
"help.sketch.construction_line.tangent_two":"Pick two circles or circular arcs, then choose a common tangent in the preview. Contacts must lie on the drawn curves.",
"help.sketch.construction_line.vertical":"Create an infinite vertical construction line through a point. Its vertical constraint follows later edits.",
"help.sketch.construction_line.vertical":"Create an infinite vertical construction line through a point, or type its distance from the sketch Y axis. A typed distance becomes a driving dimension. Its vertical constraint follows later edits.",
"help.sketch.copy":"Copies the selected sketch curves and points, together with the constraints and dimensions among them. Paste them into this sketch or another one.",
"help.sketch.create":"Starts a new 2D sketch on a plane or on a flat face of the model. Every extrude, revolve and sweep begins with a sketch.",
"help.sketch.cut":"Copies the selected sketch geometry and deletes it from the sketch, in one undo step. Paste it elsewhere in this sketch or into another one.",
@@ -2683,9 +2682,9 @@
"prefs.interface_note":"How SolidCraft looks and where its parts sit.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Invert the curved roll arrows",
"prefs.invert_curved_roll_arrows_note":"The two curved arrows at the cube's corner roll the view within the screen plane. On: the clockwise arrow rolls counterclockwise and the counterclockwise arrow rolls clockwise. Off: each arrow rolls the view the way it curls.",
"prefs.invert_navigator_arrows":"Invert the view cube rotation arrows",
"prefs.invert_navigator_arrows_note":"The arrows around the cube orbit the camera the way they point, as in Fusion and Inventor: the arrow on the left points right and swings the camera right. Off: an arrow rotates the cube toward the side it sits on, so the left arrow swings the camera left. The curved roll arrows are not affected.",
"prefs.invert_curved_roll_arrows_note":"The two curved arrows at the navigator's corner roll the view within the screen plane. On: the clockwise arrow rolls counterclockwise and the counterclockwise arrow rolls clockwise. Off: each arrow rolls the view the way it curls.",
"prefs.invert_navigator_arrows":"Invert the navigator rotation arrows",
"prefs.invert_navigator_arrows_note":"The arrows around the navigator orbit the camera the way they point, as in Fusion and Inventor: the arrow on the left points right and swings the camera right. Off: an arrow rotates the view toward the side it sits on, so the left arrow swings the camera left. The curved roll arrows are not affected.",
"prefs.keep_unsaved_also_asks":"Still ask when the tab closes",
"prefs.keep_unsaved_also_asks_tip":"With this off, the tab closes without the browser's box once every unsaved design's copy is current.",
"prefs.keep_unsaved_in_browser":"Keep unsaved work in this browser",
@@ -2745,7 +2744,6 @@
"prefs.move_drag_highlights_tip":"Off: while Move's arrows drag a component, nothing under the pointer is highlighted, which keeps large assemblies smooth. With a section or an analysis shown it is highlighted all the same, and a part dragged along its joints never highlights it.",
"prefs.navigation":"Navigation",
"prefs.navigation.axes_signed":"Axes",
"prefs.navigation.cube_compact":"Cube",
"prefs.navigation.device":"Device",
"prefs.navigation.free":"Free",
"prefs.navigation.orbit_style":"Orbit style",
@@ -2753,7 +2751,7 @@
"prefs.navigation.turntable":"Turntable",
"prefs.navigation_note":"How the mouse, trackpad and 3D mouse move the view.",
"prefs.navigator_fit":"Fit the model after a navigator view change",
"prefs.navigator_fit_note":"A face, corner or arrow of the view cube, or an axis of the gizmo, also looks at the middle of the model from the one distance that fits it from every side (its bounding sphere). Home uses the same zoom and centre while this is on, so views chosen after it only turn. Off: the view turns in place, keeping its target and zoom.",
"prefs.navigator_fit_note":"A face, corner or arrow of a navigator, or an axis of the gizmo, also looks at the middle of the model from the one distance that fits it from every side (its bounding sphere). Home uses the same zoom and centre while this is on, so views chosen after it only turn. Off: the view turns in place, keeping its target and zoom.",
"prefs.network_host_remove":"Remove",
"prefs.network_locations":"Allow network locations in designs",
"prefs.network_locations_note":"For organizations that keep designs on file servers. Off: only servers you allow.",
@@ -5300,12 +5298,14 @@
"ui.constraints_conflict":"Constraints conflict",
"ui.construction":"Construction",
"ui.construction.angle_hint":"Construction line: click a point, then type an angle or pick a direction",
"ui.construction.axis_hint":"Construction line: click a point, or type its distance from the sketch axis",
"ui.construction.bisector_hint":"Bisector: pick two intersecting straight lines, then the desired solution",
"ui.construction.common_hint":"Common tangent: pick two circles/arcs, then the desired solution",
"ui.construction.contact_hint":"Tangent: pick a circle/arc or open spline/conic, then the contact point (free-form curves: endpoints)",
"ui.construction.external_hint":"Tangent: pick a circle or arc, an external point, then the desired solution",
"ui.construction.free_hint":"Construction line: click two points; right-click to restart",
"ui.construction.normal_needs_length":"An infinite line is always construction: type a length to draw a normal line",
"ui.construction.parallel_hint":"Construction line: pick a straight line, then click or type the offset; right-click to change the reference",
"ui.construction.point_hint":"Construction line: click the point to pass through",
"ui.construction.reference_hint":"Construction line: pick a straight line, then a point; right-click to change the reference",
"cmd.sketch.construction_line.vertical":"Línea de construcción vertical",
"cmd.sketch.replace":"Reemplazar boceto",
"cmd.sketch_arc3d":"Arco 3D",
"cmd.sketch_arc_center_point":"Arco de centro y punto",
"cmd.sketch_arc_tangent":"Arco tangente",
@@ -1496,6 +1508,7 @@
"cmd.timeline_stop":"Detener historial",
"cmd.timeline_suppress":"Suprimir operación",
"cmd.ui.assistant":"Alternar Asistente",
"cmd.ui.palette":"Buscar comandos",
"cmd.view.navigator":"Navegador de vistas personalizado",
"cmd.view.render_browser":"Explorador de render",
"cmd.view.render_dock":"Mover acople de render",
@@ -2015,6 +2028,7 @@
"help.view.visual_style":"Switches how the model is drawn: shaded with or without edges, wireframe, or with hidden edges. Use wireframe to see inside, shaded for a clean look.",
"history.sketch_under_constrained":"Bajo restricción ({count} grados de libertad)",
"history.sketch_under_constrained_one":"Bajo restricción ({count} grado de libertad)",
"hole_folder.applies_to":"Los cambios se aplican a los {count} agujeros de {folder}; cada uno conserva su posición.",
"parts.family.countersunk_socket_screw":"Tornillo avellanado con hexágono interior",
"parts.family.dowel_pin":"Pasador cilíndrico",
"parts.family.hex_bolt":"Tornillo de cabeza hexagonal",
"parts.family.hex_cap_screw_inch":"Tornillo de cabeza hexagonal (pulgadas)",
@@ -2515,6 +2530,8 @@
"prefs.bottom_centre":"Centro abajo",
"prefs.bottom_right":"Derecha abajo",
"prefs.browser":"Explorador",
"prefs.browser_body_features":"Operaciones bajo los cuerpos",
"prefs.browser_body_features_note":"Cada cuerpo del explorador se despliega sobre las operaciones de la línea de tiempo que lo construyeron, en el orden de la línea de tiempo.",
"prefs.browser_components_open":"Componentes abiertos al inicio",
"prefs.browser_components_open_note":"Cada componente del explorador empieza abierto, como antes, en lugar de cerrado hasta que lo abras.",
"prefs.browser_eye_left":"Ojo junto a la flecha de plegado",
@@ -2555,7 +2572,11 @@
"prefs.design_tools.degrees":"Grados",
"prefs.design_tools.double_click_help":"O para editar uno vinculado en su lugar.",
"prefs.design_tools.ghost":"Mostrar la posición anterior de las piezas movidas como un fantasma",
"prefs.design_tools.hole_group_help":"Los agujeros creados de una vez comparten una carpeta en el historial, cerrada al principio; ábrela para editar un agujero por separado.",
"prefs.design_tools.hole_moves_help":"Volver a seleccionar la cara mueve el agujero en lugar de eliminarlo.",
"prefs.design_tools.hole_several":"Varios agujeros en una cara",
"prefs.design_tools.hole_several_help":"Hacer clic en otro punto de una cara añade otro agujero; hacer clic en un agujero lo quita.",
"prefs.design_tools.hole_snaps_help":"El centro de una cara y los centros de sus aristas redondeadas.",
"prefs.history_bar_groups":"Plegar la barra Historial en grupos",
"prefs.history_bar_groups_note":"En un historial largo, una serie de operaciones de un mismo componente se pliega en una ficha, y las carpetas de operaciones empiezan plegadas. Un ensamblaje STEP insertado va a una carpeta Grupo. Desactivado, se muestra cada operación, las carpetas empiezan abiertas y no se crea ninguna carpeta.",
"prefs.history_folder_kind_icon":"Icono de carpeta según sus operaciones",
"prefs.history_folder_kind_icon_note":"Una carpeta del historial cuyas operaciones son todas del mismo tipo muestra su icono.",
"prefs.history_group_joints":"Agrupar uniones en el Historial",
"prefs.history_group_joints_note":"Dos o más uniones se muestran como un botón con su número, y al hacer clic se muestra cada una. Desactivado, se muestra cada unión.",
"prefs.history_origin_bodies":"Mostrar origen y cuerpos al principio",
@@ -2659,9 +2682,6 @@
"prefs.interface_note":"Cómo se ve SolidCraft y dónde se sitúan sus partes.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Invertir las flechas de giro curvo",
"prefs.invert_curved_roll_arrows_note":"Las dos flechas curvas en la esquina del cubo giran la vista en el plano de la pantalla. Activado: la flecha horaria gira antihoraria y la antihoraria gira horaria. Desactivado: cada flecha gira la vista hacia donde se curva.",
"prefs.invert_navigator_arrows":"Invertir las flechas de rotación del cubo de vista",
"prefs.invert_navigator_arrows_note":"Las flechas alrededor del cubo orbitan la cámara hacia donde apuntan, como en Fusion e Inventor: la flecha de la izquierda apunta a la derecha y gira la cámara a la derecha. Desactivado: una flecha rota el cubo hacia el lado en que está, así la flecha izquierda gira la cámara a la izquierda. Las flechas de giro curvo no se ven afectadas.",
"prefs.keep_unsaved_also_asks":"Preguntar igualmente al cerrar la pestaña",
"prefs.keep_unsaved_also_asks_tip":"Con esto desactivado, la pestaña se cierra sin el cuadro del navegador en cuanto la copia de cada diseño sin guardar está al día.",
"prefs.keep_unsaved_in_browser":"Conservar el trabajo sin guardar en este navegador",
@@ -2721,7 +2741,6 @@
"prefs.move_drag_highlights_tip":"Desactivado: mientras las flechas de Mover arrastran un componente no se resalta nada bajo el puntero, y los ensamblajes grandes se mueven con fluidez. Con un corte o un análisis visible se resalta igualmente, y una pieza arrastrada a lo largo de sus uniones nunca lo resalta.",
"prefs.navigation":"Navegación",
"prefs.navigation.axes_signed":"Ejes",
"prefs.navigation.cube_compact":"Cubo",
"prefs.navigation.device":"Dispositivo",
"prefs.navigation.free":"Libre",
"prefs.navigation.orbit_style":"Estilo de órbita",
@@ -2729,7 +2748,6 @@
"prefs.navigation.turntable":"Plato giratorio",
"prefs.navigation_note":"Cómo mueven la vista el ratón, el panel táctil y el ratón 3D.",
"prefs.navigator_fit":"Ajustar el modelo tras un cambio de vista del navegador",
"prefs.navigator_fit_note":"Una cara, esquina o flecha del cubo de vista, o un eje del gizmo, también mira el centro del modelo desde la única distancia que lo encaja desde todos los lados (su esfera envolvente). Inicio usa el mismo zoom y centro mientras esto está activado, así que las vistas elegidas después solo giran. Desactivado: la vista gira en su lugar, manteniendo su objetivo y zoom.",
"prefs.network_host_remove":"Eliminar",
"prefs.network_locations":"Permitir ubicaciones de red en diseños",
"prefs.network_locations_note":"Desactivado: los archivos vinculados de un diseño en otro equipo (\\\\servidor\\\\carpeta) solo se abren desde los servidores listados aquí, o tras permitir uno cuando se pregunte.",
@@ -2742,6 +2760,7 @@
"prefs.origin_plane_names":"Nombres de los planos de origen",
"prefs.origin_plane_names_note":"Nombres que se muestran en el explorador, diálogos y listas. {top} es XY, {front} es XZ y {right} es YZ",
"prefs.overall_speed":"Velocidad general",
"prefs.panel_layout.recovery":"No se han podido restaurar las disposiciones de paneles guardadas. Los datos guardados se conservan; Clásico sigue disponible.",
"prefs.parameter_list_rows":"Filas de la lista de parámetros",
"prefs.parameter_list_rows_tip":"Número de filas visibles en la lista de parámetros",
"prefs.persistent_ribbon_pressed":"Resaltar comandos activos de la cinta",
@@ -2803,6 +2822,8 @@
"prefs.remember_dialog_values.help":"Desactivado de forma predeterminada. Si se activa, cuando un comando termina correctamente, sus valores y opciones seguros se reutilizan la próxima vez que abras el mismo comando, hasta que se cierre SolidCraft.",
"prefs.render_legacy_image_defaults":"Usar los valores de imagen anteriores de Render",
"prefs.render_legacy_image_defaults_help":"Mantiene el flujo manual anterior: las configuraciones nuevas usan rasterizado por CPU, el ray tracing por CPU conserva el muestreo fijo y los resultados renderizados se abren a mano. Al entrar en Render se reencuadra, se cambia de toma sin restaurar sus cámaras y se usa el encuadre y la navegación anteriores. Se desactiva la vista previa automática de la imagen en vivo. Las configuraciones guardadas no cambian.",
"prefs.render_legacy_memory_admission":"Usar las comprobaciones de memoria de renderizado anteriores",
"prefs.render_legacy_memory_admission_help":"Permite los renderizados nativos dentro de los presupuestos fijos de memoria compartida y del host aunque su estimación supere la RAM disponible. Los límites del dispositivo y las comprobaciones de asignación se siguen aplicando.",
"prefs.render_previous_geometry_budget":"Usar presupuesto de geometría previo de Render",
"prefs.render_previous_geometry_budget_help":"Restaurar el límite fijo previo (512 MiB en escritorio de 64 bits, 128 MiB en otros) en lugar del presupuesto basado en dispositivo.",
"prefs.reports_saved":"{n} guardados",
@@ -2829,6 +2850,8 @@
"prefs.select_other_hidden":"Seleccionar geometría oculta con Seleccionar otro",
"prefs.select_other_hidden_help":"Activado: incluir cuerpos, bocetos y aristas ocultas u ocluidas cerca del puntero. Desactivado: us",
"prefs.selecting":"Selección",
"prefs.shared_panel_layout":"Usar la disposición de paneles compartida",
"prefs.shared_panel_layout_help":"Muestra el explorador, el historial y el asistente existentes en paneles movibles. El cambio se aplica al aplicar las preferencias.",
"prefs.shift":"Mayús",
"prefs.shift_add_ctrl_toggle":"Mayús solo añade; Ctrl/Cmd alterna",
"prefs.shift_add_note":"Activado: Mayús añade y Ctrl/Cmd alterna. Desactivado (predeterminado): ambos alternan.",
@@ -2851,6 +2874,8 @@
"prefs.sketch.drawing":"Dibujo",
"prefs.sketch.ellipse_axes":"Dibujar elipses con sus ejes",
"prefs.sketch.ellipse_axes_note":"Los ejes mayor y menor como líneas de construcción, para poder acotar los semiejes y los ejes. Desactivado: solo la elipse, y al pulsar D sobre ella se da su radio menor allí donde se arrastre el valor, dibujado y etiquetado como R, como antes.",
"prefs.sketch.escape_twice_finishes":"Doble Esc finaliza el boceto",
"prefs.sketch.escape_twice_finishes_note":"Dos pulsaciones rápidas de Esc finalizan el boceto. Desactivado: Esc solo cancela la herramienta o la selección, y el boceto solo se finaliza con Finalizar boceto.",
"prefs.sketch.exit_keep":"Mantener",
"prefs.sketch.exit_previous":"Volver a la vista anterior",
"prefs.sketch.exit_unless_rotated":"Salvo que se haya girado",
@@ -3193,8 +3218,10 @@
"render.category_paint":"Pintura",
"render.category_plastic":"Plástico",
"render.category_wood":"Madera",
"render.caustics_area_lights":"Cáusticas de luces de área",
"render.caustics_depth":"Profundidad de trayectoria de cáusticas",
"render.caustics_enable":"Cáusticas de vidrio con luces puntuales y direccionales",
"render.caustics_environment":"Cáusticas del entorno",
"render.caustics_help":"El número de fotones controla el ruido y el trabajo. El radio de captación cambia detalle nítido por una estimación más suave. La CPU requiere iluminación indirecta.",
"render.caustics_path_required":"Las cáusticas por CPU requieren iluminación indirecta.",
"render.caustics_photons":"Fotones",
@@ -3371,7 +3398,7 @@
"render.cpu_opaque_required":"El trazado CPU requiere un fondo opaco. Desactiva Fondo transparente para continuar.",
"render.cpu_options_pending":"Este panel CPU todavía no puede editar emisión, efectos de superficie, vidrio ni anisotropía. Borra esas opciones de la API antes de usarlo.",
"render.cpu_path_depth":"Profundidad de trayectoria",
"render.cpu_path_help":"Añade rebotes de luz entre superficies opacas y a través de formas de vidrio admitidas. Requiere renderizado progresivo y sombras trazadas. La profundidad de trayectoria cuenta superficies sombreadas, límites de vidrio y eventos de dispersión, empezando por el primer impacto (1–16). La profundidad 1 da iluminación directa sobre superficies opacas; los valores mayores permiten más rebotes o cruces de vidrio y pueden tardar más. Desactiva la iluminación indirecta para usar reflexiones personalizadas o anisotropía.",
"render.cpu_path_help":"Añade rebotes de luz entre superficies opacas y a través de formas de vidrio admitidas. Requiere renderizado progresivo y sombras trazadas. La profundidad de trayectoria cuenta superficies sombreadas, límites de vidrio y eventos de dispersión, empezando por el primer impacto (1–64). La profundidad 1 da iluminación directa sobre superficies opacas; los valores mayores permiten más rebotes o cruces de vidrio y pueden tardar más. Desactiva la iluminación indirecta para usar reflexiones personalizadas o anisotropía.",
"render.cpu_path_optics_reason":"Se conservan tus ajustes. Desactiva la iluminación indirecta para usar reflexiones personalizadas o anisotropía.",
"render.cpu_point":"Luz puntual",
@@ -3411,6 +3438,12 @@
"render.cpu_subsurface_denoise_reason":"La dispersión subsuperficial por CPU necesita la reducción de ruido desactivada. Se conservan tus ajustes.",
"render.cpu_subsurface_help":"La dispersión redirige la luz dentro del vidrio cerrado. Las tasas positivas necesitan iluminación indirecta con la reducción de ruido desactivada. La tasa es de colisiones por mm; 0 no añade dispersión. Un sesgo de fase positivo favorece las direcciones hacia delante; uno negativo, las hacia atrás.",
"render.cpu_summary":"Trazado CPU · reflejos · HDR · luces direccionales y puntuales",
"render.cpu_threads":"Procesos de CPU",
"render.cpu_threads_auto":"Automático",
"render.cpu_threads_custom":"Personalizado",
"render.cpu_threads_help":"Automático usa los procesos de CPU disponibles. Un proceso se ejecuta en serie. Más procesos pueden necesitar más memoria.",
"render.cpu_threads_single":"Uno",
"render.cpu_threads_web":"El renderizado en el navegador usa un proceso cooperativo. Se conserva la elección de procesos guardada para el escritorio.",
"render.cpu_transparent_help":"El PNG transparente quita el fondo y conserva la cobertura de los bordes. La opacidad del cuerpo y de la cara debe mantenerse al 100 %.",
"render.cpu_volume_enable_path_plan":"Ray tracing en CPU · profundidad {depth} · {spp} muestras/píxel · semilla {seed} · sombras trazadas. Conserva la atmósfera definida y el presupuesto de imagen.",
@@ -3419,6 +3452,40 @@
"render.cpu_volume_method_incompatible":"Estos ajustes de luz, muestreo o atmósfera no se pueden transferir a este método. Se conservan tus ajustes.",
"render.cpu_volume_path_required":"Activa la iluminación indirecta para usar la atmósfera por CPU.",
"render.denoise_diagnostics_enable":"Recopilar diagnóstico de reducción de ruido",
"render.denoise_diagnostics_grid":"Recuentos de píxeles con el tamaño interno {width} × {height}.",
"render.denoise_diagnostics_help":"Recopila los recuentos de píxeles elegibles y omitidos incluso con la reducción de ruido desactivada. Los recuentos usan el tamaño interno de renderizado; la máscara usa el tamaño de salida.",
"render.denoise_diagnostics_title":"Diagnóstico de reducción de ruido",
"render.denoise_firefly":"Suprimir los píxeles brillantes aislados",
"render.denoise_format_mask":"Máscara PNG",
"render.denoise_mask_copy_help":"Exporta la máscara de reducción de ruido como PNG o vuelve a la imagen renderizada para copiarla.",
"render.denoise_mask_unavailable":"Activa el diagnóstico de reducción de ruido y renderiza una imagen para inspeccionar su máscara.",
"render.denoise_optical_filter":"Filtrar espejos y vidrio",
"render.denoise_optical_filter_help":"Filtra la luz reflejada y transmitida elegible. Desactívalo para reducir el ruido solo en superficies opacas.",
"render.denoise_passes":"Pasadas del filtro",
"render.denoise_settings_help":"Más pasadas suavizan un área mayor. La intensidad controla cuánto filtra cada pasada. Las trayectorias ópticas no admitidas no se filtran.",
"render.denoise_strength":"Intensidad de la reducción de ruido",
"render.denoise_view_image":"Imagen renderizada",
"render.denoise_view_mask":"Máscara de reducción de ruido",
"render.diag_bodies":"Cuerpos visibles",
"render.diag_job_details":"Detalles del trabajo capturado",
"render.diag_last_check":"Última comprobación",
@@ -3973,6 +4040,8 @@
"render.material_transient":"Ajustes ópticos temporales de esta pestaña. No se guardan en el diseño, la configuración de render ni la biblioteca de materiales.",
"render.material_v_up":"La coordenada V apunta hacia arriba en la imagen",
"render.material_weight":"Peso del recubrimiento (0–1)",
"render.metal_energy_compensation":"Recuperar la reflectancia del metal rugoso",
"render.metal_energy_compensation_help":"Compensa la luz perdida entre las microfacetas del metal rugoso. El renderizado por CPU necesita iluminación indirecta.",
"render.method_cpu":"Trazado CPU",
"render.method_gpu":"Trazado GPU",
"render.method_limit_raster":"Raster utiliza la vista opaca de estudio fija. Selecciona un método de trazado de rayos para los controles ópticos compatibles.",
@@ -4101,6 +4170,8 @@
"render.quality_denoise_on":"Reducción de ruido: Activada",
"render.quality_draft":"Boceto",
"render.quality_high":"Alto",
"render.quality_legacy_samples":"Valor guardado: {samples} spp. Las selecciones nuevas se limitan a {maximum} spp.",
"render.quality_preset_limit":"Este preajuste supera el límite de selección de 16384 spp. Elige un número de muestras de hasta 16384.",
"render.quality_projection_help":"Esta revisión solo cambia la resolución, el presupuesto de muestras, la profundidad de trayectoria, la semilla y la reducción de ruido. Los demás ajustes no cambian; el presupuesto no certifica la convergencia.",
"render.quality_review":"Revisar presupuesto de muestras",
"render.show_shots_label":"Mostrar tira de tomas",
"render.size_4k":"4K",
"render.size_4k_uhd":"4K UHD",
"render.size_8k_uhd":"8K UHD",
"render.size_custom":"Personalizado…",
"render.size_full_hd":"Full HD",
"render.size_hd":"HD",
@@ -4410,6 +4482,11 @@
"render.subsurface_enable":"Recorrido aleatorio de dispersión interna",
"render.subsurface_help":"Solo dieléctricos cerrados. Dispersión y absorción RGB son coeficientes separados por mm. Las trayectorias se dispersan dentro de la geometría y se refractan o reflejan en la frontera. Desactiva sombras rectas, reducción de ruido de superficies y volumen exterior; usa un estrato Fresnel. No es un filtro de difusión, rejilla de densidad ni material medido. Los medios densos pueden requerir más muestras y profundidad.",
"render.subsurface_rate":"Tasa de colisiones de dispersión",
"render.supersampling":"Supermuestreo",
"render.supersampling_dimensions":"Renderiza {width} × {height} y reduce a {output_width} × {output_height}. Usa el cuádruple de píxeles.",
"render.supersampling_help":"Renderiza al doble de ancho y alto y luego reduce en luz lineal. El tamaño de salida no cambia.",
"render.surface_import.complete":"Elige una imagen e indica su autor, fuente y licencia.",
@@ -4423,6 +4500,8 @@
"render.surface_import.saved_cpu":"Guarda los mapas para usarlos con renderizado por CPU.",
"render.surface_import.source":"Fuente",
"render.surface_import.title":"Importar imagen",
"render.texture_filtering":"Filtrar el detalle de las texturas",
"render.texture_filtering_help":"Usa niveles mip y filtrado anisotrópico para reducir el aliasing de las texturas al reducirlas. Consume más memoria; desactivado mantiene el filtrado anterior.",
"render.tone_aces_fitted":"ACES ajustado",
"render.tone_reinhard":"Reinhard",
"render.transmission_scope":"El vidrio GPU admite medios separados certificados. No se admiten medios anidados sin restricciones ni un estimador de transmisión sin ruido.",
"ui.panel_layout.custom_hint":"Se han movido paneles desde {name}.",
"ui.panel_layout.details":"Detalles",
"ui.panel_layout.layout_workspace":"Espacio de trabajo de disposición",
"ui.panel_layout.page_defaults_hint":"Restablece los ajustes de esta página. La organización de los paneles y las disposiciones guardadas se conservan.",
"ui.panel_layout.panel_layout":"Disposición de paneles",
"ui.panel_layout.panel_style":"Estilo de paneles",
"ui.panel_layout.validation.builtin":"Las disposiciones integradas no se pueden cambiar",
"ui.panel_layout.validation.duplicate":"Ya tienes este nombre",
"ui.panel_layout.validation.empty":"Introduce un nombre",
"ui.panel_layout.validation.full":"64 de 64 guardadas. Elige una para reemplazarla.",
"ui.panel_layout.validation.invalid":"Usa de 1 a 64 caracteres, sin caracteres de control",
"ui.panel_layout.workspace.classic":"Clásico",
"ui.panel_layout.workspace.modelling":"Modelado",
"ui.panel_layout.workspace.render":"Renderizado",
"ui.panel_layout.workspace.review":"Revisión",
"ui.panel_layout.workspace.sketch":"Boceto",
"ui.panel_layout.your_layout":"tu disposición",
"ui.parallel":"Paralelo",
"ui.parameters":"Parámetros",
"ui.part_size_needs_a_part":"Seleccione una pieza normalizada insertada (un tornillo, una tuerca, una arandela o un pasador) para cambiar su tamaño. Para agrandar o reducir un cuerpo, use Escala.",
@@ -5824,6 +5940,8 @@
"ui.profiles":"Perfiles",
"ui.project":"Proyectar",
"ui.project_face_edges_sketch":"Proyectar aristas de caras en bocetos nuevos",
"ui.project_references_construction":"Proyectar referencias como geometría de construcción",
"ui.project_references_construction_note":"Proyectar, Intersecar, Incluir geometría 3D y las aristas de cara de un boceto nuevo sobre una cara crean geometría de construcción: referencias sobre las que dibujar, excluidas de los perfiles. Siguen vinculadas al modelo.",
"prefs.invert_navigator_arrows_note":"Kuution ympärillä olevat nuolet kiertävät kameraa osoittamaansa suuntaan kuten Fusionissa ja Inventorissa: vasemmalla oleva nuoli osoittaa oikealle ja kääntää kameraa oikealle. Pois: nuoli kiertää kuutiota omaa puoltaan kohti, joten vasen nuoli kääntää kameraa vasemmalle. Kaarevat kallistusnuolet eivät muutu.",
"prefs.keep_unsaved_also_asks":"Kysy silti, kun välilehti suljetaan",
"prefs.keep_unsaved_also_asks_tip":"Kun tämä on pois päältä, välilehti sulkeutuu ilman selaimen kyselyikkunaa, kun jokaisen tallentamattoman mallin kopio on ajan tasalla.",
"prefs.keep_unsaved_in_browser":"Säilytä tallentamaton työ tässä selaimessa",
@@ -2743,7 +2739,6 @@
"prefs.move_drag_highlights_tip":"Pois: kun Siirrä-toiminnon nuolilla vedetään komponenttia, osoittimen alla olevaa kohdetta ei korosteta, mikä pitää suuret kokoonpanot sulavina. Kun leikkaus tai analyysi on näkyvissä, kohde korostetaan silti, eikä niveliään pitkin vedetty osa koskaan korosta sitä.",
"prefs.navigation":"Navigointi",
"prefs.navigation.axes_signed":"Akselit",
"prefs.navigation.cube_compact":"Kuutio",
"prefs.navigation.device":"Laite",
"prefs.navigation.free":"Vapaa",
"prefs.navigation.orbit_style":"Kiertotapa",
@@ -2751,7 +2746,6 @@
"prefs.navigation.turntable":"Pyörivä alusta",
"prefs.navigation_note":"Miten hiiri, kosketuslevy ja 3D-hiiri liikuttavat näkymää.",
"prefs.navigator_fit":"Sovita malli navigaattorin näkymänvaihdon jälkeen",
"prefs.navigator_fit_note":"Näkymäkuution pinta, kulma tai nuoli tai gizmon akseli katsoo myös mallin keskikohtaa siltä yhdeltä etäisyydeltä, joka sovittaa sen joka suunnasta (sen rajaava pallo). Aloitusnäkymä käyttää samaa zoomausta ja keskipistettä, kun tämä on päällä, joten sen jälkeen valitut näkymät vain kääntyvät. Pois: näkymä kääntyy paikallaan säilyttäen kohteensa ja zoomauksensa.",
"prefs.network_locations_note":"Pois: mallin linkitetyt tiedostot toisella tietokoneella (\\\\server\\share) avataan vain tässä luetelluilta palvelimilta tai sen jälkeen, kun sallit palvelimen kysyttäessä.",
"Current perspective view":"Vue en perspective actuelle",
"Current plane":"Plan actuel",
@@ -2256,9 +2255,9 @@
"prefs.interface_note":"L'apparence de SolidCraft et la place de ses éléments.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Inverser les flèches courbes de roulis",
"prefs.invert_curved_roll_arrows_note":"Les deux flèches courbes au coin du cube font rouler la vue dans le plan de l'écran. Activé : la flèche horaire fait rouler dans le sens antihoraire et la flèche antihoraire dans le sens horaire. Désactivé : chaque flèche fait rouler la vue dans le sens de sa courbure.",
"prefs.invert_navigator_arrows":"Inverser les flèches de rotation du cube de vue",
"prefs.invert_navigator_arrows_note":"Les flèches autour du cube font orbiter la caméra dans le sens où elles pointent, comme dans Fusion et Inventor: la flèche de gauche pointe vers la droite et fait pivoter la caméra vers la droite. Désactivé : une flèche fait tourner le cube vers le côté où elle se trouve, donc la flèche de gauche fait pivoter la caméra vers la gauche. Les flèches courbes de roulis ne sont pas concernées.",
"prefs.invert_curved_roll_arrows_note":"Les deux flèches courbes au coin du navigateur font rouler la vue dans le plan de l'écran. Activé : la flèche horaire fait rouler dans le sens antihoraire et la flèche antihoraire dans le sens horaire. Désactivé : chaque flèche fait rouler la vue dans le sens de sa courbure.",
"prefs.invert_navigator_arrows":"Inverser les flèches de rotation du navigateur de vue",
"prefs.invert_navigator_arrows_note":"Les flèches autour du navigateur font orbiter la caméra dans le sens où elles pointent, comme dans Fusion et Inventor: la flèche de gauche pointe vers la droite et fait pivoter la caméra vers la droite. Désactivé : une flèche fait tourner la vue vers le côté où elle se trouve, donc la flèche de gauche fait pivoter la caméra vers la gauche. Les flèches courbes de roulis ne sont pas concernées.",
"prefs.keep_work_on_tab_switch":"Garder dialogues et outils ouverts en changeant de conception",
"prefs.keep_work_on_tab_switch_note":"Chaque onglet de conception garde son dialogue ou son outil ouvert, avec ce que vous y avez sélectionné et saisi, ainsi que son onglet de barre d'outils. Vous les retrouvez en revenant à la conception. Désactivé : changer de conception les annule et affiche l'onglet Modèle.",
"prefs.labels":"Libellés",
@@ -2297,7 +2296,7 @@
"prefs.navigation":"Navigation",
"prefs.navigation_note":"Comment la souris, le pavé tactile et la souris 3D déplacent la vue.",
"prefs.navigator_fit":"Cadrer le modèle après un changement de vue du repère de navigation",
"prefs.navigator_fit_note":"Une face, un coin ou une flèche du cube de vue, ou un axe du gizmo, regarde aussi le milieu du modèle depuis la distance unique qui le cadre sous tous les côtés (sa sphère englobante). Accueil utilise le même zoom et le même centre tant que cette option est activée, de sorte que les vues choisies ensuite ne font que tourner. Désactivé : la vue tourne sur place, en gardant sa cible et son zoom.",
"prefs.navigator_fit_note":"Une face, un coin ou une flèche du navigateur de vue, ou un axe du gizmo, regarde aussi le milieu du modèle depuis la distance unique qui le cadre sous tous les côtés (sa sphère englobante). Accueil utilise le même zoom et le même centre tant que cette option est activée, de sorte que les vues choisies ensuite ne font que tourner. Désactivé : la vue tourne sur place, en gardant sa cible et son zoom.",
"prefs.network_host_remove":"Retirer",
"prefs.network_locations":"Autoriser les emplacements réseau dans les conceptions",
"prefs.network_locations_note":"Désactivé : les fichiers liés d'une conception situés sur un autre ordinateur (\\\\server\\share) ne sont ouverts que depuis les serveurs listés ici, ou après en avoir autorisé un sur demande.",
"Ctrl+Enter continues on GitHub.":"Ctrl+Enter continúa en GitHub.",
"Cube":"Cubo",
"Current orthographic view (1:1 mm)":"Vista ortográfica actual (1:1 mm)",
"Current perspective view":"Vista en perspectiva actual",
"Current plane":"Plano actual",
@@ -2240,9 +2239,6 @@
"prefs.interface_note":"Como se ve SolidCraft e onde están as súas partes.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Inverter as frechas curvas de balanceo",
"prefs.invert_curved_roll_arrows_note":"As dúas frechas curvas da esquina do cubo fan xirar a vista no plano da pantalla. Activado: a frecha en sentido horario xira en sentido antihorario e a frecha en sentido antihorario xira en sentido horario. Desactivado: cada frecha xira a vista no sentido en que se curva.",
"prefs.invert_navigator_arrows":"Invertir as frechas de rotación do cubo de vistas",
"prefs.invert_navigator_arrows_note":"As frechas arredor do cubo fan orbitar a cámara na dirección na que apuntan, como en Fusion e Inventor: a frecha da esquerda apunta á dereita e move a cámara cara á dereita. Desactivado: unha frecha xira o cubo cara ao lado no que está, así que a frecha da esquerda move a cámara cara á esquerda. As frechas curvas de balanceo non se ven afectadas.",
"prefs.keep_work_on_tab_switch":"Manter abertos os diálogos e as ferramentas ao cambiar de deseño",
"prefs.keep_work_on_tab_switch_note":"Cada lapela de deseño conserva o seu diálogo ou ferramenta aberto, co que escolliches e escribiches nel, e a súa lapela da barra de ferramentas. Volven estar aí cando regresas ao deseño. Desactivado: cambiar de deseño cancélaos e mostra a lapela Modelo.",
"prefs.labels":"Etiquetas",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navegación",
"prefs.navigation_note":"Como moven a vista o rato, o panel táctil e o rato 3D.",
"prefs.navigator_fit":"Axustar o modelo despois dun cambio de vista do navegador",
"prefs.navigator_fit_note":"Unha cara, esquina ou frecha do cubo de vistas, ou un eixe do gizmo, tamén mira ao centro do modelo desde a única distancia que o abarca por todos os lados (a súa esfera delimitadora). Inicio usa o mesmo zoom e centro mentres isto está activado, así que as vistas escollidas despois só xiran. Desactivado: a vista xira no sitio, conservando o seu punto de mira e o seu zoom.",
"prefs.network_host_remove":"Quitar",
"prefs.network_locations":"Permitir ubicacións de rede nos deseños",
"prefs.network_locations_note":"Desactivado: os ficheiros ligados dun deseño que están noutro ordenador (\\\\server\\share) só se abren desde os servidores listados aquí, ou despois de que permitas un cando se che pregunte.",
"prefs.interface_note":"איך SolidCraft נראה והיכן חלקיו ממוקמים.",
"prefs.interval_minutes":"{n} דק׳",
"prefs.invert_curved_roll_arrows":"היפוך חצי הגלילה המעוגלים",
"prefs.invert_curved_roll_arrows_note":"שני החצים המעוגלים בפינת הקובייה מסובבים את התצוגה במישור המסך. פועל: החץ עם כיוון השעון מסובב נגד כיוון השעון, והחץ נגד כיוון השעון מסובב עם כיוון השעון. כבוי: כל חץ מסובב את התצוגה לפי העקום שלו.",
"prefs.invert_navigator_arrows":"היפוך חצי סיבוב קוביית התצוגה",
"prefs.invert_navigator_arrows_note":"החצים סביב הקובייה מחוויים את המצלמה לכיוון שהחץ מצביע, כמו ב-Fusion ו-Inventor: החץ שמשמאל מצביע ימימה ומניע את המצלמה ימימה. כבוי: החץ מסובב את הקובייה אל הצד שבו הוא יושב, כך שחץ שמאל מניע את המצלמה שמאלה. חצי הגלילה המעוגלים אינם מושפעים.",
"prefs.invert_curved_roll_arrows_note":"שני החצים המעוגלים בפינת הנווט מסובבים את התצוגה במישור המסך. פועל: החץ עם כיוון השעון מסובב נגד כיוון השעון, והחץ נגד כיוון השעון מסובב עם כיוון השעון. כבוי: כל חץ מסובב את התצוגה לפי העקום שלו.",
"prefs.invert_navigator_arrows":"היפוך חצי סיבוב הנווט",
"prefs.invert_navigator_arrows_note":"החצים סביב הנווט מחוויים את המצלמה לכיוון שהחץ מצביע, כמו ב-Fusion ו-Inventor: החץ שמשמאל מצביע ימימה ומניע את המצלמה ימימה. כבוי: החץ מסובב את התצוגה אל הצד שבו הוא יושב, כך שחץ שמאל מניע את המצלמה שמאלה. חצי הגלילה המעוגלים אינם מושפעים.",
"prefs.keep_unsaved_also_asks":"לשאול גם כשהכרטיסייה נסגרת",
"prefs.keep_unsaved_also_asks_tip":"כשאפשרות זו כבויה, הכרטיסייה נסגרת ללא חלון הדפדפן לאחר שהעותק של כל תכן שלא נשמר עודכן.",
"prefs.keep_unsaved_in_browser":"לשמור עבודה שלא נשמרה בדפדפן זה",
@@ -2745,7 +2744,6 @@
"prefs.move_drag_highlights_tip":"כבוי: בזמן שחצי ההזזה גוררים רכיב, דבר אינו מודגש מתחת לסמן, וכך מכלולים גדולים נשארים חלקים. כשמוצג חתך או ניתוח ההדגשה נשארת, וחלק שנגרר לאורך המפרקים שלו לעולם אינו מדגיש.",
"prefs.navigation":"ניווט",
"prefs.navigation.axes_signed":"צירים",
"prefs.navigation.cube_compact":"קובייה",
"prefs.navigation.device":"התקן",
"prefs.navigation.free":"חופשי",
"prefs.navigation.orbit_style":"סגנון סיבוב",
@@ -2753,7 +2751,7 @@
"prefs.navigation.turntable":"שולחן מסתובב",
"prefs.navigation_note":"איך העכבר, משטח המגע והעכבר התלת־ממדי מזיזים את המבט.",
"prefs.navigator_fit":"התאמת המודל לאחר שינוי תצוגה בניווט התצוגה",
"prefs.navigator_fit_note":"לחיצה על פאה, פינה או חץ בקובייה, או על ציר בגיזמו, גם מביטה אל מרכז המודל מהמרחק האחד שמכיל אותו מכל צד (הכדור החוסם שלו). תצוגת בית משתמשת באותו זום ובאותו מרכז כל עוד זה פעיל, כך שתצוגות שנבחרות אחריה רק מסתובבות. כבוי: התצוגה מסתובבת במקומה ושומרת על היעד ועל הזום.",
"prefs.navigator_fit_note":"לחיצה על פאה, פינה או חץ בנווט, או על ציר בגיזמו, גם מביטה אל מרכז המודל מהמרחק האחד שמכיל אותו מכל צד (הכדור החוסם שלו). תצוגת בית משתמשת באותו זום ובאותו מרכז כל עוד זה פעיל, כך שתצוגות שנבחרות אחריה רק מסתובבות. כבוי: התצוגה מסתובבת במקומה ושומרת על היעד ועל הזום.",
"prefs.network_host_remove":"הסרה",
"prefs.network_locations":"התרת מיקומי רשת בתכנים",
"prefs.network_locations_note":"לארגונים השומרים תכנונים בשרתי קבצים. כשהאפשרות כבויה: רק שרתים שאישרתם.",
"prefs.interface_note":"Kako SolidCraft izgleda i gdje su njegovi dijelovi.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Obrni zakrivljene strelice za valjanje",
"prefs.invert_curved_roll_arrows_note":"Dvije zakrivljene strelice u kutu kocke valjaju prikaz unutar ravnine zaslona. Uključeno: strelica u smjeru kazaljke na satu valja prikaz suprotno od smjera kazaljke na satu, a strelica suprotno od smjera kazaljke na satu valja ga u smjeru kazaljke na satu. Isključeno: svaka strelica valja prikaz onako kako je zakrivljena.",
"prefs.invert_navigator_arrows":"Obrni strelice za rotaciju kocke prikaza",
"prefs.invert_navigator_arrows_note":"Strelice oko kocke orbitiraju kameru u smjeru u kojem pokazuju, kao u programima Fusion i Inventor: strelica s lijeve strane pokazuje udesno i zakreće kameru udesno. Isključeno: strelica okreće kocku prema strani na kojoj se nalazi, pa lijeva strelica zakreće kameru ulijevo. Postavka ne utječe na zakrivljene strelice za valjanje.",
"prefs.keep_work_on_tab_switch":"Zadrži dijaloge i alate otvorenima pri prebacivanju između dizajna",
"prefs.keep_work_on_tab_switch_note":"Svaka kartica dizajna zadržava svoj otvoreni dijalog ili alat, s onim što ste u njemu odabrali i upisali, te svoju karticu alatne trake. Kad se vratite na dizajn, ponovno su tu. Isključeno: prebacivanje između dizajna poništava ih i prikazuje karticu Model.",
"prefs.labels":"Oznake",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigacija",
"prefs.navigation_note":"Kako miš, dodirna pločica i 3D miš pomiču prikaz.",
"prefs.navigator_fit":"Uklopi model nakon promjene prikaza u navigatoru",
"prefs.navigator_fit_note":"Lice, kut ili strelica kocke prikaza, odnosno os orijentacijskog gizma, također usmjerava pogled na sredinu modela s jedne udaljenosti na kojoj se uklapa iz svakog smjera (njegova granična sfera). Početni prikaz koristi isti zum i središte dok je ova opcija uključena, pa se prikazi koje odaberete poslije njega samo okreću. Isključeno: prikaz se okreće na mjestu i zadržava svoj cilj i zum.",
"prefs.network_host_remove":"Ukloni",
"prefs.network_locations":"Dopusti mrežne lokacije u dizajnima",
"prefs.network_locations_note":"Isključeno: povezane datoteke dizajna na drugom računalu (\\\\server\\share) otvaraju se samo s poslužitelja navedenih ovdje ili nakon što ga dopustite kad se to zatraži.",
"prefs.invert_curved_roll_arrows_note":"A nézetkocka sarkában lévő két íves nyíl a nézetet a képernyő síkjában gördíti. Be: az óramutató járásával megegyező nyíl ellentétes irányba gördít, az ellentétes nyíl pedig az óramutató járásával megegyező irányba. Ki: minden nyíl abba az irányba gördíti a nézetet, amerre ível.",
"prefs.invert_navigator_arrows_note":"A kocka körüli nyilak úgy keringetik a kamerát, ahogy mutatnak, mint a Fusion és az Inventor esetében: a bal oldali nyíl jobbra mutat, és a kamerát jobbra lendíti. Ki: a nyíl a kockát afelé az oldal felé forgatja, amelyiken van, így a bal nyíl a kamerát balra lendíti. Az íves gördülőnyilakat ez nem érinti.",
"prefs.keep_work_on_tab_switch":"A párbeszédablakok és eszközök nyitva tartása tervek közötti váltáskor",
"prefs.keep_work_on_tab_switch_note":"Minden tervfül megtartja a nyitott párbeszédablakát vagy eszközét azzal együtt, amit kijelölt és beírt benne, valamint a menüszalag-fülét. A tervhez visszatérve újra ott vannak. Ki: a tervek közötti váltás megszakítja őket, és a Modell fület mutatja.",
"prefs.labels":"Feliratok",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigáció",
"prefs.navigation_note":"Hogyan mozgatja a nézetet az egér, az érintőpad és a 3D-egér.",
"prefs.navigator_fit":"A modell illesztése navigátornézet-váltás után",
"prefs.navigator_fit_note":"A nézetkocka egy lapja, sarka vagy nyila, illetve a gizmo egy tengelye a modell közepére is ránéz abból az egyetlen távolságból, amelyből minden oldalról belefér (a határoló gömbje). Amíg ez be van kapcsolva, a Kezdőnézet ugyanazt a nagyítást és középpontot használja, így az utána választott nézetek csak elfordulnak. Ki: a nézet a helyén fordul el, megtartva a célpontját és a nagyítását.",
"prefs.network_host_remove":"Eltávolítás",
"prefs.network_locations":"Hálózati helyek engedélyezése a tervekben",
"prefs.network_locations_note":"Ki: egy terv másik számítógépen lévő hivatkozott fájljai (\\\\server\\share) csak az itt felsorolt kiszolgálókról nyílnak meg, vagy miután kérdésre engedélyez egyet.",
"Ctrl+Enter continues on GitHub.":"Ctrl+Enter melanjutkan di GitHub.",
"Cube":"Kubus",
"Current orthographic view (1:1 mm)":"Tampilan ortografis saat ini (1:1 mm)",
"Current perspective view":"Tampilan perspektif saat ini",
"Current plane":"Bidang saat ini",
@@ -2240,9 +2239,6 @@
"prefs.interface_note":"Tampilan SolidCraft dan letak bagian-bagiannya.",
"prefs.interval_minutes":"{n} mnt",
"prefs.invert_curved_roll_arrows":"Balikkan panah roll melengkung",
"prefs.invert_curved_roll_arrows_note":"Dua panah melengkung di sudut kubus memutar (roll) tampilan di dalam bidang layar. Aktif: panah searah jarum jam memutar berlawanan arah jarum jam dan panah berlawanan arah jarum jam memutar searah jarum jam. Mati: tiap panah memutar tampilan sesuai arah lengkungannya.",
"prefs.invert_navigator_arrows_note":"Panah di sekeliling kubus memutar orbit kamera ke arah yang ditunjuknya, seperti di Fusion dan Inventor: panah di kiri menunjuk ke kanan dan mengayunkan kamera ke kanan. Mati: panah memutar kubus ke sisi tempatnya berada, sehingga panah kiri mengayunkan kamera ke kiri. Panah roll yang melengkung tidak terpengaruh.",
"prefs.keep_work_on_tab_switch":"Biarkan dialog dan alat tetap terbuka saat berpindah desain",
"prefs.keep_work_on_tab_switch_note":"Setiap tab desain mempertahankan dialog atau alat yang terbuka, beserta apa yang Anda pilih dan ketik di dalamnya, dan tab toolbar-nya. Semuanya kembali saat Anda kembali ke desain itu. Mati: berpindah desain membatalkannya dan menampilkan tab Model.",
"prefs.labels":"Label",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigasi",
"prefs.navigation_note":"Cara mouse, trackpad, dan mouse 3D menggerakkan tampilan.",
"prefs.navigator_fit":"Buat model pas di tampilan setelah perubahan tampilan lewat navigator",
"prefs.navigator_fit_note":"Sebuah muka, sudut, atau panah pada kubus tampilan, atau sumbu pada gizmo, juga menghadap ke tengah model dari satu jarak yang membuatnya pas dari segala sisi (bola pembatasnya). Awal memakai zoom dan pusat yang sama selama opsi ini aktif, sehingga tampilan yang dipilih setelahnya hanya berputar. Mati: tampilan berputar di tempat, dengan target dan zoom tetap.",
"prefs.network_host_remove":"Hapus",
"prefs.network_locations":"Izinkan lokasi jaringan di desain",
"prefs.network_locations_note":"Nonaktif: file tertaut sebuah desain di komputer lain (\\\\server\\share) hanya dibuka dari server yang tercantum di sini, atau setelah Anda mengizinkan salah satunya saat ditanya.",
"prefs.interface_note":"Aspetto di SolidCraft e disposizione dei suoi elementi.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Inverti le frecce curve di rollio",
"prefs.invert_curved_roll_arrows_note":"Le due frecce curve all'angolo del cubo fanno ruotare la vista nel piano dello schermo. Attivato: la freccia oraria ruota in senso antiorario e la freccia antioraria ruota in senso orario. Disattivato: ogni freccia ruota la vista nel verso in cui si curva.",
"prefs.invert_navigator_arrows":"Inverti le frecce di rotazione del cubo della vista",
"prefs.invert_navigator_arrows_note":"Le frecce attorno al cubo fanno orbitare la camera nella direzione in cui puntano, come in Fusion e Inventor: la freccia a sinistra punta a destra e sposta la camera a destra. Disattivato: una freccia ruota il cubo verso il lato in cui si trova, quindi la freccia sinistra sposta la camera a sinistra. Le frecce curve di rollio non sono interessate.",
"prefs.keep_unsaved_also_asks":"Chiedi comunque alla chiusura della scheda",
"prefs.keep_unsaved_also_asks_tip":"Se disattivato, la scheda si chiude senza la finestra del browser quando la copia di ogni progetto non salvato è aggiornata.",
"prefs.keep_unsaved_in_browser":"Conserva il lavoro non salvato in questo browser",
@@ -2745,7 +2741,6 @@
"prefs.move_drag_highlights_tip":"Disattivato: mentre le frecce di Sposta trascinano un componente, niente sotto il puntatore viene evidenziato, così i grandi assiemi restano fluidi. Con una sezione o un'analisi visibile viene evidenziato comunque, e un pezzo trascinato lungo i suoi giunti non lo evidenzia mai.",
"prefs.navigation":"Navigazione",
"prefs.navigation.axes_signed":"Assi",
"prefs.navigation.cube_compact":"Cubo",
"prefs.navigation.device":"Dispositivo",
"prefs.navigation.free":"Libera",
"prefs.navigation.orbit_style":"Stile di orbita",
@@ -2753,7 +2748,6 @@
"prefs.navigation.turntable":"Piatto girevole",
"prefs.navigation_note":"Come mouse, trackpad e mouse 3D spostano la vista.",
"prefs.navigator_fit":"Adatta il modello dopo un cambio di vista dal navigatore",
"prefs.navigator_fit_note":"Una faccia, un angolo o una freccia del cubo della vista, o un asse del gizmo, guarda anche il centro del modello dall'unica distanza che lo inquadra da ogni lato (la sua sfera di ingombro). Inizio usa lo stesso zoom e lo stesso centro finché questa opzione è attiva, quindi le viste scelte dopo si limitano a ruotare. Disattivato: la vista ruota sul posto, mantenendo punto di mira e zoom.",
"prefs.network_host_remove":"Rimuovi",
"prefs.network_locations":"Consenti posizioni di rete nei progetti",
"prefs.network_locations_note":"Disattivato: i file collegati di un progetto su un altro computer (\\\\server\\share) vengono aperti solo dai server elencati qui, o dopo che ne consenti uno quando richiesto.",
"prefs.invert_curved_roll_arrows_note":"კუბის კუთხესთან მდებარე ორი მრუდე ისარი ხედს ეკრანის სიბრტყეში აგორებს. ჩართული: საათის ისრის მიმართულების ისარი საათის ისრის საწინააღმდეგოდ აგორებს, საწინააღმდეგო კი — საათის ისრის მიმართულებით. გამორთული: თითოეული ისარი ხედს თავისი მოხრის მიმართულებით აგორებს.",
"prefs.invert_navigator_arrows_note":"კუბის გარშემო ისრები კამერას ორბიტაზე იმ მიმართულებით ატრიალებს, საითაც მიუთითებს, როგორც Fusion-სა და Inventor-ში: მარცხენა ისარი მარჯვნივ მიუთითებს და კამერას მარჯვნივ აბრუნებს. გამორთული: ისარი კუბს იმ მხრისკენ აბრუნებს, რომელზეც მდებარეობს, ამიტომ მარცხენა ისარი კამერას მარცხნივ აბრუნებს. გორების მრუდე ისრებზე ეს არ მოქმედებს.",
"prefs.keep_work_on_tab_switch":"პროექტებს შორის გადართვისას დიალოგებისა და ხელსაწყოების ღიად დატოვება",
"prefs.keep_work_on_tab_switch_note":"პროექტის თითოეული ჩანართი ინარჩუნებს თავის ღია დიალოგს ან ხელსაწყოს, მასში არჩეულსა და აკრეფილს, და ხელსაწყოთა ზოლის ჩანართს. პროექტში დაბრუნებისას ისინი ისევ იქ დაგხვდებათ. გამორთული: პროექტების გადართვა მათ აუქმებს და „მოდელის“ ჩანართს აჩვენებს.",
"prefs.labels":"წარწერები",
@@ -2355,7 +2351,6 @@
"prefs.navigation":"ნავიგაცია",
"prefs.navigation_note":"როგორ ამოძრავებს ხედს მაუსი, სენსორული პანელი და 3D მაუსი.",
"prefs.navigator_fit":"ნავიგატორით ხედის შეცვლის შემდეგ მოდელის ჩატევა",
"prefs.navigator_fit_note":"ხედის კუბის წახნაგი, კუთხე ან ისარი, ან მანიპულატორის ღერძი ასევე მოდელის შუაგულს უყურებს ერთი მანძილიდან, საიდანაც ის ყველა მხრიდან ეტევა (მისი შემომსაზღვრელი სფერო). სანამ ეს ჩართულია, საწყისი ხედი იმავე მასშტაბსა და ცენტრს იყენებს, ამიტომ მის შემდეგ არჩეული ხედები მხოლოდ ბრუნავს. გამორთული: ხედი ადგილზე ბრუნავს, სამიზნისა და მასშტაბის შენარჩუნებით.",
"prefs.move_drag_highlights_tip":"끄면 이동 화살표로 구성 요소를 끄는 동안 포인터 아래 항목을 강조 표시하지 않아 큰 어셈블리도 부드럽게 움직입니다. 단면이나 해석이 표시된 동안에는 그대로 강조 표시하며, 조인트를 따라 끄는 부품은 강조 표시하지 않습니다.",
"prefs.navigation":"탐색",
"prefs.navigation.axes_signed":"축",
"prefs.navigation.cube_compact":"큐브",
"prefs.navigation.device":"장치",
"prefs.navigation.free":"자유",
"prefs.navigation.orbit_style":"회전 방식",
@@ -2091,7 +2089,6 @@
"prefs.navigation.turntable":"턴테이블",
"prefs.navigation_note":"마우스, 트랙패드, 3D 마우스로 뷰를 움직이는 방식입니다.",
"prefs.navigator_fit":"뷰 탐색기로 뷰를 바꾼 뒤 모델 맞춤",
"prefs.navigator_fit_note":"큐브의 면, 모서리, 화살표나 기즈모의 축을 클릭하면 어느 방향에서든 전체가 들어가는 한 거리(경계 구)에서 모델의 중심을 바라봅니다. 켜져 있는 동안 홈 뷰도 같은 확대/축소와 중심을 쓰므로 그 뒤에 고른 뷰는 방향만 바뀝니다. 끄면 뷰가 제자리에서 회전하며 대상과 확대/축소를 유지합니다.",
"prefs.network_locations_note":"파일 서버에 디자인을 보관하는 조직용입니다. 끄면 허용한 서버만 사용합니다.",
"prefs.interface_note":"Rupa SolidCraft dan kedudukan bahagiannya.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Songsangkan anak panah gelek melengkung",
"prefs.invert_curved_roll_arrows_note":"Dua anak panah melengkung di sudut kubus menggelek pandangan dalam satah skrin. Hidup: anak panah ikut arah jam menggelek lawan arah jam dan anak panah lawan arah jam menggelek ikut arah jam. Mati: setiap anak panah menggelek pandangan mengikut arah lengkungannya.",
"prefs.invert_navigator_arrows":"Songsangkan anak panah putaran kubus pandangan",
"prefs.invert_navigator_arrows_note":"Anak panah di sekeliling kubus mengorbit kamera mengikut arah yang ditunjuknya, seperti dalam Fusion dan Inventor: anak panah di kiri menunjuk ke kanan dan mengayunkan kamera ke kanan. Mati: anak panah memutarkan kubus ke arah sisi tempat ia berada, jadi anak panah kiri mengayunkan kamera ke kiri. Anak panah gelek yang melengkung tidak terjejas.",
"prefs.keep_work_on_tab_switch":"Kekalkan dialog dan alat terbuka apabila menukar reka bentuk",
"prefs.keep_work_on_tab_switch_note":"Setiap tab reka bentuk mengekalkan dialog atau alat yang dibukanya, bersama apa yang anda pilih dan taip di dalamnya, serta tab bar alatnya. Semuanya ada semula apabila anda kembali ke reka bentuk itu. Mati: menukar reka bentuk membatalkannya dan menunjukkan tab Model.",
"prefs.labels":"Label",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigasi",
"prefs.navigation_note":"Cara tetikus, pad jejak dan tetikus 3D menggerakkan pandangan.",
"prefs.navigator_fit":"Muatkan model selepas perubahan pandangan navigator",
"prefs.navigator_fit_note":"Muka, sudut atau anak panah pada kubus pandangan, atau paksi pada gizmo, turut menghadap ke tengah model dari satu jarak yang memuatkannya dari semua sisi (sfera pembatasnya). Utama menggunakan zum dan pusat yang sama selagi ini dihidupkan, jadi pandangan yang dipilih selepasnya hanya berputar. Mati: pandangan berputar di tempat, dengan sasaran dan zumnya kekal.",
"prefs.network_host_remove":"Buang",
"prefs.network_locations":"Benarkan lokasi rangkaian dalam reka bentuk",
"prefs.network_locations_note":"Dimatikan: fail terpaut bagi reka bentuk pada komputer lain (\\\\server\\share) hanya dibuka daripada pelayan yang disenaraikan di sini, atau selepas anda membenarkannya apabila ditanya.",
"prefs.interface_note":"Hvordan SolidCraft ser ut, og hvor delene er plassert.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Inverter de krumme rullepilene",
"prefs.invert_curved_roll_arrows_note":"De to krumme pilene i hjørnet av kuben ruller visningen i skjermplanet. På: pilen med klokken ruller mot klokken, og pilen mot klokken ruller med klokken. Av: hver pil ruller visningen i den retningen den krummer seg.",
"prefs.invert_navigator_arrows":"Inverter rotasjonspilene på visningskuben",
"prefs.invert_navigator_arrows_note":"Pilene rundt kuben roterer kameraet i den retningen de peker, som i Fusion og Inventor: pilen til venstre peker mot høyre og svinger kameraet mot høyre. Av: en pil roterer kuben mot siden den står på, så venstre pil svinger kameraet mot venstre. De krumme rullepilene påvirkes ikke.",
"prefs.keep_work_on_tab_switch":"Behold dialoger og verktøy åpne når du bytter design",
"prefs.keep_work_on_tab_switch_note":"Hver designfane beholder sin åpne dialog eller sitt åpne verktøy, med det du har valgt og skrevet i den, og sin fane på båndet. De er der igjen når du kommer tilbake til designet. Av: å bytte design avbryter dem og viser fanen Modell.",
"prefs.labels":"Etiketter",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigasjon",
"prefs.navigation_note":"Hvordan mus, styreflate og 3D-mus beveger visningen.",
"prefs.navigator_fit":"Tilpass modellen etter en visningsendring fra navigatoren",
"prefs.navigator_fit_note":"En flate, et hjørne eller en pil på visningskuben, eller en akse på orienteringsindikatoren, ser også mot midten av modellen fra den ene avstanden som får den til å passe fra alle sider (den omsluttende kulen). Hjem bruker samme zoom og senter mens dette er på, så visninger som velges etterpå bare dreier. Av: visningen dreier på stedet og beholder målpunktet og zoomen sin.",
"prefs.network_host_remove":"Fjern",
"prefs.network_locations":"Tillat nettverksplasseringer i design",
"prefs.network_locations_note":"Av: lenkede filer i et design på en annen datamaskin (\\\\server\\share) åpnes bare fra serverne som er oppført her, eller etter at du har tillatt en når du blir spurt.",
"prefs.invert_curved_roll_arrows_note":"De twee gebogen pijlen op de hoek van de kubus laten de weergave in het schermvlak rollen. Aan: de pijl met de klok mee rolt tegen de klok in en de pijl tegen de klok in rolt met de klok mee. Uit: elke pijl rolt de weergave in de richting waarin hij krult.",
"prefs.invert_navigator_arrows":"De rotatiepijlen van de weergavekubus omkeren",
"prefs.invert_navigator_arrows_note":"De pijlen rond de kubus laten de camera roteren in de richting waarin ze wijzen, zoals in Fusion en Inventor: de pijl links wijst naar rechts en zwaait de camera naar rechts. Uit: een pijl draait de kubus naar de kant waar hij staat, dus de linkerpijl zwaait de camera naar links. De gebogen rolpijlen worden hierdoor niet beïnvloed.",
"prefs.keep_work_on_tab_switch":"Dialoogvensters en gereedschappen open houden bij het wisselen van ontwerp",
"prefs.keep_work_on_tab_switch_note":"Elk ontwerptabblad behoudt zijn geopende dialoogvenster of gereedschap, met wat je erin hebt gekozen en getypt, en zijn lint-tabblad. Ze zijn er weer als je terugkeert naar het ontwerp. Uit: wisselen van ontwerp breekt ze af en toont het tabblad Model.",
"prefs.labels":"Labels",
@@ -2290,7 +2286,6 @@
"prefs.navigation":"Navigatie",
"prefs.navigation_note":"Hoe de muis, het trackpad en de 3D-muis de weergave verplaatsen.",
"prefs.navigator_fit":"Het model passend in beeld brengen na een wijziging van de navigatorweergave",
"prefs.navigator_fit_note":"Een vlak, hoek of pijl van de weergavekubus, of een as van de gizmo, kijkt ook naar het midden van het model, vanaf de ene afstand waarop het van alle kanten in beeld past (de omsluitende bol). Beginweergave gebruikt dezelfde zoom en hetzelfde middelpunt zolang dit aan staat, zodat weergaven die je daarna kiest alleen draaien. Uit: de weergave draait ter plaatse en behoudt het richtpunt en de zoom.",
"prefs.network_host_remove":"Verwijderen",
"prefs.network_locations":"Netwerklocaties in ontwerpen toestaan",
"prefs.network_locations_note":"Uit: gekoppelde bestanden van een ontwerp op een andere computer (\\\\server\\share) worden alleen geopend vanaf de servers in deze lijst, of nadat je er een toestaat wanneer erom wordt gevraagd.",
"en":{"ui.agent_chat_snap_none_found":"No coding agent found. The Snap package is confined, so it cannot see or start the coding agents installed on this computer. To use them, install SolidCraft from the AppImage, .deb or .rpm package instead. An agent that uses an OpenAI-compatible API works in the Snap package too: set one up with Add agent."},
"ar":{"ui.agent_chat_snap_none_found":"لم يُعثر على وكيل برمجي. تعمل حزمة Snap في بيئة معزولة، لذا لا يمكنها رؤية الوكلاء البرمجيين المثبّتين على هذا الحاسوب ولا تشغيلهم. لاستخدامهم، ثبّت SolidCraft من حزمة AppImage أو .deb أو .rpm بدلًا من ذلك. يعمل الوكيل الذي يستخدم واجهة برمجية متوافقة مع OpenAI في حزمة Snap أيضًا: أعدّه من «إضافة وكيل»."},
"cs":{"ui.agent_chat_snap_none_found":"Nebyl nalezen žádný kódovací agent. Balíček Snap běží v izolovaném prostředí, takže nevidí kódovací agenty nainstalované v tomto počítači a nemůže je spustit. Chcete-li je používat, nainstalujte SolidCraft z balíčku AppImage, .deb nebo .rpm. Agent, který používá API kompatibilní s OpenAI, v balíčku Snap funguje: nastavte ho přes Přidat agenta."},
"de":{"ui.agent_chat_snap_none_found":"Kein Coding-Agent gefunden. Das Snap-Paket ist abgeschottet und kann die auf diesem Computer installierten Coding-Agenten weder sehen noch starten. Um sie zu nutzen, installieren Sie SolidCraft stattdessen aus dem AppImage-, .deb- oder .rpm-Paket. Ein Agent, der eine OpenAI-kompatible API nutzt, funktioniert auch im Snap-Paket: Richten Sie ihn mit Agent hinzufügen ein."},
"es":{"ui.agent_chat_snap_none_found":"No se ha encontrado ningún agente de programación. El paquete Snap está confinado, así que no puede ver ni iniciar los agentes de programación instalados en este equipo. Para usarlos, instala SolidCraft desde el paquete AppImage, .deb o .rpm. Un agente que use una API compatible con OpenAI también funciona en el paquete Snap: configúralo con Añadir agente."},
"fr":{"ui.agent_chat_snap_none_found":"Aucun agent de programmation trouvé. Le paquet Snap est confiné, il ne peut donc ni voir ni lancer les agents de programmation installés sur cet ordinateur. Pour les utiliser, installer plutôt SolidCraft depuis le paquet AppImage, .deb ou .rpm. Un agent qui utilise une API compatible OpenAI fonctionne aussi dans le paquet Snap. Le configurer avec Ajouter un agent."},
"he":{"ui.agent_chat_snap_none_found":"לא נמצא סוכן קוד. חבילת ה־Snap מבודדת, ולכן היא לא יכולה לראות או להפעיל את סוכני הקוד שמותקנים במחשב הזה. כדי להשתמש בהם, יש להתקין את SolidCraft מחבילת AppImage, .deb או .rpm במקום זאת. סוכן שמשתמש ב־API תואם OpenAI עובד גם בחבילת ה־Snap, ואפשר להגדיר אותו בעזרת הוספת סוכן."},
"it":{"ui.agent_chat_snap_none_found":"Nessun agente di programmazione trovato. Il pacchetto Snap è confinato, quindi non può vedere né avviare gli agenti di programmazione installati su questo computer. Per usarli, installa SolidCraft dal pacchetto AppImage, .deb o .rpm. Un agente che usa un’API compatibile con OpenAI funziona anche nel pacchetto Snap: configuralo con Aggiungi agente."},
"ko":{"ui.agent_chat_snap_none_found":"코딩 에이전트를 찾지 못했습니다. Snap 패키지는 격리된 환경에서 실행되므로 이 컴퓨터에 설치된 코딩 에이전트를 보거나 시작할 수 없습니다. 이 에이전트를 사용하려면 대신 AppImage, .deb 또는 .rpm 패키지로 SolidCraft를 설치하세요. OpenAI 호환 API를 사용하는 에이전트는 Snap 패키지에서도 작동합니다. 에이전트 추가에서 설정하세요."},
"nl":{"ui.agent_chat_snap_none_found":"Geen codeeragent gevonden. Het Snap-pakket is afgeschermd en kan de codeeragents die op deze computer zijn geïnstalleerd niet zien of starten. Installeer SolidCraft in plaats daarvan via het AppImage-, .deb- of .rpm-pakket om ze te gebruiken. Een agent die een OpenAI-compatibele API gebruikt, werkt ook in het Snap-pakket: stel er een in met Agent toevoegen."},
"pl":{"ui.agent_chat_snap_none_found":"Nie znaleziono agenta programistycznego. Pakiet Snap działa w izolacji, więc nie widzi agentów programistycznych zainstalowanych na tym komputerze i nie może ich uruchomić. Aby z nich korzystać, zainstaluj SolidCraft z pakietu AppImage, .deb lub .rpm. Agent korzystający z API zgodnego z OpenAI działa także w pakiecie Snap: skonfiguruj go przyciskiem Dodaj agenta."},
"pt-BR":{"ui.agent_chat_snap_none_found":"Nenhum agente de programação encontrado. O pacote Snap é confinado, então não consegue ver nem iniciar os agentes de programação instalados neste computador. Para usá-los, instale o SolidCraft pelo pacote AppImage, .deb ou .rpm. Um agente que usa uma API compatível com OpenAI também funciona no pacote Snap: configure-o em Adicionar agente."},
"ru":{"ui.agent_chat_snap_none_found":"Кодирующий агент не найден. Пакет Snap работает в изолированной среде, поэтому не видит и не может запускать кодирующих агентов, установленных на этом компьютере. Чтобы пользоваться ими, установите SolidCraft из пакета AppImage, .deb или .rpm. Агент, использующий API, совместимый с OpenAI, работает и в пакете Snap: настройте его через «Добавить агент»."},
"tr":{"ui.agent_chat_snap_none_found":"Kodlama ajanı bulunamadı. Snap paketi yalıtılmış çalıştığı için bu bilgisayarda yüklü kodlama ajanlarını göremez ve başlatamaz. Bunları kullanmak için SolidCraft'ı bunun yerine AppImage, .deb veya .rpm paketinden yükleyin. OpenAI uyumlu API kullanan bir ajan Snap paketinde de çalışır: Ajan ekle ile ayarlayın."},
"uk":{"ui.agent_chat_snap_none_found":"Агента для програмування не знайдено. Пакет Snap працює в ізольованому середовищі, тому не бачить і не може запускати агентів для програмування, установлених на цьому комп’ютері. Щоб користуватися ними, установіть SolidCraft з пакета AppImage, .deb або .rpm. Агент, що використовує API, сумісний з OpenAI, працює і в пакеті Snap: налаштуйте його через «Додати агента»."},
"vi":{"ui.agent_chat_snap_none_found":"Không tìm thấy tác tử lập trình nào. Gói Snap chạy trong môi trường bị giới hạn nên không thể thấy hay khởi động các tác tử lập trình được cài trên máy tính này. Để dùng chúng, hãy cài SolidCraft từ gói AppImage, .deb hoặc .rpm thay vào đó. Tác tử dùng API tương thích OpenAI vẫn hoạt động trong gói Snap: hãy thiết lập bằng Thêm AI Agent."},
"prefs.history_steps_component":"خطوات السجل حسب المكوّن",
"prefs.history_steps_component_note":"عندما يكون مكوّن نشطًا أو محددًا، تتنقل عناصر تحكم السجل بين ميزات ذلك المكوّن كما يعرضها السجل في الأسفل. عند إيقافه تتنقل بين كل ميزات التصميم. السجل على اليسار أو تحت المستعرض يتنقل دائمًا بين كل الميزات."
},
"de":{
"prefs.history_steps_component":"Verlaufsschritte je Komponente",
"prefs.history_steps_component_note":"Ist eine Komponente aktiv oder ausgewählt, gehen die Verlaufssteuerelemente durch deren Features, so wie der Verlauf unten sie zeigt. Aus: Sie gehen durch alle Features des Designs. Der Verlauf links oder unter dem Browser geht immer durch alle Features."
},
"en":{
"prefs.history_steps_component":"History steps by component",
"prefs.history_steps_component_note":"With a component active or picked, the History controls step through that component's features, as the History at the bottom shows them. Off: they step through every feature of the design. The History on the left or under the browser always steps through every feature."
},
"he":{
"prefs.history_steps_component":"צעדי ציר הזמן לפי רכיב",
"prefs.history_steps_component_note":"כשרכיב פעיל או נבחר, פקדי ציר הזמן עוברים בין הפיצ'רים של אותו רכיב, כפי שציר הזמן למטה מציג אותם. כבוי: הם עוברים בין כל הפיצ'רים של התכנון. ציר הזמן משמאל או מתחת לדפדפן עובר תמיד בין כל הפיצ'רים."
"prefs.history_steps_component_note":"구성 요소가 활성화되었거나 선택되어 있으면 기록 컨트롤이 아래쪽 히스토리에 보이는 대로 그 구성 요소의 피처만 이동합니다. 끄면 디자인의 모든 피처를 이동합니다. 왼쪽 또는 브라우저 아래의 히스토리는 항상 모든 피처를 이동합니다."
},
"vi":{
"prefs.history_steps_component":"Bước Lịch sử theo cụm chi tiết",
"prefs.history_steps_component_note":"Khi một cụm chi tiết đang kích hoạt hoặc được chọn, các điều khiển Lịch sử đi qua các tính năng của cụm đó, như dòng Lịch sử ở cạnh dưới hiển thị. Tắt: chúng đi qua mọi tính năng của thiết kế. Cây Lịch sử ở bên trái hoặc dưới cây thư mục luôn đi qua mọi tính năng."
"One more stretch of the picked edge, each with its own angle, height and turns":"One more stretch of the picked edge, each with its own angle, height and turns"
},
"ar":{
"Segments":"المقاطع",
"Split Edge":"تقسيم الحافة",
"Remove this segment":"إزالة هذا المقطع",
"One more stretch of the picked edge, each with its own angle, height and turns":"مقطع آخر من الحافة المختارة، لكل منها زاويتها وارتفاعها ولفّاتها"
},
"he":{
"Segments":"מקטעים",
"Split Edge":"פיצול המקצוע",
"Remove this segment":"הסרת המקטע הזה",
"One more stretch of the picked edge, each with its own angle, height and turns":"מקטע נוסף של המקצוע הנבחר, שלכל אחד זווית, גובה וכיפופים משלו"
},
"ja":{
"Segments":"セグメント",
"Split Edge":"エッジを分割",
"Remove this segment":"このセグメントを削除",
"One more stretch of the picked edge, each with its own angle, height and turns":"選択したエッジのもう一つの区間。それぞれに独自の角度、高さ、曲げ方向があります"
},
"vi":{
"Segments":"Đoạn",
"Split Edge":"Chia cạnh",
"Remove this segment":"Xóa đoạn này",
"One more stretch of the picked edge, each with its own angle, height and turns":"Thêm một đoạn nữa của cạnh đã chọn, mỗi đoạn có góc, chiều cao và hướng uốn riêng"
},
"zh":{
"Segments":"分段",
"Split Edge":"分割边",
"Remove this segment":"移除此分段",
"One more stretch of the picked edge, each with its own angle, height and turns":"所选边的又一段,每段都有自己的角度、高度和折弯方向"
"prefs.invert_curved_roll_arrows_note":"Dwie zakrzywione strzałki w rogu kostki przechylają widok w płaszczyźnie ekranu. Włączone: strzałka zgodna z ruchem wskazówek zegara przechyla przeciwnie, a przeciwna – zgodnie. Wyłączone: każda strzałka przechyla widok w stronę, w którą jest zakrzywiona.",
"prefs.invert_navigator_arrows_note":"Strzałki wokół kostki obracają kamerę w kierunku, w który wskazują, jak w Fusion i Inventor: strzałka po lewej wskazuje w prawo i obraca kamerę w prawo. Wyłączone: strzałka obraca kostkę w stronę, po której się znajduje, więc lewa strzałka obraca kamerę w lewo. Zakrzywione strzałki przechylenia nie są zmieniane.",
"prefs.keep_unsaved_also_asks":"Mimo to pytaj przy zamykaniu karty",
"prefs.keep_unsaved_also_asks_tip":"Gdy wyłączone, karta zamyka się bez okna przeglądarki, jeśli kopia każdego niezapisanego projektu jest aktualna.",
"prefs.keep_unsaved_in_browser":"Przechowuj niezapisaną pracę w tej przeglądarce",
@@ -2721,7 +2717,6 @@
"prefs.move_drag_highlights_tip":"Wyłączone: gdy strzałki Przesuń przeciągają komponent, nic pod wskaźnikiem nie jest podświetlane, co zachowuje płynność dużych złożeń. Przy pokazanym przekroju lub analizie podświetlenie i tak działa, a część przeciągana wzdłuż złączy nigdy go nie podświetla.",
"prefs.navigation":"Nawigacja",
"prefs.navigation.axes_signed":"Osie",
"prefs.navigation.cube_compact":"Kostka",
"prefs.navigation.device":"Urządzenie",
"prefs.navigation.free":"Swobodne",
"prefs.navigation.orbit_style":"Styl obracania",
@@ -2729,7 +2724,6 @@
"prefs.navigation.turntable":"Obrotnica",
"prefs.navigation_note":"Jak mysz, gładzik i mysz 3D poruszają widokiem.",
"prefs.navigator_fit":"Dopasuj model po zmianie widoku w nawigatorze",
"prefs.navigator_fit_note":"Ściana, narożnik lub strzałka kostki widoku albo oś wskaźnika orientacji kieruje też widok na środek modelu z odległości, z której mieści się on z każdej strony (jego sfera otaczająca). Gdy to jest włączone, Widok główny używa tego samego powiększenia i środka, więc widoki wybrane później tylko się obracają. Wyłączone: widok obraca się w miejscu, zachowując cel i powiększenie.",
"prefs.network_host_remove":"Usuń",
"prefs.network_locations":"Zezwalaj na lokalizacje sieciowe w projektach",
"prefs.network_locations_note":"Wyłączone: połączone pliki projektu na innym komputerze (\\\\serwer\\udział) są otwierane tylko z serwerów wymienionych tutaj albo po zezwoleniu, gdy pojawi się pytanie.",
"Ctrl+Enter continues on GitHub.":"Ctrl+Enter continua no GitHub.",
"Cube":"Cubo",
"Current orthographic view (1:1 mm)":"Vista ortográfica atual (1:1 mm)",
"Current perspective view":"Vista em perspectiva atual",
"Current plane":"Plano atual",
@@ -2659,9 +2658,6 @@
"prefs.interface_note":"A aparência do SolidCraft e onde ficam suas partes.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Inverter as setas curvas de rolagem",
"prefs.invert_curved_roll_arrows_note":"As duas setas curvas no canto do cubo rolam a vista no plano da tela. Ativado: a seta horária rola no sentido anti-horário e a seta anti-horária rola no sentido horário. Desativado: cada seta rola a vista no sentido em que se curva.",
"prefs.invert_navigator_arrows":"Inverter as setas de rotação do cubo de vista",
"prefs.invert_navigator_arrows_note":"As setas em volta do cubo orbitam a câmera para onde apontam, como no Fusion e no Inventor: a seta da esquerda aponta para a direita e gira a câmera para a direita. Desativado: uma seta gira o cubo em direção ao lado em que está, então a seta da esquerda gira a câmera para a esquerda. As setas curvas de rolagem não são afetadas.",
"prefs.keep_unsaved_also_asks":"Perguntar mesmo assim ao fechar a aba",
"prefs.keep_unsaved_also_asks_tip":"Com isto desativado, a aba fecha sem a caixa do navegador quando a cópia de cada projeto não salvo estiver atualizada.",
"prefs.keep_unsaved_in_browser":"Guardar trabalho não salvo neste navegador",
@@ -2721,7 +2717,6 @@
"prefs.move_drag_highlights_tip":"Desativado: enquanto as setas de Mover arrastam um componente, nada sob o ponteiro é destacado, o que mantém montagens grandes fluidas. Com um corte ou uma análise exibidos, o destaque acontece mesmo assim, e uma peça arrastada ao longo de suas juntas nunca o destaca.",
"prefs.navigation":"Navegação",
"prefs.navigation.axes_signed":"Eixos",
"prefs.navigation.cube_compact":"Cubo",
"prefs.navigation.device":"Dispositivo",
"prefs.navigation.free":"Livre",
"prefs.navigation.orbit_style":"Estilo de órbita",
@@ -2729,7 +2724,6 @@
"prefs.navigation.turntable":"Mesa giratória",
"prefs.navigation_note":"Como o mouse, o trackpad e o mouse 3D movem a vista.",
"prefs.navigator_fit":"Ajustar o modelo após uma mudança de vista do navegador",
"prefs.navigator_fit_note":"Uma face, um canto ou uma seta do cubo de vista, ou um eixo do gizmo, também olha para o meio do modelo a partir da distância única que o enquadra de todos os lados (sua esfera envolvente). Início usa o mesmo zoom e centro enquanto isto estiver ativado, então as vistas escolhidas depois só giram. Desativado: a vista gira no lugar, mantendo seu alvo e zoom.",
"prefs.network_host_remove":"Remover",
"prefs.network_locations":"Permitir locais de rede nos projetos",
"prefs.network_locations_note":"Para organizações que mantêm projetos em servidores de arquivos. Desativado: apenas servidores que você permitir.",
"Current perspective view":"Vizualizare în perspectivă curentă",
"Current plane":"Planul curent",
@@ -2240,9 +2239,6 @@
"prefs.interface_note":"Cum arată SolidCraft și unde sunt așezate elementele sale.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Inversează săgețile curbe de ruliu",
"prefs.invert_curved_roll_arrows_note":"Cele două săgeți curbe din colțul cubului rotesc vizualizarea în planul ecranului. Activat: săgeata în sens orar rotește în sens antiorar, iar săgeata în sens antiorar rotește în sens orar. Dezactivat: fiecare săgeată rotește vizualizarea în direcția în care se curbează.",
"prefs.invert_navigator_arrows":"Inversează săgețile de rotație ale cubului de vizualizare",
"prefs.invert_navigator_arrows_note":"Săgețile din jurul cubului rotesc camera în jurul modelului în direcția în care indică, ca în Fusion și Inventor: săgeata din stânga indică spre dreapta și rotește camera spre dreapta. Dezactivat: o săgeată rotește cubul spre partea pe care se află, deci săgeata din stânga rotește camera spre stânga. Săgețile curbe de ruliu nu sunt afectate.",
"prefs.keep_work_on_tab_switch":"Păstrează deschise dialogurile și instrumentele la schimbarea proiectului",
"prefs.keep_work_on_tab_switch_note":"Fiecare filă de proiect își păstrează dialogul sau instrumentul deschis, cu ce ați ales și ați tastat în el, și fila ei din bara de instrumente. Le regăsiți când reveniți la proiect. Dezactivat: schimbarea proiectului le anulează și afișează fila Model.",
"prefs.labels":"Etichete",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Navigare",
"prefs.navigation_note":"Cum mută vizualizarea mouse-ul, trackpadul și mouse-ul 3D.",
"prefs.navigator_fit":"Potrivește modelul după o schimbare de vizualizare din navigator",
"prefs.navigator_fit_note":"O față, un colț sau o săgeată a cubului de vizualizare ori o axă a reperului de orientare privește și spre mijlocul modelului, de la singura distanță care îl cuprinde din toate părțile (sfera lui de încadrare). Acasă folosește același zoom și același centru cât timp opțiunea este activată, așa că vizualizările alese după aceea doar se rotesc. Dezactivat: vizualizarea se rotește pe loc, păstrându-și ținta și zoomul.",
"prefs.network_host_remove":"Elimină",
"prefs.network_locations":"Permite locațiile de rețea în proiecte",
"prefs.network_locations_note":"Dezactivat: fișierele legate ale unui proiect aflate pe alt computer (\\\\server\\share) se deschid doar de pe serverele listate aici sau după ce permiteți unul, când vi se cere.",
"prefs.invert_curved_roll_arrows_note":"Две изогнутые стрелки в углу куба поворачивают вид в плоскости экрана. Вкл.: стрелка по часовой стрелке поворачивает вид против часовой, а стрелка против часовой — по часовой. Выкл.: каждая стрелка поворачивает вид в ту сторону, куда она изогнута.",
"prefs.invert_navigator_arrows_note":"Стрелки вокруг куба вращают камеру в ту сторону, куда указывают, как в Fusion и Inventor: стрелка слева указывает вправо и поворачивает камеру вправо. Выкл.: стрелка поворачивает куб в сторону, где она расположена, поэтому левая стрелка поворачивает камеру влево. Изогнутые стрелки крена не затрагиваются.",
"prefs.keep_work_on_tab_switch":"Сохранять открытые диалоги и инструменты при переключении между дизайнами",
"prefs.keep_work_on_tab_switch_note":"Каждая вкладка дизайна сохраняет свой открытый диалог или инструмент вместе с тем, что вы в нём выбрали и ввели, а также свою вкладку панели инструментов. Они снова на месте, когда вы возвращаетесь к дизайну. Выкл.: при переключении дизайнов они отменяются, и показывается вкладка «Модель».",
"prefs.labels":"Подписи",
@@ -2271,7 +2267,6 @@
"prefs.navigation":"Навигация",
"prefs.navigation_note":"Как мышь, тачпад и 3D-мышь двигают вид.",
"prefs.navigator_fit":"Вписывать модель после смены вида в навигаторе",
"prefs.navigator_fit_note":"Грань, угол или стрелка куба видов либо ось гизмо также направляет взгляд на середину модели с единственного расстояния, с которого она целиком видна со всех сторон (её габаритная сфера). «Главная» при включённом параметре использует тот же масштаб и центр, поэтому выбранные после неё виды только поворачиваются. Выкл.: вид поворачивается на месте, сохраняя свою цель и масштаб.",
"prefs.network_host_remove":"Убрать",
"prefs.network_locations":"Разрешить сетевые расположения в дизайнах",
"prefs.network_locations_note":"Выкл.: связанные файлы дизайна на другом компьютере (\\\\server\\share) открываются только с перечисленных здесь серверов или после того, как вы разрешите сервер по запросу.",
"prefs.interface_note":"Како SolidCraft изгледа и где су његови делови.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Обрни закривљене стрелице за ваљање",
"prefs.invert_curved_roll_arrows_note":"Две закривљене стрелице у углу коцке ваљају приказ унутар равни екрана. Укључено: стрелица у смеру казаљке на сату ваља приказ супротно од смера казаљке на сату, а стрелица супротно од смера казаљке на сату ваља га у смеру казаљке на сату. Искључено: свака стрелица ваља приказ онако како је закривљена.",
"prefs.invert_navigator_arrows":"Обрни стрелице за ротацију коцке приказа",
"prefs.invert_navigator_arrows_note":"Стрелице око коцке орбитирају камеру у смеру у којем показују, као у програмима Fusion и Inventor: стрелица са леве стране показује удесно и закреће камеру удесно. Искључено: стрелица окреће коцку ка страни на којој се налази, па лева стрелица закреће камеру улево. Подешавање не утиче на закривљене стрелице за ваљање.",
"prefs.keep_work_on_tab_switch":"Задржи дијалоге и алате отвореним при пребацивању између дизајна",
"prefs.keep_work_on_tab_switch_note":"Свака картица дизајна задржава свој отворени дијалог или алат, са оним што сте у њему изабрали и уписали, те своју картицу траке са алатима. Када се вратите на дизајн, поново су ту. Искључено: пребацивање између дизајна отказује их и приказује картицу Модел.",
"prefs.labels":"Ознаке",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Навигација",
"prefs.navigation_note":"Како миш, додирна табла и 3D миш померају приказ.",
"prefs.navigator_fit":"Уклопи модел након промене приказа у навигатору",
"prefs.navigator_fit_note":"Лице, угао или стрелица коцке приказа, односно оса гизма оријентације, такође усмерава поглед на средину модела са једног растојања на којем се уклапа из сваког смера (његова гранична сфера). Почетни приказ користи исти зум и центар док је ова опција укључена, па се прикази које изаберете после њега само окрећу. Искључено: приказ се окреће на месту и задржава свој циљ и зум.",
"prefs.network_host_remove":"Уклони",
"prefs.network_locations":"Дозволи мрежне локације у дизајнима",
"prefs.network_locations_note":"Искључено: повезане датотеке дизајна на другом рачунару (\\\\server\\share) отварају се само са сервера наведених овде или након што га дозволите када се то затражи.",
"prefs.interface_note":"Hur SolidCraft ser ut och var dess delar placeras.",
"prefs.interval_minutes":"{n} min",
"prefs.invert_curved_roll_arrows":"Invertera de böjda rullningspilarna",
"prefs.invert_curved_roll_arrows_note":"De två böjda pilarna vid kubens hörn rullar vyn i skärmplanet. På: medurspilen rullar moturs och moturspilen rullar medurs. Av: varje pil rullar vyn åt det håll den kröker sig.",
"prefs.invert_navigator_arrows":"Invertera rotationspilarna på vykuben",
"prefs.invert_navigator_arrows_note":"Pilarna runt kuben orbiterar kameran åt det håll de pekar, som i Fusion och Inventor: pilen till vänster pekar åt höger och svänger kameran åt höger. Av: en pil roterar kuben mot den sida den sitter på, så den vänstra pilen svänger kameran åt vänster. De böjda rullningspilarna påverkas inte.",
"prefs.keep_work_on_tab_switch":"Behåll dialoger och verktyg öppna när du byter konstruktion",
"prefs.keep_work_on_tab_switch_note":"Varje konstruktionsflik behåller sin öppna dialog eller sitt öppna verktyg, med det du har valt och skrivit i den, och sin flik i menyfliksområdet. De finns kvar när du kommer tillbaka till konstruktionen. Av: att byta konstruktion avbryter dem och visar fliken Modell.",
"prefs.labels":"Etiketter",
@@ -2280,7 +2276,6 @@
"prefs.navigation":"Navigering",
"prefs.navigation_note":"Hur musen, styrplattan och 3D-musen flyttar vyn.",
"prefs.navigator_fit":"Anpassa modellen efter en vyändring i navigatorn",
"prefs.navigator_fit_note":"En yta, ett hörn eller en pil på vykuben, eller en axel på orienteringsgizmot, tittar också mot modellens mitt från det enda avstånd som får den att rymmas från alla sidor (dess begränsningssfär). Hem använder samma zoom och centrum medan detta är på, så vyer som väljs efteråt bara vrids. Av: vyn vrids på stället och behåller sitt mål och sin zoom.",
"prefs.network_host_remove":"Ta bort",
"prefs.network_locations":"Tillåt nätverksplatser i konstruktioner",
"prefs.network_locations_note":"Av: en konstruktions länkade filer på en annan dator (\\\\server\\share) öppnas bara från de servrar som listas här, eller efter att du tillåtit en när du tillfrågas.",
"prefs.interface_note":"SolidCraft'ın nasıl göründüğü ve parçalarının nerede durduğu.",
"prefs.interval_minutes":"{n} dk",
"prefs.invert_curved_roll_arrows":"Eğri yuvarlanma oklarını ters çevir",
"prefs.invert_curved_roll_arrows_note":"Küpün köşesindeki iki eğri ok, görünümü ekran düzlemi içinde yuvarlar. Açık: saat yönündeki ok saat yönünün tersine, saat yönünün tersindeki ok saat yönünde yuvarlar. Kapalı: her ok görünümü kıvrıldığı yöne yuvarlar.",
"prefs.invert_navigator_arrows":"Görünüm küpünün döndürme oklarını ters çevir",
"prefs.invert_navigator_arrows_note":"Küpün çevresindeki oklar kamerayı gösterdikleri yönde döndürür, tıpkı Fusion ve Inventor'daki gibi: soldaki ok sağı gösterir ve kamerayı sağa savurur. Kapalı: bir ok küpü bulunduğu tarafa doğru döndürür, yani sol ok kamerayı sola savurur. Kıvrık yuvarlanma okları bundan etkilenmez.",
"prefs.keep_work_on_tab_switch":"Tasarımlar arasında geçerken diyalogları ve araçları açık tut",
"prefs.keep_work_on_tab_switch_note":"Her tasarım sekmesi, açık diyaloğunu veya aracını, içinde seçtiklerinizi ve yazdıklarınızı ve şerit sekmesini korur. Tasarıma döndüğünüzde yeniden oradadırlar. Kapalı: tasarımlar arasında geçmek bunları iptal eder ve Model sekmesini gösterir.",
"prefs.labels":"Etiketler",
@@ -2279,7 +2275,6 @@
"prefs.navigation":"Gezinme",
"prefs.navigation_note":"Farenin, trackpad'in ve 3B farenin görünümü nasıl hareket ettirdiği.",
"prefs.navigator_fit":"Gezgin görünümü değiştikten sonra modeli sığdır",
"prefs.navigator_fit_note":"Görünüm küpünün bir yüzü, köşesi veya oku ya da gizmonun bir ekseni, modelin ortasına da modeli her yönden sığdıran tek bir uzaklıktan (sınırlayıcı küresi) bakar. Bu açıkken Ana görünüm aynı yakınlaştırmayı ve merkezi kullanır; böylece sonrasında seçilen görünümler yalnızca döner. Kapalı: görünüm hedefini ve yakınlaştırmasını koruyarak yerinde döner.",
"prefs.network_host_remove":"Kaldır",
"prefs.network_locations":"Tasarımlarda ağ konumlarına izin ver",
"prefs.network_locations_note":"Kapalı: bir tasarımın başka bir bilgisayardaki bağlı dosyaları (\\\\server\\paylaşım) yalnızca burada listelenen sunuculardan veya sorulduğunda izin verdiklerinizden açılır.",
"prefs.invert_curved_roll_arrows_note":"Дві вигнуті стрілки в куті куба обертають вид у площині екрана. Увімкнено: стрілка за годинниковою стрілкою обертає проти годинникової, а стрілка проти годинникової — за годинниковою. Вимкнено: кожна стрілка обертає вид у напрямку свого завитку.",
"prefs.invert_navigator_arrows":"Інвертувати стрілки обертання куба виду",
"prefs.invert_navigator_arrows_note":"Стрілки навколо куба обертають камеру в той бік, куди вказують, як у Fusion та Inventor: стрілка ліворуч вказує праворуч і повертає камеру праворуч. Вимк.: стрілка обертає куб у той бік, де вона розташована, тож ліва стрілка повертає камеру ліворуч. Вигнуті стрілки крену не змінюються.",
"prefs.keep_unsaved_also_asks":"Усе одно запитувати під час закриття вкладки",
"prefs.keep_unsaved_also_asks_tip":"Коли це вимкнено, вкладка закривається без вікна браузера, щойно копія кожного незбереженого проєкту актуальна.",
"prefs.keep_unsaved_in_browser":"Зберігати незбережену роботу в цьому браузері",
@@ -2714,7 +2710,6 @@
"prefs.move_drag_highlights_tip":"Вимкнено: коли стрілки «Перемістити» тягнуть компонент, нічого під вказівником не підсвічується, що зберігає плавність великих збірок. Якщо показано переріз чи аналіз, підсвічування все одно працює, а деталь, яку тягнуть уздовж її з’єднань, ніколи його не вмикає.",
"prefs.navigation":"Навігація",
"prefs.navigation.axes_signed":"Осі",
"prefs.navigation.cube_compact":"Куб",
"prefs.navigation.device":"Пристрій",
"prefs.navigation.free":"Вільне",
"prefs.navigation.orbit_style":"Стиль обертання",
@@ -2722,7 +2717,6 @@
"prefs.navigation.turntable":"Поворотний стіл",
"prefs.navigation_note":"Як миша, трекпад і 3D-миша рухають вид.",
"prefs.navigator_fit":"Вміщувати модель після зміни виду в навігаторі",
"prefs.navigator_fit_note":"Грань, кут або стрілка куба виду чи вісь гізмо також дивляться на середину моделі з єдиної відстані, з якої її видно цілком з усіх боків (її обмежувальна сфера). «Головний вид» за ввімкненого параметра використовує той самий масштаб і центр, тож види, вибрані після нього, лише повертаються. Вимк.: вид повертається на місці, зберігаючи свою ціль і масштаб.",
"prefs.network_host_remove":"Вилучити",
"prefs.network_locations":"Дозволяти мережеві розташування в проєктах",
"prefs.network_locations_note":"Для організацій, які зберігають проєкти на файлових серверах. Вимкнено: лише дозволені вами сервери.",
"Ctrl+Enter continues on GitHub.":"Nhấn Ctrl+Enter để tiếp tục trên GitHub.",
"Cube":"Khối lập phương",
"Current orthographic view (1:1 mm)":"Góc nhìn trực giao hiện tại (1:1 mm)",
"Current perspective view":"Góc nhìn phối cảnh hiện tại",
"Current plane":"Mặt phẳng hiện tại",
@@ -2683,9 +2682,9 @@
"prefs.interface_note":"Diện mạo của SolidCraft và cách bố trí các bảng điều khiển.",
"prefs.interval_minutes":"{n} phút",
"prefs.invert_curved_roll_arrows":"Đảo ngược mũi tên cuộn cong",
"prefs.invert_curved_roll_arrows_note":"Hai mũi tên cong ở góc khối cuộn khung nhìn trong mặt phẳng màn hình. Khi bật: mũi tên theo chiều kim đồng hồ cuộn ngược chiều kim đồng hồ và ngược lại. Khi tắt: mỗi mũi tên cuộn khung nhìn theo chiều cong của nó.",
"prefs.invert_curved_roll_arrows_note":"Hai mũi tên cong ở góc bộ điều hướng cuộn khung nhìn trong mặt phẳng màn hình. Khi bật: mũi tên theo chiều kim đồng hồ cuộn ngược chiều kim đồng hồ và ngược lại. Khi tắt: mỗi mũi tên cuộn khung nhìn theo chiều cong của nó.",
"prefs.invert_navigator_arrows":"Đảo ngược mũi tên xoay của khối điều hướng",
"prefs.invert_navigator_arrows_note":"Các mũi tên quanh khối xoay camera theo hướng mũi tên chỉ, như trong Fusion và Inventor: mũi tên bên trái chỉ sang phải và đưa camera sang phải. Khi tắt: mũi tên xoay khối về phía cạnh chứa nó, nên mũi tên bên trái đưa camera sang trái. Hai mũi tên cuộn cong không bị ảnh hưởng.",
"prefs.invert_navigator_arrows_note":"Các mũi tên quanh bộ điều hướng xoay camera theo hướng mũi tên chỉ, như trong Fusion và Inventor: mũi tên bên trái chỉ sang phải và đưa camera sang phải. Khi tắt: mũi tên xoay khung nhìn về phía chứa nó, nên mũi tên bên trái đưa camera sang trái. Hai mũi tên cuộn cong không bị ảnh hưởng.",
"prefs.keep_unsaved_also_asks":"Vẫn hỏi khi đóng thẻ",
"prefs.keep_unsaved_also_asks_tip":"Khi tắt, thẻ sẽ đóng mà không có hộp thoại của trình duyệt khi bản sao của mọi thiết kế chưa lưu đã được cập nhật.",
"prefs.keep_unsaved_in_browser":"Giữ công việc chưa lưu trong trình duyệt này",
@@ -2745,7 +2744,6 @@
"prefs.move_drag_highlights_tip":"Tắt: khi các mũi tên của Di chuyển kéo một thành phần, không có gì dưới con trỏ được tô sáng, nhờ đó các cụm lắp ráp lớn vẫn mượt. Khi đang hiện mặt cắt hoặc phân tích thì vẫn tô sáng như thường, và chi tiết được kéo dọc theo khớp nối không bao giờ tô sáng.",
"prefs.navigation":"Điều hướng góc nhìn",
"prefs.navigation.axes_signed":"Trục",
"prefs.navigation.cube_compact":"Khối lập phương",
"prefs.navigation.device":"Thiết bị",
"prefs.navigation.free":"Tự do",
"prefs.navigation.orbit_style":"Kiểu xoay",
@@ -2753,7 +2751,7 @@
"prefs.navigation.turntable":"Bàn xoay",
"prefs.navigation_note":"Cách chuột, bàn di cảm ứng và chuột 3D di chuyển góc nhìn.",
"prefs.navigator_fit":"Khớp mô hình sau khi đổi góc nhìn bằng khối điều hướng",
"prefs.navigator_fit_note":"Nhấp vào một mặt, góc hoặc mũi tên của khối lập phương, hoặc một trục của gizmo, cũng nhìn vào tâm mô hình từ một khoảng cách duy nhất chứa trọn nó từ mọi phía (khối cầu bao). Khi bật, góc nhìn chuẩn (Home) dùng cùng mức thu phóng và tâm đó, nên các góc nhìn chọn sau chỉ xoay. Tắt: góc nhìn xoay tại chỗ, giữ nguyên điểm nhìn và mức thu phóng.",
"prefs.navigator_fit_note":"Nhấp vào một mặt, góc hoặc mũi tên của bộ điều hướng, hoặc một trục của gizmo, cũng nhìn vào tâm mô hình từ một khoảng cách duy nhất chứa trọn nó từ mọi phía (khối cầu bao). Khi bật, góc nhìn chuẩn (Home) dùng cùng mức thu phóng và tâm đó, nên các góc nhìn chọn sau chỉ xoay. Tắt: góc nhìn xoay tại chỗ, giữ nguyên điểm nhìn và mức thu phóng.",
"prefs.network_host_remove":"Xóa",
"prefs.network_locations":"Cho phép các vị trí mạng trong thiết kế",
"prefs.network_locations_note":"Dành cho tổ chức lưu thiết kế trên máy chủ tệp. Khi tắt: chỉ các máy chủ bạn cho phép.",
| assets/locales/lv.json | SolidCraft contributors | Original Latvian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
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.